swarm-engine 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +72 -0
- package/CLAUDE.md +89 -0
- package/LICENSE +21 -0
- package/README.md +235 -0
- package/agents/accessibility-reviewer.md +118 -0
- package/agents/api-contract-reviewer.md +99 -0
- package/agents/concurrency-reviewer.md +111 -0
- package/agents/data-integrity-reviewer.md +103 -0
- package/agents/debugger.md +99 -0
- package/agents/dependency-reviewer.md +115 -0
- package/agents/devils-advocate.md +94 -0
- package/agents/documentation-reviewer.md +114 -0
- package/agents/documenter.md +78 -0
- package/agents/error-handling-reviewer.md +113 -0
- package/agents/grounding.md +99 -0
- package/agents/guardian.md +87 -0
- package/agents/implementer.md +141 -0
- package/agents/integrator.md +95 -0
- package/agents/judge.md +79 -0
- package/agents/librarian.md +90 -0
- package/agents/orchestrator.md +331 -0
- package/agents/performance-reviewer.md +93 -0
- package/agents/planner.md +106 -0
- package/agents/refactorer.md +92 -0
- package/agents/researcher.md +97 -0
- package/agents/reviewer.md +117 -0
- package/agents/security-reviewer.md +107 -0
- package/agents/sentinel.md +92 -0
- package/agents/tester.md +93 -0
- package/agents/testing-reviewer.md +112 -0
- package/commands/diff-review.md +64 -0
- package/commands/fix-pr.md +78 -0
- package/commands/red-team.md +82 -0
- package/commands/research.md +59 -0
- package/commands/resume.md +80 -0
- package/commands/review-cycle.md +123 -0
- package/commands/swarm-setup.md +28 -0
- package/commands/swarm.md +126 -0
- package/commands/tdd.md +91 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +3 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/acp.d.ts +3 -0
- package/dist/cli/commands/acp.d.ts.map +1 -0
- package/dist/cli/commands/acp.js +29 -0
- package/dist/cli/commands/acp.js.map +1 -0
- package/dist/cli/commands/agents.d.ts +3 -0
- package/dist/cli/commands/agents.d.ts.map +1 -0
- package/dist/cli/commands/agents.js +344 -0
- package/dist/cli/commands/agents.js.map +1 -0
- package/dist/cli/commands/backends.d.ts +3 -0
- package/dist/cli/commands/backends.d.ts.map +1 -0
- package/dist/cli/commands/backends.js +22 -0
- package/dist/cli/commands/backends.js.map +1 -0
- package/dist/cli/commands/completions.d.ts +3 -0
- package/dist/cli/commands/completions.d.ts.map +1 -0
- package/dist/cli/commands/completions.js +85 -0
- package/dist/cli/commands/completions.js.map +1 -0
- package/dist/cli/commands/compound.d.ts +3 -0
- package/dist/cli/commands/compound.d.ts.map +1 -0
- package/dist/cli/commands/compound.js +119 -0
- package/dist/cli/commands/compound.js.map +1 -0
- package/dist/cli/commands/configure.d.ts +3 -0
- package/dist/cli/commands/configure.d.ts.map +1 -0
- package/dist/cli/commands/configure.js +151 -0
- package/dist/cli/commands/configure.js.map +1 -0
- package/dist/cli/commands/convert.d.ts +15 -0
- package/dist/cli/commands/convert.d.ts.map +1 -0
- package/dist/cli/commands/convert.js +218 -0
- package/dist/cli/commands/convert.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +96 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +154 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/learn.d.ts +3 -0
- package/dist/cli/commands/learn.d.ts.map +1 -0
- package/dist/cli/commands/learn.js +163 -0
- package/dist/cli/commands/learn.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +3 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +89 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/memory.d.ts +3 -0
- package/dist/cli/commands/memory.d.ts.map +1 -0
- package/dist/cli/commands/memory.js +134 -0
- package/dist/cli/commands/memory.js.map +1 -0
- package/dist/cli/commands/orchestrate.d.ts +3 -0
- package/dist/cli/commands/orchestrate.d.ts.map +1 -0
- package/dist/cli/commands/orchestrate.js +237 -0
- package/dist/cli/commands/orchestrate.js.map +1 -0
- package/dist/cli/commands/patterns.d.ts +3 -0
- package/dist/cli/commands/patterns.d.ts.map +1 -0
- package/dist/cli/commands/patterns.js +25 -0
- package/dist/cli/commands/patterns.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +3 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +77 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/plugin.d.ts +3 -0
- package/dist/cli/commands/plugin.d.ts.map +1 -0
- package/dist/cli/commands/plugin.js +124 -0
- package/dist/cli/commands/plugin.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +3 -0
- package/dist/cli/commands/resume.d.ts.map +1 -0
- package/dist/cli/commands/resume.js +55 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/run.d.ts +3 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +78 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/share.d.ts +3 -0
- package/dist/cli/commands/share.d.ts.map +1 -0
- package/dist/cli/commands/share.js +34 -0
- package/dist/cli/commands/share.js.map +1 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +148 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/template.d.ts +3 -0
- package/dist/cli/commands/template.d.ts.map +1 -0
- package/dist/cli/commands/template.js +213 -0
- package/dist/cli/commands/template.js.map +1 -0
- package/dist/cli/commands/vault.d.ts +3 -0
- package/dist/cli/commands/vault.d.ts.map +1 -0
- package/dist/cli/commands/vault.js +402 -0
- package/dist/cli/commands/vault.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +3 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +38 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/commands/version.d.ts +3 -0
- package/dist/cli/commands/version.d.ts.map +1 -0
- package/dist/cli/commands/version.js +19 -0
- package/dist/cli/commands/version.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +3 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +179 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +118 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/checkpoint.d.ts +35 -0
- package/dist/core/checkpoint.d.ts.map +1 -0
- package/dist/core/checkpoint.js +80 -0
- package/dist/core/checkpoint.js.map +1 -0
- package/dist/core/event-bus.d.ts +41 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +115 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/lifecycle.d.ts +30 -0
- package/dist/core/lifecycle.d.ts.map +1 -0
- package/dist/core/lifecycle.js +72 -0
- package/dist/core/lifecycle.js.map +1 -0
- package/dist/core/patterns.d.ts +43 -0
- package/dist/core/patterns.d.ts.map +1 -0
- package/dist/core/patterns.js +372 -0
- package/dist/core/patterns.js.map +1 -0
- package/dist/core/permissions.d.ts +40 -0
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/core/permissions.js +113 -0
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/registry.d.ts +80 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +308 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/snapshots.d.ts +20 -0
- package/dist/core/snapshots.d.ts.map +1 -0
- package/dist/core/snapshots.js +73 -0
- package/dist/core/snapshots.js.map +1 -0
- package/dist/core/types.d.ts +168 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +33 -0
- package/dist/core/types.js.map +1 -0
- package/dist/hooks/cli.d.ts +11 -0
- package/dist/hooks/cli.d.ts.map +1 -0
- package/dist/hooks/cli.js +32 -0
- package/dist/hooks/cli.js.map +1 -0
- package/dist/hooks/index.d.ts +23 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +58 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/index.d.ts +105 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +298 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/schema.d.ts +6 -0
- package/dist/memory/schema.d.ts.map +1 -0
- package/dist/memory/schema.js +71 -0
- package/dist/memory/schema.js.map +1 -0
- package/dist/plugin/index.d.ts +6 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +182 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/runtime/acp.d.ts +41 -0
- package/dist/runtime/acp.d.ts.map +1 -0
- package/dist/runtime/acp.js +137 -0
- package/dist/runtime/acp.js.map +1 -0
- package/dist/runtime/adaptive.d.ts +34 -0
- package/dist/runtime/adaptive.d.ts.map +1 -0
- package/dist/runtime/adaptive.js +229 -0
- package/dist/runtime/adaptive.js.map +1 -0
- package/dist/runtime/autonomy.d.ts +21 -0
- package/dist/runtime/autonomy.d.ts.map +1 -0
- package/dist/runtime/autonomy.js +74 -0
- package/dist/runtime/autonomy.js.map +1 -0
- package/dist/runtime/backends/claude.d.ts +9 -0
- package/dist/runtime/backends/claude.d.ts.map +1 -0
- package/dist/runtime/backends/claude.js +134 -0
- package/dist/runtime/backends/claude.js.map +1 -0
- package/dist/runtime/backends/codex.d.ts +9 -0
- package/dist/runtime/backends/codex.d.ts.map +1 -0
- package/dist/runtime/backends/codex.js +132 -0
- package/dist/runtime/backends/codex.js.map +1 -0
- package/dist/runtime/backends/gemini.d.ts +9 -0
- package/dist/runtime/backends/gemini.d.ts.map +1 -0
- package/dist/runtime/backends/gemini.js +103 -0
- package/dist/runtime/backends/gemini.js.map +1 -0
- package/dist/runtime/backends/index.d.ts +17 -0
- package/dist/runtime/backends/index.d.ts.map +1 -0
- package/dist/runtime/backends/index.js +39 -0
- package/dist/runtime/backends/index.js.map +1 -0
- package/dist/runtime/backends/mock.d.ts +21 -0
- package/dist/runtime/backends/mock.d.ts.map +1 -0
- package/dist/runtime/backends/mock.js +46 -0
- package/dist/runtime/backends/mock.js.map +1 -0
- package/dist/runtime/backends/types.d.ts +34 -0
- package/dist/runtime/backends/types.d.ts.map +1 -0
- package/dist/runtime/backends/types.js +2 -0
- package/dist/runtime/backends/types.js.map +1 -0
- package/dist/runtime/backends/vercel-ai.d.ts +14 -0
- package/dist/runtime/backends/vercel-ai.d.ts.map +1 -0
- package/dist/runtime/backends/vercel-ai.js +137 -0
- package/dist/runtime/backends/vercel-ai.js.map +1 -0
- package/dist/runtime/cache-optimizer.d.ts +37 -0
- package/dist/runtime/cache-optimizer.d.ts.map +1 -0
- package/dist/runtime/cache-optimizer.js +54 -0
- package/dist/runtime/cache-optimizer.js.map +1 -0
- package/dist/runtime/cascade.d.ts +26 -0
- package/dist/runtime/cascade.d.ts.map +1 -0
- package/dist/runtime/cascade.js +54 -0
- package/dist/runtime/cascade.js.map +1 -0
- package/dist/runtime/chunker.d.ts +36 -0
- package/dist/runtime/chunker.d.ts.map +1 -0
- package/dist/runtime/chunker.js +210 -0
- package/dist/runtime/chunker.js.map +1 -0
- package/dist/runtime/compaction.d.ts +22 -0
- package/dist/runtime/compaction.d.ts.map +1 -0
- package/dist/runtime/compaction.js +36 -0
- package/dist/runtime/compaction.js.map +1 -0
- package/dist/runtime/compounder.d.ts +66 -0
- package/dist/runtime/compounder.d.ts.map +1 -0
- package/dist/runtime/compounder.js +276 -0
- package/dist/runtime/compounder.js.map +1 -0
- package/dist/runtime/cost-model.d.ts +24 -0
- package/dist/runtime/cost-model.d.ts.map +1 -0
- package/dist/runtime/cost-model.js +120 -0
- package/dist/runtime/cost-model.js.map +1 -0
- package/dist/runtime/distiller.d.ts +21 -0
- package/dist/runtime/distiller.d.ts.map +1 -0
- package/dist/runtime/distiller.js +70 -0
- package/dist/runtime/distiller.js.map +1 -0
- package/dist/runtime/engine.d.ts +123 -0
- package/dist/runtime/engine.d.ts.map +1 -0
- package/dist/runtime/engine.js +969 -0
- package/dist/runtime/engine.js.map +1 -0
- package/dist/runtime/executor.d.ts +71 -0
- package/dist/runtime/executor.d.ts.map +1 -0
- package/dist/runtime/executor.js +283 -0
- package/dist/runtime/executor.js.map +1 -0
- package/dist/runtime/heuristics.d.ts +33 -0
- package/dist/runtime/heuristics.d.ts.map +1 -0
- package/dist/runtime/heuristics.js +188 -0
- package/dist/runtime/heuristics.js.map +1 -0
- package/dist/runtime/living-spec.d.ts +34 -0
- package/dist/runtime/living-spec.d.ts.map +1 -0
- package/dist/runtime/living-spec.js +91 -0
- package/dist/runtime/living-spec.js.map +1 -0
- package/dist/runtime/lsp.d.ts +50 -0
- package/dist/runtime/lsp.d.ts.map +1 -0
- package/dist/runtime/lsp.js +110 -0
- package/dist/runtime/lsp.js.map +1 -0
- package/dist/runtime/mcp.d.ts +27 -0
- package/dist/runtime/mcp.d.ts.map +1 -0
- package/dist/runtime/mcp.js +154 -0
- package/dist/runtime/mcp.js.map +1 -0
- package/dist/runtime/model-router.d.ts +22 -0
- package/dist/runtime/model-router.d.ts.map +1 -0
- package/dist/runtime/model-router.js +94 -0
- package/dist/runtime/model-router.js.map +1 -0
- package/dist/runtime/panes.d.ts +76 -0
- package/dist/runtime/panes.d.ts.map +1 -0
- package/dist/runtime/panes.js +279 -0
- package/dist/runtime/panes.js.map +1 -0
- package/dist/runtime/plan-search.d.ts +41 -0
- package/dist/runtime/plan-search.d.ts.map +1 -0
- package/dist/runtime/plan-search.js +140 -0
- package/dist/runtime/plan-search.js.map +1 -0
- package/dist/runtime/plugins.d.ts +59 -0
- package/dist/runtime/plugins.d.ts.map +1 -0
- package/dist/runtime/plugins.js +121 -0
- package/dist/runtime/plugins.js.map +1 -0
- package/dist/runtime/reflexion.d.ts +22 -0
- package/dist/runtime/reflexion.d.ts.map +1 -0
- package/dist/runtime/reflexion.js +85 -0
- package/dist/runtime/reflexion.js.map +1 -0
- package/dist/runtime/review-schema.d.ts +75 -0
- package/dist/runtime/review-schema.d.ts.map +1 -0
- package/dist/runtime/review-schema.js +223 -0
- package/dist/runtime/review-schema.js.map +1 -0
- package/dist/runtime/rewriter.d.ts +8 -0
- package/dist/runtime/rewriter.d.ts.map +1 -0
- package/dist/runtime/rewriter.js +81 -0
- package/dist/runtime/rewriter.js.map +1 -0
- package/dist/runtime/sharing.d.ts +15 -0
- package/dist/runtime/sharing.d.ts.map +1 -0
- package/dist/runtime/sharing.js +48 -0
- package/dist/runtime/sharing.js.map +1 -0
- package/dist/runtime/stats.d.ts +53 -0
- package/dist/runtime/stats.d.ts.map +1 -0
- package/dist/runtime/stats.js +160 -0
- package/dist/runtime/stats.js.map +1 -0
- package/dist/runtime/templates.d.ts +77 -0
- package/dist/runtime/templates.d.ts.map +1 -0
- package/dist/runtime/templates.js +221 -0
- package/dist/runtime/templates.js.map +1 -0
- package/dist/runtime/traces.d.ts +60 -0
- package/dist/runtime/traces.d.ts.map +1 -0
- package/dist/runtime/traces.js +166 -0
- package/dist/runtime/traces.js.map +1 -0
- package/dist/runtime/verifier.d.ts +54 -0
- package/dist/runtime/verifier.d.ts.map +1 -0
- package/dist/runtime/verifier.js +172 -0
- package/dist/runtime/verifier.js.map +1 -0
- package/dist/runtime/worktree.d.ts +24 -0
- package/dist/runtime/worktree.d.ts.map +1 -0
- package/dist/runtime/worktree.js +82 -0
- package/dist/runtime/worktree.js.map +1 -0
- package/dist/tui/dashboard.d.ts +65 -0
- package/dist/tui/dashboard.d.ts.map +1 -0
- package/dist/tui/dashboard.js +496 -0
- package/dist/tui/dashboard.js.map +1 -0
- package/dist/tui/progress.d.ts +32 -0
- package/dist/tui/progress.d.ts.map +1 -0
- package/dist/tui/progress.js +257 -0
- package/dist/tui/progress.js.map +1 -0
- package/dist/tui/renderer.d.ts +72 -0
- package/dist/tui/renderer.d.ts.map +1 -0
- package/dist/tui/renderer.js +205 -0
- package/dist/tui/renderer.js.map +1 -0
- package/dist/utils/compact-format.d.ts +35 -0
- package/dist/utils/compact-format.d.ts.map +1 -0
- package/dist/utils/compact-format.js +106 -0
- package/dist/utils/compact-format.js.map +1 -0
- package/dist/utils/config.d.ts +73 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +70 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/env.d.ts +6 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +28 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/errors.d.ts +14 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +120 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +56 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/output.d.ts +10 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +26 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/paths.d.ts +7 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +16 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/project-config.d.ts +18 -0
- package/dist/utils/project-config.d.ts.map +1 -0
- package/dist/utils/project-config.js +46 -0
- package/dist/utils/project-config.js.map +1 -0
- package/dist/utils/redact.d.ts +5 -0
- package/dist/utils/redact.d.ts.map +1 -0
- package/dist/utils/redact.js +25 -0
- package/dist/utils/redact.js.map +1 -0
- package/dist/utils/schemas.d.ts +109 -0
- package/dist/utils/schemas.d.ts.map +1 -0
- package/dist/utils/schemas.js +63 -0
- package/dist/utils/schemas.js.map +1 -0
- package/dist/utils/terminal.d.ts +33 -0
- package/dist/utils/terminal.d.ts.map +1 -0
- package/dist/utils/terminal.js +82 -0
- package/dist/utils/terminal.js.map +1 -0
- package/dist/utils/tokens.d.ts +9 -0
- package/dist/utils/tokens.d.ts.map +1 -0
- package/dist/utils/tokens.js +11 -0
- package/dist/utils/tokens.js.map +1 -0
- package/package.json +71 -0
- package/skills/design-system/SKILL.md +195 -0
- package/skills/orchestration-patterns/SKILL.md +81 -0
- package/skills/orchestration-patterns/examples/example-workflows.md +290 -0
- package/skills/orchestration-patterns/references/adversarial.md +51 -0
- package/skills/orchestration-patterns/references/batch.md +63 -0
- package/skills/orchestration-patterns/references/chaos.md +43 -0
- package/skills/orchestration-patterns/references/competitive.md +84 -0
- package/skills/orchestration-patterns/references/debate.md +44 -0
- package/skills/orchestration-patterns/references/emergence.md +50 -0
- package/skills/orchestration-patterns/references/fan-out-fan-in.md +74 -0
- package/skills/orchestration-patterns/references/iterative.md +99 -0
- package/skills/orchestration-patterns/references/pipeline.md +72 -0
- package/skills/swarm-output-style/SKILL.md +85 -0
- package/templates/add-endpoint.yml +44 -0
- package/templates/add-feature.yml +30 -0
- package/templates/agent-template.md +73 -0
- package/templates/bug-fix.yml +31 -0
- package/templates/explore.yml +21 -0
- package/templates/fix-pr.yml +30 -0
- package/templates/migration.yml +36 -0
- package/templates/refactor.yml +35 -0
- package/templates/security-audit.yml +27 -0
- package/templates/workflow-template.md +63 -0
|
@@ -0,0 +1,969 @@
|
|
|
1
|
+
import { emptyUsageStats, mergeUsageStats, generateId, } from '../core/types.js';
|
|
2
|
+
import { AgentExecutor } from './executor.js';
|
|
3
|
+
import { saveCheckpoint, loadCheckpoint } from '../core/checkpoint.js';
|
|
4
|
+
import { PatternRegistry } from '../core/patterns.js';
|
|
5
|
+
import { WorktreeManager } from './worktree.js';
|
|
6
|
+
import { CompactionManager } from './compaction.js';
|
|
7
|
+
import { ContextDistiller } from './distiller.js';
|
|
8
|
+
import { Verifier } from './verifier.js';
|
|
9
|
+
import { rewritePlan } from './rewriter.js';
|
|
10
|
+
import { CostModel } from './cost-model.js';
|
|
11
|
+
import { PlanSearcher } from './plan-search.js';
|
|
12
|
+
import { StatsCollector } from './stats.js';
|
|
13
|
+
import { AdaptiveReplanner } from './adaptive.js';
|
|
14
|
+
import { ReflectionEngine } from './reflexion.js';
|
|
15
|
+
import { TraceStore } from './traces.js';
|
|
16
|
+
import { HeuristicStore } from './heuristics.js';
|
|
17
|
+
import { KnowledgeCompounder } from './compounder.js';
|
|
18
|
+
import { ModelRouter } from './model-router.js';
|
|
19
|
+
import { AutonomyTracker } from './autonomy.js';
|
|
20
|
+
import { ContinuousVerifier } from './verifier.js';
|
|
21
|
+
import { optimizeForCache, estimateCacheSavings } from './cache-optimizer.js';
|
|
22
|
+
import { classifyTaskComplexity, selectStartingTier } from './cascade.js';
|
|
23
|
+
import { createLivingSpec, updateSpecFromPhase, formatLivingSpec } from './living-spec.js';
|
|
24
|
+
import { estimateTokens } from '../utils/tokens.js';
|
|
25
|
+
import { redactSecrets } from '../utils/redact.js';
|
|
26
|
+
import { join } from 'path';
|
|
27
|
+
import { existsSync, mkdirSync, writeFileSync, unlinkSync } from 'fs';
|
|
28
|
+
import { randomUUID } from 'crypto';
|
|
29
|
+
/**
|
|
30
|
+
* The Swarm Orchestration Engine.
|
|
31
|
+
*
|
|
32
|
+
* Manages the lifecycle of orchestrations — multi-phase workflows
|
|
33
|
+
* that dispatch parallel agents and manage dependencies between phases.
|
|
34
|
+
*/
|
|
35
|
+
export class SwarmEngine {
|
|
36
|
+
registry;
|
|
37
|
+
bus;
|
|
38
|
+
executor;
|
|
39
|
+
log;
|
|
40
|
+
patterns;
|
|
41
|
+
options;
|
|
42
|
+
orchestrations = new Map();
|
|
43
|
+
phaseOutputs = new Map();
|
|
44
|
+
abortController = new AbortController();
|
|
45
|
+
worktreeManager = null;
|
|
46
|
+
compaction;
|
|
47
|
+
distiller = null;
|
|
48
|
+
verifier;
|
|
49
|
+
costModel;
|
|
50
|
+
replanner;
|
|
51
|
+
reflectionEngine;
|
|
52
|
+
reflections = [];
|
|
53
|
+
traceStore = null;
|
|
54
|
+
heuristicStore = null;
|
|
55
|
+
stats;
|
|
56
|
+
modelRouter = null;
|
|
57
|
+
autonomyTracker = null;
|
|
58
|
+
compounder = null;
|
|
59
|
+
signalHandlers = [];
|
|
60
|
+
sharedContextFiles = [];
|
|
61
|
+
livingSpec = null;
|
|
62
|
+
constructor(options) {
|
|
63
|
+
this.options = options;
|
|
64
|
+
this.registry = options.registry;
|
|
65
|
+
this.bus = options.bus;
|
|
66
|
+
this.executor = new AgentExecutor({
|
|
67
|
+
bus: this.bus,
|
|
68
|
+
logger: options.logger,
|
|
69
|
+
cwd: options.cwd,
|
|
70
|
+
mock: options.mock ?? false,
|
|
71
|
+
permissionMode: options.permissionMode ?? 'default',
|
|
72
|
+
defaultBackend: options.defaultBackend,
|
|
73
|
+
permissionProfile: options.permissionProfile,
|
|
74
|
+
snapshotsEnabled: options.snapshotsEnabled,
|
|
75
|
+
doomLoopDetection: options.doomLoopDetection,
|
|
76
|
+
paneManager: options.paneManager,
|
|
77
|
+
});
|
|
78
|
+
this.log = options.logger.child('engine');
|
|
79
|
+
this.patterns = options.patterns ?? new PatternRegistry();
|
|
80
|
+
this.compaction = new CompactionManager(options.compactionEnabled === false ? { enabled: false } : undefined);
|
|
81
|
+
this.distiller = options.distillation !== false ? new ContextDistiller() : null;
|
|
82
|
+
this.verifier = new Verifier(options.cwd ?? process.cwd());
|
|
83
|
+
this.stats = new StatsCollector();
|
|
84
|
+
this.stats.attachToEventBus(this.bus, undefined);
|
|
85
|
+
this.costModel = new CostModel(this.stats);
|
|
86
|
+
this.reflectionEngine = new ReflectionEngine();
|
|
87
|
+
try {
|
|
88
|
+
this.traceStore = new TraceStore();
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
this.log.warn('TraceStore init failed', { error: e instanceof Error ? e.message : e });
|
|
92
|
+
}
|
|
93
|
+
this.replanner = new AdaptiveReplanner(options.logger, this.traceStore ?? undefined);
|
|
94
|
+
try {
|
|
95
|
+
this.heuristicStore = new HeuristicStore();
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
this.log.warn('HeuristicStore init failed', { error: e instanceof Error ? e.message : e });
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
this.modelRouter = new ModelRouter();
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
this.log.warn('ModelRouter init failed', { error: e instanceof Error ? e.message : e });
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
this.autonomyTracker = new AutonomyTracker();
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
this.log.warn('AutonomyTracker init failed', { error: e instanceof Error ? e.message : e });
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
this.compounder = new KnowledgeCompounder();
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
this.log.warn('KnowledgeCompounder init failed', { error: e instanceof Error ? e.message : e });
|
|
117
|
+
}
|
|
118
|
+
if (options.worktreeEnabled) {
|
|
119
|
+
try {
|
|
120
|
+
this.worktreeManager = new WorktreeManager(options.cwd);
|
|
121
|
+
}
|
|
122
|
+
catch { /* not a git repo */ }
|
|
123
|
+
}
|
|
124
|
+
if (options.pluginLoader) {
|
|
125
|
+
options.pluginLoader.wireHooks(this.bus);
|
|
126
|
+
}
|
|
127
|
+
this.setupSignalHandlers();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Execute an orchestration from config.
|
|
131
|
+
*/
|
|
132
|
+
async execute(config) {
|
|
133
|
+
// Re-use existing instance if present (e.g., when resuming from checkpoint)
|
|
134
|
+
const orchestration = this.orchestrations.get(config.id) ?? this.createInstance(config);
|
|
135
|
+
this.orchestrations.set(config.id, orchestration);
|
|
136
|
+
this.log.info(`Starting orchestration: ${config.name}`, {
|
|
137
|
+
pattern: config.pattern,
|
|
138
|
+
phases: config.phases.length,
|
|
139
|
+
});
|
|
140
|
+
this.bus.emit('orchestration:start', {
|
|
141
|
+
id: config.id,
|
|
142
|
+
name: config.name,
|
|
143
|
+
pattern: config.pattern,
|
|
144
|
+
phaseCount: config.phases.length,
|
|
145
|
+
}, 'engine');
|
|
146
|
+
orchestration.status = 'running';
|
|
147
|
+
orchestration.startedAt = new Date();
|
|
148
|
+
this.phaseOutputs = new Map();
|
|
149
|
+
this.reflections = [];
|
|
150
|
+
this.sharedContextFiles = [];
|
|
151
|
+
this.abortController = new AbortController();
|
|
152
|
+
this.livingSpec = createLivingSpec(config.description);
|
|
153
|
+
// Start continuous verification
|
|
154
|
+
let continuousVerifier = null;
|
|
155
|
+
if (this.verifier) {
|
|
156
|
+
continuousVerifier = new ContinuousVerifier(this.verifier, this.bus);
|
|
157
|
+
continuousVerifier.start();
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
// Execute phases using work-queue approach (handles DAGs correctly)
|
|
161
|
+
// If resuming, seed completed phases so the DAG executor skips them
|
|
162
|
+
const completed = new Set();
|
|
163
|
+
for (const phase of orchestration.phases) {
|
|
164
|
+
if (phase.status === 'completed') {
|
|
165
|
+
completed.add(phase.config.name);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
while (completed.size < orchestration.phases.length) {
|
|
169
|
+
// Check if abort was requested
|
|
170
|
+
if (this.abortController.signal.aborted) {
|
|
171
|
+
orchestration.status = 'cancelled';
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
// Find all runnable phases (dependencies met, not yet completed)
|
|
175
|
+
const runnable = orchestration.phases.filter(p => {
|
|
176
|
+
if (completed.has(p.config.name))
|
|
177
|
+
return false;
|
|
178
|
+
if (p.status === 'failed')
|
|
179
|
+
return false;
|
|
180
|
+
const deps = p.config.dependsOn ?? [];
|
|
181
|
+
return deps.every(d => completed.has(d));
|
|
182
|
+
});
|
|
183
|
+
if (runnable.length === 0) {
|
|
184
|
+
// No runnable phases — either all done or deadlocked
|
|
185
|
+
const incomplete = orchestration.phases.filter(p => !completed.has(p.config.name) && p.status !== 'failed');
|
|
186
|
+
if (incomplete.length > 0) {
|
|
187
|
+
// Deadlock — blocked phases with unmet dependencies
|
|
188
|
+
for (const p of incomplete) {
|
|
189
|
+
p.status = 'blocked';
|
|
190
|
+
}
|
|
191
|
+
orchestration.status = 'failed';
|
|
192
|
+
this.log.error('Orchestration deadlocked: blocked phases with unmet dependencies', {
|
|
193
|
+
blocked: incomplete.map(p => p.config.name),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
// Execute runnable phases (could be parallel if multiple are ready)
|
|
199
|
+
for (const phase of runnable) {
|
|
200
|
+
orchestration.currentPhase = orchestration.phases.indexOf(phase);
|
|
201
|
+
// Check for approval gate
|
|
202
|
+
if (phase.config.requiresApproval) {
|
|
203
|
+
this.log.info(`Phase requires approval: ${phase.config.name}`);
|
|
204
|
+
this.bus.emit('orchestration:paused', {
|
|
205
|
+
orchestrationId: orchestration.config.id,
|
|
206
|
+
phase: phase.config.name,
|
|
207
|
+
agentCount: phase.config.agents.length,
|
|
208
|
+
}, 'engine');
|
|
209
|
+
// In interactive mode, wait for approval
|
|
210
|
+
if (!this.options.nonInteractive) {
|
|
211
|
+
const approved = await this.requestApproval(phase.config.name, phase.config.agents);
|
|
212
|
+
if (!approved) {
|
|
213
|
+
phase.status = 'skipped';
|
|
214
|
+
this.log.info(`Phase skipped (not approved): ${phase.config.name}`);
|
|
215
|
+
completed.add(phase.config.name);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const outputs = await this.executePhase(orchestration, phase);
|
|
221
|
+
this.phaseOutputs.set(phase.config.name, outputs);
|
|
222
|
+
if (phase.status === 'completed') {
|
|
223
|
+
completed.add(phase.config.name);
|
|
224
|
+
}
|
|
225
|
+
else if (phase.status === 'failed') {
|
|
226
|
+
orchestration.status = 'failed';
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (orchestration.status === 'failed')
|
|
231
|
+
break;
|
|
232
|
+
// Check cost budget after each batch of phases
|
|
233
|
+
if (orchestration.config.costBudget) {
|
|
234
|
+
const spent = orchestration.phases
|
|
235
|
+
.filter(p => p.status === 'completed')
|
|
236
|
+
.reduce((sum, p) => sum + p.usage.costUsd, 0);
|
|
237
|
+
if (spent >= orchestration.config.costBudget) {
|
|
238
|
+
orchestration.status = 'failed';
|
|
239
|
+
this.log.warn(`Cost budget exceeded: $${spent.toFixed(2)} >= $${orchestration.config.costBudget}`, {
|
|
240
|
+
spent, budget: orchestration.config.costBudget,
|
|
241
|
+
});
|
|
242
|
+
this.bus.emit('system:warning', {
|
|
243
|
+
reason: 'budget-exceeded',
|
|
244
|
+
id: orchestration.config.id,
|
|
245
|
+
spent,
|
|
246
|
+
budget: orchestration.config.costBudget,
|
|
247
|
+
}, 'engine');
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (orchestration.status !== 'failed') {
|
|
253
|
+
orchestration.status = 'completed';
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
orchestration.status = 'failed';
|
|
258
|
+
this.log.error(`Orchestration failed: ${config.name}`, {
|
|
259
|
+
error: error instanceof Error ? error.message : String(error),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
orchestration.completedAt = new Date();
|
|
263
|
+
continuousVerifier?.stop();
|
|
264
|
+
// Clean up shared context files
|
|
265
|
+
for (const f of this.sharedContextFiles) {
|
|
266
|
+
try {
|
|
267
|
+
unlinkSync(f);
|
|
268
|
+
}
|
|
269
|
+
catch { }
|
|
270
|
+
}
|
|
271
|
+
this.sharedContextFiles = [];
|
|
272
|
+
// Build phase data once for all consumers (avoids 4x redundant mapping)
|
|
273
|
+
const phaseData = orchestration.phases.map(p => ({
|
|
274
|
+
name: p.config.name,
|
|
275
|
+
status: p.status,
|
|
276
|
+
agentCount: p.agents.length,
|
|
277
|
+
tokens: p.usage.totalTokens,
|
|
278
|
+
durationMs: p.usage.durationMs,
|
|
279
|
+
confidence: p.agents[0]?.result?.confidence ?? 'unknown',
|
|
280
|
+
}));
|
|
281
|
+
const traceData = {
|
|
282
|
+
orchestrationId: config.id,
|
|
283
|
+
task: config.description,
|
|
284
|
+
pattern: config.pattern,
|
|
285
|
+
status: orchestration.status,
|
|
286
|
+
totalTokens: orchestration.usage.totalTokens,
|
|
287
|
+
costUsd: orchestration.usage.costUsd,
|
|
288
|
+
durationMs: orchestration.usage.durationMs,
|
|
289
|
+
phaseCount: orchestration.phases.length,
|
|
290
|
+
agentCount: orchestration.phases.reduce((n, p) => n + p.agents.length, 0),
|
|
291
|
+
phases: phaseData,
|
|
292
|
+
reflections: this.reflections.map(r => redactSecrets(r)),
|
|
293
|
+
};
|
|
294
|
+
// Store execution trace for learning
|
|
295
|
+
if (this.traceStore) {
|
|
296
|
+
try {
|
|
297
|
+
this.traceStore.store(traceData);
|
|
298
|
+
}
|
|
299
|
+
catch { /* Don't fail orchestration if trace storage fails */ }
|
|
300
|
+
// Extract heuristics from the trace for future learning
|
|
301
|
+
if (this.heuristicStore) {
|
|
302
|
+
try {
|
|
303
|
+
this.heuristicStore.extractFromTrace(traceData);
|
|
304
|
+
}
|
|
305
|
+
catch { /* Don't fail orchestration if heuristic extraction fails */ }
|
|
306
|
+
}
|
|
307
|
+
// Auto-compound knowledge from completed orchestrations
|
|
308
|
+
if (orchestration.status === 'completed' && this.compounder) {
|
|
309
|
+
try {
|
|
310
|
+
const doc = this.compounder.extractFromTrace({ ...traceData, status: 'completed' }, this.reflections);
|
|
311
|
+
if (doc)
|
|
312
|
+
this.compounder.store(doc);
|
|
313
|
+
}
|
|
314
|
+
catch { /* Don't fail orchestration if compounding fails */ }
|
|
315
|
+
}
|
|
316
|
+
// Auto-capture outcomes to memory
|
|
317
|
+
try {
|
|
318
|
+
const { traceToMemoryEntries } = await import('./traces.js');
|
|
319
|
+
const memEntries = traceToMemoryEntries(traceData);
|
|
320
|
+
// Store in memory if SwarmMemory is available
|
|
321
|
+
let memory;
|
|
322
|
+
try {
|
|
323
|
+
const { SwarmMemory } = await import('../memory/index.js');
|
|
324
|
+
memory = new SwarmMemory();
|
|
325
|
+
for (const entry of memEntries) {
|
|
326
|
+
memory.store({ type: entry.type, title: entry.title, content: entry.content });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch { /* Memory not available */ }
|
|
330
|
+
finally {
|
|
331
|
+
try {
|
|
332
|
+
memory?.close();
|
|
333
|
+
}
|
|
334
|
+
catch { }
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch { /* Don't fail orchestration */ }
|
|
338
|
+
}
|
|
339
|
+
// Record model outcomes for routing
|
|
340
|
+
if (this.modelRouter) {
|
|
341
|
+
for (const phase of orchestration.phases) {
|
|
342
|
+
for (const agent of phase.agents) {
|
|
343
|
+
const model = agent.config.model;
|
|
344
|
+
if (!model)
|
|
345
|
+
continue;
|
|
346
|
+
const success = agent.status === 'shutdown' || agent.status === 'idle';
|
|
347
|
+
this.modelRouter.record(model, agent.config.name, success, success ? 1.0 : 0.0);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// Record autonomy outcome
|
|
352
|
+
if (this.autonomyTracker) {
|
|
353
|
+
const success = orchestration.status === 'completed';
|
|
354
|
+
const intervention = orchestration.phases.some(p => p.config.requiresApproval);
|
|
355
|
+
this.autonomyTracker.record(config.pattern, success, intervention);
|
|
356
|
+
}
|
|
357
|
+
// Compute total usage
|
|
358
|
+
orchestration.usage = orchestration.phases.reduce((total, phase) => mergeUsageStats(total, phase.usage), emptyUsageStats());
|
|
359
|
+
this.bus.emit(orchestration.status === 'completed' ? 'orchestration:complete' : 'orchestration:failed', {
|
|
360
|
+
id: config.id,
|
|
361
|
+
name: config.name,
|
|
362
|
+
status: orchestration.status,
|
|
363
|
+
usage: orchestration.usage,
|
|
364
|
+
durationMs: orchestration.completedAt.getTime() - (orchestration.startedAt?.getTime() ?? 0),
|
|
365
|
+
}, 'engine');
|
|
366
|
+
this.log.info(`Orchestration ${orchestration.status}: ${config.name}`, {
|
|
367
|
+
usage: orchestration.usage,
|
|
368
|
+
});
|
|
369
|
+
// Prune terminal agent instances to free memory
|
|
370
|
+
this.registry.pruneTerminalInstances();
|
|
371
|
+
return orchestration;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Execute a named pattern with a task description.
|
|
375
|
+
*/
|
|
376
|
+
async runPattern(patternName, task, options) {
|
|
377
|
+
const pattern = this.patterns.get(patternName);
|
|
378
|
+
if (!pattern) {
|
|
379
|
+
throw new Error(`Unknown pattern: ${patternName}. Available: ${this.patterns.list().map(p => p.name).join(', ')}`);
|
|
380
|
+
}
|
|
381
|
+
// Inject task into all agent prompts
|
|
382
|
+
const config = {
|
|
383
|
+
id: generateId(),
|
|
384
|
+
name: `${patternName}: ${task.slice(0, 50)}`,
|
|
385
|
+
description: task,
|
|
386
|
+
pattern: patternName,
|
|
387
|
+
phases: pattern.phases.map(phase => ({
|
|
388
|
+
...phase,
|
|
389
|
+
agents: phase.agents.map(a => ({
|
|
390
|
+
...a,
|
|
391
|
+
prompt: `${task}\n\n${a.prompt ?? ''}`.trim(),
|
|
392
|
+
})),
|
|
393
|
+
})),
|
|
394
|
+
costBudget: options?.costBudget,
|
|
395
|
+
};
|
|
396
|
+
// Substitute pattern parameters into agent prompts
|
|
397
|
+
if (options?.params && pattern.parameters) {
|
|
398
|
+
for (const phase of config.phases) {
|
|
399
|
+
for (const agent of phase.agents) {
|
|
400
|
+
if (agent.prompt) {
|
|
401
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
402
|
+
// Use string replacement instead of regex to avoid ReDoS from user-controlled keys
|
|
403
|
+
const placeholder = '${' + key + '}';
|
|
404
|
+
agent.prompt = agent.prompt.split(placeholder).join(String(value));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return this.execute(config);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Generate a serializable execution plan WITHOUT executing anything.
|
|
414
|
+
* Useful for previewing what an orchestration will do before running it.
|
|
415
|
+
*/
|
|
416
|
+
plan(patternName, task, options) {
|
|
417
|
+
const pattern = this.patterns.get(patternName);
|
|
418
|
+
if (!pattern) {
|
|
419
|
+
throw new Error(`Unknown pattern: ${patternName}. Available: ${this.patterns.list().map(p => p.name).join(', ')}`);
|
|
420
|
+
}
|
|
421
|
+
// Check for similar past traces to inform planning
|
|
422
|
+
if (this.traceStore) {
|
|
423
|
+
try {
|
|
424
|
+
const similar = this.traceStore.findSimilar(task, 3);
|
|
425
|
+
if (similar.length > 0) {
|
|
426
|
+
let traceContext = '';
|
|
427
|
+
const best = similar.find(t => t.status === 'completed');
|
|
428
|
+
if (best) {
|
|
429
|
+
traceContext = `Past successful orchestration for similar task used pattern "${best.pattern}" with ${best.agentCount} agents (${best.totalTokens} tokens, ${Math.round(best.durationMs / 1000)}s).`;
|
|
430
|
+
}
|
|
431
|
+
const failed = similar.find(t => t.status === 'failed');
|
|
432
|
+
if (failed && failed.reflections.length > 0) {
|
|
433
|
+
traceContext += ` Previous attempt failed. Lessons: ${failed.reflections[0]?.slice(0, 200)}`;
|
|
434
|
+
}
|
|
435
|
+
if (traceContext) {
|
|
436
|
+
this.log.info('Found similar past traces', { context: traceContext.slice(0, 200) });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch { /* Don't fail planning if trace lookup fails */ }
|
|
441
|
+
}
|
|
442
|
+
const id = generateId();
|
|
443
|
+
const phases = pattern.phases.map(phase => ({
|
|
444
|
+
name: phase.name,
|
|
445
|
+
parallel: phase.parallel,
|
|
446
|
+
dependsOn: phase.dependsOn,
|
|
447
|
+
requiresApproval: phase.requiresApproval,
|
|
448
|
+
tokenBudget: phase.tokenBudget,
|
|
449
|
+
agents: phase.agents.map(a => {
|
|
450
|
+
const agentConfig = this.registry.getConfig(a.type);
|
|
451
|
+
return {
|
|
452
|
+
name: a.name,
|
|
453
|
+
type: a.type,
|
|
454
|
+
model: a.model ?? agentConfig?.model ?? 'sonnet',
|
|
455
|
+
backend: a.backend ?? agentConfig?.backend,
|
|
456
|
+
prompt: `${task}\n\n${a.prompt ?? agentConfig?.description ?? ''}`.trim(),
|
|
457
|
+
permissionProfile: agentConfig?.permissionProfile,
|
|
458
|
+
worktree: a.worktree,
|
|
459
|
+
};
|
|
460
|
+
}),
|
|
461
|
+
}));
|
|
462
|
+
// Substitute pattern parameters if provided
|
|
463
|
+
if (options?.params && pattern.parameters) {
|
|
464
|
+
for (const phase of phases) {
|
|
465
|
+
for (const agent of phase.agents) {
|
|
466
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
467
|
+
const placeholder = '${' + key + '}';
|
|
468
|
+
agent.prompt = agent.prompt.split(placeholder).join(String(value));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const planResult = {
|
|
474
|
+
id,
|
|
475
|
+
name: `${patternName}: ${task.slice(0, 50)}`,
|
|
476
|
+
task,
|
|
477
|
+
pattern: patternName,
|
|
478
|
+
phases,
|
|
479
|
+
costBudget: options?.costBudget,
|
|
480
|
+
createdAt: new Date().toISOString(),
|
|
481
|
+
};
|
|
482
|
+
// Apply plan rewriting rules
|
|
483
|
+
const { plan: rewrittenPlan, rewrites } = rewritePlan(planResult);
|
|
484
|
+
if (rewrites.length > 0) {
|
|
485
|
+
this.log.info('Plan rewritten', { rewrites });
|
|
486
|
+
}
|
|
487
|
+
// Retrieve relevant heuristics and inject into first phase
|
|
488
|
+
if (this.heuristicStore) {
|
|
489
|
+
try {
|
|
490
|
+
const heuristics = this.heuristicStore.retrieve(task);
|
|
491
|
+
if (heuristics.length > 0) {
|
|
492
|
+
const context = this.heuristicStore.formatForContext(heuristics);
|
|
493
|
+
for (const agent of rewrittenPlan.phases[0]?.agents ?? []) {
|
|
494
|
+
agent.prompt = `${context}\n\n${agent.prompt}`;
|
|
495
|
+
}
|
|
496
|
+
this.log.info('Injected heuristics into plan', { count: heuristics.length });
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
catch { /* Don't fail planning if heuristic retrieval fails */ }
|
|
500
|
+
}
|
|
501
|
+
// Inject relevant past solutions from knowledge compounder (singleton)
|
|
502
|
+
if (this.compounder) {
|
|
503
|
+
try {
|
|
504
|
+
const solutions = this.compounder.findRelevant(task, 3);
|
|
505
|
+
if (solutions.length > 0) {
|
|
506
|
+
const solutionContext = this.compounder.formatForContext(solutions);
|
|
507
|
+
for (const agent of rewrittenPlan.phases[0]?.agents ?? []) {
|
|
508
|
+
agent.prompt = `${solutionContext}\n\n${agent.prompt}`;
|
|
509
|
+
}
|
|
510
|
+
this.log.info('Injected past solutions into plan', { count: solutions.length });
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
catch { /* Don't fail planning if solution retrieval fails */ }
|
|
514
|
+
}
|
|
515
|
+
// Search over candidate plans to find the best one
|
|
516
|
+
const searcher = new PlanSearcher(this.costModel, this.traceStore ?? undefined);
|
|
517
|
+
const candidates = searcher.search(rewrittenPlan, {
|
|
518
|
+
maxCandidates: 3,
|
|
519
|
+
constraints: options?.costBudget ? { maxCostUsd: options.costBudget } : undefined,
|
|
520
|
+
});
|
|
521
|
+
// Use the best candidate
|
|
522
|
+
const bestPlan = candidates[0]?.plan ?? rewrittenPlan;
|
|
523
|
+
// Store search metadata
|
|
524
|
+
bestPlan.estimates = this.costModel.estimate(bestPlan);
|
|
525
|
+
bestPlan.rewrites = rewrites;
|
|
526
|
+
if (candidates.length > 1) {
|
|
527
|
+
bestPlan._alternatives = candidates.slice(1).map(c => ({
|
|
528
|
+
source: c.source,
|
|
529
|
+
costUsd: c.cost.costUsd,
|
|
530
|
+
quality: c.cost.qualityScore,
|
|
531
|
+
}));
|
|
532
|
+
}
|
|
533
|
+
return bestPlan;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Execute a single phase.
|
|
537
|
+
*/
|
|
538
|
+
async executePhase(orchestration, phase) {
|
|
539
|
+
phase.status = 'running';
|
|
540
|
+
phase.startedAt = new Date();
|
|
541
|
+
// Evaluate agent dropout before executing
|
|
542
|
+
if (phase.config.agents.length > 1) {
|
|
543
|
+
const dropout = this.replanner.evaluateDropout(orchestration, phase.config);
|
|
544
|
+
if (dropout) {
|
|
545
|
+
this.log.info(`Agent dropout: ${dropout.reason}`);
|
|
546
|
+
phase.config.agents = phase.config.agents.filter(a => dropout.agentsToKeep.includes(a.name));
|
|
547
|
+
this.bus.emit('system:warning', { type: 'agent-dropout', ...dropout }, 'adaptive');
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const maxRetries = phase.config.maxRetries ?? 2;
|
|
551
|
+
this.log.info(`Phase started: ${phase.config.name}`, {
|
|
552
|
+
agents: phase.config.agents.length,
|
|
553
|
+
parallel: phase.config.parallel,
|
|
554
|
+
});
|
|
555
|
+
this.bus.emit('orchestration:phase-start', {
|
|
556
|
+
orchestrationId: orchestration.config.id,
|
|
557
|
+
phase: phase.config.name,
|
|
558
|
+
agentCount: phase.config.agents.length,
|
|
559
|
+
parallel: phase.config.parallel,
|
|
560
|
+
}, 'engine');
|
|
561
|
+
try {
|
|
562
|
+
if (phase.config.parallel) {
|
|
563
|
+
// Run all agents in parallel (with retry)
|
|
564
|
+
const results = await Promise.allSettled(phase.config.agents.map(assignment => this.executeAgentWithRetry(assignment, phase, orchestration, maxRetries)));
|
|
565
|
+
// Log cache savings estimate for parallel phases
|
|
566
|
+
if (phase.config.agents.length > 1) {
|
|
567
|
+
const phaseContext = this.compaction.buildContextPrefix(this.phaseOutputs);
|
|
568
|
+
const sharedTokens = estimateTokens(`## Orchestration: ${orchestration.config.name}\nPattern: ${orchestration.config.pattern}\n\n${phaseContext}`);
|
|
569
|
+
if (sharedTokens > 0) {
|
|
570
|
+
const savings = estimateCacheSavings(sharedTokens, phase.config.agents.length, 3.0);
|
|
571
|
+
this.log.info(`Cache optimization: ${savings.savingsPercent}% estimated savings on shared prefix (${phase.config.agents.length} agents)`, savings);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
// Check for failures
|
|
575
|
+
const failures = results.filter(r => r.status === 'rejected');
|
|
576
|
+
if (failures.length > 0) {
|
|
577
|
+
const failureCount = failures.length;
|
|
578
|
+
this.log.warn(`Phase had ${failureCount} agent failures`, {
|
|
579
|
+
phase: phase.config.name,
|
|
580
|
+
});
|
|
581
|
+
// If all agents failed, phase fails
|
|
582
|
+
if (failures.length === phase.config.agents.length) {
|
|
583
|
+
phase.status = 'failed';
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
phase.status = 'completed'; // Partial success
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
phase.status = 'completed';
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
// Run agents sequentially (with retry)
|
|
595
|
+
for (const assignment of phase.config.agents) {
|
|
596
|
+
await this.executeAgentWithRetry(assignment, phase, orchestration, maxRetries);
|
|
597
|
+
// Check phase token budget after each agent
|
|
598
|
+
if (phase.config.tokenBudget) {
|
|
599
|
+
const phaseTokens = phase.agents.reduce((sum, a) => sum + a.usage.totalTokens, 0);
|
|
600
|
+
if (phaseTokens >= phase.config.tokenBudget) {
|
|
601
|
+
this.log.warn(`Phase token budget exceeded: ${phaseTokens} >= ${phase.config.tokenBudget}`, {
|
|
602
|
+
phase: phase.config.name,
|
|
603
|
+
});
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
phase.status = 'completed';
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
phase.status = 'failed';
|
|
613
|
+
throw error;
|
|
614
|
+
}
|
|
615
|
+
phase.completedAt = new Date();
|
|
616
|
+
phase.usage = phase.agents.reduce((total, agent) => mergeUsageStats(total, agent.usage), emptyUsageStats());
|
|
617
|
+
// Run verification after implementation phases
|
|
618
|
+
if (this.verifier && (phase.config.name.includes('implement') || phase.config.name.includes('integrate'))) {
|
|
619
|
+
const results = await this.verifier.verify();
|
|
620
|
+
const summary = Verifier.summarize(results);
|
|
621
|
+
this.log.info(`Verification: ${summary}`, { phase: phase.config.name });
|
|
622
|
+
this.bus.emit('system:warning', { verification: summary, results }, 'verifier');
|
|
623
|
+
}
|
|
624
|
+
// Adaptive replanning based on phase results
|
|
625
|
+
const adaptation = this.replanner.analyze(orchestration, phase);
|
|
626
|
+
if (adaptation) {
|
|
627
|
+
this.replanner.apply(orchestration, adaptation);
|
|
628
|
+
this.bus.emit('system:warning', {
|
|
629
|
+
type: 'adaptive-replan',
|
|
630
|
+
reason: adaptation.reason,
|
|
631
|
+
skipped: adaptation.skipPhases,
|
|
632
|
+
overrides: Object.keys(adaptation.modelOverrides).length,
|
|
633
|
+
}, 'replanner');
|
|
634
|
+
}
|
|
635
|
+
this.bus.emit('orchestration:phase-complete', {
|
|
636
|
+
orchestrationId: orchestration.config.id,
|
|
637
|
+
phase: phase.config.name,
|
|
638
|
+
status: phase.status,
|
|
639
|
+
usage: phase.usage,
|
|
640
|
+
}, 'engine');
|
|
641
|
+
// Save checkpoint after each phase
|
|
642
|
+
saveCheckpoint(orchestration);
|
|
643
|
+
this.log.info(`Phase ${phase.status}: ${phase.config.name}`, {
|
|
644
|
+
agents: phase.agents.length,
|
|
645
|
+
usage: phase.usage,
|
|
646
|
+
});
|
|
647
|
+
// Collect agent outputs for inter-phase context
|
|
648
|
+
const outputs = phase.agents
|
|
649
|
+
.filter(a => a.result?.output)
|
|
650
|
+
.map(a => `[${a.config.name}]: ${a.result.output.slice(0, 2000)}`);
|
|
651
|
+
// Update living spec with phase outputs
|
|
652
|
+
if (this.livingSpec) {
|
|
653
|
+
this.livingSpec = updateSpecFromPhase(this.livingSpec, phase.config.name, outputs);
|
|
654
|
+
}
|
|
655
|
+
// Generate reflection and inject into outputs for next phase
|
|
656
|
+
const reflection = this.reflectionEngine.reflect(phase);
|
|
657
|
+
const reflectionText = this.reflectionEngine.format(reflection);
|
|
658
|
+
this.reflections.push(reflectionText);
|
|
659
|
+
outputs.push(reflectionText);
|
|
660
|
+
// Distill outputs if they're large
|
|
661
|
+
if (this.distiller) {
|
|
662
|
+
const distilled = await this.distiller.distill(outputs, phase.config.name);
|
|
663
|
+
return [distilled];
|
|
664
|
+
}
|
|
665
|
+
return outputs;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Execute a single agent within a phase.
|
|
669
|
+
*/
|
|
670
|
+
async executeAgent(assignment, phase, orchestration) {
|
|
671
|
+
// Spawn from registry, passing all assignment overrides
|
|
672
|
+
const instance = this.registry.spawn(assignment.type, {
|
|
673
|
+
name: assignment.name,
|
|
674
|
+
model: assignment.model,
|
|
675
|
+
backend: assignment.backend,
|
|
676
|
+
backendModel: assignment.backendModel,
|
|
677
|
+
});
|
|
678
|
+
phase.agents.push(instance);
|
|
679
|
+
// Build task prompt with cache-friendly ordering:
|
|
680
|
+
// Shared prefix (orchestration + phase context) comes first for API cache hits,
|
|
681
|
+
// agent-specific content (definition + task) comes last.
|
|
682
|
+
const orchestrationContext = `## Orchestration: ${orchestration.config.name}\nPattern: ${orchestration.config.pattern}`;
|
|
683
|
+
const phaseContext = this.compaction.buildContextPrefix(this.phaseOutputs);
|
|
684
|
+
const agentPrompt = instance.config.prompt;
|
|
685
|
+
const baseTask = assignment.prompt || `Execute task as ${assignment.type}`;
|
|
686
|
+
const optimized = optimizeForCache(orchestrationContext, phaseContext, agentPrompt, baseTask);
|
|
687
|
+
// For parallel phases with large shared prefix, use shared context file
|
|
688
|
+
const sharedContextPath = phase.config.parallel
|
|
689
|
+
? this.writeSharedContext(optimized.sharedPrefix)
|
|
690
|
+
: null;
|
|
691
|
+
// Cascade model selection: if no explicit model override, pick starting tier by complexity
|
|
692
|
+
if (!assignment.model) {
|
|
693
|
+
const complexity = classifyTaskComplexity(baseTask);
|
|
694
|
+
const startTier = selectStartingTier(complexity);
|
|
695
|
+
if (startTier !== instance.config.model) {
|
|
696
|
+
instance.config.model = startTier;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
// Inject living spec into task context
|
|
700
|
+
const specPrefix = this.livingSpec ? formatLivingSpec(this.livingSpec) + '\n\n' : '';
|
|
701
|
+
const task = sharedContextPath
|
|
702
|
+
? `Read the shared context file at ${sharedContextPath} first, then:\n\n${specPrefix}${optimized.agentSuffix}`
|
|
703
|
+
: `${specPrefix}${optimized.full}`;
|
|
704
|
+
// Transition to running
|
|
705
|
+
this.registry.updateStatus(instance.id, 'running');
|
|
706
|
+
// Create worktree if enabled and assignment requests it
|
|
707
|
+
let worktreeId;
|
|
708
|
+
if (this.worktreeManager && assignment.worktree) {
|
|
709
|
+
try {
|
|
710
|
+
const wt = this.worktreeManager.create(instance.id, assignment.name);
|
|
711
|
+
worktreeId = wt.id;
|
|
712
|
+
this.bus.emit('worktree:created', { agentId: instance.id, worktreeId: wt.id, path: wt.path }, instance.id);
|
|
713
|
+
}
|
|
714
|
+
catch (err) {
|
|
715
|
+
this.log.warn(`Failed to create worktree for ${assignment.name}: ${err}`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
// Execute with timeout
|
|
719
|
+
const timeoutMs = (instance.config.timeout ?? 300) * 1000; // default 5 min
|
|
720
|
+
let timer;
|
|
721
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
722
|
+
timer = setTimeout(() => reject(new Error(`Agent timeout after ${timeoutMs / 1000}s`)), timeoutMs);
|
|
723
|
+
});
|
|
724
|
+
try {
|
|
725
|
+
await Promise.race([
|
|
726
|
+
this.executor.execute(instance, task),
|
|
727
|
+
timeoutPromise,
|
|
728
|
+
]);
|
|
729
|
+
clearTimeout(timer);
|
|
730
|
+
// Merge worktree if created
|
|
731
|
+
if (worktreeId && this.worktreeManager) {
|
|
732
|
+
try {
|
|
733
|
+
this.worktreeManager.merge(worktreeId);
|
|
734
|
+
this.bus.emit('worktree:merged', { agentId: instance.id, worktreeId }, instance.id);
|
|
735
|
+
}
|
|
736
|
+
catch (err) {
|
|
737
|
+
this.log.warn(`Failed to merge worktree ${worktreeId}: ${err}`);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
// Transition to idle (completed successfully)
|
|
741
|
+
this.registry.updateStatus(instance.id, 'idle');
|
|
742
|
+
// Then shutdown
|
|
743
|
+
this.registry.updateStatus(instance.id, 'shutdown');
|
|
744
|
+
return instance;
|
|
745
|
+
}
|
|
746
|
+
catch (error) {
|
|
747
|
+
clearTimeout(timer);
|
|
748
|
+
if (String(error).includes('timeout')) {
|
|
749
|
+
this.registry.updateStatus(instance.id, 'timeout');
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
this.registry.updateStatus(instance.id, 'error');
|
|
753
|
+
}
|
|
754
|
+
instance.error = error instanceof Error ? error.message : String(error);
|
|
755
|
+
throw error;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
async executeAgentWithRetry(assignment, phase, orchestration, maxRetries = 2) {
|
|
759
|
+
let lastError;
|
|
760
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
761
|
+
try {
|
|
762
|
+
return await this.executeAgent(assignment, phase, orchestration);
|
|
763
|
+
}
|
|
764
|
+
catch (error) {
|
|
765
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
766
|
+
// Remove the failed instance from phase agents to prevent duplicates on retry
|
|
767
|
+
const failedIndex = phase.agents.findIndex(a => a.config.name === assignment.name && (a.status === 'error' || a.status === 'timeout'));
|
|
768
|
+
if (failedIndex >= 0)
|
|
769
|
+
phase.agents.splice(failedIndex, 1);
|
|
770
|
+
if (attempt < maxRetries) {
|
|
771
|
+
const delayMs = Math.min(1000 * Math.pow(2, attempt) + Math.random() * 1000, 30000);
|
|
772
|
+
this.log.warn(`Agent failed, retrying in ${Math.round(delayMs / 1000)}s (attempt ${attempt + 1}/${maxRetries})`, {
|
|
773
|
+
agent: assignment.name,
|
|
774
|
+
error: lastError.message,
|
|
775
|
+
});
|
|
776
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
throw lastError;
|
|
781
|
+
}
|
|
782
|
+
// ─── Resume ────────────────────────────────────────────────────
|
|
783
|
+
/**
|
|
784
|
+
* Resume an orchestration from a checkpoint.
|
|
785
|
+
*/
|
|
786
|
+
async resume(orchestrationId) {
|
|
787
|
+
const checkpoint = loadCheckpoint(orchestrationId);
|
|
788
|
+
if (!checkpoint) {
|
|
789
|
+
this.log.error(`No checkpoint found for: ${orchestrationId}`);
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
this.log.info(`Resuming orchestration: ${checkpoint.config.name} from phase ${checkpoint.currentPhase + 1}`);
|
|
793
|
+
// Reconstruct the orchestration, marking completed phases
|
|
794
|
+
const orchestration = this.createInstance(checkpoint.config);
|
|
795
|
+
orchestration.status = 'running';
|
|
796
|
+
orchestration.startedAt = new Date();
|
|
797
|
+
// Mark completed phases from checkpoint
|
|
798
|
+
for (let i = 0; i < checkpoint.phaseResults.length; i++) {
|
|
799
|
+
const saved = checkpoint.phaseResults[i];
|
|
800
|
+
if (saved && (saved.status === 'completed' || saved.status === 'failed')) {
|
|
801
|
+
orchestration.phases[i].status = saved.status;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
this.orchestrations.set(checkpoint.config.id, orchestration);
|
|
805
|
+
this.bus.emit('orchestration:resumed', {
|
|
806
|
+
id: checkpoint.config.id,
|
|
807
|
+
name: checkpoint.config.name,
|
|
808
|
+
resumedFromPhase: checkpoint.currentPhase,
|
|
809
|
+
}, 'engine');
|
|
810
|
+
// Re-run execute — the DAG executor will skip completed phases
|
|
811
|
+
// because they're already marked and seeded into the completed set
|
|
812
|
+
return this.execute(checkpoint.config);
|
|
813
|
+
}
|
|
814
|
+
// ─── Approval ─────────────────────────────────────────────────
|
|
815
|
+
async requestApproval(phaseName, agents) {
|
|
816
|
+
const { createInterface } = await import('readline');
|
|
817
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
818
|
+
return new Promise((resolve) => {
|
|
819
|
+
const agentList = agents.map(a => ` ${a.name} (${a.type})`).join('\n');
|
|
820
|
+
rl.question(`\n⏸ Phase "${phaseName}" requires approval.\n` +
|
|
821
|
+
` Agents:\n${agentList}\n` +
|
|
822
|
+
` Proceed? [Y/n] `, (answer) => {
|
|
823
|
+
rl.close();
|
|
824
|
+
resolve(answer.toLowerCase() !== 'n');
|
|
825
|
+
});
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
// ─── Signal Handling ──────────────────────────────────────────
|
|
829
|
+
/**
|
|
830
|
+
* Register signal handlers for graceful shutdown.
|
|
831
|
+
*/
|
|
832
|
+
setupSignalHandlers() {
|
|
833
|
+
// Remove any previously registered handlers to prevent accumulation
|
|
834
|
+
this.removeSignalHandlers();
|
|
835
|
+
const handler = async () => {
|
|
836
|
+
this.log.warn('Shutdown signal received — saving checkpoint and cleaning up');
|
|
837
|
+
this.abortController.abort();
|
|
838
|
+
// Save checkpoint for any running orchestration
|
|
839
|
+
for (const orchestration of this.getActiveOrchestrations()) {
|
|
840
|
+
try {
|
|
841
|
+
const path = saveCheckpoint(orchestration);
|
|
842
|
+
this.log.info(`Checkpoint saved: ${path}`);
|
|
843
|
+
}
|
|
844
|
+
catch {
|
|
845
|
+
// Best effort
|
|
846
|
+
}
|
|
847
|
+
orchestration.status = 'cancelled';
|
|
848
|
+
}
|
|
849
|
+
// Transition running agents to error
|
|
850
|
+
for (const instance of this.registry.getActiveInstances()) {
|
|
851
|
+
try {
|
|
852
|
+
this.registry.updateStatus(instance.id, 'error');
|
|
853
|
+
}
|
|
854
|
+
catch {
|
|
855
|
+
// Best effort — lifecycle may not allow transition
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
try {
|
|
859
|
+
this.stats.close();
|
|
860
|
+
}
|
|
861
|
+
catch { /* best effort */ }
|
|
862
|
+
try {
|
|
863
|
+
this.traceStore?.close();
|
|
864
|
+
}
|
|
865
|
+
catch { /* best effort */ }
|
|
866
|
+
try {
|
|
867
|
+
this.heuristicStore?.close();
|
|
868
|
+
}
|
|
869
|
+
catch { /* best effort */ }
|
|
870
|
+
try {
|
|
871
|
+
this.modelRouter?.close();
|
|
872
|
+
}
|
|
873
|
+
catch { }
|
|
874
|
+
try {
|
|
875
|
+
this.autonomyTracker?.close();
|
|
876
|
+
}
|
|
877
|
+
catch { }
|
|
878
|
+
this.bus.emit('system:warning', { message: 'Graceful shutdown complete' }, 'engine');
|
|
879
|
+
};
|
|
880
|
+
process.on('SIGINT', handler);
|
|
881
|
+
process.on('SIGTERM', handler);
|
|
882
|
+
this.signalHandlers.push({ signal: 'SIGINT', handler: handler });
|
|
883
|
+
this.signalHandlers.push({ signal: 'SIGTERM', handler: handler });
|
|
884
|
+
}
|
|
885
|
+
removeSignalHandlers() {
|
|
886
|
+
for (const { signal, handler } of this.signalHandlers) {
|
|
887
|
+
process.removeListener(signal, handler);
|
|
888
|
+
}
|
|
889
|
+
this.signalHandlers = [];
|
|
890
|
+
}
|
|
891
|
+
/** Close all resources (stats, traces, heuristics, model router, autonomy tracker). */
|
|
892
|
+
close() {
|
|
893
|
+
try {
|
|
894
|
+
this.stats.close();
|
|
895
|
+
}
|
|
896
|
+
catch { }
|
|
897
|
+
try {
|
|
898
|
+
this.traceStore?.close();
|
|
899
|
+
}
|
|
900
|
+
catch { }
|
|
901
|
+
try {
|
|
902
|
+
this.heuristicStore?.close();
|
|
903
|
+
}
|
|
904
|
+
catch { }
|
|
905
|
+
try {
|
|
906
|
+
this.modelRouter?.close();
|
|
907
|
+
}
|
|
908
|
+
catch { }
|
|
909
|
+
try {
|
|
910
|
+
this.autonomyTracker?.close();
|
|
911
|
+
}
|
|
912
|
+
catch { }
|
|
913
|
+
this.removeSignalHandlers();
|
|
914
|
+
}
|
|
915
|
+
// ─── Queries ───────────────────────────────────────────────────
|
|
916
|
+
/**
|
|
917
|
+
* Get an orchestration by ID.
|
|
918
|
+
*/
|
|
919
|
+
getOrchestration(id) {
|
|
920
|
+
return this.orchestrations.get(id);
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Get all orchestrations.
|
|
924
|
+
*/
|
|
925
|
+
getAllOrchestrations() {
|
|
926
|
+
return [...this.orchestrations.values()];
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Get active orchestrations.
|
|
930
|
+
*/
|
|
931
|
+
getActiveOrchestrations() {
|
|
932
|
+
return [...this.orchestrations.values()].filter(o => o.status === 'running' || o.status === 'paused');
|
|
933
|
+
}
|
|
934
|
+
get patternRegistry() {
|
|
935
|
+
return this.patterns;
|
|
936
|
+
}
|
|
937
|
+
// ─── Shared Context ────────────────────────────────────────────
|
|
938
|
+
/**
|
|
939
|
+
* Write shared context to a file for parallel agents to read,
|
|
940
|
+
* avoiding duplicating large context in each agent's prompt.
|
|
941
|
+
*/
|
|
942
|
+
writeSharedContext(context) {
|
|
943
|
+
if (!context || context.length < 2000)
|
|
944
|
+
return null; // Not worth sharing for small contexts
|
|
945
|
+
const contextDir = join(this.options.cwd ?? process.cwd(), '.swarm');
|
|
946
|
+
if (!existsSync(contextDir))
|
|
947
|
+
mkdirSync(contextDir, { recursive: true });
|
|
948
|
+
const contextPath = join(contextDir, `shared-context-${randomUUID().slice(0, 8)}.md`);
|
|
949
|
+
writeFileSync(contextPath, redactSecrets(context), { mode: 0o600 });
|
|
950
|
+
this.sharedContextFiles.push(contextPath);
|
|
951
|
+
return contextPath;
|
|
952
|
+
}
|
|
953
|
+
// ─── Factory ───────────────────────────────────────────────────
|
|
954
|
+
createInstance(config) {
|
|
955
|
+
return {
|
|
956
|
+
config,
|
|
957
|
+
status: 'pending',
|
|
958
|
+
phases: config.phases.map(phaseConfig => ({
|
|
959
|
+
config: phaseConfig,
|
|
960
|
+
status: 'pending',
|
|
961
|
+
agents: [],
|
|
962
|
+
usage: emptyUsageStats(),
|
|
963
|
+
})),
|
|
964
|
+
currentPhase: 0,
|
|
965
|
+
usage: emptyUsageStats(),
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
//# sourceMappingURL=engine.js.map
|