micro-models-agent 0.36.1 → 0.36.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.
Files changed (2) hide show
  1. package/dist/main.js +213 -22
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -7909,6 +7909,12 @@ var init_process_kill = __esm(() => {
7909
7909
  });
7910
7910
 
7911
7911
  // src/core/prompt-builder.ts
7912
+ function blockLabel(content) {
7913
+ const firstLine = content.split(`
7914
+ `)[0].trim();
7915
+ return firstLine.length > 70 ? firstLine.slice(0, 67) + "..." : firstLine;
7916
+ }
7917
+
7912
7918
  class PromptBuilder {
7913
7919
  blocks = [];
7914
7920
  budget;
@@ -7937,24 +7943,41 @@ class PromptBuilder {
7937
7943
  let usedTokens = 0;
7938
7944
  const included = [];
7939
7945
  const excluded = [];
7946
+ const blocks = [];
7940
7947
  for (const block of essential) {
7941
7948
  included.push(block.content);
7942
7949
  usedTokens += block.estimatedTokens;
7950
+ blocks.push({
7951
+ label: blockLabel(block.content),
7952
+ priority: block.priority,
7953
+ essential: true,
7954
+ tokens: block.estimatedTokens,
7955
+ included: true
7956
+ });
7943
7957
  }
7944
7958
  for (const block of nonEssential) {
7945
7959
  const tokens = block.estimatedTokens;
7946
- if (usedTokens + tokens <= this.budget) {
7960
+ const fits = usedTokens + tokens <= this.budget;
7961
+ if (fits) {
7947
7962
  included.push(block.content);
7948
7963
  usedTokens += tokens;
7949
7964
  } else {
7950
7965
  excluded.push(block.content);
7951
7966
  }
7967
+ blocks.push({
7968
+ label: blockLabel(block.content),
7969
+ priority: block.priority,
7970
+ essential: false,
7971
+ tokens,
7972
+ included: fits
7973
+ });
7952
7974
  }
7953
7975
  return {
7954
7976
  prompt: included.join(`
7955
7977
 
7956
7978
  `),
7957
- excluded
7979
+ excluded,
7980
+ blocks
7958
7981
  };
7959
7982
  }
7960
7983
  }
@@ -8078,14 +8101,54 @@ class SessionLogger {
8078
8101
  iteration
8079
8102
  });
8080
8103
  }
8081
- logCompaction(content, iteration, contextTokens, contextLimit) {
8104
+ logCompaction(info) {
8082
8105
  this.session?.appendLog({
8083
8106
  ts: new Date().toISOString(),
8084
8107
  type: "compaction",
8085
- content,
8108
+ content: `${info.reason}, iteration ${info.iteration}`,
8109
+ iteration: info.iteration,
8110
+ reason: info.reason,
8111
+ tokensBefore: info.tokensBefore,
8112
+ tokensAfter: info.tokensAfter,
8113
+ qualityBefore: info.qualityBefore,
8114
+ qualityAfter: info.qualityAfter,
8115
+ messagesBefore: info.messagesBefore,
8116
+ messagesAfter: info.messagesAfter,
8117
+ removedTurns: info.removedTurns,
8118
+ keptTurns: info.keptTurns,
8119
+ summary: info.summary
8120
+ });
8121
+ }
8122
+ logContext(info) {
8123
+ this.session?.appendLog({
8124
+ ts: new Date().toISOString(),
8125
+ type: "context",
8126
+ content: info.kind === "start" ? "context snapshot at session start" : `context at iteration ${info.iteration}`,
8127
+ iteration: info.iteration,
8128
+ window: info.window,
8129
+ systemBudget: info.systemBudget,
8130
+ reserveBudget: info.reserveBudget,
8131
+ historyBudget: info.historyBudget,
8132
+ systemTokens: info.systemTokens,
8133
+ toolTokens: info.toolTokens,
8134
+ contextTokens: info.tokens,
8135
+ quality: info.quality,
8136
+ messageCount: info.messageCount,
8137
+ compactionCount: info.compactionCount,
8138
+ iterationsSinceCompaction: info.iterationsSinceCompaction,
8139
+ blocks: info.blocks
8140
+ });
8141
+ }
8142
+ logLlmUsage(iteration, usage) {
8143
+ this.session?.appendLog({
8144
+ ts: new Date().toISOString(),
8145
+ type: "llm_usage",
8086
8146
  iteration,
8087
- ...contextTokens !== undefined ? { contextTokens } : {},
8088
- ...contextLimit !== undefined ? { contextLimit } : {}
8147
+ promptTokens: usage.promptTokens,
8148
+ completionTokens: usage.completionTokens,
8149
+ totalTokens: usage.totalTokens,
8150
+ source: usage.source,
8151
+ durationMs: usage.durationMs
8089
8152
  });
8090
8153
  }
8091
8154
  logError(message) {
@@ -10384,7 +10447,36 @@ class Agent {
10384
10447
  if (pluginBlocks.length > 0) {
10385
10448
  builder.addBlocks(pluginBlocks);
10386
10449
  }
10387
- return builder.build();
10450
+ const result = builder.build();
10451
+ return {
10452
+ prompt: result.prompt,
10453
+ excluded: result.excluded,
10454
+ blocks: result.blocks
10455
+ };
10456
+ }
10457
+ logContextStat(kind, iteration, slog, blocks) {
10458
+ const cm = this.deps.contextManager;
10459
+ if (typeof cm.getSnapshot !== "function")
10460
+ return;
10461
+ const snap = cm.getSnapshot();
10462
+ const history = cm.getActiveHistory();
10463
+ const systemMsg = history.find((m) => m.role === "system");
10464
+ slog.logContext({
10465
+ kind,
10466
+ iteration,
10467
+ window: snap.window,
10468
+ systemBudget: snap.budget.systemPrompt,
10469
+ reserveBudget: snap.budget.responseReserve,
10470
+ historyBudget: snap.budget.history,
10471
+ systemTokens: kind === "start" && systemMsg && typeof systemMsg.content === "string" ? this.deps.llmProvider.countTokens(systemMsg.content) : undefined,
10472
+ toolTokens: snap.toolTokens,
10473
+ tokens: snap.tokens,
10474
+ quality: snap.quality,
10475
+ messageCount: snap.messageCount,
10476
+ compactionCount: snap.compactionCount,
10477
+ iterationsSinceCompaction: snap.iterationsSinceCompaction,
10478
+ blocks
10479
+ });
10388
10480
  }
10389
10481
  getSystemPromptInfo() {
10390
10482
  const { prompt, excluded } = this.buildSystemPrompt();
@@ -10544,27 +10636,47 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10544
10636
  }
10545
10637
  });
