omnius 1.0.396 → 1.0.398

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/index.js CHANGED
@@ -23474,28 +23474,28 @@ function countNodes(db) {
23474
23474
  return 0;
23475
23475
  }
23476
23476
  }
23477
- function pruneLowSignalNodesToCap(db, dbPath, options2) {
23477
+ function selectCapEvictionCandidates(db, options2, withText) {
23478
23478
  const cap = Math.max(0, options2.graphTargetNodes ?? Number(process.env["OMNIUS_KG_TARGET_NODES"] ?? 5e3));
23479
23479
  if (!Number.isFinite(cap) || cap <= 0)
23480
- return { nodes: 0, edges: 0, archived: 0 };
23480
+ return [];
23481
23481
  const total = countNodes(db);
23482
23482
  if (total <= cap)
23483
- return { nodes: 0, edges: 0, archived: 0 };
23483
+ return [];
23484
23484
  const overflow = total - cap;
23485
23485
  const batch2 = Math.min(overflow, options2.maxGraphDeletes ?? 2e3);
23486
23486
  if (batch2 <= 0)
23487
- return { nodes: 0, edges: 0, archived: 0 };
23487
+ return [];
23488
23488
  const now2 = Date.now();
23489
23489
  const protectMs = Math.max(0, options2.graphProtectRecentMs ?? DAY);
23490
23490
  const protectCut = now2 - protectMs;
23491
23491
  const protect = new Set(options2.graphProtectNodeIds ?? []);
23492
23492
  const hubDegreeGuard = 6;
23493
- let candidates = [];
23494
23493
  try {
23495
23494
  const shortlistLimit = Math.max(1, Math.min(batch2 * 4, batch2 + 2e4));
23496
- const shortlist = db.prepare(`SELECT id,
23497
- COALESCE(mention_count, 1) AS mc,
23498
- COALESCE(last_seen, 0) AS ls
23495
+ const cols = withText ? `id, COALESCE(text, '') AS text, COALESCE(node_type, 'entity') AS nodeType,
23496
+ COALESCE(mention_count, 1) AS mc, COALESCE(last_seen, 0) AS ls` : `id, '' AS text, 'entity' AS nodeType,
23497
+ COALESCE(mention_count, 1) AS mc, COALESCE(last_seen, 0) AS ls`;
23498
+ const shortlist = db.prepare(`SELECT ${cols}
23499
23499
  FROM kg_nodes
23500
23500
  WHERE COALESCE(last_seen, 0) < ?
23501
23501
  ORDER BY mc ASC, ls ASC
@@ -23513,19 +23513,70 @@ function pruneLowSignalNodesToCap(db, dbPath, options2) {
23513
23513
  }
23514
23514
  if (deg >= hubDegreeGuard)
23515
23515
  continue;
23516
- scored.push({ id: row.id, score: row.mc + deg * 2, ls: row.ls });
23516
+ scored.push({
23517
+ id: row.id,
23518
+ text: row.text,
23519
+ nodeType: row.nodeType,
23520
+ mentionCount: row.mc,
23521
+ degree: deg,
23522
+ lastSeen: row.ls,
23523
+ score: row.mc + deg * 2
23524
+ });
23517
23525
  }
23518
- scored.sort((a2, b) => a2.score - b.score || a2.ls - b.ls);
23519
- candidates = scored.slice(0, batch2).map((s2) => s2.id);
23526
+ scored.sort((a2, b) => a2.score - b.score || a2.lastSeen - b.lastSeen);
23527
+ return scored.slice(0, batch2).map(({ score: _score, ...c9 }) => c9);
23520
23528
  } catch {
23521
- return { nodes: 0, edges: 0, archived: 0 };
23529
+ return [];
23522
23530
  }
23531
+ }
23532
+ function pruneLowSignalNodesToCap(db, dbPath, options2) {
23533
+ const candidates = selectCapEvictionCandidates(db, options2, false).map((c9) => c9.id);
23523
23534
  if (candidates.length === 0)
23524
23535
  return { nodes: 0, edges: 0, archived: 0 };
23525
23536
  if (options2.dryRun)
23526
23537
  return { nodes: candidates.length, edges: 0, archived: 0 };
23527
23538
  return archiveAndDeleteNodes(db, dbPath, candidates, options2.graphArchive !== false);
23528
23539
  }
23540
+ function selectGraphPruneCandidates(dbPath, options2) {
23541
+ if (!existsSync22(dbPath))
23542
+ return [];
23543
+ const db = initDb(dbPath);
23544
+ try {
23545
+ db.pragma("busy_timeout = 5000");
23546
+ return selectCapEvictionCandidates(db, options2, true);
23547
+ } catch {
23548
+ return [];
23549
+ } finally {
23550
+ try {
23551
+ db.close();
23552
+ } catch {
23553
+ }
23554
+ }
23555
+ }
23556
+ function reinforceGraphNodes(dbPath, ids) {
23557
+ if (!existsSync22(dbPath) || ids.length === 0)
23558
+ return 0;
23559
+ const db = initDb(dbPath);
23560
+ try {
23561
+ db.pragma("busy_timeout = 5000");
23562
+ const now2 = Date.now();
23563
+ const stmt = db.prepare("UPDATE kg_nodes SET last_seen = ?, mention_count = COALESCE(mention_count, 1) + 1 WHERE id = ?");
23564
+ let updated = 0;
23565
+ const tx = db.transaction((rows) => {
23566
+ for (const id of rows)
23567
+ updated += Number(stmt.run(now2, id).changes ?? 0);
23568
+ });
23569
+ tx(ids);
23570
+ return updated;
23571
+ } catch {
23572
+ return 0;
23573
+ } finally {
23574
+ try {
23575
+ db.close();
23576
+ } catch {
23577
+ }
23578
+ }
23579
+ }
23529
23580
  function archiveAndDeleteNodes(db, dbPath, ids, archive) {
23530
23581
  if (ids.length === 0)
23531
23582
  return { nodes: 0, edges: 0, archived: 0 };
@@ -24414,6 +24465,7 @@ __export(dist_exports, {
24414
24465
  reciprocityBias: () => reciprocityBias,
24415
24466
  recordEmbeddingMeta: () => recordEmbeddingMeta,
24416
24467
  reembedAll: () => reembedAll,
24468
+ reinforceGraphNodes: () => reinforceGraphNodes,
24417
24469
  remDream: () => remDream,
24418
24470
  resetCRLConfigStore: () => resetCRLConfigStore,
24419
24471
  retrieveByPPR: () => retrieveByPPR,
@@ -24424,6 +24476,7 @@ __export(dist_exports, {
24424
24476
  selectAndWalkGraphCandidate: () => selectAndWalkGraphCandidate,
24425
24477
  selectAttentionContextItems: () => selectAttentionContextItems,
24426
24478
  selectByAttention: () => selectByAttention,
24479
+ selectGraphPruneCandidates: () => selectGraphPruneCandidates,
24427
24480
  selectInnerGraphCandidates: () => selectInnerGraphCandidates,
24428
24481
  selfTrustFor: () => selfTrustFor,
24429
24482
  shouldTriggerConsolidation: () => shouldTriggerConsolidation,
@@ -44414,12 +44467,12 @@ var init_identity2 = __esm({
44414
44467
  function from2({ name: name10, code: code8, encode: encode15, minDigestLength, maxDigestLength }) {
44415
44468
  return new Hasher(name10, code8, encode15, minDigestLength, maxDigestLength);
44416
44469
  }
44417
- function createDigest(digest3, code8, truncate3) {
44418
- if (truncate3 != null && truncate3 !== digest3.byteLength) {
44419
- if (truncate3 > digest3.byteLength) {
44470
+ function createDigest(digest3, code8, truncate4) {
44471
+ if (truncate4 != null && truncate4 !== digest3.byteLength) {
44472
+ if (truncate4 > digest3.byteLength) {
44420
44473
  throw new Error(`Invalid truncate option, must be less than or equal to ${digest3.byteLength}`);
44421
44474
  }
44422
- digest3 = digest3.subarray(0, truncate3);
44475
+ digest3 = digest3.subarray(0, truncate4);
44423
44476
  }
44424
44477
  return create(code8, digest3);
44425
44478
  }
@@ -77364,7 +77417,7 @@ var require_truncate = __commonJS({
77364
77417
  function isLowSurrogate(codePoint) {
77365
77418
  return codePoint >= 56320 && codePoint <= 57343;
77366
77419
  }
77367
- module.exports = function truncate3(getLength, string2, byteLength) {
77420
+ module.exports = function truncate4(getLength, string2, byteLength) {
77368
77421
  if (typeof string2 !== "string") {
77369
77422
  throw new Error("Input must be string");
77370
77423
  }
@@ -77395,9 +77448,9 @@ var require_truncate = __commonJS({
77395
77448
  var require_truncate_utf8_bytes = __commonJS({
77396
77449
  "../node_modules/truncate-utf8-bytes/index.js"(exports, module) {
77397
77450
  "use strict";
77398
- var truncate3 = require_truncate();
77451
+ var truncate4 = require_truncate();
77399
77452
  var getLength = Buffer.byteLength.bind(Buffer);
77400
- module.exports = truncate3.bind(null, getLength);
77453
+ module.exports = truncate4.bind(null, getLength);
77401
77454
  }
77402
77455
  });
77403
77456
 
@@ -77405,7 +77458,7 @@ var require_truncate_utf8_bytes = __commonJS({
77405
77458
  var require_sanitize_filename = __commonJS({
77406
77459
  "../node_modules/sanitize-filename/index.js"(exports, module) {
77407
77460
  "use strict";
77408
- var truncate3 = require_truncate_utf8_bytes();
77461
+ var truncate4 = require_truncate_utf8_bytes();
77409
77462
  var illegalRe = /[\/\?<>\\:\*\|"]/g;
77410
77463
  var controlRe = /[\x00-\x1f\x80-\x9f]/g;
77411
77464
  var reservedRe = /^\.+$/;
@@ -77421,7 +77474,7 @@ var require_sanitize_filename = __commonJS({
77421
77474
  }
77422
77475
  var sanitized = input.replace(illegalRe, replacement).replace(controlRe, replacement).replace(reservedRe, replacement).replace(windowsReservedRe, replacement);
77423
77476
  sanitized = replaceTrailingDotsAndSpaces(sanitized, replacement);
77424
- return truncate3(sanitized, 255);
77477
+ return truncate4(sanitized, 255);
77425
77478
  }
77426
77479
  module.exports = function(input, options2) {
77427
77480
  var replacement = options2 && options2.replacement || "";
@@ -599434,7 +599487,7 @@ function startIdleMemoryMaintenance(options2) {
599434
599487
  const now2 = Date.now();
599435
599488
  if (now2 - lastActivity < idleMs) return;
599436
599489
  if (now2 - lastRunAt < minIntervalMs) return;
599437
- worker2 = spawnWorker(options2.repoRoot);
599490
+ worker2 = spawnWorker(options2);
599438
599491
  if (!worker2) {
599439
599492
  lastRunAt = now2;
599440
599493
  return;
@@ -599463,7 +599516,8 @@ function startIdleMemoryMaintenance(options2) {
599463
599516
  }
599464
599517
  };
599465
599518
  }
599466
- function spawnWorker(repoRoot) {
599519
+ function spawnWorker(options2) {
599520
+ const repoRoot = options2.repoRoot;
599467
599521
  const stateDir = join120(repoRoot, ".omnius");
599468
599522
  const maintenanceDir = join120(stateDir, "memory-maintenance");
599469
599523
  mkdirSync64(maintenanceDir, { recursive: true });
@@ -599478,7 +599532,10 @@ function spawnWorker(repoRoot) {
599478
599532
  cwd: repoRoot,
599479
599533
  env: {
599480
599534
  ...process.env,
599481
- OMNIUS_MEMORY_MAINTENANCE_STATE_DIR: stateDir
599535
+ OMNIUS_MEMORY_MAINTENANCE_STATE_DIR: stateDir,
599536
+ // Active idle inference for prune — only enabled when a model is provided.
599537
+ ...options2.inferenceModel ? { OMNIUS_KG_PRUNE_MODEL: options2.inferenceModel } : {},
599538
+ ...options2.inferenceBackendUrl ? { OMNIUS_KG_PRUNE_BACKEND_URL: options2.inferenceBackendUrl } : {}
599482
599539
  },
599483
599540
  stdio: ["ignore", "pipe", "pipe"]
599484
599541
  });
@@ -605898,8 +605955,8 @@ var init_command_registry = __esm({
605898
605955
  "Run memory maintenance: import deferred memory, prune episodes, compact graph stores"
605899
605956
  ],
605900
605957
  [
605901
- "/prune [now|dry|aggressive|stats] [--target <n>]",
605902
- "Shrink knowledge.db: evict low-signal graph nodes (archived), then VACUUM. No arg opens the menu"
605958
+ "/prune [now|dry|infer|aggressive|stats] [--target <n>]",
605959
+ "Shrink knowledge.db: evict low-signal graph nodes (archived), then VACUUM. 'infer' = model-guided sleep consolidation. No arg opens the menu"
605903
605960
  ],
605904
605961
  ["/verbose", "Toggle verbose mode"],
605905
605962
  ["/clear", "Clear the screen"],
@@ -633702,6 +633759,88 @@ var init_mem_metabolize = __esm({
633702
633759
  }
633703
633760
  });
633704
633761
 
633762
+ // packages/cli/src/tui/commands/kg-prune-inference.ts
633763
+ function truncate3(text2, max) {
633764
+ const clean5 = text2.replace(/\s+/g, " ").trim();
633765
+ return clean5.length > max ? clean5.slice(0, max - 1) + "…" : clean5;
633766
+ }
633767
+ async function classifySignalNodes(candidates, cfg) {
633768
+ const max = Math.max(1, Math.min(cfg.maxCandidates ?? 40, 100));
633769
+ const sample = candidates.slice(0, max);
633770
+ if (sample.length === 0) return { reviewed: 0, keepIds: [], ok: true };
633771
+ if (!cfg.model || !cfg.backendUrl) {
633772
+ return { reviewed: 0, keepIds: [], ok: false, error: "no model/endpoint" };
633773
+ }
633774
+ const list = sample.map(
633775
+ (n2, i2) => `${i2 + 1}. id=${n2.id} type=${n2.nodeType} mentions=${n2.mentionCount} edges=${n2.degree} :: ${truncate3(n2.text, 160)}`
633776
+ ).join("\n");
633777
+ const system = 'You are the memory-consolidation process of an AI agent, running during idle time (like sleep). You are shown low-activity knowledge-graph nodes that are scheduled to be FORGOTTEN to keep memory bounded. Your job: rescue only the ones that are durable SIGNAL — stable facts, real entities, decisions, identities, or reusable knowledge the agent will likely need again. Let transient, redundant, trivial, or noisy nodes be forgotten. Reply with ONLY a JSON array of the id strings to KEEP, e.g. ["a1","b2"]. Keep the array short and selective. If none are worth keeping, reply []';
633778
+ const user = `Nodes scheduled for forgetting:
633779
+ ${list}
633780
+
633781
+ Return the JSON array of ids to KEEP.`;
633782
+ const timeoutMs = Math.max(3e3, cfg.timeoutMs ?? 3e4);
633783
+ try {
633784
+ const res = await fetch(`${cfg.backendUrl.replace(/\/$/, "")}/api/chat`, {
633785
+ method: "POST",
633786
+ headers: { "Content-Type": "application/json" },
633787
+ body: JSON.stringify({
633788
+ model: cfg.model,
633789
+ messages: [
633790
+ { role: "system", content: system },
633791
+ { role: "user", content: user }
633792
+ ],
633793
+ stream: false,
633794
+ think: false,
633795
+ options: { temperature: 0 },
633796
+ keep_alive: "5m"
633797
+ }),
633798
+ signal: AbortSignal.timeout(timeoutMs)
633799
+ });
633800
+ if (!res.ok) {
633801
+ return {
633802
+ reviewed: sample.length,
633803
+ keepIds: [],
633804
+ ok: false,
633805
+ error: `HTTP ${res.status}`
633806
+ };
633807
+ }
633808
+ const data = await res.json();
633809
+ const content = data.message?.content ?? data.response ?? "";
633810
+ const keepIds = parseIdArray(content, new Set(sample.map((n2) => n2.id)));
633811
+ return { reviewed: sample.length, keepIds, ok: true };
633812
+ } catch (err) {
633813
+ return {
633814
+ reviewed: sample.length,
633815
+ keepIds: [],
633816
+ ok: false,
633817
+ error: err instanceof Error ? err.message : String(err)
633818
+ };
633819
+ }
633820
+ }
633821
+ function parseIdArray(content, valid) {
633822
+ const match = content.match(/\[[\s\S]*?\]/);
633823
+ if (!match) return [];
633824
+ let parsed;
633825
+ try {
633826
+ parsed = JSON.parse(match[0]);
633827
+ } catch {
633828
+ return [];
633829
+ }
633830
+ if (!Array.isArray(parsed)) return [];
633831
+ const out = [];
633832
+ for (const item of parsed) {
633833
+ const id = typeof item === "string" ? item : String(item ?? "");
633834
+ if (id && valid.has(id) && !out.includes(id)) out.push(id);
633835
+ }
633836
+ return out;
633837
+ }
633838
+ var init_kg_prune_inference = __esm({
633839
+ "packages/cli/src/tui/commands/kg-prune-inference.ts"() {
633840
+ "use strict";
633841
+ }
633842
+ });
633843
+
633705
633844
  // packages/cli/src/tui/commands/kg-prune.ts
633706
633845
  var kg_prune_exports = {};
633707
633846
  __export(kg_prune_exports, {
@@ -633709,7 +633848,8 @@ __export(kg_prune_exports, {
633709
633848
  formatBytes: () => formatBytes10,
633710
633849
  formatKgStatsLine: () => formatKgStatsLine,
633711
633850
  knowledgeGraphStats: () => knowledgeGraphStats,
633712
- pruneKnowledgeGraph: () => pruneKnowledgeGraph
633851
+ pruneKnowledgeGraph: () => pruneKnowledgeGraph,
633852
+ pruneKnowledgeGraphWithInference: () => pruneKnowledgeGraphWithInference
633713
633853
  });
633714
633854
  import { existsSync as existsSync130, statSync as statSync49 } from "node:fs";
633715
633855
  import { join as join142 } from "node:path";
@@ -633772,6 +633912,7 @@ function pruneKnowledgeGraph(opts) {
633772
633912
  vacuum: true,
633773
633913
  graphTargetNodes: target,
633774
633914
  graphArchive: opts.archive ?? true,
633915
+ graphProtectNodeIds: opts.protectNodeIds,
633775
633916
  maxGraphNodes: 12e3,
633776
633917
  maxGraphDeletes: opts.maxDeletes ?? 5e3,
633777
633918
  maxRuntimeMs: opts.maxRuntimeMs ?? 10 * 6e4
@@ -633793,6 +633934,47 @@ function pruneKnowledgeGraph(opts) {
633793
633934
  ].filter(Boolean).join("\n");
633794
633935
  return { report: report2, result };
633795
633936
  }
633937
+ async function pruneKnowledgeGraphWithInference(opts) {
633938
+ const stateDir = stateDirFor(opts.workingDir);
633939
+ const knowledgePath = join142(stateDir, "knowledge.db");
633940
+ if (!existsSync130(knowledgePath)) {
633941
+ return {
633942
+ report: `No knowledge.db found at ${stateDir}. Nothing to prune.`,
633943
+ result: null
633944
+ };
633945
+ }
633946
+ const target = opts.targetNodes ?? DEFAULT_KG_TARGET_NODES;
633947
+ const candidates = selectGraphPruneCandidates(knowledgePath, {
633948
+ stateDir,
633949
+ graphTargetNodes: target,
633950
+ maxGraphDeletes: opts.maxDeletes ?? 5e3
633951
+ });
633952
+ let inferenceLine = " Inference: no candidates over target — nothing to review.";
633953
+ let protectIds = [];
633954
+ if (candidates.length > 0) {
633955
+ const cls = await classifySignalNodes(candidates, {
633956
+ backendUrl: opts.backendUrl,
633957
+ model: opts.model,
633958
+ maxCandidates: opts.maxCandidates ?? 40,
633959
+ timeoutMs: opts.timeoutMs
633960
+ });
633961
+ if (cls.ok) {
633962
+ protectIds = cls.keepIds;
633963
+ if (!opts.dryRun && protectIds.length > 0) {
633964
+ reinforceGraphNodes(knowledgePath, protectIds);
633965
+ }
633966
+ inferenceLine = ` Inference: reviewed ${cls.reviewed} candidates · kept ${protectIds.length} as signal (reinforced${opts.dryRun ? " skipped (dry-run)" : ""}).`;
633967
+ } else {
633968
+ inferenceLine = ` Inference: unavailable (${cls.error ?? "error"}) — mechanical prune only.`;
633969
+ }
633970
+ }
633971
+ const { report: report2, result } = pruneKnowledgeGraph({
633972
+ ...opts,
633973
+ protectNodeIds: protectIds
633974
+ });
633975
+ return { report: `${report2}
633976
+ ${inferenceLine}`, result };
633977
+ }
633796
633978
  function formatBytes10(bytes) {
633797
633979
  if (bytes < 1024) return `${bytes} B`;
633798
633980
  const units = ["KB", "MB", "GB", "TB"];
@@ -633814,6 +633996,7 @@ var init_kg_prune = __esm({
633814
633996
  "packages/cli/src/tui/commands/kg-prune.ts"() {
633815
633997
  "use strict";
633816
633998
  init_dist();
633999
+ init_kg_prune_inference();
633817
634000
  DEFAULT_KG_TARGET_NODES = Number(
633818
634001
  process.env["OMNIUS_KG_TARGET_NODES"] ?? 5e3
633819
634002
  );
@@ -633851,6 +634034,13 @@ async function showPruneMenu(options2) {
633851
634034
  label: import_chalk2.default.yellow("Aggressive full prune"),
633852
634035
  detail: "Larger delete budget + longer time budget — for a first cleanup of a large DB."
633853
634036
  },
634037
+ ...options2.model ? [
634038
+ {
634039
+ key: "infer",
634040
+ label: import_chalk2.default.blueBright("Sleep consolidation (inference)"),
634041
+ detail: "Ask the model which about-to-be-forgotten nodes are signal; reinforce those, forget the rest."
634042
+ }
634043
+ ] : [],
633854
634044
  { key: "sep1", label: import_chalk2.default.gray("─".repeat(34)) },
633855
634045
  {
633856
634046
  key: "stats",
@@ -633892,6 +634082,19 @@ async function showPruneMenu(options2) {
633892
634082
  renderInfo(report2);
633893
634083
  break;
633894
634084
  }
634085
+ case "infer": {
634086
+ if (!options2.model) break;
634087
+ renderInfo(
634088
+ "Sleep consolidation — reviewing low-signal nodes with the model…"
634089
+ );
634090
+ const { report: report2 } = await pruneKnowledgeGraphWithInference({
634091
+ workingDir,
634092
+ model: options2.model,
634093
+ backendUrl: options2.backendUrl ?? "http://127.0.0.1:11434"
634094
+ });
634095
+ renderInfo(report2);
634096
+ break;
634097
+ }
633895
634098
  case "stats": {
633896
634099
  renderInfo(formatKgStatsLine(knowledgeGraphStats(workingDir)));
633897
634100
  await showPruneMenu(options2);
@@ -643287,7 +643490,12 @@ async function handleSlashCommand(input, ctx3) {
643287
643490
  } = await Promise.resolve().then(() => (init_kg_prune(), kg_prune_exports));
643288
643491
  if (!sub2 || sub2 === "menu") {
643289
643492
  const { showPruneMenu: showPruneMenu2 } = await Promise.resolve().then(() => (init_prune_menu(), prune_menu_exports));
643290
- await showPruneMenu2({ rl: ctx3.rl, workingDir });
643493
+ await showPruneMenu2({
643494
+ rl: ctx3.rl,
643495
+ workingDir,
643496
+ model: ctx3.config.model,
643497
+ backendUrl: ctx3.config.backendUrl
643498
+ });
643291
643499
  return "handled";
643292
643500
  }
643293
643501
  if (sub2 === "stats") {
@@ -643312,6 +643520,18 @@ async function handleSlashCommand(input, ctx3) {
643312
643520
  );
643313
643521
  return "handled";
643314
643522
  }
643523
+ if (sub2 === "infer" || sub2 === "inference" || sub2 === "sleep") {
643524
+ const { pruneKnowledgeGraphWithInference: pruneKnowledgeGraphWithInference2 } = await Promise.resolve().then(() => (init_kg_prune(), kg_prune_exports));
643525
+ renderInfo("Sleep consolidation — reviewing low-signal nodes with the model…");
643526
+ const { report: report2 } = await pruneKnowledgeGraphWithInference2({
643527
+ workingDir,
643528
+ targetNodes,
643529
+ model: ctx3.config.model,
643530
+ backendUrl: ctx3.config.backendUrl
643531
+ });
643532
+ renderInfo(report2);
643533
+ return "handled";
643534
+ }
643315
643535
  renderInfo("Pruning knowledge graph…");
643316
643536
  renderInfo(pruneKnowledgeGraph2({ workingDir, targetNodes }).report);
643317
643537
  return "handled";
@@ -721598,12 +721818,12 @@ function getRuntimeToolProfilePolicy(repoRoot) {
721598
721818
  return profile ? profilePolicy(profile) : void 0;
721599
721819
  }
721600
721820
  function formatSubAgentEventForView(event) {
721601
- const truncate3 = (s2, n2) => s2.length > n2 ? `${s2.slice(0, n2)}…` : s2;
721821
+ const truncate4 = (s2, n2) => s2.length > n2 ? `${s2.slice(0, n2)}…` : s2;
721602
721822
  switch (event.type) {
721603
721823
  case "tool_call": {
721604
721824
  let args = "";
721605
721825
  try {
721606
- args = event.toolArgs ? truncate3(JSON.stringify(event.toolArgs), 120) : "";
721826
+ args = event.toolArgs ? truncate4(JSON.stringify(event.toolArgs), 120) : "";
721607
721827
  } catch {
721608
721828
  args = "";
721609
721829
  }
@@ -721612,24 +721832,24 @@ function formatSubAgentEventForView(event) {
721612
721832
  case "tool_result": {
721613
721833
  const mark = event.success === false ? "✗" : "✓";
721614
721834
  const body = (event.content ?? "").split("\n").slice(0, 3).join(" ").trim();
721615
- return `${mark} ${event.toolName ?? "tool"}${body ? `: ${truncate3(body, 300)}` : ""}`;
721835
+ return `${mark} ${event.toolName ?? "tool"}${body ? `: ${truncate4(body, 300)}` : ""}`;
721616
721836
  }
721617
721837
  case "assistant_text":
721618
721838
  case "model_response": {
721619
721839
  const t2 = (event.content ?? "").trim();
721620
- return t2 ? truncate3(t2, 1e3) : null;
721840
+ return t2 ? truncate4(t2, 1e3) : null;
721621
721841
  }
721622
721842
  case "status": {
721623
721843
  const t2 = (event.content ?? "").trim();
721624
- return t2 ? `· ${truncate3(t2, 300)}` : null;
721844
+ return t2 ? `· ${truncate4(t2, 300)}` : null;
721625
721845
  }
721626
721846
  case "error": {
721627
721847
  const t2 = (event.content ?? "").trim();
721628
- return t2 ? `✗ ${truncate3(t2, 400)}` : null;
721848
+ return t2 ? `✗ ${truncate4(t2, 400)}` : null;
721629
721849
  }
721630
721850
  case "complete": {
721631
721851
  const t2 = (event.content ?? "").trim();
721632
- return t2 ? `✓ ${truncate3(t2, 400)}` : null;
721852
+ return t2 ? `✓ ${truncate4(t2, 400)}` : null;
721633
721853
  }
721634
721854
  default:
721635
721855
  return null;
@@ -723739,45 +723959,49 @@ ${entry.fullContent}`
723739
723959
  `
723740
723960
  );
723741
723961
  } else {
723742
- contentWrite(() => {
723743
- if (voice?.enabled && (voice.voiceMode === "action" || voice.voiceMode === "verbose")) {
723744
- const emoState = emotionEngine?.getState();
723745
- const emoCtx = emoState ? {
723746
- valence: emoState.valence,
723747
- arousal: emoState.arousal,
723748
- label: emoState.label,
723749
- emoji: emoState.emoji
723750
- } : void 0;
723751
- const desc = describeToolCall(
723752
- event.toolName ?? "unknown",
723753
- event.toolArgs ?? {},
723754
- vLevel,
723755
- emoCtx,
723756
- isStark
723757
- );
723758
- renderVoiceText(desc);
723759
- voice.speakSubordinate(desc, emoCtx);
723760
- }
723761
- renderToolCallStart(
723762
- event.toolName ?? "unknown",
723763
- event.toolArgs ?? {},
723764
- config.verbose
723765
- );
723766
- const liveShellStatusBar = statusBar;
723767
- if (event.toolName === "shell" && liveShellStatusBar?.isActive) {
723768
- const command = String(event.toolArgs?.command ?? "");
723769
- const state = createShellLiveBlockState(command);
723770
- const id = `shell-live-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
723771
- liveShellBlock = { id, state, repaintTimer: null };
723772
- lastLiveShellPaint = "";
723773
- liveShellStatusBar.registerDynamicBlock(
723774
- id,
723775
- (width) => buildShellLiveBlockLines(state, width)
723776
- );
723777
- liveShellStatusBar.appendDynamicBlock(id);
723778
- scheduleLiveShellRepaint();
723779
- }
723780
- });
723962
+ renderToolCallStart(
723963
+ event.toolName ?? "unknown",
723964
+ event.toolArgs ?? {},
723965
+ config.verbose
723966
+ );
723967
+ const wantsVoice = !!voice?.enabled && (voice.voiceMode === "action" || voice.voiceMode === "verbose");
723968
+ const wantsShellLive = event.toolName === "shell" && !!statusBar?.isActive;
723969
+ if (wantsVoice || wantsShellLive) {
723970
+ contentWrite(() => {
723971
+ if (wantsVoice && voice) {
723972
+ const emoState = emotionEngine?.getState();
723973
+ const emoCtx = emoState ? {
723974
+ valence: emoState.valence,
723975
+ arousal: emoState.arousal,
723976
+ label: emoState.label,
723977
+ emoji: emoState.emoji
723978
+ } : void 0;
723979
+ const desc = describeToolCall(
723980
+ event.toolName ?? "unknown",
723981
+ event.toolArgs ?? {},
723982
+ vLevel,
723983
+ emoCtx,
723984
+ isStark
723985
+ );
723986
+ renderVoiceText(desc);
723987
+ voice.speakSubordinate(desc, emoCtx);
723988
+ }
723989
+ const liveShellStatusBar = statusBar;
723990
+ if (wantsShellLive && liveShellStatusBar) {
723991
+ const command = String(event.toolArgs?.command ?? "");
723992
+ const state = createShellLiveBlockState(command);
723993
+ const id = `shell-live-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
723994
+ liveShellBlock = { id, state, repaintTimer: null };
723995
+ lastLiveShellPaint = "";
723996
+ liveShellStatusBar.registerDynamicBlock(
723997
+ id,
723998
+ (width) => buildShellLiveBlockLines(state, width)
723999
+ );
724000
+ liveShellStatusBar.appendDynamicBlock(id);
724001
+ scheduleLiveShellRepaint();
724002
+ }
724003
+ });
724004
+ }
723781
724005
  }
723782
724006
  if (event.toolName) sessionToolsUsed.add(event.toolName);
723783
724007
  break;
@@ -725700,7 +725924,12 @@ Rationale: ${proposal.rationale}${provenanceNote}${dmnDevDiscipline(proposal.cat
725700
725924
  if (!summary) return;
725701
725925
  writeContent(() => renderInfo(summary));
725702
725926
  if (!activeTask) showPrompt();
725703
- }
725927
+ },
725928
+ // Active idle inference for KG prune ("sleep consolidation"). Uses the
725929
+ // configured backend/model to decide which about-to-be-forgotten nodes are
725930
+ // signal. Disable with OMNIUS_KG_PRUNE_INFERENCE=0.
725931
+ inferenceModel: config.model,
725932
+ inferenceBackendUrl: config.backendUrl
725704
725933
  });
725705
725934
  const setActiveTaskContextWindowSize = (size) => {
725706
725935
  const runner = activeTask?.runner;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.396",
3
+ "version": "1.0.398",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.396",
9
+ "version": "1.0.398",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.396",
3
+ "version": "1.0.398",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",