open-agents-ai 0.105.3 → 0.105.5

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/index.js +121 -51
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12303,6 +12303,49 @@ function writeResp(id, result) {
12303
12303
  try { writeFileSync(respFile, JSON.stringify({ id, ...result }, null, 2)); } catch {}
12304
12304
  }
12305
12305
 
12306
+ // Collect CPU/GPU/memory metrics for piggybacking on inference responses.
12307
+ // Avoids separate invoke_capability round-trip that clogs the IPC.
12308
+ var _sysMetricsCache = null;
12309
+ var _sysMetricsCacheTs = 0;
12310
+ async function _collectSysMetrics() {
12311
+ // Cache for 5s \u2014 nvidia-smi is expensive, inference can fire rapidly
12312
+ var now = Date.now();
12313
+ if (_sysMetricsCache && (now - _sysMetricsCacheTs) < 5000) return _sysMetricsCache;
12314
+ try {
12315
+ var os = await import('node:os');
12316
+ var loads = os.loadavg();
12317
+ var cores = os.cpus().length;
12318
+ var totalMem = os.totalmem();
12319
+ var freeMem = os.freemem();
12320
+ var usedMem = totalMem - freeMem;
12321
+ var cpuModel = '';
12322
+ try { var cpuArr = os.cpus(); if (cpuArr.length > 0) cpuModel = cpuArr[0].model || ''; } catch {}
12323
+ var gpuInfo = { available: false, name: '', utilization: 0, vramUsedMB: 0, vramTotalMB: 0, vramUtilization: 0 };
12324
+ try {
12325
+ var cp = await import('node:child_process');
12326
+ var smiOut = cp.execSync('nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total,name --format=csv,noheader,nounits 2>/dev/null', { encoding: 'utf8', timeout: 3000 });
12327
+ var smiLine = smiOut.trim().split('\\n')[0];
12328
+ if (smiLine) {
12329
+ var sp = smiLine.split(',').map(function(s) { return s.trim(); });
12330
+ gpuInfo.available = true;
12331
+ gpuInfo.utilization = parseInt(sp[0] || '0', 10) || 0;
12332
+ gpuInfo.vramUsedMB = parseInt(sp[1] || '0', 10) || 0;
12333
+ gpuInfo.vramTotalMB = parseInt(sp[2] || '0', 10) || 0;
12334
+ gpuInfo.name = sp[3] || '';
12335
+ gpuInfo.vramUtilization = gpuInfo.vramTotalMB > 0 ? Math.round((gpuInfo.vramUsedMB / gpuInfo.vramTotalMB) * 100) : 0;
12336
+ }
12337
+ } catch {}
12338
+ _sysMetricsCache = {
12339
+ cpu: { utilization: Math.min(100, Math.round((loads[0] / cores) * 100)), cores: cores, model: cpuModel },
12340
+ memory: { utilization: Math.round((usedMem / totalMem) * 100), totalGB: Math.round((totalMem / (1024*1024*1024)) * 10) / 10, usedGB: Math.round((usedMem / (1024*1024*1024)) * 10) / 10 },
12341
+ gpu: gpuInfo,
12342
+ timestamp: new Date().toISOString(),
12343
+ };
12344
+ _sysMetricsCacheTs = now;
12345
+ return _sysMetricsCache;
12346
+ } catch { return null; }
12347
+ }
12348
+
12306
12349
  async function handleCmd(cmd) {
12307
12350
  const { id, action, args } = cmd;
12308
12351
  dlog('handleCmd: action=' + action + ' id=' + id);
@@ -12684,7 +12727,7 @@ async function handleCmd(cmd) {
12684
12727
  riTokenContent += tkn;
12685
12728
  appendFileSync(riStreamFile, JSON.stringify({ type: 'token', content: tkn }) + '\\n');
12686
12729
  } else if (riEvt.event === 'result' && riEvt.data) {
12687
- // Final metadata event \u2014 parse for usage/tool_calls
12730
+ // Final metadata event \u2014 parse for usage/tool_calls/system
12688
12731
  try {
12689
12732
  var riMeta = typeof riEvt.data === 'string' ? JSON.parse(riEvt.data) : riEvt.data;
12690
12733
  if (riMeta.usage) {
@@ -12694,6 +12737,8 @@ async function handleCmd(cmd) {
12694
12737
  if (riMeta.choices && riMeta.choices[0] && riMeta.choices[0].message && riMeta.choices[0].message.tool_calls) {
12695
12738
  riToolCalls = riMeta.choices[0].message.tool_calls;
12696
12739
  }
12740
+ // Pass through provider system metrics
12741
+ if (riMeta.system) var riSystemMetrics = riMeta.system;
12697
12742
  } catch {}
12698
12743
  }
12699
12744
  }
@@ -12712,8 +12757,13 @@ async function handleCmd(cmd) {
12712
12757
  },
12713
12758
  }],