10546
10638
  if (contextManager.needsCompaction()) {
10547
- contextManager.compact();
10548
- logger.debug("Context compacted");
10549
- slog.logCompaction(`regular compaction, iteration ${iteration}`, iteration);
10639
+ const result = contextManager.compact();
10640
+ if (result) {
10641
+ logger.debug("Context compacted");
10642
+ slog.logCompaction({ reason: "interval", iteration, ...result });
10643
+ }
10550
10644
  }
10551
10645
  const currentTokens = contextManager.getEstimatedTokens();
10552
10646
  const budget2 = contextManager.getBudget();
10553
10647
  const quality = contextManager.getQuality();
10554
10648
  if (quality < QUALITY_TRIGGER_THRESHOLD && contextManager.getCompactionCount() > 0 && iteration - lastForcedCompactionIteration >= FORCED_COMPACTION_COOLDOWN) {
10555
10649
  lastForcedCompactionIteration = iteration;
10556
- contextManager.compact();
10650
+ const result = contextManager.compact();
10557
10651
  logger.warn(`Low context quality (${quality}%) — forced compaction`);
10558
- slog.logCompaction(`quality-triggered compaction (${quality}% < ${QUALITY_TRIGGER_THRESHOLD}%), iteration ${iteration}`, iteration, currentTokens, budget2.history);
10652
+ if (result) {
10653
+ slog.logCompaction({
10654
+ reason: `quality-triggered (${quality}% < ${QUALITY_TRIGGER_THRESHOLD}%)`,
10655
+ iteration,
10656
+ ...result
10657
+ });
10658
+ }
10559
10659
  }
10560
10660
  if (currentTokens > budget2.history) {
10561
- contextManager.compact();
10661
+ const result = contextManager.compact();
10562
10662
  logger.warn(`Context overflow (${currentTokens} > ${budget2.history}), forced compaction`);
10563
- slog.logCompaction(`forced compaction (${currentTokens} > ${budget2.history}), iteration ${iteration}`, iteration, currentTokens, budget2.history);
10663
+ if (result) {
10664
+ slog.logCompaction({
10665
+ reason: `overflow (${currentTokens} > ${budget2.history})`,
10666
+ iteration,
10667
+ ...result
10668
+ });
10669
+ }
10564
10670
  }
10565
10671
  this.refreshSystemPrompt();
10566
10672
  const history = contextManager.getActiveHistory();
10567
10673
  slog.logToolDefs(allToolsForBudget.length, allToolsForBudget.map((t2) => t2.name), iteration);
10674
+ if (iteration === 1) {
10675
+ const { blocks } = this.buildSystemPrompt();
10676
+ this.logContextStat("start", iteration, slog, blocks);
10677
+ } else {
10678
+ this.logContextStat("iteration", iteration, slog);
10679
+ }
10568
10680
  let textContent = "";
