orquesta-cli 0.2.45 → 0.2.47

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 (40) hide show
  1. package/dist/agents/planner/index.js +2 -1
  2. package/dist/cli.js +17 -16
  3. package/dist/constants.d.ts +1 -1
  4. package/dist/constants.js +1 -1
  5. package/dist/core/commands/clear.d.ts +3 -0
  6. package/dist/core/commands/clear.js +22 -0
  7. package/dist/core/commands/compact.d.ts +3 -0
  8. package/dist/core/commands/compact.js +45 -0
  9. package/dist/core/commands/help.d.ts +3 -0
  10. package/dist/core/commands/help.js +50 -0
  11. package/dist/core/commands/index.d.ts +3 -0
  12. package/dist/core/commands/index.js +11 -0
  13. package/dist/core/commands/memory.d.ts +3 -0
  14. package/dist/core/commands/memory.js +40 -0
  15. package/dist/core/commands/registry.d.ts +11 -0
  16. package/dist/core/commands/registry.js +25 -0
  17. package/dist/core/commands/types.d.ts +10 -0
  18. package/dist/core/commands/types.js +2 -0
  19. package/dist/core/event-bus.d.ts +20 -0
  20. package/dist/core/event-bus.js +35 -0
  21. package/dist/core/git-context.d.ts +11 -0
  22. package/dist/core/git-context.js +62 -0
  23. package/dist/core/ignore-filter.d.ts +4 -0
  24. package/dist/core/ignore-filter.js +50 -0
  25. package/dist/core/llm/llm-client.d.ts +1 -0
  26. package/dist/core/llm/llm-client.js +118 -40
  27. package/dist/core/onboarding.d.ts +3 -0
  28. package/dist/core/onboarding.js +48 -0
  29. package/dist/core/slash-command-handler.js +8 -135
  30. package/dist/eval/eval-runner.d.ts +1 -0
  31. package/dist/eval/eval-runner.js +22 -0
  32. package/dist/orchestration/plan-executor.js +77 -71
  33. package/dist/prompts/shared/tool-usage.js +0 -1
  34. package/dist/prompts/system/plan-execute.js +50 -57
  35. package/dist/tools/llm/simple/file-tools.js +12 -1
  36. package/dist/tools/llm/simple/final-response-tool.js +7 -11
  37. package/dist/tools/registry.js +63 -10
  38. package/dist/ui/components/PlanExecuteApp.d.ts +1 -0
  39. package/dist/ui/components/PlanExecuteApp.js +59 -22
  40. package/package.json +8 -4
@@ -4,12 +4,14 @@ import { CompactManager, contextTracker, buildCompactedMessages, } from '../core
4
4
  import { configManager } from '../core/config/config-manager.js';
5
5
  import { setTodoWriteCallback, clearTodoCallbacks, } from '../tools/llm/simple/todo-tools.js';
6
6
  import { setGetTodosCallback, setFinalResponseCallback, setMarkTodosCompletedCallback, clearFinalResponseCallbacks, } from '../tools/llm/simple/final-response-tool.js';
7
+ import { eventBus, Events } from '../core/event-bus.js';
7
8
  import { setDocsSearchLLMClientGetter, clearDocsSearchLLMClientGetter, } from '../tools/llm/simple/docs-search-agent-tool.js';
8
9
  import { emitPlanCreated, emitTodoStart, emitTodoComplete, emitTodoFail, emitCompact, emitAssistantResponse, } from '../tools/llm/simple/file-tools.js';
9
10
  import { toolRegistry } from '../tools/registry.js';
10
11
  import { PLAN_EXECUTE_SYSTEM_PROMPT as PLAN_PROMPT } from '../prompts/system/plan-execute.js';
11
12
  import { getProjectContext } from '../core/project-context.js';
12
13
  import { getMemoryPrompt } from '../core/memory.js';
14
+ import { getGitContextPrompt } from '../core/git-context.js';
13
15
  import { GIT_COMMIT_RULES } from '../prompts/shared/git-rules.js';
14
16
  import { logger } from '../utils/logger.js';
15
17
  import { getStreamLogger } from '../utils/json-stream-logger.js';
