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.
- package/.env.example +5 -2
- package/dist/src/config/model-constants.js +85 -72
- package/dist/src/config/model-preferences.js +5 -4
- package/dist/src/config.js +2 -1
- package/dist/src/mcp-client.js +3 -3
- package/dist/src/modes/scout.js +2 -1
- package/dist/src/optimization/model-router.js +19 -16
- package/dist/src/orchestrator-instructions.js +1 -1
- package/dist/src/orchestrator-lite.js +1 -1
- package/dist/src/orchestrator.js +1 -1
- package/dist/src/profiles/balanced.js +1 -2
- package/dist/src/profiles/code_focus.js +1 -2
- package/dist/src/profiles/full.js +1 -2
- package/dist/src/profiles/minimal.js +1 -2
- package/dist/src/profiles/research_power.js +1 -2
- package/dist/src/server.js +13 -12
- package/dist/src/tools/gemini-tools.js +15 -16
- package/dist/src/tools/grok-enhanced.js +21 -17
- package/dist/src/tools/grok-tools.js +26 -20
- package/dist/src/tools/openai-tools.js +28 -61
- package/dist/src/tools/tool-router.js +53 -52
- package/dist/src/tools/unified-ai-provider.js +1 -1
- package/dist/src/tools/workflow-runner.js +16 -0
- package/dist/src/tools/workflow-validator-tool.js +1 -1
- package/dist/src/utils/api-keys.js +20 -0
- package/dist/src/validators/interpolation-validator.js +4 -0
- package/dist/src/validators/tool-registry-validator.js +1 -1
- package/dist/src/validators/tool-types.js +0 -1
- package/dist/src/workflows/custom-workflows.js +4 -3
- package/dist/src/workflows/engine/VariableInterpolator.js +30 -3
- package/dist/src/workflows/engine/WorkflowExecutionEngine.js +2 -2
- package/dist/src/workflows/engine/WorkflowOutputFormatter.js +27 -4
- package/dist/src/workflows/fallback-strategies.js +2 -2
- package/dist/src/workflows/model-router.js +30 -5
- package/dist/src/workflows/tool-mapper.js +41 -14
- package/docs/API_KEYS.md +10 -6
- package/docs/TOOLS_REFERENCE.md +7 -43
- package/package.json +1 -1
- package/profiles/balanced.json +1 -2
- package/profiles/code_focus.json +1 -2
- package/profiles/debug_intensive.json +0 -1
- package/profiles/full.json +2 -3
- package/profiles/minimal.json +1 -2
- package/profiles/research_power.json +1 -2
- package/profiles/workflow_builder.json +1 -2
- package/smithery.yaml +2 -2
- package/tools.config.json +15 -3
- package/workflows/code-architecture-review.yaml +5 -3
- package/workflows/creative-brainstorm-yaml.yaml +1 -1
- package/workflows/pingpong.yaml +5 -3
- package/workflows/system/README.md +1 -1
- package/workflows/system/verifier.yaml +8 -5
- 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: "
|
|
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: () =>
|
|
57
|
+
checkAvailability: () => hasOpenAIApiKey()
|
|
57
58
|
},
|
|
58
59
|
{
|
|
59
|
-
name: "
|
|
60
|
-
provider: ToolProvider.
|
|
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: () =>
|
|
67
|
+
checkAvailability: () => hasGrokApiKey()
|
|
67
68
|
},
|
|
68
69
|
{
|
|
69
|
-
name: "
|
|
70
|
-
provider: ToolProvider.
|
|
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: "
|
|
75
|
-
qualityTier: "
|
|
76
|
-
checkAvailability: () =>
|
|
75
|
+
speedTier: "fast",
|
|
76
|
+
qualityTier: "good",
|
|
77
|
+
checkAvailability: () => hasPerplexityApiKey()
|
|
77
78
|
},
|
|
78
79
|
{
|
|
79
|
-
name: "
|
|
80
|
-
provider: ToolProvider.
|
|
80
|
+
name: "kimi_thinking",
|
|
81
|
+
provider: ToolProvider.OPENROUTER,
|
|
81
82
|
categories: [ToolCategory.REASONING],
|
|
82
83
|
priority: 4,
|
|
83
84
|
costTier: "medium",
|
|
84
|
-
speedTier: "
|
|
85
|
+
speedTier: "medium",
|
|
85
86
|
qualityTier: "excellent",
|
|
86
|
-
checkAvailability: () =>
|
|
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: "
|
|
90
|
+
name: "gemini_brainstorm",
|
|
100
91
|
provider: ToolProvider.GEMINI,
|
|
101
92
|
categories: [ToolCategory.REASONING],
|
|
102
|
-
priority:
|
|
93
|
+
priority: 5,
|
|
103
94
|
costTier: "low",
|
|
104
95
|
speedTier: "fast",
|
|
105
96
|
qualityTier: "good",
|
|
106
|
-
checkAvailability: () =>
|
|
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: () =>
|
|
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: () =>
|
|
119
|
+
checkAvailability: () => hasGrokApiKey()
|
|
129
120
|
},
|
|
130
121
|
{
|
|
131
|
-
name: "
|
|
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: () =>
|
|
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: () =>
|
|
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: () =>
|
|
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: () =>
|
|
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: () =>
|
|
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: () =>
|
|
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: () =>
|
|
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: "
|
|
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: () =>
|
|
215
|
+
checkAvailability: () => hasOpenAIApiKey()
|
|
215
216
|
},
|
|
216
217
|
{
|
|
217
|
-
name: "
|
|
218
|
-
provider: ToolProvider.
|
|
218
|
+
name: "gemini_analyze_text",
|
|
219
|
+
provider: ToolProvider.GEMINI,
|
|
219
220
|
categories: [ToolCategory.ANALYSIS],
|
|
220
221
|
priority: 2,
|
|
221
|
-
costTier: "
|
|
222
|
-
speedTier: "
|
|
223
|
-
qualityTier: "
|
|
224
|
-
checkAvailability: () =>
|
|
222
|
+
costTier: "low",
|
|
223
|
+
speedTier: "fast",
|
|
224
|
+
qualityTier: "good",
|
|
225
|
+
checkAvailability: () => hasGeminiApiKey()
|
|
225
226
|
},
|
|
226
227
|
{
|
|
227
|
-
name: "
|
|
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: () =>
|
|
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: () =>
|
|
247
|
+
checkAvailability: () => hasGrokApiKey()
|
|
247
248
|
},
|
|
248
249
|
{
|
|
249
|
-
name: "
|
|
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: () =>
|
|
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
|
-
'
|
|
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
|
-
'
|
|
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
|
|
@@ -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
|
|
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: "
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
|
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: '
|
|
63
|
+
alternativeTool: 'gpt5_mini',
|
|
64
64
|
fallbackUsed: 'cheaper-model',
|
|
65
|
-
warning: 'Falling back to
|
|
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' : '
|
|
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-
|
|
183
|
-
'fact_check': ['perplexity-sonar-pro', 'gpt5', 'gemini-
|
|
184
|
-
'code_verify': ['qwen3-coder-480b', 'gpt5', 'gemini-
|
|
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'];
|