neuro-cli 4.1.2 → 4.2.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/agents/base.js +21 -9
- package/dist/agents/orchestrator.js +37 -11
- package/dist/api/openrouter.js +23 -4
- package/dist/config/config.js +8 -8
- package/dist/core/approval.js +28 -19
- package/dist/core/engine.d.ts +4 -0
- package/dist/core/engine.js +59 -10
- package/dist/core/model-router.js +3 -3
- package/dist/index.js +684 -646
- 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,9 +190,9 @@ 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
|
-
execution.result =
|
|
195
|
+
execution.result = `Max iterations reached (${maxIter}). The task may not be fully completed. Consider using a higher effort level or switching to /orchestrate mode for complex tasks.`;
|
|
189
196
|
}
|
|
190
197
|
execution.endTime = Date.now();
|
|
191
198
|
execution.iterations = iteration;
|
|
@@ -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/config/config.js
CHANGED
|
@@ -19,7 +19,7 @@ export const DEFAULT_CONFIG = {
|
|
|
19
19
|
temperature: 0.7,
|
|
20
20
|
maxTokens: 4096,
|
|
21
21
|
tools: ['read_file', 'search_files', 'list_directory'],
|
|
22
|
-
maxIterations:
|
|
22
|
+
maxIterations: 10,
|
|
23
23
|
},
|
|
24
24
|
coder: {
|
|
25
25
|
name: 'Coder',
|
|
@@ -29,7 +29,7 @@ export const DEFAULT_CONFIG = {
|
|
|
29
29
|
temperature: 0.4,
|
|
30
30
|
maxTokens: 16384,
|
|
31
31
|
tools: ['read_file', 'write_file', 'edit_file', 'search_files', 'list_directory', 'run_command', 'apply_diff'],
|
|
32
|
-
maxIterations:
|
|
32
|
+
maxIterations: 50,
|
|
33
33
|
},
|
|
34
34
|
reviewer: {
|
|
35
35
|
name: 'Reviewer',
|
|
@@ -39,7 +39,7 @@ export const DEFAULT_CONFIG = {
|
|
|
39
39
|
temperature: 0.3,
|
|
40
40
|
maxTokens: 8192,
|
|
41
41
|
tools: ['read_file', 'search_files', 'list_directory'],
|
|
42
|
-
maxIterations:
|
|
42
|
+
maxIterations: 15,
|
|
43
43
|
},
|
|
44
44
|
researcher: {
|
|
45
45
|
name: 'Researcher',
|
|
@@ -49,7 +49,7 @@ export const DEFAULT_CONFIG = {
|
|
|
49
49
|
temperature: 0.5,
|
|
50
50
|
maxTokens: 8192,
|
|
51
51
|
tools: ['read_file', 'search_files', 'list_directory', 'web_search', 'web_fetch'],
|
|
52
|
-
maxIterations:
|
|
52
|
+
maxIterations: 20,
|
|
53
53
|
},
|
|
54
54
|
tester: {
|
|
55
55
|
name: 'Tester',
|
|
@@ -59,7 +59,7 @@ export const DEFAULT_CONFIG = {
|
|
|
59
59
|
temperature: 0.3,
|
|
60
60
|
maxTokens: 8192,
|
|
61
61
|
tools: ['read_file', 'write_file', 'search_files', 'run_command', 'list_directory'],
|
|
62
|
-
maxIterations:
|
|
62
|
+
maxIterations: 25,
|
|
63
63
|
},
|
|
64
64
|
debugger: {
|
|
65
65
|
name: 'Debugger',
|
|
@@ -69,7 +69,7 @@ export const DEFAULT_CONFIG = {
|
|
|
69
69
|
temperature: 0.2,
|
|
70
70
|
maxTokens: 8192,
|
|
71
71
|
tools: ['read_file', 'edit_file', 'search_files', 'run_command', 'list_directory', 'apply_diff'],
|
|
72
|
-
maxIterations:
|
|
72
|
+
maxIterations: 20,
|
|
73
73
|
},
|
|
74
74
|
architect: {
|
|
75
75
|
name: 'Architect',
|
|
@@ -79,7 +79,7 @@ export const DEFAULT_CONFIG = {
|
|
|
79
79
|
temperature: 0.7,
|
|
80
80
|
maxTokens: 8192,
|
|
81
81
|
tools: ['read_file', 'search_files', 'list_directory'],
|
|
82
|
-
maxIterations:
|
|
82
|
+
maxIterations: 15,
|
|
83
83
|
},
|
|
84
84
|
devops: {
|
|
85
85
|
name: 'DevOps',
|
|
@@ -89,7 +89,7 @@ export const DEFAULT_CONFIG = {
|
|
|
89
89
|
temperature: 0.3,
|
|
90
90
|
maxTokens: 8192,
|
|
91
91
|
tools: ['read_file', 'write_file', 'edit_file', 'run_command', 'search_files', 'list_directory'],
|
|
92
|
-
maxIterations:
|
|
92
|
+
maxIterations: 25,
|
|
93
93
|
},
|
|
94
94
|
},
|
|
95
95
|
tools: {
|
package/dist/core/approval.js
CHANGED
|
@@ -344,27 +344,36 @@ export class ApprovalSystem {
|
|
|
344
344
|
readline(prompt) {
|
|
345
345
|
// Pause the main readline to prevent it from also reading stdin input
|
|
346
346
|
// This is the root cause of double character input (YY instead of Y)
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
else if (this.mainRl) {
|
|
353
|
-
this.mainRl.pause();
|
|
347
|
+
if (this.mainRl) {
|
|
348
|
+
const mainInputStream = this.mainRl?.input;
|
|
349
|
+
if (mainInputStream && !mainInputStream.isPaused()) {
|
|
350
|
+
this.mainRl.pause();
|
|
351
|
+
}
|
|
354
352
|
}
|
|
355
353
|
return new Promise((resolve) => {
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
354
|
+
// Use the main readline's question method directly - it handles pause/resume internally
|
|
355
|
+
if (this.mainRl) {
|
|
356
|
+
this.mainRl.question(prompt, (answer) => {
|
|
357
|
+
// Resume the main readline after getting the answer
|
|
358
|
+
if (this.mainRl) {
|
|
359
|
+
this.mainRl.resume();
|
|
360
|
+
this.mainRl.prompt();
|
|
361
|
+
}
|
|
362
|
+
resolve(answer);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
else if (this.rl) {
|
|
366
|
+
this.rl.question(prompt, (answer) => {
|
|
367
|
+
resolve(answer);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
const tempRl = createInterface({ input: process.stdin, output: process.stdout });
|
|
372
|
+
this.rl = tempRl;
|
|
373
|
+
tempRl.question(prompt, (answer) => {
|
|
374
|
+
resolve(answer);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
368
377
|
});
|
|
369
378
|
}
|
|
370
379
|
close() {
|
package/dist/core/engine.d.ts
CHANGED
|
@@ -157,6 +157,10 @@ export declare class NeuroEngine {
|
|
|
157
157
|
* Now delegates to ModelRouter for more sophisticated analysis
|
|
158
158
|
*/
|
|
159
159
|
private assessComplexity;
|
|
160
|
+
/**
|
|
161
|
+
* Select the best agent for a given task category
|
|
162
|
+
*/
|
|
163
|
+
private selectAgentForCategory;
|
|
160
164
|
/**
|
|
161
165
|
* Switch the active model
|
|
162
166
|
*/
|
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
|
});
|
|
@@ -537,7 +537,7 @@ Always consider the strengths of each agent when delegating:
|
|
|
537
537
|
temperature: 0.7,
|
|
538
538
|
maxTokens: 4096,
|
|
539
539
|
tools: [],
|
|
540
|
-
maxIterations:
|
|
540
|
+
maxIterations: 20,
|
|
541
541
|
};
|
|
542
542
|
this.orchestrator = new Orchestrator(orchestratorConfig, this.client, this.registry, process.cwd(), this.sessionManager.getCurrent()?.id || 'default');
|
|
543
543
|
// Register all agents with orchestrator
|
|
@@ -622,7 +622,7 @@ Always consider the strengths of each agent when delegating:
|
|
|
622
622
|
const cwd = process.cwd();
|
|
623
623
|
// Built-in agents
|
|
624
624
|
for (const [key, agentConfig] of Object.entries(this.config.agents)) {
|
|
625
|
-
const overrideConfig = { ...agentConfig
|
|
625
|
+
const overrideConfig = { ...agentConfig };
|
|
626
626
|
const agent = new BaseAgent(overrideConfig, this.client, this.registry, cwd, sessionId);
|
|
627
627
|
this.agents.set(agentConfig.name, agent);
|
|
628
628
|
}
|
|
@@ -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
|
}
|
|
@@ -746,15 +750,44 @@ Always consider the strengths of each agent when delegating:
|
|
|
746
750
|
}
|
|
747
751
|
else {
|
|
748
752
|
const complexity = routeDecision?.complexity || this.assessComplexity(message);
|
|
753
|
+
const category = routeDecision?.category || this.modelRouter.getCategory(message);
|
|
749
754
|
if (complexity === 'simple') {
|
|
750
|
-
|
|
755
|
+
// Route to the best agent based on task category
|
|
756
|
+
const targetAgentName = this.selectAgentForCategory(category);
|
|
757
|
+
const agent = this.agents.get(targetAgentName);
|
|
751
758
|
if (agent) {
|
|
759
|
+
this.ui.info(`Using ${targetAgentName} agent (${category} task)`);
|
|
752
760
|
this.ui.startStreaming();
|
|
753
|
-
|
|
754
|
-
|
|
761
|
+
try {
|
|
762
|
+
result = await agent.run(message, callbacks);
|
|
763
|
+
}
|
|
764
|
+
finally {
|
|
765
|
+
this.ui.endStreaming();
|
|
766
|
+
}
|
|
755
767
|
}
|
|
756
768
|
else {
|
|
757
|
-
|
|
769
|
+
// Fallback to Coder
|
|
770
|
+
const coderAgent = this.agents.get('Coder');
|
|
771
|
+
if (coderAgent) {
|
|
772
|
+
this.ui.startStreaming();
|
|
773
|
+
try {
|
|
774
|
+
result = await coderAgent.run(message, callbacks);
|
|
775
|
+
}
|
|
776
|
+
finally {
|
|
777
|
+
this.ui.endStreaming();
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
this.ui.warning('No agents initialized, using orchestration mode');
|
|
782
|
+
const orchestrateResult = await this.orchestrator.orchestrate(message, callbacks);
|
|
783
|
+
result = {
|
|
784
|
+
content: orchestrateResult.content,
|
|
785
|
+
toolCallsMade: 0,
|
|
786
|
+
iterations: orchestrateResult.execution.iterations,
|
|
787
|
+
usage: orchestrateResult.totalUsage,
|
|
788
|
+
execution: orchestrateResult.execution,
|
|
789
|
+
};
|
|
790
|
+
}
|
|
758
791
|
}
|
|
759
792
|
}
|
|
760
793
|
else {
|
|
@@ -888,6 +921,22 @@ Always consider the strengths of each agent when delegating:
|
|
|
888
921
|
const decision = this.modelRouter.route(message);
|
|
889
922
|
return decision.complexity;
|
|
890
923
|
}
|
|
924
|
+
/**
|
|
925
|
+
* Select the best agent for a given task category
|
|
926
|
+
*/
|
|
927
|
+
selectAgentForCategory(category) {
|
|
928
|
+
const categoryAgentMap = {
|
|
929
|
+
code: 'Coder',
|
|
930
|
+
reasoning: 'Architect',
|
|
931
|
+
creative: 'Coder',
|
|
932
|
+
analysis: 'Researcher',
|
|
933
|
+
conversation: 'Coder',
|
|
934
|
+
debugging: 'Debugger',
|
|
935
|
+
review: 'Reviewer',
|
|
936
|
+
refactoring: 'Coder',
|
|
937
|
+
};
|
|
938
|
+
return categoryAgentMap[category] || 'Coder';
|
|
939
|
+
}
|
|
891
940
|
/**
|
|
892
941
|
* Switch the active model
|
|
893
942
|
*/
|
|
@@ -72,7 +72,7 @@ const COMPLEXITY_SIGNALS = {
|
|
|
72
72
|
/\b(hello|hi|thanks|yes|no|ok|sure|done|correct|right|please)\b/i,
|
|
73
73
|
],
|
|
74
74
|
lengthThreshold: 80,
|
|
75
|
-
maxSteps:
|
|
75
|
+
maxSteps: 5,
|
|
76
76
|
},
|
|
77
77
|
moderate: {
|
|
78
78
|
patterns: [
|
|
@@ -83,7 +83,7 @@ const COMPLEXITY_SIGNALS = {
|
|
|
83
83
|
/\b(refactor|clean|simplify|optimize|restructure)\b/i,
|
|
84
84
|
],
|
|
85
85
|
lengthThreshold: 300,
|
|
86
|
-
maxSteps:
|
|
86
|
+
maxSteps: 15,
|
|
87
87
|
},
|
|
88
88
|
complex: {
|
|
89
89
|
patterns: [
|
|
@@ -97,7 +97,7 @@ const COMPLEXITY_SIGNALS = {
|
|
|
97
97
|
/\b(security\s+(audit|review|hardening)|performance\s+(optim|tuning|profiling)|scalab(ility|le))\b/i,
|
|
98
98
|
],
|
|
99
99
|
lengthThreshold: 500,
|
|
100
|
-
maxSteps:
|
|
100
|
+
maxSteps: 30,
|
|
101
101
|
},
|
|
102
102
|
};
|
|
103
103
|
// ---------------------------------------------------------------------------
|