12714
12759
  usage: { input_tokens: riInputTokens, output_tokens: riOutputTokens },
12760
+ system: typeof riSystemMetrics !== 'undefined' ? riSystemMetrics : undefined,
12715
12761
  };
12716
12762
  appendFileSync(riStreamFile, JSON.stringify({ type: 'done', result: JSON.stringify(riFinalPayload) }) + '\\n');
12763
+ // Write piggybacked system metrics to file for status bar
12764
+ if (typeof riSystemMetrics !== 'undefined' && riSystemMetrics) {
12765
+ try { writeFileSync(join(nexusDir, 'remote-metrics.json'), JSON.stringify({ ts: Date.now(), data: riSystemMetrics })); } catch {}
12766
+ }
12717
12767
  dlog('remote_infer: stream complete, tokens=' + riTokenContent.length + ' in=' + riInputTokens + ' out=' + riOutputTokens);
12718
12768
  break;
12719
12769
  }
@@ -12746,6 +12796,13 @@ async function handleCmd(cmd) {
12746
12796
  result: riResult,
12747
12797
  };
12748
12798
  writeResp(id, { ok: true, output: JSON.stringify(riOutput, null, 2) });
12799
+ // Write piggybacked system metrics to file for status bar (avoids IPC invoke)
12800
+ try {
12801
+ var riParsedForSys = typeof riResult === 'string' ? JSON.parse(riResult) : riResult;
12802
+ if (riParsedForSys && riParsedForSys.system) {
12803
+ writeFileSync(join(nexusDir, 'remote-metrics.json'), JSON.stringify({ ts: Date.now(), data: riParsedForSys.system }));
12804
+ }
12805
+ } catch {}
12749
12806
  }
12750
12807
  break;
12751
12808
  }
@@ -13346,6 +13403,13 @@ async function handleCmd(cmd) {
13346
13403
  };
13347
13404
  }
13348
13405
 
13406
+ // Attach system metrics to response \u2014 clients get CPU/GPU/RAM
13407
+ // for free without a separate invoke_capability round-trip
13408
+ try {
13409
+ var _sm = await _collectSysMetrics();
13410
+ if (_sm) responsePayload.system = _sm;
13411
+ } catch {}
13412
+
13349
13413
  // Stream result back
