open-agents-ai 0.104.17 → 0.104.19

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 +278 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28374,7 +28374,7 @@ import { EventEmitter as EventEmitter3 } from "node:events";
28374
28374
  import { randomBytes as randomBytes7 } from "node:crypto";
28375
28375
  import { URL as URL2 } from "node:url";
28376
28376
  import { loadavg, cpus, totalmem, freemem } from "node:os";
28377
- import { existsSync as existsSync27, readFileSync as readFileSync19, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3, mkdirSync as mkdirSync9, readdirSync as readdirSync7 } from "node:fs";
28377
+ import { existsSync as existsSync27, readFileSync as readFileSync19, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3, mkdirSync as mkdirSync9, readdirSync as readdirSync7, statSync as statSync9 } from "node:fs";
28378
28378
  import { join as join35 } from "node:path";
28379
28379
  function cleanForwardHeaders(raw, targetHost) {
28380
28380
  const out = {};
@@ -28954,6 +28954,8 @@ var init_expose = __esm({
28954
28954
  if (responseTail.length > 2048) {
28955
28955
  responseTail = responseTail.slice(-2048);
28956
28956
  }
28957
+ if (isStreaming)
28958
+ this.emit("token_flash");
28957
28959
  });
28958
28960
  if (isStreaming) {
28959
28961
  if (proxyRes.statusCode !== 200) {
@@ -29267,6 +29269,9 @@ ${this.formatConnectionInfo()}`);
29267
29269
  _loadbalance = false;
29268
29270
  _endpointAuth;
29269
29271
  _pollTimer = null;
29272
+ _activityPollTimer = null;
29273
+ _prevInvocCount = 0;
29274
+ _nexusDir = null;
29270
29275
  _stats = {
29271
29276
  status: "standby",
29272
29277
  totalRequests: 0,
@@ -29388,6 +29393,14 @@ ${this.formatConnectionInfo()}`);
29388
29393
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
29389
29394
  });
29390
29395
  }
29396
+ try {
29397
+ const invocDir = join35(nexusDir, "invocations");
29398
+ if (existsSync27(invocDir)) {
29399
+ this._prevInvocCount = readdirSync7(invocDir).filter((f) => f.endsWith(".json")).length;
29400
+ this._stats.totalRequests = this._prevInvocCount;
29401
+ }
29402
+ } catch {
29403
+ }
29391
29404
  this.startMeteringPoll(nexusDir);
29392
29405
  return this._peerId ?? "connected (peer ID pending)";
29393
29406
  }
@@ -29465,7 +29478,52 @@ ${this.formatConnectionInfo()}`);
29465
29478
  /** Poll the daemon's metering.jsonl and status.json for stats */
29466
29479
  startMeteringPoll(nexusDir) {
29467
29480
  this.stopMeteringPoll();
29481
+ this._nexusDir = nexusDir;
29468
29482
  let lastMeteringSize = 0;
29483
+ let lastMeteringLineCount = 0;
29484
+ this._activityPollTimer = setInterval(() => {
29485
+ try {
29486
+ const invocDir = join35(nexusDir, "invocations");
29487
+ if (!existsSync27(invocDir))
29488
+ return;
29489
+ const files = readdirSync7(invocDir).filter((f) => f.endsWith(".json"));
29490
+ const invocCount = files.length;
29491
+ const newRequests = invocCount - this._prevInvocCount;
29492
+ if (newRequests > 0) {
29493
+ this._stats.activeConnections = Math.max(1, newRequests);
29494
+ this._stats.totalRequests = invocCount;
29495
+ this._prevInvocCount = invocCount;
29496
+ this.emitStats();
29497
+ return;
29498
+ }
29499
+ const now = Date.now();
29500
+ let recentActive = 0;
29501
+ for (const f of files.slice(-10)) {
29502
+ try {
29503
+ const st = statSync9(join35(invocDir, f));
29504
+ if (now - st.mtimeMs < 1e4)
29505
+ recentActive++;
29506
+ } catch {
29507
+ }
29508
+ }
29509
+ const meteringFile = join35(nexusDir, "metering.jsonl");
29510
+ let meteringLines = lastMeteringLineCount;
29511
+ try {
29512
+ if (existsSync27(meteringFile)) {
29513
+ const content = readFileSync19(meteringFile, "utf8");
29514
+ meteringLines = content.split("\n").filter((l) => l.trim()).length;
29515
+ }
29516
+ } catch {
29517
+ }
29518
+ const inFlightEstimate = Math.max(0, invocCount - meteringLines);
29519
+ const prevActive = this._stats.activeConnections;
29520
+ this._stats.activeConnections = Math.max(recentActive, Math.min(inFlightEstimate, 10));
29521
+ if (this._stats.activeConnections !== prevActive)
29522
+ this.emitStats();
29523
+ } catch {
29524
+ }
29525
+ }, 1e3);
29526
+ this._activityPollTimer.unref();
29469
29527
  this._pollTimer = setInterval(() => {
29470
29528
  try {
29471
29529
  const statusPath = join35(nexusDir, "status.json");
@@ -29485,8 +29543,8 @@ ${this.formatConnectionInfo()}`);
29485
29543
  const invocCount = files.filter((f) => f.endsWith(".json")).length;
