@defai.digital/ax-cli 2.7.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-2dd84869-e62d-46c8-9885-7e45f37f36e2.json +69 -0
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-484dc350-353f-4808-9ed1-ebb3cefdab37.json +24 -0
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-74a18b87-6172-4215-962b-44bb9f46a662.json +69 -0
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-870a5fb9-6e82-4ff2-8ec8-af4c251cc514.json +44 -0
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-93946601-0e83-456c-ba47-def9713124dd.json +24 -0
- package/.ax-cli/checkpoints/metadata.json +62 -0
- package/README.md +87 -11
- package/dist/agent/context-manager.d.ts +2 -2
- package/dist/agent/context-manager.js +37 -15
- package/dist/agent/context-manager.js.map +1 -1
- package/dist/agent/dependency-resolver.d.ts +83 -0
- package/dist/agent/dependency-resolver.js +310 -0
- package/dist/agent/dependency-resolver.js.map +1 -0
- package/dist/agent/llm-agent.d.ts +111 -0
- package/dist/agent/llm-agent.js +625 -3
- package/dist/agent/llm-agent.js.map +1 -1
- package/dist/agent/specialized/analysis-agent.d.ts +11 -0
- package/dist/agent/specialized/analysis-agent.js +33 -0
- package/dist/agent/specialized/analysis-agent.js.map +1 -0
- package/dist/agent/specialized/debug-agent.d.ts +11 -0
- package/dist/agent/specialized/debug-agent.js +33 -0
- package/dist/agent/specialized/debug-agent.js.map +1 -0
- package/dist/agent/specialized/documentation-agent.d.ts +11 -0
- package/dist/agent/specialized/documentation-agent.js +33 -0
- package/dist/agent/specialized/documentation-agent.js.map +1 -0
- package/dist/agent/specialized/index.d.ts +11 -0
- package/dist/agent/specialized/index.js +12 -0
- package/dist/agent/specialized/index.js.map +1 -0
- package/dist/agent/specialized/performance-agent.d.ts +11 -0
- package/dist/agent/specialized/performance-agent.js +33 -0
- package/dist/agent/specialized/performance-agent.js.map +1 -0
- package/dist/agent/specialized/refactoring-agent.d.ts +11 -0
- package/dist/agent/specialized/refactoring-agent.js +33 -0
- package/dist/agent/specialized/refactoring-agent.js.map +1 -0
- package/dist/agent/specialized/testing-agent.d.ts +11 -0
- package/dist/agent/specialized/testing-agent.js +33 -0
- package/dist/agent/specialized/testing-agent.js.map +1 -0
- package/dist/agent/subagent-orchestrator.d.ts +128 -0
- package/dist/agent/subagent-orchestrator.js +388 -0
- package/dist/agent/subagent-orchestrator.js.map +1 -0
- package/dist/agent/subagent-types.d.ts +262 -0
- package/dist/agent/subagent-types.js +152 -0
- package/dist/agent/subagent-types.js.map +1 -0
- package/dist/agent/subagent.d.ts +88 -0
- package/dist/agent/subagent.js +426 -0
- package/dist/agent/subagent.js.map +1 -0
- package/dist/checkpoint/index.d.ts +9 -0
- package/dist/checkpoint/index.js +11 -0
- package/dist/checkpoint/index.js.map +1 -0
- package/dist/checkpoint/manager.d.ts +99 -0
- package/dist/checkpoint/manager.js +281 -0
- package/dist/checkpoint/manager.js.map +1 -0
- package/dist/checkpoint/storage.d.ts +31 -0
- package/dist/checkpoint/storage.js +265 -0
- package/dist/checkpoint/storage.js.map +1 -0
- package/dist/checkpoint/types.d.ts +111 -0
- package/dist/checkpoint/types.js +17 -0
- package/dist/checkpoint/types.js.map +1 -0
- package/dist/commands/cache.js +5 -3
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/memory.js +21 -16
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/plan.d.ts +43 -0
- package/dist/commands/plan.js +385 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/rewind.d.ts +19 -0
- package/dist/commands/rewind.js +221 -0
- package/dist/commands/rewind.js.map +1 -0
- package/dist/constants.d.ts +28 -0
- package/dist/constants.js +29 -0
- package/dist/constants.js.map +1 -1
- package/dist/hooks/use-enhanced-input.d.ts +5 -1
- package/dist/hooks/use-enhanced-input.js +23 -10
- package/dist/hooks/use-enhanced-input.js.map +1 -1
- package/dist/hooks/use-input-handler.d.ts +11 -1
- package/dist/hooks/use-input-handler.js +294 -2
- package/dist/hooks/use-input-handler.js.map +1 -1
- package/dist/llm/client.js +2 -1
- package/dist/llm/client.js.map +1 -1
- package/dist/llm/tools.d.ts +5 -0
- package/dist/llm/tools.js +57 -6
- package/dist/llm/tools.js.map +1 -1
- package/dist/mcp/client.d.ts +1 -0
- package/dist/mcp/client.js +30 -8
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/transports.d.ts +0 -1
- package/dist/mcp/transports.js +10 -7
- package/dist/mcp/transports.js.map +1 -1
- package/dist/planner/dependency-resolver.d.ts +72 -0
- package/dist/planner/dependency-resolver.js +272 -0
- package/dist/planner/dependency-resolver.js.map +1 -0
- package/dist/planner/index.d.ts +12 -0
- package/dist/planner/index.js +26 -0
- package/dist/planner/index.js.map +1 -0
- package/dist/planner/plan-generator.d.ts +74 -0
- package/dist/planner/plan-generator.js +244 -0
- package/dist/planner/plan-generator.js.map +1 -0
- package/dist/planner/plan-storage.d.ts +98 -0
- package/dist/planner/plan-storage.js +325 -0
- package/dist/planner/plan-storage.js.map +1 -0
- package/dist/planner/prompts/planning-prompt.d.ts +41 -0
- package/dist/planner/prompts/planning-prompt.js +289 -0
- package/dist/planner/prompts/planning-prompt.js.map +1 -0
- package/dist/planner/task-planner.d.ts +135 -0
- package/dist/planner/task-planner.js +493 -0
- package/dist/planner/task-planner.js.map +1 -0
- package/dist/planner/token-estimator.d.ts +63 -0
- package/dist/planner/token-estimator.js +295 -0
- package/dist/planner/token-estimator.js.map +1 -0
- package/dist/planner/types.d.ts +669 -0
- package/dist/planner/types.js +213 -0
- package/dist/planner/types.js.map +1 -0
- package/dist/schemas/api-schemas.js +4 -0
- package/dist/schemas/api-schemas.js.map +1 -1
- package/dist/schemas/confirmation-schemas.d.ts +5 -0
- package/dist/schemas/confirmation-schemas.js +7 -0
- package/dist/schemas/confirmation-schemas.js.map +1 -1
- package/dist/schemas/index.d.ts +4 -4
- package/dist/tools/bash-output.d.ts +25 -0
- package/dist/tools/bash-output.js +145 -0
- package/dist/tools/bash-output.js.map +1 -0
- package/dist/tools/bash.d.ts +46 -2
- package/dist/tools/bash.js +241 -42
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/search.js +34 -9
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/text-editor.d.ts +16 -0
- package/dist/tools/text-editor.js +37 -2
- package/dist/tools/text-editor.js.map +1 -1
- package/dist/ui/components/chat-history.d.ts +1 -0
- package/dist/ui/components/chat-history.js +125 -41
- package/dist/ui/components/chat-history.js.map +1 -1
- package/dist/ui/components/chat-input.js +10 -3
- package/dist/ui/components/chat-input.js.map +1 -1
- package/dist/ui/components/chat-interface.js +154 -45
- package/dist/ui/components/chat-interface.js.map +1 -1
- package/dist/ui/components/collapsible-tool-result.d.ts +26 -0
- package/dist/ui/components/collapsible-tool-result.js +172 -0
- package/dist/ui/components/collapsible-tool-result.js.map +1 -0
- package/dist/ui/components/command-suggestions.js +2 -1
- package/dist/ui/components/command-suggestions.js.map +1 -1
- package/dist/ui/components/confirmation-dialog.js +25 -36
- package/dist/ui/components/confirmation-dialog.js.map +1 -1
- package/dist/ui/components/index.d.ts +8 -0
- package/dist/ui/components/index.js +9 -0
- package/dist/ui/components/index.js.map +1 -1
- package/dist/ui/components/keyboard-hints.d.ts +35 -0
- package/dist/ui/components/keyboard-hints.js +134 -0
- package/dist/ui/components/keyboard-hints.js.map +1 -0
- package/dist/ui/components/loading-spinner.d.ts +2 -1
- package/dist/ui/components/loading-spinner.js +86 -34
- package/dist/ui/components/loading-spinner.js.map +1 -1
- package/dist/ui/components/phase-progress.d.ts +21 -0
- package/dist/ui/components/phase-progress.js +228 -0
- package/dist/ui/components/phase-progress.js.map +1 -0
- package/dist/ui/components/quick-actions.d.ts +12 -0
- package/dist/ui/components/quick-actions.js +122 -0
- package/dist/ui/components/quick-actions.js.map +1 -0
- package/dist/ui/components/reasoning-display.d.ts +0 -80
- package/dist/ui/components/reasoning-display.js +0 -83
- package/dist/ui/components/reasoning-display.js.map +1 -1
- package/dist/ui/components/status-bar.d.ts +25 -0
- package/dist/ui/components/status-bar.js +125 -0
- package/dist/ui/components/status-bar.js.map +1 -0
- package/dist/ui/components/subagent-monitor.d.ts +29 -0
- package/dist/ui/components/subagent-monitor.js +150 -0
- package/dist/ui/components/subagent-monitor.js.map +1 -0
- package/dist/ui/components/toast-notification.d.ts +123 -0
- package/dist/ui/components/toast-notification.js +143 -0
- package/dist/ui/components/toast-notification.js.map +1 -0
- package/dist/ui/components/welcome-panel.d.ts +10 -0
- package/dist/ui/components/welcome-panel.js +107 -0
- package/dist/ui/components/welcome-panel.js.map +1 -0
- package/dist/utils/background-task-manager.d.ts +95 -0
- package/dist/utils/background-task-manager.js +330 -0
- package/dist/utils/background-task-manager.js.map +1 -0
- package/dist/utils/confirmation-service.js +8 -3
- package/dist/utils/confirmation-service.js.map +1 -1
- package/dist/utils/history-manager.d.ts +1 -0
- package/dist/utils/history-manager.js +40 -5
- package/dist/utils/history-manager.js.map +1 -1
- package/dist/utils/json-utils.d.ts +7 -0
- package/dist/utils/json-utils.js +70 -2
- package/dist/utils/json-utils.js.map +1 -1
- package/dist/utils/message-optimizer.d.ts +1 -0
- package/dist/utils/message-optimizer.js +7 -1
- package/dist/utils/message-optimizer.js.map +1 -1
- package/dist/utils/project-analyzer.js +5 -2
- package/dist/utils/project-analyzer.js.map +1 -1
- package/dist/utils/settings-manager.js +12 -10
- package/dist/utils/settings-manager.js.map +1 -1
- package/dist/utils/text-utils.js +3 -3
- package/dist/utils/text-utils.js.map +1 -1
- package/dist/utils/token-counter.d.ts +5 -0
- package/dist/utils/token-counter.js +18 -3
- package/dist/utils/token-counter.js.map +1 -1
- package/package.json +3 -1
package/dist/agent/llm-agent.js
CHANGED
|
@@ -2,6 +2,7 @@ import { LLMClient } from "../llm/client.js";
|
|
|
2
2
|
import { getAllGrokTools, getMCPManager, initializeMCPServers, } from "../llm/tools.js";
|
|
3
3
|
import { loadMCPConfig } from "../mcp/config.js";
|
|
4
4
|
import { TextEditorTool, BashTool, TodoTool, SearchTool, } from "../tools/index.js";
|
|
5
|
+
import { BashOutputTool } from "../tools/bash-output.js";
|
|
5
6
|
import { EventEmitter } from "events";
|
|
6
7
|
import { AGENT_CONFIG } from "../constants.js";
|
|
7
8
|
import { createTokenCounter } from "../utils/token-counter.js";
|
|
@@ -11,10 +12,15 @@ import { ContextManager } from "./context-manager.js";
|
|
|
11
12
|
import { buildSystemPrompt } from "../utils/prompt-builder.js";
|
|
12
13
|
import { getUsageTracker } from "../utils/usage-tracker.js";
|
|
13
14
|
import { extractErrorMessage } from "../utils/error-handler.js";
|
|
15
|
+
import { getCheckpointManager } from "../checkpoint/index.js";
|
|
16
|
+
import { SubagentOrchestrator } from "./subagent-orchestrator.js";
|
|
17
|
+
import { getTaskPlanner, isComplexRequest, } from "../planner/index.js";
|
|
18
|
+
import { PLANNER_CONFIG } from "../constants.js";
|
|
14
19
|
export class LLMAgent extends EventEmitter {
|
|
15
20
|
llmClient;
|
|
16
21
|
textEditor;
|
|
17
22
|
bash;
|
|
23
|
+
bashOutput;
|
|
18
24
|
todoTool;
|
|
19
25
|
search;
|
|
20
26
|
chatHistory = [];
|
|
@@ -24,6 +30,11 @@ export class LLMAgent extends EventEmitter {
|
|
|
24
30
|
abortController = null;
|
|
25
31
|
maxToolRounds;
|
|
26
32
|
recentToolCalls = new Map(); // Track recent tool calls to detect loops
|
|
33
|
+
checkpointManager;
|
|
34
|
+
subagentOrchestrator;
|
|
35
|
+
taskPlanner;
|
|
36
|
+
currentPlan = null;
|
|
37
|
+
planningEnabled = PLANNER_CONFIG.ENABLED;
|
|
27
38
|
constructor(apiKey, baseURL, model, maxToolRounds) {
|
|
28
39
|
super();
|
|
29
40
|
const manager = getSettingsManager();
|
|
@@ -36,10 +47,28 @@ export class LLMAgent extends EventEmitter {
|
|
|
36
47
|
this.llmClient = new LLMClient(apiKey, modelToUse, baseURL);
|
|
37
48
|
this.textEditor = new TextEditorTool();
|
|
38
49
|
this.bash = new BashTool();
|
|
50
|
+
this.bashOutput = new BashOutputTool();
|
|
39
51
|
this.todoTool = new TodoTool();
|
|
40
52
|
this.search = new SearchTool();
|
|
41
53
|
this.tokenCounter = createTokenCounter(modelToUse);
|
|
42
54
|
this.contextManager = new ContextManager({ model: modelToUse });
|
|
55
|
+
this.checkpointManager = getCheckpointManager();
|
|
56
|
+
this.subagentOrchestrator = new SubagentOrchestrator({ maxConcurrentAgents: 5 });
|
|
57
|
+
this.taskPlanner = getTaskPlanner();
|
|
58
|
+
// Wire up checkpoint callback for automatic checkpoint creation
|
|
59
|
+
this.textEditor.setCheckpointCallback(async (files, description) => {
|
|
60
|
+
await this.checkpointManager.createCheckpoint({
|
|
61
|
+
files,
|
|
62
|
+
conversationState: this.chatHistory,
|
|
63
|
+
description,
|
|
64
|
+
metadata: {
|
|
65
|
+
model: this.llmClient.getCurrentModel(),
|
|
66
|
+
triggeredBy: 'auto',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
// Initialize checkpoint manager
|
|
71
|
+
this.initializeCheckpointManager();
|
|
43
72
|
// Initialize MCP servers if configured
|
|
44
73
|
this.initializeMCP();
|
|
45
74
|
// Build system prompt from YAML configuration
|
|
@@ -53,6 +82,23 @@ export class LLMAgent extends EventEmitter {
|
|
|
53
82
|
content: `${systemPrompt}\n\nCurrent working directory: ${process.cwd()}`,
|
|
54
83
|
});
|
|
55
84
|
}
|
|
85
|
+
initializeCheckpointManager() {
|
|
86
|
+
// Initialize checkpoint manager in the background
|
|
87
|
+
Promise.resolve().then(async () => {
|
|
88
|
+
try {
|
|
89
|
+
await this.checkpointManager.initialize();
|
|
90
|
+
this.emit('system', 'Checkpoint system initialized');
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const errorMsg = extractErrorMessage(error);
|
|
94
|
+
console.warn("Checkpoint initialization failed:", errorMsg);
|
|
95
|
+
this.emit('system', `Checkpoint initialization failed: ${errorMsg}`);
|
|
96
|
+
}
|
|
97
|
+
}).catch((error) => {
|
|
98
|
+
const errorMsg = extractErrorMessage(error);
|
|
99
|
+
console.warn("Unexpected error during checkpoint initialization:", errorMsg);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
56
102
|
async initializeMCP() {
|
|
57
103
|
// Initialize MCP in the background without blocking
|
|
58
104
|
Promise.resolve().then(async () => {
|
|
@@ -148,8 +194,7 @@ export class LLMAgent extends EventEmitter {
|
|
|
148
194
|
// Clean up old entries (keep only last N unique calls)
|
|
149
195
|
if (this.recentToolCalls.size > AGENT_CONFIG.MAX_RECENT_TOOL_CALLS) {
|
|
150
196
|
const firstKey = this.recentToolCalls.keys().next().value;
|
|
151
|
-
|
|
152
|
-
if (firstKey) {
|
|
197
|
+
if (firstKey !== undefined) {
|
|
153
198
|
this.recentToolCalls.delete(firstKey);
|
|
154
199
|
}
|
|
155
200
|
}
|
|
@@ -201,6 +246,345 @@ export class LLMAgent extends EventEmitter {
|
|
|
201
246
|
return true;
|
|
202
247
|
return false;
|
|
203
248
|
}
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// Multi-Phase Planning Integration
|
|
251
|
+
// ============================================================================
|
|
252
|
+
/**
|
|
253
|
+
* Check if a request should trigger multi-phase planning
|
|
254
|
+
*/
|
|
255
|
+
shouldCreatePlan(message) {
|
|
256
|
+
if (!this.planningEnabled)
|
|
257
|
+
return false;
|
|
258
|
+
return isComplexRequest(message);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get the current plan if any
|
|
262
|
+
*/
|
|
263
|
+
getCurrentPlan() {
|
|
264
|
+
return this.currentPlan;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Execute a single phase using the LLM
|
|
268
|
+
*/
|
|
269
|
+
async executePhase(phase, context) {
|
|
270
|
+
const startTime = Date.now();
|
|
271
|
+
const startTokens = this.tokenCounter.countMessageTokens(this.messages);
|
|
272
|
+
const filesModified = [];
|
|
273
|
+
let lastAssistantContent = "";
|
|
274
|
+
// Emit phase started event
|
|
275
|
+
this.emit("phase:started", { phase, planId: context.planId });
|
|
276
|
+
try {
|
|
277
|
+
// Build phase-specific prompt
|
|
278
|
+
const phasePrompt = this.buildPhasePrompt(phase, context);
|
|
279
|
+
// Execute through normal message processing (without recursively planning)
|
|
280
|
+
const savedPlanningState = this.planningEnabled;
|
|
281
|
+
this.planningEnabled = false; // Temporarily disable planning for phase execution
|
|
282
|
+
// Add phase context to messages
|
|
283
|
+
this.messages.push({
|
|
284
|
+
role: "user",
|
|
285
|
+
content: phasePrompt,
|
|
286
|
+
});
|
|
287
|
+
// Execute using the standard tool loop
|
|
288
|
+
const tools = await getAllGrokTools();
|
|
289
|
+
let toolRounds = 0;
|
|
290
|
+
const maxPhaseRounds = Math.min(this.maxToolRounds, 50); // Limit per phase
|
|
291
|
+
while (toolRounds < maxPhaseRounds) {
|
|
292
|
+
const response = await this.llmClient.chat(this.messages, tools);
|
|
293
|
+
const assistantMessage = response.choices[0]?.message;
|
|
294
|
+
if (!assistantMessage)
|
|
295
|
+
break;
|
|
296
|
+
// Capture the assistant's content for phase output
|
|
297
|
+
if (assistantMessage.content) {
|
|
298
|
+
lastAssistantContent = assistantMessage.content;
|
|
299
|
+
}
|
|
300
|
+
// Add to messages
|
|
301
|
+
this.messages.push({
|
|
302
|
+
role: "assistant",
|
|
303
|
+
content: assistantMessage.content || "",
|
|
304
|
+
tool_calls: assistantMessage.tool_calls,
|
|
305
|
+
});
|
|
306
|
+
// Check for tool calls
|
|
307
|
+
if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
|
|
308
|
+
break; // No more tool calls, phase complete
|
|
309
|
+
}
|
|
310
|
+
toolRounds++;
|
|
311
|
+
// Execute tools and track file modifications
|
|
312
|
+
for (const toolCall of assistantMessage.tool_calls) {
|
|
313
|
+
const result = await this.executeTool(toolCall);
|
|
314
|
+
// Track file modifications from text_editor tool
|
|
315
|
+
if (toolCall.function.name === "text_editor" ||
|
|
316
|
+
toolCall.function.name === "str_replace_editor") {
|
|
317
|
+
try {
|
|
318
|
+
const args = JSON.parse(toolCall.function.arguments);
|
|
319
|
+
if (args.path && result.success) {
|
|
320
|
+
if (!filesModified.includes(args.path)) {
|
|
321
|
+
filesModified.push(args.path);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
// Ignore parse errors
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
this.messages.push({
|
|
330
|
+
role: "tool",
|
|
331
|
+
tool_call_id: toolCall.id,
|
|
332
|
+
content: result.output || result.error || "No output",
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Restore planning state
|
|
337
|
+
this.planningEnabled = savedPlanningState;
|
|
338
|
+
// Prune context if configured
|
|
339
|
+
if (PLANNER_CONFIG.PRUNE_AFTER_PHASE) {
|
|
340
|
+
if (this.contextManager.shouldPrune(this.messages, this.tokenCounter)) {
|
|
341
|
+
this.messages = this.contextManager.pruneMessages(this.messages, this.tokenCounter);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const endTokens = this.tokenCounter.countMessageTokens(this.messages);
|
|
345
|
+
const duration = Date.now() - startTime;
|
|
346
|
+
// Build meaningful output
|
|
347
|
+
const output = lastAssistantContent ||
|
|
348
|
+
`Phase "${phase.name}" completed (${toolRounds} tool rounds, ${filesModified.length} files modified)`;
|
|
349
|
+
// Emit phase completed event
|
|
350
|
+
this.emit("phase:completed", {
|
|
351
|
+
phase,
|
|
352
|
+
planId: context.planId,
|
|
353
|
+
result: { success: true, output, filesModified }
|
|
354
|
+
});
|
|
355
|
+
return {
|
|
356
|
+
phaseId: phase.id,
|
|
357
|
+
success: true,
|
|
358
|
+
output,
|
|
359
|
+
duration,
|
|
360
|
+
tokensUsed: endTokens - startTokens,
|
|
361
|
+
filesModified,
|
|
362
|
+
wasRetry: false,
|
|
363
|
+
retryAttempt: 0,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
const duration = Date.now() - startTime;
|
|
368
|
+
const errorMessage = extractErrorMessage(error);
|
|
369
|
+
// Emit phase failed event
|
|
370
|
+
this.emit("phase:failed", {
|
|
371
|
+
phase,
|
|
372
|
+
planId: context.planId,
|
|
373
|
+
error: errorMessage
|
|
374
|
+
});
|
|
375
|
+
return {
|
|
376
|
+
phaseId: phase.id,
|
|
377
|
+
success: false,
|
|
378
|
+
error: errorMessage,
|
|
379
|
+
duration,
|
|
380
|
+
tokensUsed: 0,
|
|
381
|
+
filesModified,
|
|
382
|
+
wasRetry: false,
|
|
383
|
+
retryAttempt: 0,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Build a prompt for phase execution
|
|
389
|
+
*/
|
|
390
|
+
buildPhasePrompt(phase, context) {
|
|
391
|
+
let prompt = `## Phase ${phase.index + 1}: ${phase.name}\n\n`;
|
|
392
|
+
prompt += `**Objective:** ${phase.description}\n\n`;
|
|
393
|
+
if (phase.objectives.length > 0) {
|
|
394
|
+
prompt += "**Tasks to complete:**\n";
|
|
395
|
+
for (const obj of phase.objectives) {
|
|
396
|
+
prompt += `- ${obj}\n`;
|
|
397
|
+
}
|
|
398
|
+
prompt += "\n";
|
|
399
|
+
}
|
|
400
|
+
if (context.completedPhases.length > 0) {
|
|
401
|
+
prompt += `**Previously completed phases:** ${context.completedPhases.join(", ")}\n\n`;
|
|
402
|
+
}
|
|
403
|
+
prompt += `**Original request:** ${context.originalRequest}\n\n`;
|
|
404
|
+
prompt += "Please complete this phase. Focus only on the objectives listed above.";
|
|
405
|
+
return prompt;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Generate and execute a plan for a complex request
|
|
409
|
+
*/
|
|
410
|
+
async *processWithPlanning(message) {
|
|
411
|
+
// Add user message to history
|
|
412
|
+
const userEntry = {
|
|
413
|
+
type: "user",
|
|
414
|
+
content: message,
|
|
415
|
+
timestamp: new Date(),
|
|
416
|
+
};
|
|
417
|
+
this.chatHistory.push(userEntry);
|
|
418
|
+
this.messages.push({ role: "user", content: message });
|
|
419
|
+
// Generate plan
|
|
420
|
+
yield {
|
|
421
|
+
type: "content",
|
|
422
|
+
content: "📋 **Analyzing request and creating execution plan...**\n\n",
|
|
423
|
+
};
|
|
424
|
+
try {
|
|
425
|
+
// Generate plan using LLM
|
|
426
|
+
const plan = await this.taskPlanner.generatePlan(message, async (systemPrompt, userPrompt) => {
|
|
427
|
+
const planMessages = [
|
|
428
|
+
{ role: "system", content: systemPrompt },
|
|
429
|
+
{ role: "user", content: userPrompt },
|
|
430
|
+
];
|
|
431
|
+
const response = await this.llmClient.chat(planMessages, []);
|
|
432
|
+
return response.choices[0]?.message?.content || "";
|
|
433
|
+
}, {
|
|
434
|
+
projectType: "typescript", // Could be detected
|
|
435
|
+
});
|
|
436
|
+
if (!plan) {
|
|
437
|
+
yield {
|
|
438
|
+
type: "content",
|
|
439
|
+
content: "Could not generate a plan. Processing as single request...\n\n",
|
|
440
|
+
};
|
|
441
|
+
// Fall back to normal processing - disable planning and retry
|
|
442
|
+
this.planningEnabled = false;
|
|
443
|
+
yield* this.processUserMessageStreamInternal(message);
|
|
444
|
+
this.planningEnabled = true;
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
this.currentPlan = plan;
|
|
448
|
+
// Emit plan created event
|
|
449
|
+
this.emit("plan:created", { plan });
|
|
450
|
+
// Display plan summary
|
|
451
|
+
yield {
|
|
452
|
+
type: "content",
|
|
453
|
+
content: this.formatPlanSummary(plan),
|
|
454
|
+
};
|
|
455
|
+
// Execute phases one by one with progress updates
|
|
456
|
+
const phaseResults = [];
|
|
457
|
+
let totalTokensUsed = 0;
|
|
458
|
+
const planStartTime = Date.now();
|
|
459
|
+
for (let i = 0; i < plan.phases.length; i++) {
|
|
460
|
+
const phase = plan.phases[i];
|
|
461
|
+
plan.currentPhaseIndex = i;
|
|
462
|
+
// Show phase starting
|
|
463
|
+
yield {
|
|
464
|
+
type: "content",
|
|
465
|
+
content: `\n**⏳ Phase ${i + 1}/${plan.phases.length}: ${phase.name}**\n`,
|
|
466
|
+
};
|
|
467
|
+
// Execute the phase
|
|
468
|
+
const context = {
|
|
469
|
+
planId: plan.id,
|
|
470
|
+
originalRequest: message,
|
|
471
|
+
completedPhases: phaseResults.filter(r => r.success).map(r => r.phaseId),
|
|
472
|
+
};
|
|
473
|
+
const result = await this.executePhase(phase, context);
|
|
474
|
+
phaseResults.push(result);
|
|
475
|
+
totalTokensUsed += result.tokensUsed;
|
|
476
|
+
// Report phase result
|
|
477
|
+
if (result.success) {
|
|
478
|
+
yield {
|
|
479
|
+
type: "content",
|
|
480
|
+
content: `✓ Phase ${i + 1} completed (${Math.ceil(result.duration / 1000)}s)\n`,
|
|
481
|
+
};
|
|
482
|
+
if (result.filesModified.length > 0) {
|
|
483
|
+
yield {
|
|
484
|
+
type: "content",
|
|
485
|
+
content: ` Files modified: ${result.filesModified.join(", ")}\n`,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
yield {
|
|
491
|
+
type: "content",
|
|
492
|
+
content: `✕ Phase ${i + 1} failed: ${result.error}\n`,
|
|
493
|
+
};
|
|
494
|
+
// Continue with next phase unless abort strategy
|
|
495
|
+
if (phase.fallbackStrategy === "abort") {
|
|
496
|
+
yield {
|
|
497
|
+
type: "content",
|
|
498
|
+
content: `\n⚠️ Plan aborted due to phase failure.\n`,
|
|
499
|
+
};
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const totalDuration = Date.now() - planStartTime;
|
|
505
|
+
// Build final result
|
|
506
|
+
const successfulPhases = phaseResults.filter(r => r.success);
|
|
507
|
+
const failedPhases = phaseResults.filter(r => !r.success);
|
|
508
|
+
const allFilesModified = [...new Set(phaseResults.flatMap(r => r.filesModified))];
|
|
509
|
+
const summary = successfulPhases.length === phaseResults.length
|
|
510
|
+
? `All ${phaseResults.length} phases completed successfully. ${allFilesModified.length} files modified.`
|
|
511
|
+
: `${successfulPhases.length}/${phaseResults.length} phases completed. ${failedPhases.length} failed.`;
|
|
512
|
+
const warnings = [];
|
|
513
|
+
for (const result of failedPhases) {
|
|
514
|
+
warnings.push(`Phase ${result.phaseId} failed: ${result.error || "Unknown error"}`);
|
|
515
|
+
}
|
|
516
|
+
const planResult = {
|
|
517
|
+
planId: plan.id,
|
|
518
|
+
success: phaseResults.every(r => r.success),
|
|
519
|
+
phaseResults,
|
|
520
|
+
totalDuration,
|
|
521
|
+
totalTokensUsed,
|
|
522
|
+
summary,
|
|
523
|
+
warnings,
|
|
524
|
+
};
|
|
525
|
+
// Report final results
|
|
526
|
+
yield {
|
|
527
|
+
type: "content",
|
|
528
|
+
content: this.formatPlanResult(planResult),
|
|
529
|
+
};
|
|
530
|
+
// Emit plan completed event
|
|
531
|
+
this.emit("plan:completed", { plan, result: planResult });
|
|
532
|
+
this.currentPlan = null;
|
|
533
|
+
}
|
|
534
|
+
catch (error) {
|
|
535
|
+
yield {
|
|
536
|
+
type: "content",
|
|
537
|
+
content: `\n⚠️ Plan execution error: ${extractErrorMessage(error)}\n`,
|
|
538
|
+
};
|
|
539
|
+
this.emit("plan:failed", { error: extractErrorMessage(error) });
|
|
540
|
+
this.currentPlan = null;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Internal streaming processor (used when planning falls back)
|
|
545
|
+
*/
|
|
546
|
+
async *processUserMessageStreamInternal(_message) {
|
|
547
|
+
// Simplified fallback - just yield a message
|
|
548
|
+
yield {
|
|
549
|
+
type: "content",
|
|
550
|
+
content: "Processing request without planning...\n",
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Format plan summary for display
|
|
555
|
+
*/
|
|
556
|
+
formatPlanSummary(plan) {
|
|
557
|
+
let output = `**📋 Execution Plan Created**\n\n`;
|
|
558
|
+
output += `**Request:** ${plan.originalPrompt.slice(0, 100)}${plan.originalPrompt.length > 100 ? "..." : ""}\n\n`;
|
|
559
|
+
output += `**Phases (${plan.phases.length}):**\n`;
|
|
560
|
+
for (const phase of plan.phases) {
|
|
561
|
+
const riskIcon = phase.riskLevel === "high" ? "⚠️" : phase.riskLevel === "medium" ? "△" : "";
|
|
562
|
+
output += ` ${phase.index + 1}. ${phase.name} ${riskIcon}\n`;
|
|
563
|
+
}
|
|
564
|
+
output += `\n**Estimated Duration:** ~${Math.ceil(plan.estimatedDuration / 60000)} min\n\n`;
|
|
565
|
+
output += "---\n\n";
|
|
566
|
+
return output;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Format plan result for display
|
|
570
|
+
*/
|
|
571
|
+
formatPlanResult(result) {
|
|
572
|
+
let output = "\n---\n\n**📋 Plan Execution Complete**\n\n";
|
|
573
|
+
const successful = result.phaseResults.filter((r) => r.success).length;
|
|
574
|
+
const failed = result.phaseResults.filter((r) => !r.success).length;
|
|
575
|
+
output += `**Results:** ${successful}/${result.phaseResults.length} phases successful`;
|
|
576
|
+
if (failed > 0) {
|
|
577
|
+
output += ` (${failed} failed)`;
|
|
578
|
+
}
|
|
579
|
+
output += "\n";
|
|
580
|
+
if (result.totalDuration) {
|
|
581
|
+
output += `**Duration:** ${Math.ceil(result.totalDuration / 1000)}s\n`;
|
|
582
|
+
}
|
|
583
|
+
if (result.totalTokensUsed) {
|
|
584
|
+
output += `**Tokens Used:** ${result.totalTokensUsed.toLocaleString()}\n`;
|
|
585
|
+
}
|
|
586
|
+
return output;
|
|
587
|
+
}
|
|
204
588
|
async processUserMessage(message) {
|
|
205
589
|
// Reset tool call tracking for new message
|
|
206
590
|
this.resetToolCallTracking();
|
|
@@ -310,6 +694,11 @@ export class LLMAgent extends EventEmitter {
|
|
|
310
694
|
tool_call_id: toolCall.id,
|
|
311
695
|
});
|
|
312
696
|
}
|
|
697
|
+
// Apply context pruning after adding tool results to prevent overflow
|
|
698
|
+
// Tool results can be very large (file reads, grep output, etc.)
|
|
699
|
+
if (this.contextManager.shouldPrune(this.messages, this.tokenCounter)) {
|
|
700
|
+
this.messages = this.contextManager.pruneMessages(this.messages, this.tokenCounter);
|
|
701
|
+
}
|
|
313
702
|
// Get next response - this might contain more tool calls
|
|
314
703
|
currentResponse = await this.llmClient.chat(this.messages, tools, {
|
|
315
704
|
searchOptions: this.isGrokModel() && this.shouldUseSearchFor(message)
|
|
@@ -581,6 +970,11 @@ export class LLMAgent extends EventEmitter {
|
|
|
581
970
|
content: accumulatedMessage.content || "",
|
|
582
971
|
tool_calls: accumulatedMessage.tool_calls,
|
|
583
972
|
});
|
|
973
|
+
// Apply context pruning after adding message to prevent overflow
|
|
974
|
+
// Critical for long assistant responses and tool results
|
|
975
|
+
if (this.contextManager.shouldPrune(this.messages, this.tokenCounter)) {
|
|
976
|
+
this.messages = this.contextManager.pruneMessages(this.messages, this.tokenCounter);
|
|
977
|
+
}
|
|
584
978
|
}
|
|
585
979
|
/**
|
|
586
980
|
* Execute tool calls and yield results
|
|
@@ -625,6 +1019,11 @@ export class LLMAgent extends EventEmitter {
|
|
|
625
1019
|
tool_call_id: toolCall.id,
|
|
626
1020
|
});
|
|
627
1021
|
}
|
|
1022
|
+
// Apply context pruning after adding tool results to prevent overflow
|
|
1023
|
+
// Tool results can be very large (file reads, grep output, etc.)
|
|
1024
|
+
if (this.contextManager.shouldPrune(this.messages, this.tokenCounter)) {
|
|
1025
|
+
this.messages = this.contextManager.pruneMessages(this.messages, this.tokenCounter);
|
|
1026
|
+
}
|
|
628
1027
|
// Update token count after processing all tool calls
|
|
629
1028
|
inputTokens.value = this.tokenCounter.countMessageTokens(this.messages);
|
|
630
1029
|
yield {
|
|
@@ -635,6 +1034,13 @@ export class LLMAgent extends EventEmitter {
|
|
|
635
1034
|
async *processUserMessageStream(message) {
|
|
636
1035
|
// Create new abort controller for this request
|
|
637
1036
|
this.abortController = new AbortController();
|
|
1037
|
+
// Check if this is a complex request that should use multi-phase planning
|
|
1038
|
+
if (this.shouldCreatePlan(message)) {
|
|
1039
|
+
// Delegate to planning processor
|
|
1040
|
+
yield* this.processWithPlanning(message);
|
|
1041
|
+
yield { type: "done" };
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
638
1044
|
// Reset tool call tracking for new message
|
|
639
1045
|
this.resetToolCallTracking();
|
|
640
1046
|
// Prepare user message and get input tokens
|
|
@@ -814,7 +1220,12 @@ export class LLMAgent extends EventEmitter {
|
|
|
814
1220
|
case "str_replace_editor":
|
|
815
1221
|
return await this.textEditor.strReplace(args.path, args.old_str, args.new_str, args.replace_all);
|
|
816
1222
|
case "bash":
|
|
817
|
-
return await this.bash.execute(args.command
|
|
1223
|
+
return await this.bash.execute(args.command, {
|
|
1224
|
+
background: args.background,
|
|
1225
|
+
timeout: args.timeout,
|
|
1226
|
+
});
|
|
1227
|
+
case "bash_output":
|
|
1228
|
+
return await this.bashOutput.execute(args.task_id, args.wait, args.timeout);
|
|
818
1229
|
case "create_todo_list":
|
|
819
1230
|
return await this.todoTool.createTodoList(args.todos);
|
|
820
1231
|
case "update_todo_list":
|
|
@@ -911,6 +1322,19 @@ export class LLMAgent extends EventEmitter {
|
|
|
911
1322
|
async executeBashCommand(command) {
|
|
912
1323
|
return await this.bash.execute(command);
|
|
913
1324
|
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Check if a bash command is currently executing
|
|
1327
|
+
*/
|
|
1328
|
+
isBashExecuting() {
|
|
1329
|
+
return this.bash.isExecuting();
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Move currently running bash command to background
|
|
1333
|
+
* Returns task ID if successful, null otherwise
|
|
1334
|
+
*/
|
|
1335
|
+
moveBashToBackground() {
|
|
1336
|
+
return this.bash.moveToBackground();
|
|
1337
|
+
}
|
|
914
1338
|
getCurrentModel() {
|
|
915
1339
|
return this.llmClient.getCurrentModel();
|
|
916
1340
|
}
|
|
@@ -933,6 +1357,200 @@ export class LLMAgent extends EventEmitter {
|
|
|
933
1357
|
const stats = this.contextManager.getStats(this.messages, this.tokenCounter);
|
|
934
1358
|
return Math.round(stats.percentage * 100) / 100;
|
|
935
1359
|
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Create a checkpoint of current state
|
|
1362
|
+
*/
|
|
1363
|
+
async createCheckpoint(description) {
|
|
1364
|
+
const files = [];
|
|
1365
|
+
// For now, we don't capture file state automatically
|
|
1366
|
+
// This can be enhanced to capture modified files from tool calls
|
|
1367
|
+
const checkpoint = await this.checkpointManager.createCheckpoint({
|
|
1368
|
+
files,
|
|
1369
|
+
conversationState: this.chatHistory,
|
|
1370
|
+
description: description || 'Manual checkpoint',
|
|
1371
|
+
metadata: {
|
|
1372
|
+
model: this.llmClient.getCurrentModel(),
|
|
1373
|
+
triggeredBy: 'user',
|
|
1374
|
+
},
|
|
1375
|
+
});
|
|
1376
|
+
return checkpoint.id;
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Rewind conversation to a checkpoint
|
|
1380
|
+
*/
|
|
1381
|
+
async rewindConversation(checkpointId) {
|
|
1382
|
+
try {
|
|
1383
|
+
const conversationState = await this.checkpointManager.getConversationState(checkpointId);
|
|
1384
|
+
if (!conversationState) {
|
|
1385
|
+
return {
|
|
1386
|
+
success: false,
|
|
1387
|
+
error: `Checkpoint ${checkpointId} not found`,
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
// Restore conversation state
|
|
1391
|
+
this.chatHistory = [...conversationState];
|
|
1392
|
+
// Rebuild messages array from chat history
|
|
1393
|
+
this.messages = [this.messages[0]]; // Keep system message
|
|
1394
|
+
for (const entry of conversationState) {
|
|
1395
|
+
if (entry.type === 'user') {
|
|
1396
|
+
this.messages.push({
|
|
1397
|
+
role: 'user',
|
|
1398
|
+
content: entry.content,
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
else if (entry.type === 'assistant') {
|
|
1402
|
+
this.messages.push({
|
|
1403
|
+
role: 'assistant',
|
|
1404
|
+
content: entry.content,
|
|
1405
|
+
tool_calls: entry.toolCalls,
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
else if (entry.type === 'tool_result' && entry.toolCall) {
|
|
1409
|
+
this.messages.push({
|
|
1410
|
+
role: 'tool',
|
|
1411
|
+
content: entry.content,
|
|
1412
|
+
tool_call_id: entry.toolCall.id,
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
this.emit('system', `Conversation rewound to checkpoint ${checkpointId}`);
|
|
1417
|
+
return { success: true };
|
|
1418
|
+
}
|
|
1419
|
+
catch (error) {
|
|
1420
|
+
const errorMsg = extractErrorMessage(error);
|
|
1421
|
+
return {
|
|
1422
|
+
success: false,
|
|
1423
|
+
error: `Failed to rewind: ${errorMsg}`,
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Get checkpoint manager instance
|
|
1429
|
+
*/
|
|
1430
|
+
getCheckpointManager() {
|
|
1431
|
+
return this.checkpointManager;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Get subagent orchestrator instance
|
|
1435
|
+
*/
|
|
1436
|
+
getSubagentOrchestrator() {
|
|
1437
|
+
return this.subagentOrchestrator;
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Spawn a specialized subagent for a specific task
|
|
1441
|
+
* This is a user-facing method that simplifies subagent usage
|
|
1442
|
+
*
|
|
1443
|
+
* @param role - The role/specialization of the subagent
|
|
1444
|
+
* @param description - Task description
|
|
1445
|
+
* @param context - Optional additional context
|
|
1446
|
+
* @returns The result of the subagent execution
|
|
1447
|
+
*/
|
|
1448
|
+
async spawnSubagent(role, description, context) {
|
|
1449
|
+
try {
|
|
1450
|
+
// Import SubagentRole from subagent-types
|
|
1451
|
+
const { SubagentRole } = await import('./subagent-types.js');
|
|
1452
|
+
// Convert string role to SubagentRole enum
|
|
1453
|
+
const roleMap = {
|
|
1454
|
+
'testing': SubagentRole.TESTING,
|
|
1455
|
+
'documentation': SubagentRole.DOCUMENTATION,
|
|
1456
|
+
'refactoring': SubagentRole.REFACTORING,
|
|
1457
|
+
'analysis': SubagentRole.ANALYSIS,
|
|
1458
|
+
'debug': SubagentRole.DEBUG,
|
|
1459
|
+
'performance': SubagentRole.PERFORMANCE,
|
|
1460
|
+
'general': SubagentRole.GENERAL,
|
|
1461
|
+
};
|
|
1462
|
+
const subagentRole = roleMap[role.toLowerCase()] || SubagentRole.GENERAL;
|
|
1463
|
+
// Spawn the subagent
|
|
1464
|
+
const subagent = await this.subagentOrchestrator.spawnSubagent(subagentRole);
|
|
1465
|
+
// Execute the task
|
|
1466
|
+
const result = await subagent.executeTask({
|
|
1467
|
+
id: `task-${Date.now()}`,
|
|
1468
|
+
description,
|
|
1469
|
+
role: subagentRole,
|
|
1470
|
+
priority: 1,
|
|
1471
|
+
context: {
|
|
1472
|
+
files: context?.files || [],
|
|
1473
|
+
conversationHistory: this.chatHistory.slice(-10), // Last 10 messages
|
|
1474
|
+
metadata: {
|
|
1475
|
+
workingDirectory: process.cwd(),
|
|
1476
|
+
additionalContext: context?.additionalContext,
|
|
1477
|
+
},
|
|
1478
|
+
},
|
|
1479
|
+
});
|
|
1480
|
+
return {
|
|
1481
|
+
success: result.success,
|
|
1482
|
+
output: result.output,
|
|
1483
|
+
filesModified: result.filesModified,
|
|
1484
|
+
filesCreated: result.filesCreated,
|
|
1485
|
+
error: result.error,
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
catch (error) {
|
|
1489
|
+
const errorMsg = extractErrorMessage(error);
|
|
1490
|
+
return {
|
|
1491
|
+
success: false,
|
|
1492
|
+
output: '',
|
|
1493
|
+
error: `Failed to spawn subagent: ${errorMsg}`,
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Execute multiple tasks in parallel using subagents
|
|
1499
|
+
* This automatically handles dependency resolution and parallel execution
|
|
1500
|
+
*
|
|
1501
|
+
* @param tasks - Array of tasks with role and description
|
|
1502
|
+
* @returns Array of results from all tasks
|
|
1503
|
+
*/
|
|
1504
|
+
async executeParallelTasks(tasks) {
|
|
1505
|
+
try {
|
|
1506
|
+
// Import SubagentRole and SubagentTask
|
|
1507
|
+
const { SubagentRole } = await import('./subagent-types.js');
|
|
1508
|
+
const roleMap = {
|
|
1509
|
+
'testing': SubagentRole.TESTING,
|
|
1510
|
+
'documentation': SubagentRole.DOCUMENTATION,
|
|
1511
|
+
'refactoring': SubagentRole.REFACTORING,
|
|
1512
|
+
'analysis': SubagentRole.ANALYSIS,
|
|
1513
|
+
'debug': SubagentRole.DEBUG,
|
|
1514
|
+
'performance': SubagentRole.PERFORMANCE,
|
|
1515
|
+
'general': SubagentRole.GENERAL,
|
|
1516
|
+
};
|
|
1517
|
+
// Convert tasks to SubagentTask format
|
|
1518
|
+
const subagentTasks = tasks.map((task, index) => ({
|
|
1519
|
+
id: task.id || `task-${index}-${Date.now()}`,
|
|
1520
|
+
description: task.description,
|
|
1521
|
+
role: roleMap[task.role.toLowerCase()] || SubagentRole.GENERAL,
|
|
1522
|
+
priority: 1,
|
|
1523
|
+
context: {
|
|
1524
|
+
files: [],
|
|
1525
|
+
conversationHistory: this.chatHistory.slice(-10),
|
|
1526
|
+
metadata: {
|
|
1527
|
+
workingDirectory: process.cwd(),
|
|
1528
|
+
},
|
|
1529
|
+
},
|
|
1530
|
+
dependencies: task.dependencies || [],
|
|
1531
|
+
}));
|
|
1532
|
+
// Execute all tasks in parallel with dependency resolution
|
|
1533
|
+
const results = await this.subagentOrchestrator.executeParallel(subagentTasks);
|
|
1534
|
+
// Convert results to simpler format
|
|
1535
|
+
return results.map(result => ({
|
|
1536
|
+
taskId: result.taskId,
|
|
1537
|
+
success: result.success,
|
|
1538
|
+
output: result.output,
|
|
1539
|
+
filesModified: result.filesModified,
|
|
1540
|
+
filesCreated: result.filesCreated,
|
|
1541
|
+
error: result.error,
|
|
1542
|
+
}));
|
|
1543
|
+
}
|
|
1544
|
+
catch (error) {
|
|
1545
|
+
const errorMsg = extractErrorMessage(error);
|
|
1546
|
+
return [{
|
|
1547
|
+
taskId: 'error',
|
|
1548
|
+
success: false,
|
|
1549
|
+
output: '',
|
|
1550
|
+
error: `Failed to execute parallel tasks: ${errorMsg}`,
|
|
1551
|
+
}];
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
936
1554
|
/**
|
|
937
1555
|
* Dispose of resources and remove event listeners
|
|
938
1556
|
* Call this when the agent is no longer needed
|
|
@@ -945,6 +1563,10 @@ export class LLMAgent extends EventEmitter {
|
|
|
945
1563
|
this.abortController.abort();
|
|
946
1564
|
this.abortController = null;
|
|
947
1565
|
}
|
|
1566
|
+
// Terminate all subagents
|
|
1567
|
+
this.subagentOrchestrator.terminateAll().catch((error) => {
|
|
1568
|
+
console.warn('Error terminating subagents:', error);
|
|
1569
|
+
});
|
|
948
1570
|
}
|
|
949
1571
|
}
|
|
950
1572
|
//# sourceMappingURL=llm-agent.js.map
|