10569
10681
  let reasoningContent = "";
10570
10682
  const toolCalls = [];
@@ -10573,6 +10685,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10573
10685
  const textChunks = [];
10574
10686
  this.emitPhase(iteration, "thinking", onPhase);
10575
10687
  const llmStart = Date.now();
10688
+ const promptBefore = apiPromptTokens;
10689
+ const completionBefore = apiCompletionTokens;
10576
10690
  logger.logLLMRequest(config.model, history.length, input, "agent");
10577
10691
  try {
10578
10692
  for await (const chunk of llmProvider.chat(history, allToolsForBudget, this.abortController?.signal)) {
@@ -10636,6 +10750,20 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10636
10750
  }
10637
10751
  apiCompletionChars += (textContent || reasoningContent).length;
10638
10752
  logger.logLLMResponse(config.model, (textContent || reasoningContent).length, Date.now() - llmStart, undefined, "agent");
10753
+ {
10754
+ const usagePrompt = apiPromptTokens - promptBefore;
10755
+ const usageCompletion = apiCompletionTokens - completionBefore;
10756
+ const source = usagePrompt > 0 || usageCompletion > 0 ? "api" : "estimate";
10757
+ const prompt = source === "api" ? usagePrompt : contextManager.getEstimatedTokens();
10758
+ const completion = source === "api" ? usageCompletion : Math.ceil((textContent || reasoningContent).length / 4);
10759
+ slog.logLlmUsage(iteration, {
10760
+ promptTokens: prompt,
10761
+ completionTokens: completion,
10762
+ totalTokens: prompt + completion,
10763
+ source,
10764
+ durationMs: Date.now() - llmStart
10765
+ });
10766
+ }
10639
10767
  if (this.shutdownRequested) {
10640
10768
  break;
10641
10769
  }
@@ -10753,8 +10881,15 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10753
10881
  slog.logToolResult(call, result, duration, iteration);
10754
10882
  }
10755
10883
  if (contextManager.needsCompaction()) {
10756
- contextManager.compact();
10757
- logger.debug("Context compacted after tool result");
10884
+ const result2 = contextManager.compact();
10885
+ if (result2) {
10886
+ logger.debug("Context compacted after tool result");
10887
+ slog.logCompaction({
10888
+ reason: "after_tool",
10889
+ iteration,
10890
+ ...result2
10891
+ });
10892
+ }
10758
10893
  }
