micro-models-agent 0.13.1 → 0.13.2
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 +30 -248
- 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/cli/commands.js
CHANGED
|
@@ -6,11 +6,12 @@ import { t } from "../i18n/index";
|
|
|
6
6
|
import { join } from "path";
|
|
7
7
|
import { homedir } from "os";
|
|
8
8
|
import { createSecurityCommand } from "./security-commands";
|
|
9
|
+
import { version } from "../../package.json";
|
|
9
10
|
export function createProgram() {
|
|
10
11
|
const program = new Command()
|
|
11
12
|
.name("mma")
|
|
12
13
|
.description(t("cli.description"))
|
|
13
|
-
.version(
|
|
14
|
+
.version(version)
|
|
14
15
|
.option("--no-agents-md", t("cli.no_agents_md"))
|
|
15
16
|
.option("-d, --dir <path>", t("cli.dir"))
|
|
16
17
|
.option("-e, --exit-on-complete", t("cli.exit_on_complete"))
|
package/dist/cli/repl.js
CHANGED
|
@@ -43,6 +43,7 @@ export class Repl {
|
|
|
43
43
|
commands = new Map();
|
|
44
44
|
completer = new Completer();
|
|
45
45
|
running = false;
|
|
46
|
+
agentRunning = false;
|
|
46
47
|
agent;
|
|
47
48
|
config;
|
|
48
49
|
sessionManager;
|
|
@@ -609,6 +610,15 @@ export class Repl {
|
|
|
609
610
|
let inMultiLine = false;
|
|
610
611
|
this.rl.on("line", async (line) => {
|
|
611
612
|
const trimmed = line.trim();
|
|
613
|
+
if (this.agentRunning) {
|
|
614
|
+
if (trimmed) {
|
|
615
|
+
this.history.push(trimmed);
|
|
616
|
+
if (this.history.length > this.maxHistory) {
|
|
617
|
+
this.history = this.history.slice(-this.maxHistory);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
612
622
|
if (trimmed) {
|
|
613
623
|
this.history.push(trimmed);
|
|
614
624
|
if (this.history.length > this.maxHistory) {
|
|
@@ -692,29 +702,41 @@ export class Repl {
|
|
|
692
702
|
return false;
|
|
693
703
|
}
|
|
694
704
|
async runAgent(input) {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
705
|
+
if (this.agentRunning)
|
|
706
|
+
return;
|
|
707
|
+
this.agentRunning = true;
|
|
708
|
+
this.rl.pause();
|
|
709
|
+
try {
|
|
710
|
+
process.stdout.write("\n" + pc.green(t("repl.agent")));
|
|
711
|
+
const renderer = new Renderer({ spinner: this.config.ui?.spinner ?? true });
|
|
712
|
+
const result = await this.agent.run(input, (c) => renderer.text(c), (m) => renderer.meta(m), (ev) => {
|
|
713
|
+
if (ev.type === "start") {
|
|
714
|
+
renderer.toolStart(ev.tool, ev.args);
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error);
|
|
718
|
+
}
|
|
719
|
+
}, (phase) => {
|
|
720
|
+
if (phase === "thinking") {
|
|
721
|
+
renderer.thinkingStart();
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
renderer.thinkingEnd();
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
renderer.flush();
|
|
728
|
+
process.stdout.write("\n");
|
|
729
|
+
if (!result.success) {
|
|
730
|
+
console.error(pc.red(`${t("error.prefix")}${result.error}`));
|
|
707
731
|
}
|
|
708
|
-
|
|
709
|
-
|
|
732
|
+
this.showContextBar(result);
|
|
733
|
+
}
|
|
734
|
+
finally {
|
|
735
|
+
this.agentRunning = false;
|
|
736
|
+
if (this.running) {
|
|
737
|
+
this.rl.resume();
|
|
710
738
|
}
|
|
711
|
-
});
|
|
712
|
-
renderer.flush();
|
|
713
|
-
process.stdout.write("\n");
|
|
714
|
-
if (!result.success) {
|
|
715
|
-
console.error(pc.red(`${t("error.prefix")}${result.error}`));
|
|
716
739
|
}
|
|
717
|
-
this.showContextBar(result);
|
|
718
740
|
}
|
|
719
741
|
registerCommand(cmd) {
|
|
720
742
|
this.commands.set(cmd.name, cmd);
|
package/dist/config/config.js
CHANGED
|
@@ -7,6 +7,13 @@ import { MigrationDetector } from '../migration/detect';
|
|
|
7
7
|
import { BackupManager } from '../migration/backup';
|
|
8
8
|
import { validateExpertConfig } from './experts';
|
|
9
9
|
import { ConfigEncryptor } from '../modules/security/encryption';
|
|
10
|
+
/**
|
|
11
|
+
* When the user's config version is older than this, force-overwrite
|
|
12
|
+
* the security bash settings with the current defaults.
|
|
13
|
+
* This ensures critical security changes (like operator allowlists)
|
|
14
|
+
* are applied even if the user has a saved config.
|
|
15
|
+
*/
|
|
16
|
+
const FORCE_SECURITY_UPDATE_VERSION = '2.1.0';
|
|
10
17
|
/**
|
|
11
18
|
* Restore RegExp instances in dangerousPatterns that were serialized as {}
|
|
12
19
|
* (pre-0.8.0 configs) or as {__regex, source, flags} (new format).
|
|
@@ -115,6 +122,39 @@ function applyEnvVars(config) {
|
|
|
115
122
|
result.moe = { ...result.moe, enabled: env.MMA_MOE_ENABLED === 'true' };
|
|
116
123
|
return result;
|
|
117
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Force-update security settings when upgrading from an older version.
|
|
127
|
+
* This overrides user config for critical security fields that must
|
|
128
|
+
* match the current code defaults (e.g. dangerousOperators).
|
|
129
|
+
*/
|
|
130
|
+
function forceSecurityUpdate(config) {
|
|
131
|
+
const userVersion = config.version || '0.0.0';
|
|
132
|
+
if (userVersion >= FORCE_SECURITY_UPDATE_VERSION)
|
|
133
|
+
return config;
|
|
134
|
+
// Force-overwrite bash security with current defaults
|
|
135
|
+
const security = config.security || {};
|
|
136
|
+
const userBash = security.bash || {};
|
|
137
|
+
security.bash = {
|
|
138
|
+
...DEFAULT_SECURITY_CONFIG.bash,
|
|
139
|
+
// Keep user's custom blacklist additions, but ensure defaults are present
|
|
140
|
+
blacklist: [...new Set([
|
|
141
|
+
...DEFAULT_SECURITY_CONFIG.bash.blacklist,
|
|
142
|
+
...(userBash.blacklist || []),
|
|
143
|
+
])],
|
|
144
|
+
whitelist: userBash.whitelist || DEFAULT_SECURITY_CONFIG.bash.whitelist,
|
|
145
|
+
dangerousFlags: [...new Set([
|
|
146
|
+
...DEFAULT_SECURITY_CONFIG.bash.dangerousFlags,
|
|
147
|
+
...(userBash.dangerousFlags || []),
|
|
148
|
+
])],
|
|
149
|
+
// dangerousOperators: ALWAYS use defaults — user overrides are dangerous
|
|
150
|
+
dangerousOperators: [...DEFAULT_SECURITY_CONFIG.bash.dangerousOperators],
|
|
151
|
+
blockDangerousFlags: DEFAULT_SECURITY_CONFIG.bash.blockDangerousFlags,
|
|
152
|
+
logCommands: DEFAULT_SECURITY_CONFIG.bash.logCommands,
|
|
153
|
+
};
|
|
154
|
+
config.security = security;
|
|
155
|
+
console.log(t('migration.security_updated', { from: userVersion, to: FORCE_SECURITY_UPDATE_VERSION }));
|
|
156
|
+
return config;
|
|
157
|
+
}
|
|
118
158
|
export function loadConfig(options) {
|
|
119
159
|
const globalPath = join(options.configDir, 'config.json');
|
|
120
160
|
mkdirSync(options.configDir, { recursive: true });
|
|
@@ -146,6 +186,8 @@ export function loadConfig(options) {
|
|
|
146
186
|
if (config.security?.contentScan?.dangerousPatterns) {
|
|
147
187
|
config.security.contentScan.dangerousPatterns = restoreDangerousPatterns(config.security.contentScan.dangerousPatterns, DEFAULT_SECURITY_CONFIG.contentScan.dangerousPatterns);
|
|
148
188
|
}
|
|
189
|
+
// Force-update security settings when upgrading from older version
|
|
190
|
+
config = forceSecurityUpdate(config);
|
|
149
191
|
config = applyEnvVars(config);
|
|
150
192
|
// Decrypt sensitive fields in the loaded config
|
|
151
193
|
try {
|
package/dist/config/defaults.js
CHANGED
|
@@ -76,7 +76,7 @@ export const DEFAULTS = {
|
|
|
76
76
|
transport: "http",
|
|
77
77
|
url: "https://mcp.context7.com/mcp",
|
|
78
78
|
headers: {
|
|
79
|
-
CONTEXT7_API_KEY: "
|
|
79
|
+
CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY || "",
|
|
80
80
|
},
|
|
81
81
|
enabled: true,
|
|
82
82
|
timeout: 30000,
|
package/dist/config/security.js
CHANGED
|
@@ -65,7 +65,7 @@ export const DEFAULT_SECURITY_CONFIG = {
|
|
|
65
65
|
"--no-confirm",
|
|
66
66
|
],
|
|
67
67
|
// Dangerous operators that are always blocked
|
|
68
|
-
dangerousOperators: [">", ">>", "2>", "2>>", "
|
|
68
|
+
dangerousOperators: [">", ">>", "2>", "2>>", "&", "`"],
|
|
69
69
|
},
|
|
70
70
|
paths: {
|
|
71
71
|
// Glob patterns for paths that are always denied
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { OrchestratorClient } from "../llm/orchestrator";
|
|
2
|
+
import { validatePlan, applyAutoFixes, } from "../modules/execution/plan-validator";
|
|
3
|
+
import { MoEExecutor } from "../modules/execution/moe-executor";
|
|
4
|
+
import { StepVerifier } from "../modules/execution/verifier";
|
|
5
|
+
/**
|
|
6
|
+
* Execute the MoE (Mixture of Experts) path: plan → validate → execute → verify.
|
|
7
|
+
* Returns a fallback signal when MoE cannot proceed so the caller can fall
|
|
8
|
+
* back to the single-agent loop.
|
|
9
|
+
*/
|
|
10
|
+
export async function runWithMoE(deps, input, fallback, opts = {}) {
|
|
11
|
+
const { config, llmProvider, logger, toolExecutor, baseDir } = deps;
|
|
12
|
+
const { onMeta, onPhase } = opts;
|
|
13
|
+
const orchestrator = new OrchestratorClient({
|
|
14
|
+
model: config.orchestrator.model,
|
|
15
|
+
provider: config.orchestrator.provider,
|
|
16
|
+
}, llmProvider);
|
|
17
|
+
if (!orchestrator.isEnabled()) {
|
|
18
|
+
logger.debug("MoE enabled but no orchestrator model configured — falling back to single-agent");
|
|
19
|
+
return fallback();
|
|
20
|
+
}
|
|
21
|
+
onMeta?.("🤖 Planning with MoE mode...\n");
|
|
22
|
+
onPhase?.("thinking");
|
|
23
|
+
const planResult = await orchestrator.plan(input);
|
|
24
|
+
onPhase?.("done");
|
|
25
|
+
if ("error" in planResult) {
|
|
26
|
+
logger.warn(`MoE plan failed: ${planResult.error} — falling back to single-agent`);
|
|
27
|
+
return fallback();
|
|
28
|
+
}
|
|
29
|
+
const { plan } = planResult;
|
|
30
|
+
onMeta?.(`📋 Plan created: "${plan.title}" (${plan.subtasks.length} subtasks)\n`);
|
|
31
|
+
const validation = validatePlan(plan, config);
|
|
32
|
+
if (!validation.valid) {
|
|
33
|
+
const applied = applyAutoFixes(plan, validation.autoFixes);
|
|
34
|
+
const retry = validatePlan(applied, config);
|
|
35
|
+
if (!retry.valid) {
|
|
36
|
+
logger.warn(`MoE plan validation failed: ${retry.errors.join("; ")} — falling back to single-agent`);
|
|
37
|
+
onMeta?.(`⚠️ Plan validation failed. Falling back to single-agent mode.\n`);
|
|
38
|
+
return fallback();
|
|
39
|
+
}
|
|
40
|
+
Object.assign(plan, applied);
|
|
41
|
+
onMeta?.(`🔧 Auto-fixed ${validation.autoFixes.length} plan issues.\n`);
|
|
42
|
+
}
|
|
43
|
+
const moeDeps = {
|
|
44
|
+
config,
|
|
45
|
+
toolRegistry: toolExecutor.getRegistry(),
|
|
46
|
+
toolExecutor,
|
|
47
|
+
llmProvider,
|
|
48
|
+
logger,
|
|
49
|
+
baseDir,
|
|
50
|
+
};
|
|
51
|
+
const executor = new MoEExecutor(moeDeps);
|
|
52
|
+
onMeta?.(`⚙️ Executing ${plan.subtasks.length} subtasks...\n`);
|
|
53
|
+
const planResults = await executor.executePlan(plan);
|
|
54
|
+
onMeta?.(`✅ Execution complete: ${planResults.results.filter((r) => r.success).length}/${planResults.results.length} succeeded\n`);
|
|
55
|
+
const verifier = new StepVerifier(baseDir);
|
|
56
|
+
const knownTags = [
|
|
57
|
+
"file",
|
|
58
|
+
"code",
|
|
59
|
+
"shell",
|
|
60
|
+
"research",
|
|
61
|
+
"browser",
|
|
62
|
+
"vision",
|
|
63
|
+
"core",
|
|
64
|
+
];
|
|
65
|
+
const verification = await verifier.verifyMoEManifest(plan, config, knownTags);
|
|
66
|
+
onPhase?.("thinking");
|
|
67
|
+
const verifyResult = await orchestrator.verifyAndMerge({
|
|
68
|
+
plan,
|
|
69
|
+
results: planResults.results.map((r) => ({
|
|
70
|
+
subtaskId: r.subtaskId,
|
|
71
|
+
success: r.success,
|
|
72
|
+
summary: r.summary,
|
|
73
|
+
result: r.result,
|
|
74
|
+
error: r.error,
|
|
75
|
+
})),
|
|
76
|
+
verifierErrors: verification.errors,
|
|
77
|
+
verifierWarnings: verification.warnings,
|
|
78
|
+
});
|
|
79
|
+
onPhase?.("done");
|
|
80
|
+
const outputLines = [`## MoE Execution Results\n`];
|
|
81
|
+
for (const r of planResults.results) {
|
|
82
|
+
const icon = r.success ? "✅" : "❌";
|
|
83
|
+
outputLines.push(`${icon} **${r.subtaskId}**: ${r.summary} (${r.durationMs}ms)`);
|
|
84
|
+
}
|
|
85
|
+
outputLines.push("");
|
|
86
|
+
if (verifyResult.type === "final") {
|
|
87
|
+
outputLines.push(`**Result:** ${verifyResult.finalAnswer || "Complete"}`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
outputLines.push(`**Re-plan requested:** ${verifyResult.explanation || ""}`);
|
|
91
|
+
}
|
|
92
|
+
const failedCount = planResults.results.filter((r) => !r.success).length;
|
|
93
|
+
return {
|
|
94
|
+
success: failedCount === 0 && verification.success,
|
|
95
|
+
text: outputLines.join("\n"),
|
|
96
|
+
iterationCount: planResults.results.length,
|
|
97
|
+
};
|
|
98
|
+
}
|
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,43 +119,19 @@ 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 = [];
|
|
@@ -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,
|
|
@@ -257,28 +210,8 @@ export class Agent {
|
|
|
257
210
|
}
|
|
258
211
|
lastToolSignature = signature;
|
|
259
212
|
}
|
|
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
|
-
}
|
|
213
|
+
if (sawToolCall) {
|
|
214
|
+
slog.logAssistant(textContent || "", reasoningContent, toolCalls, iteration);
|
|
282
215
|
}
|
|
283
216
|
if (sawToolCall) {
|
|
284
217
|
contextManager.addMessage({
|
|
@@ -303,16 +236,7 @@ export class Agent {
|
|
|
303
236
|
});
|
|
304
237
|
pluginManager.runOnToolStart({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments });
|
|
305
238
|
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
|
-
}
|
|
239
|
+
slog.logToolCall(call, iteration);
|
|
316
240
|
const result = await toolExecutor.execute(call);
|
|
317
241
|
const duration = Date.now() - startTime;
|
|
318
242
|
pluginManager.runOnToolEnd({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
|
|
@@ -341,24 +265,8 @@ export class Agent {
|
|
|
341
265
|
tool_call_id: call.id,
|
|
342
266
|
});
|
|
343
267
|
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
|
-
});
|
|
268
|
+
if (config.session.autoSave) {
|
|
269
|
+
slog.logToolResult(call, result, duration, iteration);
|
|
362
270
|
}
|
|
363
271
|
if (contextManager.needsCompaction()) {
|
|
364
272
|
contextManager.compact();
|
|
@@ -423,39 +331,8 @@ export class Agent {
|
|
|
423
331
|
}
|
|
424
332
|
if (textContent) {
|
|
425
333
|
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
|
-
}
|
|
334
|
+
slog.saveAssistantMessage(textContent);
|
|
335
|
+
slog.logAssistant(textContent, reasoningContent, undefined, iteration);
|
|
459
336
|
}
|
|
460
337
|
lastText = textContent;
|
|
461
338
|
if (!sawToolCall) {
|
|
@@ -471,14 +348,7 @@ export class Agent {
|
|
|
471
348
|
steps,
|
|
472
349
|
})}</system-summary>`,
|
|
473
350
|
});
|
|
474
|
-
|
|
475
|
-
sessionManager.appendLog({
|
|
476
|
-
ts: new Date().toISOString(),
|
|
477
|
-
type: "audit",
|
|
478
|
-
content: audit.summary,
|
|
479
|
-
iteration,
|
|
480
|
-
});
|
|
481
|
-
}
|
|
351
|
+
slog.logAudit(audit.summary, iteration);
|
|
482
352
|
if (iteration >= config.maxToolIterations - 1) {
|
|
483
353
|
break;
|
|
484
354
|
}
|
|
@@ -508,94 +378,6 @@ export class Agent {
|
|
|
508
378
|
contextLimit: budget.history,
|
|
509
379
|
};
|
|
510
380
|
}
|
|
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
381
|
clearContext() {
|
|
600
382
|
this.deps.contextManager.clear();
|
|
601
383
|
this.systemPromptAdded = false;
|