xcoder-dna 0.0.0-stage → 1.3.1
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 +21 -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 +38 -3
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve whether lesson-learned is enabled from CLI option + env var.
|
|
6
|
+
* CLI flag "--lesson-learned" → true; "--no-lesson-learned" → false.
|
|
7
|
+
* If neither is passed, check XCODER_LESSON_LEARNED ("true" → true).
|
|
8
|
+
* Default: false.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveLessonFlag(cliFlag, env) {
|
|
11
|
+
if (cliFlag === true)
|
|
12
|
+
return true;
|
|
13
|
+
if (cliFlag === false)
|
|
14
|
+
return false;
|
|
15
|
+
const envVal = env.XCODER_LESSON_LEARNED;
|
|
16
|
+
if (envVal !== undefined)
|
|
17
|
+
return envVal.toLowerCase() === "true";
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const LESSON_FILE = ".xcoder/lesson.md";
|
|
21
|
+
const MAX_FILE_SIZE = 64 * 1024; // 64 KB
|
|
22
|
+
/**
|
|
23
|
+
* Read the full contents of .xcoder/lesson.md.
|
|
24
|
+
* Returns the raw Markdown string (empty string if the file doesn't exist).
|
|
25
|
+
*/
|
|
26
|
+
export async function loadLessons(workspaceRoot) {
|
|
27
|
+
const filePath = path.join(workspaceRoot, LESSON_FILE);
|
|
28
|
+
try {
|
|
29
|
+
return await fs.readFile(filePath, "utf8");
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
const code = e.code;
|
|
33
|
+
if (code === "ENOENT")
|
|
34
|
+
return "";
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if the lessons file exceeds 64 KB.
|
|
40
|
+
* Returns a warning string if it does, or null if it's within bounds.
|
|
41
|
+
*/
|
|
42
|
+
export async function checkLessonFileSize(workspaceRoot) {
|
|
43
|
+
const filePath = path.join(workspaceRoot, LESSON_FILE);
|
|
44
|
+
try {
|
|
45
|
+
const stat = await fs.stat(filePath);
|
|
46
|
+
if (stat.size > MAX_FILE_SIZE) {
|
|
47
|
+
const kb = Math.round(stat.size / 1024);
|
|
48
|
+
return (`.xcoder/lesson.md is ${kb} KB (limit: 64 KB). ` +
|
|
49
|
+
`Lessons beyond 64 KB are NOT injected into Sub Agent prompts. ` +
|
|
50
|
+
`Consider pruning older entries manually.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// File doesn't exist — nothing to warn about.
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Record a lesson into .xcoder/lesson.md with an atomic write.
|
|
60
|
+
* Uses temp-file + rename for concurrency safety.
|
|
61
|
+
* Creates .xcoder/ dir and lesson.md on first write.
|
|
62
|
+
* Non-fatal: if the write fails, the caller logs a warning and continues.
|
|
63
|
+
*/
|
|
64
|
+
export async function recordLesson(workspaceRoot, entry) {
|
|
65
|
+
const dir = path.join(workspaceRoot, ".xcoder");
|
|
66
|
+
await fs.mkdir(dir, { recursive: true });
|
|
67
|
+
const filePath = path.join(workspaceRoot, LESSON_FILE);
|
|
68
|
+
const ts = new Date().toISOString();
|
|
69
|
+
const tagsFormatted = `[${entry.tags.join(", ")}]`;
|
|
70
|
+
const block = [
|
|
71
|
+
`## Lesson — ${ts}`,
|
|
72
|
+
`- **source:** \`${entry.source}\``,
|
|
73
|
+
`- **taskId:** \`${entry.taskId}\``,
|
|
74
|
+
`- **tags:** \`${tagsFormatted}\``,
|
|
75
|
+
`- **context:** ${entry.context}`,
|
|
76
|
+
`- **lesson:** ${entry.lesson}`,
|
|
77
|
+
"",
|
|
78
|
+
].join("\n");
|
|
79
|
+
let oldContent = "";
|
|
80
|
+
try {
|
|
81
|
+
oldContent = await fs.readFile(filePath, "utf8");
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
const code = e.code;
|
|
85
|
+
if (code !== "ENOENT")
|
|
86
|
+
throw e;
|
|
87
|
+
}
|
|
88
|
+
const newContent = oldContent ? `${oldContent}${block}` : block;
|
|
89
|
+
const tmpPath = `${filePath}.tmp-${randomUUID()}`;
|
|
90
|
+
try {
|
|
91
|
+
await fs.writeFile(tmpPath, newContent, "utf8");
|
|
92
|
+
await fs.rename(tmpPath, filePath);
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
// If rename fails (e.g. another process wrote concurrently),
|
|
96
|
+
// retry once: re-read current content and try again.
|
|
97
|
+
try {
|
|
98
|
+
let currentContent = "";
|
|
99
|
+
try {
|
|
100
|
+
currentContent = await fs.readFile(filePath, "utf8");
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// file might have been deleted — that's fine
|
|
104
|
+
}
|
|
105
|
+
const retryContent = currentContent ? `${currentContent}${block}` : block;
|
|
106
|
+
const retryTmp = `${filePath}.tmp-${randomUUID()}`;
|
|
107
|
+
await fs.writeFile(retryTmp, retryContent, "utf8");
|
|
108
|
+
await fs.rename(retryTmp, filePath);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Swallow — never crash the orchestrator on a lesson write failure.
|
|
112
|
+
// The lesson is lost, but the run continues.
|
|
113
|
+
}
|
|
114
|
+
// Clean up the original temp file if it still exists.
|
|
115
|
+
try {
|
|
116
|
+
await fs.unlink(tmpPath);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Best effort.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TaskRun } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Written to `.xcoder/tasks/<taskId>.md` at every terminal state (done,
|
|
4
|
+
* escalated, or halted) and refreshed again after a resume. Two audiences:
|
|
5
|
+
*
|
|
6
|
+
* 1. A human re-opening this task later — a readable "what happened"
|
|
7
|
+
* without digging through think.log/sys.log/score.log by hand.
|
|
8
|
+
* 2. A resumed Sub Agent — its `healing.log` (persisted per-node) already
|
|
9
|
+
* carries what it needs to continue without re-discovery, but this
|
|
10
|
+
* file is what `--resume` points a human at, and its file-list section
|
|
11
|
+
* is exactly the "don't re-query every file to understand everything"
|
|
12
|
+
* ask: a resumed subtask's healing hint references it directly rather
|
|
13
|
+
* than making the model glob the whole workspace again.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildProgressSummaryMarkdown(run: TaskRun): string;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Written to `.xcoder/tasks/<taskId>.md` at every terminal state (done,
|
|
3
|
+
* escalated, or halted) and refreshed again after a resume. Two audiences:
|
|
4
|
+
*
|
|
5
|
+
* 1. A human re-opening this task later — a readable "what happened"
|
|
6
|
+
* without digging through think.log/sys.log/score.log by hand.
|
|
7
|
+
* 2. A resumed Sub Agent — its `healing.log` (persisted per-node) already
|
|
8
|
+
* carries what it needs to continue without re-discovery, but this
|
|
9
|
+
* file is what `--resume` points a human at, and its file-list section
|
|
10
|
+
* is exactly the "don't re-query every file to understand everything"
|
|
11
|
+
* ask: a resumed subtask's healing hint references it directly rather
|
|
12
|
+
* than making the model glob the whole workspace again.
|
|
13
|
+
*/
|
|
14
|
+
export function buildProgressSummaryMarkdown(run) {
|
|
15
|
+
const { spec, dag, status, escalations, totalTokenUsage } = run;
|
|
16
|
+
const lines = [];
|
|
17
|
+
lines.push(`# Progress Summary — ${spec.taskId}`);
|
|
18
|
+
lines.push("");
|
|
19
|
+
lines.push(`**Goal:** ${spec.goal}`);
|
|
20
|
+
lines.push(`**Status:** ${status}`);
|
|
21
|
+
lines.push(`**Last updated:** ${new Date().toISOString()}`);
|
|
22
|
+
lines.push(`**Tokens spent so far:** ${totalTokenUsage.inputTokens} in / ${totalTokenUsage.outputTokens} out`);
|
|
23
|
+
lines.push("");
|
|
24
|
+
lines.push("## Plan");
|
|
25
|
+
for (const n of dag) {
|
|
26
|
+
const box = n.status === "done" ? "[x]" : "[ ]";
|
|
27
|
+
const healingNote = n.healing.attempts > 0 ? ` (healing ${n.healing.attempts}/${n.healing.maxAttempts})` : "";
|
|
28
|
+
lines.push(`- ${box} \`${n.id}\` (${n.role}) — ${n.status}${healingNote}`);
|
|
29
|
+
}
|
|
30
|
+
lines.push("");
|
|
31
|
+
const done = dag.filter((n) => n.status === "done");
|
|
32
|
+
if (done.length) {
|
|
33
|
+
lines.push("## What's already done");
|
|
34
|
+
for (const n of done) {
|
|
35
|
+
lines.push(`### \`${n.id}\` (${n.role}) — done`);
|
|
36
|
+
lines.push(n.result?.summary ?? "(no summary recorded)");
|
|
37
|
+
lines.push("");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const allFiles = new Set();
|
|
41
|
+
for (const n of dag) {
|
|
42
|
+
for (const f of n.result?.filesTouched ?? [])
|
|
43
|
+
allFiles.add(f);
|
|
44
|
+
}
|
|
45
|
+
if (allFiles.size) {
|
|
46
|
+
lines.push("## Files created or modified (by subtask)");
|
|
47
|
+
lines.push("A resumed subtask should check these with `read_file`/`glob` before writing anything new — " +
|
|
48
|
+
"most of what's needed to continue is already in this list, not in the original design doc.");
|
|
49
|
+
for (const n of dag) {
|
|
50
|
+
const meta = n.result?._filesTouchedMeta;
|
|
51
|
+
const files = n.result?.filesTouched;
|
|
52
|
+
if ((!meta || meta.length === 0) && (!files || files.length === 0))
|
|
53
|
+
continue;
|
|
54
|
+
lines.push(`### \`${n.id}\` (${n.role})`);
|
|
55
|
+
if (meta && meta.length > 0) {
|
|
56
|
+
for (const entry of meta) {
|
|
57
|
+
const label = entry.operation === "created" ? "[created]" : entry.operation === "modified" ? "[modified]" : "[touched]";
|
|
58
|
+
lines.push(`- \`${entry.path}\` ${label}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (files) {
|
|
62
|
+
for (const f of [...files].sort()) {
|
|
63
|
+
lines.push(`- \`${f}\` [touched]`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
lines.push("");
|
|
67
|
+
}
|
|
68
|
+
lines.push("");
|
|
69
|
+
}
|
|
70
|
+
const blocked = dag.filter((n) => n.status === "escalated" || n.status === "failed");
|
|
71
|
+
if (blocked.length) {
|
|
72
|
+
lines.push("## What's blocking");
|
|
73
|
+
for (const n of blocked) {
|
|
74
|
+
const esc = escalations.find((e) => e.subtaskId === n.id);
|
|
75
|
+
lines.push(`### \`${n.id}\` (${n.role}) — ${n.status}`);
|
|
76
|
+
lines.push(`Reason: ${esc?.reason ?? n.healing.log.at(-1) ?? "(no reason recorded)"}`);
|
|
77
|
+
lines.push(`Healing attempts used: ${n.healing.attempts}/${n.healing.maxAttempts}`);
|
|
78
|
+
lines.push("");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const remaining = dag.filter((n) => n.status !== "done");
|
|
82
|
+
if (remaining.length && status !== "done") {
|
|
83
|
+
lines.push("## Next steps when resumed");
|
|
84
|
+
for (const n of remaining) {
|
|
85
|
+
lines.push(`- \`${n.id}\` (${n.role}, ${n.status}) — ${nextStepNote(n)}`);
|
|
86
|
+
}
|
|
87
|
+
lines.push("");
|
|
88
|
+
}
|
|
89
|
+
if (status === "done") {
|
|
90
|
+
lines.push("## Result");
|
|
91
|
+
lines.push("This task completed successfully — nothing left to resume.");
|
|
92
|
+
}
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
95
|
+
function nextStepNote(n) {
|
|
96
|
+
if (n.status === "escalated") {
|
|
97
|
+
return "will be retried with a fresh healing budget on resume; its healing log (what was already tried) carries forward.";
|
|
98
|
+
}
|
|
99
|
+
if (n.status === "pending") {
|
|
100
|
+
return "blocked on an upstream dependency finishing first.";
|
|
101
|
+
}
|
|
102
|
+
if (n.status === "ready" || n.status === "healing") {
|
|
103
|
+
return "will be (re-)dispatched on resume.";
|
|
104
|
+
}
|
|
105
|
+
return "no action needed.";
|
|
106
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { SubtaskNode, SubtaskResult } from "../types.js";
|
|
2
|
+
import type { ValidationOutcome } from "./validation-gate.js";
|
|
3
|
+
export interface ReasoningScore {
|
|
4
|
+
score: number;
|
|
5
|
+
breakdown: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* WHAT THIS IS: a transparent, additive rubric over one subtask attempt —
|
|
9
|
+
* acceptance-criteria pass rate, iteration efficiency, error rate, and
|
|
10
|
+
* self-check discipline. Every point gained or lost has a one-line reason
|
|
11
|
+
* in `breakdown`, on purpose: a score nobody can audit is a score nobody
|
|
12
|
+
* should trust enough to act on.
|
|
13
|
+
*
|
|
14
|
+
* WHAT THIS IS NOT: a gate. Validation Gate (`validation-gate.ts`) already
|
|
15
|
+
* decides pass/fail from acceptance criteria alone — that decision is
|
|
16
|
+
* final and this score never overrides it. This is a SEPARATE signal for
|
|
17
|
+
* two specific, bounded uses:
|
|
18
|
+
*
|
|
19
|
+
* 1. Within a single task run: a low-efficiency score on a failed
|
|
20
|
+
* attempt (many iterations, repeated errors) gets folded into the
|
|
21
|
+
* next healing retry's hint — see `sdlc-orchestrator.ts`'s
|
|
22
|
+
* `routeFailure` — so the retry gets a concrete nudge ("you spent 18
|
|
23
|
+
* iterations and repeated the same failed edit_file call 4 times")
|
|
24
|
+
* instead of just "try again." That's a real, bounded improvement
|
|
25
|
+
* inside one run's healing loop.
|
|
26
|
+
*
|
|
27
|
+
* 2. Across benchmark runs over time: `bench/run.ts` reports the average
|
|
28
|
+
* score per fixture per run. A maintainer comparing this week's
|
|
29
|
+
* average against last week's is the actual feedback loop for "does
|
|
30
|
+
* this directive/skill/prompt change help" — a human decision
|
|
31
|
+
* informed by a trend, not an automatic one.
|
|
32
|
+
*
|
|
33
|
+
* What this deliberately does NOT claim to do: make the model itself
|
|
34
|
+
* smarter mid-conversation. Claude isn't fine-tuned by this score, and
|
|
35
|
+
* feeding a bare number back into the model's own context is a Goodhart's-
|
|
36
|
+
* law risk (a model that can see its score can learn to game the score's
|
|
37
|
+
* proxies — fewer tool calls, a confident-sounding self_check — rather
|
|
38
|
+
* than the actual acceptance criteria). That's why the score itself is
|
|
39
|
+
* never shown to the Sub Agent; only the specific, concrete FAILURE REASON
|
|
40
|
+
* (already the existing healing-hint mechanism) is.
|
|
41
|
+
*/
|
|
42
|
+
export declare function computeReasoningScore(node: SubtaskNode, result: SubtaskResult, validation: ValidationOutcome): ReasoningScore;
|
|
43
|
+
/**
|
|
44
|
+
* The one place a score's breakdown is allowed to influence the model: a
|
|
45
|
+
* short, concrete efficiency note appended to the NEXT healing attempt's
|
|
46
|
+
* hint — never the raw number, never phrased as "your score was X."
|
|
47
|
+
* Silent no-op if the attempt wasn't actually inefficient.
|
|
48
|
+
*/
|
|
49
|
+
export declare function efficiencyHint(result: SubtaskResult): string | null;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const SELF_CHECK_EXPECTED_ROLES = new Set(["programmer", "refactorer", "tester", "devops", "ui-api-tester"]);
|
|
2
|
+
/**
|
|
3
|
+
* WHAT THIS IS: a transparent, additive rubric over one subtask attempt —
|
|
4
|
+
* acceptance-criteria pass rate, iteration efficiency, error rate, and
|
|
5
|
+
* self-check discipline. Every point gained or lost has a one-line reason
|
|
6
|
+
* in `breakdown`, on purpose: a score nobody can audit is a score nobody
|
|
7
|
+
* should trust enough to act on.
|
|
8
|
+
*
|
|
9
|
+
* WHAT THIS IS NOT: a gate. Validation Gate (`validation-gate.ts`) already
|
|
10
|
+
* decides pass/fail from acceptance criteria alone — that decision is
|
|
11
|
+
* final and this score never overrides it. This is a SEPARATE signal for
|
|
12
|
+
* two specific, bounded uses:
|
|
13
|
+
*
|
|
14
|
+
* 1. Within a single task run: a low-efficiency score on a failed
|
|
15
|
+
* attempt (many iterations, repeated errors) gets folded into the
|
|
16
|
+
* next healing retry's hint — see `sdlc-orchestrator.ts`'s
|
|
17
|
+
* `routeFailure` — so the retry gets a concrete nudge ("you spent 18
|
|
18
|
+
* iterations and repeated the same failed edit_file call 4 times")
|
|
19
|
+
* instead of just "try again." That's a real, bounded improvement
|
|
20
|
+
* inside one run's healing loop.
|
|
21
|
+
*
|
|
22
|
+
* 2. Across benchmark runs over time: `bench/run.ts` reports the average
|
|
23
|
+
* score per fixture per run. A maintainer comparing this week's
|
|
24
|
+
* average against last week's is the actual feedback loop for "does
|
|
25
|
+
* this directive/skill/prompt change help" — a human decision
|
|
26
|
+
* informed by a trend, not an automatic one.
|
|
27
|
+
*
|
|
28
|
+
* What this deliberately does NOT claim to do: make the model itself
|
|
29
|
+
* smarter mid-conversation. Claude isn't fine-tuned by this score, and
|
|
30
|
+
* feeding a bare number back into the model's own context is a Goodhart's-
|
|
31
|
+
* law risk (a model that can see its score can learn to game the score's
|
|
32
|
+
* proxies — fewer tool calls, a confident-sounding self_check — rather
|
|
33
|
+
* than the actual acceptance criteria). That's why the score itself is
|
|
34
|
+
* never shown to the Sub Agent; only the specific, concrete FAILURE REASON
|
|
35
|
+
* (already the existing healing-hint mechanism) is.
|
|
36
|
+
*/
|
|
37
|
+
export function computeReasoningScore(node, result, validation) {
|
|
38
|
+
const breakdown = [];
|
|
39
|
+
let score = 0;
|
|
40
|
+
// --- Acceptance criteria: up to 60 points -------------------------------
|
|
41
|
+
const totalAC = node.acceptanceCriteria.length;
|
|
42
|
+
if (totalAC === 0) {
|
|
43
|
+
score += 60;
|
|
44
|
+
breakdown.push("+60 no acceptance criteria defined for this stage (e.g. conversation) — full credit");
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const passedAC = validation.perCriterion.filter((c) => c.passed).length;
|
|
48
|
+
const points = Math.round((60 * passedAC) / totalAC);
|
|
49
|
+
score += points;
|
|
50
|
+
breakdown.push(`+${points} acceptance criteria: ${passedAC}/${totalAC} passed`);
|
|
51
|
+
}
|
|
52
|
+
// --- Iteration efficiency: up to 25 points ------------------------------
|
|
53
|
+
const iterations = result.iterations;
|
|
54
|
+
if (iterations <= 8) {
|
|
55
|
+
score += 25;
|
|
56
|
+
breakdown.push(`+25 efficient: reached a result in ${iterations} iteration(s) (≤8 baseline)`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const penalty = Math.min(25, (iterations - 8) * 2);
|
|
60
|
+
const points = 25 - penalty;
|
|
61
|
+
score += points;
|
|
62
|
+
breakdown.push(`+${points} took ${iterations} iterations, ${penalty} points off for exceeding the 8-iteration baseline`);
|
|
63
|
+
}
|
|
64
|
+
// --- Error rate: down to -20 points -------------------------------------
|
|
65
|
+
const errorCount = result.raoTrace.filter((s) => s.observation.startsWith("protocol_error") || s.observation.startsWith("llm_error")).length;
|
|
66
|
+
if (errorCount > 0) {
|
|
67
|
+
const penalty = Math.min(20, errorCount * 4);
|
|
68
|
+
score -= penalty;
|
|
69
|
+
breakdown.push(`-${penalty} ${errorCount} protocol/LLM error(s) during the attempt`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
breakdown.push("+0 no protocol/LLM errors");
|
|
73
|
+
}
|
|
74
|
+
// --- Self-check discipline: ±15 points -----------------------------------
|
|
75
|
+
const expectsSelfCheck = SELF_CHECK_EXPECTED_ROLES.has(node.role);
|
|
76
|
+
if (result.selfCheck?.ran && result.selfCheck.passed) {
|
|
77
|
+
score += 15;
|
|
78
|
+
breakdown.push("+15 self_check ran and passed");
|
|
79
|
+
}
|
|
80
|
+
else if (result.selfCheck?.ran && !result.selfCheck.passed) {
|
|
81
|
+
breakdown.push("+0 self_check ran but did not pass (consistent with a failed validation, not double-penalized)");
|
|
82
|
+
}
|
|
83
|
+
else if (expectsSelfCheck) {
|
|
84
|
+
score -= 5;
|
|
85
|
+
breakdown.push(`-5 role "${node.role}" is expected to self-check mechanically-verifiable work, but self_check was never reported`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
breakdown.push("+0 self_check not expected for this role");
|
|
89
|
+
}
|
|
90
|
+
// --- Healing cost: -5 per attempt already spent getting here -----------
|
|
91
|
+
if (node.healing.attempts > 0) {
|
|
92
|
+
const penalty = node.healing.attempts * 5;
|
|
93
|
+
score -= penalty;
|
|
94
|
+
breakdown.push(`-${penalty} this is healing attempt ${node.healing.attempts}/${node.healing.maxAttempts} — it took retries to get here`);
|
|
95
|
+
}
|
|
96
|
+
const clamped = Math.max(0, Math.min(100, score));
|
|
97
|
+
if (clamped !== score)
|
|
98
|
+
breakdown.push(`(raw ${score}, clamped to ${clamped})`);
|
|
99
|
+
return { score: clamped, breakdown };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The one place a score's breakdown is allowed to influence the model: a
|
|
103
|
+
* short, concrete efficiency note appended to the NEXT healing attempt's
|
|
104
|
+
* hint — never the raw number, never phrased as "your score was X."
|
|
105
|
+
* Silent no-op if the attempt wasn't actually inefficient.
|
|
106
|
+
*/
|
|
107
|
+
export function efficiencyHint(result) {
|
|
108
|
+
const notes = [];
|
|
109
|
+
if (result.iterations > 12) {
|
|
110
|
+
notes.push(`the previous attempt took ${result.iterations} iterations — read each observation fully before deciding the next action, rather than re-checking the same thing repeatedly`);
|
|
111
|
+
}
|
|
112
|
+
const errorCount = result.raoTrace.filter((s) => s.observation.startsWith("protocol_error") || s.observation.startsWith("llm_error")).length;
|
|
113
|
+
if (errorCount >= 2) {
|
|
114
|
+
notes.push(`the previous attempt hit ${errorCount} protocol/tool errors — re-read the response contract and each tool's input shape carefully before calling it again`);
|
|
115
|
+
}
|
|
116
|
+
return notes.length ? notes.join("; ") : null;
|
|
117
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { SubtaskNode, SubtaskResult } from "../types.js";
|
|
2
|
+
import type { WksProtocolMap } from "../protocol/wks-protocol-manager.js";
|
|
3
|
+
import { LlmClient } from "../llm/client.js";
|
|
4
|
+
export interface SwarmDispatchResult {
|
|
5
|
+
subtaskId: string;
|
|
6
|
+
result: SubtaskResult;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The "swarm" the brief asked for: when more than one subtask is `ready`
|
|
10
|
+
* at once, Xcoder SDLC runs a Sub Agent for each concurrently rather than one at
|
|
11
|
+
* a time, bounded by `concurrency`.
|
|
12
|
+
*
|
|
13
|
+
* ISOLATION CONTRACT: this function itself never throws because of
|
|
14
|
+
* anything a single Sub Agent does. `SubAgent.run()` is already designed
|
|
15
|
+
* not to throw (see `agents/sub-agent.ts`), but the `try/catch` below is a
|
|
16
|
+
* deliberate second layer — if a future change to `SubAgent` or a Node-
|
|
17
|
+
* level failure (e.g. an out-of-memory kill translated into a rejected
|
|
18
|
+
* promise) ever does throw, this is where it's caught, logged, and turned
|
|
19
|
+
* into a `crashed: true` result that flows through the SAME validate →
|
|
20
|
+
* heal → escalate pipeline as any other failed subtask, rather than
|
|
21
|
+
* aborting the whole `Promise.all` and taking every other in-flight Sub
|
|
22
|
+
* Agent — and the Master Orchestrator itself — down with it.
|
|
23
|
+
*/
|
|
24
|
+
export declare function dispatchSwarm(ready: SubtaskNode[], opts: {
|
|
25
|
+
taskId: string;
|
|
26
|
+
workspaceRoot: string;
|
|
27
|
+
llm: LlmClient;
|
|
28
|
+
subAgentLlm?: LlmClient;
|
|
29
|
+
concurrency: number;
|
|
30
|
+
healingHints?: Record<string, string>;
|
|
31
|
+
protocolOverrides?: WksProtocolMap;
|
|
32
|
+
lessonLearned?: boolean;
|
|
33
|
+
lessonContext?: string;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
quiet?: boolean;
|
|
36
|
+
}): Promise<SwarmDispatchResult[]>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SubAgent } from "../agents/sub-agent.js";
|
|
2
|
+
import { appendSys } from "../util/logs.js";
|
|
3
|
+
import { emitXcoderEvent } from "../events/bus.js";
|
|
4
|
+
import * as term from "../util/terminal.js";
|
|
5
|
+
import { silentTerminal } from "../util/terminal-silent.js";
|
|
6
|
+
/**
|
|
7
|
+
* The "swarm" the brief asked for: when more than one subtask is `ready`
|
|
8
|
+
* at once, Xcoder SDLC runs a Sub Agent for each concurrently rather than one at
|
|
9
|
+
* a time, bounded by `concurrency`.
|
|
10
|
+
*
|
|
11
|
+
* ISOLATION CONTRACT: this function itself never throws because of
|
|
12
|
+
* anything a single Sub Agent does. `SubAgent.run()` is already designed
|
|
13
|
+
* not to throw (see `agents/sub-agent.ts`), but the `try/catch` below is a
|
|
14
|
+
* deliberate second layer — if a future change to `SubAgent` or a Node-
|
|
15
|
+
* level failure (e.g. an out-of-memory kill translated into a rejected
|
|
16
|
+
* promise) ever does throw, this is where it's caught, logged, and turned
|
|
17
|
+
* into a `crashed: true` result that flows through the SAME validate →
|
|
18
|
+
* heal → escalate pipeline as any other failed subtask, rather than
|
|
19
|
+
* aborting the whole `Promise.all` and taking every other in-flight Sub
|
|
20
|
+
* Agent — and the Master Orchestrator itself — down with it.
|
|
21
|
+
*/
|
|
22
|
+
export async function dispatchSwarm(ready, opts) {
|
|
23
|
+
const results = [];
|
|
24
|
+
const queue = [...ready];
|
|
25
|
+
const concurrency = Math.max(1, opts.concurrency);
|
|
26
|
+
const resolvedTerm = opts.quiet ? silentTerminal : term;
|
|
27
|
+
async function worker() {
|
|
28
|
+
while (queue.length) {
|
|
29
|
+
// Stop pulling new nodes from the queue when the abort signal fires.
|
|
30
|
+
// In-flight agents complete naturally — no real-time cancellation of
|
|
31
|
+
// in-progress LLM calls (explicit non-goal).
|
|
32
|
+
if (opts.signal?.aborted)
|
|
33
|
+
return;
|
|
34
|
+
const node = queue.shift();
|
|
35
|
+
if (!node)
|
|
36
|
+
return;
|
|
37
|
+
resolvedTerm.orchestratorNote(`dispatching ${node.id} (${node.role} / ${node.engine})`);
|
|
38
|
+
emitXcoderEvent({ type: "orchestrator:note", taskId: opts.taskId, subtaskId: node.id, data: { message: `dispatching ${node.id} (${node.role} / ${node.engine})` } });
|
|
39
|
+
const agent = new SubAgent({
|
|
40
|
+
taskId: opts.taskId,
|
|
41
|
+
workspaceRoot: opts.workspaceRoot,
|
|
42
|
+
llm: opts.subAgentLlm ?? opts.llm,
|
|
43
|
+
healingHint: opts.healingHints?.[node.id],
|
|
44
|
+
protocolOverrides: opts.protocolOverrides,
|
|
45
|
+
lessonLearned: opts.lessonLearned,
|
|
46
|
+
lessonContext: opts.lessonContext,
|
|
47
|
+
quiet: opts.quiet,
|
|
48
|
+
});
|
|
49
|
+
let result;
|
|
50
|
+
try {
|
|
51
|
+
result = await agent.run(node);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// The safety net described above. This branch existing at all means
|
|
55
|
+
// a bug somewhere else — it should never fire in practice — but if
|
|
56
|
+
// it does, the orchestrator keeps running instead of the whole CLI
|
|
57
|
+
// process dying with no report.
|
|
58
|
+
const message = e.stack ?? String(e);
|
|
59
|
+
await appendSys(opts.workspaceRoot, opts.taskId, `[${node.id}] UNCAUGHT crash in swarm worker: ${message}`);
|
|
60
|
+
resolvedTerm.printCrash(node.id, `uncaught exception, isolated at the swarm boundary: ${e.message}`);
|
|
61
|
+
emitXcoderEvent({ type: "agent:crash", taskId: opts.taskId, subtaskId: node.id, data: { message: e.message, recoverable: false } });
|
|
62
|
+
result = {
|
|
63
|
+
summary: `Sub Agent crashed unexpectedly: ${e.message}`,
|
|
64
|
+
deliverable: null,
|
|
65
|
+
filesTouched: [],
|
|
66
|
+
raoTrace: [],
|
|
67
|
+
tokenUsage: { inputTokens: 0, outputTokens: 0 },
|
|
68
|
+
iterations: 0,
|
|
69
|
+
crashed: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
results.push({ subtaskId: node.id, result });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, () => worker()));
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AcceptanceCriterion, SdlcStage, SubtaskNode, TaskInputs, TaskSpec } from "../types.js";
|
|
2
|
+
export declare function deliverableFor(stage: SdlcStage): string;
|
|
3
|
+
export declare function acceptanceCriteriaFor(stage: SdlcStage): AcceptanceCriterion[];
|
|
4
|
+
/**
|
|
5
|
+
* Builds the initial DAG for a non-conversation task. Deliberately linear
|
|
6
|
+
* for v1 (matching the brief's stage sequences) rather than inventing
|
|
7
|
+
* parallel branches Xcoder SDLC has no evidence justify — the swarm dispatcher
|
|
8
|
+
* (see `orchestrator/swarm.ts`) is what actually gives you fan-out, once a
|
|
9
|
+
* `replan` action splits a stage into independent nodes (e.g. multiple
|
|
10
|
+
* files to implement in parallel). Nodes are fully self-contained: a Sub
|
|
11
|
+
* Agent needs nothing beyond the node it's handed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildInitialPlan(spec: TaskSpec): SubtaskNode[];
|
|
14
|
+
export declare function isConversation(spec: TaskSpec): boolean;
|
|
15
|
+
export declare function buildTaskSpec(rawTask: string, cliInputs: Partial<TaskInputs>, constraints: TaskSpec["constraints"]): TaskSpec;
|