micro-models-agent 0.24.11 → 0.24.12

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.
Files changed (2) hide show
  1. package/dist/main.js +98 -33
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -3810,10 +3810,6 @@ var init_data_sanitizer = __esm(() => {
3810
3810
  {
3811
3811
  pattern: /Bearer\s+[a-zA-Z0-9_-]{20,}/gi,
3812
3812
  replacement: "Bearer [REDACTED]"
3813
- },
3814
- {
3815
- pattern: /\b([a-zA-Z0-9_-]{30,})\b/g,
3816
- replacement: "[REDACTED]"
3817
3813
  }
3818
3814
  ];
3819
3815
  });
@@ -5012,6 +5008,14 @@ class ToolResultCache {
5012
5008
  has(key) {
5013
5009
  return this.get(key) !== undefined;
5014
5010
  }
5011
+ cleanExpired() {
5012
+ const now = Date.now();
5013
+ for (const [key, entry] of this.cache) {
5014
+ if (now > entry.expiresAt) {
5015
+ this.cache.delete(key);
5016
+ }
5017
+ }
5018
+ }
5015
5019
  clear() {
5016
5020
  this.cache.clear();
5017
5021
  }
@@ -5132,6 +5136,7 @@ class ToolExecutor {
5132
5136
  this.cache.set(key, result.output);
5133
5137
  this.ctx.logger.debug(`Tool ${call.name}: CACHED`);
5134
5138
  }
5139
+ this.cache.cleanExpired();
5135
5140
  this.ctx.logger.debug(`Tool ${call.name}: ${result.success ? "OK" : "FAIL"}`);
5136
5141
  return result;
5137
5142
  }
