tachibot-mcp 2.0.5 → 2.0.7

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 (53) hide show
  1. package/.env.example +5 -2
  2. package/dist/src/config/model-constants.js +85 -72
  3. package/dist/src/config/model-preferences.js +5 -4
  4. package/dist/src/config.js +2 -1
  5. package/dist/src/mcp-client.js +3 -3
  6. package/dist/src/modes/scout.js +2 -1
  7. package/dist/src/optimization/model-router.js +19 -16
  8. package/dist/src/orchestrator-instructions.js +1 -1
  9. package/dist/src/orchestrator-lite.js +1 -1
  10. package/dist/src/orchestrator.js +1 -1
  11. package/dist/src/profiles/balanced.js +1 -2
  12. package/dist/src/profiles/code_focus.js +1 -2
  13. package/dist/src/profiles/full.js +1 -2
  14. package/dist/src/profiles/minimal.js +1 -2
  15. package/dist/src/profiles/research_power.js +1 -2
  16. package/dist/src/server.js +13 -12
  17. package/dist/src/tools/gemini-tools.js +15 -16
  18. package/dist/src/tools/grok-enhanced.js +21 -17
  19. package/dist/src/tools/grok-tools.js +26 -20
  20. package/dist/src/tools/openai-tools.js +28 -61
  21. package/dist/src/tools/tool-router.js +53 -52
  22. package/dist/src/tools/unified-ai-provider.js +1 -1
  23. package/dist/src/tools/workflow-runner.js +16 -0
  24. package/dist/src/tools/workflow-validator-tool.js +1 -1
  25. package/dist/src/utils/api-keys.js +20 -0
  26. package/dist/src/validators/interpolation-validator.js +4 -0
  27. package/dist/src/validators/tool-registry-validator.js +1 -1
  28. package/dist/src/validators/tool-types.js +0 -1
  29. package/dist/src/workflows/custom-workflows.js +4 -3
  30. package/dist/src/workflows/engine/VariableInterpolator.js +30 -3
  31. package/dist/src/workflows/engine/WorkflowExecutionEngine.js +2 -2
  32. package/dist/src/workflows/engine/WorkflowOutputFormatter.js +27 -4
  33. package/dist/src/workflows/fallback-strategies.js +2 -2
  34. package/dist/src/workflows/model-router.js +30 -5
  35. package/dist/src/workflows/tool-mapper.js +41 -14
  36. package/docs/API_KEYS.md +10 -6
  37. package/docs/TOOLS_REFERENCE.md +7 -43
  38. package/package.json +1 -1
  39. package/profiles/balanced.json +1 -2
  40. package/profiles/code_focus.json +1 -2
  41. package/profiles/debug_intensive.json +0 -1
  42. package/profiles/full.json +2 -3
  43. package/profiles/minimal.json +1 -2
  44. package/profiles/research_power.json +1 -2
  45. package/profiles/workflow_builder.json +1 -2
  46. package/smithery.yaml +2 -2
  47. package/tools.config.json +15 -3
  48. package/workflows/code-architecture-review.yaml +5 -3
  49. package/workflows/creative-brainstorm-yaml.yaml +1 -1
  50. package/workflows/pingpong.yaml +5 -3
  51. package/workflows/system/README.md +1 -1
  52. package/workflows/system/verifier.yaml +8 -5
  53. package/workflows/ultra-creative-brainstorm.yaml +3 -3
@@ -2,6 +2,7 @@
2
2
  * Smart Tool Router for Focus MCP Server
3
3
  * Manages tool selection based on availability, preferences, and capabilities
4
4
  */
5
+ import { hasGrokApiKey, hasOpenAIApiKey, hasPerplexityApiKey, hasGeminiApiKey, hasOpenRouterApiKey } from "../utils/api-keys.js";
5
6
  // Tool capability categories
6
7
  export var ToolCategory;
