tachibot-mcp 2.7.10 → 2.9.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 (52) hide show
  1. package/dist/src/application/services/focus/FocusExecutionService.js +359 -0
  2. package/dist/src/application/services/focus/FocusTool.service.js +52 -13
  3. package/dist/src/application/services/focus/modes/focus-deep.mode.js +25 -0
  4. package/dist/src/application/services/focus/types/FocusSession.js +8 -0
  5. package/dist/src/config/model-constants.js +7 -0
  6. package/dist/src/focus-deep.js +34 -0
  7. package/dist/src/modes/challenger.js +40 -1
  8. package/dist/src/modes/scout.js +23 -1
  9. package/dist/src/modes/shared/helpers/challenger-helpers.js +14 -1
  10. package/dist/src/modes/shared/helpers/verifier-helpers.js +16 -1
  11. package/dist/src/modes/verifier.js +39 -1
  12. package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +3 -1
  13. package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +39 -1
  14. package/dist/src/profiles/balanced.js +5 -0
  15. package/dist/src/profiles/code_focus.js +5 -0
  16. package/dist/src/profiles/full.js +5 -0
  17. package/dist/src/profiles/heavy_coding.js +7 -2
  18. package/dist/src/profiles/minimal.js +5 -0
  19. package/dist/src/profiles/research_power.js +5 -0
  20. package/dist/src/prompt-engineer-lite.js +58 -10
  21. package/dist/src/sequential-thinking.js +24 -10
  22. package/dist/src/server.js +84 -56
  23. package/dist/src/tools/advanced-modes.js +48 -38
  24. package/dist/src/tools/claude-integration.js +16 -12
  25. package/dist/src/tools/gemini-tools.js +208 -67
  26. package/dist/src/tools/grok-enhanced.js +18 -13
  27. package/dist/src/tools/grok-tools.js +71 -51
  28. package/dist/src/tools/lmstudio-tools.js +18 -16
  29. package/dist/src/tools/openai-tools.js +181 -38
  30. package/dist/src/tools/openrouter-tools.js +111 -61
  31. package/dist/src/tools/perplexity-tools.js +43 -49
  32. package/dist/src/tools/prompt-technique-tools.js +404 -0
  33. package/dist/src/tools/qwen-wrapper.js +16 -11
  34. package/dist/src/tools/tachi-tool.js +47 -53
  35. package/dist/src/tools/workflow-runner.js +52 -75
  36. package/dist/src/utils/ansi-renderer.js +5 -2
  37. package/dist/src/utils/format-constants.js +47 -0
  38. package/dist/src/utils/format-stripper.js +28 -0
  39. package/dist/src/utils/ink-renderer.js +36 -5
  40. package/dist/src/utils/input-validator.js +42 -15
  41. package/dist/src/utils/progress-stream.js +4 -1
  42. package/dist/src/utils/usage-tracker.js +65 -7
  43. package/dist/src/workflows/custom-workflows.js +14 -2
  44. package/dist/src/workflows/tool-mapper.js +24 -21
  45. package/package.json +1 -1
  46. package/profiles/balanced.json +7 -2
  47. package/profiles/code_focus.json +7 -2
  48. package/profiles/full.json +7 -2
  49. package/profiles/heavy_coding.json +7 -2
  50. package/profiles/minimal.json +7 -2
  51. package/profiles/research_power.json +7 -2
  52. package/tools.config.json +2 -1
