micro-models-agent 0.36.1 → 0.36.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.
Files changed (2) hide show
  1. package/dist/main.js +256 -28
  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");
@@ -29948,6 +30109,8 @@ class LineEditor {
29948
30109
  stash = null;
29949
30110
  isDone = false;
29950
30111
  isPaste = false;
30112
+ pasteBuffer = "";
30113
+ pasteLastWasCR = false;
29951
30114
  lastWasCR = false;
29952
30115
  questionCb = null;
29953
30116
  rawMode = false;
@@ -30074,22 +30237,38 @@ class LineEditor {
30074
30237
  }
30075
30238
  if (name === "paste-start" || seq2 === "\x1B[200~") {
30076
30239
  this.isPaste = true;
30240
+ this.pasteBuffer = "";
30241
+ this.pasteLastWasCR = false;
30077
30242
  return;
30078
30243
  }
30079
30244
  if (name === "paste-end" || seq2 === "\x1B[201~") {
30080
30245
  this.isPaste = false;
30081
- this.render();
30246
+ if (this.pasteBuffer) {
30247
+ this.insertPaste(this.pasteBuffer);
30248
+ } else {
30249
+ this.render();
30250
+ }
30251
+ this.pasteBuffer = "";
30252
+ this.pasteLastWasCR = false;
30082
30253
  return;
30083
30254
  }
30084
30255
  if (this.isPaste) {
30085
- if (name === "return" || seq2 === "\r" || seq2 === `
30256
+ if (name === "return" || seq2 === "\r") {
30257
+ this.pasteBuffer += `
30258
+ `;
30259
+ this.pasteLastWasCR = true;
30260
+ } else if (name === "enter" || seq2 === `
30086
30261
  `) {
30087
- this.insertText(`
30088
- `);
30262
+ if (this.pasteLastWasCR) {
30263
+ this.pasteLastWasCR = false;
30264
+ } else {
30265
+ this.pasteBuffer += `
30266
+ `;
30267
+ }
30089
30268
  } else if (str) {
30090
- this.insertText(str);
30269
+ this.pasteLastWasCR = false;
30270
+ this.pasteBuffer += str;
30091
30271
  }
30092
- this.render();
30093
30272
  return;
30094
30273
  }
30095
30274
  if (name === "return" && (seq2 === "\x1B\r" || k.meta)) {
@@ -30294,6 +30473,25 @@ class LineEditor {
30294
30473
  this.col += t2.length;
30295
30474
  this.render();
30296
30475
  }
30476
+ insertPaste(text) {
30477
+ const segments = text.split(`
30478
+ `);
30479
+ if (segments.length === 1) {
30480
+ this.insertText(segments[0]);
30481
+ return;
30482
+ }
30483
+ const chars = this.currentChars();
30484
+ const head = chars.slice(0, this.col).join("");
30485
+ const tail = chars.slice(this.col).join("");
30486
+ const lines = [head + segments[0]];
30487
+ for (let i = 1;i < segments.length - 1; i++)
30488
+ lines.push(segments[i]);
30489
+ lines.push(segments[segments.length - 1] + tail);
30490
+ this.lines.splice(this.row, 1, ...lines);
30491
+ this.row += segments.length - 1;
30492
+ this.col = charLen(segments[segments.length - 1]);
30493
+ this.render();
30494
+ }
30297
30495
  insertNewline() {
30298
30496
  const chars = this.currentChars();
30299
30497
  const rest = chars.slice(this.col).join("");
@@ -31602,6 +31800,17 @@ import { fileURLToPath as fileURLToPath6 } from "url";
31602
31800
  // src/modules/updater/checker.ts
31603
31801
  init_command();
31604
31802
  import { platform as platform9 } from "os";
31803
+ function semverGt(a, b) {
31804
+ const pa = a.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
31805
+ const pb = b.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
31806
+ for (let i = 0;i < 3; i++) {
31807
+ const na = pa[i] ?? 0;
31808
+ const nb = pb[i] ?? 0;
31809
+ if (na !== nb)
31810
+ return na > nb;
31811
+ }
31812
+ return false;
31813
+ }
31605
31814
  var defaultRunner2 = async (command, args, options) => {
31606
31815
  const { execFile } = await import("child_process");
31607
31816
  return new Promise((resolve24) => {
@@ -31638,7 +31847,7 @@ class Updater {
31638
31847
  if (!latest) {
31639
31848
  return { updateAvailable: false, current: this.currentVersion, error: "No latest tag" };
31640
31849
  }
31641
- const updateAvailable = latest !== this.currentVersion;
31850
+ const updateAvailable = semverGt(latest, this.currentVersion);
31642
31851
  return { updateAvailable, current: this.currentVersion, latest };
31643
31852
  } catch (e) {
31644
31853
  return { updateAvailable: false, current: this.currentVersion, error: e.message };
@@ -31680,6 +31889,7 @@ class UpdaterModule {
31680
31889
  logger;
31681
31890
  started = false;
31682
31891
  timer = null;
31892
+ idlePromise = null;
31683
31893
  installEnabled = true;
31684
31894
  constructor(config, currentVersion, packageName, logger, installRunner) {
31685
31895
  this.config = config;
@@ -31703,7 +31913,7 @@ class UpdaterModule {
31703
31913
  if (this.started || !this.config.enabled)
31704
31914
  return;
31705
31915
  this.started = true;
31706
- this.runLoop();
31916
+ this.idlePromise = this.runLoop();
31707
31917
  }
31708
31918
  stop() {
31709
31919
  this.started = false;
@@ -31712,6 +31922,12 @@ class UpdaterModule {
31712
31922
  this.timer = null;
31713
31923
  }
31714
31924
  }
31925
+ async waitForIdle(timeoutMs = 130000) {
31926
+ const idle = this.idlePromise;
31927
+ if (!idle)
31928
+ return;
31929
+ await Promise.race([idle, new Promise((r) => setTimeout(r, timeoutMs))]);
31930
+ }
31715
31931
  async runLoop() {
31716
31932
  while (this.started) {
31717
31933
  await this.checkOnce();
@@ -31775,9 +31991,19 @@ function readVersion5() {
31775
31991
  }
31776
31992
  function startAutoUpdate(config) {
31777
31993
  try {
31778
- const module = new UpdaterModule(config.updater, readVersion5(), "micro-models-agent");
31994
+ const module = new UpdaterModule(config.updater, readVersion5(), "micro-models-agent", {
31995
+ info: (m) => process.stderr.write(pc2.dim(m) + `
31996
+ `),
31997
+ warn: (m) => process.stderr.write(pc2.yellow(m) + `
31998
+ `),
31999
+ error: (m) => process.stderr.write(pc2.red(m) + `
32000
+ `)
32001
+ });
31779
32002
  module.start();
31780
- } catch {}
32003
+ return module;
32004
+ } catch {
32005
+ return;
32006
+ }
31781
32007
  }
31782
32008
  async function main() {
31783
32009
  const program2 = createProgram();
@@ -31795,7 +32021,7 @@ async function main() {
31795
32021
  if (program2.args.length > 0) {
31796
32022
  const prompt = program2.args.join(" ");
31797
32023
  const { agent, config } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
31798
- startAutoUpdate(config);
32024
+ const updater = exitOnComplete ? undefined : startAutoUpdate(config);
31799
32025
  if (jsonMode) {
31800
32026
  const result2 = await agent.run(prompt);
31801
32027
  agent.shutdown();
@@ -31812,6 +32038,7 @@ async function main() {
31812
32038
  }, null, 2));
31813
32039
  process.stdout.write(`
31814
32040
  `);
32041
+ await updater?.waitForIdle();
31815
32042
  process.exit(result2.success ? 0 : 1);
31816
32043
  }
31817
32044
  const renderer = new Renderer({
@@ -31834,6 +32061,7 @@ async function main() {
31834
32061
  renderer.flush();
31835
32062
  const exitCode = printRunResult(result, () => renderer.flush());
31836
32063
  agent.shutdown();
32064
+ await updater?.waitForIdle();
31837
32065
  process.exit(exitCode);
31838
32066
  } else {
31839
32067
  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.3",
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": {