7
8
  (function (ToolCategory) {
@@ -46,64 +47,54 @@ export class ToolRouter {
46
47
  return {
47
48
  [ToolCategory.REASONING]: [
48
49
  {
49
- name: "gpt5_reason",
50
+ name: "openai_reason",
50
51
  provider: ToolProvider.OPENAI,
51
52
  categories: [ToolCategory.REASONING],
52
53
  priority: 1,
53
54
  costTier: "high",
54
55
  speedTier: "medium",
55
56
  qualityTier: "excellent",
56
- checkAvailability: () => !!process.env.OPENAI_API_KEY && process.env.ENABLE_GPT5 === 'true'
57
+ checkAvailability: () => hasOpenAIApiKey()
57
58
  },
58
59
  {
59
- name: "gpt5_mini_reason",
60
- provider: ToolProvider.OPENAI,
60
+ name: "grok_reason",
61
+ provider: ToolProvider.GROK,
61
62
  categories: [ToolCategory.REASONING],
62
63
  priority: 2,
63
64
  costTier: "medium",
64
65
  speedTier: "fast",
65
66
  qualityTier: "excellent",
66
- checkAvailability: () => !!process.env.OPENAI_API_KEY
67
+ checkAvailability: () => hasGrokApiKey()
67
68
  },
68
69
  {
69
- name: "qwq_reason",
70
- provider: ToolProvider.OPENROUTER,
71
- categories: [ToolCategory.REASONING],
70
+ name: "perplexity_reason",
71
+ provider: ToolProvider.PERPLEXITY,
72
+ categories: [ToolCategory.REASONING, ToolCategory.SEARCH],
72
73
  priority: 3,
73
74
  costTier: "medium",
74
- speedTier: "medium",
75
- qualityTier: "excellent",
76
- checkAvailability: () => !!process.env.OPENROUTER_API_KEY
75
+ speedTier: "fast",
76
+ qualityTier: "good",
77
+ checkAvailability: () => hasPerplexityApiKey()
77
78
  },
78
79
  {
79
- name: "grok_reason",
80
- provider: ToolProvider.GROK,
80
+ name: "kimi_thinking",
81
+ provider: ToolProvider.OPENROUTER,
81
82
  categories: [ToolCategory.REASONING],
82
83
  priority: 4,
83
84
  costTier: "medium",
84
- speedTier: "fast",
85
+ speedTier: "medium",
85
86
  qualityTier: "excellent",
86
- checkAvailability: () => !!process.env.GROK_API_KEY
87
- },
88
- {
89
- name: "perplexity_reason",
90
- provider: ToolProvider.PERPLEXITY,
91
- categories: [ToolCategory.REASONING, ToolCategory.SEARCH],
92
- priority: 5,
93
- costTier: "medium",
94
- speedTier: "fast",
95
- qualityTier: "good",
96
- checkAvailability: () => !!process.env.PERPLEXITY_API_KEY
87
+ checkAvailability: () => hasOpenRouterApiKey()
97
88
  },
98
89
  {
99
- name: "gemini_query",
90
+ name: "gemini_brainstorm",
100
91
  provider: ToolProvider.GEMINI,
101
92
  categories: [ToolCategory.REASONING],
102
- priority: 6,
93
+ priority: 5,
103
94
  costTier: "low",
104
95
  speedTier: "fast",
105
96
  qualityTier: "good",
106
- checkAvailability: () => !!process.env.GOOGLE_API_KEY
97
+ checkAvailability: () => hasGeminiApiKey()
107
98
  }
108
99
  ],
109
100
  [ToolCategory.CODE]: [
@@ -115,7 +106,7 @@ export class ToolRouter {
115
106
  costTier: "medium",
116
107
  speedTier: "medium",
117
108
  qualityTier: "excellent",
118
- checkAvailability: () => !!process.env.OPENROUTER_API_KEY
109
+ checkAvailability: () => hasOpenRouterApiKey()
119
110
  },
120
111
  {
121
112
  name: "grok_code",
@@ -125,17 +116,17 @@ export class ToolRouter {
125
116
  costTier: "medium",
126
117
  speedTier: "fast",
127
118
  qualityTier: "excellent",
128
- checkAvailability: () => !!process.env.GROK_API_KEY
119
+ checkAvailability: () => hasGrokApiKey()
129
120
  },
130
121
  {
131
- name: "gpt5_code",
122
+ name: "openai_code_review",
132
123
  provider: ToolProvider.OPENAI,
133
124
  categories: [ToolCategory.CODE, ToolCategory.ANALYSIS],
134
125
  priority: 3,
135
126
  costTier: "medium",
136
127
  speedTier: "fast",
137
128
  qualityTier: "excellent",
138
- checkAvailability: () => !!process.env.OPENAI_API_KEY
129
+ checkAvailability: () => hasOpenAIApiKey()
139
130
  },
140
131
  {
141
132
  name: "gemini_analyze_code",
@@ -145,7 +136,7 @@ export class ToolRouter {
145
136
  costTier: "low",
146
137
  speedTier: "fast",
147
138
  qualityTier: "good",
148
- checkAvailability: () => !!process.env.GOOGLE_API_KEY
139
+ checkAvailability: () => hasGeminiApiKey()
149
140
  }
150
141
  ],
151
142
  [ToolCategory.BRAINSTORM]: [
@@ -157,7 +148,7 @@ export class ToolRouter {
157
148
  costTier: "medium",
158
149
  speedTier: "medium",
159
150
  qualityTier: "excellent",
160
- checkAvailability: () => !!process.env.GROK_API_KEY
151
+ checkAvailability: () => hasGrokApiKey()
161
152
  },
162
153
  {
163
154
  name: "openai_brainstorm",
@@ -167,7 +158,7 @@ export class ToolRouter {
167
158
  costTier: "high",
168
159
  speedTier: "medium",
169
160
  qualityTier: "excellent",
170
- checkAvailability: () => !!process.env.OPENAI_API_KEY
161
+ checkAvailability: () => hasOpenAIApiKey()
171
162
  },
172
163
  {
173
164
  name: "gemini_brainstorm",
@@ -177,7 +168,7 @@ export class ToolRouter {
177
168
  costTier: "low",
178
169
  speedTier: "fast",
179
170
  qualityTier: "good",
180
- checkAvailability: () => !!process.env.GOOGLE_API_KEY
171
+ checkAvailability: () => hasGeminiApiKey()
181
172
  }
182
173
  ],
183
174
  [ToolCategory.SEARCH]: [
@@ -189,7 +180,7 @@ export class ToolRouter {
189
180
  costTier: "medium",
190
181
  speedTier: "fast",
191
182
  qualityTier: "excellent",
192
- checkAvailability: () => !!process.env.PERPLEXITY_API_KEY
183
+ checkAvailability: () => hasPerplexityApiKey()
193
184
  },
194
185
  {
195
186
  name: "perplexity_research",
@@ -199,39 +190,49 @@ export class ToolRouter {
199
190
  costTier: "medium",
200
191
  speedTier: "medium",
201
192
  qualityTier: "excellent",
202
- checkAvailability: () => !!process.env.PERPLEXITY_API_KEY
193
+ checkAvailability: () => hasPerplexityApiKey()
194
+ },
195
+ {
196
+ name: "grok_search",
197
+ provider: ToolProvider.GROK,
198
+ categories: [ToolCategory.SEARCH],
199
+ priority: 3,
200
+ costTier: "medium",
201
+ speedTier: "fast",
202
+ qualityTier: "good",
203
+ checkAvailability: () => hasGrokApiKey()
203
204
  }
204
205
  ],
205
206
  [ToolCategory.ANALYSIS]: [
206
207
  {
207
- name: "gpt5_mini_analyze",
208
+ name: "openai_code_review",
208
209
  provider: ToolProvider.OPENAI,
209
210
  categories: [ToolCategory.ANALYSIS],
210
211
  priority: 1,
211
212
  costTier: "medium",
212
213
  speedTier: "fast",
213
214
  qualityTier: "excellent",
214
- checkAvailability: () => !!process.env.OPENAI_API_KEY
215
+ checkAvailability: () => hasOpenAIApiKey()
215
216
  },
216
217
  {
217
- name: "openai_compare",
218
- provider: ToolProvider.OPENAI,
218
+ name: "gemini_analyze_text",
219
+ provider: ToolProvider.GEMINI,
219
220
  categories: [ToolCategory.ANALYSIS],
220
221
  priority: 2,
221
- costTier: "medium",
222
- speedTier: "medium",
223
- qualityTier: "excellent",
224
- checkAvailability: () => !!process.env.OPENAI_API_KEY
222
+ costTier: "low",
223
+ speedTier: "fast",
224
+ qualityTier: "good",
225
+ checkAvailability: () => hasGeminiApiKey()
225
226
  },
226
227
  {
227
- name: "gemini_analyze_text",
228
+ name: "gemini_analyze_code",
228
229
  provider: ToolProvider.GEMINI,
229
- categories: [ToolCategory.ANALYSIS],
230
+ categories: [ToolCategory.ANALYSIS, ToolCategory.CODE],
230
231
  priority: 3,
231
232
  costTier: "low",
232
233
  speedTier: "fast",
233
234
  qualityTier: "good",
234
- checkAvailability: () => !!process.env.GOOGLE_API_KEY
235
+ checkAvailability: () => hasGeminiApiKey()
235
236
  }
236
237
  ],
237
238
  [ToolCategory.DEBUG]: [
@@ -243,17 +244,17 @@ export class ToolRouter {
243
244
  costTier: "medium",
244
245
  speedTier: "fast",
245
246
  qualityTier: "excellent",
246
- checkAvailability: () => !!process.env.GROK_API_KEY
247
+ checkAvailability: () => hasGrokApiKey()
247
248
  },
248
249
  {
249
- name: "gpt5_code",
250
+ name: "openai_code_review",
250
251
  provider: ToolProvider.OPENAI,
251
252
  categories: [ToolCategory.DEBUG, ToolCategory.CODE],
252
253
  priority: 2,
253
254
  costTier: "medium",
254
255
  speedTier: "fast",
255
256
  qualityTier: "excellent",
256
- checkAvailability: () => !!process.env.OPENAI_API_KEY
257
+ checkAvailability: () => hasOpenAIApiKey()
257
258
  }
258
259
  ]
259
260
  };
@@ -31,7 +31,7 @@ const PROVIDER_CONFIGS = {
31
31
  gemini: {
32
32
  base: 'https://generativelanguage.googleapis.com/v1beta/',
33
33
  key: process.env.GOOGLE_API_KEY,
34
- models: ['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']
34
+ models: ['gemini-3-pro-preview', 'gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']
35
35
  },
36
36
  openrouter: {
37
37
  base: 'https://openrouter.ai/api/v1',
@@ -73,6 +73,22 @@ export function registerWorkflowTools(server) {
73
73
  output += `## Step ${i + 1}: ${step.step}\n\n`;
74
74
  // Format the step output - keep it clean and readable
75
75
  let stepOutput = step.output;
76
+ // DEFENSIVE: Ensure stepOutput is a string (fix [object Object] issue)
77
+ if (stepOutput !== null && typeof stepOutput === 'object') {
78
+ // Handle FileReference objects - extract summary or stringify
79
+ if ('summary' in stepOutput && typeof stepOutput.summary === 'string') {
80
+ stepOutput = stepOutput.summary;
81
+ }
82
+ else if ('content' in stepOutput && typeof stepOutput.content === 'string') {
83
+ stepOutput = stepOutput.content;
84
+ }
85
+ else {
86
+ stepOutput = JSON.stringify(stepOutput, null, 2);
87
+ }
88
+ }
89
+ else if (stepOutput === undefined || stepOutput === null) {
90
+ stepOutput = '[No output]';
91
+ }
76
92
  // Truncate based on settings
77
93
  if (truncate && typeof stepOutput === 'string' && stepOutput.length > maxChars) {
78
94
  const approxTokens = Math.floor(stepOutput.length / 4);
@@ -16,7 +16,7 @@ function getAllKnownTools() {
16
16
  // Grok
17
17
  'grok_reason', 'grok_code', 'grok_debug', 'grok_architect', 'grok_brainstorm', 'grok_search',
18
18
  // OpenAI
19
- 'openai_compare', 'openai_brainstorm', 'openai_gpt5_reason', 'openai_code_review', 'openai_explain',
19
+ 'openai_brainstorm', 'openai_reason', 'openai_code_review', 'openai_explain',
20
20
  // Gemini
21
21
  'gemini_brainstorm', 'gemini_analyze_code', 'gemini_analyze_text',
22
22
  // Qwen
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Centralized API Key Resolution
3
+ * Single source of truth for all API key lookups (SRP)
4
+ */
5
+ // Grok/xAI - supports both XAI_API_KEY (new) and GROK_API_KEY (legacy)
6
+ export const getGrokApiKey = () => process.env.XAI_API_KEY || process.env.GROK_API_KEY;
7
+ export const hasGrokApiKey = () => !!(process.env.XAI_API_KEY || process.env.GROK_API_KEY);
8
+ // Other providers (single key each)
9
+ export const getOpenAIApiKey = () => process.env.OPENAI_API_KEY;
10
+ export const hasOpenAIApiKey = () => !!process.env.OPENAI_API_KEY;
11
+ export const getPerplexityApiKey = () => process.env.PERPLEXITY_API_KEY;
12
+ export const hasPerplexityApiKey = () => !!process.env.PERPLEXITY_API_KEY;
13
+ export const getGeminiApiKey = () => process.env.GOOGLE_API_KEY;
14
+ export const hasGeminiApiKey = () => !!process.env.GOOGLE_API_KEY;
15
+ export const getOpenRouterApiKey = () => process.env.OPENROUTER_API_KEY;
16
+ export const hasOpenRouterApiKey = () => !!process.env.OPENROUTER_API_KEY;
17
+ export const getQwenApiKey = () => process.env.QWEN_API_KEY;
18
+ export const hasQwenApiKey = () => !!process.env.QWEN_API_KEY;
19
+ export const getDeepSeekApiKey = () => process.env.DEEPSEEK_API_KEY;
20
+ export const hasDeepSeekApiKey = () => !!process.env.DEEPSEEK_API_KEY;
@@ -117,6 +117,10 @@ export class InterpolationValidator {
117
117
  }
118
118
  }
119
119
  validateStringInterpolations(content, stepNames, variables, path, errors, currentStepIndex, stepOrder) {
120
+ // Safety check: return early if content is null/undefined
121
+ if (!content || typeof content !== 'string') {
122
+ return;
123
+ }
120
124
  const matches = content.matchAll(this.interpolationRegex);
121
125
  for (const match of matches) {
122
126
  const fullMatch = match[0]; // e.g., "${step1.output}"
@@ -62,7 +62,7 @@ export class ToolRegistryValidator {
62
62
  // Grok
63
63
  'grok_reason', 'grok_code', 'grok_debug', 'grok_architect', 'grok_brainstorm', 'grok_search',
64
64
  // OpenAI
65
- 'openai_compare', 'openai_brainstorm', 'openai_gpt5_reason', 'openai_code_review', 'openai_explain',
65
+ 'openai_brainstorm', 'openai_reason', 'openai_code_review', 'openai_explain',
66
66
  // Gemini
67
67
  'gemini_brainstorm', 'gemini_analyze_code', 'gemini_analyze_text',
68
68
  // Qwen
@@ -29,7 +29,6 @@ export const KNOWN_TOOLS = [
29
29
  'openai_brainstorm',
30
30
  'openai_analyze',
31
31
  'openai_reason',
32
- 'openai_compare',
33
32
  'gpt5',
34
33
  'gpt5_mini',
35
34
  'gpt5_nano',
@@ -122,22 +122,23 @@ export class CustomWorkflowEngine {
122
122
  {
123
123
  name: "Gemini Ideas",
124
124
  tool: "gemini_brainstorm",
125
+ input: { prompt: "${query}" },
125
126
  maxTokens: 500,
126
127
  },
127
128
  {
128
129
  name: "GPT-5 Creative",
129
130
  tool: "gpt5_mini",
130
- input: { prompt: "Build on previous ideas with creative twists" },
131
+ input: { prompt: "Build on these ideas with creative twists for: ${query}\n\nPrevious ideas: ${Gemini Ideas.output}" },
131
132
  },
132
133
  {
133
134
  name: "Perplexity Research",
134
135
  tool: "perplexity_research",
135
- input: { prompt: "Find real-world examples and evidence" },
136
+ input: { prompt: "Find real-world examples and evidence for: ${query}" },
136
137
  },
137
138
  {
138
139
  name: "Final Synthesis",
139
140
  tool: "focus",
140
- input: { prompt: "Combine all ideas into top 5 recommendations" },
141
+ input: { prompt: "Synthesize all brainstorming results into top 5 creative recommendations for: ${query}\n\nIdeas to combine:\n${Gemini Ideas.output}\n${GPT-5 Creative.output}\n${Perplexity Research.output}" },
141
142
  },
142
143
  ],
143
144
  },
@@ -18,6 +18,14 @@ export class VariableInterpolator {
18
18
  * Interpolates variables and step outputs in template strings
19
19
  */
20
20
  async interpolate(template, context) {
21
+ // Safety check: return empty string if template is null/undefined
22
+ if (template === null || template === undefined) {
23
+ return '';
24
+ }
25
+ // Ensure template is a string
26
+ if (typeof template !== 'string') {
27
+ return String(template);
28
+ }
21
29
  const mergedContext = {
22
30
  ...context.variables,
23
31
  ...Object.fromEntries(context.fileReferences)
@@ -76,14 +84,33 @@ export class VariableInterpolator {
76
84
  return { match: fullMatch, replacement: filename };
77
85
  }
78
86
  else if (property === 'output') {
79
- // Handle ${step.output} by returning summary
80
- console.error(`✓ Interpolated '${key}': using summary (${value.summary.length} chars)`);
81
- return { match: fullMatch, replacement: value.summary };
87
+ // Handle ${step.output} by returning FULL content (not summary!)
88
+ // This is the primary interpolation method - must have complete output for chaining
89
+ const content = await value.getContent();
90
+ console.error(`✓ Interpolated '${key}': loaded full content (${content.length} chars)`);
91
+ return { match: fullMatch, replacement: content };
82
92
  }
83
93
  else {
84
94
  throw new Error(`Unknown FileReference property: ${property}`);
85
95
  }
86
96
  }
97
+ // Handle objects that aren't FileReferences (convert to JSON or extract summary)
98
+ if (typeof value === 'object' && value !== null) {
99
+ // Check if it has a summary property (duck typing for FileReference-like objects)
100
+ if ('summary' in value && typeof value.summary === 'string') {
101
+ console.error(`✓ Interpolated '${key}': using summary from object (${value.summary.length} chars)`);
102
+ return { match: fullMatch, replacement: value.summary };
103
+ }
104
+ // Check if it has a content property
105
+ if ('content' in value && typeof value.content === 'string') {
106
+ console.error(`✓ Interpolated '${key}': using content from object (${value.content.length} chars)`);
107
+ return { match: fullMatch, replacement: value.content };
108
+ }
109
+ // Fallback: JSON stringify the object
110
+ const jsonStr = JSON.stringify(value, null, 2);
111
+ console.error(`✓ Interpolated '${key}': stringified object (${jsonStr.length} chars)`);
112
+ return { match: fullMatch, replacement: jsonStr };
113
+ }
87
114
  // Handle primitive values
88
115
  console.error(`✓ Interpolated '${key}': type=${typeof value}, length=${String(value).length}`);
89
116
  return { match: fullMatch, replacement: String(value) };
@@ -137,7 +137,7 @@ async function executeWorkflowImpl(parent, workflowName, input, options) {
137
137
  console.error(`🔍 Available variables: [${Object.keys(variables).join(', ')}]`);
138
138
  console.error(`🔍 Available stepOutputs: [${Object.keys(stepOutputs).join(', ')}]`);
139
139
  // Extract variables that this step wants to use
140
- const inputStr = JSON.stringify(step.input);
140
+ const inputStr = step.input ? JSON.stringify(step.input) : '';
141
141
  const usedVars = [...inputStr.matchAll(/\$\{([^}]+)\}/g)].map(m => m[1]);
142
142
  if (usedVars.length > 0) {
143
143
  console.error(`🔍 Variables needed by this step: [${usedVars.join(', ')}]`);
@@ -210,7 +210,7 @@ async function executeWorkflowImpl(parent, workflowName, input, options) {
210
210
  if (workflow.settings?.optimization?.enabled && typeof stepInput === "string") {
211
211
  const optimized = await tokenOptimizer.optimize({
212
212
  prompt: stepInput,
213
- model: model || "gpt-5-mini",
213
+ model: model || "gpt-5.1-codex-mini",
214
214
  maxTokens: resolvedParams.maxTokens,
215
215
  });
216
216
  if (optimized.fromCache) {
@@ -11,7 +11,8 @@ export class WorkflowOutputFormatter {
11
11
  const synthesisStep = execution.outputs.find(step => step.step === 'auto-synthesis');
12
12
  if (synthesisStep) {
13
13
  // Auto-synthesis ran - return only the synthesis output to prevent MCP 25k limit
14
- return synthesisStep.output;
14
+ // DEFENSIVE: Ensure output is a string
15
+ return this.ensureString(synthesisStep.output);
15
16
  }
16
17
  switch (format) {
17
18
  case "json":
@@ -22,7 +23,7 @@ export class WorkflowOutputFormatter {
22
23
  status: execution.status,
23
24
  steps: execution.outputs.map(out => ({
24
25
  step: out.step,
25
- summary: out.output,
26
+ summary: this.ensureString(out.output),
26
27
  filePath: out.filePath
27
28
  }))
28
29
  };
@@ -33,6 +34,28 @@ export class WorkflowOutputFormatter {
33
34
  return this.formatSummary(execution);
34
35
  }
35
36
  }
37
+ /**
38
+ * Ensures a value is converted to a string (handles FileReference and objects)
39
+ */
40
+ ensureString(value) {
41
+ if (value === null || value === undefined) {
42
+ return '[No output]';
43
+ }
44
+ if (typeof value === 'string') {
45
+ return value;
46
+ }
47
+ if (typeof value === 'object') {
48
+ // Handle FileReference objects - extract summary or content
49
+ if ('summary' in value && typeof value.summary === 'string') {
50
+ return value.summary;
51
+ }
52
+ if ('content' in value && typeof value.content === 'string') {
53
+ return value.content;
54
+ }
55
+ return JSON.stringify(value, null, 2);
56
+ }
57
+ return String(value);
58
+ }
36
59
  /**
37
60
  * Formats detailed output with all step information
38
61
  */
@@ -54,7 +77,7 @@ export class WorkflowOutputFormatter {
54
77
  if (step.input && step.input !== '[cached]') {
55
78
  output += `**Input:**\n${step.input}...\n\n`;
56
79
  }
57
- output += `${step.output}\n\n`;
80
+ output += `${this.ensureString(step.output)}\n\n`;
58
81
  if (step.filePath) {
59
82
  output += `📄 *Full output saved to: ${step.filePath}*\n\n`;
60
83
  }
@@ -73,7 +96,7 @@ export class WorkflowOutputFormatter {
73
96
  .filter(out => out.filePath)
74
97
  .map(out => ` - ${out.step}: ${out.filePath}`)
75
98
  .join('\n');
76
- let result = lastOutput?.output || "Workflow completed";
99
+ let result = lastOutput ? this.ensureString(lastOutput.output) : "Workflow completed";
77
100
  if (savedFiles) {
78
101
  result += `\n\n**Files saved:**\n${savedFiles}\n\n`;
79
102
  result += `Use Read tool to access full content for detailed analysis.`;
@@ -60,9 +60,9 @@ export class FallbackStrategies {
60
60
  },
61
61
  execute: async (context) => {
62
62
  return {
63
- alternativeTool: 'gpt-5-nano',
63
+ alternativeTool: 'gpt5_mini',
64
64
  fallbackUsed: 'cheaper-model',
65
- warning: 'Falling back to GPT-5 Nano for cost efficiency'
65
+ warning: 'Falling back to gpt-5.1-codex-mini (cost-efficient coding model)'
66
66
  };
67
67
  }
68
68
  });
@@ -9,6 +9,15 @@ export class ModelRouter {
9
9
  reasoning: 10,
10
10
  useFor: ['primary reasoning', 'code', 'analysis']
11
11
  }],
12
+ ['gemini-3-pro-preview', {
13
+ id: 'gemini-3-pro-preview',
14
+ cost: 10,
15
+ quality: 10,
16
+ speed: 8,
17
+ reasoning: 10,
18
+ context: '1M tokens',
19
+ useFor: ['latest Gemini (Nov 2025)', 'enhanced structured outputs', 'multimodal', 'complex analysis']
20
+ }],
12
21
  ['gemini-2.5-pro', {
13
22
  id: 'gemini-2.5-pro',
14
23
  cost: 10,
@@ -43,6 +52,22 @@ export class ModelRouter {
43
52
  reasoning: 10,
44
53
  useFor: ['complex reasoning with evidence']
45
54
  }],
55
+ ['grok-4-1-fast-reasoning', {
56
+ id: 'grok-4-1-fast-reasoning',
57
+ cost: 9,
58
+ quality: 10,
59
+ speed: 7,
60
+ reasoning: 10,
61
+ useFor: ['enhanced reasoning', 'creativity', 'emotional intelligence', 'first-principles']
62
+ }],
63
+ ['grok-4-1-fast-non-reasoning', {
64
+ id: 'grok-4-1-fast-non-reasoning',
65
+ cost: 9,
66
+ quality: 10,
67
+ speed: 9,
68
+ reasoning: 8,
69
+ useFor: ['tool-calling', 'agentic workflows', 'code analysis', 'fast reasoning']
70
+ }],
46
71
  ['grok-4', {
47
72
  id: 'grok-4',
48
73
  cost: 9,
@@ -134,14 +159,14 @@ export class ModelRouter {
134
159
  return 'lmstudio-local';
135
160
  }
136
161
  const taskTypeMap = {
137
- 'code': task.complexity > 0.7 ? 'qwen3-coder-480b' : 'gemini-2.5-flash',
162
+ 'code': task.complexity > 0.7 ? 'qwen3-coder-480b' : 'grok-4-1-fast-non-reasoning',
138
163
  'research': 'perplexity-sonar-pro',
139
164
  'reasoning': task.complexity > 0.5 ? 'gpt5' : 'gpt5_mini',
140
165
  'scout': 'multi-model',
141
166
  'verifier': task.complexity > 0.5 ? 'gpt5' : 'gpt5_mini',
142
167
  'challenger': 'gpt5_mini',
143
168
  'auditor': 'perplexity-sonar-pro',
144
- 'architect': 'grok-4',
169
+ 'architect': 'grok-4-1-fast-reasoning',
145
170
  'commit_guardian': 'gemini-2.5-flash'
146
171
  };
147
172
  return taskTypeMap[task.type] || this.selectByConstraints(task, constraints);
@@ -179,9 +204,9 @@ export class ModelRouter {
179
204
  selectModelsForVerification(variant) {
180
205
  const variants = {
181
206
  'quick_verify': ['gpt5_mini', 'gemini-2.5-flash', 'qwen3-30b'],
182
- 'deep_verify': ['gpt5', 'qwq-32b', 'gpt5_reason', 'gemini-2.5-pro', 'qwen3-coder-480b'],
183
- 'fact_check': ['perplexity-sonar-pro', 'gpt5', 'gemini-2.5-pro'],
184
- 'code_verify': ['qwen3-coder-480b', 'gpt5', 'gemini-2.5-pro'],
207
+ 'deep_verify': ['gpt5', 'qwq-32b', 'gpt5_reason', 'gemini-3-pro-preview', 'qwen3-coder-480b'],
208
+ 'fact_check': ['perplexity-sonar-pro', 'gpt5', 'gemini-3-pro-preview'],
209
+ 'code_verify': ['qwen3-coder-480b', 'gpt5', 'gemini-3-pro-preview'],
185
210
  'security_verify': ['gpt5', 'qwen3-coder-480b', 'grok-4']
186
211
  };
187
212
  return variants[variant] || variants['quick_verify'];