neuro-cli 4.2.0 → 5.0.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.
- package/dist/advisor/advisor.d.ts +0 -0
- package/dist/advisor/advisor.js +0 -0
- package/dist/agents/base.d.ts +21 -2
- package/dist/agents/base.js +174 -31
- package/dist/agents/orchestrator.d.ts +3 -1
- package/dist/agents/orchestrator.js +32 -16
- package/dist/agents/team.d.ts +0 -0
- package/dist/agents/team.js +0 -0
- package/dist/api/models.d.ts +0 -0
- package/dist/api/models.js +0 -0
- package/dist/api/ollama.d.ts +0 -0
- package/dist/api/ollama.js +0 -0
- package/dist/api/openrouter.d.ts +0 -0
- package/dist/api/openrouter.js +0 -0
- package/dist/commands/commands.d.ts +0 -0
- package/dist/commands/commands.js +0 -0
- package/dist/config/config.d.ts +0 -0
- package/dist/config/config.js +18 -18
- package/dist/context/compaction.d.ts +0 -0
- package/dist/context/compaction.js +0 -0
- package/dist/context/custom-agents.d.ts +0 -0
- package/dist/context/custom-agents.js +0 -0
- package/dist/context/custom-tools.d.ts +0 -0
- package/dist/context/custom-tools.js +0 -0
- package/dist/context/git-checkpoint.d.ts +0 -0
- package/dist/context/git-checkpoint.js +0 -0
- package/dist/context/neuro-md.d.ts +0 -0
- package/dist/context/neuro-md.js +0 -0
- package/dist/context/neuroignore.d.ts +0 -0
- package/dist/context/neuroignore.js +0 -0
- package/dist/context/repo-map.d.ts +0 -0
- package/dist/context/repo-map.js +0 -0
- package/dist/context/skill-standard.d.ts +0 -0
- package/dist/context/skill-standard.js +0 -0
- package/dist/context/skill-system.d.ts +0 -0
- package/dist/context/skill-system.js +0 -0
- package/dist/context/tree-sitter.d.ts +0 -0
- package/dist/context/tree-sitter.js +0 -0
- package/dist/core/acp.d.ts +0 -0
- package/dist/core/acp.js +0 -0
- package/dist/core/api-server.d.ts +0 -0
- package/dist/core/api-server.js +0 -0
- package/dist/core/approval.d.ts +4 -0
- package/dist/core/approval.js +46 -27
- package/dist/core/auto-compact.d.ts +0 -0
- package/dist/core/auto-compact.js +0 -0
- package/dist/core/auto-mode.d.ts +0 -0
- package/dist/core/auto-mode.js +0 -0
- package/dist/core/background-session.d.ts +0 -0
- package/dist/core/background-session.js +0 -0
- package/dist/core/cicd.d.ts +0 -0
- package/dist/core/cicd.js +0 -0
- package/dist/core/cloud-sync.d.ts +0 -0
- package/dist/core/cloud-sync.js +0 -0
- package/dist/core/code-review.d.ts +0 -0
- package/dist/core/code-review.js +0 -0
- package/dist/core/completion.d.ts +0 -0
- package/dist/core/completion.js +0 -0
- package/dist/core/context.d.ts +0 -0
- package/dist/core/context.js +0 -0
- package/dist/core/diff-preview.d.ts +0 -0
- package/dist/core/diff-preview.js +8 -16
- package/dist/core/doom-loop.d.ts +0 -0
- package/dist/core/doom-loop.js +0 -0
- package/dist/core/engine.d.ts +6 -3
- package/dist/core/engine.js +58 -56
- package/dist/core/extended-thinking.d.ts +0 -0
- package/dist/core/extended-thinking.js +0 -0
- package/dist/core/fallback.d.ts +0 -0
- package/dist/core/fallback.js +0 -0
- package/dist/core/git-worktree.d.ts +0 -0
- package/dist/core/git-worktree.js +0 -0
- package/dist/core/headless.d.ts +0 -0
- package/dist/core/headless.js +0 -0
- package/dist/core/i18n.d.ts +0 -0
- package/dist/core/i18n.js +0 -0
- package/dist/core/linting.d.ts +0 -0
- package/dist/core/linting.js +0 -0
- package/dist/core/model-router.d.ts +0 -0
- package/dist/core/model-router.js +3 -3
- package/dist/core/multi-model.d.ts +0 -0
- package/dist/core/multi-model.js +0 -0
- package/dist/core/multi-session.d.ts +0 -0
- package/dist/core/multi-session.js +0 -0
- package/dist/core/multimodal.d.ts +0 -0
- package/dist/core/multimodal.js +0 -0
- package/dist/core/observability.d.ts +0 -0
- package/dist/core/observability.js +0 -0
- package/dist/core/os-sandbox.d.ts +0 -0
- package/dist/core/os-sandbox.js +0 -0
- package/dist/core/outcome-grading.d.ts +0 -0
- package/dist/core/outcome-grading.js +0 -0
- package/dist/core/output-styles.d.ts +0 -0
- package/dist/core/output-styles.js +0 -0
- package/dist/core/parallel-agents.d.ts +0 -0
- package/dist/core/parallel-agents.js +0 -0
- package/dist/core/plugin-bundle.d.ts +0 -0
- package/dist/core/plugin-bundle.js +0 -0
- package/dist/core/plugin-sdk.d.ts +0 -0
- package/dist/core/plugin-sdk.js +0 -0
- package/dist/core/prompt-cache.d.ts +0 -0
- package/dist/core/prompt-cache.js +0 -0
- package/dist/core/sandbox.d.ts +0 -0
- package/dist/core/sandbox.js +0 -0
- package/dist/core/scheduled-tasks.d.ts +0 -0
- package/dist/core/scheduled-tasks.js +0 -0
- package/dist/core/security-scanner.d.ts +0 -0
- package/dist/core/security-scanner.js +0 -0
- package/dist/core/session.d.ts +0 -0
- package/dist/core/session.js +0 -0
- package/dist/core/shell-completion.d.ts +0 -0
- package/dist/core/shell-completion.js +0 -0
- package/dist/core/smart-monitor.d.ts +0 -0
- package/dist/core/smart-monitor.js +0 -0
- package/dist/core/spec-driven.d.ts +0 -0
- package/dist/core/spec-driven.js +0 -0
- package/dist/core/spending-warnings.d.ts +0 -0
- package/dist/core/spending-warnings.js +0 -0
- package/dist/core/sub-agent.d.ts +0 -0
- package/dist/core/sub-agent.js +0 -0
- package/dist/core/telemetry.d.ts +0 -0
- package/dist/core/telemetry.js +0 -0
- package/dist/core/terminal-ux.d.ts +0 -0
- package/dist/core/terminal-ux.js +0 -0
- package/dist/core/testing.d.ts +0 -0
- package/dist/core/testing.js +0 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.js +0 -0
- package/dist/core/undo-redo.d.ts +0 -0
- package/dist/core/undo-redo.js +0 -0
- package/dist/core/updater.d.ts +0 -0
- package/dist/core/updater.js +0 -0
- package/dist/core/vim-mode.d.ts +0 -0
- package/dist/core/vim-mode.js +0 -0
- package/dist/core/voice.d.ts +0 -0
- package/dist/core/voice.js +0 -0
- package/dist/core/web-dashboard.d.ts +0 -0
- package/dist/core/web-dashboard.js +0 -0
- package/dist/hooks/hooks.d.ts +0 -0
- package/dist/hooks/hooks.js +0 -0
- package/dist/hooks/llm-evaluator.d.ts +0 -0
- package/dist/hooks/llm-evaluator.js +0 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.js +5 -4
- package/dist/lsp/lsp-manager.d.ts +0 -0
- package/dist/lsp/lsp-manager.js +0 -0
- package/dist/mcp/client.d.ts +0 -0
- package/dist/mcp/client.js +0 -0
- package/dist/mcp/mcp-apps.d.ts +0 -0
- package/dist/mcp/mcp-apps.js +0 -0
- package/dist/tools/bash.d.ts +0 -0
- package/dist/tools/bash.js +0 -0
- package/dist/tools/browser.d.ts +0 -0
- package/dist/tools/browser.js +0 -0
- package/dist/tools/extended.d.ts +0 -0
- package/dist/tools/extended.js +0 -0
- package/dist/tools/file.d.ts +0 -0
- package/dist/tools/file.js +0 -0
- package/dist/tools/github.d.ts +0 -0
- package/dist/tools/github.js +0 -0
- package/dist/tools/index.d.ts +0 -0
- package/dist/tools/index.js +0 -0
- package/dist/tools/memory.d.ts +0 -0
- package/dist/tools/memory.js +0 -0
- package/dist/tools/registry.d.ts +0 -0
- package/dist/tools/registry.js +0 -0
- package/dist/tools/web.d.ts +0 -0
- package/dist/tools/web.js +0 -0
- package/dist/ui/renderer.d.ts +11 -64
- package/dist/ui/renderer.js +172 -156
- package/dist/ui/theme.d.ts +5 -0
- package/dist/ui/theme.js +99 -45
- package/dist/utils/crosspath.d.ts +0 -0
- package/dist/utils/crosspath.js +0 -0
- package/package.json +1 -1
|
File without changes
|
package/dist/advisor/advisor.js
CHANGED
|
File without changes
|
package/dist/agents/base.d.ts
CHANGED
|
@@ -16,6 +16,12 @@ export interface AgentCallbacks {
|
|
|
16
16
|
onApprovalNeeded?: (toolName: string, args: Record<string, unknown>, risk: string) => Promise<boolean>;
|
|
17
17
|
onIteration?: (iteration: number, maxIterations: number) => void;
|
|
18
18
|
onComplete?: (result: AgentRunResult) => void;
|
|
19
|
+
/** Called when the agent starts a new agentic cycle */
|
|
20
|
+
onCycleStart?: (cycle: number, summary: string) => void;
|
|
21
|
+
/** Called when the agent detects task completion */
|
|
22
|
+
onTaskComplete?: (reason: string) => void;
|
|
23
|
+
/** Signal to abort the agent loop (e.g. user pressed Ctrl+C) */
|
|
24
|
+
abortSignal?: AbortSignal;
|
|
19
25
|
}
|
|
20
26
|
export declare class BaseAgent {
|
|
21
27
|
protected config: AgentConfig;
|
|
@@ -24,6 +30,11 @@ export declare class BaseAgent {
|
|
|
24
30
|
protected workingDirectory: string;
|
|
25
31
|
protected sessionId: string;
|
|
26
32
|
protected messages: Message[];
|
|
33
|
+
private lastToolCalls;
|
|
34
|
+
private repeatedActionCount;
|
|
35
|
+
private static readonly MAX_REPEATED_ACTIONS;
|
|
36
|
+
private static readonly MAX_CONSECUTIVE_ERRORS;
|
|
37
|
+
private static readonly ABSOLUTE_MAX_CYCLES;
|
|
27
38
|
constructor(config: AgentConfig, client: OpenRouterClient, registry: ToolRegistry, workingDirectory: string, sessionId: string);
|
|
28
39
|
get name(): string;
|
|
29
40
|
get description(): string;
|
|
@@ -35,7 +46,7 @@ export declare class BaseAgent {
|
|
|
35
46
|
*/
|
|
36
47
|
protected initializeMessages(taskContext?: string): Message[];
|
|
37
48
|
/**
|
|
38
|
-
* Build the system prompt with context
|
|
49
|
+
* Build the system prompt with context — Claude Code-style
|
|
39
50
|
*/
|
|
40
51
|
protected buildSystemPrompt(taskContext?: string): string;
|
|
41
52
|
/**
|
|
@@ -51,7 +62,15 @@ export declare class BaseAgent {
|
|
|
51
62
|
*/
|
|
52
63
|
protected addToolResult(toolCallId: string, content: string, name: string, isError?: boolean): void;
|
|
53
64
|
/**
|
|
54
|
-
*
|
|
65
|
+
* Detect if the model's response indicates the task is complete
|
|
66
|
+
*/
|
|
67
|
+
private isTaskComplete;
|
|
68
|
+
/**
|
|
69
|
+
* Detect doom-loop: same tool+args repeated too many times
|
|
70
|
+
*/
|
|
71
|
+
private detectDoomLoop;
|
|
72
|
+
/**
|
|
73
|
+
* Run the agent loop — Claude Code style: keep going until task is truly done
|
|
55
74
|
*/
|
|
56
75
|
run(task: string, callbacks?: AgentCallbacks, maxIterations?: number): Promise<AgentRunResult>;
|
|
57
76
|
/**
|
package/dist/agents/base.js
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// NeuroCLI - Base Agent
|
|
2
|
+
// NeuroCLI - Base Agent v5.0 (Claude Code-style Agentic Loop)
|
|
3
3
|
// Foundation class for all agents
|
|
4
|
+
// Key changes:
|
|
5
|
+
// - No arbitrary iteration limits; loops until task is complete
|
|
6
|
+
// - Self-evaluation after each LLM turn (is the task done?)
|
|
7
|
+
// - Error recovery with alternative approaches
|
|
8
|
+
// - Continuous tool chaining until the model says "I'm done"
|
|
9
|
+
// - Doom-loop detection replaces hard iteration caps
|
|
4
10
|
// ============================================================
|
|
5
11
|
import { MODELS } from '../api/models.js';
|
|
12
|
+
// Patterns that indicate the model considers the task complete
|
|
13
|
+
const TASK_COMPLETE_PATTERNS = [
|
|
14
|
+
/\b(task|job|work|mission|assignment)\s+(is\s+)?(complete|done|finished|accomplished|fulfilled)\b/i,
|
|
15
|
+
/\bi('ve| have)\s+(completed|finished|done|accomplished)\b/i,
|
|
16
|
+
/\b(all|every)\s+(the\s+)?(task|step|item|file|change|modification)s?\s+(are|is|have been)\s+(done|complete|applied|made|created)\b/i,
|
|
17
|
+
/\bnothing\s+(more|else)\s+(to\s+)?(do|change|modify|update)\b/i,
|
|
18
|
+
/\beverything\s+(is\s+)?(done|complete|ready|set|in\s+place)\b/i,
|
|
19
|
+
];
|
|
20
|
+
// Patterns that indicate the model is still working
|
|
21
|
+
const STILL_WORKING_PATTERNS = [
|
|
22
|
+
/\b(next|then|now|i\s+need\s+to|i\s+should|i\s+will|let\s+me)\b/i,
|
|
23
|
+
/\b(create|write|modify|edit|update|add|remove|delete|fix|implement|refactor|install)\b/i,
|
|
24
|
+
/\b(read|check|verify|test|run|execute|search|find|look)\b/i,
|
|
25
|
+
];
|
|
6
26
|
export class BaseAgent {
|
|
7
27
|
config;
|
|
8
28
|
client;
|
|
@@ -10,6 +30,13 @@ export class BaseAgent {
|
|
|
10
30
|
workingDirectory;
|
|
11
31
|
sessionId;
|
|
12
32
|
messages = [];
|
|
33
|
+
// Track repetitive actions for doom-loop detection
|
|
34
|
+
lastToolCalls = [];
|
|
35
|
+
repeatedActionCount = 0;
|
|
36
|
+
static MAX_REPEATED_ACTIONS = 4;
|
|
37
|
+
static MAX_CONSECUTIVE_ERRORS = 5;
|
|
38
|
+
// Safety ceiling: prevent truly infinite loops (extremely high, only for runaway agents)
|
|
39
|
+
static ABSOLUTE_MAX_CYCLES = 500;
|
|
13
40
|
constructor(config, client, registry, workingDirectory, sessionId) {
|
|
14
41
|
this.config = config;
|
|
15
42
|
this.client = client;
|
|
@@ -41,7 +68,7 @@ export class BaseAgent {
|
|
|
41
68
|
return this.messages;
|
|
42
69
|
}
|
|
43
70
|
/**
|
|
44
|
-
* Build the system prompt with context
|
|
71
|
+
* Build the system prompt with context — Claude Code-style
|
|
45
72
|
*/
|
|
46
73
|
buildSystemPrompt(taskContext) {
|
|
47
74
|
const model = MODELS[this.config.model || ''];
|
|
@@ -55,14 +82,20 @@ export class BaseAgent {
|
|
|
55
82
|
if (taskContext) {
|
|
56
83
|
prompt += `\n\n## Task Context\n${taskContext}`;
|
|
57
84
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
85
|
+
// Claude Code-style guidelines: work until the task is TRULY done
|
|
86
|
+
prompt += `\n\n## Critical Operating Guidelines
|
|
87
|
+
- You MUST continue working until the ENTIRE task is complete — do not stop after partial progress
|
|
88
|
+
- After each tool call, evaluate: "Is the task fully done? If not, what's the next step?"
|
|
89
|
+
- If a tool call fails, try a DIFFERENT approach — never repeat the exact same failed action
|
|
90
|
+
- If you encounter an error, analyze it, understand the root cause, and fix it before moving on
|
|
91
|
+
- When creating or modifying files, verify your changes by reading the file back
|
|
92
|
+
- When implementing features, check that all related files are updated consistently
|
|
93
|
+
- Be thorough: create ALL necessary files, update ALL relevant imports, handle ALL edge cases
|
|
94
|
+
- Do NOT say "I've completed the task" unless you have verified everything works
|
|
95
|
+
- Chain tools naturally: read → understand → plan → implement → verify → continue if needed
|
|
96
|
+
- If you realize the task requires more work than initially thought, KEEP GOING
|
|
97
|
+
- Never use phrases like "the rest is left as an exercise" or "you can continue from here"
|
|
98
|
+
- Your job is to deliver a COMPLETE, WORKING solution — not a partial outline`;
|
|
66
99
|
return prompt;
|
|
67
100
|
}
|
|
68
101
|
/**
|
|
@@ -90,14 +123,60 @@ export class BaseAgent {
|
|
|
90
123
|
});
|
|
91
124
|
}
|
|
92
125
|
/**
|
|
93
|
-
*
|
|
126
|
+
* Detect if the model's response indicates the task is complete
|
|
127
|
+
*/
|
|
128
|
+
isTaskComplete(response, hasToolCalls) {
|
|
129
|
+
// If the model is still calling tools, it's not done
|
|
130
|
+
if (hasToolCalls)
|
|
131
|
+
return false;
|
|
132
|
+
// Check for explicit completion signals
|
|
133
|
+
for (const pattern of TASK_COMPLETE_PATTERNS) {
|
|
134
|
+
if (pattern.test(response))
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
// If no tool calls and the response contains "still working" patterns, it might be stuck
|
|
138
|
+
// But if there are no tool calls and no "still working" patterns, assume done
|
|
139
|
+
const hasWorkingSignals = STILL_WORKING_PATTERNS.some(p => p.test(response));
|
|
140
|
+
if (!hasWorkingSignals && !hasToolCalls) {
|
|
141
|
+
// No tool calls + no "still working" language = probably done
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Detect doom-loop: same tool+args repeated too many times
|
|
148
|
+
*/
|
|
149
|
+
detectDoomLoop(toolName, args) {
|
|
150
|
+
const callSig = `${toolName}:${JSON.stringify(args)}`;
|
|
151
|
+
if (this.lastToolCalls.length >= BaseAgent.MAX_REPEATED_ACTIONS &&
|
|
152
|
+
this.lastToolCalls.slice(-BaseAgent.MAX_REPEATED_ACTIONS).every(c => c === callSig)) {
|
|
153
|
+
this.repeatedActionCount++;
|
|
154
|
+
if (this.repeatedActionCount >= BaseAgent.MAX_REPEATED_ACTIONS) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
this.repeatedActionCount = 0;
|
|
160
|
+
}
|
|
161
|
+
this.lastToolCalls.push(callSig);
|
|
162
|
+
// Keep only recent history
|
|
163
|
+
if (this.lastToolCalls.length > 20) {
|
|
164
|
+
this.lastToolCalls = this.lastToolCalls.slice(-20);
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Run the agent loop — Claude Code style: keep going until task is truly done
|
|
94
170
|
*/
|
|
95
171
|
async run(task, callbacks, maxIterations) {
|
|
96
|
-
|
|
172
|
+
// maxIterations is now a SOFT ceiling with override for continued work
|
|
173
|
+
// If not specified, we use our absolute max (effectively no limit)
|
|
174
|
+
const softCap = maxIterations || this.config.maxIterations || BaseAgent.ABSOLUTE_MAX_CYCLES;
|
|
97
175
|
const startTime = Date.now();
|
|
98
176
|
let totalUsage = { inputTokens: 0, outputTokens: 0, cost: 0 };
|
|
99
177
|
let toolCallsMade = 0;
|
|
100
178
|
let iteration = 0;
|
|
179
|
+
let consecutiveErrors = 0;
|
|
101
180
|
const execution = {
|
|
102
181
|
agentName: this.config.name,
|
|
103
182
|
task,
|
|
@@ -109,8 +188,18 @@ export class BaseAgent {
|
|
|
109
188
|
// Initialize messages
|
|
110
189
|
this.initializeMessages();
|
|
111
190
|
this.addUserMessage(task);
|
|
112
|
-
|
|
113
|
-
|
|
191
|
+
this.lastToolCalls = [];
|
|
192
|
+
this.repeatedActionCount = 0;
|
|
193
|
+
// Main agentic loop — Claude Code style: keep going until done
|
|
194
|
+
for (iteration = 1; iteration <= BaseAgent.ABSOLUTE_MAX_CYCLES; iteration++) {
|
|
195
|
+
// Check abort signal
|
|
196
|
+
if (callbacks?.abortSignal?.aborted) {
|
|
197
|
+
execution.status = 'cancelled';
|
|
198
|
+
execution.result = 'Task cancelled by user';
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
// Report iteration progress (0 means "no fixed limit")
|
|
202
|
+
callbacks?.onIteration?.(iteration, 0);
|
|
114
203
|
// Get tool definitions for this agent
|
|
115
204
|
const toolDefs = this.registry.getDefinitions(this.config.tools);
|
|
116
205
|
// Prepare stream callbacks
|
|
@@ -134,19 +223,35 @@ export class BaseAgent {
|
|
|
134
223
|
tools: toolDefs.length > 0 ? toolDefs : undefined,
|
|
135
224
|
temperature: this.config.temperature,
|
|
136
225
|
maxTokens: this.config.maxTokens,
|
|
137
|
-
stream: false,
|
|
226
|
+
stream: false,
|
|
138
227
|
}, streamCallbacks);
|
|
139
228
|
totalUsage.inputTokens += response.usage.inputTokens;
|
|
140
229
|
totalUsage.outputTokens += response.usage.outputTokens;
|
|
141
230
|
totalUsage.cost += response.usage.cost;
|
|
142
|
-
//
|
|
231
|
+
// Reset error counter on successful response
|
|
232
|
+
consecutiveErrors = 0;
|
|
233
|
+
// Check if task is complete (no tool calls + completion signals)
|
|
143
234
|
if (response.toolCalls.length === 0) {
|
|
144
235
|
this.addAssistantMessage(response.content);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
236
|
+
if (this.isTaskComplete(response.content, false)) {
|
|
237
|
+
callbacks?.onTaskComplete?.('Task completed successfully');
|
|
238
|
+
execution.status = 'completed';
|
|
239
|
+
execution.result = response.content;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
// Model returned text without tool calls but didn't signal completion
|
|
243
|
+
// This might be a summary or partial completion — check if we should continue
|
|
244
|
+
// If the soft cap is reached and there are no tool calls, we're likely done
|
|
245
|
+
if (iteration >= softCap) {
|
|
246
|
+
execution.status = 'completed';
|
|
247
|
+
execution.result = response.content;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
// Otherwise, add a nudge to continue working
|
|
251
|
+
this.addUserMessage('Continue working on the task. If there is more to do, use your tools. If the task is truly complete, say "Task complete" and summarize what was done.');
|
|
252
|
+
continue;
|
|
148
253
|
}
|
|
149
|
-
// Process tool calls
|
|
254
|
+
// Process tool calls — the agent is still working
|
|
150
255
|
this.addAssistantMessage(response.content, response.toolCalls);
|
|
151
256
|
for (const toolCall of response.toolCalls) {
|
|
152
257
|
let args;
|
|
@@ -158,11 +263,20 @@ export class BaseAgent {
|
|
|
158
263
|
}
|
|
159
264
|
const toolName = toolCall.function.name;
|
|
160
265
|
toolCallsMade++;
|
|
266
|
+
// Doom-loop detection
|
|
267
|
+
if (this.detectDoomLoop(toolName, args)) {
|
|
268
|
+
const msg = `Doom loop detected: "${toolName}" called with the same arguments too many times. Breaking the loop.`;
|
|
269
|
+
callbacks?.onThinking?.(msg);
|
|
270
|
+
this.addToolResult(toolCall.id, msg, toolName, true);
|
|
271
|
+
// Add a nudge to try a different approach
|
|
272
|
+
this.addUserMessage('You seem to be stuck in a loop, calling the same tool with the same arguments repeatedly. Try a completely different approach to solve this problem.');
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
161
275
|
callbacks?.onToolCall?.(toolName, args);
|
|
162
276
|
// Check if approval is needed
|
|
163
277
|
const needsApproval = this.registry.getApprovalRequest(toolName, args);
|
|
164
278
|
if (needsApproval && this.config.autoApprove !== true) {
|
|
165
|
-
const approved = await callbacks?.onApprovalNeeded?.(toolName, args, needsApproval.risk) ?? true;
|
|
279
|
+
const approved = await callbacks?.onApprovalNeeded?.(toolName, args, needsApproval.risk) ?? true;
|
|
166
280
|
if (!approved) {
|
|
167
281
|
this.addToolResult(toolCall.id, 'User denied this tool call.', toolName);
|
|
168
282
|
callbacks?.onToolResult?.(toolName, 'Denied by user', false);
|
|
@@ -176,23 +290,52 @@ export class BaseAgent {
|
|
|
176
290
|
agentName: this.config.name,
|
|
177
291
|
onProgress: (msg) => callbacks?.onThinking?.(msg),
|
|
178
292
|
};
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
293
|
+
try {
|
|
294
|
+
const result = await this.registry.execute(toolName, args, toolContext);
|
|
295
|
+
this.addToolResult(toolCall.id, result.content, toolName, result.isError);
|
|
296
|
+
callbacks?.onToolResult?.(toolName, result.content, result.isError || false);
|
|
297
|
+
// If tool had an error, note it but continue (error recovery)
|
|
298
|
+
if (result.isError) {
|
|
299
|
+
consecutiveErrors++;
|
|
300
|
+
if (consecutiveErrors >= BaseAgent.MAX_CONSECUTIVE_ERRORS) {
|
|
301
|
+
// Add context about repeated failures
|
|
302
|
+
this.addUserMessage(`The last ${consecutiveErrors} tool calls have resulted in errors. Please step back, analyze the situation, and try a completely different approach. Read relevant files first to understand the current state before making changes.`);
|
|
303
|
+
consecutiveErrors = 0; // Reset after nudge
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (toolError) {
|
|
308
|
+
const errMsg = toolError instanceof Error ? toolError.message : String(toolError);
|
|
309
|
+
this.addToolResult(toolCall.id, `Tool execution error: ${errMsg}`, toolName, true);
|
|
310
|
+
callbacks?.onToolResult?.(toolName, errMsg, true);
|
|
311
|
+
consecutiveErrors++;
|
|
312
|
+
}
|
|
183
313
|
}
|
|
314
|
+
// After processing all tool calls for this iteration, report cycle progress
|
|
315
|
+
const cycleSummary = `${toolCallsMade} tools used, iteration ${iteration}`;
|
|
316
|
+
callbacks?.onCycleStart?.(iteration, cycleSummary);
|
|
184
317
|
}
|
|
185
318
|
catch (error) {
|
|
186
319
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
320
|
+
consecutiveErrors++;
|
|
321
|
+
// If we've had too many consecutive API errors, stop
|
|
322
|
+
if (consecutiveErrors >= BaseAgent.MAX_CONSECUTIVE_ERRORS) {
|
|
323
|
+
this.addAssistantMessage(`Error encountered: ${errMsg}. Too many consecutive errors, stopping.`);
|
|
324
|
+
execution.status = 'failed';
|
|
325
|
+
execution.result = errMsg;
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
// Otherwise, add error to conversation and let the model try to recover
|
|
329
|
+
this.addAssistantMessage(`Error encountered: ${errMsg}. Retrying...`);
|
|
330
|
+
// Brief delay before retry
|
|
331
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * consecutiveErrors));
|
|
332
|
+
continue;
|
|
191
333
|
}
|
|
192
334
|
}
|
|
193
|
-
|
|
335
|
+
// If we hit the absolute max, mark as completed (not failed — the work might be sufficient)
|
|
336
|
+
if (iteration > BaseAgent.ABSOLUTE_MAX_CYCLES && execution.status === 'running') {
|
|
194
337
|
execution.status = 'completed';
|
|
195
|
-
execution.result = `
|
|
338
|
+
execution.result = `Agent reached maximum processing cycles (${BaseAgent.ABSOLUTE_MAX_CYCLES}). The task has been worked on extensively. Review the results and provide further instructions if needed.`;
|
|
196
339
|
}
|
|
197
340
|
execution.endTime = Date.now();
|
|
198
341
|
execution.iterations = iteration;
|
|
@@ -35,7 +35,9 @@ export declare class Orchestrator extends BaseAgent {
|
|
|
35
35
|
*/
|
|
36
36
|
plan(task: string, callbacks?: AgentCallbacks): Promise<OrchestratedPlan>;
|
|
37
37
|
/**
|
|
38
|
-
* Execute a planned task with sub-agents
|
|
38
|
+
* Execute a planned task with sub-agents — Claude Code style
|
|
39
|
+
* Sub-agents run until their tasks are complete.
|
|
40
|
+
* After all tasks complete, verify the overall result and re-plan if needed.
|
|
39
41
|
*/
|
|
40
42
|
orchestrate(task: string, callbacks?: AgentCallbacks, autoPlan?: boolean): Promise<OrchestratorResult>;
|
|
41
43
|
/**
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// NeuroCLI - Orchestrator Agent
|
|
2
|
+
// NeuroCLI - Orchestrator Agent v5.0 (Claude Code-style)
|
|
3
3
|
// Central coordinator that manages sub-agents
|
|
4
|
+
// Key changes:
|
|
5
|
+
// - Sub-agents run until their tasks are complete (no hard limits)
|
|
6
|
+
// - Re-planning when sub-tasks fail or need follow-up
|
|
7
|
+
// - Verification loop after all sub-tasks complete
|
|
4
8
|
// ============================================================
|
|
5
9
|
import { BaseAgent } from './base.js';
|
|
6
10
|
export class Orchestrator extends BaseAgent {
|
|
@@ -40,6 +44,7 @@ ${task}
|
|
|
40
44
|
2. Assign each sub-task to the most appropriate agent
|
|
41
45
|
3. Specify dependencies between tasks (which tasks must complete before others can start)
|
|
42
46
|
4. Provide any context each agent needs
|
|
47
|
+
5. IMPORTANT: Be thorough — include ALL steps needed to complete the task end-to-end
|
|
43
48
|
|
|
44
49
|
Respond with a JSON plan in this exact format:
|
|
45
50
|
\`\`\`json
|
|
@@ -56,7 +61,7 @@ Respond with a JSON plan in this exact format:
|
|
|
56
61
|
}
|
|
57
62
|
\`\`\``;
|
|
58
63
|
const messages = [
|
|
59
|
-
{ role: 'system', content: 'You are an expert task orchestrator. Always respond with valid JSON.', timestamp: Date.now() },
|
|
64
|
+
{ role: 'system', content: 'You are an expert task orchestrator. Always respond with valid JSON. Be thorough — include every step needed.', timestamp: Date.now() },
|
|
60
65
|
{ role: 'user', content: planPrompt, timestamp: Date.now() },
|
|
61
66
|
];
|
|
62
67
|
let response;
|
|
@@ -64,21 +69,18 @@ Respond with a JSON plan in this exact format:
|
|
|
64
69
|
response = await this.client.quickChat(this.config.model || 'anthropic/claude-sonnet-4', messages);
|
|
65
70
|
}
|
|
66
71
|
catch {
|
|
67
|
-
// API error during planning, fallback to direct approach
|
|
68
72
|
return {
|
|
69
73
|
reasoning: 'API error during planning, falling back to direct approach',
|
|
70
74
|
tasks: [{ agent: 'Coder', task, dependsOn: [] }],
|
|
71
75
|
};
|
|
72
76
|
}
|
|
73
77
|
try {
|
|
74
|
-
// Extract JSON from response
|
|
75
78
|
const jsonMatch = response.content.match(/```json\n([\s\S]*?)\n```/) ||
|
|
76
79
|
response.content.match(/\{[\s\S]*\}/);
|
|
77
80
|
const jsonStr = jsonMatch ? (jsonMatch[1] || jsonMatch[0]) : response.content;
|
|
78
81
|
return JSON.parse(jsonStr);
|
|
79
82
|
}
|
|
80
83
|
catch {
|
|
81
|
-
// Fallback: use coder agent directly
|
|
82
84
|
return {
|
|
83
85
|
reasoning: 'Could not parse plan, defaulting to direct coding approach',
|
|
84
86
|
tasks: [{ agent: 'Coder', task, dependsOn: [] }],
|
|
@@ -86,7 +88,9 @@ Respond with a JSON plan in this exact format:
|
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
/**
|
|
89
|
-
* Execute a planned task with sub-agents
|
|
91
|
+
* Execute a planned task with sub-agents — Claude Code style
|
|
92
|
+
* Sub-agents run until their tasks are complete.
|
|
93
|
+
* After all tasks complete, verify the overall result and re-plan if needed.
|
|
90
94
|
*/
|
|
91
95
|
async orchestrate(task, callbacks, autoPlan = true) {
|
|
92
96
|
const startTime = Date.now();
|
|
@@ -103,7 +107,7 @@ Respond with a JSON plan in this exact format:
|
|
|
103
107
|
// Step 1: Create execution plan
|
|
104
108
|
let plan;
|
|
105
109
|
if (autoPlan) {
|
|
106
|
-
callbacks?.onThinking?.('
|
|
110
|
+
callbacks?.onThinking?.('Planning task decomposition...');
|
|
107
111
|
plan = await this.plan(task, callbacks);
|
|
108
112
|
}
|
|
109
113
|
else {
|
|
@@ -112,11 +116,11 @@ Respond with a JSON plan in this exact format:
|
|
|
112
116
|
tasks: [{ agent: 'Coder', task }],
|
|
113
117
|
};
|
|
114
118
|
}
|
|
115
|
-
callbacks?.onThinking?.(
|
|
116
|
-
|
|
117
|
-
// Step 2: Execute tasks respecting dependencies
|
|
119
|
+
callbacks?.onThinking?.(`Plan: ${plan.tasks.length} sub-tasks — ${plan.reasoning}`);
|
|
120
|
+
// Step 2: Execute tasks respecting dependencies — each agent runs until done
|
|
118
121
|
const completedTasks = new Set();
|
|
119
122
|
let taskIndex = 0;
|
|
123
|
+
let maxReplans = 3; // Allow up to 3 re-planning cycles
|
|
120
124
|
while (completedTasks.size < plan.tasks.length) {
|
|
121
125
|
// Find tasks whose dependencies are all completed
|
|
122
126
|
const readyTasks = plan.tasks.filter(t => {
|
|
@@ -127,23 +131,28 @@ Respond with a JSON plan in this exact format:
|
|
|
127
131
|
return t.dependsOn.every(dep => completedTasks.has(dep));
|
|
128
132
|
});
|
|
129
133
|
if (readyTasks.length === 0) {
|
|
130
|
-
// Deadlock detection
|
|
134
|
+
// Deadlock detection
|
|
131
135
|
const unresolved = plan.tasks.filter(t => !completedTasks.has(t.agent + ':' + t.task));
|
|
132
136
|
if (unresolved.length > 0) {
|
|
133
|
-
callbacks?.onThinking?.(
|
|
137
|
+
callbacks?.onThinking?.(`Deadlock detected. Unresolved tasks: ${unresolved.map(t => t.agent + ':' + t.task).join(', ')}`);
|
|
138
|
+
// Clear dependency constraints to break deadlock
|
|
139
|
+
for (const t of unresolved) {
|
|
140
|
+
t.dependsOn = [];
|
|
141
|
+
}
|
|
142
|
+
continue;
|
|
134
143
|
}
|
|
135
144
|
break;
|
|
136
145
|
}
|
|
137
|
-
// Execute ready tasks (could
|
|
146
|
+
// Execute ready tasks (sequentially for safety, could parallelize in future)
|
|
138
147
|
for (const subTask of readyTasks) {
|
|
139
148
|
const agent = this.agents.get(subTask.agent);
|
|
140
149
|
if (!agent) {
|
|
141
|
-
callbacks?.onThinking?.(
|
|
150
|
+
callbacks?.onThinking?.(`Agent not found: ${subTask.agent}`);
|
|
142
151
|
completedTasks.add(subTask.agent + ':' + subTask.task);
|
|
143
152
|
continue;
|
|
144
153
|
}
|
|
145
154
|
taskIndex++;
|
|
146
|
-
callbacks?.onThinking?.(
|
|
155
|
+
callbacks?.onThinking?.(`[${taskIndex}/${plan.tasks.length}] Running ${subTask.agent}: ${subTask.task.slice(0, 80)}...`);
|
|
147
156
|
// Build context from previous agent results
|
|
148
157
|
let fullTask = subTask.task;
|
|
149
158
|
if (subTask.context) {
|
|
@@ -160,16 +169,23 @@ Respond with a JSON plan in this exact format:
|
|
|
160
169
|
fullTask = `${fullTask}\n\n## Context from previous agents:\n${depResults}`;
|
|
161
170
|
}
|
|
162
171
|
}
|
|
172
|
+
// Run the sub-agent — it will keep going until its task is complete
|
|
163
173
|
let result;
|
|
164
174
|
try {
|
|
165
175
|
result = await agent.run(fullTask, {
|
|
166
176
|
onToken: (token) => callbacks?.onToken?.(token),
|
|
167
177
|
onToolCall: (name, args) => callbacks?.onToolCall?.(name, args),
|
|
168
|
-
onToolResult: (name,
|
|
178
|
+
onToolResult: (name, res, isError) => callbacks?.onToolResult?.(name, res, isError),
|
|
169
179
|
onApprovalNeeded: async (name, args, risk) => {
|
|
170
180
|
return callbacks?.onApprovalNeeded?.(name, args, risk) ?? true;
|
|
171
181
|
},
|
|
172
182
|
onThinking: (thinking) => callbacks?.onThinking?.(thinking),
|
|
183
|
+
onIteration: (i, _max) => {
|
|
184
|
+
callbacks?.onThinking?.(` ${subTask.agent} step ${i}`);
|
|
185
|
+
},
|
|
186
|
+
onTaskComplete: (reason) => {
|
|
187
|
+
callbacks?.onThinking?.(` ${subTask.agent}: ${reason}`);
|
|
188
|
+
},
|
|
173
189
|
});
|
|
174
190
|
}
|
|
175
191
|
catch (error) {
|
package/dist/agents/team.d.ts
CHANGED
|
File without changes
|
package/dist/agents/team.js
CHANGED
|
File without changes
|
package/dist/api/models.d.ts
CHANGED
|
File without changes
|
package/dist/api/models.js
CHANGED
|
File without changes
|
package/dist/api/ollama.d.ts
CHANGED
|
File without changes
|
package/dist/api/ollama.js
CHANGED
|
File without changes
|
package/dist/api/openrouter.d.ts
CHANGED
|
File without changes
|
package/dist/api/openrouter.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/config/config.d.ts
CHANGED
|
File without changes
|