10759
10894
  if (!result.success) {
10760
10895
  const key = call.name;
@@ -11362,9 +11497,12 @@ class ContextManager {
11362
11497
  return totalTokens > this.budget.history * this.compactionThreshold;
11363
11498
  }
11364
11499
  compact() {
11500
+ const tokensBefore = this.getEstimatedTokens();
11501
+ const qualityBefore = this.getQuality();
11502
+ const messagesBefore = this.messages.length;
11365
11503
  this.iterationsSinceCompaction = 0;
11366
11504
  if (this.messages.length <= KEEP_LAST_N * 2)
11367
- return;
11505
+ return null;
11368
11506
  this.compactionCount++;
11369
11507
  const cutoff = this.messages.length - KEEP_LAST_N * 2;
11370
11508
  const oldTurns = this.messages.slice(0, cutoff);
@@ -11418,6 +11556,29 @@ ${lines.join(`
11418
11556
  if (this.onCompact) {
11419
11557
  this.onCompact(summary);
11420
11558
  }
11559
+ return {
11560
+ removedTurns: oldTurns.length,
11561
+ keptTurns: freshRecent.length,
11562
+ tokensBefore,
11563
+ tokensAfter: this.getEstimatedTokens(),
11564
+ qualityBefore,
11565
+ qualityAfter: this.getQuality(),
11566
+ messagesBefore,
11567
+ messagesAfter: this.messages.length,
11568
+ summary: this.compactedBlock ?? ""
11569
+ };
11570
+ }
11571
+ getSnapshot() {
11572
+ return {
11573
+ window: this.contextWindow,
11574
+ budget: { ...this.budget },
11575
+ tokens: this.getEstimatedTokens(),
11576
+ toolTokens: this.toolTokens,
11577
+ messageCount: this.messages.length,
11578
+ quality: this.getQuality(),
11579
+ compactionCount: this.compactionCount,
11580
+ iterationsSinceCompaction: this.iterationsSinceCompaction
11581
+ };
11421
11582
  }
11422
11583
  updateSystemPrompt(content) {
11423
11584
  const idx = this.messages.findIndex((m) => m.role === "system");
@@ -31602,6 +31763,17 @@ import { fileURLToPath as fileURLToPath6 } from "url";
31602
31763
  // src/modules/updater/checker.ts
31603
31764
  init_command();
31604
31765
  import { platform as platform9 } from "os";
31766
+ function semverGt(a, b) {
31767
+ const pa = a.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
31768
+ const pb = b.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
31769
+ for (let i = 0;i < 3; i++) {
31770
+ const na = pa[i] ?? 0;
31771
+ const nb = pb[i] ?? 0;
31772
+ if (na !== nb)
31773
+ return na > nb;
31774
+ }
31775
+ return false;
31776
+ }
31605
31777
  var defaultRunner2 = async (command, args, options) => {
31606
31778
  const { execFile } = await import("child_process");
31607
31779
  return new Promise((resolve24) => {
@@ -31638,7 +31810,7 @@ class Updater {
31638
31810
  if (!latest) {
31639
31811
  return { updateAvailable: false, current: this.currentVersion, error: "No latest tag" };
31640
31812
  }
31641
- const updateAvailable = latest !== this.currentVersion;
31813
+ const updateAvailable = semverGt(latest, this.currentVersion);
31642
31814
  return { updateAvailable, current: this.currentVersion, latest };
31643
31815
  } catch (e) {
31644
31816
  return { updateAvailable: false, current: this.currentVersion, error: e.message };
@@ -31680,6 +31852,7 @@ class UpdaterModule {
31680
31852
  logger;
31681
31853
  started = false;
31682
31854
  timer = null;
31855
+ idlePromise = null;
31683
31856
  installEnabled = true;
31684
31857
  constructor(config, currentVersion, packageName, logger, installRunner) {
31685
31858
  this.config = config;
@@ -31703,7 +31876,7 @@ class UpdaterModule {
31703
31876
  if (this.started || !this.config.enabled)
31704
31877
  return;
31705
31878
  this.started = true;
31706
- this.runLoop();
31879
+ this.idlePromise = this.runLoop();
31707
31880
  }
31708
31881
  stop() {
31709
31882
  this.started = false;
@@ -31712,6 +31885,12 @@ class UpdaterModule {
31712
31885
  this.timer = null;
31713
31886
  }
31714
31887
  }
31888
+ async waitForIdle(timeoutMs = 130000) {
31889
+ const idle = this.idlePromise;
31890
+ if (!idle)
31891
+ return;
31892
+ await Promise.race([idle, new Promise((r) => setTimeout(r, timeoutMs))]);
31893
+ }
31715
31894
  async runLoop() {
31716
31895
  while (this.started) {
31717
31896
  await this.checkOnce();
@@ -31775,9 +31954,19 @@ function readVersion5() {
31775
31954
  }
31776
31955
  function startAutoUpdate(config) {
31777
31956
  try {
31778
- const module = new UpdaterModule(config.updater, readVersion5(), "micro-models-agent");
31957
+ const module = new UpdaterModule(config.updater, readVersion5(), "micro-models-agent", {
31958
+ info: (m) => process.stderr.write(pc2.dim(m) + `
31959
+ `),
31960
+ warn: (m) => process.stderr.write(pc2.yellow(m) + `
31961
+ `),
31962
+ error: (m) => process.stderr.write(pc2.red(m) + `
31963
+ `)
31964
+ });
31779
31965
  module.start();
31780
- } catch {}
31966
+ return module;
31967
+ } catch {
31968
+ return;
31969
+ }
31781
31970
  }
31782
31971
  async function main() {
31783
31972
  const program2 = createProgram();
@@ -31795,7 +31984,7 @@ async function main() {
31795
31984
  if (program2.args.length > 0) {
31796
31985
  const prompt = program2.args.join(" ");
31797
31986
  const { agent, config } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
31798
- startAutoUpdate(config);
31987
+ const updater = exitOnComplete ? undefined : startAutoUpdate(config);
31799
31988
  if (jsonMode) {
31800
31989
  const result2 = await agent.run(prompt);
31801
31990
  agent.shutdown();
@@ -31812,6 +32001,7 @@ async function main() {
31812
32001
  }, null, 2));
31813
32002
  process.stdout.write(`
31814
32003
  `);
32004
+ await updater?.waitForIdle();
31815
32005
  process.exit(result2.success ? 0 : 1);
31816
32006
  }
31817
32007
  const renderer = new Renderer({
@@ -31834,6 +32024,7 @@ async function main() {
31834
32024
  renderer.flush();
31835
32025
  const exitCode = printRunResult(result, () => renderer.flush());
31836
32026
  agent.shutdown();
32027
+ await updater?.waitForIdle();
31837
32028
  process.exit(exitCode);
31838
32029
  } else {
31839
32030
  const configPath = join45(homedir18(), ".mma", "config.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.36.1",
3
+ "version": "0.36.2",
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": {