@@ -0,0 +1,359 @@
1
+ /**
2
+ * FocusExecutionService - Manages focus sessions and step execution
3
+ * Enables executeNow mode for focus tool to actually run model calls
4
+ */
5
+ import { randomBytes } from "crypto";
6
+ import { ToolExecutionService } from "../../../orchestrators/collaborative/services/tool-execution/ToolExecutionService.js";
7
+ import { ReasoningMode } from "../../../reasoning-chain.js";
8
+ import { createFocusDeepPlan, generateFocusDeepVisualization } from "../../../focus-deep.js";
9
+ import { SESSION_TIMEOUT_MS, } from "./types/FocusSession.js";
10
+ /**
11
+ * Generate UUID v4 using randomBytes
12
+ */
13
+ function generateUUID() {
14
+ const bytes = randomBytes(16);
15
+ // Set version (4) and variant bits
16
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
17
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
18
+ const hex = bytes.toString('hex');
19
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
20
+ }
21
+ /**
22
+ * FocusExecutionService - Session management and step execution for focus modes
23
+ */
24
+ export class FocusExecutionService {
25
+ constructor(toolExecutionService) {
26
+ this.sessions = new Map();
27
+ this.cleanupInterval = null;
28
+ this.toolExecutionService = toolExecutionService || new ToolExecutionService({ verbose: false });
29
+ // Start cleanup interval
30
+ this.startCleanupInterval();
31
+ }
32
+ /**
33
+ * Start a new focus session and execute the first step
34
+ */
35
+ async startFocusSession(params) {
36
+ const { query, mode, domain, rounds = 5, models = [], temperature = 0.7, maxTokensPerRound = 2000, pingPongStyle = "collaborative", tokenEfficient = false, saveSession = true, } = params;
37
+ // Create the focus plan
38
+ const plan = createFocusDeepPlan(query, domain);
39
+ // Create session
40
+ const sessionId = generateUUID();
41
+ const session = {
42
+ sessionId,
43
+ mode,
44
+ query,
45
+ domain,
46
+ currentStepIndex: 0,
47
+ totalSteps: plan.steps.length,
48
+ plan,
49
+ stepOutputs: new Map(),
50
+ status: "running",
51
+ config: {
52
+ rounds,
53
+ models: models.length > 0 ? models : plan.availableModels,
54
+ temperature,
55
+ maxTokensPerRound,
56
+ pingPongStyle,
57
+ tokenEfficient,
58
+ saveSession,
59
+ },
60
+ createdAt: new Date(),
61
+ lastActivityAt: new Date(),
62
+ };
63
+ this.sessions.set(sessionId, session);
64
+ // Execute first step
65
+ return this.executeNextStep(session);
66
+ }
67
+ /**
68
+ * Continue an existing focus session
69
+ */
70
+ async continueFocus(sessionId) {
71
+ const session = this.sessions.get(sessionId);
72
+ if (!session) {
73
+ return {
74
+ sessionId,
75
+ status: "failed",
76
+ progress: 0,
77
+ output: `Error: Session not found. Session ID: ${sessionId}`,
78
+ hasMoreSteps: false,
79
+ totalSteps: 0,
80
+ currentStep: 0,
81
+ };
82
+ }
83
+ // Check timeout
84
+ if (this.isSessionTimedOut(session)) {
85
+ session.status = "failed";
86
+ session.error = "Session timed out after 30 minutes of inactivity";
87
+ return {
88
+ sessionId,
89
+ status: "failed",
90
+ progress: this.calculateProgress(session),
91
+ output: `Error: ${session.error}`,
92
+ hasMoreSteps: false,
93
+ totalSteps: session.totalSteps,
94
+ currentStep: session.currentStepIndex,
95
+ };
96
+ }
97
+ if (session.status === "completed") {
98
+ return this.buildCompletedResult(session);
99
+ }
100
+ if (session.status === "failed") {
101
+ return {
102
+ sessionId,
103
+ status: "failed",
104
+ progress: this.calculateProgress(session),
105
+ output: `Error: Session failed - ${session.error}`,
106
+ hasMoreSteps: false,
107
+ totalSteps: session.totalSteps,
108
+ currentStep: session.currentStepIndex,
109
+ };
110
+ }
111
+ // Execute next step
112
+ return this.executeNextStep(session);
113
+ }
114
+ /**
115
+ * Get session status without executing
116
+ */
117
+ getSession(sessionId) {
118
+ return this.sessions.get(sessionId);
119
+ }
120
+ /**
121
+ * Execute the next step in the session
122
+ */
123
+ async executeNextStep(session) {
124
+ const stepIndex = session.currentStepIndex;
125
+ const step = session.plan.steps[stepIndex];
126
+ if (!step) {
127
+ // No more steps - session complete
128
+ session.status = "completed";
129
+ return this.buildCompletedResult(session);
130
+ }
131
+ const startTime = Date.now();
132
+ try {
133
+ // Build context from previous steps
134
+ const context = this.buildStepContext(session, stepIndex);
135
+ // Build the prompt for this step
136
+ const prompt = this.buildStepPrompt(session, step, context);
137
+ // Execute the step using ToolExecutionService
138
+ const output = await this.toolExecutionService.executeRealTool(step.model, prompt, ReasoningMode.DEEP_REASONING);
139
+ const duration = Date.now() - startTime;
140
+ // Store step output
141
+ const stepOutput = {
142
+ stepIndex,
143
+ model: step.model,
144
+ action: step.action,
145
+ output,
146
+ duration,
147
+ timestamp: new Date(),
148
+ };
149
+ session.stepOutputs.set(stepIndex, stepOutput);
150
+ // Move to next step
151
+ session.currentStepIndex++;
152
+ session.lastActivityAt = new Date();
153
+ // Check if we're done
154
+ if (session.currentStepIndex >= session.totalSteps) {
155
+ session.status = "completed";
156
+ return this.buildCompletedResult(session);
157
+ }
158
+ // Return progress result
159
+ return {
160
+ sessionId: session.sessionId,
161
+ status: "running",
162
+ progress: this.calculateProgress(session),
163
+ stepOutput,
164
+ output: this.formatStepOutput(stepOutput, session),
165
+ hasMoreSteps: true,
166
+ totalSteps: session.totalSteps,
167
+ currentStep: session.currentStepIndex,
168
+ };
169
+ }
170
+ catch (error) {
171
+ const errorMsg = error instanceof Error ? error.message : String(error);
172
+ const duration = Date.now() - startTime;
173
+ // Store error output
174
+ const stepOutput = {
175
+ stepIndex,
176
+ model: step.model,
177
+ action: step.action,
178
+ output: "",
179
+ duration,
180
+ timestamp: new Date(),
181
+ error: errorMsg,
182
+ };
183
+ session.stepOutputs.set(stepIndex, stepOutput);
184
+ session.status = "failed";
185
+ session.error = `Step ${stepIndex + 1} failed: ${errorMsg}`;
186
+ return {
187
+ sessionId: session.sessionId,
188
+ status: "failed",
189
+ progress: this.calculateProgress(session),
190
+ stepOutput,
191
+ output: `Error executing step ${stepIndex + 1} (${step.model}:${step.action}): ${errorMsg}`,
192
+ hasMoreSteps: false,
193
+ totalSteps: session.totalSteps,
194
+ currentStep: session.currentStepIndex,
195
+ };
196
+ }
197
+ }
198
+ /**
199
+ * Build context from previous step outputs
200
+ */
201
+ buildStepContext(session, currentStepIndex) {
202
+ if (currentStepIndex === 0) {
203
+ return "";
204
+ }
205
+ const previousOutputs = [];
206
+ for (let i = 0; i < currentStepIndex; i++) {
207
+ const output = session.stepOutputs.get(i);
208
+ if (output && !output.error) {
209
+ const step = session.plan.steps[i];
210
+ previousOutputs.push(`### Step ${i + 1}: ${step.action} (${step.model})\n${output.output}`);
211
+ }
212
+ }
213
+ return previousOutputs.join("\n\n---\n\n");
214
+ }
215
+ /**
216
+ * Build prompt for a specific step
217
+ */
218
+ buildStepPrompt(session, step, previousContext) {
219
+ let prompt = `## Objective\n${session.query}\n\n`;
220
+ if (session.domain) {
221
+ prompt += `## Domain\n${session.domain}\n\n`;
222
+ }
223
+ if (previousContext) {
224
+ prompt += `## Previous Analysis\n${previousContext}\n\n---\n\n`;
225
+ }
226
+ prompt += `## Current Task: ${step.action.toUpperCase()}\n`;
227
+ prompt += `${step.prompt}\n\n`;
228
+ if (step.reasoning) {
229
+ prompt += `**Why this step**: ${step.reasoning}\n\n`;
230
+ }
231
+ prompt += `Please provide your ${step.action} analysis.`;
232
+ return prompt;
233
+ }
234
+ /**
235
+ * Format step output for display
236
+ */
237
+ formatStepOutput(stepOutput, session) {
238
+ const step = session.plan.steps[stepOutput.stepIndex];
239
+ const progress = this.calculateProgress(session);
240
+ let output = `## Step ${stepOutput.stepIndex + 1}/${session.totalSteps}: ${step.action.toUpperCase()}\n`;
241
+ output += `**Model**: ${stepOutput.model}\n`;
242
+ output += `**Duration**: ${stepOutput.duration}ms\n`;
243
+ output += `**Progress**: ${progress.toFixed(0)}%\n\n`;
244
+ if (stepOutput.error) {
245
+ output += `### Error\n${stepOutput.error}\n`;
246
+ }
247
+ else {
248
+ output += `### Output\n${stepOutput.output}\n`;
249
+ }
250
+ output += `\n---\n`;
251
+ output += `Session ID: \`${session.sessionId}\`\n`;
252
+ output += `Use \`continue_focus\` with this session ID to continue.\n`;
253
+ return output;
254
+ }
255
+ /**
256
+ * Build completed session result with summary
257
+ */
258
+ buildCompletedResult(session) {
259
+ // Collect all outputs
260
+ const allOutputs = [];
261
+ for (let i = 0; i < session.totalSteps; i++) {
262
+ const output = session.stepOutputs.get(i);
263
+ if (output) {
264
+ allOutputs.push(output);
265
+ }
266
+ }
267
+ // Build summary output
268
+ let output = `# Focus-Deep Session Complete\n\n`;
269
+ output += `**Objective**: ${session.query}\n`;
270
+ output += `**Mode**: ${session.mode}\n`;
271
+ output += `**Steps Completed**: ${allOutputs.length}/${session.totalSteps}\n\n`;
272
+ // Add each step's output
273
+ output += `## Step-by-Step Analysis\n\n`;
274
+ for (const stepOutput of allOutputs) {
275
+ const step = session.plan.steps[stepOutput.stepIndex];
276
+ output += `### Step ${stepOutput.stepIndex + 1}: ${step.action.toUpperCase()} (${stepOutput.model})\n`;
277
+ output += `*Duration: ${stepOutput.duration}ms*\n\n`;
278
+ if (stepOutput.error) {
279
+ output += `**Error**: ${stepOutput.error}\n\n`;
280
+ }
281
+ else {
282
+ output += `${stepOutput.output}\n\n`;
283
+ }
284
+ output += `---\n\n`;
285
+ }
286
+ // Add final synthesis prompt
287
+ const lastOutput = allOutputs[allOutputs.length - 1];
288
+ if (lastOutput && !lastOutput.error) {
289
+ output += `## Final Synthesis\n\n`;
290
+ output += `The focus-deep analysis has completed. The final step (${session.plan.steps[lastOutput.stepIndex].action}) provides the synthesized conclusion.\n`;
291
+ }
292
+ return {
293
+ sessionId: session.sessionId,
294
+ status: "completed",
295
+ progress: 100,
296
+ output,
297
+ hasMoreSteps: false,
298
+ totalSteps: session.totalSteps,
299
+ currentStep: session.totalSteps,
300
+ allOutputs,
301
+ };
302
+ }
303
+ /**
304
+ * Calculate session progress percentage
305
+ */
306
+ calculateProgress(session) {
307
+ if (session.totalSteps === 0)
308
+ return 100;
309
+ return (session.currentStepIndex / session.totalSteps) * 100;
310
+ }
311
+ /**
312
+ * Check if session has timed out
313
+ */
314
+ isSessionTimedOut(session) {
315
+ const now = Date.now();
316
+ const lastActivity = session.lastActivityAt.getTime();
317
+ return now - lastActivity > SESSION_TIMEOUT_MS;
318
+ }
319
+ /**
320
+ * Start interval to clean up timed out sessions
321
+ */
322
+ startCleanupInterval() {
323
+ // Clean up every 5 minutes
324
+ this.cleanupInterval = setInterval(() => {
325
+ this.cleanupTimedOutSessions();
326
+ }, 5 * 60 * 1000);
327
+ }
328
+ /**
329
+ * Clean up timed out sessions
330
+ */
331
+ cleanupTimedOutSessions() {
332
+ const now = Date.now();
333
+ for (const [sessionId, session] of this.sessions) {
334
+ const lastActivity = session.lastActivityAt.getTime();
335
+ if (now - lastActivity > SESSION_TIMEOUT_MS) {
336
+ this.sessions.delete(sessionId);
337
+ console.error(`[FocusExecutionService] Cleaned up timed out session: ${sessionId}`);
338
+ }
339
+ }
340
+ }
341
+ /**
342
+ * Stop the cleanup interval (for testing/shutdown)
343
+ */
344
+ stopCleanup() {
345
+ if (this.cleanupInterval) {
346
+ clearInterval(this.cleanupInterval);
347
+ this.cleanupInterval = null;
348
+ }
349
+ }
350
+ /**
351
+ * Get plan-only visualization (for executeNow: false)
352
+ */
353
+ getPlanVisualization(query, domain) {
354
+ const plan = createFocusDeepPlan(query, domain);
355
+ return generateFocusDeepVisualization(plan);
356
+ }
357
+ }
358
+ // Export singleton instance
359
+ export const focusExecutionService = new FocusExecutionService();
@@ -6,29 +6,70 @@
6
6
  * Architecture:
7
7
  * 1. FocusModeRegistry - For complex extracted modes (FocusDeepMode, TachibotStatusMode)
8
8
  * 2. Delegate Map - For simple orchestrator calls
9
- * 3. Legacy fallback - For modes with complex conditional logic (handled by server.ts)
9
+ * 3. FocusExecutionService - For executeNow mode (actual model execution)
10
+ * 4. Legacy fallback - For modes with complex conditional logic (handled by server.ts)
10
11
  */
11
12
  import { TechnicalDomain } from '../../../reasoning-chain.js';
12
- import { renderBigText } from '../../../utils/ink-renderer.js';
13
+ import { stripFormatting } from '../../../utils/format-stripper.js';
13
14
  export class FocusToolService {
14
- constructor(modeRegistry, collaborativeOrchestrator) {
15
+ constructor(modeRegistry, collaborativeOrchestrator, executionService) {
15
16
  this.modeRegistry = modeRegistry;
16
17
  this.collaborativeOrchestrator = collaborativeOrchestrator;
17
18
  this.name = 'focus';
18
19
  this.description = 'Multi-model reasoning with various modes';
20
+ this.executionService = null;
19
21
  this.delegateHandlers = this.createDelegateHandlers();
22
+ this.executionService = executionService || null;
23
+ }
24
+ /**
25
+ * Set the execution service (for lazy initialization)
26
+ */
27
+ setExecutionService(service) {
28
+ this.executionService = service;
20
29
  }
21
30
  async execute(params) {
22
31
  const modeName = params.mode || 'simple';
23
- // BigText header (disabled via TACHIBOT_BIG_HEADERS=false)
24
- const header = renderBigText('FOCUS', { font: 'block', gradient: 'cristal' });
32
+ const executeNow = params.executeNow !== false; // Default to true
33
+ // BigText header disabled - plain text only
34
+ const header = '';
35
+ // 0. For focus-deep mode with executeNow, use the execution service
36
+ if (modeName === 'focus-deep' && executeNow && this.executionService) {
37
+ const result = await this.executionService.startFocusSession({
38
+ query: params.query,
39
+ mode: modeName,
40
+ domain: params.domain,
41
+ context: params.context,
42
+ rounds: params.rounds,
43
+ models: params.models,
44
+ temperature: params.temperature,
45
+ maxTokensPerRound: params.maxTokensPerRound,
46
+ pingPongStyle: params.pingPongStyle,
47
+ tokenEfficient: params.tokenEfficient,
48
+ saveSession: params.saveSession,
49
+ });
50
+ return {
51
+ output: stripFormatting(header + result.output),
52
+ metadata: {
53
+ mode: modeName,
54
+ sessionId: result.sessionId,
55
+ status: result.status,
56
+ progress: result.progress,
57
+ hasMoreSteps: result.hasMoreSteps,
58
+ totalSteps: result.totalSteps,
59
+ currentStep: result.currentStep,
60
+ timestamp: Date.now()
61
+ }
62
+ };
63
+ }
25
64
  // 1. Try extracted modes first (Strategy Pattern)
65
+ // For focus-deep with executeNow: false, this returns the plan visualization
26
66
  const mode = this.modeRegistry.get(modeName);
27
67
  if (mode) {
28
- const result = await mode.execute(params);
68
+ // Pass executeNow to the mode for conditional behavior
69
+ const result = await mode.execute({ ...params, executeNow });
29
70
  return {
30
71
  ...result,
31
- output: header + result.output
72
+ output: stripFormatting(header + result.output)
32
73
  };
33
74
  }
34
75
  // 2. Try delegate handlers (simple orchestrator calls)
@@ -36,7 +77,7 @@ export class FocusToolService {
36
77
  if (handler) {
37
78
  const output = await handler(params);
38
79
  return {
39
- output: header + output,
80
+ output: stripFormatting(header + output),
40
81
  metadata: {
41
82
  mode: modeName,
42
83
  timestamp: Date.now()
@@ -71,22 +112,20 @@ export class FocusToolService {
71
112
  const query = params.query;
72
113
  const domain = parseDomain(params.domain);
73
114
  const plan = await this.collaborativeOrchestrator.startDeepReasoning(query, domain);
74
- const title = "🧠 **DEEP COLLABORATIVE REASONING**";
75
- return `${title}\n\n${plan}\n\n🔄 **How it works**: Models collaborate, critique, and build upon each other's ideas to find optimal solutions.`;
115
+ return `🧠 DEEP COLLABORATIVE REASONING\n${'═'.repeat(40)}\n\n${plan}\n\n🔄 How it works: Models collaborate, critique, and build upon each other's ideas to find optimal solutions.`;
76
116
  });
77
117
  handlers.set('code-brainstorm', async (params) => {
78
118
  const query = params.query;
79
119
  const domain = parseDomain(params.domain);
80
120
  const plan = await this.collaborativeOrchestrator.startDeepReasoning(query, domain);
81
- const title = "💡 **CODE BRAINSTORMING SESSION**";
82
- return `${title}\n\n${plan}\n\n🔄 **How it works**: Models collaborate, critique, and build upon each other's ideas to find optimal solutions.`;
121
+ return `💡 CODE BRAINSTORMING SESSION\n${'═'.repeat(40)}\n\n${plan}\n\n🔄 How it works: Models collaborate, critique, and build upon each other's ideas to find optimal solutions.`;
83
122
  });
84
123
  // Dynamic debate
85
124
  handlers.set('dynamic-debate', async (params) => {
86
125
  const query = params.query;
87
126
  const domain = parseDomain(params.domain);
88
127
  const plan = await this.collaborativeOrchestrator.startDynamicDebate(query, domain);
89
- return `⚔️ **DYNAMIC DEBATE SESSION**\n\n${plan}\n\n🥊 **How it works**: Models take opposing positions, argue their cases, provide rebuttals, and engage in intellectual combat to explore all angles of complex problems.`;
128
+ return `⚔️ DYNAMIC DEBATE SESSION\n${'═'.repeat(40)}\n\n${plan}\n\n🥊 How it works: Models take opposing positions, argue their cases, provide rebuttals, and engage in intellectual combat to explore all angles of complex problems.`;
90
129
  });
91
130
  // Template-based modes
92
131
  const templateModes = [
@@ -2,17 +2,42 @@
2
2
  * FocusDeep Mode - Ultimate reasoning combining Sequential Thinking + Multi-Model
3
3
  * Extracted from server.ts (Phase 2: SOLID refactoring)
4
4
  * Wraps existing focus-deep.ts functionality
5
+ *
6
+ * Supports two modes:
7
+ * - executeNow: false - Returns plan visualization (current behavior)
8
+ * - executeNow: true - Actual execution handled by FocusExecutionService in FocusTool.service.ts
5
9
  */
6
10
  import { createFocusDeepPlan, generateFocusDeepVisualization } from '../../../../focus-deep.js';
7
11
  export class FocusDeepMode {
8
12
  constructor() {
9
13
  this.modeName = 'focus-deep';
10
14
  this.description = 'Ultimate reasoning combining Sequential Thinking + Multi-Model orchestration';
15
+ this.supportsExecution = true;
11
16
  }
12
17
  async execute(params) {
13
18
  const query = params.query;
14
19
  const domain = params.domain;
20
+ const executeNow = params.executeNow;
21
+ // Create the plan (needed for both visualization and execution metadata)
15
22
  const plan = createFocusDeepPlan(query, domain);
23
+ // If executeNow is explicitly false, return plan visualization only
24
+ // (executeNow: true is handled by FocusExecutionService in FocusTool.service.ts)
25
+ if (executeNow === false) {
26
+ const viz = generateFocusDeepVisualization(plan);
27
+ return {
28
+ output: viz,
29
+ metadata: {
30
+ mode: this.modeName,
31
+ sessionId: plan.sessionId,
32
+ models: plan.availableModels,
33
+ estimatedThoughts: plan.estimatedThoughts,
34
+ executeNow: false
35
+ }
36
+ };
37
+ }
38
+ // Default: return plan visualization
39
+ // Note: When executeNow is true and FocusExecutionService is available,
40
+ // the request is intercepted by FocusTool.service.ts before reaching here
16
41
  const viz = generateFocusDeepVisualization(plan);
17
42
  return {
18
43
  output: viz,
@@ -0,0 +1,8 @@
1
+ /**
2
+ * FocusSession Types - Type definitions for Focus execution sessions
3
+ * Supports step-by-step execution of focus plans with session management
4
+ */
5
+ /**
6
+ * Session timeout configuration
7
+ */
8
+ export const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
@@ -125,6 +125,7 @@ export const CURRENT_MODELS = {
125
125
  brainstorm: OPENAI_MODELS.DEFAULT, // Creative ideation (gpt-5.2 + effort=medium)
126
126
  code: OPENAI_MODELS.DEFAULT, // Code tasks (gpt-5.2 + effort=medium)
127
127
  explain: OPENAI_MODELS.DEFAULT, // Explanations (gpt-5.2 + effort=low)
128
+ search: OPENAI_MODELS.DEFAULT, // Web search (gpt-5.2 + web_search tool)
128
129
  // Premium option for opt-in (use sparingly - 12x more expensive)
129
130
  premium: OPENAI_MODELS.PRO, // Expert mode (gpt-5.2-pro - 88.4% GPQA, $21/$168)
130
131
  },
@@ -175,6 +176,12 @@ export const TOOL_DEFAULTS = {
175
176
  maxTokens: 1500,
176
177
  temperature: 0.7,
177
178
  },
179
+ openai_search: {
180
+ model: CURRENT_MODELS.openai.search,
181
+ reasoning_effort: OPENAI_REASONING.LOW,
182
+ maxTokens: 6000,
183
+ temperature: 0.3,
184
+ },
178
185
  // Gemini tools
179
186
  gemini_brainstorm: {
180
187
  model: CURRENT_MODELS.gemini.default,
@@ -243,3 +243,37 @@ export function canRunFocusDeep() {
243
243
  quality
244
244
  };
245
245
  }
246
+ /**
247
+ * Execute a Focus-Deep step with actual tool execution
248
+ * Uses ToolExecutionService for model calls
249
+ */
250
+ export async function executeFocusDeepStepWithTools(step, context, toolService) {
251
+ const startTime = Date.now();
252
+ // Build the full prompt with context
253
+ let fullPrompt = context;
254
+ if (step.reasoning) {
255
+ fullPrompt += `\n\n**Why this step**: ${step.reasoning}`;
256
+ }
257
+ fullPrompt += `\n\n**Task**: ${step.prompt}`;
258
+ try {
259
+ // Execute using the tool service
260
+ // Import ReasoningMode dynamically to avoid circular dependency
261
+ const { ReasoningMode } = await import("./reasoning-chain.js");
262
+ const output = await toolService.executeRealTool(step.model, fullPrompt, ReasoningMode.DEEP_REASONING);
263
+ const duration = Date.now() - startTime;
264
+ return {
265
+ output,
266
+ model: step.model,
267
+ duration
268
+ };
269
+ }
270
+ catch (error) {
271
+ const duration = Date.now() - startTime;
272
+ const errorMsg = error instanceof Error ? error.message : String(error);
273
+ return {
274
+ output: `[Error executing ${step.model}: ${errorMsg}]`,
275
+ model: step.model,
276
+ duration
277
+ };
278
+ }
279
+ }
@@ -21,7 +21,46 @@ import { createProgressStream } from '../utils/progress-stream.js';
21
21
  import { smartAPIClient } from '../utils/smart-api-client.js';
22
22
  import { getSmartTimeout } from '../config/timeout-config.js';
23
23
  import { execSync } from 'child_process';
24
- import { renderTable, renderKeyValueTable, renderGradientBorderBox, renderGradientDivider, brailleBar, icons, } from '../utils/ink-renderer.js';
24
+ // import {
25
+ // renderTable,
26
+ // renderKeyValueTable,
27
+ // renderGradientBorderBox,
28
+ // renderGradientDivider,
29
+ // brailleBar,
30
+ // icons,
31
+ // } from '../utils/ink-renderer.js';
32
+ // Ink disabled - using plain text functions
33
+ const renderTable = (data) => {
34
+ if (data.length === 0)
35
+ return '';
36
+ const keys = Object.keys(data[0]);
37
+ const header = '| ' + keys.join(' | ') + ' |';
38
+ const separator = '|' + keys.map(() => '---').join('|') + '|';
39
+ const rows = data.map(row => '| ' + keys.map(k => row[k] || '').join(' | ') + ' |');
40
+ return [header, separator, ...rows].join('\n');
41
+ };
42
+ const renderKeyValueTable = (data) => {
43
+ return Object.entries(data).map(([k, v]) => `${k}: ${v}`).join('\n');
44
+ };
45
+ const renderGradientBorderBox = (content, _opts) => {
46
+ return `--- ${content} ---`;
47
+ };
48
+ const renderGradientDivider = (width = 50, _preset) => '-'.repeat(width);
49
+ const brailleBar = (value, max, width = 20) => {
50
+ const filled = Math.round((value / max) * width);
51
+ return '[' + '#'.repeat(filled) + '-'.repeat(width - filled) + ']';
52
+ };
53
+ const icons = {
54
+ search: '?',
55
+ brain: '*',
56
+ check: '+',
57
+ alertCircle: '!',
58
+ file: '#',
59
+ folder: '#',
60
+ bot: '@',
61
+ sparkle: '*',
62
+ chartBar: '|',
63
+ };
25
64
  // ============================================
26
65
  // CONSTANTS
27
66
  // ============================================
@@ -5,7 +5,29 @@ import { createProgressStream } from '../utils/progress-stream.js';
5
5
  import { providerRouter } from '../utils/provider-router.js';
6
6
  import { getSmartTimeout } from '../config/timeout-config.js';
7
7
  import { execSync } from 'child_process';
8
- import { renderKeyValueTable, renderGradientBorderBox, renderGradientDivider, icons, } from '../utils/ink-renderer.js';
8
+ // import {
9
+ // renderKeyValueTable,
10
+ // renderGradientBorderBox,
11
+ // renderGradientDivider,
12
+ // icons,
13
+ // } from '../utils/ink-renderer.js';
14
+ // Ink disabled - using plain text functions
15
+ const renderKeyValueTable = (data) => {
16
+ return Object.entries(data)
17
+ .filter(([_, v]) => v !== undefined)
18
+ .map(([k, v]) => `${k}: ${v}`)
19
+ .join('\n');
20
+ };
21
+ const renderGradientBorderBox = (content, _opts) => {
22
+ return `--- ${content} ---`;
23
+ };
24
+ const renderGradientDivider = (width = 50, _preset) => '-'.repeat(width);
25
+ const icons = {
26
+ search: '?',
27
+ brain: '*',
28
+ info: 'i',
29
+ check: '+',
30
+ };
9
31
  // Provider name constants
10
32
  const PROVIDER_PERPLEXITY = 'perplexity';
11
33
  const PROVIDER_GROK = 'grok';