13350
13414
  await swrite({
13351
13415
  type: 'invoke.event', version: 1,
@@ -21479,6 +21543,9 @@ var init_nexusBackend = __esm({
21479
21543
  consecutiveFailures = 0;
21480
21544
  static MAX_CONSECUTIVE_FAILURES = 3;
21481
21545
  thinking;
21546
+ /** System metrics piggybacked on inference responses from the provider */
21547
+ lastSystemMetrics = null;
21548
+ lastSystemMetricsTs = 0;
21482
21549
  constructor(sendFn, model, targetPeer, authKey, thinking) {
21483
21550
  this.sendFn = sendFn;
21484
21551
  this.model = model;
@@ -21561,6 +21628,10 @@ var init_nexusBackend = __esm({
21561
21628
  if (!responseData) {
21562
21629
  throw new Error("No response data from remote inference");
21563
21630
  }
21631
+ if (responseData.system && typeof responseData.system === "object") {
21632
+ this.lastSystemMetrics = responseData.system;
21633
+ this.lastSystemMetricsTs = Date.now();
21634
+ }
21564
21635
  const choices = responseData.choices;
21565
21636
  if (choices && Array.isArray(choices)) {
21566
21637
  return {
@@ -21679,6 +21750,10 @@ var init_nexusBackend = __esm({
21679
21750
  }
21680
21751
  }
21681
21752
  }
21753
+ if (responseData?.system && typeof responseData.system === "object") {
21754
+ this.lastSystemMetrics = responseData.system;
21755
+ this.lastSystemMetricsTs = Date.now();
21756
+ }
21682
21757
  const content = responseData ? responseData.response || "" : "";
21683
21758
  if (content) {
21684
21759
  const words = content.split(/(\s+)/);
@@ -21781,6 +21856,10 @@ var init_nexusBackend = __esm({
21781
21856
  }
21782
21857
  };
21783
21858
  }
21859
+ if (r.system && typeof r.system === "object") {
21860
+ this.lastSystemMetrics = r.system;
21861
+ this.lastSystemMetricsTs = Date.now();
21862
+ }
21784
21863
  } catch {
21785
21864
  }
21786
21865
  }
@@ -31387,7 +31466,7 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
31387
31466
  async function fetchPeerModels(peerId, authKey) {
31388
31467
  try {
31389
31468
  const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
31390
- const { existsSync: existsSync45, readFileSync: readFileSync32 } = await import("node:fs");
31469
+ const { existsSync: existsSync45, readFileSync: readFileSync33 } = await import("node:fs");
31391
31470
  const { join: join57 } = await import("node:path");
31392
31471
  const cwd4 = process.cwd();
31393
31472
  const nexusTool = new NexusTool2(cwd4);
@@ -31396,7 +31475,7 @@ async function fetchPeerModels(peerId, authKey) {
31396
31475
  try {
31397
31476
  const statusPath = join57(nexusDir, "status.json");
31398
31477
  if (existsSync45(statusPath)) {
31399
- const status = JSON.parse(readFileSync32(statusPath, "utf8"));
31478
+ const status = JSON.parse(readFileSync33(statusPath, "utf8"));
31400
31479
  if (status.peerId === peerId)
31401
31480
  isLocalPeer = true;
31402
31481
  }
@@ -31406,7 +31485,7 @@ async function fetchPeerModels(peerId, authKey) {
31406
31485
  const pricingPath = join57(nexusDir, "pricing.json");
31407
31486
  if (existsSync45(pricingPath)) {
31408
31487
  try {
31409
- const pricing = JSON.parse(readFileSync32(pricingPath, "utf8"));
31488
+ const pricing = JSON.parse(readFileSync33(pricingPath, "utf8"));
31410
31489
  const localModels = (pricing.models || []).map((m) => ({
31411
31490
  name: m.model || "unknown",
31412
31491
  size: m.parameterSize || "",
@@ -31423,7 +31502,7 @@ async function fetchPeerModels(peerId, authKey) {
31423
31502
  const cachePath = join57(nexusDir, "peer-models-cache.json");
31424
31503
  if (existsSync45(cachePath)) {
31425
31504
  try {
31426
- const cache4 = JSON.parse(readFileSync32(cachePath, "utf8"));
31505
+ const cache4 = JSON.parse(readFileSync33(cachePath, "utf8"));
31427
31506
  if (cache4.peerId === peerId && cache4.models?.length > 0) {
31428
31507
  const age = Date.now() - new Date(cache4.cachedAt).getTime();
31429
31508
  if (age < 5 * 60 * 1e3) {
@@ -31541,7 +31620,7 @@ async function fetchPeerModels(peerId, authKey) {
31541
31620
  const pricingPath = join57(nexusDir, "pricing.json");
31542
31621
  if (existsSync45(pricingPath)) {
31543
31622
  try {
31544
- const pricing = JSON.parse(readFileSync32(pricingPath, "utf8"));
31623
+ const pricing = JSON.parse(readFileSync33(pricingPath, "utf8"));
31545
31624
  return (pricing.models || []).map((m) => ({
31546
31625
  name: m.model || "unknown",
31547
31626
  size: m.parameterSize || "",
@@ -47760,6 +47839,7 @@ var init_system_metrics = __esm({
47760
47839
  });
47761
47840
 
47762
47841
  // packages/cli/dist/tui/status-bar.js
47842
+ import { readFileSync as readFileSync31 } from "node:fs";
47763
47843
  var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, StatusBar;
47764
47844
  var init_status_bar = __esm({
47765
47845
  "packages/cli/dist/tui/status-bar.js"() {
@@ -48319,7 +48399,7 @@ var init_status_bar = __esm({
48319
48399
  * Queries the remote peer's system_metrics capability for CPU/GPU data,
48320
48400
  * with fallback to local metering data.
48321
48401
  */
48322
- startPeerMetricsPolling(sendCommand, peerId, authKey) {
48402
+ startPeerMetricsPolling(sendCommand, peerId, authKey, nexusDir) {
48323
48403
  this.stopRemoteMetricsPolling();
48324
48404
  this._metricsCollector.startRemote((m) => {
48325
48405
  this._unifiedMetrics = m;
@@ -48392,48 +48472,38 @@ var init_status_bar = __esm({
48392
48472
  pollInFlight = true;
48393
48473
  try {
48394
48474
  pollAttempt++;
48395
- try {
48396
- const queryData = { type: "query" };
48397
- if (authKey)
48398
- queryData.auth_key = authKey;
48399
- const raw = await sendCommand("invoke_capability", {
48400
- target_peer: peerId,
48401
- capability: "system_metrics",
48402
- input: queryData
48403
- }, 15e3);
48404
- if (typeof raw === "string" && raw.startsWith("Invoke error:")) {
48405
- lastPeerMetricsDebug = `invoke error: ${raw.slice(0, 200)}`;
48406
- throw new Error(raw);
48407
- }
48408
- const metricsData = extractMetrics(raw);
48409
- if (metricsData?.cpu) {
48410
- lastPeerMetricsDebug = `ok: cpu=${metricsData.cpu.utilization}%`;
48411
- this.setRemoteMetrics({
48412
- cpuUtil: metricsData.cpu?.utilization ?? 0,
48413
- cpuCores: metricsData.cpu?.cores ?? 0,
48414
- cpuModel: metricsData.cpu?.model ?? "",
48415
- gpuUtil: metricsData.gpu?.available ? metricsData.gpu.utilization ?? 0 : -1,
48416
- gpuName: metricsData.gpu?.name ?? "",
48417
- vramUtil: metricsData.gpu?.available ? metricsData.gpu.vramUtilization ?? 0 : -1,
48418
- vramUsedMB: metricsData.gpu?.vramUsedMB ?? 0,
48419
- vramTotalMB: metricsData.gpu?.vramTotalMB ?? 0,
48420
- memUtil: metricsData.memory?.utilization ?? 0,
48421
- memTotalGB: metricsData.memory?.totalGB ?? 0,
48422
- memUsedGB: metricsData.memory?.usedGB ?? 0
48423
- });
48424
- return;
48425
- }
48426
- lastPeerMetricsDebug = `parse fail: ${typeof raw === "string" ? raw.slice(0, 300) : "non-string"}`;
48427
- } catch (e) {
48428
- if (!lastPeerMetricsDebug.startsWith("invoke error:")) {
48429
- lastPeerMetricsDebug = `exception: ${e instanceof Error ? e.message.slice(0, 200) : String(e).slice(0, 200)}`;
48475
+ if (nexusDir) {
48476
+ try {
48477
+ const metricsPath = nexusDir + "/remote-metrics.json";
48478
+ const raw = readFileSync31(metricsPath, "utf8");
48479
+ const cached = JSON.parse(raw);
48480
+ if (cached && cached.ts && Date.now() - cached.ts < 6e4) {
48481
+ const m = cached.data;
48482
+ if (m?.cpu) {
48483
+ lastPeerMetricsDebug = `ok: cpu=${m.cpu?.utilization}%`;
48484
+ this.setRemoteMetrics({
48485
+ cpuUtil: m.cpu?.utilization ?? 0,
48486
+ cpuCores: m.cpu?.cores ?? 0,
48487
+ cpuModel: m.cpu?.model ?? "",
48488
+ gpuUtil: m.gpu?.available ? m.gpu.utilization ?? 0 : -1,
48489
+ gpuName: m.gpu?.name ?? "",
48490
+ vramUtil: m.gpu?.available ? m.gpu.vramUtilization ?? 0 : -1,
48491
+ vramUsedMB: m.gpu?.vramUsedMB ?? 0,
48492
+ vramTotalMB: m.gpu?.vramTotalMB ?? 0,
48493
+ memUtil: m.memory?.utilization ?? 0,
48494
+ memTotalGB: m.memory?.totalGB ?? 0,
48495
+ memUsedGB: m.memory?.usedGB ?? 0
48496
+ });
48497
+ return;
48498
+ }
48499
+ }
48500
+ } catch {
48430
48501
  }
48431
48502
  }
48432
48503
  this.setRemoteMetrics({
48433
48504
  cpuUtil: -1,
48434
- // -1 = unavailable (won't render bar)
48435
48505
  gpuUtil: -1,
48436
- gpuName: pollAttempt <= 5 ? "connecting..." : `peer (${lastPeerMetricsDebug.slice(0, 40)})`,
48506
+ gpuName: "peer (send a message to get metrics)",
48437
48507
  vramUtil: -1,
48438
48508
  memUtil: -1
48439
48509
  });
@@ -49280,7 +49350,7 @@ import { cwd } from "node:process";
49280
49350
  import { resolve as resolve29, join as join52, dirname as dirname18, extname as extname10 } from "node:path";
49281
49351
  import { createRequire as createRequire2 } from "node:module";
49282
49352
  import { fileURLToPath as fileURLToPath12 } from "node:url";
49283
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync18, appendFileSync as appendFileSync4, rmSync as rmSync2, readdirSync as readdirSync16, mkdirSync as mkdirSync19 } from "node:fs";
49353
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync18, appendFileSync as appendFileSync4, rmSync as rmSync2, readdirSync as readdirSync16, mkdirSync as mkdirSync19 } from "node:fs";
49284
49354
  import { existsSync as existsSync43 } from "node:fs";
49285
49355
  import { homedir as homedir13 } from "node:os";
49286
49356
  function formatTimeAgo(date) {
@@ -49521,7 +49591,7 @@ function gatherMemorySnippets(root) {
49521
49591
  continue;
49522
49592
  try {
49523
49593
  for (const f of readdirSync16(dir).filter((f2) => f2.endsWith(".json"))) {
49524
- const data = JSON.parse(readFileSync31(join52(dir, f), "utf-8"));
49594
+ const data = JSON.parse(readFileSync32(join52(dir, f), "utf-8"));
49525
49595
  for (const val of Object.values(data)) {
49526
49596
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
49527
49597
  if (v.length > 10)
@@ -50700,7 +50770,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
50700
50770
  let savedHistory = [];
50701
50771
  try {
50702
50772
  if (existsSync43(HISTORY_FILE)) {
50703
- const raw = readFileSync31(HISTORY_FILE, "utf8").trim();
50773
+ const raw = readFileSync32(HISTORY_FILE, "utf8").trim();
50704
50774
  if (raw)
50705
50775
  savedHistory = raw.split("\n").reverse();
50706
50776
  }
@@ -50722,7 +50792,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
50722
50792
  mkdirSync19(HISTORY_DIR, { recursive: true });
50723
50793
  appendFileSync4(HISTORY_FILE, line + "\n", "utf8");
50724
50794
  if (Math.random() < 0.02) {
50725
- const all = readFileSync31(HISTORY_FILE, "utf8").trim().split("\n");
50795
+ const all = readFileSync32(HISTORY_FILE, "utf8").trim().split("\n");
50726
50796
  if (all.length > MAX_HISTORY_LINES) {
50727
50797
  writeFileSync18(HISTORY_FILE, all.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
50728
50798
  }
@@ -50961,7 +51031,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
50961
51031
  await new Promise((r) => setTimeout(r, 2e3));
50962
51032
  } catch {
50963
51033
  }
50964
- statusBar.startPeerMetricsPolling(nexusTool.sendCommand.bind(nexusTool), peerId, currentConfig.apiKey);
51034
+ statusBar.startPeerMetricsPolling(nexusTool.sendCommand.bind(nexusTool), peerId, currentConfig.apiKey, nexusTool.getNexusDir());
50965
51035
  } else if (isRemote) {
50966
51036
  statusBar.startRemoteMetricsPolling(currentConfig.backendUrl, currentConfig.apiKey);
50967
51037
  }
@@ -51104,7 +51174,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
51104
51174
  if (isPeer) {
51105
51175
  const peerId = url.slice(7);
51106
51176
  const nexusTool = new NexusTool(repoRoot);
51107
- statusBar.startPeerMetricsPolling(nexusTool.sendCommand.bind(nexusTool), peerId, apiKey);
51177
+ statusBar.startPeerMetricsPolling(nexusTool.sendCommand.bind(nexusTool), peerId, apiKey, nexusTool.getNexusDir());
51108
51178
  } else if (isRemote) {
51109
51179
  statusBar.startRemoteMetricsPolling(url, apiKey);
51110
51180
  } else {
@@ -52413,7 +52483,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
52413
52483
  if (isImage) {
52414
52484
  try {
52415
52485
  const imgPath = resolve29(repoRoot, cleanPath);
52416
- const imgBuffer = readFileSync31(imgPath);
52486
+ const imgBuffer = readFileSync32(imgPath);
52417
52487
  const base64 = imgBuffer.toString("base64");
52418
52488
  const ext = extname10(cleanPath).toLowerCase();
52419
52489
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.105.3",
3
+ "version": "0.105.5",
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",