@@ -5147,6 +5152,9 @@ class ToolExecutor {
5147
5152
  setScope(scope) {
5148
5153
  this.ctx.scope = scope;
5149
5154
  }
5155
+ setOnMeta(onMeta) {
5156
+ this.ctx.onMeta = onMeta;
5157
+ }
5150
5158
  updateProvider(provider) {
5151
5159
  this.ctx.llmProvider = provider;
5152
5160
  }
@@ -6260,7 +6268,8 @@ var init_edit_file = __esm(() => {
6260
6268
  output: t("file.string_not_found", { str: oldStr })
6261
6269
  };
6262
6270
  }
6263
- const updated = content.replace(oldStr, newStr);
6271
+ const occurrences = content.split(oldStr).length - 1;
6272
+ const updated = occurrences > 1 ? content.replaceAll(oldStr, newStr) : content.replace(oldStr, newStr);
6264
6273
  const scanResult = scanContent(updated, path, ctx.config.security?.contentScan);
6265
6274
  if (!scanResult.allowed) {
6266
6275
  logSecurityBlock(ctx.sessionId, "file_write", scanResult.reason || "Content contains dangerous patterns", path);
@@ -6667,6 +6676,12 @@ var init_file_info = __esm(() => {
6667
6676
  });
6668
6677
 
6669
6678
  // src/modules/security/command-validator.ts
6679
+ function hasShellIndirection(command) {
6680
+ return /\$\{[^}]+\}/.test(command) || /\$\(/.test(command) || /`[^`]+`/.test(command) || /\$\(\(/.test(command) || /\$\{[!]/.test(command);
6681
+ }
6682
+ function hasEvalConstruct(command) {
6683
+ return /\beval\b/.test(command) || /\bsource\b/.test(command);
6684
+ }
6670
6685
  function extractBaseCommand(trimmed) {
6671
6686
  const tokens = trimmed.split(/\s+/);
6672
6687
  let i = 0;
@@ -6722,6 +6737,18 @@ function isCommandAllowed(command, securityConfig) {
6722
6737
  reason: `Shell wrapper "${baseForShellCheck}" is blocked — use the bash tool directly`
6723
6738
  };
6724
6739
  }
6740
+ if (hasShellIndirection(trimmedCommand)) {
6741
+ return {
6742
+ allowed: false,
6743
+ reason: `Shell indirection (variable expansion, command substitution, or backtick execution) is not allowed`
6744
+ };
6745
+ }
6746
+ if (hasEvalConstruct(trimmedCommand)) {
6747
+ return {
6748
+ allowed: false,
6749
+ reason: `eval/source constructs are not allowed`
6750
+ };
6751
+ }
6725
6752
  if (config.blockDangerousFlags) {
6726
6753
  for (const op of config.dangerousOperators || []) {
6727
6754
  if (containsOperator(trimmedCommand, op)) {
@@ -6732,7 +6759,15 @@ function isCommandAllowed(command, securityConfig) {
6732
6759
  }
6733
6760
  }
6734
6761
  }
6735
- const baseCommand = extractBaseCommand(trimmedCommand);
6762
+ const baseCommand = baseForShellCheck;
6763
+ if (!baseCommand || baseCommand === trimmedCommand.trim()) {
6764
+ if (baseCommand === "sudo" || baseCommand === "env" || baseCommand === "nohup" || baseCommand === "exec") {
6765
+ return {
6766
+ allowed: false,
6767
+ reason: `Prefix "${baseCommand}" requires a target command`
6768
+ };
6769
+ }
6770
+ }
6736
6771
  if (config.whitelist.length > 0) {
6737
6772
  if (!config.whitelist.includes(baseCommand)) {
6738
6773
  return {
@@ -8408,7 +8443,12 @@ function validatePlan(plan, config) {
8408
8443
  const warnings = [];
8409
8444
  const autoFixes = [];
8410
8445
  if (!plan.subtasks || plan.subtasks.length === 0) {
8411
- return { valid: false, errors: ["Plan has no subtasks"], warnings: [], autoFixes: [] };
8446
+ return {
8447
+ valid: false,
8448
+ errors: ["Plan has no subtasks"],
8449
+ warnings: [],
8450
+ autoFixes: []
8451
+ };
8412
8452
  }
8413
8453
  errors.push(...expertTagsExist(plan.subtasks, config));
8414
8454
  errors.push(...hasCycle(plan.subtasks));
@@ -8417,7 +8457,10 @@ function validatePlan(plan, config) {
8417
8457
  const readOverlaps = deleteReadOverlap(plan.subtasks);
8418
8458
  for (const overlap of readOverlaps) {
8419
8459
  if (overlap.canAutoFix) {
8420
- autoFixes.push({ subtaskId: overlap.subtaskId, fix: `Add depends_on: ${overlap.dependsOn}` });
8460
+ autoFixes.push({
8461
+ subtaskId: overlap.subtaskId,
8462
+ fix: `Add depends_on: ${overlap.dependsOn}`
8463
+ });
8421
8464
  } else if (overlap.error) {
8422
8465
  errors.push(overlap.error);
8423
8466
  }
@@ -8463,7 +8506,6 @@ function hasCycle(subtasks) {
8463
8506
  }
8464
8507
  const errors = [];
8465
8508
  for (const s of subtasks) {
8466
- visited.clear();
8467
8509
  inStack.clear();
8468
8510
  if (dfs(s.id)) {
8469
8511
  errors.push(`Cycle detected involving subtask "${s.id}"`);
@@ -8721,13 +8763,26 @@ class MoEExecutor {
8721
8763
  constructor(deps) {
8722
8764
  this.deps = deps;
8723
8765
  }
8724
- async executePlan(plan) {
8766
+ async executePlan(plan, signal) {
8725
8767
  const results = [];
8726
8768
  const errors = [];
8727
8769
  const warnings = [];
8770
+ if (signal?.aborted) {
8771
+ return {
8772
+ success: false,
8773
+ results: [],
8774
+ errors: ["Execution aborted before start"],
8775
+ warnings: []
8776
+ };
8777
+ }
8728
8778
  const waves = topologicalSort(plan.subtasks);
8729
8779
  if (waves.length === 0) {
8730
- return { success: false, results: [], errors: ["Failed to topologically sort subtasks (possible cycle)"], warnings: [] };
8780
+ return {
8781
+ success: false,
8782
+ results: [],
8783
+ errors: ["Failed to topologically sort subtasks (possible cycle)"],
8784
+ warnings: []
8785
+ };
8731
8786
  }
8732
8787
  const sortedCount = waves.flat().length;
8733
8788
  if (sortedCount < plan.subtasks.length) {
@@ -8735,11 +8790,17 @@ class MoEExecutor {
8735
8790
  return {
8736
8791
  success: false,
8737
8792
  results: [],
8738
- errors: [`Missing subtasks after topological sort: ${missing.join(", ")} (dangling or invalid depends_on)`],
8793
+ errors: [
8794
+ `Missing subtasks after topological sort: ${missing.join(", ")} (dangling or invalid depends_on)`
8795
+ ],
8739
8796
  warnings: []
8740
8797
  };
8741
8798
  }
8742
8799
  for (let waveIdx = 0;waveIdx < waves.length; waveIdx++) {
8800
+ if (signal?.aborted) {
8801
+ warnings.push(`Execution aborted during wave ${waveIdx + 1}/${waves.length}`);
8802
+ break;
8803
+ }
8743
8804
  const wave = waves[waveIdx];
8744
8805
  const wavePromises = wave.map((subtask) => executeSubtask(subtask, this.deps, plan.shared_context).then((result) => {
8745
8806
  results.push(result);
@@ -8939,7 +9000,12 @@ var init_verifier = __esm(() => {
8939
9000
  // src/core/agent-moe.ts
8940
9001
  async function runWithMoE(deps, input, fallback, opts = {}) {
8941
9002
  const { config, llmProvider, logger, toolExecutor, baseDir } = deps;
8942
- const { onMeta, onPhase } = opts;
9003
+ const { onMeta, onPhase, signal } = opts;
9004
+ const abortCheck = () => {
9005
+ if (signal?.aborted)
9006
+ return true;
9007
+ return false;
9008
+ };
8943
9009
  const orchestrator = new OrchestratorClient({
8944
9010
  model: config.orchestrator.model,
8945
9011
  provider: config.orchestrator.provider
@@ -8985,7 +9051,7 @@ async function runWithMoE(deps, input, fallback, opts = {}) {
8985
9051
  const executor = new MoEExecutor(moeDeps);
8986
9052
  onMeta?.(`⚙️ Executing ${plan.subtasks.length} subtasks...
8987
9053
  `);
8988
- const planResults = await executor.executePlan(plan);
9054
+ const planResults = await executor.executePlan(plan, signal);
8989
9055
  onMeta?.(`✅ Execution complete: ${planResults.results.filter((r) => r.success).length}/${planResults.results.length} succeeded
8990
9056
  `);
8991
9057
  const verifier = new StepVerifier(baseDir);
@@ -9196,10 +9262,6 @@ class Agent {
9196
9262
  const systemBudget = Math.floor(this.deps.config.contextWindow * this.deps.config.contextBudget.systemPrompt);
9197
9263
  const builder = new PromptBuilder(systemBudget);
9198
9264
  builder.addBlocks(this.deps.promptBlocks);
9199
- const dynamic = this.deps.getDynamicPromptBlocks?.() ?? [];
9200
- if (dynamic.length > 0) {
9201
- builder.addBlocks(dynamic);
9202
- }
9203
9265
  const pluginBlocks = (this.deps.pluginManager.runOnBuildPrompt?.() ?? []).filter((s) => Boolean(s && s.trim() !== "")).map((content) => ({
9204
9266
  content,
9205
9267
  priority: "low",
@@ -9209,6 +9271,10 @@ class Agent {
9209
9271
  if (pluginBlocks.length > 0) {
9210
9272
  builder.addBlocks(pluginBlocks);
9211
9273
  }
9274
+ const dynamic = this.deps.getDynamicPromptBlocks?.() ?? [];
9275
+ if (dynamic.length > 0) {
9276
+ builder.addBlocks(dynamic);
9277
+ }
9212
9278
  return builder.build();
9213
9279
  }
9214
9280
  getSystemPromptInfo() {
@@ -9279,7 +9345,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
9279
9345
  toolExecutor,
9280
9346
  logger,
9281
9347
  baseDir: this.deps.baseDir
9282
- }, input, () => this.executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase), { onMeta, onTool, onPhase });
9348
+ }, input, () => this.executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase), { onMeta, onTool, onPhase, signal: this.abortController?.signal });
9283
9349
  }
9284
9350
  return this.executeSingleAgentLoop(input, onChunk, onMeta, onTool, onPhase);
9285
9351
  }
@@ -9449,15 +9515,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
9449
9515
  onTool?.({ type: "start", tool: call.name, args: call.arguments });
9450
9516
  slog.logToolCall(call, iteration);
9451
9517
  const tokensBeforeTool = contextManager.getEstimatedTokens();
9452
- const toolCtx = this.deps.toolExecutor?.getContext?.();
9453
- const prevOnMeta = toolCtx?.onMeta;
9454
- if (toolCtx) {
9455
- toolCtx.onMeta = onMeta;
9456
- }
9457
9518
  const result = await toolExecutor.execute(call, this.abortController?.signal);
9458
- if (toolCtx) {
9459
- toolCtx.onMeta = prevOnMeta;
9460
- }
9461
9519
  const duration = Date.now() - startTime;
9462
9520
  pluginManager.runOnToolEnd({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
9463
9521
  return { call, result, duration, tokensBefore: tokensBeforeTool };
@@ -9510,11 +9568,12 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
9510
9568
  if (config.session.autoSave) {
9511
9569
  slog.logToolResult(call, result, duration, iteration);
9512
9570
  }
9513
- if (contextManager.needsCompaction()) {
9514
- contextManager.compact();
9515
- logger.debug("Context compacted after tool result");
9516
- }
9517
9571
  };
9572
+ const toolExecutorForMeta = this.deps.toolExecutor;
9573
+ const savedOnMeta = toolExecutorForMeta?.getContext?.()?.onMeta;
9574
+ if (toolExecutorForMeta?.setOnMeta) {
9575
+ toolExecutorForMeta.setOnMeta(onMeta);
9576
+ }
9518
9577
  if (canRunInParallel(toolCalls)) {
9519
9578
  const entries = await Promise.all(toolCalls.map(executeToolCall));
9520
9579
  for (const entry of entries) {
@@ -9526,6 +9585,13 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
9526
9585
  processToolResult(entry);
9527
9586
  }
9528
9587
  }
9588
+ if (toolExecutorForMeta?.setOnMeta) {
9589
+ toolExecutorForMeta.setOnMeta(savedOnMeta);
9590
+ }
9591
+ if (contextManager.needsCompaction()) {
9592
+ contextManager.compact();
9593
+ logger.debug("Context compacted after tool results");
9594
+ }
9529
9595
  if (anyToolFailed) {
9530
9596
  consecutiveToolFailures++;
9531
9597
  } else {
@@ -10454,8 +10520,7 @@ var init_subagent = __esm(() => {
10454
10520
  baseDir: ctx.baseDir,
10455
10521
  scope,
10456
10522
  toolTags,
10457
- promptBlocks: [systemPrompt],
10458
- recursionDepth: currentDepth + 1
10523
+ promptBlocks: [systemPrompt]
10459
10524
  };
10460
10525
  const subAgent = new Agent(subDeps);
10461
10526
  const fullTask = context ? `${task}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.24.11",
3
+ "version": "0.24.12",
4
4
  "description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
5
5
  "type": "module",
6
6
  "bin": {