neuro-cli 4.1.2 → 4.1.3
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/dist/agents/base.js +20 -8
- package/dist/agents/orchestrator.js +37 -11
- package/dist/api/openrouter.js +23 -4
- package/dist/core/engine.js +24 -7
- package/dist/index.js +682 -645
- package/package.json +1 -1
package/dist/agents/base.js
CHANGED
|
@@ -117,7 +117,14 @@ export class BaseAgent {
|
|
|
117
117
|
const streamCallbacks = {
|
|
118
118
|
onToken: (token) => callbacks?.onToken?.(token),
|
|
119
119
|
onThinking: (thinking) => callbacks?.onThinking?.(thinking),
|
|
120
|
-
onToolCall: (tc) =>
|
|
120
|
+
onToolCall: (tc) => {
|
|
121
|
+
let args = {};
|
|
122
|
+
try {
|
|
123
|
+
args = JSON.parse(tc.function.arguments);
|
|
124
|
+
}
|
|
125
|
+
catch { /* malformed LLM args */ }
|
|
126
|
+
callbacks?.onToolCall?.(tc.function.name, args);
|
|
127
|
+
},
|
|
121
128
|
};
|
|
122
129
|
// Call LLM
|
|
123
130
|
try {
|
|
@@ -183,7 +190,7 @@ export class BaseAgent {
|
|
|
183
190
|
break;
|
|
184
191
|
}
|
|
185
192
|
}
|
|
186
|
-
if (iteration > maxIter) {
|
|
193
|
+
if (iteration > maxIter && execution.status === 'running') {
|
|
187
194
|
execution.status = 'completed';
|
|
188
195
|
execution.result = 'Max iterations reached';
|
|
189
196
|
}
|
|
@@ -204,12 +211,17 @@ export class BaseAgent {
|
|
|
204
211
|
* Quick single-turn query (no tool loop)
|
|
205
212
|
*/
|
|
206
213
|
async query(prompt) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
214
|
+
try {
|
|
215
|
+
const messages = [
|
|
216
|
+
{ role: 'system', content: this.config.systemPrompt, timestamp: Date.now() },
|
|
217
|
+
{ role: 'user', content: prompt, timestamp: Date.now() },
|
|
218
|
+
];
|
|
219
|
+
const response = await this.client.quickChat(this.config.model || 'anthropic/claude-sonnet-4', messages);
|
|
220
|
+
return response.content;
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
224
|
+
}
|
|
213
225
|
}
|
|
214
226
|
}
|
|
215
227
|
//# sourceMappingURL=base.js.map
|
|
@@ -59,7 +59,17 @@ Respond with a JSON plan in this exact format:
|
|
|
59
59
|
{ role: 'system', content: 'You are an expert task orchestrator. Always respond with valid JSON.', timestamp: Date.now() },
|
|
60
60
|
{ role: 'user', content: planPrompt, timestamp: Date.now() },
|
|
61
61
|
];
|
|
62
|
-
|
|
62
|
+
let response;
|
|
63
|
+
try {
|
|
64
|
+
response = await this.client.quickChat(this.config.model || 'anthropic/claude-sonnet-4', messages);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// API error during planning, fallback to direct approach
|
|
68
|
+
return {
|
|
69
|
+
reasoning: 'API error during planning, falling back to direct approach',
|
|
70
|
+
tasks: [{ agent: 'Coder', task, dependsOn: [] }],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
63
73
|
try {
|
|
64
74
|
// Extract JSON from response
|
|
65
75
|
const jsonMatch = response.content.match(/```json\n([\s\S]*?)\n```/) ||
|
|
@@ -117,7 +127,11 @@ Respond with a JSON plan in this exact format:
|
|
|
117
127
|
return t.dependsOn.every(dep => completedTasks.has(dep));
|
|
118
128
|
});
|
|
119
129
|
if (readyTasks.length === 0) {
|
|
120
|
-
// Deadlock
|
|
130
|
+
// Deadlock detection - log unresolved tasks
|
|
131
|
+
const unresolved = plan.tasks.filter(t => !completedTasks.has(t.agent + ':' + t.task));
|
|
132
|
+
if (unresolved.length > 0) {
|
|
133
|
+
callbacks?.onThinking?.(`⚠️ Deadlock detected. Unresolved tasks: ${unresolved.map(t => t.agent + ':' + t.task).join(', ')}`);
|
|
134
|
+
}
|
|
121
135
|
break;
|
|
122
136
|
}
|
|
123
137
|
// Execute ready tasks (could be parallelized in future)
|
|
@@ -146,15 +160,27 @@ Respond with a JSON plan in this exact format:
|
|
|
146
160
|
fullTask = `${fullTask}\n\n## Context from previous agents:\n${depResults}`;
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
let result;
|
|
164
|
+
try {
|
|
165
|
+
result = await agent.run(fullTask, {
|
|
166
|
+
onToken: (token) => callbacks?.onToken?.(token),
|
|
167
|
+
onToolCall: (name, args) => callbacks?.onToolCall?.(name, args),
|
|
168
|
+
onToolResult: (name, result, isError) => callbacks?.onToolResult?.(name, result, isError),
|
|
169
|
+
onApprovalNeeded: async (name, args, risk) => {
|
|
170
|
+
return callbacks?.onApprovalNeeded?.(name, args, risk) ?? true;
|
|
171
|
+
},
|
|
172
|
+
onThinking: (thinking) => callbacks?.onThinking?.(thinking),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
177
|
+
result = {
|
|
178
|
+
content: `Agent ${subTask.agent} failed: ${errMsg}`,
|
|
179
|
+
toolCallsMade: 0, iterations: 0,
|
|
180
|
+
usage: { inputTokens: 0, outputTokens: 0, cost: 0 },
|
|
181
|
+
execution: { agentName: subTask.agent, task: subTask.task, startTime: Date.now(), iterations: 0, tokensUsed: 0, status: 'failed' },
|
|
182
|
+
};
|
|
183
|
+
}
|
|
158
184
|
agentResults.set(subTask.agent + ':' + subTask.task, result);
|
|
159
185
|
totalUsage.inputTokens += result.usage.inputTokens;
|
|
160
186
|
totalUsage.outputTokens += result.usage.outputTokens;
|
package/dist/api/openrouter.js
CHANGED
|
@@ -173,13 +173,26 @@ export class OpenRouterClient {
|
|
|
173
173
|
const { done, value } = await reader.read();
|
|
174
174
|
if (done)
|
|
175
175
|
break;
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
try {
|
|
177
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
178
|
+
parser.feed(chunk);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Skip malformed SSE chunks
|
|
182
|
+
}
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
catch (error) {
|
|
181
186
|
callbacks.onError?.(error);
|
|
182
|
-
|
|
187
|
+
// Return partial result instead of crashing the agent
|
|
188
|
+
const content = fullContent.join('');
|
|
189
|
+
const toolCallList = Array.from(toolCalls.values());
|
|
190
|
+
const cost = calculateCost(modelId, inputTokens, outputTokens);
|
|
191
|
+
const usage = { inputTokens, outputTokens, cost };
|
|
192
|
+
this.totalUsage.inputTokens += inputTokens;
|
|
193
|
+
this.totalUsage.outputTokens += outputTokens;
|
|
194
|
+
this.totalUsage.cost += cost;
|
|
195
|
+
return { content, toolCalls: toolCallList, usage };
|
|
183
196
|
}
|
|
184
197
|
const content = fullContent.join('');
|
|
185
198
|
const toolCallList = Array.from(toolCalls.values());
|
|
@@ -195,7 +208,13 @@ export class OpenRouterClient {
|
|
|
195
208
|
* Handle non-streaming response
|
|
196
209
|
*/
|
|
197
210
|
async handleNonStreamingResponse(response, modelId) {
|
|
198
|
-
|
|
211
|
+
let data;
|
|
212
|
+
try {
|
|
213
|
+
data = await response.json();
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
throw new Error('Invalid JSON response from OpenRouter API');
|
|
217
|
+
}
|
|
199
218
|
const content = data.choices?.[0]?.message?.content || '';
|
|
200
219
|
const toolCalls = data.choices?.[0]?.message?.tool_calls || [];
|
|
201
220
|
const inputTokens = data.usage?.prompt_tokens || 0;
|
package/dist/core/engine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// NeuroCLI - NeuroEngine v4.1.
|
|
2
|
+
// NeuroCLI - NeuroEngine v4.1.3
|
|
3
3
|
// The main engine that ties everything together
|
|
4
4
|
// Now with: Sandbox, Plugin SDK, Enhanced MCP, Enhanced Approval,
|
|
5
5
|
// Model Router, Prompt Cache, Undo/Redo, Output Styles,
|
|
@@ -483,7 +483,7 @@ export class NeuroEngine {
|
|
|
483
483
|
this.gitWorktree = new GitWorktreeManager(process.cwd());
|
|
484
484
|
// Auto-Updater
|
|
485
485
|
this.updater = new AutoUpdater({
|
|
486
|
-
currentVersion: '4.1.
|
|
486
|
+
currentVersion: '4.1.3',
|
|
487
487
|
autoCheck: true,
|
|
488
488
|
autoUpdate: false,
|
|
489
489
|
});
|
|
@@ -729,8 +729,12 @@ Always consider the strengths of each agent when delegating:
|
|
|
729
729
|
agent.configModel = this.config.defaultModel;
|
|
730
730
|
}
|
|
731
731
|
this.ui.startStreaming();
|
|
732
|
-
|
|
733
|
-
|
|
732
|
+
try {
|
|
733
|
+
result = await agent.run(message, callbacks);
|
|
734
|
+
}
|
|
735
|
+
finally {
|
|
736
|
+
this.ui.endStreaming();
|
|
737
|
+
}
|
|
734
738
|
// Restore original model
|
|
735
739
|
agent.configModel = originalModel;
|
|
736
740
|
}
|
|
@@ -750,11 +754,24 @@ Always consider the strengths of each agent when delegating:
|
|
|
750
754
|
const agent = this.agents.get('Coder');
|
|
751
755
|
if (agent) {
|
|
752
756
|
this.ui.startStreaming();
|
|
753
|
-
|
|
754
|
-
|
|
757
|
+
try {
|
|
758
|
+
result = await agent.run(message, callbacks);
|
|
759
|
+
}
|
|
760
|
+
finally {
|
|
761
|
+
this.ui.endStreaming();
|
|
762
|
+
}
|
|
755
763
|
}
|
|
756
764
|
else {
|
|
757
|
-
|
|
765
|
+
// Fallback to orchestration instead of crashing
|
|
766
|
+
this.ui.warning('Coder agent not initialized, using orchestration mode');
|
|
767
|
+
const orchestrateResult = await this.orchestrator.orchestrate(message, callbacks);
|
|
768
|
+
result = {
|
|
769
|
+
content: orchestrateResult.content,
|
|
770
|
+
toolCallsMade: 0,
|
|
771
|
+
iterations: orchestrateResult.execution.iterations,
|
|
772
|
+
usage: orchestrateResult.totalUsage,
|
|
773
|
+
execution: orchestrateResult.execution,
|
|
774
|
+
};
|
|
758
775
|
}
|
|
759
776
|
}
|
|
760
777
|
else {
|