open-agents-ai 0.103.53 → 0.103.55

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 +235 -60
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12071,7 +12071,7 @@ var init_nexus = __esm({
12071
12071
  * Usage: node nexus-daemon.mjs <nexus-dir> <agent-name> [agent-type]
12072
12072
  */
12073
12073
 
12074
- import { NexusClient, createFileAuditHook } from 'open-agents-nexus';
12074
+ import { NexusClient } from 'open-agents-nexus';
12075
12075
  import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, appendFileSync, watch as fsWatch } from 'node:fs';
12076
12076
  import { join } from 'node:path';
12077
12077
 
@@ -12961,6 +12961,24 @@ async function handleCmd(cmd) {
12961
12961
  tokens: inputTokens + outputTokens,
12962
12962
  },
12963
12963
  });
12964
+
12965
+ // Write metering record directly (createFileAuditHook does not capture token data)
12966
+ try {
12967
+ appendFileSync(meteringFile, JSON.stringify({
12968
+ timestamp: Date.now(),
12969
+ peerId: request.from || 'unknown',
12970
+ service: capName,
12971
+ capability: capName,
12972
+ model: model.name,
12973
+ direction: 'inbound',
12974
+ inputBytes: prompt.length,
12975
+ outputBytes: (output || '').length,
12976
+ tokens: inputTokens + outputTokens,
12977
+ inputTokens: inputTokens,
12978
+ outputTokens: outputTokens,
12979
+ durationMs: Date.now() - logEntry.ts,
12980
+ }) + '\\n');
12981
+ } catch (mErr) { dlog('metering write error: ' + (mErr.message || mErr)); }
12964
12982
  } catch (e) {
12965
12983
  dlog('expose: inference error: ' + (e.message || e));
12966
12984
  await swrite({
@@ -13020,9 +13038,11 @@ async function handleCmd(cmd) {
13020
13038
  gpuInfo.vramUtilization = gpuInfo.vramTotalMB > 0 ? Math.round((gpuInfo.vramUsedMB / gpuInfo.vramTotalMB) * 100) : 0;
13021
13039
  }
13022
13040
  } catch (ge) { /* no GPU */ }
13041
+ var cpuModel = '';
13042
+ try { var cpuInfoArr = os.cpus(); if (cpuInfoArr.length > 0) cpuModel = cpuInfoArr[0].model || ''; } catch {}
13023
13043
  var metricsPayload = {
13024
- cpu: { utilization: Math.min(100, Math.round((loads[0] / cores) * 100)), cores: cores },
13025
- memory: { utilization: Math.round((usedMem / totalMem) * 100), totalGB: Math.round((totalMem / (1024*1024*1024)) * 10) / 10 },
13044
+ cpu: { utilization: Math.min(100, Math.round((loads[0] / cores) * 100)), cores: cores, model: cpuModel },
13045
+ 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 },
13026
13046
  gpu: gpuInfo,
13027
13047
  timestamp: new Date().toISOString(),
13028
13048
  };
@@ -13185,12 +13205,9 @@ process.on('unhandledRejection', (reason) => {
13185
13205
  dlog('WARNING: could not patch node.dialProtocol \u2014 node=' + !!_patchNode);
13186
13206
  }
13187
13207
 
13188
- // v1.5.0: Setup metering audit hook
13189
- try {
13190
- if (nexus.metering && typeof nexus.metering.addHook === 'function') {
13191
- nexus.metering.addHook(createFileAuditHook(meteringFile));
13192
- }
13193
- } catch {}
13208
+ // v1.5.0: Metering is now written directly from inference handlers (with token counts).
13209
+ // createFileAuditHook was previously used but produced records with 0 bytes / no tokens.
13210
+ // Keep metering hooks for payment tracking only (below).
13194
13211
 
13195
13212
  // Write payment events to ledger.jsonl
13196
13213
  try {
@@ -28837,18 +28854,23 @@ ${this.formatConnectionInfo()}`);
28837
28854
  continue;
28838
28855
  try {
28839
28856
  const record = JSON.parse(line);
28840
- const recInputBytes = record.inputBytes || 0;
28841
- const recOutputBytes = record.outputBytes || 0;
28842
- const recTotalTokens = typeof record.tokens === "number" ? record.tokens : typeof record.tokens === "object" && record.tokens ? (record.tokens.input || 0) + (record.tokens.output || 0) : 0;
28843
- const totalBytes = recInputBytes + recOutputBytes;
28844
28857
  let tokIn = 0;
28845
28858
  let tokOut = 0;
28846
- if (recTotalTokens > 0 && totalBytes > 0) {
28847
- tokIn = Math.round(recTotalTokens * (recInputBytes / totalBytes));
28848
- tokOut = recTotalTokens - tokIn;
28849
- } else if (recTotalTokens > 0) {
28850
- tokIn = Math.round(recTotalTokens / 2);
28851
- tokOut = recTotalTokens - tokIn;
28859
+ if (typeof record.inputTokens === "number" && typeof record.outputTokens === "number") {
28860
+ tokIn = record.inputTokens;
28861
+ tokOut = record.outputTokens;
28862
+ } else {
28863
+ const recInputBytes = record.inputBytes || 0;
28864
+ const recOutputBytes = record.outputBytes || 0;
28865
+ const recTotalTokens = typeof record.tokens === "number" ? record.tokens : typeof record.tokens === "object" && record.tokens ? (record.tokens.input || 0) + (record.tokens.output || 0) : 0;
28866
+ const totalBytes = recInputBytes + recOutputBytes;
28867
+ if (recTotalTokens > 0 && totalBytes > 0) {
28868
+ tokIn = Math.round(recTotalTokens * (recInputBytes / totalBytes));
28869
+ tokOut = recTotalTokens - tokIn;
28870
+ } else if (recTotalTokens > 0) {
28871
+ tokIn = Math.round(recTotalTokens / 2);
28872
+ tokOut = recTotalTokens - tokIn;
28873
+ }
28852
28874
  }
28853
28875
  this._stats.totalTokensIn += tokIn;
28854
28876
  this._stats.totalTokensOut += tokOut;
@@ -37123,6 +37145,47 @@ function pick(key, variants) {
37123
37145
  narration.lastVariantIdx[key] = idx;
37124
37146
  return variants[idx];
37125
37147
  }
37148
+ function extractSubAgentTopic(args) {
37149
+ const raw = String(args["description"] ?? args["prompt"] ?? "");
37150
+ if (!raw || raw.length < 5)
37151
+ return "";
37152
+ let text = raw.replace(/[#*`_~\[\]]/g, "").replace(/^\d+[.)]\s*/gm, "").trim();
37153
+ const sentEnd = text.search(/[.!?]\s/);
37154
+ if (sentEnd > 10 && sentEnd < 80) {
37155
+ text = text.slice(0, sentEnd);
37156
+ } else if (text.length > 80) {
37157
+ const cut = text.lastIndexOf(" ", 80);
37158
+ text = text.slice(0, cut > 20 ? cut : 80);
37159
+ }
37160
+ if (text.length > 0)
37161
+ text = text.charAt(0).toLowerCase() + text.slice(1);
37162
+ return text;
37163
+ }
37164
+ function extractShellEssence(cmd) {
37165
+ if (!cmd || cmd.length < 3)
37166
+ return "";
37167
+ const npmTest = cmd.match(/npm\s+test\b/);
37168
+ if (npmTest)
37169
+ return "running tests";
37170
+ const npmBuild = cmd.match(/npm\s+run\s+(\S+)/);
37171
+ if (npmBuild)
37172
+ return `running ${npmBuild[1]}`;
37173
+ const gitCmd = cmd.match(/git\s+(commit|push|pull|merge|rebase|checkout|diff|status|log|stash|add)\b/);
37174
+ if (gitCmd)
37175
+ return `git ${gitCmd[1]}`;
37176
+ if (/npm\s+publish/.test(cmd))
37177
+ return "publishing to npm";
37178
+ const nodeScript = cmd.match(/(?:node|tsx?)\s+(\S+)/);
37179
+ if (nodeScript)
37180
+ return `running ${nodeScript[1].split("/").pop()}`;
37181
+ const curlUrl = cmd.match(/curl\s+.*?(https?:\/\/\S{10,40})/);
37182
+ if (curlUrl)
37183
+ return `fetching ${curlUrl[1].replace(/https?:\/\//, "").split("/")[0]}`;
37184
+ const pnpm = cmd.match(/pnpm\s+(-r\s+)?(\S+)/);
37185
+ if (pnpm)
37186
+ return `pnpm ${pnpm[1] || ""}${pnpm[2]}`.trim();
37187
+ return "";
37188
+ }
37126
37189
  function getShellCategory(cmd) {
37127
37190
  if (/npm\s+test|vitest|jest|mocha/.test(cmd))
37128
37191
  return "test";
@@ -37198,24 +37261,35 @@ function emotionColor(emotion, stark = false, autist = false) {
37198
37261
  return "";
37199
37262
  if (Math.random() > (stark ? 0.5 : 0.3))
37200
37263
  return "";
37264
+ const lastEmoKey = narration.lastVariantIdx["_emo_quadrant"] ?? -1;
37201
37265
  if (emotion.valence > 0.5 && emotion.arousal > 0.6) {
37266
+ narration.lastVariantIdx["_emo_quadrant"] = 1;
37267
+ if (lastEmoKey === 1 && Math.random() < 0.5)
37268
+ return "";
37202
37269
  return pick("emo_excited", stark ? [
37203
37270
  "Now we're cooking. ",
37204
37271
  "This is what I'm talking about. ",
37205
- "Brilliant. Absolutely brilliant. ",
37272
+ "Brilliant. ",
37206
37273
  "Watch this. ",
37207
37274
  "We're onto something here. ",
37208
- "I live for this. ",
37209
- "Oh this is good. "
37275
+ "Oh this is good. ",
37276
+ "The pieces are falling into place. ",
37277
+ "This is where it gets interesting. ",
37278
+ "I can feel this coming together. "
37210
37279
  ] : [
37211
37280
  "Feeling good about this. ",
37212
37281
  "On a roll here. ",
37213
37282
  "This is going great. ",
37214
37283
  "Momentum building. ",
37215
- "Loving this. "
37284
+ "Loving this. ",
37285
+ "Making real progress now. ",
37286
+ "Things are clicking. "
37216
37287
  ]);
37217
37288
  }
37218
37289
  if (emotion.valence < -0.3 && emotion.arousal > 0.5) {
37290
+ narration.lastVariantIdx["_emo_quadrant"] = 2;
37291
+ if (lastEmoKey === 2 && Math.random() < 0.5)
37292
+ return "";
37219
37293
  return pick("emo_stressed", stark ? [
37220
37294
  "Fine. The hard way then. ",
37221
37295
  "This is getting personal. ",
@@ -37223,41 +37297,54 @@ function emotionColor(emotion, stark = false, autist = false) {
37223
37297
  "Come on. Think. ",
37224
37298
  "Not today. Not this bug. ",
37225
37299
  "I've beaten worse. ",
37226
- "Pain is just information. "
37300
+ "There's got to be a way. ",
37301
+ "Okay, new angle. "
37227
37302
  ] : [
37228
37303
  "Pushing through. ",
37229
37304
  "Staying focused. ",
37230
37305
  "Not giving up. ",
37231
37306
  "Determined to crack this. ",
37232
- "Bear with me. "
37307
+ "Bear with me. ",
37308
+ "Working through this. ",
37309
+ "Let me rethink this. "
37233
37310
  ]);
37234
37311
  }
37235
37312
  if (emotion.valence > 0.3 && emotion.arousal < 0.3) {
37313
+ narration.lastVariantIdx["_emo_quadrant"] = 3;
37314
+ if (lastEmoKey === 3 && Math.random() < 0.5)
37315
+ return "";
37236
37316
  return pick("emo_calm", stark ? [
37237
37317
  "Clean. Just how I like it. ",
37238
37318
  "Smooth. ",
37239
37319
  "Textbook. ",
37240
37320
  "That's the stuff. ",
37241
- "Effortless. "
37321
+ "Effortless. ",
37322
+ "Exactly as expected. "
37242
37323
  ] : [
37243
37324
  "Nice and steady. ",
37244
37325
  "Smooth sailing. ",
37245
37326
  "Easy does it. ",
37246
- "Taking it easy. "
37327
+ "Taking it easy. ",
37328
+ "Going well. "
37247
37329
  ]);
37248
37330
  }
37249
37331
  if (emotion.valence < -0.2 && emotion.arousal < 0.3) {
37332
+ narration.lastVariantIdx["_emo_quadrant"] = 4;
37333
+ if (lastEmoKey === 4 && Math.random() < 0.5)
37334
+ return "";
37250
37335
  return pick("emo_subdued", stark ? [
37251
37336
  "Careful now. ",
37252
37337
  "One wrong move and this unravels. ",
37253
37338
  "Measured steps. ",
37254
37339
  "Trust the process. ",
37255
- "Precision matters here. "
37340
+ "Precision matters here. ",
37341
+ "Methodical. "
37256
37342
  ] : [
37257
37343
  "Being careful here. ",
37258
37344
  "Treading carefully. ",
37259
37345
  "Let me be precise. ",
37260
- "Taking extra care. "
37346
+ "Taking extra care. ",
37347
+ "One step at a time. "
37261
37348
  ]);
37262
37349
  }
37263
37350
  return "";
@@ -37290,9 +37377,16 @@ function describeToolCall(toolName, args, personality = 2, emotion, stark = fals
37290
37377
  base = pick(poolKey, (FILE_EDIT_VARIANTS[tier] ?? FILE_EDIT_VARIANTS.terse)(file, args));
37291
37378
  break;
37292
37379
  case "shell": {
37293
- const cat = getShellCategory(String(args["command"] ?? ""));
37294
- const pool = SHELL_VARIANTS[cat] ?? SHELL_VARIANTS.generic;
37295
- base = pick(`shell_${cat}_${tier}`, pool[tier] ?? pool.terse);
37380
+ const cmd = String(args["command"] ?? "");
37381
+ const cat = getShellCategory(cmd);
37382
+ const essence = extractShellEssence(cmd);
37383
+ if (essence && tier !== "terse") {
37384
+ const essenceVariants = tier === "chatty" ? [`Alright, ${essence}`, `Let me handle ${essence}`, `Time to ${essence.startsWith("running") ? essence : "run " + essence}`] : [`Now ${essence}`, essence.charAt(0).toUpperCase() + essence.slice(1)];
37385
+ base = pick(`shell_ess_${tier}`, essenceVariants);
37386
+ } else {
37387
+ const pool = SHELL_VARIANTS[cat] ?? SHELL_VARIANTS.generic;
37388
+ base = pick(`shell_${cat}_${tier}`, pool[tier] ?? pool.terse);
37389
+ }
37296
37390
  break;
37297
37391
  }
37298
37392
  case "grep_search":
@@ -37492,9 +37586,47 @@ function extractResultDigest(toolName, content) {
37492
37586
  const exitMatch = text.match(/exit\s*(?:code)?[:\s]*(\d+)/i);
37493
37587
  if (exitMatch && exitMatch[1] !== "0")
37494
37588
  nuggets.push(`exit code ${exitMatch[1]}`);
37589
+ if (nuggets.length === 0) {
37590
+ const contentDigest = extractContentSummary(text, toolName);
37591
+ if (contentDigest)
37592
+ nuggets.push(contentDigest);
37593
+ }
37495
37594
  const digest = nuggets.slice(0, 3).join(", ");
37496
37595
  return digest.length > 100 ? digest.slice(0, 97) + "..." : digest;
37497
37596
  }
37597
+ function extractContentSummary(text, toolName) {
37598
+ let cleaned = text.replace(/^\s*\d+[│|:→]\s*/gm, "").replace(/\d{4}-\d{2}-\d{2}T[\d:.]+Z?\s*/g, "").replace(/^[\s*#=-]+$/gm, "").replace(/```[\s\S]*?```/g, "").replace(/^\s*(import|export|const|let|var|function|class|interface|type)\s/gm, "").replace(/[{}()\[\];]/g, "").trim();
37599
+ const lines = cleaned.split("\n").map((l) => l.trim()).filter((l) => l.length > 10);
37600
+ for (const line of lines) {
37601
+ const wordChars = line.replace(/[^a-zA-Z\s]/g, "").trim();
37602
+ if (wordChars.length < line.length * 0.4)
37603
+ continue;
37604
+ if (/^(\/|\.\/)/.test(line))
37605
+ continue;
37606
+ if (/^\s*[{<]/.test(line))
37607
+ continue;
37608
+ let summary = line;
37609
+ if (summary.length > 80) {
37610
+ const cut = summary.lastIndexOf(" ", 80);
37611
+ summary = summary.slice(0, cut > 20 ? cut : 80);
37612
+ }
37613
+ if (summary.length > 0) {
37614
+ summary = summary.charAt(0).toLowerCase() + summary.slice(1);
37615
+ summary = summary.replace(/[.,;:!?]+$/, "");
37616
+ }
37617
+ return summary;
37618
+ }
37619
+ if (toolName === "sub_agent") {
37620
+ const summaryMatch = text.match(/(?:summary|created|completed|result)[:\s]+(.{10,80}?)(?:\n|$)/i);
37621
+ if (summaryMatch) {
37622
+ let s = summaryMatch[1].trim();
37623
+ if (s.length > 0)
37624
+ s = s.charAt(0).toLowerCase() + s.slice(1);
37625
+ return s;
37626
+ }
37627
+ }
37628
+ return "";
37629
+ }
37498
37630
  function describeToolResult(toolName, success, personality = 2, resultContent, emotion, stark = false) {
37499
37631
  if (toolName === "task_complete")
37500
37632
  return "";
@@ -37510,6 +37642,16 @@ function describeToolResult(toolName, success, personality = 2, resultContent, e
37510
37642
  return "";
37511
37643
  const base = pick(`result_ok_${tier}`, RESULT_SUCCESS_VARIANTS[tier] ?? RESULT_SUCCESS_VARIANTS.terse);
37512
37644
  if (digest && personality >= 2) {
37645
+ if (tier === "chatty" && digest.length > 15) {
37646
+ if (Math.random() < 0.5) {
37647
+ const contentLeads = [
37648
+ `${emo}Interesting, ${digest}`,
37649
+ `${emo}So we've got ${digest}`,
37650
+ `${emo}${digest.charAt(0).toUpperCase() + digest.slice(1)}, that's useful`
37651
+ ];
37652
+ return pick("digest_lead", contentLeads);
37653
+ }
37654
+ }
37513
37655
  const connectors = tier === "chatty" ? [` \u2014 ${digest}`, `, I see ${digest}`, `. Looks like ${digest}`, `, showing ${digest}`] : tier === "conv" ? [` \u2014 ${digest}`, `, ${digest}`, `. Shows ${digest}`] : [`: ${digest}`];
37514
37656
  return emo + base + pick("digest_conn_ok", connectors);
37515
37657
  }
@@ -38635,18 +38777,18 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
38635
38777
  ]
38636
38778
  };
38637
38779
  SUB_AGENT_VARIANTS = {
38638
- terse: () => [`Delegating to sub agent`, `Spawning sub agent`, `Handing off`],
38639
- conv: () => [
38640
- `Handing this off to a sub agent`,
38641
- `Bringing in some help for this`,
38642
- `Delegating this part of the work`
38643
- ],
38644
- chatty: () => [
38645
- `Bringing in reinforcements for this one`,
38646
- `Let me hand this off to a specialist`,
38647
- `Calling in backup on this`,
38648
- `This needs a dedicated agent, passing it along`
38649
- ]
38780
+ terse: (_f, a) => {
38781
+ const topic = extractSubAgentTopic(a);
38782
+ return topic ? [`Sub agent: ${topic}`, `Delegating: ${topic}`] : [`Delegating to sub agent`, `Spawning sub agent`];
38783
+ },
38784
+ conv: (_f, a) => {
38785
+ const topic = extractSubAgentTopic(a);
38786
+ return topic ? [`Handing off to a sub agent to ${topic}`, `Bringing in help to ${topic}`, `Delegating the ${topic} work`] : [`Handing this off to a sub agent`, `Bringing in some help for this`, `Delegating this part of the work`];
38787
+ },
38788
+ chatty: (_f, a) => {
38789
+ const topic = extractSubAgentTopic(a);
38790
+ return topic ? [`Spinning up a sub agent to ${topic}`, `This part needs a specialist, delegating to ${topic}`, `Calling in backup to handle ${topic}`] : [`Bringing in reinforcements for this one`, `Let me hand this off to a specialist`, `Calling in backup on this`];
38791
+ }
38650
38792
  };
38651
38793
  SHELL_VARIANTS = {
38652
38794
  test: {
@@ -44527,7 +44669,15 @@ var init_status_bar = __esm({
44527
44669
  _remoteMetricsTimer = null;
44528
44670
  /** Update remote host system metrics (from polling /v1/system/metrics) */
44529
44671
  setRemoteMetrics(metrics) {
44530
- this._remoteMetrics = metrics;
44672
+ this._remoteMetrics = {
44673
+ ...metrics,
44674
+ cpuCores: metrics.cpuCores ?? 0,
44675
+ cpuModel: metrics.cpuModel ?? "",
44676
+ vramUsedMB: metrics.vramUsedMB ?? 0,
44677
+ vramTotalMB: metrics.vramTotalMB ?? 0,
44678
+ memTotalGB: metrics.memTotalGB ?? 0,
44679
+ memUsedGB: metrics.memUsedGB ?? 0
44680
+ };
44531
44681
  if (this.active)
44532
44682
  this.renderFooterPreserveCursor();
44533
44683
  }
@@ -44598,10 +44748,16 @@ var init_status_bar = __esm({
44598
44748
  if (metricsData?.cpu) {
44599
44749
  this.setRemoteMetrics({
44600
44750
  cpuUtil: metricsData.cpu?.utilization ?? 0,
44751
+ cpuCores: metricsData.cpu?.cores ?? 0,
44752
+ cpuModel: metricsData.cpu?.model ?? "",
44601
44753
  gpuUtil: metricsData.gpu?.available ? metricsData.gpu.utilization ?? 0 : -1,
44602
44754
  gpuName: metricsData.gpu?.name ?? "",
44603
44755
  vramUtil: metricsData.gpu?.available ? metricsData.gpu.vramUtilization ?? 0 : -1,
44604
- memUtil: metricsData.memory?.utilization ?? 0
44756
+ vramUsedMB: metricsData.gpu?.vramUsedMB ?? 0,
44757
+ vramTotalMB: metricsData.gpu?.vramTotalMB ?? 0,
44758
+ memUtil: metricsData.memory?.utilization ?? 0,
44759
+ memTotalGB: metricsData.memory?.totalGB ?? 0,
44760
+ memUsedGB: metricsData.memory?.usedGB ?? 0
44605
44761
  });
44606
44762
  return;
44607
44763
  }
@@ -45003,29 +45159,44 @@ var init_status_bar = __esm({
45003
45159
  if (this._remoteMetrics) {
45004
45160
  const rm2 = this._remoteMetrics;
45005
45161
  const cpuColor = rm2.cpuUtil > 80 ? c2.red : rm2.cpuUtil > 50 ? c2.yellow : c2.green;
45006
- const cpuStr = `CPU:${cpuColor(rm2.cpuUtil + "%")}`;
45007
- const cpuW = 4 + `${rm2.cpuUtil}%`.length;
45008
- let gpuStr = "";
45009
- let gpuW = 0;
45162
+ const cpuCoresInfo = rm2.cpuCores > 0 ? ` (${rm2.cpuCores}c` + (rm2.cpuModel ? " " + rm2.cpuModel.replace(/\s+/g, " ").slice(0, 30) : "") + ")" : "";
45163
+ const cpuExpStr = `CPU:${cpuColor(rm2.cpuUtil + "%")}${c2.dim(cpuCoresInfo)}`;
45164
+ const cpuCompStr = `CPU:${cpuColor(rm2.cpuUtil + "%")}`;
45165
+ const cpuExpW = 4 + `${rm2.cpuUtil}%`.length + cpuCoresInfo.length;
45166
+ const cpuCompW = 4 + `${rm2.cpuUtil}%`.length;
45167
+ const memColor = rm2.memUtil > 80 ? c2.red : rm2.memUtil > 50 ? c2.yellow : c2.green;
45168
+ const memDetail = rm2.memTotalGB > 0 ? ` (${rm2.memUsedGB}/${rm2.memTotalGB}GB)` : "";
45169
+ const memExpStr = ` RAM:${memColor(rm2.memUtil + "%")}${c2.dim(memDetail)}`;
45170
+ const memCompStr = ` RAM:${memColor(rm2.memUtil + "%")}`;
45171
+ const memExpW = 5 + `${rm2.memUtil}%`.length + memDetail.length;
45172
+ const memCompW = 5 + `${rm2.memUtil}%`.length;
45173
+ let gpuExpStr = "";
45174
+ let gpuCompStr = "";
45175
+ let gpuExpW = 0;
45176
+ let gpuCompW = 0;
45010
45177
  if (rm2.gpuUtil >= 0) {
45011
45178
  const gpuColor = rm2.gpuUtil > 80 ? c2.red : rm2.gpuUtil > 50 ? c2.yellow : c2.green;
45012
- gpuStr = ` GPU:${gpuColor(rm2.gpuUtil + "%")}`;
45013
- gpuW = 5 + `${rm2.gpuUtil}%`.length;
45179
+ const gpuNameShort = rm2.gpuName ? ` (${rm2.gpuName.slice(0, 20)})` : "";
45180
+ gpuExpStr = ` GPU:${gpuColor(rm2.gpuUtil + "%")}${c2.dim(gpuNameShort)}`;
45181
+ gpuCompStr = ` GPU:${gpuColor(rm2.gpuUtil + "%")}`;
45182
+ gpuExpW = 5 + `${rm2.gpuUtil}%`.length + gpuNameShort.length;
45183
+ gpuCompW = 5 + `${rm2.gpuUtil}%`.length;
45014
45184
  }
45015
45185
  let vramStr = "";
45016
45186
  let vramW = 0;
45017
45187
  if (rm2.vramUtil >= 0) {
45018
45188
  const vramColor = rm2.vramUtil > 80 ? c2.red : rm2.vramUtil > 50 ? c2.yellow : c2.green;
45019
- vramStr = ` VRAM:${vramColor(rm2.vramUtil + "%")}`;
45020
- vramW = 6 + `${rm2.vramUtil}%`.length;
45189
+ const vramDetail = rm2.vramTotalMB > 0 ? ` (${rm2.vramUsedMB}/${rm2.vramTotalMB}MB)` : "";
45190
+ vramStr = ` VRAM:${vramColor(rm2.vramUtil + "%")}${c2.dim(vramDetail)}`;
45191
+ vramW = 6 + `${rm2.vramUtil}%`.length + vramDetail.length;
45021
45192
  }
45022
- const remoteExpanded = pastel2(117, "\u{1F4E1}") + " " + cpuStr + gpuStr + vramStr;
45023
- const remoteCompact = pastel2(117, "\u{1F4E1}") + " " + cpuStr + gpuStr;
45193
+ const remoteExpanded = pastel2(117, "\u{1F4E1}") + " " + cpuExpStr + memExpStr + gpuExpStr + vramStr;
45194
+ const remoteCompact = pastel2(117, "\u{1F4E1}") + " " + cpuCompStr + gpuCompStr;
45024
45195
  sections.push({
45025
45196
  expanded: remoteExpanded,
45026
45197
  compact: remoteCompact,
45027
- expandedW: 3 + cpuW + gpuW + vramW,
45028
- compactW: 3 + cpuW + gpuW,
45198
+ expandedW: 3 + cpuExpW + memExpW + gpuExpW + vramW,
45199
+ compactW: 3 + cpuCompW + gpuCompW,
45029
45200
  empty: false
45030
45201
  });
45031
45202
  }
@@ -48204,8 +48375,12 @@ ${result.text}`;
48204
48375
  if (parsed.summary)
48205
48376
  steering = parsed.summary;
48206
48377
  } catch {
48207
- if (result.summary)
48208
- steering = `USER STEERING: ${result.summary}`;
48378
+ const raw = result.summary || "";
48379
+ const ackMatch = raw.match(/acknowledgment[:\s]*["']?([^"'\n]{5,80})["']?/i) || raw.match(/^([^.!?\n]{5,80}[.!?])/);
48380
+ if (ackMatch)
48381
+ acknowledgment = ackMatch[1].trim();
48382
+ if (raw.length > 10)
48383
+ steering = `USER STEERING: ${raw}`;
48209
48384
  }
48210
48385
  if (voiceEngine?.enabled) {
48211
48386
  writeContent(() => renderVoiceText(acknowledgment));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.53",
3
+ "version": "0.103.55",
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",