29486
29544
  if (invocCount > this._stats.totalRequests) {
29487
29545
  this._stats.totalRequests = invocCount;
29488
- this.emitStats();
29489
29546
  }
29547
+ this._prevInvocCount = invocCount;
29490
29548
  }
29491
29549
  } catch {
29492
29550
  }
@@ -29497,6 +29555,7 @@ ${this.formatConnectionInfo()}`);
29497
29555
  if (content.length > lastMeteringSize) {
29498
29556
  const newContent = content.slice(lastMeteringSize);
29499
29557
  lastMeteringSize = content.length;
29558
+ lastMeteringLineCount = content.split("\n").filter((l) => l.trim()).length;
29500
29559
  for (const line of newContent.split("\n")) {
29501
29560
  if (!line.trim())
29502
29561
  continue;
@@ -29572,6 +29631,10 @@ ${this.formatConnectionInfo()}`);
29572
29631
  clearInterval(this._pollTimer);
29573
29632
  this._pollTimer = null;
29574
29633
  }
29634
+ if (this._activityPollTimer) {
29635
+ clearInterval(this._activityPollTimer);
29636
+ this._activityPollTimer = null;
29637
+ }
29575
29638
  }
29576
29639
  emitStats() {
29577
29640
  this.emit("stats", {
@@ -31613,7 +31676,7 @@ var init_dist6 = __esm({
31613
31676
  });
31614
31677
 
31615
31678
  // packages/cli/dist/tui/oa-directory.js
31616
- import { existsSync as existsSync30, mkdirSync as mkdirSync11, readFileSync as readFileSync22, writeFileSync as writeFileSync11, readdirSync as readdirSync8, statSync as statSync9, unlinkSync as unlinkSync4 } from "node:fs";
31679
+ import { existsSync as existsSync30, mkdirSync as mkdirSync11, readFileSync as readFileSync22, writeFileSync as writeFileSync11, readdirSync as readdirSync8, statSync as statSync10, unlinkSync as unlinkSync4 } from "node:fs";
31617
31680
  import { join as join39, relative as relative2, basename as basename9, extname as extname8 } from "node:path";
31618
31681
  import { homedir as homedir9 } from "node:os";
31619
31682
  function initOaDirectory(repoRoot) {
@@ -31806,7 +31869,7 @@ function loadRecentSessions(repoRoot, limit = 5) {
31806
31869
  return [];
31807
31870
  try {
31808
31871
  const files = readdirSync8(historyDir).filter((f) => f.endsWith(".json") && f !== "pending-task.json").map((f) => {
31809
- const stat5 = statSync9(join39(historyDir, f));
31872
+ const stat5 = statSync10(join39(historyDir, f));
31810
31873
  return { file: f, mtime: stat5.mtimeMs };
31811
31874
  }).sort((a, b) => b.mtime - a.mtime).slice(0, limit);
31812
31875
  return files.map((f) => {
@@ -34698,7 +34761,8 @@ async function startNeovimMode(opts) {
34698
34761
  return;
34699
34762
  }
34700
34763
  if (state.focused) {
34701
- nvimPty.write(seq);
34764
+ const normalized = seq.replace(/\x1BO([ABCD])/g, "\x1B[$1");
34765
+ nvimPty.write(normalized);
34702
34766
  }
34703
34767
  };
34704
34768
  stdin.on("data", state.stdinHandler);
@@ -34769,8 +34833,13 @@ async function connectRPC(state, neovimPkg, cols) {
34769
34833
  if (state.cleanedUp)
34770
34834
  return;
34771
34835
  try {
34772
- const editorWidth = Math.floor(cols * 0.7);
34773
- const outputWidth = cols - editorWidth;
34836
+ await new Promise((r) => setTimeout(r, 600));
34837
+ if (state.cleanedUp)
34838
+ return;
34839
+ const neoTreeWidth = 30;
34840
+ const remainingWidth = cols - neoTreeWidth;
34841
+ const outputWidth = Math.max(30, Math.floor(remainingWidth * 0.35));
34842
+ await nvim.command("wincmd l");
34774
34843
  await nvim.command("botright vsplit");
34775
34844
  await nvim.command(`vertical resize ${outputWidth}`);
34776
34845
  const buf = await nvim.createBuffer(false, true);
@@ -34931,7 +35000,7 @@ var init_neovim_mode = __esm({
34931
35000
  init_setup();
34932
35001
  isTTY5 = process.stdout.isTTY ?? false;
34933
35002
  PTY_MODE_ENABLE_RE = /\x1B\[\?(?:1000|1002|1003|1004|1005|1006|1015|2004)h/g;
34934
- STDIN_MOUSE_FOCUS_RE = /\x1B\[<[\d;]+[Mm]|\x1B\[M[\s\S]{3}|\x1B\[[IO]/g;
35003
+ STDIN_MOUSE_FOCUS_RE = /\x1B\[<[\d;]+[Mm]|\x1B\[M[\s\S]{3}|\x1B\[[IO]|\x1BO[ABCD]/g;
34935
35004
  _state = null;
34936
35005
  }
34937
35006
  });
@@ -34948,7 +35017,7 @@ __export(voice_exports, {
34948
35017
  registerCustomOnnxModel: () => registerCustomOnnxModel,
34949
35018
  resetNarrationContext: () => resetNarrationContext
34950
35019
  });
34951
- import { existsSync as existsSync34, mkdirSync as mkdirSync13, writeFileSync as writeFileSync13, readFileSync as readFileSync24, unlinkSync as unlinkSync6, readdirSync as readdirSync9, renameSync, statSync as statSync10 } from "node:fs";
35020
+ import { existsSync as existsSync34, mkdirSync as mkdirSync13, writeFileSync as writeFileSync13, readFileSync as readFileSync24, unlinkSync as unlinkSync6, readdirSync as readdirSync9, renameSync, statSync as statSync11 } from "node:fs";
34952
35021
  import { join as join42 } from "node:path";
34953
35022
  import { homedir as homedir11, tmpdir as tmpdir7, platform as platform2 } from "node:os";
34954
35023
  import { execSync as execSync27, spawn as nodeSpawn } from "node:child_process";
@@ -35998,7 +36067,7 @@ var init_voice = __esm({
35998
36067
  const p = join42(dir, f);
35999
36068
  let size = 0;
36000
36069
  try {
36001
- size = statSync10(p).size;
36070
+ size = statSync11(p).size;
36002
36071
  } catch {
36003
36072
  }
36004
36073
  return {
@@ -37362,6 +37431,13 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
37362
37431
  });
37363
37432
 
37364
37433
  // packages/cli/dist/tui/commands.js
37434
+ function safeLog(text) {
37435
+ if (isNeovimActive()) {
37436
+ writeToNeovimOutput(text + "\n");
37437
+ } else {
37438
+ process.stdout.write(text + "\n");
37439
+ }
37440
+ }
37365
37441
  async function handleSlashCommand(input, ctx) {
37366
37442
  const trimmed = input.trim();
37367
37443
  if (!trimmed.startsWith("/"))
@@ -37428,7 +37504,7 @@ async function handleSlashCommand(input, ctx) {
37428
37504
  }
37429
37505
  }
37430
37506
  lines.push("");
37431
- console.log(lines.join("\n"));
37507
+ safeLog(lines.join("\n"));
37432
37508
  } else {
37433
37509
  renderInfo("Cost tracking not available.");
37434
37510
  }
@@ -37460,7 +37536,7 @@ async function handleSlashCommand(input, ctx) {
37460
37536
  lines.push(` ${c2.dim(evalResult.feedback)}`);
37461
37537
  }
37462
37538
  lines.push("");
37463
- console.log(lines.join("\n"));
37539
+ safeLog(lines.join("\n"));
37464
37540
  } else {
37465
37541
  renderError(`Evaluation failed: ${evalResult.error}`);
37466
37542
  }
@@ -37697,9 +37773,9 @@ async function handleSlashCommand(input, ctx) {
37697
37773
  const template = getTaskTemplate(type);
37698
37774
  ctx.setTaskType?.(type);
37699
37775
  renderInfo(`Task type: ${c2.bold(template.label)} (${type})`);
37700
- console.log(` ${c2.dim(template.description)}`);
37701
- console.log(` ${c2.dim("Recommended tools:")} ${template.recommendedTools.slice(0, 5).join(", ")}`);
37702
- console.log("");
37776
+ safeLog(` ${c2.dim(template.description)}`);
37777
+ safeLog(` ${c2.dim("Recommended tools:")} ${template.recommendedTools.slice(0, 5).join(", ")}`);
37778
+ safeLog("");
37703
37779
  } else {
37704
37780
  const lines = [];
37705
37781
  lines.push(`
@@ -37718,7 +37794,7 @@ async function handleSlashCommand(input, ctx) {
37718
37794
  lines.push(`
37719
37795
  ${c2.dim("Usage: /task-type <type> or /task-type auto")}`);
37720
37796
  lines.push("");
37721
- console.log(lines.join("\n"));
37797
+ safeLog(lines.join("\n"));
37722
37798
  }
37723
37799
  return "handled";
37724
37800
  }
@@ -37777,7 +37853,7 @@ async function handleSlashCommand(input, ctx) {
37777
37853
  Avg eval score: ${c2.bold(`${Math.round(summary.averageEvalScore * 100)}%`)}`);
37778
37854
  }
37779
37855
  lines.push("");
37780
- console.log(lines.join("\n"));
37856
+ safeLog(lines.join("\n"));
37781
37857
  return "handled";
37782
37858
  }
37783
37859
  case "model":
@@ -39641,19 +39717,19 @@ async function handleParallel(arg, ctx) {
39641
39717
  } catch {
39642
39718
  }
39643
39719
  const effective = envVal || systemdVal || "1 (default)";
39644
- console.log("");
39645
- console.log(` ${c2.bold("Parallel Inference Slots")}`);
39646
- console.log("");
39647
- console.log(` ${c2.dim("OLLAMA_NUM_PARALLEL:")} ${c2.bold(c2.cyan(effective))}`);
39648
- console.log(` ${c2.dim("Endpoint:")} ${baseUrl}`);
39720
+ safeLog("");
39721
+ safeLog(` ${c2.bold("Parallel Inference Slots")}`);
39722
+ safeLog("");
39723
+ safeLog(` ${c2.dim("OLLAMA_NUM_PARALLEL:")} ${c2.bold(c2.cyan(effective))}`);
39724
+ safeLog(` ${c2.dim("Endpoint:")} ${baseUrl}`);
39649
39725
  if (runningInfo) {
39650
- console.log(` ${c2.dim("Running models:")}`);
39651
- console.log(runningInfo);
39726
+ safeLog(` ${c2.dim("Running models:")}`);
39727
+ safeLog(runningInfo);
39652
39728
  }
39653
- console.log("");
39654
- console.log(` ${c2.dim("Set with:")} ${c2.cyan("/parallel <1-15>")}`);
39655
- console.log(` ${c2.dim("Each slot multiplies context memory usage per model.")}`);
39656
- console.log("");
39729
+ safeLog("");
39730
+ safeLog(` ${c2.dim("Set with:")} ${c2.cyan("/parallel <1-15>")}`);
39731
+ safeLog(` ${c2.dim("Each slot multiplies context memory usage per model.")}`);
39732
+ safeLog("");
39657
39733
  return;
39658
39734
  }
39659
39735
  const n = parseInt(arg, 10);
@@ -39781,23 +39857,34 @@ async function handleUpdate(subcommand, ctx) {
39781
39857
  } catch {
39782
39858
  }
39783
39859
  const BRAILLE_CYCLE = ["\u2800", "\u2840", "\u28C0", "\u28C4", "\u28E4", "\u28E6", "\u28F6", "\u28F7", "\u28FF", "\u28F7", "\u28F6", "\u28E6", "\u28E4", "\u28C4", "\u28C0", "\u2840"];
39860
+ const safeWrite = (text) => {
39861
+ if (isNeovimActive()) {
39862
+ writeToNeovimOutput(text);
39863
+ } else {
39864
+ process.stdout.write(text);
39865
+ }
39866
+ };
39784
39867
  function startInlineSpinner(prefix) {
39785
39868
  let frame = 0;
39869
+ const nvActive = isNeovimActive();
39786
39870
  const timer = setInterval(() => {
39871
+ if (nvActive)
39872
+ return;
39787
39873
  const braille = BRAILLE_CYCLE[frame % BRAILLE_CYCLE.length];
39788
39874
  process.stdout.write(`\r ${c2.cyan("\u25CF")} ${prefix} ${c2.cyan(braille)}`);
39789
39875
  frame++;
39790
39876
  }, 80);
39791
- process.stdout.write(` ${c2.cyan("\u25CF")} ${prefix} ${c2.cyan(BRAILLE_CYCLE[0])}`);
39877
+ safeWrite(` ${c2.cyan("\u25CF")} ${prefix} ${c2.cyan(BRAILLE_CYCLE[0])}
39878
+ `);
39792
39879
  return {
39793
39880
  stop(completionText) {
39794
39881
  clearInterval(timer);
39795
- process.stdout.write(`\r ${c2.green("\u2714")} ${completionText}${" ".repeat(20)}
39882
+ safeWrite(`\r ${c2.green("\u2714")} ${completionText}${" ".repeat(20)}
39796
39883
  `);
39797
39884
  }
39798
39885
  };
39799
39886
  }
39800
- process.stdout.write("\n");
39887
+ safeWrite("\n");
39801
39888
  const checkSpinner = startInlineSpinner(`Checking for updates ${c2.dim(`(current: v${currentVersion})`)}`);
39802
39889
  const info = await checkForUpdate(currentVersion, true);
39803
39890
  const { exec: exec4, execSync: es2 } = await import("node:child_process");
@@ -39824,7 +39911,7 @@ async function handleUpdate(subcommand, ctx) {
39824
39911
  if (needsSudo) {
39825
39912
  renderInfo("Global npm directory requires elevated permissions.");
39826
39913
  renderInfo("You'll be asked for your password once \u2014 it covers all update steps.");
39827
- process.stdout.write("\n");
39914
+ safeWrite("\n");
39828
39915
  try {
39829
39916
  es2("sudo -v", { stdio: "inherit", timeout: 6e4 });
39830
39917
  } catch {
@@ -39896,7 +39983,7 @@ async function handleUpdate(subcommand, ctx) {
39896
39983
  depsSpinner.stop(depsUpdated ? "Dependencies updated to latest." : "Dependencies checked.");
39897
39984
  if (!primaryUpdated && !depsUpdated) {
39898
39985
  renderInfo("Everything is up to date \u2014 no changes needed.");
39899
- process.stdout.write("\n");
39986
+ safeWrite("\n");
39900
39987
  return;
39901
39988
  }
39902
39989
  const rebuildSpinner = startInlineSpinner("Rebuilding native modules");
@@ -45636,7 +45723,7 @@ var init_tool_policy = __esm({
45636
45723
  });
45637
45724
 
45638
45725
  // packages/cli/dist/tui/telegram-bridge.js
45639
- import { mkdirSync as mkdirSync18, existsSync as existsSync41, unlinkSync as unlinkSync8, readdirSync as readdirSync15, statSync as statSync11 } from "node:fs";
45726
+ import { mkdirSync as mkdirSync18, existsSync as existsSync41, unlinkSync as unlinkSync8, readdirSync as readdirSync15, statSync as statSync12 } from "node:fs";
45640
45727
  import { join as join50, resolve as resolve28 } from "node:path";
45641
45728
  import { writeFile as writeFileAsync } from "node:fs/promises";
45642
45729
  function convertMarkdownToTelegramHTML(md) {
@@ -47730,7 +47817,7 @@ var init_status_bar = __esm({
47730
47817
  }
47731
47818
  /** Expose gateway status — shown after capability emojis */
47732
47819
  _expose = null;
47733
- /** Blink state for expose icon during active inference router LED pattern */
47820
+ /** Blink state for expose icon driven by token generation events */
47734
47821
  _exposeBlinkOn = true;
47735
47822
  _exposeBlinkTimer = null;
47736
47823
  /** Trail timer — keeps blinking briefly after connections drop */
@@ -47738,6 +47825,8 @@ var init_status_bar = __esm({
47738
47825
  /** Rolling request count for activity-based trail duration */
47739
47826
  _exposeRecentReqs = 0;
47740
47827
  _exposeLastReqCount = 0;
47828
+ /** Token-driven flash: OFF timeout after each token triggers ON */
47829
+ _exposeTokenFlashTimer = null;
47741
47830
  /** Update expose gateway status */
47742
47831
  setExposeStatus(status) {
47743
47832
  this._expose = status;
@@ -47786,11 +47875,36 @@ var init_status_bar = __esm({
47786
47875
  clearTimeout(this._exposeTrailTimer);
47787
47876
  this._exposeTrailTimer = null;
47788
47877
  }
47878
+ if (this._exposeTokenFlashTimer) {
47879
+ clearTimeout(this._exposeTokenFlashTimer);
47880
+ this._exposeTokenFlashTimer = null;
47881
+ }
47789
47882
  this._exposeBlinkOn = true;
47790
47883
  this._exposeRecentReqs = 0;
47791
47884
  if (this.active)
47792
47885
  this.renderFooterPreserveCursor();
47793
47886
  }
47887
+ /**
47888
+ * Flash the expose LED once per token — called during active inference when
47889
+ * this node is serving tokens to remote clients. Each call turns the LED ON
47890
+ * for ~50ms then back OFF, creating a 1:1 visual link between generated
47891
+ * tokens and the LED activity indicator (like a network TX LED on a router).
47892
+ */
47893
+ flashExposeToken() {
47894
+ if (!this._expose || this._expose.status !== "active")
47895
+ return;
47896
+ this._exposeBlinkOn = true;
47897
+ if (this._exposeTokenFlashTimer)
47898
+ clearTimeout(this._exposeTokenFlashTimer);
47899
+ this._exposeTokenFlashTimer = setTimeout(() => {
47900
+ this._exposeBlinkOn = false;
47901
+ this._exposeTokenFlashTimer = null;
47902
+ if (this.active)
47903
+ this.renderFooterPreserveCursor();
47904
+ }, 50);
47905
+ if (this.active)
47906
+ this.renderFooterPreserveCursor();
47907
+ }
47794
47908
  /** Unified system metrics collector (local or remote) */
47795
47909
  _metricsCollector = new SystemMetricsCollector();
47796
47910
  /** Cached unified metrics snapshot — updated by collector callback */
@@ -47897,78 +48011,106 @@ var init_status_bar = __esm({
47897
48011
  if (this.active)
47898
48012
  this.renderFooterPreserveCursor();
47899
48013
  });
47900
- const poll = async () => {
47901
- try {
47902
- const queryData = { type: "query" };
47903
- if (authKey)
47904
- queryData.auth_key = authKey;
47905
- const raw = await sendCommand("invoke_capability", {
47906
- target_peer: peerId,
47907
- capability: "system_metrics",
47908
- input: queryData
47909
- }, 8e3);
47910
- let parsed = raw;
48014
+ let pollAttempt = 0;
48015
+ const extractMetrics = (raw) => {
48016
+ let obj = raw;
48017
+ for (let depth = 0; depth < 3; depth++) {
48018
+ if (typeof obj !== "string")
48019
+ break;
47911
48020
  try {
47912
- parsed = JSON.parse(parsed);
48021
+ obj = JSON.parse(obj);
47913
48022
  } catch {
48023
+ break;
48024
+ }
48025
+ }
48026
+ if (!obj || typeof obj !== "object")
48027
+ return null;
48028
+ if (obj.cpu)
48029
+ return obj;
48030
+ if (obj.result) {
48031
+ let r = obj.result;
48032
+ if (typeof r === "string") {
48033
+ try {
48034
+ r = JSON.parse(r);
48035
+ } catch {
48036
+ return null;
48037
+ }
47914
48038
  }
47915
- if (typeof parsed === "string") {
48039
+ if (r && typeof r === "object" && r.cpu)
48040
+ return r;
48041
+ }
48042
+ if (obj.data) {
48043
+ let d = obj.data;
48044
+ if (typeof d === "string") {
47916
48045
  try {
47917
- parsed = JSON.parse(parsed);
48046
+ d = JSON.parse(d);
47918
48047
  } catch {
48048
+ return null;
47919
48049
  }
47920
48050
  }
47921
- if (parsed && typeof parsed === "object") {
47922
- let metricsData = null;
47923
- if (parsed.result) {
47924
- let r = parsed.result;
47925
- if (typeof r === "string") {
48051
+ if (d && typeof d === "object" && d.cpu)
48052
+ return d;
48053
+ }
48054
+ if (Array.isArray(obj.events)) {
48055
+ for (const evt of obj.events) {
48056
+ if (evt?.data) {
48057
+ let ed = evt.data;
48058
+ if (typeof ed === "string") {
47926
48059
  try {
47927
- r = JSON.parse(r);
48060
+ ed = JSON.parse(ed);
47928
48061
  } catch {
48062
+ continue;
47929
48063
  }
47930
48064
  }
47931
- metricsData = typeof r === "object" && r?.cpu ? r : null;
47932
- }
47933
- if (!metricsData && parsed.cpu) {
47934
- metricsData = parsed;
47935
- }
47936
- if (metricsData?.cpu) {
47937
- this.setRemoteMetrics({
47938
- cpuUtil: metricsData.cpu?.utilization ?? 0,
47939
- cpuCores: metricsData.cpu?.cores ?? 0,
47940
- cpuModel: metricsData.cpu?.model ?? "",
47941
- gpuUtil: metricsData.gpu?.available ? metricsData.gpu.utilization ?? 0 : -1,
47942
- gpuName: metricsData.gpu?.name ?? "",
47943
- vramUtil: metricsData.gpu?.available ? metricsData.gpu.vramUtilization ?? 0 : -1,
47944
- vramUsedMB: metricsData.gpu?.vramUsedMB ?? 0,
47945
- vramTotalMB: metricsData.gpu?.vramTotalMB ?? 0,
47946
- memUtil: metricsData.memory?.utilization ?? 0,
47947
- memTotalGB: metricsData.memory?.totalGB ?? 0,
47948
- memUsedGB: metricsData.memory?.usedGB ?? 0
47949
- });
47950
- return;
48065
+ if (ed && typeof ed === "object" && ed.cpu)
48066
+ return ed;
47951
48067
  }
47952
48068
  }
47953
- } catch {
47954
48069
  }
48070
+ return null;
48071
+ };
48072
+ const poll = async () => {
48073
+ pollAttempt++;
47955
48074
  try {
47956
- const raw = await sendCommand("metering_status", { peer_id: peerId }, 5e3);
47957
- const result = JSON.parse(raw);
47958
- if (result.ok !== false && result.output) {
48075
+ const queryData = { type: "query" };
48076
+ if (authKey)
48077
+ queryData.auth_key = authKey;
48078
+ const raw = await sendCommand("invoke_capability", {
48079
+ target_peer: peerId,
48080
+ capability: "system_metrics",
48081
+ input: queryData
48082
+ }, 1e4);
48083
+ if (typeof raw === "string" && raw.startsWith("Invoke error:"))
48084
+ throw new Error(raw);
48085
+ const metricsData = extractMetrics(raw);
48086
+ if (metricsData?.cpu) {
47959
48087
  this.setRemoteMetrics({
47960
- cpuUtil: -1,
47961
- // -1 = unavailable (won't render bar)
47962
- gpuUtil: -1,
47963
- gpuName: "peer",
47964
- vramUtil: -1,
47965
- memUtil: -1
48088
+ cpuUtil: metricsData.cpu?.utilization ?? 0,
48089
+ cpuCores: metricsData.cpu?.cores ?? 0,
48090
+ cpuModel: metricsData.cpu?.model ?? "",
48091
+ gpuUtil: metricsData.gpu?.available ? metricsData.gpu.utilization ?? 0 : -1,
48092
+ gpuName: metricsData.gpu?.name ?? "",
48093
+ vramUtil: metricsData.gpu?.available ? metricsData.gpu.vramUtilization ?? 0 : -1,
48094
+ vramUsedMB: metricsData.gpu?.vramUsedMB ?? 0,
48095
+ vramTotalMB: metricsData.gpu?.vramTotalMB ?? 0,
48096
+ memUtil: metricsData.memory?.utilization ?? 0,
48097
+ memTotalGB: metricsData.memory?.totalGB ?? 0,
48098
+ memUsedGB: metricsData.memory?.usedGB ?? 0
47966
48099
  });
48100
+ return;
47967
48101
  }
47968
48102
  } catch {
47969
48103
  }
48104
+ this.setRemoteMetrics({
48105
+ cpuUtil: -1,
48106
+ // -1 = unavailable (won't render bar)
48107
+ gpuUtil: -1,
48108
+ gpuName: "peer",
48109
+ vramUtil: -1,
48110
+ memUtil: -1
48111
+ });
47970
48112
  };
47971
- poll();
48113
+ setTimeout(poll, 3e3);
47972
48114
  this._remoteMetricsTimer = setInterval(poll, 1e4);
47973
48115
  }
47974
48116
  /** Stop polling remote metrics and switch back to local */
@@ -49182,14 +49324,18 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
49182
49324
  const msg = `
49183
49325
  [cascade] Failover \u2192 ${to.label ?? to.url}: ${reason}
49184
49326
  `;
49185
- if (!bufferIfOverlay("stderr", msg))
49327
+ if (isNeovimActive()) {
49328
+ writeToNeovimOutput(msg);
49329
+ } else if (!bufferIfOverlay("stderr", msg))
49186
49330
  process.stderr.write(msg);
49187
49331
  },
49188
49332
  onProbeResult: (ep, success) => {
49189
49333
  if (success) {
49190
49334
  const msg = `[cascade] Primary recovered: ${ep.label ?? ep.url}
49191
49335
  `;
49192
- if (!bufferIfOverlay("stderr", msg))
49336
+ if (isNeovimActive()) {
49337
+ writeToNeovimOutput(msg);
49338
+ } else if (!bufferIfOverlay("stderr", msg))
49193
49339
  process.stderr.write(msg);
49194
49340
  }
49195
49341
  }
@@ -50237,7 +50383,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
50237
50383
  const contentRows = statusBar.isActive ? statusBar.availableContentRows : Math.max(5, (process.stdout.rows ?? 24) - 6);
50238
50384
  resizeNeovim(process.stdout.columns ?? 80, contentRows);
50239
50385
  }
50240
- if (!carouselRetired) {
50386
+ if (!carouselRetired && !isNeovimActive()) {
50241
50387
  const termRows = process.stdout.rows ?? 24;
50242
50388
  const scrollStart = carousel.reservedRows + 1;
50243
50389
  const scrollEnd = Math.max(termRows - statusBar.reservedRows, scrollStart + 1);
@@ -50308,6 +50454,33 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
50308
50454
  fn();
50309
50455
  }
50310
50456
  }
50457
+ async function writeContentAsync(fn) {
50458
+ if (isNeovimActive()) {
50459
+ const origWrite = process.stdout.write;
50460
+ let captured = "";
50461
+ process.stdout.write = ((chunk) => {
50462
+ captured += typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString() : "";
50463
+ return true;
50464
+ });
50465
+ try {
50466
+ const result = await fn();
50467
+ return result;
50468
+ } finally {
50469
+ process.stdout.write = origWrite;
50470
+ if (captured)
50471
+ writeToNeovimOutput(captured);
50472
+ }
50473
+ }
50474
+ if (statusBar.isActive) {
50475
+ statusBar.beginContentWrite();
50476
+ try {
50477
+ return await fn();
50478
+ } finally {
50479
+ statusBar.endContentWrite();
50480
+ }
50481
+ }
50482
+ return fn();
50483
+ }
50311
50484
  (async () => {
50312
50485
  if (!isResumed && !isFirstRun()) {
50313
50486
  try {
@@ -50385,6 +50558,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
50385
50558
  modelUsage: stats.modelUsage
50386
50559
  });
50387
50560
  });
50561
+ reconnected.on("token_flash", () => statusBar.flashExposeToken());
50388
50562
  writeContent(() => {
50389
50563
  renderInfo(`Reconnected to existing expose tunnel: ${reconnected.tunnelUrl}`);
50390
50564
  });
@@ -50419,6 +50593,11 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
50419
50593
  if (isPeer) {
50420
50594
  const peerId = currentConfig.backendUrl.slice(7);
50421
50595
  const nexusTool = new NexusTool(repoRoot);
50596
+ try {
50597
+ await nexusTool.execute({ action: "connect", agent_name: "open-agents-node", agent_type: "general" });
50598
+ await new Promise((r) => setTimeout(r, 2e3));
50599
+ } catch {
50600
+ }
50422
50601
  statusBar.startPeerMetricsPolling(nexusTool.sendCommand.bind(nexusTool), peerId, currentConfig.apiKey);
50423
50602
  } else if (isRemote) {
50424
50603
  statusBar.startRemoteMetricsPolling(currentConfig.backendUrl, currentConfig.apiKey);
@@ -50570,6 +50749,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
50570
50749
  }
50571
50750
  },
50572
50751
  clearScreen() {
50752
+ if (isNeovimActive()) {
50753
+ writeToNeovimOutput("[screen cleared]\n");
50754
+ return;
50755
+ }
50573
50756
  process.stdout.write("\x1B[2J\x1B[H");
50574
50757
  renderCompactHeader(currentConfig.model);
50575
50758
  if (statusBar.isActive)
@@ -51257,6 +51440,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
51257
51440
  modelUsage: stats.modelUsage
51258
51441
  });
51259
51442
  });
51443
+ newTunnel.on("token_flash", () => statusBar.flashExposeToken());
51260
51444
  try {
51261
51445
  const url = await newTunnel.start();
51262
51446
  tunnelGateway = newTunnel;
@@ -51520,6 +51704,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
51520
51704
  } else {
51521
51705
  writeContent(() => renderInfo(`No ${OA_DIR}/ directory found.`));
51522
51706
  }
51707
+ if (isNeovimActive())
51708
+ stopNeovimMode();
51523
51709
  process.stdout.write("\x1Bc");
51524
51710
  },
51525
51711
  contextSave() {
@@ -51778,11 +51964,7 @@ ${result.content.slice(0, 2e3)}${result.content.length > 2e3 ? "\n[truncated]" :
51778
51964
  return;
51779
51965
  }
51780
51966
  if (input.startsWith("/")) {
51781
- if (statusBar.isActive)
51782
- statusBar.beginContentWrite();
51783
- const cmdResult = await handleSlashCommand(input, commandCtx);
51784
- if (statusBar.isActive)
51785
- statusBar.endContentWrite();
51967
+ const cmdResult = await writeContentAsync(() => handleSlashCommand(input, commandCtx));
51786
51968
  if (cmdResult === "exit") {
51787
51969
  if (activeTask) {
51788
51970
  activeTask.runner.abort();
@@ -52634,7 +52816,7 @@ __export(index_repo_exports, {
52634
52816
  indexRepoCommand: () => indexRepoCommand
52635
52817
  });
52636
52818
  import { resolve as resolve30 } from "node:path";
52637
- import { existsSync as existsSync43, statSync as statSync12 } from "node:fs";
52819
+ import { existsSync as existsSync43, statSync as statSync13 } from "node:fs";
52638
52820
  import { cwd as cwd2 } from "node:process";
52639
52821
  async function indexRepoCommand(opts, _config) {
52640
52822
  const repoRoot = resolve30(opts.repoPath ?? cwd2());
@@ -52644,7 +52826,7 @@ async function indexRepoCommand(opts, _config) {
52644
52826
  printError(`Path does not exist: ${repoRoot}`);
52645
52827
  process.exit(1);
52646
52828
  }
52647
- const stat5 = statSync12(repoRoot);
52829
+ const stat5 = statSync13(repoRoot);
52648
52830
  if (!stat5.isDirectory()) {
52649
52831
  printError(`Path is not a directory: ${repoRoot}`);
52650
52832
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.104.17",
3
+ "version": "0.104.19",
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",