@@ -34,7 +36,7 @@ function buildSystemPrompt() {
34
36
  const projectContext = getProjectContext();
35
37
  const base = isGitRepo ? `${PLAN_PROMPT}\n\n${GIT_COMMIT_RULES}` : PLAN_PROMPT;
36
38
  const appended = appendedSystemPrompt ? `\n\n${appendedSystemPrompt}` : '';
37
- return base + buildEnvironmentContext() + projectContext + getMemoryPrompt() + appended;
39
+ return base + buildEnvironmentContext() + projectContext + getMemoryPrompt() + getGitContextPrompt() + appended;
38
40
  }
39
41
  export class PlanExecutor {
40
42
  currentLLMClient = null;
@@ -80,74 +82,82 @@ export class PlanExecutor {
80
82
  throw new Error('INTERRUPTED');
81
83
  }
82
84
  let currentMessages = messages;
83
- callbacks.setCurrentActivity('Thinking');
84
- const plannerModel = configManager.getRoleModel('planner');
85
- const planningLLM = new PlanningLLM(llmClient, plannerModel ?? undefined);
86
- const plannerStartedAt = Date.now();
87
- if (callbacks.askUser) {
88
- planningLLM.setAskUserCallback(callbacks.askUser);
85
+ const isSimpleTask = userMessage.length < 500 &&
86
+ !/\b(and then|after that|first.*then|step \d|multiple|several|refactor.*entire|migrate|rewrite.*all)\b/i.test(userMessage);
87
+ if (isSimpleTask) {
88
+ logger.flow('Simple task detected — skipping planner, executor will handle directly');
89
+ streamLogger?.logPlanningEnd(0, [], false, 0);
89
90
  }
90
- const planResult = await planningLLM.generateTODOListWithDocsDecision(userMessage, currentMessages);
91
- auditLog.emit(auditSid, 'planner.complete', {
92
- runId,
93
- model: plannerModel,
94
- durationMs: Date.now() - plannerStartedAt,
95
- todoCount: planResult.todos.length,
96
- directResponse: !!planResult.directResponse,
97
- });
98
- if (planResult.clarificationMessages?.length) {
99
- currentMessages = [...currentMessages, ...planResult.clarificationMessages];
100
- callbacks.setMessages([...currentMessages]);
101
- logger.flow('Added planning clarification messages to history', {
102
- count: planResult.clarificationMessages.length,
91
+ else {
92
+ callbacks.setCurrentActivity('Thinking');
93
+ const plannerModel = configManager.getRoleModel('planner');
94
+ const planningLLM = new PlanningLLM(llmClient, plannerModel ?? undefined);
95
+ const plannerStartedAt = Date.now();
96
+ if (callbacks.askUser) {
97
+ planningLLM.setAskUserCallback(callbacks.askUser);
98
+ }
99
+ const planResult = await planningLLM.generateTODOListWithDocsDecision(userMessage, currentMessages);
100
+ auditLog.emit(auditSid, 'planner.complete', {
101
+ runId,
102
+ model: plannerModel,
103
+ durationMs: Date.now() - plannerStartedAt,
104
+ todoCount: planResult.todos.length,
105
+ directResponse: !!planResult.directResponse,
103
106
  });
104
- }
105
- if (planResult.directResponse) {
106
- logger.flow('Direct response - no execution needed');
107
- streamLogger?.logPlanningEnd(0, [], true, Date.now() - planningStartTime);
108
- const lastMsg = currentMessages[currentMessages.length - 1];
109
- const needsUserMessage = !(lastMsg?.role === 'user' && lastMsg?.content === userMessage);
110
- const updatedMessages = needsUserMessage
107
+ if (planResult.clarificationMessages?.length) {
108
+ currentMessages = [...currentMessages, ...planResult.clarificationMessages];
109
+ callbacks.setMessages([...currentMessages]);
110
+ logger.flow('Added planning clarification messages to history', {
111
+ count: planResult.clarificationMessages.length,
112
+ });
113
+ }
114
+ if (planResult.directResponse) {
115
+ logger.flow('Direct response - no execution needed');
116
+ streamLogger?.logPlanningEnd(0, [], true, Date.now() - planningStartTime);
117
+ const lastMsg = currentMessages[currentMessages.length - 1];
118
+ const needsUserMessage = !(lastMsg?.role === 'user' && lastMsg?.content === userMessage);
119
+ const updatedMessages = needsUserMessage
120
+ ? [
121
+ ...currentMessages,
122
+ { role: 'user', content: userMessage },
123
+ { role: 'assistant', content: planResult.directResponse }
124
+ ]
125
+ : [
126
+ ...currentMessages,
127
+ { role: 'assistant', content: planResult.directResponse }
128
+ ];
129
+ emitAssistantResponse(planResult.directResponse);
130
+ callbacks.setMessages([...updatedMessages]);
131
+ sessionManager.autoSaveCurrentSession(updatedMessages);
132
+ callbacks.setExecutionPhase('idle');
133
+ logger.exit('PlanExecutor.executePlanMode', { directResponse: true });
134
+ return;
135
+ }
136
+ currentTodos = planResult.todos;
137
+ streamLogger?.logPlanningEnd(currentTodos.length, currentTodos.map(t => ({ id: t.id, title: t.title, status: t.status })), false, Date.now() - planningStartTime);
138
+ logger.vars({ name: 'todoCount', value: currentTodos.length }, { name: 'docsSearchNeeded', value: planResult.docsSearchNeeded });
139
+ callbacks.setTodos(currentTodos);
140
+ emitPlanCreated(currentTodos.map(t => t.title));
141
+ const planMessage = planResult.docsSearchNeeded
142
+ ? `📋 Created ${currentTodos.length} tasks (including docs search). Starting execution...`
143
+ : `📋 Created ${currentTodos.length} tasks. Starting execution...`;
144
+ const lastMsgForPlan = currentMessages[currentMessages.length - 1];
145
+ const needsUserMessageForPlan = !(lastMsgForPlan?.role === 'user' && lastMsgForPlan?.content === userMessage);
146
+ currentMessages = needsUserMessageForPlan
111
147
  ? [
112
148
  ...currentMessages,
113
149
  { role: 'user', content: userMessage },
114
- { role: 'assistant', content: planResult.directResponse }
150
+ { role: 'assistant', content: planMessage }
115
151
  ]
116
152
  : [
117
153
  ...currentMessages,
118
- { role: 'assistant', content: planResult.directResponse }
154
+ { role: 'assistant', content: planMessage }
119
155
  ];
120
- emitAssistantResponse(planResult.directResponse);
121
- callbacks.setMessages([...updatedMessages]);
122
- sessionManager.autoSaveCurrentSession(updatedMessages);
123
- callbacks.setExecutionPhase('idle');
124
- logger.exit('PlanExecutor.executePlanMode', { directResponse: true });
125
- return;
156
+ callbacks.setMessages(currentMessages);
157
+ this.setupTodoCallbacks(currentTodos, callbacks, (updated) => {
158
+ currentTodos = updated;
159
+ });
126
160
  }
127
- currentTodos = planResult.todos;
128
- streamLogger?.logPlanningEnd(currentTodos.length, currentTodos.map(t => ({ id: t.id, title: t.title, status: t.status })), false, Date.now() - planningStartTime);
129
- logger.vars({ name: 'todoCount', value: currentTodos.length }, { name: 'docsSearchNeeded', value: planResult.docsSearchNeeded });
130
- callbacks.setTodos(currentTodos);
131
- emitPlanCreated(currentTodos.map(t => t.title));
132
- const planMessage = planResult.docsSearchNeeded
133
- ? `📋 Created ${currentTodos.length} tasks (including docs search). Starting execution...`
134
- : `📋 Created ${currentTodos.length} tasks. Starting execution...`;
135
- const lastMsgForPlan = currentMessages[currentMessages.length - 1];
136
- const needsUserMessageForPlan = !(lastMsgForPlan?.role === 'user' && lastMsgForPlan?.content === userMessage);
137
- currentMessages = needsUserMessageForPlan
138
- ? [
139
- ...currentMessages,
140
- { role: 'user', content: userMessage },
141
- { role: 'assistant', content: planMessage }
142
- ]
143
- : [
144
- ...currentMessages,
145
- { role: 'assistant', content: planMessage }
146
- ];
147
- callbacks.setMessages(currentMessages);
148
- this.setupTodoCallbacks(currentTodos, callbacks, (updated) => {
149
- currentTodos = updated;
150
- });
151
161
  callbacks.setExecutionPhase('executing');
152
162
  const tools = toolRegistry.getLLMToolDefinitions();
153
163
  const hasSystemMessage = currentMessages.some(m => m.role === 'system');
@@ -198,12 +208,9 @@ export class PlanExecutor {
198
208
  }
199
209
  else {
200
210
  const todoContext = buildTodoContext(currentTodos);
201
- const lastUserMsgIndex = currentMessages.map(m => m.role).lastIndexOf('user');
202
- const messagesForLLM = lastUserMsgIndex >= 0
203
- ? currentMessages.map((m, i) => i === lastUserMsgIndex
204
- ? { ...m, content: m.content + todoContext }
205
- : m)
206
- : [...currentMessages, { role: 'user', content: `Execute the TODO list.${todoContext}` }];
211
+ const messagesForLLM = todoContext
212
+ ? [...currentMessages, { role: 'user', content: `[Current task status]${todoContext}` }]
213
+ : currentMessages;
207
214
  const executorModel = configManager.getRoleModel('executor');
208
215
  const result = await llmClient.chatCompletionWithTools(messagesForLLM, tools, {
209
216
  getPendingMessage: callbacks.getPendingMessage,
@@ -314,12 +321,9 @@ export class PlanExecutor {
314
321
  const activeTodo = findActiveTodo(currentTodos);
315
322
  callbacks.setCurrentActivity(activeTodo?.title || 'Working on tasks');
316
323
  const todoContext = buildTodoContext(currentTodos);
317
- const lastUserMsgIndex = currentMessages.map(m => m.role).lastIndexOf('user');
318
- const messagesForLLM = lastUserMsgIndex >= 0
319
- ? currentMessages.map((m, i) => i === lastUserMsgIndex
320
- ? { ...m, content: m.content + todoContext }
321
- : m)
322
- : [...currentMessages, { role: 'user', content: `Resume the TODO list.${todoContext}` }];
324
+ const messagesForLLM = todoContext
325
+ ? [...currentMessages, { role: 'user', content: `[Current task status]${todoContext}` }]
326
+ : currentMessages;
323
327
  const executorModel = configManager.getRoleModel('executor');
324
328
  const result = await llmClient.chatCompletionWithTools(messagesForLLM, tools, {
325
329
  getPendingMessage: callbacks.getPendingMessage,
@@ -460,6 +464,8 @@ export class PlanExecutor {
460
464
  setFinalResponseCallback((message) => {
461
465
  emitAssistantResponse(message);
462
466
  });
467
+ void eventBus.on(Events.FINAL_RESPONSE, (_message) => {
468
+ });
463
469
  setMarkTodosCompletedCallback(() => {
464
470
  const completed = todosRef.map(t => t.status === 'completed' || t.status === 'failed'
465
471
  ? t
@@ -22,7 +22,6 @@ export const AVAILABLE_TOOLS_WITH_TODO = `
22
22
  - **tell_to_user**: Send status updates to the user
23
23
  - **ask_to_user**: Ask user a question with multiple choice options
24
24
  - **write_todos**: Update entire TODO list (replaces current list)
25
- - **call_docs_search_agent**: Search local documentation (~/.local-cli/docs)
26
25
  `.trim();
27
26
  export const TOOL_REASON_GUIDE = `
28
27
  ## CRITICAL - Tool "reason" Parameter
@@ -1,83 +1,76 @@
1
1
  import { LANGUAGE_PRIORITY_RULE } from '../shared/language-rules.js';
2
- import { AVAILABLE_TOOLS_WITH_TODO, TOOL_REASON_GUIDE } from '../shared/tool-usage.js';
3
2
  import { CODEBASE_FIRST_RULE } from '../shared/codebase-rules.js';
4
- export const PLAN_EXECUTE_SYSTEM_PROMPT = `You are an AI assistant executing a TODO-based plan.
3
+ export const PLAN_EXECUTE_SYSTEM_PROMPT = `You are Orquesta, an expert AI coding assistant working in the user's terminal. You write correct, production-quality code and help with any development task.
5
4
 
6
5
  ${LANGUAGE_PRIORITY_RULE}
7
6
 
8
- ## TODO Workflow
7
+ ## How You Work
9
8
 
10
- 1. Work through TODOs systematically
11
- 2. Update status using \`write_todos\` (include ALL todos with current status)
12
- 3. **DONE when ALL TODOs are "completed"**
9
+ 1. **Understand first** Read relevant code before modifying it. Never guess file contents.
10
+ 2. **Act, don't describe** Use tools to do the work. Don't say "I would do X", just do X.
11
+ 3. **Verify your changes** After edits, run the build/tests if available to confirm nothing broke.
12
+ 4. **Be concise** — Short answers for simple questions. Thorough work for complex tasks.
13
+ 5. **Match the user's intent** — Do what was asked, no more. Don't add unrequested features or refactors.
13
14
 
14
- **CRITICAL: Keep TODO status in sync with your actual progress!**
15
- - When starting a task → mark it "in_progress" IMMEDIATELY
16
- - When finishing a task → mark it "completed" IMMEDIATELY
17
- - The user sees the TODO list in real-time - mismatched status is confusing
18
- - Call \`write_todos\` FREQUENTLY, not just at the end
15
+ ## Decision Framework
19
16
 
20
- ${AVAILABLE_TOOLS_WITH_TODO}
17
+ - **Simple questions** (what is X, explain Y): Respond directly with knowledge.
18
+ - **Code tasks** (fix, add, edit, refactor): Read → Edit → Verify. Use tools.
19
+ - **Investigation** (why is this failing, what does X do): Read code, search, then explain.
20
+ - **Ambiguous requests**: Infer the most useful action and proceed. Only ask if truly blocked.
21
21
 
22
- ${TOOL_REASON_GUIDE}
22
+ ## Tool Usage
23
23
 
24
- ## Execution Rules
24
+ Use tools for all file operations, commands, and code changes. Your available tools:
25
+ - **read_file**: Always read before editing
26
+ - **edit_file**: Modify existing files (match exact content for old_string)
27
+ - **create_file**: Create new files
28
+ - **list_files** / **find_files** / **search_content**: Navigate the codebase
29
+ - **bash**: Run commands (build, test, git, etc.)
30
+ - **tell_to_user**: Show progress updates
31
+ - **write_todos**: Track task progress (for multi-step work)
25
32
 
26
- 1. **Read before modify** - Always read existing code first
27
- 2. **Use tools** - Perform actual work, don't just describe
28
- 3. **Retry on error** - Up to 3 attempts before marking "failed"
29
- 4. **Stay focused** - Only work on TODOs, no unrelated features
33
+ Every tool has a "reason" parameter shown to the user. Write it naturally in the user's language.
30
34
 
31
- ${CODEBASE_FIRST_RULE}
32
-
33
- ## CRITICAL: Tool Error Handling
34
-
35
- **If a tool returns an error, you MUST retry the same tool with corrected parameters.**
35
+ ## Code Quality Rules
36
36
 
37
- 1. STOP - Read the error message carefully
38
- 2. Investigate - Use \`read_file\` to check actual file content
39
- 3. **RETRY THE SAME TOOL** with corrected parameters (DO NOT skip or move on)
40
- 4. Repeat until success or 3 failures
37
+ - Write minimal, correct code that solves the problem
38
+ - Follow existing project conventions (style, naming, patterns)
39
+ - Use secure coding practices by default
40
+ - Don't introduce new dependencies unless necessary
41
+ - Include error handling where appropriate
41
42
 
42
- **You are NOT allowed to:**
43
- - Skip the failed tool and move to next task
44
- - Say "I'll try a different approach" without actually retrying
45
- - Mark TODO as complete if the tool failed
43
+ ## Error Handling
46
44
 
47
- Example flow:
48
- 1. \`edit_file\` fails: "Line 77 content does not match"
49
- 2. Call \`read_file\` to see actual content
50
- 3. **Call \`edit_file\` again** with correct \`old_string\`
51
- 4. Only proceed after edit succeeds
45
+ If a tool fails:
46
+ 1. Read the error carefully
47
+ 2. Investigate (read_file to check actual content)
48
+ 3. Retry with corrected parameters
49
+ 4. Only give up after 3 failed attempts
52
50
 
53
- ## CRITICAL: When to Respond
51
+ Common edit_file failures: wrong old_string → re-read file, copy exact text, retry.
54
52
 
55
- **ONLY respond when ALL TODOs are "completed" or "failed".**
56
-
57
- - Responding early = execution ends prematurely
58
- - Use \`tell_to_user\` to communicate progress during execution
59
- - \`write_todos\` only updates internal state
60
-
61
- **Before final response, verify:**
62
- - All TODOs completed?
63
- - All tool calls successful?
64
- - User's request fulfilled?
53
+ ${CODEBASE_FIRST_RULE}
65
54
 
66
- ## CRITICAL: Final Response
55
+ ## Response Style
67
56
 
68
- Your final response MUST contain the **actual answer or result**:
69
- - Question Answer with information found
70
- - Task Summarize what was done
57
+ - Direct and concise. No filler phrases.
58
+ - Code in markdown blocks with language tags.
59
+ - When summarizing completed work: state what was done in 1-3 sentences.
60
+ - Match the user's language (if they write in Spanish, respond in Spanish).
71
61
 
72
- **DO NOT** just say "Task complete" or give task statistics.
62
+ ## TODO Workflow (for multi-step tasks)
73
63
 
74
- Example:
75
- - User: "What's the project name?" "This project is **LOCAL-CLI**."
76
- - User: "Add a debug function""Added debug function to logger.ts."
64
+ When working on a plan with TODOs:
65
+ - Update status via write_todos as you progress
66
+ - Mark "in_progress" when starting, "completed" when done
67
+ - Stay focused on the current task
77
68
 
78
- ## Loop Detection
69
+ ## IMPORTANT
79
70
 
80
- If TODO context keeps repeating but work is done IMMEDIATELY mark all as "completed".
71
+ - You can respond directly without using any tool do so for simple questions or when you're done working.
72
+ - After completing all requested work, give a brief summary of what was done.
73
+ - Never fabricate file contents, paths, or command outputs. If unsure, investigate first.
81
74
  `;
82
75
  export default PLAN_EXECUTE_SYSTEM_PROMPT;
83
76
  //# sourceMappingURL=plan-execute.js.map
@@ -1,6 +1,7 @@
1
1
  import * as fs from 'fs/promises';
2
2
  import * as path from 'path';
3
3
  import { logger } from '../../../utils/logger.js';
4
+ import { shouldIgnore } from '../../../core/ignore-filter.js';
4
5
  const EXCLUDED_DIRS = new Set([
5
6
  'node_modules',
6
7
  '.git',
@@ -451,6 +452,9 @@ async function getFilesRecursively(dirPath, baseDir = dirPath, depth = 0, fileCo
451
452
  }
452
453
  const fullPath = path.join(dirPath, entry.name);
453
454
  const relativePath = path.relative(baseDir, fullPath);
455
+ if (shouldIgnore(fullPath)) {
456
+ continue;
457
+ }
454
458
  if (entry.isDirectory()) {
455
459
  if (EXCLUDED_DIRS.has(entry.name)) {
456
460
  continue;
@@ -491,7 +495,9 @@ async function _executeListFilesInternal(args) {
491
495
  }
492
496
  else {
493
497
  const entries = await fs.readdir(resolvedPath, { withFileTypes: true });
494
- const files = entries.map((entry) => ({
498
+ const files = entries
499
+ .filter(entry => !shouldIgnore(path.join(resolvedPath, entry.name)))
500
+ .map((entry) => ({
495
501
  name: entry.name,
496
502
  type: entry.isDirectory() ? 'directory' : 'file',
497
503
  path: path.join(directoryPath, entry.name),
@@ -580,6 +586,9 @@ async function findFilesRecursively(dirPath, regex, baseDir, depth = 0, fileCoun
580
586
  continue;
581
587
  }
582
588
  const fullPath = path.join(dirPath, entry.name);
589
+ if (shouldIgnore(fullPath)) {
590
+ continue;
591
+ }
583
592
  if (entry.isDirectory()) {
584
593
  if (EXCLUDED_DIRS.has(entry.name)) {
585
594
  continue;
@@ -700,6 +709,8 @@ async function searchContentRecursively(dirPath, searchRegex, baseDir, fileRegex
700
709
  if (entry.name.startsWith('.'))
701
710
  continue;
702
711
  const fullPath = path.join(dirPath, entry.name);
712
+ if (shouldIgnore(fullPath))
713
+ continue;
703
714
  if (entry.isDirectory()) {
704
715
  if (EXCLUDED_DIRS.has(entry.name))
705
716
  continue;
@@ -1,4 +1,5 @@
1
1
  import { logger } from '../../../utils/logger.js';
2
+ import { eventBus, Events } from '../../../core/event-bus.js';
2
3
  let getTodosCallback = null;
3
4
  let finalResponseCallback = null;
4
5
  let markTodosCompletedCallback = null;
@@ -28,23 +29,16 @@ const FINAL_RESPONSE_DEFINITION = {
28
29
  type: 'function',
29
30
  function: {
30
31
  name: 'final_response',
31
- description: `Use this tool to deliver your final response to the user after completing all tasks.
32
+ description: `Deliver a final summary to the user after completing work. Optional — you can also respond directly without this tool.
32
33
 
33
- IMPORTANT:
34
- - You MUST complete all TODOs before calling this tool
35
- - If any TODO is not completed, this tool will return an error
36
- - After all tasks are done, use this tool to summarize what was accomplished
37
-
38
- Example:
39
- {
40
- "message": "I've completed all the requested tasks:\\n\\n1. Fixed the bug in the login form\\n2. Added input validation\\n3. Updated the tests\\n\\nAll changes have been committed."
41
- }`,
34
+ Use this when you want to explicitly signal task completion with a summary.
35
+ Any incomplete TODOs will be auto-marked as done.`,
42
36
  parameters: {
43
37
  type: 'object',
44
38
  properties: {
45
39
  message: {
46
40
  type: 'string',
47
- description: 'Your final response message to the user. Summarize what was accomplished.',
41
+ description: 'Your final response message to the user.',
48
42
  },
49
43
  },
50
44
  required: ['message'],
@@ -138,6 +132,7 @@ async function executeFinalResponse(args) {
138
132
  if (finalResponseCallback) {
139
133
  finalResponseCallback(message);
140
134
  }
135
+ eventBus.emit(Events.FINAL_RESPONSE, message);
141
136
  return {
142
137
  success: true,
143
138
  result: message,
@@ -163,6 +158,7 @@ async function executeFinalResponse(args) {
163
158
  if (finalResponseCallback) {
164
159
  finalResponseCallback(message);
165
160
  }
161
+ eventBus.emit(Events.FINAL_RESPONSE, message);
166
162
  return {
167
163
  success: true,
168
164
  result: message,
@@ -9,10 +9,33 @@ import { PLANNING_TOOLS } from './llm/simple/planning-tools.js';
9
9
  import { FinalResponseTool } from './llm/simple/final-response-tool.js';
10
10
  import { RemotePhoneTool } from './llm/simple/remote-phone-tool.js';
11
11
  import { getShellTools } from './llm/simple/index.js';
12
- import { BROWSER_TOOLS, startBrowserServer, shutdownBrowserServer } from './browser/index.js';
13
- import { WORD_TOOLS, EXCEL_TOOLS, POWERPOINT_TOOLS } from './office/index.js';
12
+ let _browserTools = null;
13
+ let _startBrowserServer = null;
14
+ let _shutdownBrowserServer = null;
15
+ async function getBrowserModule() {
16
+ if (!_browserTools) {
17
+ const mod = await import('./browser/index.js');
18
+ _browserTools = mod.BROWSER_TOOLS;
19
+ _startBrowserServer = mod.startBrowserServer;
20
+ _shutdownBrowserServer = mod.shutdownBrowserServer;
21
+ }
22
+ return { BROWSER_TOOLS: _browserTools, startBrowserServer: _startBrowserServer, shutdownBrowserServer: _shutdownBrowserServer };
23
+ }
24
+ let _wordTools = null;
25
+ let _excelTools = null;
26
+ let _powerpointTools = null;
27
+ async function getOfficeModule() {
28
+ if (!_wordTools) {
29
+ const mod = await import('./office/index.js');
30
+ _wordTools = mod.WORD_TOOLS;
31
+ _excelTools = mod.EXCEL_TOOLS;
32
+ _powerpointTools = mod.POWERPOINT_TOOLS;
33
+ }
34
+ return { WORD_TOOLS: _wordTools, EXCEL_TOOLS: _excelTools, POWERPOINT_TOOLS: _powerpointTools };
35
+ }
14
36
  async function validateBrowserTools() {
15
37
  try {
38
+ const { startBrowserServer } = await getBrowserModule();
16
39
  await startBrowserServer();
17
40
  return { success: true };
18
41
  }
@@ -29,10 +52,19 @@ function getOptionalToolGroupsConfig() {
29
52
  id: 'browser',
30
53
  name: 'Browser Automation',
31
54
  description: 'Control Chrome/Edge browser for web testing (navigate, click, screenshot, etc.)',
32
- tools: BROWSER_TOOLS,
55
+ tools: [],
33
56
  enabled: false,
34
- onEnable: validateBrowserTools,
35
- onDisable: shutdownBrowserServer,
57
+ onEnable: async () => {
58
+ const { BROWSER_TOOLS } = await getBrowserModule();
59
+ const group = OPTIONAL_TOOL_GROUPS.find(g => g.id === 'browser');
60
+ if (group)
61
+ group.tools = BROWSER_TOOLS;
62
+ return validateBrowserTools();
63
+ },
64
+ onDisable: async () => {
65
+ const { shutdownBrowserServer } = await getBrowserModule();
66
+ await shutdownBrowserServer();
67
+ },
36
68
  },
37
69
  ];
38
70
  if (hasWindowsAccess()) {
@@ -40,20 +72,41 @@ function getOptionalToolGroupsConfig() {
40
72
  id: 'word',
41
73
  name: 'Microsoft Word',
42
74
  description: 'Control Word for document editing (write, read, save, export PDF, header/footer)',
43
- tools: WORD_TOOLS,
75
+ tools: [],
44
76
  enabled: false,
77
+ onEnable: async () => {
78
+ const { WORD_TOOLS } = await getOfficeModule();
79
+ const group = OPTIONAL_TOOL_GROUPS.find(g => g.id === 'word');
80
+ if (group)
81
+ group.tools = WORD_TOOLS;
82
+ return { success: true };
83
+ },
45
84
  }, {
46
85
  id: 'excel',
47
86
  name: 'Microsoft Excel',
48
87
  description: 'Control Excel for spreadsheet editing (cells, ranges, formulas, charts)',
49
- tools: EXCEL_TOOLS,
88
+ tools: [],
50
89
  enabled: false,
90
+ onEnable: async () => {
91
+ const { EXCEL_TOOLS } = await getOfficeModule();
92
+ const group = OPTIONAL_TOOL_GROUPS.find(g => g.id === 'excel');
93
+ if (group)
94
+ group.tools = EXCEL_TOOLS;
95
+ return { success: true };
96
+ },
51
97
  }, {
52
98
  id: 'powerpoint',
53
99
  name: 'Microsoft PowerPoint',
54
100
  description: 'Control PowerPoint for presentations (slides, shapes, transitions, PDF export)',
55
- tools: POWERPOINT_TOOLS,
101
+ tools: [],
56
102
  enabled: false,
103
+ onEnable: async () => {
104
+ const { POWERPOINT_TOOLS } = await getOfficeModule();
105
+ const group = OPTIONAL_TOOL_GROUPS.find(g => g.id === 'powerpoint');
106
+ if (group)
107
+ group.tools = POWERPOINT_TOOLS;
108
+ return { success: true };
109
+ },
57
110
  });
58
111
  }
59
112
  return groups;
@@ -138,9 +191,9 @@ class ToolRegistry {
138
191
  logger.error('Tool group not found', { groupId });
139
192
  return { success: false, error: `Tool group '${groupId}' not found` };
140
193
  }
141
- if (!skipValidation && group.onEnable) {
194
+ if (group.onEnable) {
142
195
  const result = await group.onEnable();
143
- if (!result.success) {
196
+ if (!skipValidation && !result.success) {
144
197
  return result;
145
198
  }
146
199
  }
@@ -17,6 +17,7 @@ interface PlanExecuteAppProps {
17
17
  model: string;
18
18
  endpoint: string;
19
19
  };
20
+ resumeLastSession?: boolean;
20
21
  }
21
22
  export declare const PlanExecuteApp: React.FC<PlanExecuteAppProps>;
22
23
  export default PlanExecuteApp;