@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.
Files changed (197) hide show
  1. package/.ax-cli/checkpoints/2025-11-20/checkpoint-2dd84869-e62d-46c8-9885-7e45f37f36e2.json +69 -0
  2. package/.ax-cli/checkpoints/2025-11-20/checkpoint-484dc350-353f-4808-9ed1-ebb3cefdab37.json +24 -0
  3. package/.ax-cli/checkpoints/2025-11-20/checkpoint-74a18b87-6172-4215-962b-44bb9f46a662.json +69 -0
  4. package/.ax-cli/checkpoints/2025-11-20/checkpoint-870a5fb9-6e82-4ff2-8ec8-af4c251cc514.json +44 -0
  5. package/.ax-cli/checkpoints/2025-11-20/checkpoint-93946601-0e83-456c-ba47-def9713124dd.json +24 -0
  6. package/.ax-cli/checkpoints/metadata.json +62 -0
  7. package/README.md +87 -11
  8. package/dist/agent/context-manager.d.ts +2 -2
  9. package/dist/agent/context-manager.js +37 -15
  10. package/dist/agent/context-manager.js.map +1 -1
  11. package/dist/agent/dependency-resolver.d.ts +83 -0
  12. package/dist/agent/dependency-resolver.js +310 -0
  13. package/dist/agent/dependency-resolver.js.map +1 -0
  14. package/dist/agent/llm-agent.d.ts +111 -0
  15. package/dist/agent/llm-agent.js +625 -3
  16. package/dist/agent/llm-agent.js.map +1 -1
  17. package/dist/agent/specialized/analysis-agent.d.ts +11 -0
  18. package/dist/agent/specialized/analysis-agent.js +33 -0
  19. package/dist/agent/specialized/analysis-agent.js.map +1 -0
  20. package/dist/agent/specialized/debug-agent.d.ts +11 -0
  21. package/dist/agent/specialized/debug-agent.js +33 -0
  22. package/dist/agent/specialized/debug-agent.js.map +1 -0
  23. package/dist/agent/specialized/documentation-agent.d.ts +11 -0
  24. package/dist/agent/specialized/documentation-agent.js +33 -0
  25. package/dist/agent/specialized/documentation-agent.js.map +1 -0
  26. package/dist/agent/specialized/index.d.ts +11 -0
  27. package/dist/agent/specialized/index.js +12 -0
  28. package/dist/agent/specialized/index.js.map +1 -0
  29. package/dist/agent/specialized/performance-agent.d.ts +11 -0
  30. package/dist/agent/specialized/performance-agent.js +33 -0
  31. package/dist/agent/specialized/performance-agent.js.map +1 -0
  32. package/dist/agent/specialized/refactoring-agent.d.ts +11 -0
  33. package/dist/agent/specialized/refactoring-agent.js +33 -0
  34. package/dist/agent/specialized/refactoring-agent.js.map +1 -0
  35. package/dist/agent/specialized/testing-agent.d.ts +11 -0
  36. package/dist/agent/specialized/testing-agent.js +33 -0
  37. package/dist/agent/specialized/testing-agent.js.map +1 -0
  38. package/dist/agent/subagent-orchestrator.d.ts +128 -0
  39. package/dist/agent/subagent-orchestrator.js +388 -0
  40. package/dist/agent/subagent-orchestrator.js.map +1 -0
  41. package/dist/agent/subagent-types.d.ts +262 -0
  42. package/dist/agent/subagent-types.js +152 -0
  43. package/dist/agent/subagent-types.js.map +1 -0
  44. package/dist/agent/subagent.d.ts +88 -0
  45. package/dist/agent/subagent.js +426 -0
  46. package/dist/agent/subagent.js.map +1 -0
  47. package/dist/checkpoint/index.d.ts +9 -0
  48. package/dist/checkpoint/index.js +11 -0
  49. package/dist/checkpoint/index.js.map +1 -0
  50. package/dist/checkpoint/manager.d.ts +99 -0
  51. package/dist/checkpoint/manager.js +281 -0
  52. package/dist/checkpoint/manager.js.map +1 -0
  53. package/dist/checkpoint/storage.d.ts +31 -0
  54. package/dist/checkpoint/storage.js +265 -0
  55. package/dist/checkpoint/storage.js.map +1 -0
  56. package/dist/checkpoint/types.d.ts +111 -0
  57. package/dist/checkpoint/types.js +17 -0
  58. package/dist/checkpoint/types.js.map +1 -0
  59. package/dist/commands/cache.js +5 -3
  60. package/dist/commands/cache.js.map +1 -1
  61. package/dist/commands/memory.js +21 -16
  62. package/dist/commands/memory.js.map +1 -1
  63. package/dist/commands/plan.d.ts +43 -0
  64. package/dist/commands/plan.js +385 -0
  65. package/dist/commands/plan.js.map +1 -0
  66. package/dist/commands/rewind.d.ts +19 -0
  67. package/dist/commands/rewind.js +221 -0
  68. package/dist/commands/rewind.js.map +1 -0
  69. package/dist/constants.d.ts +28 -0
  70. package/dist/constants.js +29 -0
  71. package/dist/constants.js.map +1 -1
  72. package/dist/hooks/use-enhanced-input.d.ts +5 -1
  73. package/dist/hooks/use-enhanced-input.js +23 -10
  74. package/dist/hooks/use-enhanced-input.js.map +1 -1
  75. package/dist/hooks/use-input-handler.d.ts +11 -1
  76. package/dist/hooks/use-input-handler.js +294 -2
  77. package/dist/hooks/use-input-handler.js.map +1 -1
  78. package/dist/llm/client.js +2 -1
  79. package/dist/llm/client.js.map +1 -1
  80. package/dist/llm/tools.d.ts +5 -0
  81. package/dist/llm/tools.js +57 -6
  82. package/dist/llm/tools.js.map +1 -1
  83. package/dist/mcp/client.d.ts +1 -0
  84. package/dist/mcp/client.js +30 -8
  85. package/dist/mcp/client.js.map +1 -1
  86. package/dist/mcp/transports.d.ts +0 -1
  87. package/dist/mcp/transports.js +10 -7
  88. package/dist/mcp/transports.js.map +1 -1
  89. package/dist/planner/dependency-resolver.d.ts +72 -0
  90. package/dist/planner/dependency-resolver.js +272 -0
  91. package/dist/planner/dependency-resolver.js.map +1 -0
  92. package/dist/planner/index.d.ts +12 -0
  93. package/dist/planner/index.js +26 -0
  94. package/dist/planner/index.js.map +1 -0
  95. package/dist/planner/plan-generator.d.ts +74 -0
  96. package/dist/planner/plan-generator.js +244 -0
  97. package/dist/planner/plan-generator.js.map +1 -0
  98. package/dist/planner/plan-storage.d.ts +98 -0
  99. package/dist/planner/plan-storage.js +325 -0
  100. package/dist/planner/plan-storage.js.map +1 -0
  101. package/dist/planner/prompts/planning-prompt.d.ts +41 -0
  102. package/dist/planner/prompts/planning-prompt.js +289 -0
  103. package/dist/planner/prompts/planning-prompt.js.map +1 -0
  104. package/dist/planner/task-planner.d.ts +135 -0
  105. package/dist/planner/task-planner.js +493 -0
  106. package/dist/planner/task-planner.js.map +1 -0
  107. package/dist/planner/token-estimator.d.ts +63 -0
  108. package/dist/planner/token-estimator.js +295 -0
  109. package/dist/planner/token-estimator.js.map +1 -0
  110. package/dist/planner/types.d.ts +669 -0
  111. package/dist/planner/types.js +213 -0
  112. package/dist/planner/types.js.map +1 -0
  113. package/dist/schemas/api-schemas.js +4 -0
  114. package/dist/schemas/api-schemas.js.map +1 -1
  115. package/dist/schemas/confirmation-schemas.d.ts +5 -0
  116. package/dist/schemas/confirmation-schemas.js +7 -0
  117. package/dist/schemas/confirmation-schemas.js.map +1 -1
  118. package/dist/schemas/index.d.ts +4 -4
  119. package/dist/tools/bash-output.d.ts +25 -0
  120. package/dist/tools/bash-output.js +145 -0
  121. package/dist/tools/bash-output.js.map +1 -0
  122. package/dist/tools/bash.d.ts +46 -2
  123. package/dist/tools/bash.js +241 -42
  124. package/dist/tools/bash.js.map +1 -1
  125. package/dist/tools/search.js +34 -9
  126. package/dist/tools/search.js.map +1 -1
  127. package/dist/tools/text-editor.d.ts +16 -0
  128. package/dist/tools/text-editor.js +37 -2
  129. package/dist/tools/text-editor.js.map +1 -1
  130. package/dist/ui/components/chat-history.d.ts +1 -0
  131. package/dist/ui/components/chat-history.js +125 -41
  132. package/dist/ui/components/chat-history.js.map +1 -1
  133. package/dist/ui/components/chat-input.js +10 -3
  134. package/dist/ui/components/chat-input.js.map +1 -1
  135. package/dist/ui/components/chat-interface.js +154 -45
  136. package/dist/ui/components/chat-interface.js.map +1 -1
  137. package/dist/ui/components/collapsible-tool-result.d.ts +26 -0
  138. package/dist/ui/components/collapsible-tool-result.js +172 -0
  139. package/dist/ui/components/collapsible-tool-result.js.map +1 -0
  140. package/dist/ui/components/command-suggestions.js +2 -1
  141. package/dist/ui/components/command-suggestions.js.map +1 -1
  142. package/dist/ui/components/confirmation-dialog.js +25 -36
  143. package/dist/ui/components/confirmation-dialog.js.map +1 -1
  144. package/dist/ui/components/index.d.ts +8 -0
  145. package/dist/ui/components/index.js +9 -0
  146. package/dist/ui/components/index.js.map +1 -1
  147. package/dist/ui/components/keyboard-hints.d.ts +35 -0
  148. package/dist/ui/components/keyboard-hints.js +134 -0
  149. package/dist/ui/components/keyboard-hints.js.map +1 -0
  150. package/dist/ui/components/loading-spinner.d.ts +2 -1
  151. package/dist/ui/components/loading-spinner.js +86 -34
  152. package/dist/ui/components/loading-spinner.js.map +1 -1
  153. package/dist/ui/components/phase-progress.d.ts +21 -0
  154. package/dist/ui/components/phase-progress.js +228 -0
  155. package/dist/ui/components/phase-progress.js.map +1 -0
  156. package/dist/ui/components/quick-actions.d.ts +12 -0
  157. package/dist/ui/components/quick-actions.js +122 -0
  158. package/dist/ui/components/quick-actions.js.map +1 -0
  159. package/dist/ui/components/reasoning-display.d.ts +0 -80
  160. package/dist/ui/components/reasoning-display.js +0 -83
  161. package/dist/ui/components/reasoning-display.js.map +1 -1
  162. package/dist/ui/components/status-bar.d.ts +25 -0
  163. package/dist/ui/components/status-bar.js +125 -0
  164. package/dist/ui/components/status-bar.js.map +1 -0
  165. package/dist/ui/components/subagent-monitor.d.ts +29 -0
  166. package/dist/ui/components/subagent-monitor.js +150 -0
  167. package/dist/ui/components/subagent-monitor.js.map +1 -0
  168. package/dist/ui/components/toast-notification.d.ts +123 -0
  169. package/dist/ui/components/toast-notification.js +143 -0
  170. package/dist/ui/components/toast-notification.js.map +1 -0
  171. package/dist/ui/components/welcome-panel.d.ts +10 -0
  172. package/dist/ui/components/welcome-panel.js +107 -0
  173. package/dist/ui/components/welcome-panel.js.map +1 -0
  174. package/dist/utils/background-task-manager.d.ts +95 -0
  175. package/dist/utils/background-task-manager.js +330 -0
  176. package/dist/utils/background-task-manager.js.map +1 -0
  177. package/dist/utils/confirmation-service.js +8 -3
  178. package/dist/utils/confirmation-service.js.map +1 -1
  179. package/dist/utils/history-manager.d.ts +1 -0
  180. package/dist/utils/history-manager.js +40 -5
  181. package/dist/utils/history-manager.js.map +1 -1
  182. package/dist/utils/json-utils.d.ts +7 -0
  183. package/dist/utils/json-utils.js +70 -2
  184. package/dist/utils/json-utils.js.map +1 -1
  185. package/dist/utils/message-optimizer.d.ts +1 -0
  186. package/dist/utils/message-optimizer.js +7 -1
  187. package/dist/utils/message-optimizer.js.map +1 -1
  188. package/dist/utils/project-analyzer.js +5 -2
  189. package/dist/utils/project-analyzer.js.map +1 -1
  190. package/dist/utils/settings-manager.js +12 -10
  191. package/dist/utils/settings-manager.js.map +1 -1
  192. package/dist/utils/text-utils.js +3 -3
  193. package/dist/utils/text-utils.js.map +1 -1
  194. package/dist/utils/token-counter.d.ts +5 -0
  195. package/dist/utils/token-counter.js +18 -3
  196. package/dist/utils/token-counter.js.map +1 -1
  197. package/package.json +3 -1
@@ -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
- // Map.keys().next().value is guaranteed to exist when size > 0, but TypeScript doesn't know this
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