attocode 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +111 -1
- package/README.md +7 -0
- package/dist/src/adapters.d.ts +6 -1
- package/dist/src/adapters.d.ts.map +1 -1
- package/dist/src/adapters.js +14 -1
- package/dist/src/adapters.js.map +1 -1
- package/dist/src/agent.d.ts +50 -0
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +734 -316
- package/dist/src/agent.js.map +1 -1
- package/dist/src/defaults.d.ts +1 -1
- package/dist/src/defaults.d.ts.map +1 -1
- package/dist/src/defaults.js +2 -0
- package/dist/src/defaults.js.map +1 -1
- package/dist/src/integrations/agent-registry.d.ts +9 -2
- package/dist/src/integrations/agent-registry.d.ts.map +1 -1
- package/dist/src/integrations/agent-registry.js +30 -4
- package/dist/src/integrations/agent-registry.js.map +1 -1
- package/dist/src/integrations/async-subagent.d.ts +135 -0
- package/dist/src/integrations/async-subagent.d.ts.map +1 -0
- package/dist/src/integrations/async-subagent.js +213 -0
- package/dist/src/integrations/async-subagent.js.map +1 -0
- package/dist/src/integrations/auto-checkpoint.d.ts +98 -0
- package/dist/src/integrations/auto-checkpoint.d.ts.map +1 -0
- package/dist/src/integrations/auto-checkpoint.js +252 -0
- package/dist/src/integrations/auto-checkpoint.js.map +1 -0
- package/dist/src/integrations/budget-pool.d.ts +13 -1
- package/dist/src/integrations/budget-pool.d.ts.map +1 -1
- package/dist/src/integrations/budget-pool.js +17 -0
- package/dist/src/integrations/budget-pool.js.map +1 -1
- package/dist/src/integrations/complexity-classifier.d.ts +86 -0
- package/dist/src/integrations/complexity-classifier.d.ts.map +1 -0
- package/dist/src/integrations/complexity-classifier.js +233 -0
- package/dist/src/integrations/complexity-classifier.js.map +1 -0
- package/dist/src/integrations/delegation-protocol.d.ts +86 -0
- package/dist/src/integrations/delegation-protocol.d.ts.map +1 -0
- package/dist/src/integrations/delegation-protocol.js +127 -0
- package/dist/src/integrations/delegation-protocol.js.map +1 -0
- package/dist/src/integrations/dynamic-budget.d.ts +81 -0
- package/dist/src/integrations/dynamic-budget.d.ts.map +1 -0
- package/dist/src/integrations/dynamic-budget.js +151 -0
- package/dist/src/integrations/dynamic-budget.js.map +1 -0
- package/dist/src/integrations/economics.d.ts +44 -1
- package/dist/src/integrations/economics.d.ts.map +1 -1
- package/dist/src/integrations/economics.js +182 -3
- package/dist/src/integrations/economics.js.map +1 -1
- package/dist/src/integrations/environment-facts.d.ts +52 -0
- package/dist/src/integrations/environment-facts.d.ts.map +1 -0
- package/dist/src/integrations/environment-facts.js +84 -0
- package/dist/src/integrations/environment-facts.js.map +1 -0
- package/dist/src/integrations/index.d.ts +16 -1
- package/dist/src/integrations/index.d.ts.map +1 -1
- package/dist/src/integrations/index.js +31 -1
- package/dist/src/integrations/index.js.map +1 -1
- package/dist/src/integrations/injection-budget.d.ts +71 -0
- package/dist/src/integrations/injection-budget.d.ts.map +1 -0
- package/dist/src/integrations/injection-budget.js +136 -0
- package/dist/src/integrations/injection-budget.js.map +1 -0
- package/dist/src/integrations/mcp-client.d.ts.map +1 -1
- package/dist/src/integrations/mcp-client.js +14 -0
- package/dist/src/integrations/mcp-client.js.map +1 -1
- package/dist/src/integrations/mcp-custom-tools.d.ts +102 -0
- package/dist/src/integrations/mcp-custom-tools.d.ts.map +1 -0
- package/dist/src/integrations/mcp-custom-tools.js +232 -0
- package/dist/src/integrations/mcp-custom-tools.js.map +1 -0
- package/dist/src/integrations/mcp-tool-validator.d.ts +60 -0
- package/dist/src/integrations/mcp-tool-validator.d.ts.map +1 -0
- package/dist/src/integrations/mcp-tool-validator.js +141 -0
- package/dist/src/integrations/mcp-tool-validator.js.map +1 -0
- package/dist/src/integrations/routing.d.ts +2 -1
- package/dist/src/integrations/routing.d.ts.map +1 -1
- package/dist/src/integrations/routing.js.map +1 -1
- package/dist/src/integrations/self-improvement.d.ts +90 -0
- package/dist/src/integrations/self-improvement.d.ts.map +1 -0
- package/dist/src/integrations/self-improvement.js +217 -0
- package/dist/src/integrations/self-improvement.js.map +1 -0
- package/dist/src/integrations/smart-decomposer.d.ts +4 -0
- package/dist/src/integrations/smart-decomposer.d.ts.map +1 -1
- package/dist/src/integrations/smart-decomposer.js +55 -28
- package/dist/src/integrations/smart-decomposer.js.map +1 -1
- package/dist/src/integrations/subagent-output-store.d.ts +91 -0
- package/dist/src/integrations/subagent-output-store.d.ts.map +1 -0
- package/dist/src/integrations/subagent-output-store.js +257 -0
- package/dist/src/integrations/subagent-output-store.js.map +1 -0
- package/dist/src/integrations/swarm/index.d.ts +1 -1
- package/dist/src/integrations/swarm/index.d.ts.map +1 -1
- package/dist/src/integrations/swarm/index.js +1 -1
- package/dist/src/integrations/swarm/index.js.map +1 -1
- package/dist/src/integrations/swarm/model-selector.d.ts +1 -0
- package/dist/src/integrations/swarm/model-selector.d.ts.map +1 -1
- package/dist/src/integrations/swarm/model-selector.js +37 -3
- package/dist/src/integrations/swarm/model-selector.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-config-loader.d.ts +10 -1
- package/dist/src/integrations/swarm/swarm-config-loader.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-config-loader.js +72 -6
- package/dist/src/integrations/swarm/swarm-config-loader.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-event-bridge.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-event-bridge.js +26 -4
- package/dist/src/integrations/swarm/swarm-event-bridge.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-events.d.ts +11 -0
- package/dist/src/integrations/swarm/swarm-events.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-events.js +4 -0
- package/dist/src/integrations/swarm/swarm-events.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-orchestrator.d.ts +11 -0
- package/dist/src/integrations/swarm/swarm-orchestrator.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-orchestrator.js +233 -10
- package/dist/src/integrations/swarm/swarm-orchestrator.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-quality-gate.d.ts +9 -2
- package/dist/src/integrations/swarm/swarm-quality-gate.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-quality-gate.js +128 -11
- package/dist/src/integrations/swarm/swarm-quality-gate.js.map +1 -1
- package/dist/src/integrations/swarm/task-queue.d.ts +11 -1
- package/dist/src/integrations/swarm/task-queue.d.ts.map +1 -1
- package/dist/src/integrations/swarm/task-queue.js +125 -15
- package/dist/src/integrations/swarm/task-queue.js.map +1 -1
- package/dist/src/integrations/swarm/types.d.ts +40 -1
- package/dist/src/integrations/swarm/types.d.ts.map +1 -1
- package/dist/src/integrations/swarm/types.js +6 -1
- package/dist/src/integrations/swarm/types.js.map +1 -1
- package/dist/src/integrations/swarm/worker-pool.d.ts +9 -3
- package/dist/src/integrations/swarm/worker-pool.d.ts.map +1 -1
- package/dist/src/integrations/swarm/worker-pool.js +89 -17
- package/dist/src/integrations/swarm/worker-pool.js.map +1 -1
- package/dist/src/integrations/thinking-strategy.d.ts +52 -0
- package/dist/src/integrations/thinking-strategy.d.ts.map +1 -0
- package/dist/src/integrations/thinking-strategy.js +129 -0
- package/dist/src/integrations/thinking-strategy.js.map +1 -0
- package/dist/src/integrations/tool-recommendation.d.ts +58 -0
- package/dist/src/integrations/tool-recommendation.d.ts.map +1 -0
- package/dist/src/integrations/tool-recommendation.js +215 -0
- package/dist/src/integrations/tool-recommendation.js.map +1 -0
- package/dist/src/integrations/verification-gate.d.ts +80 -0
- package/dist/src/integrations/verification-gate.d.ts.map +1 -0
- package/dist/src/integrations/verification-gate.js +146 -0
- package/dist/src/integrations/verification-gate.js.map +1 -0
- package/dist/src/integrations/work-log.d.ts +87 -0
- package/dist/src/integrations/work-log.d.ts.map +1 -0
- package/dist/src/integrations/work-log.js +275 -0
- package/dist/src/integrations/work-log.js.map +1 -0
- package/dist/src/main.js +5 -4
- package/dist/src/main.js.map +1 -1
- package/dist/src/modes.d.ts +6 -0
- package/dist/src/modes.d.ts.map +1 -1
- package/dist/src/modes.js +73 -2
- package/dist/src/modes.js.map +1 -1
- package/dist/src/providers/adapters/anthropic.d.ts.map +1 -1
- package/dist/src/providers/adapters/anthropic.js +20 -3
- package/dist/src/providers/adapters/anthropic.js.map +1 -1
- package/dist/src/providers/adapters/openrouter.d.ts.map +1 -1
- package/dist/src/providers/adapters/openrouter.js +3 -1
- package/dist/src/providers/adapters/openrouter.js.map +1 -1
- package/dist/src/providers/types.d.ts +4 -0
- package/dist/src/providers/types.d.ts.map +1 -1
- package/dist/src/providers/types.js.map +1 -1
- package/dist/src/tools/bash.d.ts +8 -2
- package/dist/src/tools/bash.d.ts.map +1 -1
- package/dist/src/tools/bash.js +14 -1
- package/dist/src/tools/bash.js.map +1 -1
- package/dist/src/tools/coercion.d.ts +14 -0
- package/dist/src/tools/coercion.d.ts.map +1 -0
- package/dist/src/tools/coercion.js +25 -0
- package/dist/src/tools/coercion.js.map +1 -0
- package/dist/src/tools/file.d.ts +2 -2
- package/dist/src/tools/file.d.ts.map +1 -1
- package/dist/src/tools/file.js +2 -1
- package/dist/src/tools/file.js.map +1 -1
- package/dist/src/tools/standard.d.ts +17 -1
- package/dist/src/tools/standard.d.ts.map +1 -1
- package/dist/src/tools/standard.js +64 -11
- package/dist/src/tools/standard.js.map +1 -1
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +8 -1
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/event-display.d.ts.map +1 -1
- package/dist/src/tui/event-display.js +8 -1
- package/dist/src/tui/event-display.js.map +1 -1
- package/dist/src/types.d.ts +26 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +6 -2
|
@@ -76,6 +76,10 @@ export function formatSwarmEvent(event) {
|
|
|
76
76
|
const roleLabel = event.role.charAt(0).toUpperCase() + event.role.slice(1);
|
|
77
77
|
return `${roleLabel} ${event.action}: ${event.model.split('/').pop() ?? event.model}${event.taskId ? ` (task ${event.taskId})` : ''}`;
|
|
78
78
|
}
|
|
79
|
+
case 'swarm.wave.allFailed':
|
|
80
|
+
return `Wave ${event.wave}: ALL tasks failed — attempting recovery`;
|
|
81
|
+
case 'swarm.phase.progress':
|
|
82
|
+
return `[${event.phase}] ${event.message}`;
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
85
|
//# sourceMappingURL=swarm-events.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swarm-events.js","sourceRoot":"","sources":["../../../../src/integrations/swarm/swarm-events.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"swarm-events.js","sourceRoot":"","sources":["../../../../src/integrations/swarm/swarm-events.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,13 @@ import type { SwarmConfig, SwarmExecutionResult, SwarmStatus } from './types.js'
|
|
|
23
23
|
import { type SwarmBudgetPool } from './swarm-budget.js';
|
|
24
24
|
import { type SpawnAgentFn } from './worker-pool.js';
|
|
25
25
|
import type { SwarmEvent } from './swarm-events.js';
|
|
26
|
+
import type { SpawnResult } from '../agent-registry.js';
|
|
27
|
+
/**
|
|
28
|
+
* V10: Minimal hollow completion detection — let the quality gate judge everything else.
|
|
29
|
+
* Only catches truly empty completions: zero tool calls AND trivial output (<50 chars).
|
|
30
|
+
* No task-type lists, no closure report checks, no hardcoded thresholds beyond the bare minimum.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isHollowCompletion(spawnResult: SpawnResult): boolean;
|
|
26
33
|
export type SwarmEventListener = (event: SwarmEvent) => void;
|
|
27
34
|
export declare class SwarmOrchestrator {
|
|
28
35
|
private config;
|
|
@@ -55,6 +62,9 @@ export declare class SwarmOrchestrator {
|
|
|
55
62
|
private static readonly CIRCUIT_BREAKER_WINDOW_MS;
|
|
56
63
|
private static readonly CIRCUIT_BREAKER_THRESHOLD;
|
|
57
64
|
private static readonly CIRCUIT_BREAKER_PAUSE_MS;
|
|
65
|
+
private consecutiveQualityRejections;
|
|
66
|
+
private qualityGateDisabled;
|
|
67
|
+
private static readonly QUALITY_CIRCUIT_BREAKER_THRESHOLD;
|
|
58
68
|
constructor(config: SwarmConfig, provider: LLMProvider, agentRegistry: AgentRegistry, spawnAgentFn: SpawnAgentFn, blackboard?: SharedBlackboard);
|
|
59
69
|
/**
|
|
60
70
|
* Get the swarm budget pool (used by parent agent to override its own pool).
|
|
@@ -119,6 +129,7 @@ export declare class SwarmOrchestrator {
|
|
|
119
129
|
private executeWave;
|
|
120
130
|
/**
|
|
121
131
|
* Dispatch a single task to a worker.
|
|
132
|
+
* Selects the worker once and passes it through to avoid double-selection.
|
|
122
133
|
*/
|
|
123
134
|
private dispatchTask;
|
|
124
135
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swarm-orchestrator.d.ts","sourceRoot":"","sources":["../../../../src/integrations/swarm/swarm-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,KAAK,EACV,WAAW,EACX,oBAAoB,EAGpB,WAAW,EAQZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAA0C,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAI7F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"swarm-orchestrator.d.ts","sourceRoot":"","sources":["../../../../src/integrations/swarm/swarm-orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,6 +25,20 @@ import { createSwarmWorkerPool } from './worker-pool.js';
|
|
|
25
25
|
import { evaluateWorkerOutput } from './swarm-quality-gate.js';
|
|
26
26
|
import { ModelHealthTracker, selectAlternativeModel } from './model-selector.js';
|
|
27
27
|
import { SwarmStateStore } from './swarm-state-store.js';
|
|
28
|
+
// ─── Hollow Completion Detection ──────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* V10: Minimal hollow completion detection — let the quality gate judge everything else.
|
|
31
|
+
* Only catches truly empty completions: zero tool calls AND trivial output (<50 chars).
|
|
32
|
+
* No task-type lists, no closure report checks, no hardcoded thresholds beyond the bare minimum.
|
|
33
|
+
*/
|
|
34
|
+
export function isHollowCompletion(spawnResult) {
|
|
35
|
+
// Timeout uses toolCalls === -1, not hollow
|
|
36
|
+
if (spawnResult.metrics.toolCalls === -1)
|
|
37
|
+
return false;
|
|
38
|
+
// Only catch truly empty completions: zero tools AND trivial output
|
|
39
|
+
return spawnResult.metrics.toolCalls === 0
|
|
40
|
+
&& (spawnResult.output?.trim().length ?? 0) < 50;
|
|
41
|
+
}
|
|
28
42
|
// ─── Orchestrator ──────────────────────────────────────────────────────────
|
|
29
43
|
export class SwarmOrchestrator {
|
|
30
44
|
config;
|
|
@@ -61,6 +75,10 @@ export class SwarmOrchestrator {
|
|
|
61
75
|
static CIRCUIT_BREAKER_WINDOW_MS = 30_000;
|
|
62
76
|
static CIRCUIT_BREAKER_THRESHOLD = 3;
|
|
63
77
|
static CIRCUIT_BREAKER_PAUSE_MS = 15_000;
|
|
78
|
+
// Quality gate circuit breaker: disable quality gates after too many consecutive rejections
|
|
79
|
+
consecutiveQualityRejections = 0;
|
|
80
|
+
qualityGateDisabled = false;
|
|
81
|
+
static QUALITY_CIRCUIT_BREAKER_THRESHOLD = 8;
|
|
64
82
|
constructor(config, provider, agentRegistry, spawnAgentFn, blackboard) {
|
|
65
83
|
this.config = { ...DEFAULT_SWARM_CONFIG, ...config };
|
|
66
84
|
this.provider = provider;
|
|
@@ -78,6 +96,8 @@ export class SwarmOrchestrator {
|
|
|
78
96
|
const llmDecompose = async (task, _context) => {
|
|
79
97
|
const systemPrompt = `You are a task decomposition expert. Break down the given task into well-defined subtasks with clear dependencies.
|
|
80
98
|
|
|
99
|
+
CRITICAL: Dependencies MUST use zero-based integer indices referring to other subtasks in the array.
|
|
100
|
+
|
|
81
101
|
Respond with valid JSON matching this exact schema:
|
|
82
102
|
{
|
|
83
103
|
"subtasks": [
|
|
@@ -85,7 +105,7 @@ Respond with valid JSON matching this exact schema:
|
|
|
85
105
|
"description": "Clear description of what this subtask does",
|
|
86
106
|
"type": "implement" | "research" | "analysis" | "design" | "test" | "refactor" | "review" | "document" | "integrate" | "deploy" | "merge",
|
|
87
107
|
"complexity": 1-10,
|
|
88
|
-
"dependencies": [
|
|
108
|
+
"dependencies": [0, 1],
|
|
89
109
|
"parallelizable": true | false,
|
|
90
110
|
"relevantFiles": ["src/path/to/file.ts"]
|
|
91
111
|
}
|
|
@@ -94,10 +114,34 @@ Respond with valid JSON matching this exact schema:
|
|
|
94
114
|
"reasoning": "Brief explanation of why this decomposition was chosen"
|
|
95
115
|
}
|
|
96
116
|
|
|
117
|
+
EXAMPLE 1 — Research task (3 parallel research + 1 merge):
|
|
118
|
+
{
|
|
119
|
+
"subtasks": [
|
|
120
|
+
{ "description": "Research React state management", "type": "research", "complexity": 3, "dependencies": [], "parallelizable": true },
|
|
121
|
+
{ "description": "Research routing options", "type": "research", "complexity": 3, "dependencies": [], "parallelizable": true },
|
|
122
|
+
{ "description": "Research testing frameworks", "type": "research", "complexity": 2, "dependencies": [], "parallelizable": true },
|
|
123
|
+
{ "description": "Synthesize findings into recommendation", "type": "merge", "complexity": 4, "dependencies": [0, 1, 2], "parallelizable": false }
|
|
124
|
+
],
|
|
125
|
+
"strategy": "parallel",
|
|
126
|
+
"reasoning": "Independent research tasks feed into a single merge"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
EXAMPLE 2 — Implementation task (sequential chain):
|
|
130
|
+
{
|
|
131
|
+
"subtasks": [
|
|
132
|
+
{ "description": "Design API schema", "type": "design", "complexity": 4, "dependencies": [], "parallelizable": false },
|
|
133
|
+
{ "description": "Implement API endpoints", "type": "implement", "complexity": 6, "dependencies": [0], "parallelizable": false },
|
|
134
|
+
{ "description": "Write integration tests", "type": "test", "complexity": 3, "dependencies": [1], "parallelizable": false }
|
|
135
|
+
],
|
|
136
|
+
"strategy": "sequential",
|
|
137
|
+
"reasoning": "Each step depends on the previous"
|
|
138
|
+
}
|
|
139
|
+
|
|
97
140
|
Rules:
|
|
141
|
+
- Dependencies MUST be integer indices (e.g., [0, 1]), NOT descriptions or strings
|
|
98
142
|
- Each subtask must have a clear, actionable description
|
|
99
|
-
- Dependencies reference other subtask descriptions or zero-based indices
|
|
100
143
|
- Mark subtasks as parallelizable: true if they don't depend on each other
|
|
144
|
+
- If there are multiple independent subtasks, ALWAYS create a final merge task that depends on ALL of them
|
|
101
145
|
- Complexity 1-3: simple, 4-6: moderate, 7-10: complex
|
|
102
146
|
- Return at least 2 subtasks for non-trivial tasks`;
|
|
103
147
|
const response = await this.provider.chat([
|
|
@@ -174,6 +218,7 @@ Rules:
|
|
|
174
218
|
}
|
|
175
219
|
// Phase 1: Decompose
|
|
176
220
|
this.currentPhase = 'decomposing';
|
|
221
|
+
this.emit({ type: 'swarm.phase.progress', phase: 'decomposing', message: 'Decomposing task into subtasks...' });
|
|
177
222
|
const decomposition = await this.decompose(task);
|
|
178
223
|
if (!decomposition) {
|
|
179
224
|
this.currentPhase = 'failed';
|
|
@@ -181,12 +226,22 @@ Rules:
|
|
|
181
226
|
}
|
|
182
227
|
// Phase 2: Schedule into waves
|
|
183
228
|
this.currentPhase = 'scheduling';
|
|
229
|
+
this.emit({ type: 'swarm.phase.progress', phase: 'scheduling', message: `Scheduling ${decomposition.subtasks.length} subtasks into waves...` });
|
|
184
230
|
this.taskQueue.loadFromDecomposition(decomposition, this.config);
|
|
231
|
+
// Emit skip events when tasks are cascade-skipped due to dependency failures
|
|
232
|
+
this.taskQueue.setOnCascadeSkip((skippedTaskId, reason) => {
|
|
233
|
+
this.emit({ type: 'swarm.task.skipped', taskId: skippedTaskId, reason });
|
|
234
|
+
});
|
|
185
235
|
const stats = this.taskQueue.getStats();
|
|
186
|
-
|
|
236
|
+
this.emit({ type: 'swarm.phase.progress', phase: 'scheduling', message: `Scheduled ${stats.total} tasks in ${this.taskQueue.getTotalWaves()} waves` });
|
|
237
|
+
// V2: Phase 2.5: Plan execution — fire in background, don't block waves
|
|
238
|
+
let planPromise;
|
|
187
239
|
if (this.config.enablePlanning) {
|
|
188
240
|
this.currentPhase = 'planning';
|
|
189
|
-
|
|
241
|
+
this.emit({ type: 'swarm.phase.progress', phase: 'planning', message: 'Creating acceptance criteria...' });
|
|
242
|
+
planPromise = this.planExecution(task, decomposition).catch(err => {
|
|
243
|
+
this.logDecision('planning', 'Planning failed (non-fatal)', err.message);
|
|
244
|
+
});
|
|
190
245
|
}
|
|
191
246
|
this.emit({
|
|
192
247
|
type: 'swarm.start',
|
|
@@ -204,9 +259,12 @@ Rules:
|
|
|
204
259
|
type: 'swarm.tasks.loaded',
|
|
205
260
|
tasks: this.taskQueue.getAllTasks(),
|
|
206
261
|
});
|
|
207
|
-
// Phase 3: Execute waves (
|
|
262
|
+
// Phase 3: Execute waves (planning runs concurrently)
|
|
208
263
|
this.currentPhase = 'executing';
|
|
209
264
|
await this.executeWaves();
|
|
265
|
+
// Ensure planning completed before verification/synthesis
|
|
266
|
+
if (planPromise)
|
|
267
|
+
await planPromise;
|
|
210
268
|
// V2: Phase 3.5: Verify integration
|
|
211
269
|
if (this.config.enableVerification && this.plan?.integrationTestPlan) {
|
|
212
270
|
this.currentPhase = 'verifying';
|
|
@@ -257,6 +315,16 @@ Rules:
|
|
|
257
315
|
// Too simple for swarm mode
|
|
258
316
|
return null;
|
|
259
317
|
}
|
|
318
|
+
// Reject heuristic fallback — the generic 3-task chain is worse than aborting
|
|
319
|
+
if (!result.metadata.llmAssisted) {
|
|
320
|
+
this.logDecision('decomposition', 'Rejected heuristic fallback DAG', 'LLM decomposition failed after retries. Heuristic DAG is not useful.');
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
// Flat-DAG detection: warn when all tasks land in wave 0 with no dependencies
|
|
324
|
+
const hasAnyDependency = result.subtasks.some(s => s.dependencies.length > 0);
|
|
325
|
+
if (!hasAnyDependency && result.subtasks.length >= 3) {
|
|
326
|
+
this.logDecision('decomposition', `Flat DAG: ${result.subtasks.length} tasks, zero dependencies`, 'All tasks will execute in wave 0 without ordering');
|
|
327
|
+
}
|
|
260
328
|
return result;
|
|
261
329
|
}
|
|
262
330
|
catch (error) {
|
|
@@ -412,6 +480,11 @@ Respond with valid JSON:
|
|
|
412
480
|
}
|
|
413
481
|
if (fixupTasks.length > 0) {
|
|
414
482
|
this.taskQueue.addFixupTasks(fixupTasks);
|
|
483
|
+
// V5: Re-emit full task list so dashboard picks up fixup tasks + edges
|
|
484
|
+
this.emit({
|
|
485
|
+
type: 'swarm.tasks.loaded',
|
|
486
|
+
tasks: this.taskQueue.getAllTasks(),
|
|
487
|
+
});
|
|
415
488
|
}
|
|
416
489
|
}
|
|
417
490
|
const result = {
|
|
@@ -520,6 +593,11 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
520
593
|
fixInstructions: f.description,
|
|
521
594
|
}));
|
|
522
595
|
this.taskQueue.addFixupTasks(fixupTasks);
|
|
596
|
+
// V5: Re-emit full task list so dashboard picks up verification fixup tasks
|
|
597
|
+
this.emit({
|
|
598
|
+
type: 'swarm.tasks.loaded',
|
|
599
|
+
tasks: this.taskQueue.getAllTasks(),
|
|
600
|
+
});
|
|
523
601
|
// Execute fix-up wave
|
|
524
602
|
this.currentPhase = 'executing';
|
|
525
603
|
await this.executeWave(fixupTasks);
|
|
@@ -566,6 +644,19 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
566
644
|
waves: checkpoint.waves,
|
|
567
645
|
currentWave: checkpoint.currentWave,
|
|
568
646
|
});
|
|
647
|
+
// Reset orphaned dispatched tasks — their workers died with the previous process
|
|
648
|
+
let resetCount = 0;
|
|
649
|
+
for (const task of this.taskQueue.getAllTasks()) {
|
|
650
|
+
if (task.status === 'dispatched') {
|
|
651
|
+
task.status = 'ready';
|
|
652
|
+
// Preserve at least 1 retry attempt
|
|
653
|
+
task.attempts = Math.min(task.attempts, Math.max(0, this.config.workerRetries - 1));
|
|
654
|
+
resetCount++;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (resetCount > 0) {
|
|
658
|
+
this.logDecision('resume', `Reset ${resetCount} orphaned dispatched tasks to ready`, 'Workers died with previous process');
|
|
659
|
+
}
|
|
569
660
|
// Continue from where we left off
|
|
570
661
|
this.currentPhase = 'executing';
|
|
571
662
|
await this.executeWaves();
|
|
@@ -623,12 +714,48 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
623
714
|
failed: waveFailed,
|
|
624
715
|
skipped: waveSkipped,
|
|
625
716
|
});
|
|
717
|
+
// Wave failure recovery: if ALL tasks in a wave failed, retry with adapted context
|
|
718
|
+
if (waveCompleted === 0 && waveFailed > 0 && readyTasks.length > 0) {
|
|
719
|
+
this.emit({ type: 'swarm.wave.allFailed', wave: waveIndex + 1 });
|
|
720
|
+
this.logDecision('wave-recovery', `Entire wave ${waveIndex + 1} failed (${waveFailed} tasks)`, 'Checking if budget allows retry with adapted strategy');
|
|
721
|
+
// Re-queue failed tasks with retry context if budget allows
|
|
722
|
+
const budgetRemaining = this.budgetPool.hasCapacity();
|
|
723
|
+
const failedWaveTasks = readyTasks.filter(t => {
|
|
724
|
+
const task = this.taskQueue.getTask(t.id);
|
|
725
|
+
return task && task.status === 'failed' && task.attempts < (this.config.workerRetries + 1);
|
|
726
|
+
});
|
|
727
|
+
if (budgetRemaining && failedWaveTasks.length > 0) {
|
|
728
|
+
for (const t of failedWaveTasks) {
|
|
729
|
+
const task = this.taskQueue.getTask(t.id);
|
|
730
|
+
if (!task)
|
|
731
|
+
continue;
|
|
732
|
+
task.status = 'ready';
|
|
733
|
+
task.retryContext = {
|
|
734
|
+
previousFeedback: 'All tasks in this batch failed. Try a fundamentally different approach — the previous strategy did not work.',
|
|
735
|
+
previousScore: 0,
|
|
736
|
+
attempt: task.attempts,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
this.logDecision('wave-recovery', `Re-queued ${failedWaveTasks.length} tasks with adapted retry context`, 'Budget allows retry');
|
|
740
|
+
// Re-execute the wave with adapted tasks
|
|
741
|
+
await this.executeWave(failedWaveTasks.map(t => this.taskQueue.getTask(t.id)).filter(t => t.status === 'ready'));
|
|
742
|
+
}
|
|
743
|
+
}
|
|
626
744
|
// V2: Review wave outputs
|
|
627
745
|
const review = await this.reviewWave(waveIndex);
|
|
628
746
|
if (review && review.fixupTasks.length > 0) {
|
|
629
747
|
// Execute fix-up tasks immediately
|
|
630
748
|
await this.executeWave(review.fixupTasks);
|
|
631
749
|
}
|
|
750
|
+
// Reset quality circuit breaker at wave boundary — each wave gets a fresh chance.
|
|
751
|
+
// Within a wave, rejections accumulate properly so the breaker can trip.
|
|
752
|
+
// Between waves, we reset so each wave gets a fresh quality evaluation window.
|
|
753
|
+
// (The within-wave reset at quality-gate-passed is kept — that's correct.)
|
|
754
|
+
if (this.qualityGateDisabled) {
|
|
755
|
+
this.qualityGateDisabled = false;
|
|
756
|
+
this.consecutiveQualityRejections = 0;
|
|
757
|
+
this.logDecision('quality-circuit-breaker', `Re-enabled quality gates at wave ${waveIndex + 1} boundary`, 'Each wave gets a fresh quality evaluation window');
|
|
758
|
+
}
|
|
632
759
|
// V2: Checkpoint after each wave
|
|
633
760
|
this.checkpoint(`wave-${waveIndex}`);
|
|
634
761
|
// Advance to next wave
|
|
@@ -700,6 +827,7 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
700
827
|
}
|
|
701
828
|
/**
|
|
702
829
|
* Dispatch a single task to a worker.
|
|
830
|
+
* Selects the worker once and passes it through to avoid double-selection.
|
|
703
831
|
*/
|
|
704
832
|
async dispatchTask(task) {
|
|
705
833
|
const worker = this.workerPool.selectWorker(task);
|
|
@@ -718,7 +846,8 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
718
846
|
}
|
|
719
847
|
try {
|
|
720
848
|
this.taskQueue.markDispatched(task.id, worker.model);
|
|
721
|
-
|
|
849
|
+
// Pass the pre-selected worker to avoid double-selection in dispatch()
|
|
850
|
+
await this.workerPool.dispatch(task, worker);
|
|
722
851
|
this.emit({
|
|
723
852
|
type: 'swarm.task.dispatched',
|
|
724
853
|
taskId: task.id,
|
|
@@ -752,6 +881,9 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
752
881
|
const task = this.taskQueue.getTask(taskId);
|
|
753
882
|
if (!task)
|
|
754
883
|
return;
|
|
884
|
+
// Guard: task was cascade-skipped while its worker was running — ignore the result
|
|
885
|
+
if (task.status === 'skipped' || task.status === 'failed')
|
|
886
|
+
return;
|
|
755
887
|
const durationMs = Date.now() - startedAt;
|
|
756
888
|
const taskResult = this.workerPool.toTaskResult(spawnResult, task, durationMs);
|
|
757
889
|
// Track model usage
|
|
@@ -791,6 +923,19 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
791
923
|
this.logDecision('failover', `Switched ${taskId} from ${model} to ${alternative.model}`, `${errorType} error`);
|
|
792
924
|
}
|
|
793
925
|
}
|
|
926
|
+
// V5/V7: Store error context so retry gets different prompt
|
|
927
|
+
if (!(is429 || is402)) {
|
|
928
|
+
// V7: Timeout-specific feedback — the worker WAS working, just ran out of time
|
|
929
|
+
const isTimeout = spawnResult.metrics.toolCalls === -1;
|
|
930
|
+
const timeoutSeconds = isTimeout ? Math.round(durationMs / 1000) : 0;
|
|
931
|
+
task.retryContext = {
|
|
932
|
+
previousFeedback: isTimeout
|
|
933
|
+
? `Previous attempt timed out after ${timeoutSeconds}s. You must complete this task more efficiently — work faster, use fewer tool calls, and produce your result sooner.`
|
|
934
|
+
: spawnResult.output.slice(0, 500),
|
|
935
|
+
previousScore: 0,
|
|
936
|
+
attempt: task.attempts,
|
|
937
|
+
};
|
|
938
|
+
}
|
|
794
939
|
// Worker failed — use higher retry limit for rate limit errors
|
|
795
940
|
const retryLimit = (is429 || is402)
|
|
796
941
|
? (this.config.rateLimitRetries ?? 3)
|
|
@@ -815,12 +960,56 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
815
960
|
});
|
|
816
961
|
return;
|
|
817
962
|
}
|
|
818
|
-
//
|
|
963
|
+
// V6: Hollow completion detection — workers that "succeed" without doing any work
|
|
964
|
+
// Must check BEFORE recording success, otherwise hollow completions inflate health scores
|
|
965
|
+
if (isHollowCompletion(spawnResult)) {
|
|
966
|
+
// Record health failure so hollow-prone models accumulate failure records
|
|
967
|
+
// and eventually trigger failover via selectAlternativeModel
|
|
968
|
+
this.healthTracker.recordFailure(model, 'error');
|
|
969
|
+
task.retryContext = {
|
|
970
|
+
previousFeedback: 'Previous attempt produced no meaningful output. Try again with a concrete approach.',
|
|
971
|
+
previousScore: 1,
|
|
972
|
+
attempt: task.attempts,
|
|
973
|
+
};
|
|
974
|
+
// Model failover for hollow completions — same pattern as quality failover
|
|
975
|
+
if (this.config.enableModelFailover) {
|
|
976
|
+
const capability = SUBTASK_TO_CAPABILITY[task.type] ?? 'code';
|
|
977
|
+
const alternative = selectAlternativeModel(this.config.workers, model, capability, this.healthTracker);
|
|
978
|
+
if (alternative) {
|
|
979
|
+
this.emit({
|
|
980
|
+
type: 'swarm.model.failover',
|
|
981
|
+
taskId,
|
|
982
|
+
fromModel: model,
|
|
983
|
+
toModel: alternative.model,
|
|
984
|
+
reason: 'hollow-completion',
|
|
985
|
+
});
|
|
986
|
+
task.assignedModel = alternative.model;
|
|
987
|
+
this.logDecision('failover', `Hollow failover ${taskId}: ${model} → ${alternative.model}`, 'Model produced hollow completion');
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
const canRetry = this.taskQueue.markFailed(taskId, this.config.workerRetries);
|
|
991
|
+
if (canRetry)
|
|
992
|
+
this.retries++;
|
|
993
|
+
this.emit({
|
|
994
|
+
type: 'swarm.task.failed',
|
|
995
|
+
taskId,
|
|
996
|
+
error: 'Hollow completion: worker used no tools',
|
|
997
|
+
attempt: task.attempts,
|
|
998
|
+
maxAttempts: 1 + this.config.workerRetries,
|
|
999
|
+
willRetry: canRetry,
|
|
1000
|
+
});
|
|
1001
|
+
this.logDecision('hollow-completion', `${taskId}: worker completed with 0 tool calls`, 'Marking as failed for retry');
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
// Record model health on success (only for non-hollow completions)
|
|
819
1005
|
this.healthTracker.recordSuccess(model, durationMs);
|
|
820
|
-
// Run quality gate if enabled — skip under API pressure
|
|
1006
|
+
// Run quality gate if enabled — skip under API pressure, skip if circuit breaker tripped,
|
|
1007
|
+
// and let the final attempt through without quality gate (so tasks produce *something*)
|
|
821
1008
|
const recentRLCount = this.recentRateLimits.filter(t => t > Date.now() - 30_000).length;
|
|
1009
|
+
const isLastAttempt = task.attempts >= (this.config.workerRetries + 1);
|
|
822
1010
|
const shouldRunQualityGate = this.config.qualityGates
|
|
823
|
-
&&
|
|
1011
|
+
&& !this.qualityGateDisabled
|
|
1012
|
+
&& !isLastAttempt
|
|
824
1013
|
&& Date.now() >= this.circuitBreakerUntil
|
|
825
1014
|
&& recentRLCount < 2;
|
|
826
1015
|
if (shouldRunQualityGate) {
|
|
@@ -832,11 +1021,39 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
832
1021
|
persona: this.config.hierarchy?.judge?.persona,
|
|
833
1022
|
};
|
|
834
1023
|
this.emit({ type: 'swarm.role.action', role: 'judge', action: 'quality-gate', model: judgeModel, taskId });
|
|
835
|
-
const quality = await evaluateWorkerOutput(this.provider, judgeModel, task, taskResult, judgeConfig);
|
|
1024
|
+
const quality = await evaluateWorkerOutput(this.provider, judgeModel, task, taskResult, judgeConfig, this.config.qualityThreshold ?? 3);
|
|
836
1025
|
taskResult.qualityScore = quality.score;
|
|
837
1026
|
taskResult.qualityFeedback = quality.feedback;
|
|
838
1027
|
if (!quality.passed) {
|
|
839
1028
|
this.qualityRejections++;
|
|
1029
|
+
this.consecutiveQualityRejections++;
|
|
1030
|
+
// Quality circuit breaker: disable gates after too many consecutive rejections
|
|
1031
|
+
if (this.consecutiveQualityRejections >= SwarmOrchestrator.QUALITY_CIRCUIT_BREAKER_THRESHOLD) {
|
|
1032
|
+
this.qualityGateDisabled = true;
|
|
1033
|
+
this.logDecision('quality-circuit-breaker', `Disabled quality gates after ${this.consecutiveQualityRejections} consecutive rejections`, 'Workers cannot meet quality threshold — letting remaining tasks through');
|
|
1034
|
+
}
|
|
1035
|
+
// V5: Attach feedback so retry prompt includes it
|
|
1036
|
+
task.retryContext = {
|
|
1037
|
+
previousFeedback: quality.feedback,
|
|
1038
|
+
previousScore: quality.score,
|
|
1039
|
+
attempt: task.attempts,
|
|
1040
|
+
};
|
|
1041
|
+
// V5: Model failover on severe quality rejection — but NOT on artifact auto-fails
|
|
1042
|
+
if (quality.score <= 1 && this.config.enableModelFailover && !quality.artifactAutoFail) {
|
|
1043
|
+
const capability = SUBTASK_TO_CAPABILITY[task.type] ?? 'code';
|
|
1044
|
+
const alternative = selectAlternativeModel(this.config.workers, model, capability, this.healthTracker);
|
|
1045
|
+
if (alternative) {
|
|
1046
|
+
this.emit({
|
|
1047
|
+
type: 'swarm.model.failover',
|
|
1048
|
+
taskId,
|
|
1049
|
+
fromModel: model,
|
|
1050
|
+
toModel: alternative.model,
|
|
1051
|
+
reason: `quality-score-${quality.score}`,
|
|
1052
|
+
});
|
|
1053
|
+
task.assignedModel = alternative.model;
|
|
1054
|
+
this.logDecision('failover', `Quality failover ${taskId}: ${model} → ${alternative.model}`, `Score ${quality.score}/5`);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
840
1057
|
const canRetry = this.taskQueue.markFailed(taskId, this.config.workerRetries);
|
|
841
1058
|
if (canRetry) {
|
|
842
1059
|
this.retries++;
|
|
@@ -850,6 +1067,8 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
850
1067
|
});
|
|
851
1068
|
return;
|
|
852
1069
|
}
|
|
1070
|
+
// Quality passed — reset consecutive rejection counter
|
|
1071
|
+
this.consecutiveQualityRejections = 0;
|
|
853
1072
|
}
|
|
854
1073
|
// Task passed — mark completed
|
|
855
1074
|
this.taskQueue.markCompleted(taskId, taskResult);
|
|
@@ -885,6 +1104,10 @@ Respond with JSON: { "fixups": [{ "description": "what to fix", "type": "impleme
|
|
|
885
1104
|
costUsed: taskResult.costUsed,
|
|
886
1105
|
durationMs: taskResult.durationMs,
|
|
887
1106
|
qualityScore: taskResult.qualityScore,
|
|
1107
|
+
qualityFeedback: taskResult.qualityFeedback,
|
|
1108
|
+
output: taskResult.output,
|
|
1109
|
+
closureReport: taskResult.closureReport,
|
|
1110
|
+
toolCalls: spawnResult.metrics.toolCalls,
|
|
888
1111
|
});
|
|
889
1112
|
}
|
|
890
1113
|
/**
|