micro-models-agent 0.13.1 → 0.13.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/cli/commands.js +2 -1
- package/dist/cli/repl.js +42 -20
- package/dist/config/config.js +42 -0
- package/dist/config/defaults.js +1 -1
- package/dist/config/security.js +1 -1
- package/dist/core/agent-moe.js +98 -0
- package/dist/core/agent.js +41 -250
- package/dist/core/bootstrap.js +4 -3
- package/dist/core/session-logger.js +122 -0
- package/dist/i18n/en.json +1 -0
- package/dist/i18n/ru.json +1 -0
- package/dist/llm/openai-compat.js +6 -9
- package/dist/modules/execution/moe-executor.js +13 -0
- package/dist/modules/hallucination/confidence.js +22 -15
- package/dist/modules/hallucination/consistency.js +29 -1
- package/dist/modules/hallucination/factual.js +49 -7
- package/dist/modules/processes/registry.js +6 -0
- package/dist/modules/security/command-validator.js +57 -14
- package/dist/modules/security/encryption.js +8 -6
- package/dist/modules/session/module.js +0 -4
- package/dist/tools/bash.js +27 -0
- package/dist/tools/create-dir.js +3 -4
- package/dist/tools/delete-file.js +3 -4
- package/dist/tools/edit-file.js +3 -4
- package/dist/tools/executor.js +6 -0
- package/dist/tools/file-info.js +3 -4
- package/dist/tools/list-dir.js +4 -4
- package/dist/tools/path-utils.js +51 -0
- package/dist/tools/read-file.js +6 -3
- package/dist/tools/write-file.js +5 -5
- package/dist/ui/renderer.js +1 -1
- package/package.json +1 -1
package/dist/core/agent.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { t } from "../i18n/index";
|
|
2
2
|
import { pc } from "../ui/colors";
|
|
3
3
|
import { PromptBuilder } from "./prompt-builder";
|
|
4
|
-
import { OrchestratorClient } from "../llm/orchestrator";
|
|
5
|
-
import { validatePlan, applyAutoFixes, } from "../modules/execution/plan-validator";
|
|
6
|
-
import { MoEExecutor } from "../modules/execution/moe-executor";
|
|
7
|
-
import { StepVerifier } from "../modules/execution/verifier";
|
|
8
4
|
import { processRegistry } from "../modules/processes";
|
|
5
|
+
import { SessionLogger } from "./session-logger";
|
|
6
|
+
import { runWithMoE } from "./agent-moe";
|
|
9
7
|
const TOOL_RESULT_MAX_TOKENS_RATIO = 0.3;
|
|
10
8
|
const TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000;
|
|
11
9
|
export class Agent {
|
|
@@ -15,9 +13,8 @@ export class Agent {
|
|
|
15
13
|
this.deps = deps;
|
|
16
14
|
}
|
|
17
15
|
setScope() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
executor.ctx.scope = this.deps.scope;
|
|
16
|
+
if (this.deps.scope) {
|
|
17
|
+
this.deps.toolExecutor.setScope(this.deps.scope);
|
|
21
18
|
}
|
|
22
19
|
}
|
|
23
20
|
buildSystemPrompt() {
|
|
@@ -42,7 +39,6 @@ export class Agent {
|
|
|
42
39
|
}
|
|
43
40
|
return builder.build();
|
|
44
41
|
}
|
|
45
|
-
/** Refresh the system prompt in context if dynamic blocks changed. */
|
|
46
42
|
refreshSystemPrompt() {
|
|
47
43
|
const { prompt } = this.buildSystemPrompt();
|
|
48
44
|
const current = this.deps.contextManager
|
|
@@ -70,7 +66,8 @@ export class Agent {
|
|
|
70
66
|
}
|
|
71
67
|
async run(input, onChunk, onMeta, onTool, onPhase) {
|
|
72
68
|
this.setScope();
|
|
73
|
-
const { config, llmProvider, toolExecutor, pluginManager, contextManager,
|
|
69
|
+
const { config, llmProvider, toolExecutor, pluginManager, contextManager, logger, sessionManager, } = this.deps;
|
|
70
|
+
const slog = new SessionLogger(sessionManager);
|
|
74
71
|
if (sessionManager && !sessionManager.getActive()) {
|
|
75
72
|
sessionManager.create();
|
|
76
73
|
logger.debug(`Session started: ${sessionManager.getActive()}`);
|
|
@@ -80,19 +77,9 @@ export class Agent {
|
|
|
80
77
|
const { prompt: systemPrompt, excluded } = this.buildSystemPrompt();
|
|
81
78
|
contextManager.addMessage({ role: "system", content: systemPrompt });
|
|
82
79
|
this.systemPromptAdded = true;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
type: "system",
|
|
87
|
-
content: systemPrompt.slice(0, 2000),
|
|
88
|
-
});
|
|
89
|
-
if (excluded.length > 0) {
|
|
90
|
-
sessionManager.appendLog({
|
|
91
|
-
ts: new Date().toISOString(),
|
|
92
|
-
type: "system",
|
|
93
|
-
content: `[Excluded prompt blocks: ${excluded.length}]`,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
80
|
+
slog.logSystem(systemPrompt.slice(0, 2000));
|
|
81
|
+
if (excluded.length > 0) {
|
|
82
|
+
slog.logSystem(`[Excluded prompt blocks: ${excluded.length}]`);
|
|
96
83
|
}
|
|
97
84
|
pluginManager.runOnSessionStart({
|
|
98
85
|
logger,
|
|
@@ -100,25 +87,21 @@ export class Agent {
|
|
|
100
87
|
});
|
|
101
88
|
}
|
|
102
89
|
contextManager.addMessage({ role: "user", content: input });
|
|
103
|
-
|
|
104
|
-
sessionManager.appendMessage({
|
|
105
|
-
role: "user",
|
|
106
|
-
content: input,
|
|
107
|
-
timestamp: new Date().toISOString(),
|
|
108
|
-
});
|
|
109
|
-
sessionManager.appendLog({
|
|
110
|
-
ts: new Date().toISOString(),
|
|
111
|
-
type: "user",
|
|
112
|
-
content: input,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
90
|
+
slog.logUser(input);
|
|
115
91
|
if (config.moe?.enabled) {
|
|
116
|
-
return
|
|
92
|
+
return runWithMoE({
|
|
93
|
+
config,
|
|
94
|
+
llmProvider,
|
|
95
|
+
toolExecutor,
|
|
96
|
+
logger,
|
|
97
|
+
baseDir: this.deps.baseDir,
|
|
98
|
+
}, input, () => this.executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase), { onMeta, onTool, onPhase });
|
|
117
99
|
}
|
|
118
100
|
return this.executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase);
|
|
119
101
|
}
|
|
120
102
|
async executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase) {
|
|
121
103
|
const { config, llmProvider, toolExecutor, pluginManager, contextManager, hallucinationDetector, logger, sessionManager, } = this.deps;
|
|
104
|
+
const slog = new SessionLogger(sessionManager);
|
|
122
105
|
let iteration = 0;
|
|
123
106
|
let lastText = "";
|
|
124
107
|
let hallucinationRetries = 0;
|
|
@@ -136,48 +119,25 @@ export class Agent {
|
|
|
136
119
|
if (contextManager.needsCompaction()) {
|
|
137
120
|
contextManager.compact();
|
|
138
121
|
logger.debug("Context compacted");
|
|
139
|
-
|
|
140
|
-
sessionManager.appendLog({
|
|
141
|
-
ts: new Date().toISOString(),
|
|
142
|
-
type: "compaction",
|
|
143
|
-
content: `regular compaction, iteration ${iteration}`,
|
|
144
|
-
iteration,
|
|
145
|
-
});
|
|
146
|
-
}
|
|
122
|
+
slog.logCompaction(`regular compaction, iteration ${iteration}`, iteration);
|
|
147
123
|
}
|
|
148
124
|
const currentTokens = contextManager.getEstimatedTokens();
|
|
149
125
|
const budget = contextManager.getBudget();
|
|
150
126
|
if (currentTokens > budget.history) {
|
|
151
127
|
contextManager.compact();
|
|
152
128
|
logger.warn(`Context overflow (${currentTokens} > ${budget.history}), forced compaction`);
|
|
153
|
-
|
|
154
|
-
sessionManager.appendLog({
|
|
155
|
-
ts: new Date().toISOString(),
|
|
156
|
-
type: "compaction",
|
|
157
|
-
content: `forced compaction (${currentTokens} > ${budget.history}), iteration ${iteration}`,
|
|
158
|
-
iteration,
|
|
159
|
-
contextTokens: currentTokens,
|
|
160
|
-
contextLimit: budget.history,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
129
|
+
slog.logCompaction(`forced compaction (${currentTokens} > ${budget.history}), iteration ${iteration}`, iteration, currentTokens, budget.history);
|
|
163
130
|
}
|
|
164
131
|
this.refreshSystemPrompt();
|
|
165
132
|
const history = contextManager.getActiveHistory();
|
|
166
133
|
const allTools = toolExecutor.getToolDefinitions(this.deps.toolTags);
|
|
167
|
-
|
|
168
|
-
sessionManager.appendLog({
|
|
169
|
-
ts: new Date().toISOString(),
|
|
170
|
-
type: "tool_defs",
|
|
171
|
-
toolCount: allTools.length,
|
|
172
|
-
toolNames: allTools.map((t) => t.name),
|
|
173
|
-
iteration,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
134
|
+
slog.logToolDefs(allTools.length, allTools.map((t) => t.name), iteration);
|
|
176
135
|
let textContent = "";
|
|
177
136
|
let reasoningContent = "";
|
|
178
137
|
const toolCalls = [];
|
|
179
138
|
let sawToolCall = false;
|
|
180
139
|
let emittedReasoning = false;
|
|
140
|
+
const textChunks = [];
|
|
181
141
|
this.emitPhase(iteration, "thinking", onPhase);
|
|
182
142
|
try {
|
|
183
143
|
for await (const chunk of llmProvider.chat(history, allTools)) {
|
|
@@ -186,8 +146,7 @@ export class Agent {
|
|
|
186
146
|
onMeta?.("\n\n");
|
|
187
147
|
}
|
|
188
148
|
textContent += chunk.content;
|
|
189
|
-
|
|
190
|
-
onChunk?.(textOut);
|
|
149
|
+
textChunks.push(chunk.content);
|
|
191
150
|
}
|
|
192
151
|
if (chunk.type === "reasoning" && chunk.content) {
|
|
193
152
|
reasoningContent += chunk.content;
|
|
@@ -218,13 +177,7 @@ export class Agent {
|
|
|
218
177
|
}
|
|
219
178
|
catch (err) {
|
|
220
179
|
logger.error(`LLM call failed: ${err.message}`);
|
|
221
|
-
|
|
222
|
-
sessionManager.appendLog({
|
|
223
|
-
ts: new Date().toISOString(),
|
|
224
|
-
type: "error",
|
|
225
|
-
content: err.message,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
180
|
+
slog.logError(err.message);
|
|
228
181
|
pluginManager.runOnError({ iteration, logger }, err);
|
|
229
182
|
return {
|
|
230
183
|
success: false,
|
|
@@ -236,6 +189,15 @@ export class Agent {
|
|
|
236
189
|
finally {
|
|
237
190
|
this.emitPhase(iteration, "done", onPhase);
|
|
238
191
|
}
|
|
192
|
+
// Display buffered text only if no tool call in this response.
|
|
193
|
+
// When a tool call is present, text is just the model describing
|
|
194
|
+
// its tool call (e.g. raw JSON args) — suppress it.
|
|
195
|
+
if (!sawToolCall && textChunks.length > 0) {
|
|
196
|
+
for (const chunk of textChunks) {
|
|
197
|
+
const textOut = pluginManager.runOnText({ iteration, logger }, chunk);
|
|
198
|
+
onChunk?.(textOut);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
239
201
|
let llmResponse = null;
|
|
240
202
|
if (sawToolCall) {
|
|
241
203
|
llmResponse = { type: "tool_call", calls: toolCalls };
|
|
@@ -257,28 +219,8 @@ export class Agent {
|
|
|
257
219
|
}
|
|
258
220
|
lastToolSignature = signature;
|
|
259
221
|
}
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
sessionManager.appendLog({
|
|
263
|
-
ts: new Date().toISOString(),
|
|
264
|
-
type: "reasoning",
|
|
265
|
-
content: reasoningContent,
|
|
266
|
-
iteration,
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
if (sawToolCall) {
|
|
270
|
-
sessionManager.appendLog({
|
|
271
|
-
ts: new Date().toISOString(),
|
|
272
|
-
type: "assistant",
|
|
273
|
-
content: textContent || "",
|
|
274
|
-
tool_calls: toolCalls.map((tc) => ({
|
|
275
|
-
id: tc.id,
|
|
276
|
-
name: tc.name,
|
|
277
|
-
arguments: tc.arguments,
|
|
278
|
-
})),
|
|
279
|
-
iteration,
|
|
280
|
-
});
|
|
281
|
-
}
|
|
222
|
+
if (sawToolCall) {
|
|
223
|
+
slog.logAssistant(textContent || "", reasoningContent, toolCalls, iteration);
|
|
282
224
|
}
|
|
283
225
|
if (sawToolCall) {
|
|
284
226
|
contextManager.addMessage({
|
|
@@ -303,16 +245,7 @@ export class Agent {
|
|
|
303
245
|
});
|
|
304
246
|
pluginManager.runOnToolStart({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments });
|
|
305
247
|
onTool?.({ type: "start", tool: call.name, args: call.arguments });
|
|
306
|
-
|
|
307
|
-
sessionManager.appendLog({
|
|
308
|
-
ts: new Date().toISOString(),
|
|
309
|
-
type: "tool_call",
|
|
310
|
-
tool: call.name,
|
|
311
|
-
tool_call_id: call.id,
|
|
312
|
-
args: call.arguments,
|
|
313
|
-
iteration,
|
|
314
|
-
});
|
|
315
|
-
}
|
|
248
|
+
slog.logToolCall(call, iteration);
|
|
316
249
|
const result = await toolExecutor.execute(call);
|
|
317
250
|
const duration = Date.now() - startTime;
|
|
318
251
|
pluginManager.runOnToolEnd({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
|
|
@@ -341,24 +274,8 @@ export class Agent {
|
|
|
341
274
|
tool_call_id: call.id,
|
|
342
275
|
});
|
|
343
276
|
summaries.push(`[Tool: ${call.name} (${JSON.stringify(call.arguments)}) → ${truncatedOutput.slice(0, 200)}]`);
|
|
344
|
-
if (
|
|
345
|
-
|
|
346
|
-
role: "tool",
|
|
347
|
-
content: truncatedOutput.slice(0, 500),
|
|
348
|
-
name: call.name,
|
|
349
|
-
timestamp: new Date().toISOString(),
|
|
350
|
-
});
|
|
351
|
-
sessionManager.appendLog({
|
|
352
|
-
ts: new Date().toISOString(),
|
|
353
|
-
type: "tool_result",
|
|
354
|
-
tool: call.name,
|
|
355
|
-
tool_call_id: call.id,
|
|
356
|
-
success: result.success,
|
|
357
|
-
content: result.output.slice(0, 1000),
|
|
358
|
-
diff: result.diff,
|
|
359
|
-
duration,
|
|
360
|
-
iteration,
|
|
361
|
-
});
|
|
277
|
+
if (config.session.autoSave) {
|
|
278
|
+
slog.logToolResult(call, result, duration, iteration);
|
|
362
279
|
}
|
|
363
280
|
if (contextManager.needsCompaction()) {
|
|
364
281
|
contextManager.compact();
|
|
@@ -423,39 +340,8 @@ export class Agent {
|
|
|
423
340
|
}
|
|
424
341
|
if (textContent) {
|
|
425
342
|
contextManager.addMessage({ role: "assistant", content: textContent });
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
role: "assistant",
|
|
429
|
-
content: textContent.slice(0, 500),
|
|
430
|
-
timestamp: new Date().toISOString(),
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
if (sessionManager) {
|
|
435
|
-
if (reasoningContent) {
|
|
436
|
-
sessionManager.appendLog({
|
|
437
|
-
ts: new Date().toISOString(),
|
|
438
|
-
type: "reasoning",
|
|
439
|
-
content: reasoningContent,
|
|
440
|
-
iteration,
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
if (textContent) {
|
|
444
|
-
sessionManager.appendLog({
|
|
445
|
-
ts: new Date().toISOString(),
|
|
446
|
-
type: "assistant",
|
|
447
|
-
content: textContent,
|
|
448
|
-
iteration,
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
else if (reasoningContent) {
|
|
452
|
-
sessionManager.appendLog({
|
|
453
|
-
ts: new Date().toISOString(),
|
|
454
|
-
type: "assistant",
|
|
455
|
-
content: reasoningContent,
|
|
456
|
-
iteration,
|
|
457
|
-
});
|
|
458
|
-
}
|
|
343
|
+
slog.saveAssistantMessage(textContent);
|
|
344
|
+
slog.logAssistant(textContent, reasoningContent, undefined, iteration);
|
|
459
345
|
}
|
|
460
346
|
lastText = textContent;
|
|
461
347
|
if (!sawToolCall) {
|
|
@@ -471,14 +357,7 @@ export class Agent {
|
|
|
471
357
|
steps,
|
|
472
358
|
})}</system-summary>`,
|
|
473
359
|
});
|
|
474
|
-
|
|
475
|
-
sessionManager.appendLog({
|
|
476
|
-
ts: new Date().toISOString(),
|
|
477
|
-
type: "audit",
|
|
478
|
-
content: audit.summary,
|
|
479
|
-
iteration,
|
|
480
|
-
});
|
|
481
|
-
}
|
|
360
|
+
slog.logAudit(audit.summary, iteration);
|
|
482
361
|
if (iteration >= config.maxToolIterations - 1) {
|
|
483
362
|
break;
|
|
484
363
|
}
|
|
@@ -508,94 +387,6 @@ export class Agent {
|
|
|
508
387
|
contextLimit: budget.history,
|
|
509
388
|
};
|
|
510
389
|
}
|
|
511
|
-
async runWithMoE(input, _onChunk, onMeta, onTool, onPhase) {
|
|
512
|
-
const { config, llmProvider, logger } = this.deps;
|
|
513
|
-
const orchestrator = new OrchestratorClient({
|
|
514
|
-
model: config.orchestrator.model,
|
|
515
|
-
provider: config.orchestrator.provider,
|
|
516
|
-
}, llmProvider);
|
|
517
|
-
if (!orchestrator.isEnabled()) {
|
|
518
|
-
logger.debug("MoE enabled but no orchestrator model configured — falling back to single-agent");
|
|
519
|
-
return this.executeSingleAgentLoop(input, _onChunk, onMeta, onTool, onPhase);
|
|
520
|
-
}
|
|
521
|
-
onMeta?.("🤖 Planning with MoE mode...\n");
|
|
522
|
-
this.emitPhase(0, "thinking", onPhase);
|
|
523
|
-
const planResult = await orchestrator.plan(input);
|
|
524
|
-
this.emitPhase(0, "done", onPhase);
|
|
525
|
-
if ("error" in planResult) {
|
|
526
|
-
logger.warn(`MoE plan failed: ${planResult.error} — falling back to single-agent`);
|
|
527
|
-
return this.executeSingleAgentLoop(input, _onChunk, onMeta, onTool);
|
|
528
|
-
}
|
|
529
|
-
const { plan } = planResult;
|
|
530
|
-
onMeta?.(`📋 Plan created: "${plan.title}" (${plan.subtasks.length} subtasks)\n`);
|
|
531
|
-
const validation = validatePlan(plan, config);
|
|
532
|
-
if (!validation.valid) {
|
|
533
|
-
const applied = applyAutoFixes(plan, validation.autoFixes);
|
|
534
|
-
const retry = validatePlan(applied, config);
|
|
535
|
-
if (!retry.valid) {
|
|
536
|
-
logger.warn(`MoE plan validation failed: ${retry.errors.join("; ")} — falling back to single-agent`);
|
|
537
|
-
onMeta?.(`⚠️ Plan validation failed. Falling back to single-agent mode.\n`);
|
|
538
|
-
return this.executeSingleAgentLoop(input, _onChunk, onMeta, onTool);
|
|
539
|
-
}
|
|
540
|
-
Object.assign(plan, applied);
|
|
541
|
-
onMeta?.(`🔧 Auto-fixed ${validation.autoFixes.length} plan issues.\n`);
|
|
542
|
-
}
|
|
543
|
-
const moeDeps = {
|
|
544
|
-
config,
|
|
545
|
-
toolRegistry: this.deps.toolExecutor.registry,
|
|
546
|
-
toolExecutor: this.deps.toolExecutor,
|
|
547
|
-
llmProvider,
|
|
548
|
-
logger,
|
|
549
|
-
baseDir: this.deps.baseDir,
|
|
550
|
-
};
|
|
551
|
-
const executor = new MoEExecutor(moeDeps);
|
|
552
|
-
onMeta?.(`⚙️ Executing ${plan.subtasks.length} subtasks...\n`);
|
|
553
|
-
const planResults = await executor.executePlan(plan);
|
|
554
|
-
onMeta?.(`✅ Execution complete: ${planResults.results.filter((r) => r.success).length}/${planResults.results.length} succeeded\n`);
|
|
555
|
-
const verifier = new StepVerifier(this.deps.baseDir);
|
|
556
|
-
const knownTags = [
|
|
557
|
-
"file",
|
|
558
|
-
"code",
|
|
559
|
-
"shell",
|
|
560
|
-
"research",
|
|
561
|
-
"browser",
|
|
562
|
-
"vision",
|
|
563
|
-
"core",
|
|
564
|
-
];
|
|
565
|
-
const verification = await verifier.verifyMoEManifest(plan, config, knownTags);
|
|
566
|
-
this.emitPhase(0, "thinking", onPhase);
|
|
567
|
-
const verifyResult = await orchestrator.verifyAndMerge({
|
|
568
|
-
plan,
|
|
569
|
-
results: planResults.results.map((r) => ({
|
|
570
|
-
subtaskId: r.subtaskId,
|
|
571
|
-
success: r.success,
|
|
572
|
-
summary: r.summary,
|
|
573
|
-
result: r.result,
|
|
574
|
-
error: r.error,
|
|
575
|
-
})),
|
|
576
|
-
verifierErrors: verification.errors,
|
|
577
|
-
verifierWarnings: verification.warnings,
|
|
578
|
-
});
|
|
579
|
-
this.emitPhase(0, "done", onPhase);
|
|
580
|
-
const outputLines = [`## MoE Execution Results\n`];
|
|
581
|
-
for (const r of planResults.results) {
|
|
582
|
-
const icon = r.success ? "✅" : "❌";
|
|
583
|
-
outputLines.push(`${icon} **${r.subtaskId}**: ${r.summary} (${r.durationMs}ms)`);
|
|
584
|
-
}
|
|
585
|
-
outputLines.push("");
|
|
586
|
-
if (verifyResult.type === "final") {
|
|
587
|
-
outputLines.push(`**Result:** ${verifyResult.finalAnswer || "Complete"}`);
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
outputLines.push(`**Re-plan requested:** ${verifyResult.explanation || ""}`);
|
|
591
|
-
}
|
|
592
|
-
const failedCount = planResults.results.filter((r) => !r.success).length;
|
|
593
|
-
return {
|
|
594
|
-
success: failedCount === 0 && verification.success,
|
|
595
|
-
text: outputLines.join("\n"),
|
|
596
|
-
iterationCount: planResults.results.length,
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
390
|
clearContext() {
|
|
600
391
|
this.deps.contextManager.clear();
|
|
601
392
|
this.systemPromptAdded = false;
|
package/dist/core/bootstrap.js
CHANGED
|
@@ -22,7 +22,7 @@ import { MCPModule } from "../modules/mcp/index";
|
|
|
22
22
|
import { setLocale } from "../i18n/index";
|
|
23
23
|
import { Agent } from "./agent";
|
|
24
24
|
import { homedir } from "os";
|
|
25
|
-
import { join } from "path";
|
|
25
|
+
import { join, resolve } from "path";
|
|
26
26
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
27
27
|
function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
28
28
|
const now = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
@@ -45,7 +45,7 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
45
45
|
`- Do not duplicate code, logic, or configuration (DRY — Don't Repeat Yourself). Extract shared logic into a single place, reuse existing utilities and patterns before writing new ones.`,
|
|
46
46
|
];
|
|
47
47
|
if (isWin) {
|
|
48
|
-
lines.push(`
|
|
48
|
+
lines.push(``, `Windows environment — use Windows-compatible commands:`, `- Use "dir" instead of "ls". Use "dir /b" for bare listing.`, `- Use "type" or "Get-Content" instead of "cat".`, `- Use "cd" instead of "pwd". Use "echo %cd%" to print working directory.`, `- Use "copy" instead of "cp", "move" instead of "mv", "del" instead of "rm".`, `- Do not use "mkdir -p" — Windows mkdir creates intermediate dirs by default. Use the create_dir tool instead.`, `- Use forward slashes (/) or escaped backslashes (\\\\) in file paths.`, `- Your working directory is: ${baseDir}`);
|
|
49
49
|
}
|
|
50
50
|
if (config.autoPlan) {
|
|
51
51
|
lines.push(``, `Plan rule: For any task with 2+ steps, create a plan first using the "plan" tool. After each step, call "plan update" to mark progress. Stay focused on the current step.`);
|
|
@@ -110,7 +110,7 @@ export async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplet
|
|
|
110
110
|
retry: config.retry,
|
|
111
111
|
rateLimits: config.security?.rateLimits,
|
|
112
112
|
});
|
|
113
|
-
const baseDir = projectDir
|
|
113
|
+
const baseDir = projectDir ? resolve(projectDir) : process.cwd();
|
|
114
114
|
const projectMapCacheDir = join(baseDir, ".mma");
|
|
115
115
|
const indexerModule = new IndexerModule({
|
|
116
116
|
baseDir,
|
|
@@ -191,6 +191,7 @@ export async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplet
|
|
|
191
191
|
toolCtx.trackReadPath = (p) => factualCheck.trackReadPath(p);
|
|
192
192
|
toolCtx.trackCreatedPath = (p) => factualCheck.trackCreatedPath(p);
|
|
193
193
|
toolCtx.trackDeletedPath = (p) => factualCheck.trackDeletedPath(p);
|
|
194
|
+
toolCtx.trackDocumentContent = (c) => factualCheck.trackDocumentContent(c);
|
|
194
195
|
const moduleRegistry = new ModuleRegistry();
|
|
195
196
|
const execModule = new ExecutionModule(baseDir, config.stuckThreshold);
|
|
196
197
|
moduleRegistry.register(execModule);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around SessionManager that eliminates repetitive
|
|
3
|
+
* `if (sessionManager)` + `new Date().toISOString()` boilerplate
|
|
4
|
+
* from the agent loop.
|
|
5
|
+
*/
|
|
6
|
+
export class SessionLogger {
|
|
7
|
+
session;
|
|
8
|
+
constructor(session) {
|
|
9
|
+
this.session = session;
|
|
10
|
+
}
|
|
11
|
+
get active() {
|
|
12
|
+
return !!this.session?.getActive();
|
|
13
|
+
}
|
|
14
|
+
logSystem(content) {
|
|
15
|
+
this.session?.appendLog({
|
|
16
|
+
ts: new Date().toISOString(),
|
|
17
|
+
type: "system",
|
|
18
|
+
content,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
logUser(content) {
|
|
22
|
+
this.session?.appendMessage({
|
|
23
|
+
role: "user",
|
|
24
|
+
content,
|
|
25
|
+
timestamp: new Date().toISOString(),
|
|
26
|
+
});
|
|
27
|
+
this.session?.appendLog({
|
|
28
|
+
ts: new Date().toISOString(),
|
|
29
|
+
type: "user",
|
|
30
|
+
content,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
logAssistant(content, reasoning, toolCalls, iteration) {
|
|
34
|
+
if (reasoning) {
|
|
35
|
+
this.session?.appendLog({
|
|
36
|
+
ts: new Date().toISOString(),
|
|
37
|
+
type: "reasoning",
|
|
38
|
+
content: reasoning,
|
|
39
|
+
iteration,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
this.session?.appendLog({
|
|
43
|
+
ts: new Date().toISOString(),
|
|
44
|
+
type: "assistant",
|
|
45
|
+
content,
|
|
46
|
+
...(toolCalls
|
|
47
|
+
? { tool_calls: toolCalls.map((tc) => ({ id: tc.id, name: tc.name, arguments: tc.arguments })) }
|
|
48
|
+
: {}),
|
|
49
|
+
iteration,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
saveAssistantMessage(content) {
|
|
53
|
+
this.session?.appendMessage({
|
|
54
|
+
role: "assistant",
|
|
55
|
+
content: content.slice(0, 500),
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
logToolDefs(toolCount, toolNames, iteration) {
|
|
60
|
+
this.session?.appendLog({
|
|
61
|
+
ts: new Date().toISOString(),
|
|
62
|
+
type: "tool_defs",
|
|
63
|
+
toolCount,
|
|
64
|
+
toolNames,
|
|
65
|
+
iteration,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
logToolCall(call, iteration) {
|
|
69
|
+
this.session?.appendLog({
|
|
70
|
+
ts: new Date().toISOString(),
|
|
71
|
+
type: "tool_call",
|
|
72
|
+
tool: call.name,
|
|
73
|
+
tool_call_id: call.id,
|
|
74
|
+
args: call.arguments,
|
|
75
|
+
iteration,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
logToolResult(call, result, duration, iteration) {
|
|
79
|
+
this.session?.appendMessage({
|
|
80
|
+
role: "tool",
|
|
81
|
+
content: result.output.slice(0, 500),
|
|
82
|
+
name: call.name,
|
|
83
|
+
timestamp: new Date().toISOString(),
|
|
84
|
+
});
|
|
85
|
+
this.session?.appendLog({
|
|
86
|
+
ts: new Date().toISOString(),
|
|
87
|
+
type: "tool_result",
|
|
88
|
+
tool: call.name,
|
|
89
|
+
tool_call_id: call.id,
|
|
90
|
+
success: result.success,
|
|
91
|
+
content: result.output.slice(0, 1000),
|
|
92
|
+
diff: result.diff,
|
|
93
|
+
duration,
|
|
94
|
+
iteration,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
logCompaction(content, iteration, contextTokens, contextLimit) {
|
|
98
|
+
this.session?.appendLog({
|
|
99
|
+
ts: new Date().toISOString(),
|
|
100
|
+
type: "compaction",
|
|
101
|
+
content,
|
|
102
|
+
iteration,
|
|
103
|
+
...(contextTokens !== undefined ? { contextTokens } : {}),
|
|
104
|
+
...(contextLimit !== undefined ? { contextLimit } : {}),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
logError(message) {
|
|
108
|
+
this.session?.appendLog({
|
|
109
|
+
ts: new Date().toISOString(),
|
|
110
|
+
type: "error",
|
|
111
|
+
content: message,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
logAudit(summary, iteration) {
|
|
115
|
+
this.session?.appendLog({
|
|
116
|
+
ts: new Date().toISOString(),
|
|
117
|
+
type: "audit",
|
|
118
|
+
content: summary,
|
|
119
|
+
iteration,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
package/dist/i18n/en.json
CHANGED
|
@@ -411,6 +411,7 @@
|
|
|
411
411
|
"migration.dir_bak": "- .mma/ \u2192 .mma.bak/ ({count} files)",
|
|
412
412
|
"migration.migrated": "Migrated:",
|
|
413
413
|
"migration.not_needed": "No migration needed",
|
|
414
|
+
"migration.security_updated": "[MMA] Security config force-updated from v{from} to v{to} (dangerous operators reset to defaults)",
|
|
414
415
|
"ui.error_prefix": "Error: ",
|
|
415
416
|
"ui.success_prefix": "\u2713 ",
|
|
416
417
|
"ui.warning_prefix": "\u26a0 ",
|
package/dist/i18n/ru.json
CHANGED
|
@@ -411,6 +411,7 @@
|
|
|
411
411
|
"migration.dir_bak": "- .mma/ → .mma.bak/ ({count} файлов)",
|
|
412
412
|
"migration.migrated": "Мигрировано:",
|
|
413
413
|
"migration.not_needed": "Миграция не требуется",
|
|
414
|
+
"migration.security_updated": "[MMA] Конфигурация безопасности принудительно обновлена с v{from} до v{to} (опасные операторы сброшены)",
|
|
414
415
|
"ui.error_prefix": "Ошибка: ",
|
|
415
416
|
"ui.success_prefix": "✓ ",
|
|
416
417
|
"ui.warning_prefix": "⚠ ",
|
|
@@ -201,13 +201,15 @@ export class OpenAICompatProvider {
|
|
|
201
201
|
});
|
|
202
202
|
if (!response.ok) {
|
|
203
203
|
const errorText = await response.text();
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
throw new Error(t("error.llm_api", {
|
|
205
|
+
status: response.status,
|
|
206
|
+
statusText: response.statusText,
|
|
207
|
+
errorText: errorText.slice(0, 500),
|
|
208
|
+
}));
|
|
206
209
|
}
|
|
207
210
|
const data = await response.json();
|
|
208
211
|
const choice = data.choices?.[0];
|
|
209
212
|
if (!choice) {
|
|
210
|
-
console.error("[doNonStreaming] No choices in response");
|
|
211
213
|
return [];
|
|
212
214
|
}
|
|
213
215
|
const msg = choice.message || {};
|
|
@@ -230,15 +232,10 @@ export class OpenAICompatProvider {
|
|
|
230
232
|
});
|
|
231
233
|
}
|
|
232
234
|
}
|
|
233
|
-
console.error("[doNonStreaming] chunks:", chunks.length, "tool_calls:", msg.tool_calls?.length, "content len:", msg.content?.length);
|
|
234
|
-
if (chunks.length === 0) {
|
|
235
|
-
console.error("[doNonStreaming] Empty response: no content, no tool_calls, no reasoning");
|
|
236
|
-
}
|
|
237
235
|
return chunks;
|
|
238
236
|
}
|
|
239
237
|
catch (err) {
|
|
240
|
-
|
|
241
|
-
return [];
|
|
238
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
242
239
|
}
|
|
243
240
|
}
|
|
244
241
|
countTokens(text) {
|
|
@@ -163,6 +163,19 @@ export class MoEExecutor {
|
|
|
163
163
|
if (waves.length === 0) {
|
|
164
164
|
return { success: false, results: [], errors: ['Failed to topologically sort subtasks (possible cycle)'], warnings: [] };
|
|
165
165
|
}
|
|
166
|
+
// Verify all subtasks are included — catch silent drops from unresolved dependencies
|
|
167
|
+
const sortedCount = waves.flat().length;
|
|
168
|
+
if (sortedCount < plan.subtasks.length) {
|
|
169
|
+
const missing = plan.subtasks
|
|
170
|
+
.filter(s => !waves.flat().some(w => w.id === s.id))
|
|
171
|
+
.map(s => s.id);
|
|
172
|
+
return {
|
|
173
|
+
success: false,
|
|
174
|
+
results: [],
|
|
175
|
+
errors: [`Missing subtasks after topological sort: ${missing.join(', ')} (dangling or invalid depends_on)`],
|
|
176
|
+
warnings: [],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
166
179
|
for (let waveIdx = 0; waveIdx < waves.length; waveIdx++) {
|
|
167
180
|
const wave = waves[waveIdx];
|
|
168
181
|
const wavePromises = wave.map(subtask => executeSubtask(subtask, this.deps, plan.shared_context)
|