open-agents-ai 0.103.86 → 0.103.88

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 +675 -88
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6726,8 +6726,8 @@ async function loadTranscribeCli() {
6726
6726
  const nvmBase = join16(homedir6(), ".nvm", "versions", "node");
6727
6727
  if (existsSync13(nvmBase)) {
6728
6728
  try {
6729
- const { readdirSync: readdirSync16 } = await import("node:fs");
6730
- for (const ver of readdirSync16(nvmBase)) {
6729
+ const { readdirSync: readdirSync17 } = await import("node:fs");
6730
+ for (const ver of readdirSync17(nvmBase)) {
6731
6731
  const tcPath = join16(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
6732
6732
  if (existsSync13(join16(tcPath, "dist", "index.js"))) {
6733
6733
  const { createRequire: createRequire4 } = await import("node:module");
@@ -11057,9 +11057,9 @@ function getVersion(binary) {
11057
11057
  }
11058
11058
  }
11059
11059
  function installOpencode() {
11060
- const platform4 = process.platform;
11060
+ const platform5 = process.platform;
11061
11061
  try {
11062
- if (platform4 === "win32") {
11062
+ if (platform5 === "win32") {
11063
11063
  execSync19('powershell -Command "irm https://opencode.ai/install | iex"', {
11064
11064
  stdio: "pipe",
11065
11065
  timeout: 12e4
@@ -11331,9 +11331,9 @@ function getVersion2(binary) {
11331
11331
  }
11332
11332
  }
11333
11333
  function installDroid() {
11334
- const platform4 = process.platform;
11334
+ const platform5 = process.platform;
11335
11335
  try {
11336
- if (platform4 === "win32") {
11336
+ if (platform5 === "win32") {
11337
11337
  execSync20('powershell -Command "irm https://app.factory.ai/cli/windows | iex"', {
11338
11338
  stdio: "pipe",
11339
11339
  timeout: 12e4
@@ -16925,8 +16925,8 @@ var init_code_retriever = __esm({
16925
16925
  });
16926
16926
  }
16927
16927
  async getFileContent(filePath, startLine, endLine) {
16928
- const { readFile: readFile17 } = await import("node:fs/promises");
16929
- const content = await readFile17(filePath, "utf-8");
16928
+ const { readFile: readFile18 } = await import("node:fs/promises");
16929
+ const content = await readFile18(filePath, "utf-8");
16930
16930
  if (startLine === void 0)
16931
16931
  return content;
16932
16932
  const lines = content.split("\n");
@@ -22272,8 +22272,8 @@ function isTranscribablePath(path) {
22272
22272
  return isAudioPath(path) || isVideoPath(path);
22273
22273
  }
22274
22274
  function findMicCaptureCommand() {
22275
- const platform4 = process.platform;
22276
- if (platform4 === "linux") {
22275
+ const platform5 = process.platform;
22276
+ if (platform5 === "linux") {
22277
22277
  try {
22278
22278
  execSync23("which arecord", { stdio: "pipe" });
22279
22279
  return {
@@ -22283,7 +22283,7 @@ function findMicCaptureCommand() {
22283
22283
  } catch {
22284
22284
  }
22285
22285
  }
22286
- if (platform4 === "darwin") {
22286
+ if (platform5 === "darwin") {
22287
22287
  try {
22288
22288
  execSync23("which sox", { stdio: "pipe" });
22289
22289
  return {
@@ -22295,7 +22295,7 @@ function findMicCaptureCommand() {
22295
22295
  }
22296
22296
  try {
22297
22297
  execSync23("which ffmpeg", { stdio: "pipe" });
22298
- if (platform4 === "linux") {
22298
+ if (platform5 === "linux") {
22299
22299
  return {
22300
22300
  cmd: "ffmpeg",
22301
22301
  args: [
@@ -22314,7 +22314,7 @@ function findMicCaptureCommand() {
22314
22314
  "pipe:1"
22315
22315
  ]
22316
22316
  };
22317
- } else if (platform4 === "darwin") {
22317
+ } else if (platform5 === "darwin") {
22318
22318
  return {
22319
22319
  cmd: "ffmpeg",
22320
22320
  args: [
@@ -22397,9 +22397,9 @@ function ensureTranscribeCliBackground() {
22397
22397
  } catch {
22398
22398
  }
22399
22399
  try {
22400
- const { exec: exec3 } = await import("node:child_process");
22400
+ const { exec: exec4 } = await import("node:child_process");
22401
22401
  return new Promise((resolve31) => {
22402
- exec3("npm i -g transcribe-cli", { timeout: 18e4 }, (err) => {
22402
+ exec4("npm i -g transcribe-cli", { timeout: 18e4 }, (err) => {
22403
22403
  resolve31(!err);
22404
22404
  });
22405
22405
  });
@@ -22625,8 +22625,8 @@ var init_listen = __esm({
22625
22625
  const nvmBase = join34(homedir8(), ".nvm", "versions", "node");
22626
22626
  if (existsSync26(nvmBase)) {
22627
22627
  try {
22628
- const { readdirSync: readdirSync16 } = await import("node:fs");
22629
- for (const ver of readdirSync16(nvmBase)) {
22628
+ const { readdirSync: readdirSync17 } = await import("node:fs");
22629
+ for (const ver of readdirSync17(nvmBase)) {
22630
22630
  const tcPath = join34(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
22631
22631
  if (existsSync26(join34(tcPath, "dist", "index.js"))) {
22632
22632
  const { createRequire: createRequire4 } = await import("node:module");
@@ -33690,7 +33690,8 @@ function tuiSelect(opts) {
33690
33690
  } else {
33691
33691
  const actionHint = opts.onAction ? " \u2190/\u2192/Space toggle" : "";
33692
33692
  const deleteHint = opts.onDelete ? " Del remove" : "";
33693
- lines.push(` ${selectColors.dim("\u2191/\u2193 navigate Enter select" + actionHint + deleteHint + " Esc " + (filter ? "clear filter" : "cancel") + " Type to filter")}`);
33693
+ const customHint = opts.customKeyHint ?? "";
33694
+ lines.push(` ${selectColors.dim("\u2191/\u2193 navigate Enter select" + actionHint + deleteHint + customHint + " Esc " + (filter ? "clear filter" : "cancel") + " Type to filter")}`);
33694
33695
  }
33695
33696
  lines.push("");
33696
33697
  const output = lines.join("\n");
@@ -33858,6 +33859,23 @@ function tuiSelect(opts) {
33858
33859
  render();
33859
33860
  }
33860
33861
  } else if (seq.length === 1 && seq.charCodeAt(0) >= 32 && seq.charCodeAt(0) < 127) {
33862
+ if (opts.onCustomKey && !isSkippable(cursor) && matchSet.has(cursor)) {
33863
+ const consumed = opts.onCustomKey(items[cursor], seq, {
33864
+ done: () => render(),
33865
+ resolve: (result) => {
33866
+ cleanup();
33867
+ resolve31(result);
33868
+ },
33869
+ getInput: (prompt, prefill) => getInputFromUser(prompt, prefill),
33870
+ render: () => render(),
33871
+ updateItem: (index, updates) => {
33872
+ Object.assign(items[index], updates);
33873
+ render();
33874
+ }
33875
+ });
33876
+ if (consumed)
33877
+ return;
33878
+ }
33861
33879
  filter += seq;
33862
33880
  updateFilter();
33863
33881
  if (matchSet.size > 0) {
@@ -33869,6 +33887,72 @@ function tuiSelect(opts) {
33869
33887
  render();
33870
33888
  }
33871
33889
  }
33890
+ function getInputFromUser(prompt, prefill = "") {
33891
+ return new Promise((inputResolve) => {
33892
+ let inputBuf = prefill;
33893
+ let inputCursor = prefill.length;
33894
+ process.stdout.write("\x1B[?25h");
33895
+ function renderInput() {
33896
+ process.stdout.write(`\x1B[${lastRenderedLines}A`);
33897
+ const saveFooter = lastRenderedLines;
33898
+ for (let i = 0; i < saveFooter; i++)
33899
+ process.stdout.write("\x1B[B");
33900
+ process.stdout.write("\x1B[2A");
33901
+ process.stdout.write("\x1B[2K");
33902
+ const before = inputBuf.slice(0, inputCursor);
33903
+ const after = inputBuf.slice(inputCursor);
33904
+ process.stdout.write(` ${selectColors.cyan("\u203A")} ${selectColors.bold(prompt)} ${before}\x1B[7m${after.charAt(0) || " "}\x1B[27m${after.slice(1)}`);
33905
+ process.stdout.write("\n\x1B[2K");
33906
+ process.stdout.write(` ${selectColors.dim("Enter confirm Esc cancel")}`);
33907
+ process.stdout.write("\n");
33908
+ }
33909
+ function onInputData(chunk) {
33910
+ const s = chunk.toString("utf8");
33911
+ if (s === "\r" || s === "\n") {
33912
+ stdin.removeListener("data", onInputData);
33913
+ stdin.on("data", onData);
33914
+ process.stdout.write("\x1B[?25l");
33915
+ render();
33916
+ inputResolve(inputBuf);
33917
+ } else if (s === "\x1B" || s === "\x1B\x1B" || s === "") {
33918
+ stdin.removeListener("data", onInputData);
33919
+ stdin.on("data", onData);
33920
+ process.stdout.write("\x1B[?25l");
33921
+ render();
33922
+ inputResolve(null);
33923
+ } else if (s === "\x7F" || s === "\b") {
33924
+ if (inputCursor > 0) {
33925
+ inputBuf = inputBuf.slice(0, inputCursor - 1) + inputBuf.slice(inputCursor);
33926
+ inputCursor--;
33927
+ renderInput();
33928
+ }
33929
+ } else if (s === "\x1B[D") {
33930
+ if (inputCursor > 0) {
33931
+ inputCursor--;
33932
+ renderInput();
33933
+ }
33934
+ } else if (s === "\x1B[C") {
33935
+ if (inputCursor < inputBuf.length) {
33936
+ inputCursor++;
33937
+ renderInput();
33938
+ }
33939
+ } else if (s === "\x1B[H") {
33940
+ inputCursor = 0;
33941
+ renderInput();
33942
+ } else if (s === "\x1B[F") {
33943
+ inputCursor = inputBuf.length;
33944
+ renderInput();
33945
+ } else if (s.length === 1 && s.charCodeAt(0) >= 32 && s.charCodeAt(0) < 127) {
33946
+ inputBuf = inputBuf.slice(0, inputCursor) + s + inputBuf.slice(inputCursor);
33947
+ inputCursor++;
33948
+ renderInput();
33949
+ }
33950
+ }
33951
+ stdin.removeListener("data", onData);
33952
+ stdin.on("data", onInputData);
33953
+ renderInput();
33954
+ });
33955
+ }
33872
33956
  const onResize = () => render();
33873
33957
  process.stdout.on("resize", onResize);
33874
33958
  stdin.on("data", onData);
@@ -34363,6 +34447,10 @@ async function handleSlashCommand(input, ctx) {
34363
34447
  renderInfo(msg2);
34364
34448
  return "handled";
34365
34449
  }
34450
+ if (arg === "list" || arg === "ls" || arg === "voices") {
34451
+ await handleVoiceList(ctx);
34452
+ return "handled";
34453
+ }
34366
34454
  const msg = await ctx.voiceSetModel(arg);
34367
34455
  save({ voice: true, voiceModel: arg });
34368
34456
  renderInfo(msg + (hasLocal ? " (project-local)" : ""));
@@ -35421,6 +35509,82 @@ async function showModelPicker(ctx, local = false) {
35421
35509
  renderError(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
35422
35510
  }
35423
35511
  }
35512
+ function formatFileSize(bytes) {
35513
+ if (bytes < 1024)
35514
+ return `${bytes}B`;
35515
+ if (bytes < 1024 * 1024)
35516
+ return `${(bytes / 1024).toFixed(0)}KB`;
35517
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
35518
+ }
35519
+ async function handleVoiceList(ctx) {
35520
+ if (!ctx.voiceListClones) {
35521
+ renderWarning("Voice clone management not available in this context.");
35522
+ return;
35523
+ }
35524
+ const clones = ctx.voiceListClones();
35525
+ if (clones.length === 0) {
35526
+ renderInfo("No clone reference voices found.");
35527
+ renderInfo("Add one with: /voice clone <audio-file>");
35528
+ renderInfo("Or generate from ONNX: /voice clone glados");
35529
+ return;
35530
+ }
35531
+ const items = clones.map((cl) => ({
35532
+ key: cl.filename,
35533
+ label: cl.name,
35534
+ detail: `${cl.filename} ${formatFileSize(cl.size)}`
35535
+ }));
35536
+ const activeFilename = clones.find((cl) => cl.isActive)?.filename;
35537
+ const result = await tuiSelect({
35538
+ items,
35539
+ activeKey: activeFilename,
35540
+ title: "Voice Clone References",
35541
+ rl: ctx.rl,
35542
+ availableRows: ctx.availableContentRows?.(),
35543
+ customKeyHint: " e rename p play",
35544
+ onDelete: (item, done) => {
35545
+ if (ctx.voiceDeleteClone?.(item.key)) {
35546
+ done(true);
35547
+ } else {
35548
+ done(false);
35549
+ }
35550
+ },
35551
+ onCustomKey: (item, key, helpers) => {
35552
+ if (key === "e" || key === "E") {
35553
+ const currentName = item.label;
35554
+ helpers.getInput("New name:", currentName).then((newName) => {
35555
+ if (newName !== null && newName.trim() && newName.trim() !== currentName) {
35556
+ ctx.voiceRenameClone?.(item.key, newName.trim());
35557
+ const idx = items.findIndex((i) => i.key === item.key);
35558
+ if (idx >= 0) {
35559
+ helpers.updateItem(idx, { label: newName.trim() });
35560
+ }
35561
+ } else {
35562
+ helpers.render();
35563
+ }
35564
+ });
35565
+ return true;
35566
+ }
35567
+ if (key === "p" || key === "P") {
35568
+ helpers.getInput("Speak text:").then((text) => {
35569
+ if (text !== null && text.trim()) {
35570
+ const prevMsg = ctx.voiceSetActiveClone?.(item.key);
35571
+ if (prevMsg)
35572
+ renderInfo(prevMsg);
35573
+ ctx.voiceSpeak?.(text.trim());
35574
+ }
35575
+ helpers.render();
35576
+ });
35577
+ return true;
35578
+ }
35579
+ return false;
35580
+ }
35581
+ });
35582
+ if (result.confirmed && result.key) {
35583
+ const msg = ctx.voiceSetActiveClone?.(result.key);
35584
+ if (msg)
35585
+ renderInfo(msg);
35586
+ }
35587
+ }
35424
35588
  async function handleEndpoint(arg, ctx, local = false) {
35425
35589
  if (!arg) {
35426
35590
  const history = loadUsageHistory("endpoint", ctx.repoRoot);
@@ -35961,7 +36125,7 @@ async function handleUpdate(subcommand, ctx) {
35961
36125
  process.stdout.write("\n");
35962
36126
  const checkSpinner = startInlineSpinner(`Checking for updates ${c2.dim(`(current: v${currentVersion})`)}`);
35963
36127
  const info = await checkForUpdate(currentVersion, true);
35964
- const { exec: exec3, execSync: es2 } = await import("node:child_process");
36128
+ const { exec: exec4, execSync: es2 } = await import("node:child_process");
35965
36129
  let needsSudo = false;
35966
36130
  try {
35967
36131
  const prefix = es2("npm prefix -g", { encoding: "utf8", timeout: 5e3 }).trim();
@@ -35975,7 +36139,7 @@ async function handleUpdate(subcommand, ctx) {
35975
36139
  }
35976
36140
  let installError = "";
35977
36141
  const runInstall2 = (cmd) => new Promise((resolve31) => {
35978
- const child = exec3(cmd, { timeout: 18e4 }, (err, _stdout, stderr) => {
36142
+ const child = exec4(cmd, { timeout: 18e4 }, (err, _stdout, stderr) => {
35979
36143
  if (err)
35980
36144
  installError = (stderr || err.message || "").trim();
35981
36145
  resolve31(!err);
@@ -36062,7 +36226,7 @@ async function handleUpdate(subcommand, ctx) {
36062
36226
  }
36063
36227
  const rebuildSpinner = startInlineSpinner("Rebuilding native modules");
36064
36228
  const rebuildOk = await new Promise((resolve31) => {
36065
- const child = exec3(`${sudoPrefix}npm rebuild -g open-agents-ai 2>/dev/null || true`, { timeout: 12e4 }, () => resolve31(true));
36229
+ const child = exec4(`${sudoPrefix}npm rebuild -g open-agents-ai 2>/dev/null || true`, { timeout: 12e4 }, () => resolve31(true));
36066
36230
  child.stdout?.resume();
36067
36231
  child.stderr?.resume();
36068
36232
  });
@@ -36095,7 +36259,7 @@ async function handleUpdate(subcommand, ctx) {
36095
36259
  if (fsExists(venvPip)) {
36096
36260
  const pySpinner = startInlineSpinner("Upgrading Python venv packages");
36097
36261
  const pyOk = await new Promise((resolve31) => {
36098
- const child = exec3(`"${venvPip}" install --upgrade moondream-station pytesseract Pillow opencv-python-headless numpy 2>/dev/null || true`, { timeout: 3e5 }, (err) => resolve31(!err));
36262
+ const child = exec4(`"${venvPip}" install --upgrade moondream-station pytesseract Pillow opencv-python-headless numpy 2>/dev/null || true`, { timeout: 3e5 }, (err) => resolve31(!err));
36099
36263
  child.stdout?.resume();
36100
36264
  child.stderr?.resume();
36101
36265
  });
@@ -36242,15 +36406,15 @@ function fmtUptime(ms) {
36242
36406
  }
36243
36407
  function getLocalSystemMetrics() {
36244
36408
  const os = __require("node:os");
36245
- const cpus2 = os.cpus();
36409
+ const cpus3 = os.cpus();
36246
36410
  const loads = os.loadavg();
36247
36411
  const totalMem = os.totalmem();
36248
36412
  const freeMem = os.freemem();
36249
36413
  const usedMem = totalMem - freeMem;
36250
36414
  const result = {
36251
- cpuModel: cpus2[0]?.model?.trim() ?? "unknown",
36252
- cpuCores: cpus2.length,
36253
- cpuUtil: Math.min(100, Math.round(loads[0] / cpus2.length * 100)),
36415
+ cpuModel: cpus3[0]?.model?.trim() ?? "unknown",
36416
+ cpuCores: cpus3.length,
36417
+ cpuUtil: Math.min(100, Math.round(loads[0] / cpus3.length * 100)),
36254
36418
  memTotalGB: Math.round(totalMem / (1024 * 1024 * 1024) * 10) / 10,
36255
36419
  memUsedGB: Math.round(usedMem / (1024 * 1024 * 1024) * 10) / 10,
36256
36420
  memUtil: Math.round(usedMem / totalMem * 100),
@@ -37973,7 +38137,7 @@ var init_carousel_descriptors = __esm({
37973
38137
  });
37974
38138
 
37975
38139
  // packages/cli/dist/tui/voice.js
37976
- import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync14, readFileSync as readFileSync25, unlinkSync as unlinkSync5 } from "node:fs";
38140
+ import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync14, readFileSync as readFileSync25, unlinkSync as unlinkSync5, readdirSync as readdirSync11, renameSync, statSync as statSync10 } from "node:fs";
37977
38141
  import { join as join43 } from "node:path";
37978
38142
  import { homedir as homedir12, tmpdir as tmpdir6, platform as platform3 } from "node:os";
37979
38143
  import { execSync as execSync27, spawn as nodeSpawn } from "node:child_process";
@@ -38708,7 +38872,7 @@ var init_voice = __esm({
38708
38872
  backend: "luxtts"
38709
38873
  }
38710
38874
  };
38711
- VoiceEngine = class {
38875
+ VoiceEngine = class _VoiceEngine {
38712
38876
  enabled = false;
38713
38877
  modelId = "glados";
38714
38878
  ready = false;
@@ -38913,6 +39077,93 @@ var init_voice = __esm({
38913
39077
  this.config = prevConfig;
38914
39078
  }
38915
39079
  }
39080
+ // -------------------------------------------------------------------------
39081
+ // Clone reference management — list / delete / rename / metadata
39082
+ // -------------------------------------------------------------------------
39083
+ /** Metadata file for friendly names of clone refs */
39084
+ static cloneMetaFile() {
39085
+ return join43(luxttsCloneRefsDir(), "meta.json");
39086
+ }
39087
+ loadCloneMeta() {
39088
+ const p = _VoiceEngine.cloneMetaFile();
39089
+ if (!existsSync34(p))
39090
+ return {};
39091
+ try {
39092
+ return JSON.parse(readFileSync25(p, "utf8"));
39093
+ } catch {
39094
+ return {};
39095
+ }
39096
+ }
39097
+ saveCloneMeta(meta) {
39098
+ const dir = luxttsCloneRefsDir();
39099
+ if (!existsSync34(dir))
39100
+ mkdirSync14(dir, { recursive: true });
39101
+ writeFileSync14(_VoiceEngine.cloneMetaFile(), JSON.stringify(meta, null, 2));
39102
+ }
39103
+ /** Audio file extensions recognized as clone references */
39104
+ static AUDIO_EXTS = /* @__PURE__ */ new Set(["wav", "mp3", "ogg", "flac", "m4a", "opus", "aac"]);
39105
+ /**
39106
+ * List all clone reference audio files with metadata.
39107
+ * Returns array of { filename, path, name, size, isActive }.
39108
+ */
39109
+ listCloneRefs() {
39110
+ const dir = luxttsCloneRefsDir();
39111
+ if (!existsSync34(dir))
39112
+ return [];
39113
+ const meta = this.loadCloneMeta();
39114
+ const files = readdirSync11(dir).filter((f) => {
39115
+ const ext = f.split(".").pop()?.toLowerCase() ?? "";
39116
+ return _VoiceEngine.AUDIO_EXTS.has(ext);
39117
+ });
39118
+ return files.map((f) => {
39119
+ const p = join43(dir, f);
39120
+ let size = 0;
39121
+ try {
39122
+ size = statSync10(p).size;
39123
+ } catch {
39124
+ }
39125
+ return {
39126
+ filename: f,
39127
+ path: p,
39128
+ name: meta[f] ?? f.replace(/\.[^.]+$/, "").replace(/[-_]/g, " "),
39129
+ size,
39130
+ isActive: this.luxttsCloneRef === p
39131
+ };
39132
+ });
39133
+ }
39134
+ /** Delete a clone reference file by filename. Returns true if deleted. */
39135
+ deleteCloneRef(filename) {
39136
+ const p = join43(luxttsCloneRefsDir(), filename);
39137
+ if (!existsSync34(p))
39138
+ return false;
39139
+ try {
39140
+ unlinkSync5(p);
39141
+ const meta = this.loadCloneMeta();
39142
+ delete meta[filename];
39143
+ this.saveCloneMeta(meta);
39144
+ if (this.luxttsCloneRef === p) {
39145
+ this.luxttsCloneRef = null;
39146
+ this.autoDetectCloneRef();
39147
+ }
39148
+ return true;
39149
+ } catch {
39150
+ return false;
39151
+ }
39152
+ }
39153
+ /** Rename a clone reference's friendly name (stored in meta.json). */
39154
+ renameCloneRef(filename, newName) {
39155
+ const meta = this.loadCloneMeta();
39156
+ meta[filename] = newName;
39157
+ this.saveCloneMeta(meta);
39158
+ }
39159
+ /** Set the active clone reference by filename. */
39160
+ setActiveCloneRef(filename) {
39161
+ const p = join43(luxttsCloneRefsDir(), filename);
39162
+ if (!existsSync34(p))
39163
+ return `File not found: ${filename}`;
39164
+ this.luxttsCloneRef = p;
39165
+ return `Active clone voice set to: ${filename}`;
39166
+ }
38916
39167
  /**
38917
39168
  * Speak text asynchronously (non-blocking) at full volume.
38918
39169
  * Long text is chunked on sentence/line boundaries for reliable TTS.
@@ -40865,7 +41116,7 @@ var init_promptLoader3 = __esm({
40865
41116
  });
40866
41117
 
40867
41118
  // packages/cli/dist/tui/dream-engine.js
40868
- import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync15, readFileSync as readFileSync27, existsSync as existsSync36, cpSync, rmSync, readdirSync as readdirSync11 } from "node:fs";
41119
+ import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync15, readFileSync as readFileSync27, existsSync as existsSync36, cpSync, rmSync, readdirSync as readdirSync12 } from "node:fs";
40869
41120
  import { join as join46, basename as basename12 } from "node:path";
40870
41121
  import { execSync as execSync28 } from "node:child_process";
40871
41122
  function loadAutoresearchMemory(repoRoot) {
@@ -42071,7 +42322,7 @@ Each proposal includes implementation entrypoints and estimated effort.
42071
42322
  /** Update the master proposal index */
42072
42323
  updateProposalIndex() {
42073
42324
  try {
42074
- const files = readdirSync11(this.dreamsDir).filter((f) => f.endsWith(".md") && f !== "PROPOSAL-INDEX.md" && f !== "dream-state.json").sort();
42325
+ const files = readdirSync12(this.dreamsDir).filter((f) => f.endsWith(".md") && f !== "PROPOSAL-INDEX.md" && f !== "dream-state.json").sort();
42075
42326
  const index = `# Dream Proposals Index
42076
42327
 
42077
42328
  **Last updated**: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
@@ -42473,7 +42724,7 @@ var init_bless_engine = __esm({
42473
42724
  });
42474
42725
 
42475
42726
  // packages/cli/dist/tui/dmn-engine.js
42476
- import { existsSync as existsSync37, readFileSync as readFileSync28, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17, readdirSync as readdirSync12, unlinkSync as unlinkSync6 } from "node:fs";
42727
+ import { existsSync as existsSync37, readFileSync as readFileSync28, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17, readdirSync as readdirSync13, unlinkSync as unlinkSync6 } from "node:fs";
42477
42728
  import { join as join47, basename as basename13 } from "node:path";
42478
42729
  function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
42479
42730
  const competenceReport = competence.length > 0 ? competence.map((c3) => {
@@ -43185,7 +43436,7 @@ OUTPUT: Call task_complete with JSON:
43185
43436
  if (!existsSync37(dir))
43186
43437
  continue;
43187
43438
  try {
43188
- const files = readdirSync12(dir).filter((f) => f.endsWith(".json"));
43439
+ const files = readdirSync13(dir).filter((f) => f.endsWith(".json"));
43189
43440
  for (const f of files) {
43190
43441
  const topic = basename13(f, ".json");
43191
43442
  if (!topics.includes(topic))
@@ -43216,7 +43467,7 @@ OUTPUT: Call task_complete with JSON:
43216
43467
  try {
43217
43468
  const filename = `cycle-${result.cycleNumber}-${Date.now()}.json`;
43218
43469
  writeFileSync16(join47(this.historyDir, filename), JSON.stringify(result, null, 2) + "\n", "utf-8");
43219
- const files = readdirSync12(this.historyDir).filter((f) => f.startsWith("cycle-") && f.endsWith(".json")).sort();
43470
+ const files = readdirSync13(this.historyDir).filter((f) => f.startsWith("cycle-") && f.endsWith(".json")).sort();
43220
43471
  if (files.length > 50) {
43221
43472
  for (const old of files.slice(0, files.length - 50)) {
43222
43473
  try {
@@ -43233,7 +43484,7 @@ OUTPUT: Call task_complete with JSON:
43233
43484
  });
43234
43485
 
43235
43486
  // packages/cli/dist/tui/snr-engine.js
43236
- import { existsSync as existsSync38, readdirSync as readdirSync13, readFileSync as readFileSync29 } from "node:fs";
43487
+ import { existsSync as existsSync38, readdirSync as readdirSync14, readFileSync as readFileSync29 } from "node:fs";
43237
43488
  import { join as join48, basename as basename14 } from "node:path";
43238
43489
  function computeDPrime(signalScores, noiseScores) {
43239
43490
  if (signalScores.length === 0 || noiseScores.length === 0)
@@ -43481,7 +43732,7 @@ Call task_complete with the JSON array when done.`, onEvent)
43481
43732
  if (!existsSync38(dir))
43482
43733
  continue;
43483
43734
  try {
43484
- const files = readdirSync13(dir).filter((f) => f.endsWith(".json"));
43735
+ const files = readdirSync14(dir).filter((f) => f.endsWith(".json"));
43485
43736
  for (const f of files) {
43486
43737
  const topic = basename14(f, ".json");
43487
43738
  if (topics.length > 0 && !topics.includes(topic))
@@ -44054,7 +44305,7 @@ var init_tool_policy = __esm({
44054
44305
  });
44055
44306
 
44056
44307
  // packages/cli/dist/tui/telegram-bridge.js
44057
- import { mkdirSync as mkdirSync18, existsSync as existsSync39, unlinkSync as unlinkSync7, readdirSync as readdirSync14, statSync as statSync10 } from "node:fs";
44308
+ import { mkdirSync as mkdirSync18, existsSync as existsSync39, unlinkSync as unlinkSync7, readdirSync as readdirSync15, statSync as statSync11 } from "node:fs";
44058
44309
  import { join as join49, resolve as resolve27 } from "node:path";
44059
44310
  import { writeFile as writeFileAsync } from "node:fs/promises";
44060
44311
  function convertMarkdownToTelegramHTML(md) {
@@ -45468,6 +45719,267 @@ var init_braille_spinner = __esm({
45468
45719
  }
45469
45720
  });
45470
45721
 
45722
+ // packages/cli/dist/tui/system-metrics.js
45723
+ import { loadavg as loadavg2, cpus as cpus2, totalmem as totalmem2, freemem as freemem2, platform as platform4 } from "node:os";
45724
+ import { exec as exec3 } from "node:child_process";
45725
+ import { readFile as readFile16 } from "node:fs/promises";
45726
+ function formatRate(bytesPerSec) {
45727
+ if (bytesPerSec < 1024)
45728
+ return `${Math.round(bytesPerSec)}B`;
45729
+ if (bytesPerSec < 1024 * 1024)
45730
+ return `${(bytesPerSec / 1024).toFixed(1)}K`;
45731
+ if (bytesPerSec < 1024 * 1024 * 1024)
45732
+ return `${(bytesPerSec / (1024 * 1024)).toFixed(1)}M`;
45733
+ return `${(bytesPerSec / (1024 * 1024 * 1024)).toFixed(2)}G`;
45734
+ }
45735
+ async function readProcNetDev() {
45736
+ try {
45737
+ const data = await readFile16("/proc/net/dev", "utf8");
45738
+ let rxTotal = 0;
45739
+ let txTotal = 0;
45740
+ for (const line of data.split("\n")) {
45741
+ const trimmed = line.trim();
45742
+ if (trimmed.startsWith("Inter") || trimmed.startsWith("face") || trimmed.startsWith("lo:"))
45743
+ continue;
45744
+ const match = trimmed.match(/^\S+:\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)/);
45745
+ if (match) {
45746
+ rxTotal += parseInt(match[1], 10);
45747
+ txTotal += parseInt(match[2], 10);
45748
+ }
45749
+ }
45750
+ return { rxBytes: rxTotal, txBytes: txTotal };
45751
+ } catch {
45752
+ return null;
45753
+ }
45754
+ }
45755
+ async function collectNetworkMetrics() {
45756
+ const now = Date.now();
45757
+ const plat = platform4();
45758
+ if (plat === "linux") {
45759
+ const snap = await readProcNetDev();
45760
+ if (!snap)
45761
+ return { rxBytesPerSec: 0, txBytesPerSec: 0 };
45762
+ const current = { rxBytes: snap.rxBytes, txBytes: snap.txBytes, timestamp: now };
45763
+ if (!_lastNetSnapshot) {
45764
+ _lastNetSnapshot = current;
45765
+ return { rxBytesPerSec: 0, txBytesPerSec: 0 };
45766
+ }
45767
+ const dtSec = Math.max(0.1, (now - _lastNetSnapshot.timestamp) / 1e3);
45768
+ const rxRate = Math.max(0, (current.rxBytes - _lastNetSnapshot.rxBytes) / dtSec);
45769
+ const txRate = Math.max(0, (current.txBytes - _lastNetSnapshot.txBytes) / dtSec);
45770
+ _lastNetSnapshot = current;
45771
+ return { rxBytesPerSec: rxRate, txBytesPerSec: txRate };
45772
+ }
45773
+ if (plat === "darwin") {
45774
+ try {
45775
+ const output = await new Promise((resolve31, reject) => {
45776
+ exec3("netstat -ib 2>/dev/null | head -30", { encoding: "utf8", timeout: 3e3 }, (err, stdout) => err ? reject(err) : resolve31(stdout));
45777
+ });
45778
+ let rxBytes = 0, txBytes = 0;
45779
+ for (const line of output.split("\n")) {
45780
+ if (line.includes("en0") && !line.includes("Name")) {
45781
+ const parts = line.trim().split(/\s+/);
45782
+ if (parts.length >= 10) {
45783
+ rxBytes = parseInt(parts[6] ?? "0", 10) || 0;
45784
+ txBytes = parseInt(parts[9] ?? "0", 10) || 0;
45785
+ break;
45786
+ }
45787
+ }
45788
+ }
45789
+ const current = { rxBytes, txBytes, timestamp: now };
45790
+ if (!_lastNetSnapshot) {
45791
+ _lastNetSnapshot = current;
45792
+ return { rxBytesPerSec: 0, txBytesPerSec: 0 };
45793
+ }
45794
+ const dtSec = Math.max(0.1, (now - _lastNetSnapshot.timestamp) / 1e3);
45795
+ const rxRate = Math.max(0, (current.rxBytes - _lastNetSnapshot.rxBytes) / dtSec);
45796
+ const txRate = Math.max(0, (current.txBytes - _lastNetSnapshot.txBytes) / dtSec);
45797
+ _lastNetSnapshot = current;
45798
+ return { rxBytesPerSec: rxRate, txBytesPerSec: txRate };
45799
+ } catch {
45800
+ return { rxBytesPerSec: 0, txBytesPerSec: 0 };
45801
+ }
45802
+ }
45803
+ return { rxBytesPerSec: 0, txBytesPerSec: 0 };
45804
+ }
45805
+ async function collectGpuMetrics() {
45806
+ const noGpu = { available: false, name: "", utilization: 0, vramUsedMB: 0, vramTotalMB: 0, vramUtilization: 0 };
45807
+ if (_nvidiaSmiAvailable === false)
45808
+ return noGpu;
45809
+ try {
45810
+ const smi = await new Promise((resolve31, reject) => {
45811
+ exec3("nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 3e3 }, (err, stdout) => err ? reject(err) : resolve31(stdout));
45812
+ });
45813
+ _nvidiaSmiAvailable = true;
45814
+ const line = smi.trim().split("\n")[0];
45815
+ if (!line)
45816
+ return noGpu;
45817
+ const parts = line.split(",").map((s) => s.trim());
45818
+ const vramUsed = parseInt(parts[1] ?? "0", 10) || 0;
45819
+ const vramTotal = parseInt(parts[2] ?? "0", 10) || 0;
45820
+ return {
45821
+ available: true,
45822
+ utilization: parseInt(parts[0] ?? "0", 10) || 0,
45823
+ vramUsedMB: vramUsed,
45824
+ vramTotalMB: vramTotal,
45825
+ name: parts[3] ?? "",
45826
+ vramUtilization: vramTotal > 0 ? Math.round(vramUsed / vramTotal * 100) : 0
45827
+ };
45828
+ } catch {
45829
+ _nvidiaSmiAvailable = false;
45830
+ return noGpu;
45831
+ }
45832
+ }
45833
+ function collectCpuRam() {
45834
+ const [l1] = loadavg2();
45835
+ const cores = cpus2().length;
45836
+ const cpuModel = cpus2()[0]?.model ?? "";
45837
+ const totalMem = totalmem2();
45838
+ const usedMem = totalMem - freemem2();
45839
+ return {
45840
+ cpuUtil: Math.min(100, Math.round(l1 / cores * 100)),
45841
+ cpuCores: cores,
45842
+ cpuModel,
45843
+ memUtil: Math.round(usedMem / totalMem * 100),
45844
+ memUsedGB: Math.round(usedMem / 1024 ** 3 * 10) / 10,
45845
+ memTotalGB: Math.round(totalMem / 1024 ** 3 * 10) / 10
45846
+ };
45847
+ }
45848
+ async function collectLocalMetrics() {
45849
+ const cpuRam = collectCpuRam();
45850
+ const [gpu, network] = await Promise.all([
45851
+ collectGpuMetrics(),
45852
+ collectNetworkMetrics()
45853
+ ]);
45854
+ return {
45855
+ source: "local",
45856
+ hardware: {
45857
+ cpuUtil: cpuRam.cpuUtil,
45858
+ cpuCores: cpuRam.cpuCores,
45859
+ cpuModel: cpuRam.cpuModel,
45860
+ gpuUtil: gpu.available ? gpu.utilization : -1,
45861
+ gpuName: gpu.name,
45862
+ vramUtil: gpu.available ? gpu.vramUtilization : -1,
45863
+ vramUsedMB: gpu.vramUsedMB,
45864
+ vramTotalMB: gpu.vramTotalMB,
45865
+ memUtil: cpuRam.memUtil,
45866
+ memUsedGB: cpuRam.memUsedGB,
45867
+ memTotalGB: cpuRam.memTotalGB
45868
+ },
45869
+ network
45870
+ };
45871
+ }
45872
+ var _lastNetSnapshot, _nvidiaSmiAvailable, SystemMetricsCollector;
45873
+ var init_system_metrics = __esm({
45874
+ "packages/cli/dist/tui/system-metrics.js"() {
45875
+ "use strict";
45876
+ _lastNetSnapshot = null;
45877
+ _nvidiaSmiAvailable = null;
45878
+ SystemMetricsCollector = class {
45879
+ _timer = null;
45880
+ _source = "local";
45881
+ _callback = null;
45882
+ _latest = null;
45883
+ _intervalMs = 3e3;
45884
+ _collecting = false;
45885
+ /** Current metrics snapshot (may be null before first poll) */
45886
+ get latest() {
45887
+ return this._latest;
45888
+ }
45889
+ /** Current source mode */
45890
+ get source() {
45891
+ return this._source;
45892
+ }
45893
+ /**
45894
+ * Start collecting LOCAL metrics on an interval.
45895
+ * @param callback Called each time new metrics arrive
45896
+ * @param intervalMs Polling interval (default 3000ms)
45897
+ */
45898
+ startLocal(callback, intervalMs = 3e3) {
45899
+ this.stop();
45900
+ this._source = "local";
45901
+ this._callback = callback;
45902
+ this._intervalMs = intervalMs;
45903
+ this._poll();
45904
+ this._timer = setInterval(() => this._poll(), intervalMs);
45905
+ }
45906
+ /**
45907
+ * Switch to REMOTE mode — stops local polling, accepts pushed metrics.
45908
+ * Call pushRemoteMetrics() to feed data from the remote provider.
45909
+ */
45910
+ startRemote(callback) {
45911
+ this.stop();
45912
+ this._source = "remote";
45913
+ this._callback = callback;
45914
+ this._timer = setInterval(() => this._pollNetworkOnly(), this._intervalMs);
45915
+ this._pollNetworkOnly();
45916
+ }
45917
+ /**
45918
+ * Push hardware metrics received from a remote /expose provider.
45919
+ * Network metrics are always collected locally regardless.
45920
+ */
45921
+ pushRemoteMetrics(hw) {
45922
+ if (this._source !== "remote")
45923
+ return;
45924
+ const hardware = {
45925
+ cpuUtil: hw.cpuUtil ?? -1,
45926
+ cpuCores: hw.cpuCores ?? 0,
45927
+ cpuModel: hw.cpuModel ?? "",
45928
+ gpuUtil: hw.gpuUtil ?? -1,
45929
+ gpuName: hw.gpuName ?? "",
45930
+ vramUtil: hw.vramUtil ?? -1,
45931
+ vramUsedMB: hw.vramUsedMB ?? 0,
45932
+ vramTotalMB: hw.vramTotalMB ?? 0,
45933
+ memUtil: hw.memUtil ?? -1,
45934
+ memUsedGB: hw.memUsedGB ?? 0,
45935
+ memTotalGB: hw.memTotalGB ?? 0
45936
+ };
45937
+ this._latest = {
45938
+ source: "remote",
45939
+ hardware,
45940
+ network: this._latest?.network ?? { rxBytesPerSec: 0, txBytesPerSec: 0 }
45941
+ };
45942
+ this._callback?.(this._latest);
45943
+ }
45944
+ /** Stop all polling and clear state */
45945
+ stop() {
45946
+ if (this._timer) {
45947
+ clearInterval(this._timer);
45948
+ this._timer = null;
45949
+ }
45950
+ this._latest = null;
45951
+ this._callback = null;
45952
+ this._collecting = false;
45953
+ }
45954
+ /** Whether the collector is active */
45955
+ get isActive() {
45956
+ return this._timer !== null;
45957
+ }
45958
+ async _poll() {
45959
+ if (this._collecting)
45960
+ return;
45961
+ this._collecting = true;
45962
+ try {
45963
+ this._latest = await collectLocalMetrics();
45964
+ this._callback?.(this._latest);
45965
+ } catch {
45966
+ }
45967
+ this._collecting = false;
45968
+ }
45969
+ async _pollNetworkOnly() {
45970
+ try {
45971
+ const network = await collectNetworkMetrics();
45972
+ if (this._latest) {
45973
+ this._latest = { ...this._latest, network };
45974
+ this._callback?.(this._latest);
45975
+ }
45976
+ } catch {
45977
+ }
45978
+ }
45979
+ };
45980
+ }
45981
+ });
45982
+
45471
45983
  // packages/cli/dist/tui/status-bar.js
45472
45984
  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;
45473
45985
  var init_status_bar = __esm({
@@ -45475,6 +45987,7 @@ var init_status_bar = __esm({
45475
45987
  "use strict";
45476
45988
  init_render();
45477
45989
  init_braille_spinner();
45990
+ init_system_metrics();
45478
45991
  EXPERT_TOOL_BASELINES = {
45479
45992
  file_read: 12,
45480
45993
  structured_read: 15,
@@ -45894,39 +46407,77 @@ var init_status_bar = __esm({
45894
46407
  if (this.active)
45895
46408
  this.renderFooterPreserveCursor();
45896
46409
  }
45897
- /** Remote host metrics (when connected to a remote endpoint) */
45898
- _remoteMetrics = null;
46410
+ /** Unified system metrics collector (local or remote) */
46411
+ _metricsCollector = new SystemMetricsCollector();
46412
+ /** Cached unified metrics snapshot — updated by collector callback */
46413
+ _unifiedMetrics = null;
46414
+ /** Legacy remote metrics polling timer (for peer/HTTP polling) */
45899
46415
  _remoteMetricsTimer = null;
45900
46416
  /** Update remote host system metrics (from polling /v1/system/metrics) */
45901
46417
  setRemoteMetrics(metrics) {
45902
- this._remoteMetrics = {
45903
- ...metrics,
46418
+ if (!this._metricsCollector.isActive || this._metricsCollector.source !== "remote") {
46419
+ this._metricsCollector.startRemote((m) => {
46420
+ this._unifiedMetrics = m;
46421
+ if (this.active)
46422
+ this.renderFooterPreserveCursor();
46423
+ });
46424
+ }
46425
+ this._metricsCollector.pushRemoteMetrics({
46426
+ cpuUtil: metrics.cpuUtil,
45904
46427
  cpuCores: metrics.cpuCores ?? 0,
45905
46428
  cpuModel: metrics.cpuModel ?? "",
46429
+ gpuUtil: metrics.gpuUtil,
46430
+ gpuName: metrics.gpuName,
46431
+ vramUtil: metrics.vramUtil,
45906
46432
  vramUsedMB: metrics.vramUsedMB ?? 0,
45907
46433
  vramTotalMB: metrics.vramTotalMB ?? 0,
46434
+ memUtil: metrics.memUtil,
45908
46435
  memTotalGB: metrics.memTotalGB ?? 0,
45909
46436
  memUsedGB: metrics.memUsedGB ?? 0
45910
- };
45911
- if (this.active)
45912
- this.renderFooterPreserveCursor();
46437
+ });
45913
46438
  }
45914
- /** Clear remote metrics (e.g. when switching to local endpoint) */
46439
+ /** Clear remote metrics and switch back to local collection */
45915
46440
  clearRemoteMetrics() {
45916
- this._remoteMetrics = null;
46441
+ this._unifiedMetrics = null;
45917
46442
  if (this._remoteMetricsTimer) {
45918
46443
  clearInterval(this._remoteMetricsTimer);
45919
46444
  this._remoteMetricsTimer = null;
45920
46445
  }
46446
+ this.startLocalMetrics();
45921
46447
  if (this.active)
45922
46448
  this.renderFooterPreserveCursor();
45923
46449
  }
46450
+ /**
46451
+ * Start collecting LOCAL system metrics (CPU/RAM/GPU + network I/O).
46452
+ * Called automatically on startup when endpoint is local.
46453
+ */
46454
+ startLocalMetrics(intervalMs = 3e3) {
46455
+ this._metricsCollector.startLocal((m) => {
46456
+ this._unifiedMetrics = m;
46457
+ if (this.active)
46458
+ this.renderFooterPreserveCursor();
46459
+ }, intervalMs);
46460
+ }
46461
+ /** Stop all metrics collection (local and remote) */
46462
+ stopAllMetrics() {
46463
+ this._metricsCollector.stop();
46464
+ this._unifiedMetrics = null;
46465
+ if (this._remoteMetricsTimer) {
46466
+ clearInterval(this._remoteMetricsTimer);
46467
+ this._remoteMetricsTimer = null;
46468
+ }
46469
+ }
45924
46470
  /**
45925
46471
  * Start polling a remote endpoint for system metrics.
45926
46472
  * Call when /endpoint is set to a remote URL that has /v1/system/metrics.
45927
46473
  */
45928
46474
  startRemoteMetricsPolling(endpointUrl, authKey) {
45929
46475
  this.stopRemoteMetricsPolling();
46476
+ this._metricsCollector.startRemote((m) => {
46477
+ this._unifiedMetrics = m;
46478
+ if (this.active)
46479
+ this.renderFooterPreserveCursor();
46480
+ });
45930
46481
  const poll = async () => {
45931
46482
  try {
45932
46483
  const url = new URL("/v1/system/metrics", endpointUrl);
@@ -45957,6 +46508,11 @@ var init_status_bar = __esm({
45957
46508
  */
45958
46509
  startPeerMetricsPolling(sendCommand, peerId, authKey) {
45959
46510
  this.stopRemoteMetricsPolling();
46511
+ this._metricsCollector.startRemote((m) => {
46512
+ this._unifiedMetrics = m;
46513
+ if (this.active)
46514
+ this.renderFooterPreserveCursor();
46515
+ });
45960
46516
  const poll = async () => {
45961
46517
  try {
45962
46518
  const queryData = { type: "query" };
@@ -46037,7 +46593,7 @@ var init_status_bar = __esm({
46037
46593
  clearInterval(this._remoteMetricsTimer);
46038
46594
  this._remoteMetricsTimer = null;
46039
46595
  }
46040
- this._remoteMetrics = null;
46596
+ this._unifiedMetrics = null;
46041
46597
  }
46042
46598
  /** Update token metrics from a token_usage event */
46043
46599
  updateMetrics(update) {
@@ -46098,10 +46654,14 @@ var init_status_bar = __esm({
46098
46654
  this.applyScrollRegion();
46099
46655
  this.renderFooterAndPositionInput();
46100
46656
  this.hookStdin();
46657
+ if (!this._metricsCollector.isActive) {
46658
+ this.startLocalMetrics();
46659
+ }
46101
46660
  }
46102
46661
  /** Deactivate — restore full-screen scroll region */
46103
46662
  deactivate() {
46104
46663
  this.active = false;
46664
+ this.stopAllMetrics();
46105
46665
  const rows = process.stdout.rows ?? 24;
46106
46666
  process.stdout.write(`\x1B[1;${rows}r`);
46107
46667
  }
@@ -46437,47 +46997,59 @@ var init_status_bar = __esm({
46437
46997
  empty: false
46438
46998
  });
46439
46999
  }
46440
- if (this._remoteMetrics) {
46441
- const rm2 = this._remoteMetrics;
46442
- const cpuColor = rm2.cpuUtil > 80 ? c2.red : rm2.cpuUtil > 50 ? c2.yellow : c2.green;
46443
- const cpuCoresInfo = rm2.cpuCores > 0 ? ` (${rm2.cpuCores}c` + (rm2.cpuModel ? " " + rm2.cpuModel.replace(/\s+/g, " ").slice(0, 30) : "") + ")" : "";
46444
- const cpuExpStr = `CPU:${cpuColor(rm2.cpuUtil + "%")}${c2.dim(cpuCoresInfo)}`;
46445
- const cpuCompStr = `CPU:${cpuColor(rm2.cpuUtil + "%")}`;
46446
- const cpuExpW = 4 + `${rm2.cpuUtil}%`.length + cpuCoresInfo.length;
46447
- const cpuCompW = 4 + `${rm2.cpuUtil}%`.length;
46448
- const memColor = rm2.memUtil > 80 ? c2.red : rm2.memUtil > 50 ? c2.yellow : c2.green;
46449
- const memDetail = rm2.memTotalGB > 0 ? ` (${rm2.memUsedGB}/${rm2.memTotalGB}GB)` : "";
46450
- const memExpStr = ` RAM:${memColor(rm2.memUtil + "%")}${c2.dim(memDetail)}`;
46451
- const memCompStr = ` RAM:${memColor(rm2.memUtil + "%")}`;
46452
- const memExpW = 5 + `${rm2.memUtil}%`.length + memDetail.length;
46453
- const memCompW = 5 + `${rm2.memUtil}%`.length;
46454
- let gpuExpStr = "";
46455
- let gpuCompStr = "";
46456
- let gpuExpW = 0;
46457
- let gpuCompW = 0;
47000
+ if (this._unifiedMetrics) {
47001
+ const um = this._unifiedMetrics;
47002
+ const rm2 = um.hardware;
47003
+ const isLocal = um.source === "local";
47004
+ const srcTag = isLocal ? pastel2(120, "L") : pastel2(117, "R");
47005
+ const srcW = 1;
47006
+ let hwExpStr = "";
47007
+ let hwCompStr = "";
47008
+ let hwExpW = 0;
47009
+ let hwCompW = 0;
47010
+ if (rm2.cpuUtil >= 0) {
47011
+ const cpuColor = rm2.cpuUtil > 80 ? c2.red : rm2.cpuUtil > 50 ? c2.yellow : c2.green;
47012
+ const cpuCoresInfo = rm2.cpuCores > 0 ? ` (${rm2.cpuCores}c` + (rm2.cpuModel ? " " + rm2.cpuModel.replace(/\s+/g, " ").slice(0, 30) : "") + ")" : "";
47013
+ hwExpStr += `CPU:${cpuColor(rm2.cpuUtil + "%")}${c2.dim(cpuCoresInfo)}`;
47014
+ hwCompStr += `CPU:${cpuColor(rm2.cpuUtil + "%")}`;
47015
+ hwExpW += 4 + `${rm2.cpuUtil}%`.length + cpuCoresInfo.length;
47016
+ hwCompW += 4 + `${rm2.cpuUtil}%`.length;
47017
+ }
47018
+ if (rm2.memUtil >= 0) {
47019
+ const memColor = rm2.memUtil > 80 ? c2.red : rm2.memUtil > 50 ? c2.yellow : c2.green;
47020
+ const memDetail = rm2.memTotalGB > 0 ? ` (${rm2.memUsedGB}/${rm2.memTotalGB}GB)` : "";
47021
+ hwExpStr += ` RAM:${memColor(rm2.memUtil + "%")}${c2.dim(memDetail)}`;
47022
+ hwCompStr += ` RAM:${memColor(rm2.memUtil + "%")}`;
47023
+ hwExpW += 5 + `${rm2.memUtil}%`.length + memDetail.length;
47024
+ hwCompW += 5 + `${rm2.memUtil}%`.length;
47025
+ }
46458
47026
  if (rm2.gpuUtil >= 0) {
46459
47027
  const gpuColor = rm2.gpuUtil > 80 ? c2.red : rm2.gpuUtil > 50 ? c2.yellow : c2.green;
46460
47028
  const gpuNameShort = rm2.gpuName ? ` (${rm2.gpuName.slice(0, 20)})` : "";
46461
- gpuExpStr = ` GPU:${gpuColor(rm2.gpuUtil + "%")}${c2.dim(gpuNameShort)}`;
46462
- gpuCompStr = ` GPU:${gpuColor(rm2.gpuUtil + "%")}`;
46463
- gpuExpW = 5 + `${rm2.gpuUtil}%`.length + gpuNameShort.length;
46464
- gpuCompW = 5 + `${rm2.gpuUtil}%`.length;
47029
+ hwExpStr += ` GPU:${gpuColor(rm2.gpuUtil + "%")}${c2.dim(gpuNameShort)}`;
47030
+ hwCompStr += ` GPU:${gpuColor(rm2.gpuUtil + "%")}`;
47031
+ hwExpW += 5 + `${rm2.gpuUtil}%`.length + gpuNameShort.length;
47032
+ hwCompW += 5 + `${rm2.gpuUtil}%`.length;
46465
47033
  }
46466
- let vramStr = "";
46467
- let vramW = 0;
46468
47034
  if (rm2.vramUtil >= 0) {
46469
47035
  const vramColor = rm2.vramUtil > 80 ? c2.red : rm2.vramUtil > 50 ? c2.yellow : c2.green;
46470
47036
  const vramDetail = rm2.vramTotalMB > 0 ? ` (${rm2.vramUsedMB}/${rm2.vramTotalMB}MB)` : "";
46471
- vramStr = ` VRAM:${vramColor(rm2.vramUtil + "%")}${c2.dim(vramDetail)}`;
46472
- vramW = 6 + `${rm2.vramUtil}%`.length + vramDetail.length;
46473
- }
46474
- const remoteExpanded = pastel2(117, "\u{1F4E1}") + " " + cpuExpStr + memExpStr + gpuExpStr + vramStr;
46475
- const remoteCompact = pastel2(117, "\u{1F4E1}") + " " + cpuCompStr + gpuCompStr;
47037
+ hwExpStr += ` VRAM:${vramColor(rm2.vramUtil + "%")}${c2.dim(vramDetail)}`;
47038
+ hwExpW += 6 + `${rm2.vramUtil}%`.length + vramDetail.length;
47039
+ }
47040
+ const net = um.network;
47041
+ const rxStr = formatRate(net.rxBytesPerSec);
47042
+ const txStr = formatRate(net.txBytesPerSec);
47043
+ const netExpStr = ` ${c2.dim("i:")}${pastel2(116, rxStr)} ${c2.dim("o:")}${pastel2(218, txStr)}`;
47044
+ const netCompStr = ` ${c2.dim("i:")}${pastel2(116, rxStr)} ${c2.dim("o:")}${pastel2(218, txStr)}`;
47045
+ const netW = 3 + rxStr.length + 3 + txStr.length;
47046
+ const sysExpanded = srcTag + " " + hwExpStr + netExpStr;
47047
+ const sysCompact = srcTag + " " + hwCompStr + netCompStr;
46476
47048
  sections.push({
46477
- expanded: remoteExpanded,
46478
- compact: remoteCompact,
46479
- expandedW: 3 + cpuExpW + memExpW + gpuExpW + vramW,
46480
- compactW: 3 + cpuCompW + gpuCompW,
47049
+ expanded: sysExpanded,
47050
+ compact: sysCompact,
47051
+ expandedW: srcW + 1 + hwExpW + netW,
47052
+ compactW: srcW + 1 + hwCompW + netW,
46481
47053
  empty: false
46482
47054
  });
46483
47055
  }
@@ -46817,7 +47389,7 @@ import { cwd } from "node:process";
46817
47389
  import { resolve as resolve28, join as join50, dirname as dirname18, extname as extname9 } from "node:path";
46818
47390
  import { createRequire as createRequire2 } from "node:module";
46819
47391
  import { fileURLToPath as fileURLToPath12 } from "node:url";
46820
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync17, appendFileSync as appendFileSync3, rmSync as rmSync2, readdirSync as readdirSync15, mkdirSync as mkdirSync19 } from "node:fs";
47392
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync17, appendFileSync as appendFileSync3, rmSync as rmSync2, readdirSync as readdirSync16, mkdirSync as mkdirSync19 } from "node:fs";
46821
47393
  import { existsSync as existsSync40 } from "node:fs";
46822
47394
  import { homedir as homedir13 } from "node:os";
46823
47395
  function formatTimeAgo(date) {
@@ -47057,7 +47629,7 @@ function gatherMemorySnippets(root) {
47057
47629
  if (!existsSync40(dir))
47058
47630
  continue;
47059
47631
  try {
47060
- for (const f of readdirSync15(dir).filter((f2) => f2.endsWith(".json"))) {
47632
+ for (const f of readdirSync16(dir).filter((f2) => f2.endsWith(".json"))) {
47061
47633
  const data = JSON.parse(readFileSync30(join50(dir, f), "utf-8"));
47062
47634
  for (const val of Object.values(data)) {
47063
47635
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
@@ -48503,6 +49075,21 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
48503
49075
  async voiceGenerateCloneRef(sourceModelId) {
48504
49076
  return voiceEngine.generateCloneRef(sourceModelId);
48505
49077
  },
49078
+ voiceListClones() {
49079
+ return voiceEngine.listCloneRefs();
49080
+ },
49081
+ voiceDeleteClone(filename) {
49082
+ return voiceEngine.deleteCloneRef(filename);
49083
+ },
49084
+ voiceRenameClone(filename, newName) {
49085
+ voiceEngine.renameCloneRef(filename, newName);
49086
+ },
49087
+ voiceSetActiveClone(filename) {
49088
+ return voiceEngine.setActiveCloneRef(filename);
49089
+ },
49090
+ voiceSpeak(text) {
49091
+ voiceEngine.speak(text);
49092
+ },
48506
49093
  streamToggle() {
48507
49094
  streamEnabled = !streamEnabled;
48508
49095
  return streamEnabled;
@@ -49961,8 +50548,8 @@ NEW TASK: ${fullInput}`;
49961
50548
  try {
49962
50549
  const updateInfo = await checkForUpdate(version);
49963
50550
  if (updateInfo) {
49964
- const { exec: exec3 } = await import("node:child_process");
49965
- exec3(`npm install -g open-agents-ai@latest --prefer-online`, { timeout: 18e4 }, (err) => {
50551
+ const { exec: exec4 } = await import("node:child_process");
50552
+ exec4(`npm install -g open-agents-ai@latest --prefer-online`, { timeout: 18e4 }, (err) => {
49966
50553
  if (!err) {
49967
50554
  writeContent(() => renderInfo(`Updated to v${updateInfo.latestVersion} in background. Takes effect next session.`));
49968
50555
  }
@@ -50260,7 +50847,7 @@ var init_run = __esm({
50260
50847
  // packages/indexer/dist/codebase-indexer.js
50261
50848
  import { glob } from "glob";
50262
50849
  import ignore from "ignore";
50263
- import { readFile as readFile16, stat as stat4 } from "node:fs/promises";
50850
+ import { readFile as readFile17, stat as stat4 } from "node:fs/promises";
50264
50851
  import { createHash as createHash4 } from "node:crypto";
50265
50852
  import { join as join51, relative as relative3, extname as extname10, basename as basename15 } from "node:path";
50266
50853
  var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
@@ -50306,7 +50893,7 @@ var init_codebase_indexer = __esm({
50306
50893
  const ig = ignore.default();
50307
50894
  if (this.config.respectGitignore) {
50308
50895
  try {
50309
- const gitignoreContent = await readFile16(join51(this.config.rootDir, ".gitignore"), "utf-8");
50896
+ const gitignoreContent = await readFile17(join51(this.config.rootDir, ".gitignore"), "utf-8");
50310
50897
  ig.add(gitignoreContent);
50311
50898
  } catch {
50312
50899
  }
@@ -50326,7 +50913,7 @@ var init_codebase_indexer = __esm({
50326
50913
  const fileStat = await stat4(fullPath);
50327
50914
  if (fileStat.size > this.config.maxFileSize)
50328
50915
  continue;
50329
- const content = await readFile16(fullPath);
50916
+ const content = await readFile17(fullPath);
50330
50917
  const hash = createHash4("sha256").update(content).digest("hex");
50331
50918
  const ext = extname10(relativePath);
50332
50919
  indexed.push({
@@ -50450,7 +51037,7 @@ __export(index_repo_exports, {
50450
51037
  indexRepoCommand: () => indexRepoCommand
50451
51038
  });
50452
51039
  import { resolve as resolve29 } from "node:path";
50453
- import { existsSync as existsSync41, statSync as statSync11 } from "node:fs";
51040
+ import { existsSync as existsSync41, statSync as statSync12 } from "node:fs";
50454
51041
  import { cwd as cwd2 } from "node:process";
50455
51042
  async function indexRepoCommand(opts, _config) {
50456
51043
  const repoRoot = resolve29(opts.repoPath ?? cwd2());
@@ -50460,7 +51047,7 @@ async function indexRepoCommand(opts, _config) {
50460
51047
  printError(`Path does not exist: ${repoRoot}`);
50461
51048
  process.exit(1);
50462
51049
  }
50463
- const stat5 = statSync11(repoRoot);
51050
+ const stat5 = statSync12(repoRoot);
50464
51051
  if (!stat5.isDirectory()) {
50465
51052
  printError(`Path is not a directory: ${repoRoot}`);
50466
51053
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.86",
3
+ "version": "0.103.88",
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",