xcoder-dna 0.0.0-stage → 1.3.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/LICENSE +66 -0
- package/README.md +87 -2
- package/dist/agents/ai-chat-debate.d.ts +36 -0
- package/dist/agents/ai-chat-debate.js +300 -0
- package/dist/agents/ai-chat-report-writer.d.ts +7 -0
- package/dist/agents/ai-chat-report-writer.js +100 -0
- package/dist/agents/duplicate-detector.d.ts +41 -0
- package/dist/agents/duplicate-detector.js +65 -0
- package/dist/agents/genesis-agent.d.ts +61 -0
- package/dist/agents/genesis-agent.js +340 -0
- package/dist/agents/samurai-agent.d.ts +53 -0
- package/dist/agents/samurai-agent.js +279 -0
- package/dist/agents/sub-agent.d.ts +56 -0
- package/dist/agents/sub-agent.js +353 -0
- package/dist/backlog/backlog-requirement-validator.d.ts +10 -0
- package/dist/backlog/backlog-requirement-validator.js +60 -0
- package/dist/backlog/backlog-store.d.ts +29 -0
- package/dist/backlog/backlog-store.js +148 -0
- package/dist/backlog/po-agent.d.ts +16 -0
- package/dist/backlog/po-agent.js +121 -0
- package/dist/backlog/types.d.ts +25 -0
- package/dist/backlog/types.js +4 -0
- package/dist/bench/run.d.ts +11 -0
- package/dist/bench/run.js +185 -0
- package/dist/bench/types.d.ts +50 -0
- package/dist/bench/types.js +1 -0
- package/dist/cli/swarm.d.ts +10 -0
- package/dist/cli/swarm.js +100 -0
- package/dist/cli/task.d.ts +14 -0
- package/dist/cli/task.js +121 -0
- package/dist/cli/wbc-arg.d.ts +6 -0
- package/dist/cli/wbc-arg.js +40 -0
- package/dist/cli/wbc.d.ts +45 -0
- package/dist/cli/wbc.js +98 -0
- package/dist/commands/license.d.ts +9 -0
- package/dist/commands/license.js +140 -0
- package/dist/commands/release.d.ts +44 -0
- package/dist/commands/release.js +180 -0
- package/dist/commands/test.d.ts +50 -0
- package/dist/commands/test.js +385 -0
- package/dist/config/loadConfig.d.ts +82 -0
- package/dist/config/loadConfig.js +240 -0
- package/dist/config/secure-mode.d.ts +29 -0
- package/dist/config/secure-mode.js +63 -0
- package/dist/config/user-config.d.ts +39 -0
- package/dist/config/user-config.js +202 -0
- package/dist/engine/reasoning-engine.d.ts +21 -0
- package/dist/engine/reasoning-engine.js +59 -0
- package/dist/events/bus.d.ts +22 -0
- package/dist/events/bus.js +18 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +77 -0
- package/dist/indexing/ignoreRules.d.ts +1 -0
- package/dist/indexing/ignoreRules.js +53 -0
- package/dist/instance-swarm/instance-swarm-orchestrator.d.ts +57 -0
- package/dist/instance-swarm/instance-swarm-orchestrator.js +204 -0
- package/dist/instance-swarm/protocol-name-resolver.d.ts +23 -0
- package/dist/instance-swarm/protocol-name-resolver.js +64 -0
- package/dist/license/gate.d.ts +31 -0
- package/dist/license/gate.js +188 -0
- package/dist/license/http-client.d.ts +43 -0
- package/dist/license/http-client.js +195 -0
- package/dist/license/manager.d.ts +39 -0
- package/dist/license/manager.js +175 -0
- package/dist/license/receipt.d.ts +27 -0
- package/dist/license/receipt.js +108 -0
- package/dist/license/state-codec.d.ts +72 -0
- package/dist/license/state-codec.js +218 -0
- package/dist/license/state-paths.d.ts +17 -0
- package/dist/license/state-paths.js +64 -0
- package/dist/license/store.d.ts +44 -0
- package/dist/license/store.js +223 -0
- package/dist/license/trust-anchor.d.ts +24 -0
- package/dist/license/trust-anchor.js +74 -0
- package/dist/license/types.d.ts +34 -0
- package/dist/license/types.js +19 -0
- package/dist/llm/budget.d.ts +23 -0
- package/dist/llm/budget.js +58 -0
- package/dist/llm/client.d.ts +53 -0
- package/dist/llm/client.js +122 -0
- package/dist/llm/factory.d.ts +14 -0
- package/dist/llm/factory.js +32 -0
- package/dist/orchestrator/defect-logger.d.ts +31 -0
- package/dist/orchestrator/defect-logger.js +101 -0
- package/dist/orchestrator/defect-manager.d.ts +118 -0
- package/dist/orchestrator/defect-manager.js +338 -0
- package/dist/orchestrator/defect-migration.d.ts +14 -0
- package/dist/orchestrator/defect-migration.js +174 -0
- package/dist/orchestrator/defect-store.d.ts +48 -0
- package/dist/orchestrator/defect-store.js +76 -0
- package/dist/orchestrator/healing.d.ts +14 -0
- package/dist/orchestrator/healing.js +11 -0
- package/dist/orchestrator/interactive-steering.d.ts +64 -0
- package/dist/orchestrator/interactive-steering.js +107 -0
- package/dist/orchestrator/interrupt-handler.d.ts +30 -0
- package/dist/orchestrator/interrupt-handler.js +70 -0
- package/dist/orchestrator/lesson-manager.d.ts +25 -0
- package/dist/orchestrator/lesson-manager.js +122 -0
- package/dist/orchestrator/progress-summary.d.ts +15 -0
- package/dist/orchestrator/progress-summary.js +106 -0
- package/dist/orchestrator/reasoning-score.d.ts +49 -0
- package/dist/orchestrator/reasoning-score.js +117 -0
- package/dist/orchestrator/swarm.d.ts +36 -0
- package/dist/orchestrator/swarm.js +77 -0
- package/dist/orchestrator/task-factory.d.ts +15 -0
- package/dist/orchestrator/task-factory.js +176 -0
- package/dist/orchestrator/validation-gate.d.ts +23 -0
- package/dist/orchestrator/validation-gate.js +155 -0
- package/dist/protocol/react-contract.d.ts +136 -0
- package/dist/protocol/react-contract.js +698 -0
- package/dist/protocol/wks-protocol-manager.d.ts +22 -0
- package/dist/protocol/wks-protocol-manager.js +72 -0
- package/dist/sdlc-runtime/conversation-runner.d.ts +8 -0
- package/dist/sdlc-runtime/conversation-runner.js +64 -0
- package/dist/sdlc-runtime/dag-utils.d.ts +15 -0
- package/dist/sdlc-runtime/dag-utils.js +67 -0
- package/dist/sdlc-runtime/defect-integration.d.ts +7 -0
- package/dist/sdlc-runtime/defect-integration.js +99 -0
- package/dist/sdlc-runtime/execution-loop.d.ts +38 -0
- package/dist/sdlc-runtime/execution-loop.js +274 -0
- package/dist/sdlc-runtime/lesson-recorder.d.ts +9 -0
- package/dist/sdlc-runtime/lesson-recorder.js +27 -0
- package/dist/sdlc-runtime/plan-initializer.d.ts +47 -0
- package/dist/sdlc-runtime/plan-initializer.js +135 -0
- package/dist/sdlc-runtime/progress-persister.d.ts +10 -0
- package/dist/sdlc-runtime/progress-persister.js +34 -0
- package/dist/sdlc-runtime/protocol-loader.d.ts +10 -0
- package/dist/sdlc-runtime/protocol-loader.js +21 -0
- package/dist/sdlc-runtime/reporting.d.ts +24 -0
- package/dist/sdlc-runtime/reporting.js +68 -0
- package/dist/sdlc-runtime/sdlc-orchestrator.d.ts +59 -0
- package/dist/sdlc-runtime/sdlc-orchestrator.js +170 -0
- package/dist/sdlc-runtime/sdlc-skill.d.ts +44 -0
- package/dist/sdlc-runtime/sdlc-skill.js +148 -0
- package/dist/sdlc-runtime/steering-handler.d.ts +14 -0
- package/dist/sdlc-runtime/steering-handler.js +51 -0
- package/dist/sdlc-runtime/trace-recorder.d.ts +10 -0
- package/dist/sdlc-runtime/trace-recorder.js +7 -0
- package/dist/security/index.d.ts +16 -0
- package/dist/security/index.js +12 -0
- package/dist/security/input-screen.d.ts +30 -0
- package/dist/security/input-screen.js +75 -0
- package/dist/security/tool-call-guard.d.ts +16 -0
- package/dist/security/tool-call-guard.js +163 -0
- package/dist/security/untrusted-content.d.ts +34 -0
- package/dist/security/untrusted-content.js +63 -0
- package/dist/skills/advanced-dfir/SKILL.md +85 -0
- package/dist/skills/advanced-pentest-lab/SKILL.md +88 -0
- package/dist/skills/analyst/SKILL.md +55 -0
- package/dist/skills/architect/SKILL.md +54 -0
- package/dist/skills/aws/SKILL.md +92 -0
- package/dist/skills/azure/SKILL.md +90 -0
- package/dist/skills/bylaw-context.d.ts +45 -0
- package/dist/skills/bylaw-context.js +61 -0
- package/dist/skills/bylaw-review.d.ts +20 -0
- package/dist/skills/bylaw-review.js +101 -0
- package/dist/skills/conversation/SKILL.md +29 -0
- package/dist/skills/design-patterns/SKILL.md +190 -0
- package/dist/skills/devops/SKILL.md +55 -0
- package/dist/skills/directives.d.ts +20 -0
- package/dist/skills/directives.js +313 -0
- package/dist/skills/docker/SKILL.md +76 -0
- package/dist/skills/docker-expert/SKILL.md +56 -0
- package/dist/skills/enterprise-systems-analyst/SKILL.md +83 -0
- package/dist/skills/enterprise-systems-analyst/references/crm.md +41 -0
- package/dist/skills/enterprise-systems-analyst/references/erp.md +40 -0
- package/dist/skills/enterprise-systems-analyst/references/financial-systems.md +35 -0
- package/dist/skills/enterprise-systems-analyst/references/hcm.md +38 -0
- package/dist/skills/enterprise-systems-analyst/references/mrp.md +37 -0
- package/dist/skills/enterprise-systems-analyst/references/scm.md +32 -0
- package/dist/skills/ethical-hacking-methodology/SKILL.md +119 -0
- package/dist/skills/externalSkillLoader.d.ts +25 -0
- package/dist/skills/externalSkillLoader.js +65 -0
- package/dist/skills/externalSkillRegistry.d.ts +33 -0
- package/dist/skills/externalSkillRegistry.js +94 -0
- package/dist/skills/filesystem-management/SKILL.md +61 -0
- package/dist/skills/first-run-seed.d.ts +26 -0
- package/dist/skills/first-run-seed.js +75 -0
- package/dist/skills/git-vcs/SKILL.md +84 -0
- package/dist/skills/kafka/SKILL.md +89 -0
- package/dist/skills/kubernetes/SKILL.md +93 -0
- package/dist/skills/kubernetes-expert/SKILL.md +54 -0
- package/dist/skills/openshift/SKILL.md +93 -0
- package/dist/skills/pentester/SKILL.md +59 -0
- package/dist/skills/performance-tester/SKILL.md +57 -0
- package/dist/skills/playwright-ui-tester/SKILL.md +67 -0
- package/dist/skills/programmer/SKILL.md +58 -0
- package/dist/skills/project-scaffolding/SKILL.md +133 -0
- package/dist/skills/qa-engineer/SKILL.md +91 -0
- package/dist/skills/rca/SKILL.md +58 -0
- package/dist/skills/redhat/SKILL.md +92 -0
- package/dist/skills/rosa/SKILL.md +80 -0
- package/dist/skills/scrum-framework/SKILL.md +74 -0
- package/dist/skills/scrum-framework/references/artifacts_and_quality.md +79 -0
- package/dist/skills/scrum-framework/references/roles_and_events.md +105 -0
- package/dist/skills/scrum-master-agent/SKILL.md +74 -0
- package/dist/skills/scrum-master-agent/references/behaviors.md +56 -0
- package/dist/skills/sdlc-skill.d.ts +1 -0
- package/dist/skills/sdlc-skill.js +4 -0
- package/dist/skills/secops/SKILL.md +52 -0
- package/dist/skills/secure-architect.bylaws.d.ts +68 -0
- package/dist/skills/secure-architect.bylaws.js +525 -0
- package/dist/skills/seed-skills.d.ts +48 -0
- package/dist/skills/seed-skills.js +213 -0
- package/dist/skills/skill-authoring/SKILL.md +123 -0
- package/dist/skills/skill-header.d.ts +51 -0
- package/dist/skills/skill-header.js +163 -0
- package/dist/skills/skill-path-guard.d.ts +41 -0
- package/dist/skills/skill-path-guard.js +95 -0
- package/dist/skills/skillRouter.d.ts +51 -0
- package/dist/skills/skillRouter.js +90 -0
- package/dist/skills/skills-root.d.ts +64 -0
- package/dist/skills/skills-root.js +168 -0
- package/dist/skills/software-architect/SKILL.md +77 -0
- package/dist/skills/software-engineer/SKILL.md +74 -0
- package/dist/skills/solid-principles/SKILL.md +94 -0
- package/dist/skills/task-planning/SKILL.md +115 -0
- package/dist/skills/tester/SKILL.md +54 -0
- package/dist/skills/ubuntu/SKILL.md +87 -0
- package/dist/skills/ui-ux-design/SKILL.md +114 -0
- package/dist/skills/workspace-context/SKILL.md +103 -0
- package/dist/sprint/controller.d.ts +37 -0
- package/dist/sprint/controller.js +220 -0
- package/dist/sprint/dag-builder.d.ts +14 -0
- package/dist/sprint/dag-builder.js +105 -0
- package/dist/sprint/reporter.d.ts +11 -0
- package/dist/sprint/reporter.js +97 -0
- package/dist/sprint/state-store.d.ts +10 -0
- package/dist/sprint/state-store.js +71 -0
- package/dist/sprint/types.d.ts +24 -0
- package/dist/sprint/types.js +4 -0
- package/dist/store/file-version-store.d.ts +24 -0
- package/dist/store/file-version-store.js +73 -0
- package/dist/store/task-store.d.ts +90 -0
- package/dist/store/task-store.js +166 -0
- package/dist/task-ingest/architect-detailer.d.ts +43 -0
- package/dist/task-ingest/architect-detailer.js +141 -0
- package/dist/task-ingest/detail-schema.d.ts +9 -0
- package/dist/task-ingest/detail-schema.js +195 -0
- package/dist/task-ingest/detailed-run.d.ts +36 -0
- package/dist/task-ingest/detailed-run.js +108 -0
- package/dist/task-ingest/extract-id.d.ts +9 -0
- package/dist/task-ingest/extract-id.js +24 -0
- package/dist/task-ingest/index.d.ts +23 -0
- package/dist/task-ingest/index.js +14 -0
- package/dist/task-ingest/ingest.d.ts +45 -0
- package/dist/task-ingest/ingest.js +163 -0
- package/dist/task-ingest/list-ingested.d.ts +13 -0
- package/dist/task-ingest/list-ingested.js +30 -0
- package/dist/task-ingest/pending-filter.d.ts +30 -0
- package/dist/task-ingest/pending-filter.js +59 -0
- package/dist/task-ingest/scanner.d.ts +56 -0
- package/dist/task-ingest/scanner.js +216 -0
- package/dist/task-ingest/source-reader.d.ts +25 -0
- package/dist/task-ingest/source-reader.js +93 -0
- package/dist/task-ingest/spec-builder.d.ts +34 -0
- package/dist/task-ingest/spec-builder.js +89 -0
- package/dist/task-ingest/wbs-extract.d.ts +50 -0
- package/dist/task-ingest/wbs-extract.js +226 -0
- package/dist/task-ingest/wbs-parser.d.ts +36 -0
- package/dist/task-ingest/wbs-parser.js +170 -0
- package/dist/tools/apiTestTool.d.ts +37 -0
- package/dist/tools/apiTestTool.js +128 -0
- package/dist/tools/ast-tools.d.ts +183 -0
- package/dist/tools/ast-tools.js +853 -0
- package/dist/tools/browse-tool.d.ts +105 -0
- package/dist/tools/browse-tool.js +394 -0
- package/dist/tools/credential.d.ts +17 -0
- package/dist/tools/credential.js +32 -0
- package/dist/tools/dependency-graph.d.ts +23 -0
- package/dist/tools/dependency-graph.js +164 -0
- package/dist/tools/edit-tool.d.ts +34 -0
- package/dist/tools/edit-tool.js +95 -0
- package/dist/tools/file-header-manager.d.ts +27 -0
- package/dist/tools/file-header-manager.js +153 -0
- package/dist/tools/fs-tools.d.ts +154 -0
- package/dist/tools/fs-tools.js +337 -0
- package/dist/tools/git-tools.d.ts +79 -0
- package/dist/tools/git-tools.js +348 -0
- package/dist/tools/github-tool.d.ts +48 -0
- package/dist/tools/github-tool.js +147 -0
- package/dist/tools/globTool.d.ts +20 -0
- package/dist/tools/globTool.js +34 -0
- package/dist/tools/grepTool.d.ts +19 -0
- package/dist/tools/grepTool.js +44 -0
- package/dist/tools/index-freshness.d.ts +58 -0
- package/dist/tools/index-freshness.js +190 -0
- package/dist/tools/index-glob.d.ts +48 -0
- package/dist/tools/index-glob.js +175 -0
- package/dist/tools/load-skill-tool.d.ts +15 -0
- package/dist/tools/load-skill-tool.js +114 -0
- package/dist/tools/patch-tools.d.ts +58 -0
- package/dist/tools/patch-tools.js +153 -0
- package/dist/tools/playwright-tool.d.ts +66 -0
- package/dist/tools/playwright-tool.js +121 -0
- package/dist/tools/project-detect.d.ts +20 -0
- package/dist/tools/project-detect.js +87 -0
- package/dist/tools/prompt-guidance.d.ts +22 -0
- package/dist/tools/prompt-guidance.js +23 -0
- package/dist/tools/scheduleTool.d.ts +19 -0
- package/dist/tools/scheduleTool.js +181 -0
- package/dist/tools/search-tools.d.ts +27 -0
- package/dist/tools/search-tools.js +78 -0
- package/dist/tools/sed-tool.d.ts +66 -0
- package/dist/tools/sed-tool.js +402 -0
- package/dist/tools/shell-result.d.ts +11 -0
- package/dist/tools/shell-result.js +1 -0
- package/dist/tools/shell-tool.d.ts +45 -0
- package/dist/tools/shell-tool.js +141 -0
- package/dist/tools/siteCrawlerTool.d.ts +32 -0
- package/dist/tools/siteCrawlerTool.js +205 -0
- package/dist/tools/tool-registry.d.ts +67 -0
- package/dist/tools/tool-registry.js +596 -0
- package/dist/tools/validate-tool.d.ts +23 -0
- package/dist/tools/validate-tool.js +201 -0
- package/dist/tools/workspace-index.d.ts +46 -0
- package/dist/tools/workspace-index.js +173 -0
- package/dist/types.d.ts +222 -0
- package/dist/types.js +7 -0
- package/dist/updater/index.d.ts +14 -0
- package/dist/updater/index.js +120 -0
- package/dist/util/banner.d.ts +25 -0
- package/dist/util/banner.js +140 -0
- package/dist/util/command-suggester.d.ts +6 -0
- package/dist/util/command-suggester.js +25 -0
- package/dist/util/confirm-interrupt.d.ts +10 -0
- package/dist/util/confirm-interrupt.js +31 -0
- package/dist/util/continue-trigger.d.ts +11 -0
- package/dist/util/continue-trigger.js +20 -0
- package/dist/util/env-info.d.ts +18 -0
- package/dist/util/env-info.js +37 -0
- package/dist/util/file-access-log.d.ts +35 -0
- package/dist/util/file-access-log.js +140 -0
- package/dist/util/footer-manager.d.ts +124 -0
- package/dist/util/footer-manager.js +195 -0
- package/dist/util/footer-renderer.d.ts +67 -0
- package/dist/util/footer-renderer.js +319 -0
- package/dist/util/footer-state.d.ts +34 -0
- package/dist/util/footer-state.js +18 -0
- package/dist/util/footer-text.d.ts +51 -0
- package/dist/util/footer-text.js +74 -0
- package/dist/util/ids.d.ts +1 -0
- package/dist/util/ids.js +4 -0
- package/dist/util/levenshtein.d.ts +6 -0
- package/dist/util/levenshtein.js +34 -0
- package/dist/util/logger.d.ts +10 -0
- package/dist/util/logger.js +41 -0
- package/dist/util/logs.d.ts +26 -0
- package/dist/util/logs.js +88 -0
- package/dist/util/output-handler.d.ts +89 -0
- package/dist/util/output-handler.js +193 -0
- package/dist/util/output-interceptor.d.ts +18 -0
- package/dist/util/output-interceptor.js +49 -0
- package/dist/util/output-paths.d.ts +43 -0
- package/dist/util/output-paths.js +121 -0
- package/dist/util/package-json.d.ts +6 -0
- package/dist/util/package-json.js +36 -0
- package/dist/util/process-kill.d.ts +27 -0
- package/dist/util/process-kill.js +148 -0
- package/dist/util/protocol-error-logger.d.ts +33 -0
- package/dist/util/protocol-error-logger.js +73 -0
- package/dist/util/redact.d.ts +25 -0
- package/dist/util/redact.js +103 -0
- package/dist/util/scroll-region-guard.d.ts +33 -0
- package/dist/util/scroll-region-guard.js +63 -0
- package/dist/util/spinner.d.ts +15 -0
- package/dist/util/spinner.js +49 -0
- package/dist/util/terminal-output.d.ts +34 -0
- package/dist/util/terminal-output.js +1 -0
- package/dist/util/terminal-silent.d.ts +6 -0
- package/dist/util/terminal-silent.js +20 -0
- package/dist/util/terminal.d.ts +56 -0
- package/dist/util/terminal.js +124 -0
- package/dist/util/text-sanitize.d.ts +8 -0
- package/dist/util/text-sanitize.js +45 -0
- package/dist/util/thinking-spinner.d.ts +27 -0
- package/dist/util/thinking-spinner.js +189 -0
- package/dist/util/tool-error-logger.d.ts +33 -0
- package/dist/util/tool-error-logger.js +77 -0
- package/dist/util/tty.d.ts +13 -0
- package/dist/util/tty.js +16 -0
- package/dist/util/xcoder-paths.d.ts +56 -0
- package/dist/util/xcoder-paths.js +63 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +4 -0
- package/dist/worker-agent/to-subtask-node.d.ts +35 -0
- package/dist/worker-agent/to-subtask-node.js +41 -0
- package/dist/worker-agent/worker-orchestrator.d.ts +104 -0
- package/dist/worker-agent/worker-orchestrator.js +201 -0
- package/package.json +39 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Role } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The set of keys that can appear as a top-level section heading in
|
|
4
|
+
* `wks_protocol.md`. Every known `Role` is valid, plus `master-orchestrator`
|
|
5
|
+
* for the orchestrator's own protocol section.
|
|
6
|
+
*/
|
|
7
|
+
export type WksProtocolKey = Role | "master-orchestrator";
|
|
8
|
+
/**
|
|
9
|
+
* A map from protocol key to its section body text. Keys not present in the
|
|
10
|
+
* file are undefined; keys with an empty section produce an empty string.
|
|
11
|
+
*/
|
|
12
|
+
export type WksProtocolMap = Partial<Record<WksProtocolKey, string>>;
|
|
13
|
+
/**
|
|
14
|
+
* Parses the full text of `wks_protocol.md` and returns a map from
|
|
15
|
+
* section heading (Role name or `master-orchestrator`) to that section's
|
|
16
|
+
* verbatim body. Headings are identified by lines that match
|
|
17
|
+
* `# role_name:` (hash, space, identifier, colon). Everything between one
|
|
18
|
+
* heading and the next (or EOF) is the section body. Unknown headings are
|
|
19
|
+
* silently skipped. Trailing blank lines in each body are trimmed.
|
|
20
|
+
* An empty file or one with no recognised headings returns an empty map.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseWksProtocol(markdown: string): WksProtocolMap;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses the full text of `wks_protocol.md` and returns a map from
|
|
3
|
+
* section heading (Role name or `master-orchestrator`) to that section's
|
|
4
|
+
* verbatim body. Headings are identified by lines that match
|
|
5
|
+
* `# role_name:` (hash, space, identifier, colon). Everything between one
|
|
6
|
+
* heading and the next (or EOF) is the section body. Unknown headings are
|
|
7
|
+
* silently skipped. Trailing blank lines in each body are trimmed.
|
|
8
|
+
* An empty file or one with no recognised headings returns an empty map.
|
|
9
|
+
*/
|
|
10
|
+
export function parseWksProtocol(markdown) {
|
|
11
|
+
const map = {};
|
|
12
|
+
if (!markdown)
|
|
13
|
+
return map;
|
|
14
|
+
const lines = markdown.split("\n");
|
|
15
|
+
// Only lines matching exactly "# <key>:" are section boundaries.
|
|
16
|
+
const headingRegex = /^#\s+([a-z][a-z-]*):\s*$/;
|
|
17
|
+
let currentKey = null;
|
|
18
|
+
let currentBody = [];
|
|
19
|
+
function flushSection() {
|
|
20
|
+
if (currentKey) {
|
|
21
|
+
// Trim trailing blank lines.
|
|
22
|
+
while (currentBody.length && currentBody[currentBody.length - 1].trim() === "") {
|
|
23
|
+
currentBody.pop();
|
|
24
|
+
}
|
|
25
|
+
map[currentKey] = currentBody.join("\n");
|
|
26
|
+
}
|
|
27
|
+
currentBody = [];
|
|
28
|
+
}
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
const match = headingRegex.exec(line);
|
|
31
|
+
if (match) {
|
|
32
|
+
flushSection();
|
|
33
|
+
const key = match[1];
|
|
34
|
+
// Only accept recognised Role names or 'master-orchestrator'.
|
|
35
|
+
if (isRecognisedKey(key)) {
|
|
36
|
+
currentKey = key;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// Unknown heading — skip until the next recognised heading.
|
|
40
|
+
currentKey = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (currentKey) {
|
|
44
|
+
currentBody.push(line);
|
|
45
|
+
}
|
|
46
|
+
// If we don't have a currentKey, we skip the line (between unknown headings).
|
|
47
|
+
}
|
|
48
|
+
flushSection();
|
|
49
|
+
return map;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* All known Role values plus `master-orchestrator`. We inline this list
|
|
53
|
+
* rather than importing every directive so the protocol manager stays
|
|
54
|
+
* decoupled from the role registry — it just validates against a fixed
|
|
55
|
+
* set of strings that happen to match the Role union.
|
|
56
|
+
*/
|
|
57
|
+
const RECOGNISED_KEYS = new Set([
|
|
58
|
+
"requirements-analyst",
|
|
59
|
+
"architect",
|
|
60
|
+
"programmer",
|
|
61
|
+
"refactorer",
|
|
62
|
+
"tester",
|
|
63
|
+
"ui-api-tester",
|
|
64
|
+
"technical-writer",
|
|
65
|
+
"devops",
|
|
66
|
+
"conversation",
|
|
67
|
+
"business-analyst",
|
|
68
|
+
"master-orchestrator",
|
|
69
|
+
]);
|
|
70
|
+
function isRecognisedKey(key) {
|
|
71
|
+
return RECOGNISED_KEYS.has(key);
|
|
72
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TaskRun, TaskSpec } from "../types.js";
|
|
2
|
+
import type { WksProtocolMap } from "../protocol/wks-protocol-manager.js";
|
|
3
|
+
import type { OrchestratorOptions } from "./sdlc-orchestrator.js";
|
|
4
|
+
/**
|
|
5
|
+
* Lightweight single-subagent dispatch for conversation tasks.
|
|
6
|
+
* Returns the completed TaskRun.
|
|
7
|
+
*/
|
|
8
|
+
export declare function runConversation(spec: TaskSpec, opts: OrchestratorOptions, protocolMap: WksProtocolMap, masterProtocolSection: string | undefined): Promise<TaskRun>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { SubAgent } from "../agents/sub-agent.js";
|
|
2
|
+
import { saveTaskRun } from "../store/task-store.js";
|
|
3
|
+
import { emitXcoderEvent } from "../events/bus.js";
|
|
4
|
+
import * as term from "../util/terminal.js";
|
|
5
|
+
import { dagSnapshot } from "./dag-utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Lightweight single-subagent dispatch for conversation tasks.
|
|
8
|
+
* Returns the completed TaskRun.
|
|
9
|
+
*/
|
|
10
|
+
export async function runConversation(spec, opts, protocolMap, masterProtocolSection) {
|
|
11
|
+
term.taskIntro(spec.taskId, spec.goal);
|
|
12
|
+
emitXcoderEvent({ type: "task:intro", taskId: spec.taskId, data: { goal: spec.goal, conversation: true } });
|
|
13
|
+
const node = {
|
|
14
|
+
id: "conversation",
|
|
15
|
+
stage: "conversation",
|
|
16
|
+
role: "conversation",
|
|
17
|
+
engine: "react-v1",
|
|
18
|
+
dependsOn: [],
|
|
19
|
+
goal: spec.goal,
|
|
20
|
+
upstreamContext: {},
|
|
21
|
+
deliverableSpec: "A direct answer.",
|
|
22
|
+
acceptanceCriteria: [],
|
|
23
|
+
status: "ready",
|
|
24
|
+
healing: { attempts: 0, maxAttempts: 0, log: [] },
|
|
25
|
+
};
|
|
26
|
+
const agent = new SubAgent({
|
|
27
|
+
taskId: spec.taskId,
|
|
28
|
+
workspaceRoot: opts.workspaceRoot,
|
|
29
|
+
llm: opts.llm,
|
|
30
|
+
});
|
|
31
|
+
const result = await agent.run(node);
|
|
32
|
+
node.status = result.crashed ? "failed" : "done";
|
|
33
|
+
node.result = result;
|
|
34
|
+
const run = {
|
|
35
|
+
spec,
|
|
36
|
+
dag: [node],
|
|
37
|
+
status: result.crashed ? "escalated" : "done",
|
|
38
|
+
escalations: result.crashed
|
|
39
|
+
? [{ subtaskId: node.id, reason: result.summary }]
|
|
40
|
+
: [],
|
|
41
|
+
orchestratorTrace: [
|
|
42
|
+
{
|
|
43
|
+
thought: "classified as conversation — lightweight path, no plan",
|
|
44
|
+
action: null,
|
|
45
|
+
observation: result.summary,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
finalDeliverable: result.summary,
|
|
49
|
+
totalTokenUsage: result.tokenUsage,
|
|
50
|
+
};
|
|
51
|
+
await saveTaskRun(opts.workspaceRoot, run);
|
|
52
|
+
emitXcoderEvent({ type: "task:dag", taskId: spec.taskId, data: dagSnapshot(run.dag) });
|
|
53
|
+
term.taskOutro(run.status);
|
|
54
|
+
emitXcoderEvent({
|
|
55
|
+
type: "task:outro",
|
|
56
|
+
taskId: spec.taskId,
|
|
57
|
+
data: {
|
|
58
|
+
status: run.status,
|
|
59
|
+
finalDeliverable: run.finalDeliverable,
|
|
60
|
+
totalTokenUsage: run.totalTokenUsage,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return run;
|
|
64
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SubtaskNode } from "../types.js";
|
|
2
|
+
/** A UI-friendly projection of the DAG — just enough for a live status board, no full RAO trace duplicated here (that streams separately as agent:* events). */
|
|
3
|
+
export declare function dagSnapshot(dag: SubtaskNode[]): {
|
|
4
|
+
id: string;
|
|
5
|
+
stage: string;
|
|
6
|
+
role: string;
|
|
7
|
+
status: string;
|
|
8
|
+
healingAttempts: number;
|
|
9
|
+
healingMax: number;
|
|
10
|
+
reasoningScore?: number;
|
|
11
|
+
}[];
|
|
12
|
+
/** Frontier computation: a node is `ready` once every dependency is `done`. */
|
|
13
|
+
export declare function recomputeReady(dag: SubtaskNode[]): void;
|
|
14
|
+
/** Inlines a finished node's deliverable into every direct dependent — by value, never a lookup id. */
|
|
15
|
+
export declare function inlineIntoDependents(dag: SubtaskNode[], done: SubtaskNode): void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** A UI-friendly projection of the DAG — just enough for a live status board, no full RAO trace duplicated here (that streams separately as agent:* events). */
|
|
2
|
+
export function dagSnapshot(dag) {
|
|
3
|
+
return dag.map((n) => ({
|
|
4
|
+
id: n.id,
|
|
5
|
+
stage: n.stage,
|
|
6
|
+
role: n.role,
|
|
7
|
+
status: n.status,
|
|
8
|
+
healingAttempts: n.healing.attempts,
|
|
9
|
+
healingMax: n.healing.maxAttempts,
|
|
10
|
+
reasoningScore: n.reasoningScore?.score,
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
13
|
+
/** Frontier computation: a node is `ready` once every dependency is `done`. */
|
|
14
|
+
export function recomputeReady(dag) {
|
|
15
|
+
const byId = new Map(dag.map((n) => [n.id, n]));
|
|
16
|
+
for (const node of dag) {
|
|
17
|
+
if (node.status !== "pending")
|
|
18
|
+
continue;
|
|
19
|
+
const depsDone = node.dependsOn.every((d) => byId.get(d)?.status === "done");
|
|
20
|
+
if (depsDone)
|
|
21
|
+
node.status = "ready";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Inlines a finished node's deliverable into every direct dependent — by value, never a lookup id. */
|
|
25
|
+
export function inlineIntoDependents(dag, done) {
|
|
26
|
+
const filesTouched = done.result?.filesTouched ?? [];
|
|
27
|
+
const topLevelFolders = deriveTopLevelFolders(filesTouched);
|
|
28
|
+
for (const node of dag) {
|
|
29
|
+
if (node.dependsOn.includes(done.id)) {
|
|
30
|
+
node.upstreamContext[done.id] = {
|
|
31
|
+
summary: done.result?.summary,
|
|
32
|
+
deliverable: done.result?.deliverable,
|
|
33
|
+
filesTouched,
|
|
34
|
+
topLevelFolders,
|
|
35
|
+
};
|
|
36
|
+
// If a test node depends on a code/refactor node, inject folder-scoped
|
|
37
|
+
// test instructions into the goal so the tester runs only affected folders.
|
|
38
|
+
if (node.stage === "test" && topLevelFolders.length > 0) {
|
|
39
|
+
const scopeLine = buildFolderScopeLine(topLevelFolders);
|
|
40
|
+
if (!node.goal.includes("FOLDER_SCOPE:")) {
|
|
41
|
+
node.goal = `${scopeLine}\n\n${node.goal}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function deriveTopLevelFolders(paths) {
|
|
48
|
+
const folders = new Set();
|
|
49
|
+
for (const p of paths) {
|
|
50
|
+
// Normalize: strip leading ./ if present, then extract top-level folder under src/
|
|
51
|
+
let normalized = p.replace(/^\.\//, "");
|
|
52
|
+
const srcIdx = normalized.indexOf("src/");
|
|
53
|
+
if (srcIdx === 0 || (srcIdx > 0 && normalized[srcIdx - 1] === "/")) {
|
|
54
|
+
const afterSrc = normalized.slice(srcIdx + 4); // length of "src/"
|
|
55
|
+
const topFolder = afterSrc.split("/")[0];
|
|
56
|
+
if (topFolder)
|
|
57
|
+
folders.add(topFolder);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return [...folders].sort();
|
|
61
|
+
}
|
|
62
|
+
function buildFolderScopeLine(folders) {
|
|
63
|
+
const vitestArgs = folders
|
|
64
|
+
.map((f) => `"src/${f}/**/*.test.ts"`)
|
|
65
|
+
.join(" ");
|
|
66
|
+
return `FOLDER_SCOPE: Run tests only for these top-level src/ folders: ${folders.join(", ")}. Use npx vitest with per-folder file paths: npx vitest run --config vitest.config.ts ${vitestArgs}. Per-folder timeout 60s.`;
|
|
67
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SubtaskNode, SubtaskResult, TaskRun } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Post-validation defect management:
|
|
4
|
+
* - After test/ui_api_test: scans for new defect RCA files, injects fix_defect nodes
|
|
5
|
+
* - After fix_defect: checks _defects.json status, resolves defect if passed
|
|
6
|
+
*/
|
|
7
|
+
export declare function integrateDefectManagement(dag: SubtaskNode[], run: TaskRun, node: SubtaskNode, result: SubtaskResult, workspaceRoot: string): Promise<void>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { scanForDefects, classifyDefectRouting, buildFixDefectNode, resolveDefect, recordFixAttempt, } from "../orchestrator/defect-manager.js";
|
|
2
|
+
import { loadDefectsIndex, upsertDefect } from "../orchestrator/defect-store.js";
|
|
3
|
+
import { logDefectEvent } from "../orchestrator/defect-logger.js";
|
|
4
|
+
import * as term from "../util/terminal.js";
|
|
5
|
+
/**
|
|
6
|
+
* Post-validation defect management:
|
|
7
|
+
* - After test/ui_api_test: scans for new defect RCA files, injects fix_defect nodes
|
|
8
|
+
* - After fix_defect: checks _defects.json status, resolves defect if passed
|
|
9
|
+
*/
|
|
10
|
+
export async function integrateDefectManagement(dag, run, node, result, workspaceRoot) {
|
|
11
|
+
// ── fix_defect resolution check (status-driven) ────────────
|
|
12
|
+
if (node.stage === "fix_defect") {
|
|
13
|
+
const defectCtx = node.upstreamContext?.defect;
|
|
14
|
+
if (defectCtx?.defectId) {
|
|
15
|
+
const index = await loadDefectsIndex(workspaceRoot);
|
|
16
|
+
const record = index[defectCtx.defectId];
|
|
17
|
+
if (record && record.status === "in_progress") {
|
|
18
|
+
await resolveDefect(workspaceRoot, defectCtx.defectId, node.id);
|
|
19
|
+
run.orchestratorTrace.push({
|
|
20
|
+
thought: `${node.id} resolved defect ${defectCtx.defectId}`,
|
|
21
|
+
action: null,
|
|
22
|
+
observation: "defect status updated to resolved — RCA archived",
|
|
23
|
+
});
|
|
24
|
+
term.orchestratorNote(`${node.id} resolved defect ${defectCtx.defectId}`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (record && record.status !== "in_progress") {
|
|
28
|
+
run.orchestratorTrace.push({
|
|
29
|
+
thought: `${node.id} fix_defect passed but defect ${defectCtx.defectId} is already ${record.status}`,
|
|
30
|
+
action: null,
|
|
31
|
+
observation: `defect status: ${record.status}`,
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Defect not in index at all — treat as unresolved.
|
|
36
|
+
const unresolvedReason = `Defect ${defectCtx.defectId} not found in _defects.json after fix_defect — unable to verify resolution.`;
|
|
37
|
+
run.orchestratorTrace.push({
|
|
38
|
+
thought: `${node.id} marked done but defect ${defectCtx.defectId} missing from index — forcing healing`,
|
|
39
|
+
action: null,
|
|
40
|
+
observation: unresolvedReason,
|
|
41
|
+
});
|
|
42
|
+
term.orchestratorNote(`${node.id} defect missing from index, re-routing to healing`);
|
|
43
|
+
return; // caller handles the healer re-route
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// ── New defect discovery ────────────────────────────────────
|
|
48
|
+
if (node.stage === "test" || node.stage === "ui_api_test") {
|
|
49
|
+
const index = await loadDefectsIndex(workspaceRoot);
|
|
50
|
+
const newDefects = await scanForDefects(workspaceRoot, index);
|
|
51
|
+
if (newDefects.length === 0)
|
|
52
|
+
return;
|
|
53
|
+
const upstreamStages = new Set();
|
|
54
|
+
for (const depId of node.dependsOn) {
|
|
55
|
+
const dep = dag.find((n) => n.id === depId);
|
|
56
|
+
if (dep)
|
|
57
|
+
upstreamStages.add(dep.stage);
|
|
58
|
+
}
|
|
59
|
+
const primaryUpstreamStage = upstreamStages.has("code")
|
|
60
|
+
? "code"
|
|
61
|
+
: upstreamStages.has("refactor")
|
|
62
|
+
? "refactor"
|
|
63
|
+
: undefined;
|
|
64
|
+
const addNodes = [];
|
|
65
|
+
for (const defect of newDefects) {
|
|
66
|
+
const role = classifyDefectRouting(primaryUpstreamStage);
|
|
67
|
+
const fixNode = buildFixDefectNode(defect, role, run.spec, [node.id]);
|
|
68
|
+
addNodes.push(fixNode);
|
|
69
|
+
const now = new Date().toISOString();
|
|
70
|
+
await upsertDefect(workspaceRoot, {
|
|
71
|
+
id: defect.id,
|
|
72
|
+
path: defect.path,
|
|
73
|
+
status: "open",
|
|
74
|
+
discoveredAt: now,
|
|
75
|
+
lastUpdated: now,
|
|
76
|
+
severity: defect.severity,
|
|
77
|
+
fixAttempts: [],
|
|
78
|
+
});
|
|
79
|
+
await logDefectEvent(workspaceRoot, {
|
|
80
|
+
timestamp: now,
|
|
81
|
+
defectId: defect.id,
|
|
82
|
+
event: "discovered",
|
|
83
|
+
detail: `New defect discovered in ${defect.path}`,
|
|
84
|
+
metadata: { path: defect.path, severity: defect.severity },
|
|
85
|
+
});
|
|
86
|
+
await recordFixAttempt(workspaceRoot, defect.id, fixNode.id, 1);
|
|
87
|
+
}
|
|
88
|
+
dag.push(...addNodes);
|
|
89
|
+
run.orchestratorTrace.push({
|
|
90
|
+
thought: `injected ${addNodes.length} fix_defect node(s) for newly discovered defects`,
|
|
91
|
+
action: {
|
|
92
|
+
tool: "orchestrator_action",
|
|
93
|
+
input: { type: "replan", patch: { addNodes: addNodes.map((n) => n.id) } },
|
|
94
|
+
},
|
|
95
|
+
observation: `New defects: ${newDefects.map((d) => d.path).join(", ")}`,
|
|
96
|
+
});
|
|
97
|
+
term.orchestratorNote(`${addNodes.length} defect(s) discovered after ${node.id}, injecting fix_defect node(s): ${addNodes.map((n) => n.id).join(", ")}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { SubtaskNode, SubtaskResult, TaskRun } from "../types.js";
|
|
2
|
+
import type { WksProtocolMap } from "../protocol/wks-protocol-manager.js";
|
|
3
|
+
import type { OrchestratorOptions } from "./sdlc-orchestrator.js";
|
|
4
|
+
import { type ValidationOutcome } from "../orchestrator/validation-gate.js";
|
|
5
|
+
import type { InteractiveSteeringManager } from "../orchestrator/interactive-steering.js";
|
|
6
|
+
/**
|
|
7
|
+
* Shared context object passed through the loop to avoid parameter explosion.
|
|
8
|
+
* Callbacks replace methods that would have been on the orchestrator class.
|
|
9
|
+
*/
|
|
10
|
+
export interface OrchestrationContext {
|
|
11
|
+
opts: OrchestratorOptions;
|
|
12
|
+
protocolMap: WksProtocolMap;
|
|
13
|
+
masterProtocolSection: string | undefined;
|
|
14
|
+
lessonContext: string | undefined;
|
|
15
|
+
signal: AbortSignal;
|
|
16
|
+
steering: InteractiveSteeringManager;
|
|
17
|
+
quiet: boolean;
|
|
18
|
+
/** Called per round and per result to check for steering input — returns true if injection was applied. */
|
|
19
|
+
onSteeringInjection: (run: TaskRun) => Promise<boolean>;
|
|
20
|
+
/** Called after each round to persist progress. */
|
|
21
|
+
onSaveProgress: (run: TaskRun) => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The main orchestration loop — rounds of dispatching, result collection,
|
|
25
|
+
* and processing until the DAG is complete, escalated, or halted.
|
|
26
|
+
* Includes the abort signal polling and steering check integration.
|
|
27
|
+
*/
|
|
28
|
+
export declare function executeLoop(run: TaskRun, ctx: OrchestrationContext): Promise<TaskRun>;
|
|
29
|
+
/**
|
|
30
|
+
* Processes a single subtask result:
|
|
31
|
+
* - Logs the trace entry
|
|
32
|
+
* - Computes reasoning score
|
|
33
|
+
* - Handles crashes, iteration exhaustion, and validation routing
|
|
34
|
+
* - Dispatches success or failure routes
|
|
35
|
+
*/
|
|
36
|
+
export declare function handleResult(dag: SubtaskNode[], run: TaskRun, subtaskId: string, result: SubtaskResult, ctx: OrchestrationContext): Promise<void>;
|
|
37
|
+
/** Shared by both a validation failure and a crash — same bounded healing pipeline either way. */
|
|
38
|
+
export declare function routeFailure(dag: SubtaskNode[], run: TaskRun, node: SubtaskNode, result: SubtaskResult | null, outcome: ValidationOutcome, reason: string, ctx: OrchestrationContext): Promise<void>;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { validateSubtask } from "../orchestrator/validation-gate.js";
|
|
2
|
+
import { decideHealing } from "../orchestrator/healing.js";
|
|
3
|
+
import { dispatchSwarm } from "../orchestrator/swarm.js";
|
|
4
|
+
import { getOrchestratorEngine } from "../engine/reasoning-engine.js";
|
|
5
|
+
import { appendSys, appendScore } from "../util/logs.js";
|
|
6
|
+
import { computeReasoningScore, efficiencyHint } from "../orchestrator/reasoning-score.js";
|
|
7
|
+
import { emitXcoderEvent } from "../events/bus.js";
|
|
8
|
+
import { loadDefectsIndex, upsertDefect } from "../orchestrator/defect-store.js";
|
|
9
|
+
import { logDefectEvent } from "../orchestrator/defect-logger.js";
|
|
10
|
+
import * as term from "../util/terminal.js";
|
|
11
|
+
import { FooterManager } from "../util/footer-manager.js";
|
|
12
|
+
import { recomputeReady, inlineIntoDependents } from "./dag-utils.js";
|
|
13
|
+
import { summarize, buildExhaustionTrace } from "./reporting.js";
|
|
14
|
+
import { recordTaskLesson } from "./lesson-recorder.js";
|
|
15
|
+
import { integrateDefectManagement } from "./defect-integration.js";
|
|
16
|
+
/**
|
|
17
|
+
* The main orchestration loop — rounds of dispatching, result collection,
|
|
18
|
+
* and processing until the DAG is complete, escalated, or halted.
|
|
19
|
+
* Includes the abort signal polling and steering check integration.
|
|
20
|
+
*/
|
|
21
|
+
export async function executeLoop(run, ctx) {
|
|
22
|
+
const dag = run.dag;
|
|
23
|
+
const taskId = run.spec.taskId;
|
|
24
|
+
const maxRounds = ctx.opts.maxRounds ?? 40;
|
|
25
|
+
let round = 0;
|
|
26
|
+
while (round++ < maxRounds) {
|
|
27
|
+
recomputeReady(dag);
|
|
28
|
+
const ready = dag.filter((n) => n.status === "ready");
|
|
29
|
+
if (ready.length === 0) {
|
|
30
|
+
const stillPending = dag.some((n) => n.status === "pending" || n.status === "healing");
|
|
31
|
+
if (!stillPending)
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
const healingHints = {};
|
|
35
|
+
for (const n of dag) {
|
|
36
|
+
if (n.status === "ready" && n.healing.attempts > 0) {
|
|
37
|
+
healingHints[n.id] = n.healing.log.at(-1) ?? "";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const swarmResults = await dispatchSwarm(ready, {
|
|
42
|
+
taskId,
|
|
43
|
+
workspaceRoot: ctx.opts.workspaceRoot,
|
|
44
|
+
llm: ctx.opts.llm,
|
|
45
|
+
subAgentLlm: ctx.opts.subAgentLlm ?? ctx.opts.roleLlm?.swarm_agent ?? ctx.opts.llm,
|
|
46
|
+
concurrency: ctx.opts.concurrency,
|
|
47
|
+
healingHints,
|
|
48
|
+
protocolOverrides: ctx.protocolMap,
|
|
49
|
+
lessonLearned: ctx.opts.lessonLearned,
|
|
50
|
+
lessonContext: ctx.lessonContext,
|
|
51
|
+
signal: ctx.signal,
|
|
52
|
+
quiet: ctx.quiet,
|
|
53
|
+
});
|
|
54
|
+
for (const { subtaskId, result } of swarmResults) {
|
|
55
|
+
run.totalTokenUsage.inputTokens += result.tokenUsage.inputTokens;
|
|
56
|
+
run.totalTokenUsage.outputTokens += result.tokenUsage.outputTokens;
|
|
57
|
+
await handleResult(dag, run, subtaskId, result, ctx);
|
|
58
|
+
// Derive current SDLC stage from DAG and update footer.
|
|
59
|
+
updateFooterStage(dag);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
const message = e.stack ?? String(e);
|
|
64
|
+
await appendSys(ctx.opts.workspaceRoot, taskId, `round ${round} failed: ${message}`);
|
|
65
|
+
run.orchestratorTrace.push({
|
|
66
|
+
thought: `round ${round} failed unexpectedly`,
|
|
67
|
+
action: null,
|
|
68
|
+
observation: e.message,
|
|
69
|
+
});
|
|
70
|
+
term.printCrash("orchestrator", `round ${round} failed, continuing: ${e.message}`);
|
|
71
|
+
}
|
|
72
|
+
await ctx.onSaveProgress(run);
|
|
73
|
+
// Check abort signal between rounds.
|
|
74
|
+
if (ctx.signal.aborted) {
|
|
75
|
+
run.status = "halted";
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
// Interactive steering check.
|
|
79
|
+
const injected = await ctx.onSteeringInjection(run);
|
|
80
|
+
if (injected) {
|
|
81
|
+
await ctx.onSaveProgress(run);
|
|
82
|
+
}
|
|
83
|
+
if (run.status === "halted")
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
const allDone = dag.every((n) => n.status === "done");
|
|
87
|
+
run.status = allDone ? "done" : run.escalations.length ? "escalated" : run.status;
|
|
88
|
+
run.finalDeliverable = summarize(dag, run, run.spec, ctx.opts.workspaceRoot);
|
|
89
|
+
await ctx.onSaveProgress(run);
|
|
90
|
+
FooterManager.instance()?.shutdown();
|
|
91
|
+
term.taskOutro(run.status);
|
|
92
|
+
emitXcoderEvent({
|
|
93
|
+
type: "task:outro",
|
|
94
|
+
taskId,
|
|
95
|
+
data: {
|
|
96
|
+
status: run.status,
|
|
97
|
+
finalDeliverable: run.finalDeliverable,
|
|
98
|
+
totalTokenUsage: run.totalTokenUsage,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
return run;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Processes a single subtask result:
|
|
105
|
+
* - Logs the trace entry
|
|
106
|
+
* - Computes reasoning score
|
|
107
|
+
* - Handles crashes, iteration exhaustion, and validation routing
|
|
108
|
+
* - Dispatches success or failure routes
|
|
109
|
+
*/
|
|
110
|
+
export async function handleResult(dag, run, subtaskId, result, ctx) {
|
|
111
|
+
const node = dag.find((n) => n.id === subtaskId);
|
|
112
|
+
node.status = "validating";
|
|
113
|
+
node.result = result;
|
|
114
|
+
if (result.crashed) {
|
|
115
|
+
run.orchestratorTrace.push({
|
|
116
|
+
thought: `${node.id} crashed rather than producing a result`,
|
|
117
|
+
action: null,
|
|
118
|
+
observation: result.summary,
|
|
119
|
+
});
|
|
120
|
+
const syntheticOutcome = {
|
|
121
|
+
passed: false,
|
|
122
|
+
report: result.summary,
|
|
123
|
+
perCriterion: [],
|
|
124
|
+
};
|
|
125
|
+
await routeFailure(dag, run, node, result, syntheticOutcome, `crashed: ${result.summary}`, ctx);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// ── Iteration exhaustion signal ─────────────────────────────
|
|
129
|
+
if (result.iterationsExhausted === true) {
|
|
130
|
+
const ex = buildExhaustionTrace(node.id, run.spec.taskId, result);
|
|
131
|
+
run.orchestratorTrace.push(ex.trace);
|
|
132
|
+
emitXcoderEvent(ex.event);
|
|
133
|
+
const index = await loadDefectsIndex(ctx.opts.workspaceRoot);
|
|
134
|
+
if (!(ex.defectId in index)) {
|
|
135
|
+
const now = new Date().toISOString();
|
|
136
|
+
await upsertDefect(ctx.opts.workspaceRoot, {
|
|
137
|
+
id: ex.defectId,
|
|
138
|
+
path: `.xcoder/exhaustion/${node.id}.md`,
|
|
139
|
+
status: "open",
|
|
140
|
+
severity: "high",
|
|
141
|
+
discoveredAt: now,
|
|
142
|
+
lastUpdated: now,
|
|
143
|
+
fixAttempts: [],
|
|
144
|
+
});
|
|
145
|
+
await logDefectEvent(ctx.opts.workspaceRoot, {
|
|
146
|
+
defectId: ex.defectId,
|
|
147
|
+
event: "discovered",
|
|
148
|
+
detail: "SubAgent exhausted iterations without final_answer",
|
|
149
|
+
timestamp: now,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const outcome = await validateSubtask(node, result, ctx.opts.workspaceRoot);
|
|
154
|
+
run.orchestratorTrace.push({
|
|
155
|
+
thought: `validating ${node.id} against its own acceptance criteria (not the overall task goal)`,
|
|
156
|
+
action: null,
|
|
157
|
+
observation: outcome.report,
|
|
158
|
+
});
|
|
159
|
+
if (outcome.passed) {
|
|
160
|
+
node.status = "done";
|
|
161
|
+
await recordScore(run, node, result, outcome, ctx.opts.workspaceRoot);
|
|
162
|
+
inlineIntoDependents(dag, node);
|
|
163
|
+
await integrateDefectManagement(dag, run, node, result, ctx.opts.workspaceRoot);
|
|
164
|
+
const engine = getOrchestratorEngine("react-v1");
|
|
165
|
+
const decision = await engine.decide({ dag, triggeringEvent: "subtask_done" });
|
|
166
|
+
run.orchestratorTrace.push({
|
|
167
|
+
thought: `${node.id} passed validation`,
|
|
168
|
+
action: { tool: "orchestrator_action", input: decision },
|
|
169
|
+
observation: "dispatch recomputed on next round",
|
|
170
|
+
});
|
|
171
|
+
term.orchestratorNote(`${node.id} done`);
|
|
172
|
+
// Record lesson if applicable.
|
|
173
|
+
try {
|
|
174
|
+
await recordTaskLesson(ctx.opts.workspaceRoot, result, run, node.id);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// lesson recording is best-effort
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
await routeFailure(dag, run, node, result, outcome, outcome.report, ctx);
|
|
182
|
+
}
|
|
183
|
+
/** Computes, attaches, logs, and broadcasts the reasoning-effectiveness score. */
|
|
184
|
+
async function recordScore(run, node, result, outcome, workspaceRoot) {
|
|
185
|
+
const reasoningScore = computeReasoningScore(node, result, outcome);
|
|
186
|
+
node.reasoningScore = reasoningScore;
|
|
187
|
+
await appendScore(workspaceRoot, run.spec.taskId, node.id, reasoningScore);
|
|
188
|
+
emitXcoderEvent({
|
|
189
|
+
type: "agent:score",
|
|
190
|
+
taskId: run.spec.taskId,
|
|
191
|
+
subtaskId: node.id,
|
|
192
|
+
data: reasoningScore,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/** Shared by both a validation failure and a crash — same bounded healing pipeline either way. */
|
|
196
|
+
export async function routeFailure(dag, run, node, result, outcome, reason, ctx) {
|
|
197
|
+
if (result) {
|
|
198
|
+
await recordScore(run, node, result, outcome, ctx.opts.workspaceRoot);
|
|
199
|
+
}
|
|
200
|
+
const efficiency = result ? efficiencyHint(result) : undefined;
|
|
201
|
+
const filesNote = result && result.filesTouched.length > 0
|
|
202
|
+
? `IMPORTANT: the previous attempt already created ${result.filesTouched.length} file(s) before failing: ` +
|
|
203
|
+
`${result.filesTouched.slice(0, 25).join(", ")}${result.filesTouched.length > 25 ? ", ..." : ""}. ` +
|
|
204
|
+
`Check what already exists (search_workspace/glob/read_file) before writing — extend or fix it, don't recreate it from scratch.`
|
|
205
|
+
: null;
|
|
206
|
+
const reasonWithEfficiency = [reason, efficiency, filesNote]
|
|
207
|
+
.filter(Boolean)
|
|
208
|
+
.join(" Also: ");
|
|
209
|
+
node.status = "healing";
|
|
210
|
+
const healing = decideHealing(node, reasonWithEfficiency);
|
|
211
|
+
if (healing.action === "escalate") {
|
|
212
|
+
node.status = "escalated";
|
|
213
|
+
run.escalations.push({ subtaskId: node.id, reason });
|
|
214
|
+
run.orchestratorTrace.push({
|
|
215
|
+
thought: `${node.id} exhausted healing attempts`,
|
|
216
|
+
action: { tool: "orchestrator_action", input: { type: "escalate" } },
|
|
217
|
+
observation: reason,
|
|
218
|
+
});
|
|
219
|
+
term.printCrash(node.id, `escalated: ${reason}`);
|
|
220
|
+
if (node.upstreamContext?.defect != null) {
|
|
221
|
+
run.status = "halted";
|
|
222
|
+
run.orchestratorTrace.push({
|
|
223
|
+
thought: `unresolved defect escalation halted the task run (${node.id})`,
|
|
224
|
+
action: null,
|
|
225
|
+
observation: `Defect: ${JSON.stringify(node.upstreamContext.defect)}`,
|
|
226
|
+
});
|
|
227
|
+
term.orchestratorNote(`${node.id} defect escalation halted the run: ${reason}`);
|
|
228
|
+
}
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
node.status = "ready";
|
|
232
|
+
run.orchestratorTrace.push({
|
|
233
|
+
thought: `retrying ${node.id}, forcing react-v1 healing per design (a specific deviation is an observe→act→observe problem)`,
|
|
234
|
+
action: { tool: "orchestrator_action", input: { type: "retry", hint: healing.hint } },
|
|
235
|
+
observation: `attempt ${node.healing.attempts}/${node.healing.maxAttempts}`,
|
|
236
|
+
});
|
|
237
|
+
term.orchestratorNote(`${node.id} healing (attempt ${node.healing.attempts}/${node.healing.maxAttempts})`);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Derive the current SDLC stage from the DAG by finding the most common
|
|
241
|
+
* stage among in-progress/validating/ready nodes, falling back to the
|
|
242
|
+
* stage of the last done node, then falling back to null.
|
|
243
|
+
*/
|
|
244
|
+
function updateFooterStage(dag) {
|
|
245
|
+
const active = dag.filter((n) => n.status === "in_progress" || n.status === "validating" || n.status === "ready");
|
|
246
|
+
if (active.length > 0) {
|
|
247
|
+
// Pick the most common stage among active nodes.
|
|
248
|
+
const counts = new Map();
|
|
249
|
+
for (const n of active) {
|
|
250
|
+
counts.set(n.stage, (counts.get(n.stage) ?? 0) + 1);
|
|
251
|
+
}
|
|
252
|
+
let bestStage = null;
|
|
253
|
+
let bestCount = 0;
|
|
254
|
+
for (const [stage, count] of counts) {
|
|
255
|
+
if (count > bestCount) {
|
|
256
|
+
bestCount = count;
|
|
257
|
+
bestStage = stage;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (bestStage) {
|
|
261
|
+
FooterManager.instance()?.updateStage(bestStage);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Fallback: last done node's stage.
|
|
266
|
+
const doneNodes = dag.filter((n) => n.status === "done");
|
|
267
|
+
if (doneNodes.length > 0) {
|
|
268
|
+
const lastDone = doneNodes[doneNodes.length - 1];
|
|
269
|
+
FooterManager.instance()?.updateStage(lastDone.stage);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// No activity yet — clear stage.
|
|
273
|
+
FooterManager.instance()?.updateStage(dag[0]?.stage ?? "requirements");
|
|
274
|
+
}
|