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.
@@ -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("0.4.0")
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
- process.stdout.write("\n" + pc.green(t("repl.agent")));
696
- const renderer = new Renderer({ spinner: this.config.ui?.spinner ?? true });
697
- const result = await this.agent.run(input, (c) => renderer.text(c), (m) => renderer.meta(m), (ev) => {
698
- if (ev.type === "start") {
699
- renderer.toolStart(ev.tool, ev.args);
700
- }
701
- else {
702
- renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error);
703
- }
704
- }, (phase) => {
705
- if (phase === "thinking") {
706
- renderer.thinkingStart();
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
- else {
709
- renderer.thinkingEnd();
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);
@@ -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 {
@@ -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: "ctx7sk-f3dd1f44-bc3a-4f6b-94c0-87cacb6c084b",
79
+ CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY || "",
80
80
  },
81
81
  enabled: true,
82
82
  timeout: 30000,
@@ -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
+ }