open-agents-ai 0.150.0 → 0.151.0

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 +381 -104
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20524,6 +20524,160 @@ var init_repo_map = __esm({
20524
20524
  }
20525
20525
  });
20526
20526
 
20527
+ // packages/execution/dist/tools/process-health.js
20528
+ import { execSync as execSync22 } from "node:child_process";
20529
+ function getSystemStatus() {
20530
+ const lines = ["# System Health\n"];
20531
+ try {
20532
+ const uptime = execSync22("uptime", { encoding: "utf-8", timeout: 3e3 }).trim();
20533
+ const loadMatch = uptime.match(/load average:\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
20534
+ if (loadMatch) {
20535
+ lines.push(`CPU Load: ${loadMatch[1]} (1m) ${loadMatch[2]} (5m) ${loadMatch[3]} (15m)`);
20536
+ }
20537
+ } catch {
20538
+ }
20539
+ try {
20540
+ const cpuCount = __require("node:os").cpus().length;
20541
+ lines.push(`CPU Cores: ${cpuCount}`);
20542
+ } catch {
20543
+ }
20544
+ try {
20545
+ const mem = execSync22("free -h | head -2", { encoding: "utf-8", timeout: 3e3 }).trim();
20546
+ lines.push(`
20547
+ ${mem}`);
20548
+ } catch {
20549
+ }
20550
+ try {
20551
+ const top = execSync22("ps aux --sort=-%cpu | head -8", { encoding: "utf-8", timeout: 3e3 }).trim();
20552
+ lines.push(`
20553
+ Top processes by CPU:
20554
+ ${top}`);
20555
+ } catch {
20556
+ }
20557
+ try {
20558
+ const nodeCount = execSync22("ps aux | grep 'node ' | grep -v grep | wc -l", { encoding: "utf-8", timeout: 3e3 }).trim();
20559
+ lines.push(`
20560
+ Node.js processes: ${nodeCount}`);
20561
+ } catch {
20562
+ }
20563
+ return lines.join("\n");
20564
+ }
20565
+ function findOrphans() {
20566
+ try {
20567
+ const psOutput = execSync22(`ps -eo pid,ppid,%cpu,%mem,etime,args --no-headers 2>/dev/null | grep -E "${OA_PATTERNS}" | grep -v grep`, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
20568
+ if (!psOutput)
20569
+ return "No OA-related orphan processes found.";
20570
+ const myPid = process.pid;
20571
+ const myPpid = process.ppid;
20572
+ const lines = ["OA-related processes:\n"];
20573
+ lines.push(" PID PPID CPU% MEM% ELAPSED COMMAND");
20574
+ lines.push(" " + "-".repeat(75));
20575
+ for (const line of psOutput.split("\n")) {
20576
+ const parts = line.trim().split(/\s+/);
20577
+ const pid = parseInt(parts[0], 10);
20578
+ if (pid === myPid || pid === myPpid)
20579
+ continue;
20580
+ const ppid = parts[1];
20581
+ const cpu = parts[2];
20582
+ const mem = parts[3];
20583
+ const elapsed = parts[4];
20584
+ const cmd = parts.slice(5).join(" ");
20585
+ const shortCmd = cmd.length > 50 ? cmd.slice(0, 47) + "..." : cmd;
20586
+ const isHot = parseFloat(cpu) > 10 ? " \u26A0 HIGH CPU" : "";
20587
+ lines.push(` ${String(pid).padStart(7)} ${String(ppid).padStart(7)} ${String(cpu).padStart(6)} ${String(mem).padStart(5)} ${String(elapsed).padStart(9)} ${shortCmd}${isHot}`);
20588
+ }
20589
+ lines.push(`
20590
+ To kill a process: process_health(action='kill', pid=PID)`);
20591
+ lines.push(`Or from the prompt: /destroy processes --global`);
20592
+ return lines.join("\n");
20593
+ } catch {
20594
+ return "Could not scan for orphan processes.";
20595
+ }
20596
+ }
20597
+ function killProcess(pid) {
20598
+ try {
20599
+ try {
20600
+ process.kill(-pid, "SIGTERM");
20601
+ } catch {
20602
+ }
20603
+ process.kill(pid, "SIGTERM");
20604
+ try {
20605
+ execSync22(`sleep 1 && kill -0 ${pid} 2>/dev/null && kill -9 ${pid} 2>/dev/null`, {
20606
+ timeout: 3e3,
20607
+ stdio: "pipe"
20608
+ });
20609
+ } catch {
20610
+ }
20611
+ return `Killed PID ${pid} (SIGTERM \u2192 SIGKILL)`;
20612
+ } catch (err) {
20613
+ return `Failed to kill PID ${pid}: ${err instanceof Error ? err.message : String(err)}`;
20614
+ }
20615
+ }
20616
+ var OA_PATTERNS, ProcessHealthTool;
20617
+ var init_process_health = __esm({
20618
+ "packages/execution/dist/tools/process-health.js"() {
20619
+ "use strict";
20620
+ OA_PATTERNS = [
20621
+ "open-agents",
20622
+ "nexus-daemon",
20623
+ "vitest",
20624
+ "jest",
20625
+ "esbuild.*--service",
20626
+ "python3 -u -i",
20627
+ "chromium.*headless",
20628
+ "chrome.*headless",
20629
+ "crawlee-scraper",
20630
+ "web_scrape",
20631
+ "start-moondream",
20632
+ "live-whisper"
20633
+ ].join("|");
20634
+ ProcessHealthTool = class {
20635
+ name = "process_health";
20636
+ description = "Check system CPU/memory load and find orphaned processes. Actions: 'status' (CPU + memory + top processes), 'orphans' (find stale OA-related processes), 'kill' (kill a specific PID). Use this when the system feels slow or when a shell command seems stuck.";
20637
+ parameters = {
20638
+ type: "object",
20639
+ properties: {
20640
+ action: {
20641
+ type: "string",
20642
+ enum: ["status", "orphans", "kill"],
20643
+ description: "Action (default: status)"
20644
+ },
20645
+ pid: {
20646
+ type: "number",
20647
+ description: "Process ID to kill (for 'kill' action)"
20648
+ }
20649
+ },
20650
+ required: []
20651
+ };
20652
+ async execute(args) {
20653
+ const action = String(args["action"] ?? "status");
20654
+ const start = performance.now();
20655
+ try {
20656
+ switch (action) {
20657
+ case "status":
20658
+ return { success: true, output: getSystemStatus(), durationMs: performance.now() - start };
20659
+ case "orphans":
20660
+ return { success: true, output: findOrphans(), durationMs: performance.now() - start };
20661
+ case "kill": {
20662
+ const pid = Number(args["pid"]);
20663
+ if (!pid || pid <= 10)
20664
+ return { success: false, output: "", error: "Valid PID required", durationMs: performance.now() - start };
20665
+ if (pid === process.pid || pid === process.ppid) {
20666
+ return { success: false, output: "", error: "Cannot kill self or parent process", durationMs: performance.now() - start };
20667
+ }
20668
+ return { success: true, output: killProcess(pid), durationMs: performance.now() - start };
20669
+ }
20670
+ default:
20671
+ return { success: false, output: "", error: `Unknown action: ${action}`, durationMs: performance.now() - start };
20672
+ }
20673
+ } catch (err) {
20674
+ return { success: false, output: "", error: String(err instanceof Error ? err.message : err), durationMs: performance.now() - start };
20675
+ }
20676
+ }
20677
+ };
20678
+ }
20679
+ });
20680
+
20527
20681
  // packages/execution/dist/tools/fortemi-bridge.js
20528
20682
  import { existsSync as existsSync27, readFileSync as readFileSync20 } from "node:fs";
20529
20683
  import { join as join41 } from "node:path";
@@ -21321,6 +21475,7 @@ __export(dist_exports, {
21321
21475
  OcrPdfTool: () => OcrPdfTool,
21322
21476
  OpenCodeTool: () => OpenCodeTool,
21323
21477
  PdfToTextTool: () => PdfToTextTool,
21478
+ ProcessHealthTool: () => ProcessHealthTool,
21324
21479
  ReflectionIntegrityTool: () => ReflectionIntegrityTool,
21325
21480
  ReminderTool: () => ReminderTool,
21326
21481
  ReplTool: () => ReplTool,
@@ -21458,6 +21613,7 @@ var init_dist2 = __esm({
21458
21613
  init_semantic_map();
21459
21614
  init_change_log();
21460
21615
  init_repo_map();
21616
+ init_process_health();
21461
21617
  init_nexus();
21462
21618
  init_fortemi_bridge();
21463
21619
  init_system_deps();
@@ -28267,7 +28423,7 @@ __export(listen_exports, {
28267
28423
  isVideoPath: () => isVideoPath,
28268
28424
  waitForTranscribeCli: () => waitForTranscribeCli
28269
28425
  });
28270
- import { spawn as spawn15, execSync as execSync22 } from "node:child_process";
28426
+ import { spawn as spawn15, execSync as execSync23 } from "node:child_process";
28271
28427
  import { existsSync as existsSync31, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, readdirSync as readdirSync7 } from "node:fs";
28272
28428
  import { join as join46, dirname as dirname14 } from "node:path";
28273
28429
  import { homedir as homedir10 } from "node:os";
@@ -28289,7 +28445,7 @@ function findMicCaptureCommand() {
28289
28445
  const platform5 = process.platform;
28290
28446
  if (platform5 === "linux") {
28291
28447
  try {
28292
- execSync22("which arecord", { stdio: "pipe" });
28448
+ execSync23("which arecord", { stdio: "pipe" });
28293
28449
  return {
28294
28450
  cmd: "arecord",
28295
28451
  args: ["-f", "S16_LE", "-r", "16000", "-c", "1", "-t", "raw", "-q", "-"]
@@ -28299,7 +28455,7 @@ function findMicCaptureCommand() {
28299
28455
  }
28300
28456
  if (platform5 === "darwin") {
28301
28457
  try {
28302
- execSync22("which sox", { stdio: "pipe" });
28458
+ execSync23("which sox", { stdio: "pipe" });
28303
28459
  return {
28304
28460
  cmd: "sox",
28305
28461
  args: ["-d", "-t", "raw", "-r", "16000", "-c", "1", "-b", "16", "-e", "signed-integer", "-"]
@@ -28308,7 +28464,7 @@ function findMicCaptureCommand() {
28308
28464
  }
28309
28465
  }
28310
28466
  try {
28311
- execSync22("which ffmpeg", { stdio: "pipe" });
28467
+ execSync23("which ffmpeg", { stdio: "pipe" });
28312
28468
  if (platform5 === "linux") {
28313
28469
  return {
28314
28470
  cmd: "ffmpeg",
@@ -28367,7 +28523,7 @@ function findLiveWhisperScript() {
28367
28523
  return p;
28368
28524
  }
28369
28525
  try {
28370
- const globalRoot = execSync22("npm root -g", {
28526
+ const globalRoot = execSync23("npm root -g", {
28371
28527
  encoding: "utf-8",
28372
28528
  timeout: 5e3,
28373
28529
  stdio: ["pipe", "pipe", "pipe"]
@@ -28400,7 +28556,7 @@ function ensureTranscribeCliBackground() {
28400
28556
  return;
28401
28557
  _bgInstallPromise = (async () => {
28402
28558
  try {
28403
- const globalRoot = execSync22("npm root -g", {
28559
+ const globalRoot = execSync23("npm root -g", {
28404
28560
  encoding: "utf-8",
28405
28561
  timeout: 5e3,
28406
28562
  stdio: ["pipe", "pipe", "pipe"]
@@ -28606,7 +28762,7 @@ var init_listen = __esm({
28606
28762
  }
28607
28763
  if (!this.transcribeCliAvailable) {
28608
28764
  try {
28609
- execSync22("which transcribe-cli", { stdio: "pipe" });
28765
+ execSync23("which transcribe-cli", { stdio: "pipe" });
28610
28766
  this.transcribeCliAvailable = true;
28611
28767
  } catch {
28612
28768
  this.transcribeCliAvailable = false;
@@ -28623,7 +28779,7 @@ var init_listen = __esm({
28623
28779
  } catch {
28624
28780
  }
28625
28781
  try {
28626
- const globalRoot = execSync22("npm root -g", {
28782
+ const globalRoot = execSync23("npm root -g", {
28627
28783
  encoding: "utf-8",
28628
28784
  timeout: 5e3,
28629
28785
  stdio: ["pipe", "pipe", "pipe"]
@@ -28673,7 +28829,7 @@ var init_listen = __esm({
28673
28829
  }
28674
28830
  if (!tc) {
28675
28831
  try {
28676
- execSync22("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
28832
+ execSync23("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
28677
28833
  this.transcribeCliAvailable = null;
28678
28834
  tc = await this.loadTranscribeCli();
28679
28835
  } catch {
@@ -28856,7 +29012,7 @@ transcribe-cli error: ${transcribeCliError}` : "";
28856
29012
  }
28857
29013
  if (!tc) {
28858
29014
  try {
28859
- execSync22("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
29015
+ execSync23("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
28860
29016
  this.transcribeCliAvailable = null;
28861
29017
  tc = await this.loadTranscribeCli();
28862
29018
  } catch {
@@ -28910,7 +29066,7 @@ transcribe-cli error: ${transcribeCliError}` : "";
28910
29066
  }
28911
29067
  if (!tc) {
28912
29068
  try {
28913
- execSync22("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
29069
+ execSync23("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
28914
29070
  this.transcribeCliAvailable = null;
28915
29071
  tc = await this.loadTranscribeCli();
28916
29072
  } catch {
@@ -33358,7 +33514,9 @@ var init_render = __esm({
33358
33514
  ["/pause", "Pause after current turn finishes (gentle halt, /resume to continue)"],
33359
33515
  ["/stop", "Kill current inference immediately and save state (/resume to continue)"],
33360
33516
  ["/resume", "Resume a paused or stopped task"],
33361
- ["/destroy", "Remove .oa folder, kill all tasks, clear console, and exit"],
33517
+ ["/destroy processes", "Find and kill orphaned OA child processes (this project)"],
33518
+ ["/destroy processes --global", "Kill ALL orphaned OA processes system-wide"],
33519
+ ["/destroy project", "Remove .oa folder, kill all tasks, clear console, and exit"],
33362
33520
  ["/context save", "Force-save session context to .oa/context/"],
33363
33521
  ["/context restore", "Restore context from previous sessions into next task"],
33364
33522
  ["/context show", "Show saved session context status"],
@@ -33415,7 +33573,7 @@ var init_render = __esm({
33415
33573
 
33416
33574
  // packages/cli/dist/tui/voice-session.js
33417
33575
  import { createServer as createServer2 } from "node:http";
33418
- import { spawn as spawn16, execSync as execSync23 } from "node:child_process";
33576
+ import { spawn as spawn16, execSync as execSync24 } from "node:child_process";
33419
33577
  import { EventEmitter as EventEmitter2 } from "node:events";
33420
33578
  function generateFrontendHTML() {
33421
33579
  return `<!DOCTYPE html>
@@ -38045,7 +38203,7 @@ var init_oa_directory = __esm({
38045
38203
 
38046
38204
  // packages/cli/dist/tui/setup.js
38047
38205
  import * as readline from "node:readline";
38048
- import { execSync as execSync24, spawn as spawn18, exec as exec2 } from "node:child_process";
38206
+ import { execSync as execSync25, spawn as spawn18, exec as exec2 } from "node:child_process";
38049
38207
  import { promisify as promisify6 } from "node:util";
38050
38208
  import { existsSync as existsSync36, writeFileSync as writeFileSync15, readFileSync as readFileSync27, appendFileSync as appendFileSync2, mkdirSync as mkdirSync14 } from "node:fs";
38051
38209
  import { join as join52 } from "node:path";
@@ -38056,7 +38214,7 @@ function detectSystemSpecs() {
38056
38214
  let gpuVramGB = 0;
38057
38215
  let gpuName = "";
38058
38216
  try {
38059
- const memInfo = execSync24("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
38217
+ const memInfo = execSync25("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
38060
38218
  encoding: "utf8",
38061
38219
  timeout: 5e3
38062
38220
  });
@@ -38076,7 +38234,7 @@ function detectSystemSpecs() {
38076
38234
  } catch {
38077
38235
  }
38078
38236
  try {
38079
- const nvidiaSmi = execSync24("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
38237
+ const nvidiaSmi = execSync25("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
38080
38238
  const lines = nvidiaSmi.trim().split("\n");
38081
38239
  if (lines.length > 0) {
38082
38240
  for (const line of lines) {
@@ -38255,7 +38413,7 @@ function ensureCurl() {
38255
38413
  for (const s of strategies) {
38256
38414
  if (hasCmd(s.check)) {
38257
38415
  try {
38258
- execSync24(s.install, { stdio: "inherit", timeout: 12e4 });
38416
+ execSync25(s.install, { stdio: "inherit", timeout: 12e4 });
38259
38417
  if (hasCmd("curl")) {
38260
38418
  process.stdout.write(` ${c2.green("\u2714")} curl installed via ${s.label}.
38261
38419
  `);
@@ -38269,7 +38427,7 @@ function ensureCurl() {
38269
38427
  }
38270
38428
  if (plat === "darwin") {
38271
38429
  try {
38272
- execSync24("xcode-select --install", { stdio: "inherit", timeout: 3e5 });
38430
+ execSync25("xcode-select --install", { stdio: "inherit", timeout: 3e5 });
38273
38431
  if (hasCmd("curl"))
38274
38432
  return true;
38275
38433
  } catch {
@@ -38304,7 +38462,7 @@ function installOllamaLinux() {
38304
38462
 
38305
38463
  `);
38306
38464
  try {
38307
- execSync24("curl -fsSL https://ollama.com/install.sh | sh", {
38465
+ execSync25("curl -fsSL https://ollama.com/install.sh | sh", {
38308
38466
  stdio: "inherit",
38309
38467
  timeout: 3e5
38310
38468
  });
@@ -38332,7 +38490,7 @@ async function installOllamaMac(_rl) {
38332
38490
 
38333
38491
  `);
38334
38492
  try {
38335
- execSync24('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit", timeout: 6e5 });
38493
+ execSync25('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit", timeout: 6e5 });
38336
38494
  if (!hasCmd("brew")) {
38337
38495
  try {
38338
38496
  const brewPrefix = existsSync36("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local";
@@ -38365,7 +38523,7 @@ async function installOllamaMac(_rl) {
38365
38523
 
38366
38524
  `);
38367
38525
  try {
38368
- execSync24("brew install ollama", {
38526
+ execSync25("brew install ollama", {
38369
38527
  stdio: "inherit",
38370
38528
  timeout: 3e5
38371
38529
  });
@@ -38392,7 +38550,7 @@ function installOllamaWindows() {
38392
38550
 
38393
38551
  `);
38394
38552
  try {
38395
- execSync24('powershell -Command "irm https://ollama.com/install.ps1 | iex"', {
38553
+ execSync25('powershell -Command "irm https://ollama.com/install.ps1 | iex"', {
38396
38554
  stdio: "inherit",
38397
38555
  timeout: 3e5
38398
38556
  });
@@ -38473,7 +38631,7 @@ async function ensureOllamaRunning(backendUrl, rl) {
38473
38631
  }
38474
38632
  function pullModelWithAutoUpdate(tag) {
38475
38633
  try {
38476
- execSync24(`ollama pull ${tag}`, {
38634
+ execSync25(`ollama pull ${tag}`, {
38477
38635
  stdio: "inherit",
38478
38636
  timeout: 36e5
38479
38637
  // 1 hour max
@@ -38493,7 +38651,7 @@ function pullModelWithAutoUpdate(tag) {
38493
38651
 
38494
38652
  `);
38495
38653
  try {
38496
- execSync24("curl -fsSL https://ollama.com/install.sh | sh", {
38654
+ execSync25("curl -fsSL https://ollama.com/install.sh | sh", {
38497
38655
  stdio: "inherit",
38498
38656
  timeout: 3e5
38499
38657
  // 5 min max for install
@@ -38504,7 +38662,7 @@ function pullModelWithAutoUpdate(tag) {
38504
38662
  process.stdout.write(` ${c2.cyan("\u25CF")} Retrying pull of ${c2.bold(tag)}...
38505
38663
 
38506
38664
  `);
38507
- execSync24(`ollama pull ${tag}`, {
38665
+ execSync25(`ollama pull ${tag}`, {
38508
38666
  stdio: "inherit",
38509
38667
  timeout: 36e5
38510
38668
  });
@@ -38606,7 +38764,7 @@ function ensurePython3() {
38606
38764
  if (plat === "darwin") {
38607
38765
  if (hasCmd("brew")) {
38608
38766
  try {
38609
- execSync24("brew install python3", { stdio: "inherit", timeout: 3e5 });
38767
+ execSync25("brew install python3", { stdio: "inherit", timeout: 3e5 });
38610
38768
  if (hasCmd("python3")) {
38611
38769
  process.stdout.write(` ${c2.green("\u2714")} Python3 installed via Homebrew.
38612
38770
  `);
@@ -38619,7 +38777,7 @@ function ensurePython3() {
38619
38777
  for (const s of strategies) {
38620
38778
  if (hasCmd(s.check)) {
38621
38779
  try {
38622
- execSync24(s.install, { stdio: "inherit", timeout: 12e4 });
38780
+ execSync25(s.install, { stdio: "inherit", timeout: 12e4 });
38623
38781
  if (hasCmd("python3") || hasCmd("python")) {
38624
38782
  process.stdout.write(` ${c2.green("\u2714")} Python3 installed via ${s.label}.
38625
38783
  `);
@@ -38635,11 +38793,11 @@ function ensurePython3() {
38635
38793
  }
38636
38794
  function checkPythonVenv() {
38637
38795
  try {
38638
- execSync24("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
38796
+ execSync25("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
38639
38797
  return true;
38640
38798
  } catch {
38641
38799
  try {
38642
- execSync24("python -m venv --help", { stdio: "pipe", timeout: 5e3 });
38800
+ execSync25("python -m venv --help", { stdio: "pipe", timeout: 5e3 });
38643
38801
  return true;
38644
38802
  } catch {
38645
38803
  return false;
@@ -38658,7 +38816,7 @@ function ensurePythonVenv() {
38658
38816
  for (const s of strategies) {
38659
38817
  if (hasCmd(s.check)) {
38660
38818
  try {
38661
- execSync24(s.install, { stdio: "inherit", timeout: 12e4 });
38819
+ execSync25(s.install, { stdio: "inherit", timeout: 12e4 });
38662
38820
  if (checkPythonVenv()) {
38663
38821
  process.stdout.write(` ${c2.green("\u2714")} python3-venv installed via ${s.label}.
38664
38822
  `);
@@ -39094,7 +39252,7 @@ async function doSetup(config, rl) {
39094
39252
  const modelfilePath = join52(modelDir2, `Modelfile.${customName}`);
39095
39253
  writeFileSync15(modelfilePath, modelfileContent + "\n", "utf8");
39096
39254
  process.stdout.write(` ${c2.dim("Creating model...")} `);
39097
- execSync24(`ollama create ${customName} -f ${modelfilePath}`, {
39255
+ execSync25(`ollama create ${customName} -f ${modelfilePath}`, {
39098
39256
  stdio: "pipe",
39099
39257
  timeout: 12e4
39100
39258
  });
@@ -39145,7 +39303,7 @@ function isFirstRun() {
39145
39303
  function hasCmd(cmd) {
39146
39304
  try {
39147
39305
  const whichCmd = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
39148
- execSync24(whichCmd, { stdio: "pipe", timeout: 3e3 });
39306
+ execSync25(whichCmd, { stdio: "pipe", timeout: 3e3 });
39149
39307
  return true;
39150
39308
  } catch {
39151
39309
  return false;
@@ -39178,7 +39336,7 @@ function getVenvDir() {
39178
39336
  }
39179
39337
  function hasVenvModule() {
39180
39338
  try {
39181
- execSync24("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
39339
+ execSync25("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
39182
39340
  return true;
39183
39341
  } catch {
39184
39342
  return false;
@@ -39200,8 +39358,8 @@ function ensureVenv(log) {
39200
39358
  }
39201
39359
  try {
39202
39360
  mkdirSync14(join52(homedir12(), ".open-agents"), { recursive: true });
39203
- execSync24(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
39204
- execSync24(`"${join52(venvDir, "bin", "pip")}" install --upgrade pip`, {
39361
+ execSync25(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
39362
+ execSync25(`"${join52(venvDir, "bin", "pip")}" install --upgrade pip`, {
39205
39363
  stdio: "pipe",
39206
39364
  timeout: 6e4
39207
39365
  });
@@ -39214,7 +39372,7 @@ function ensureVenv(log) {
39214
39372
  }
39215
39373
  function trySudoPasswordless(cmd, timeoutMs = 12e4) {
39216
39374
  try {
39217
- execSync24(`sudo -n ${cmd}`, {
39375
+ execSync25(`sudo -n ${cmd}`, {
39218
39376
  stdio: "pipe",
39219
39377
  timeout: timeoutMs,
39220
39378
  env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
@@ -39227,7 +39385,7 @@ function trySudoPasswordless(cmd, timeoutMs = 12e4) {
39227
39385
  function runWithSudo(cmd, password, timeoutMs = 12e4) {
39228
39386
  try {
39229
39387
  const escaped = cmd.replace(/'/g, "'\\''");
39230
- execSync24(`sudo -S bash -c '${escaped}'`, {
39388
+ execSync25(`sudo -S bash -c '${escaped}'`, {
39231
39389
  input: password + "\n",
39232
39390
  stdio: ["pipe", "pipe", "pipe"],
39233
39391
  timeout: timeoutMs,
@@ -39334,7 +39492,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
39334
39492
  ok = await sudoInstall(batchCmd, getPassword, log, cachedPasswordRef, 18e4);
39335
39493
  } else {
39336
39494
  try {
39337
- execSync24(batchCmd, { stdio: "pipe", timeout: 18e4 });
39495
+ execSync25(batchCmd, { stdio: "pipe", timeout: 18e4 });
39338
39496
  ok = true;
39339
39497
  } catch {
39340
39498
  ok = false;
@@ -39371,7 +39529,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
39371
39529
  const venvCmds = {
39372
39530
  apt: () => {
39373
39531
  try {
39374
- const pyVer = execSync24(`python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).trim();
39532
+ const pyVer = execSync25(`python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).trim();
39375
39533
  return `apt-get install -y python3-venv python${pyVer}-venv`;
39376
39534
  } catch {
39377
39535
  return "apt-get install -y python3-venv";
@@ -39399,12 +39557,12 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
39399
39557
  const venvPip = join52(venvBin, "pip");
39400
39558
  log("Installing moondream-station in ~/.open-agents/venv...");
39401
39559
  try {
39402
- execSync24(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
39560
+ execSync25(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
39403
39561
  if (existsSync36(venvMoondream)) {
39404
39562
  log("moondream-station installed successfully.");
39405
39563
  } else {
39406
39564
  try {
39407
- const check = execSync24(`"${venvPip}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
39565
+ const check = execSync25(`"${venvPip}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
39408
39566
  if (check.includes("moondream")) {
39409
39567
  log("moondream-station package installed.");
39410
39568
  }
@@ -39421,7 +39579,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
39421
39579
  const venvPip2 = join52(venvBin, "pip");
39422
39580
  let ocrStackInstalled = false;
39423
39581
  try {
39424
- execSync24(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
39582
+ execSync25(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
39425
39583
  ocrStackInstalled = true;
39426
39584
  } catch {
39427
39585
  }
@@ -39429,9 +39587,9 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
39429
39587
  const ocrPackages = "pytesseract Pillow opencv-python-headless numpy";
39430
39588
  log("Installing OCR Python stack (pytesseract, OpenCV, Pillow, numpy)...");
39431
39589
  try {
39432
- execSync24(`"${venvPip2}" install ${ocrPackages}`, { stdio: "pipe", timeout: 3e5 });
39590
+ execSync25(`"${venvPip2}" install ${ocrPackages}`, { stdio: "pipe", timeout: 3e5 });
39433
39591
  try {
39434
- execSync24(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
39592
+ execSync25(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
39435
39593
  log("OCR Python stack installed successfully.");
39436
39594
  } catch {
39437
39595
  log("OCR Python stack install completed but import verification failed.");
@@ -39465,7 +39623,7 @@ function ensureCloudflaredBackground(onInfo) {
39465
39623
  const archMap = { x64: "amd64", arm64: "arm64", arm: "arm" };
39466
39624
  const cfArch = archMap[arch] ?? "amd64";
39467
39625
  try {
39468
- execSync24(`curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cfArch}" -o /tmp/cloudflared && chmod +x /tmp/cloudflared && mkdir -p "${homedir12()}/.local/bin" && mv /tmp/cloudflared "${homedir12()}/.local/bin/cloudflared"`, { stdio: "pipe", timeout: 6e4 });
39626
+ execSync25(`curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cfArch}" -o /tmp/cloudflared && chmod +x /tmp/cloudflared && mkdir -p "${homedir12()}/.local/bin" && mv /tmp/cloudflared "${homedir12()}/.local/bin/cloudflared"`, { stdio: "pipe", timeout: 6e4 });
39469
39627
  if (!process.env.PATH?.includes(`${homedir12()}/.local/bin`)) {
39470
39628
  process.env.PATH = `${homedir12()}/.local/bin:${process.env.PATH}`;
39471
39629
  }
@@ -39476,7 +39634,7 @@ function ensureCloudflaredBackground(onInfo) {
39476
39634
  } catch {
39477
39635
  }
39478
39636
  try {
39479
- execSync24(`curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cfArch}" -o /tmp/cloudflared && chmod +x /tmp/cloudflared && sudo mv /tmp/cloudflared /usr/local/bin/cloudflared 2>/dev/null`, { stdio: "pipe", timeout: 6e4 });
39637
+ execSync25(`curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cfArch}" -o /tmp/cloudflared && chmod +x /tmp/cloudflared && sudo mv /tmp/cloudflared /usr/local/bin/cloudflared 2>/dev/null`, { stdio: "pipe", timeout: 6e4 });
39480
39638
  if (hasCmd("cloudflared")) {
39481
39639
  log("cloudflared installed.");
39482
39640
  return true;
@@ -39485,7 +39643,7 @@ function ensureCloudflaredBackground(onInfo) {
39485
39643
  }
39486
39644
  } else if (os === "darwin") {
39487
39645
  try {
39488
- execSync24("brew install cloudflared", { stdio: "pipe", timeout: 12e4 });
39646
+ execSync25("brew install cloudflared", { stdio: "pipe", timeout: 12e4 });
39489
39647
  if (hasCmd("cloudflared")) {
39490
39648
  log("cloudflared installed via Homebrew.");
39491
39649
  return true;
@@ -39627,7 +39785,7 @@ async function ensureExpandedContext(modelName, backendUrl) {
39627
39785
  }
39628
39786
  async function ensureNeovim() {
39629
39787
  try {
39630
- const nvimPath = execSync24("which nvim 2>/dev/null || where nvim 2>nul", {
39788
+ const nvimPath = execSync25("which nvim 2>/dev/null || where nvim 2>nul", {
39631
39789
  encoding: "utf8",
39632
39790
  stdio: "pipe",
39633
39791
  timeout: 5e3
@@ -39649,14 +39807,14 @@ async function ensureNeovim() {
39649
39807
  const url = `https://github.com/neovim/neovim/releases/latest/download/${appImageName}`;
39650
39808
  console.log(` Downloading Neovim (${appImageName})...`);
39651
39809
  try {
39652
- execSync24(`curl -fsSL "${url}" -o "${nvimDest}"`, { stdio: "pipe", timeout: 6e4 });
39653
- execSync24(`chmod +x "${nvimDest}"`, { stdio: "pipe", timeout: 3e3 });
39810
+ execSync25(`curl -fsSL "${url}" -o "${nvimDest}"`, { stdio: "pipe", timeout: 6e4 });
39811
+ execSync25(`chmod +x "${nvimDest}"`, { stdio: "pipe", timeout: 3e3 });
39654
39812
  } catch (err) {
39655
39813
  console.log(` Failed to download Neovim: ${err instanceof Error ? err.message : String(err)}`);
39656
39814
  return null;
39657
39815
  }
39658
39816
  try {
39659
- const ver = execSync24(`"${nvimDest}" --version`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).split("\n")[0];
39817
+ const ver = execSync25(`"${nvimDest}" --version`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).split("\n")[0];
39660
39818
  console.log(` Installed: ${ver}`);
39661
39819
  } catch {
39662
39820
  console.log(" Warning: nvim binary downloaded but may not work (missing FUSE? Try: nvim --appimage-extract)");
@@ -39671,8 +39829,8 @@ async function ensureNeovim() {
39671
39829
  if (hasCmd("brew")) {
39672
39830
  console.log(" Installing Neovim via Homebrew...");
39673
39831
  try {
39674
- execSync24("brew install neovim", { stdio: "inherit", timeout: 12e4 });
39675
- const nvimPath = execSync24("which nvim", { encoding: "utf8", stdio: "pipe", timeout: 3e3 }).trim();
39832
+ execSync25("brew install neovim", { stdio: "inherit", timeout: 12e4 });
39833
+ const nvimPath = execSync25("which nvim", { encoding: "utf8", stdio: "pipe", timeout: 3e3 }).trim();
39676
39834
  return nvimPath || null;
39677
39835
  } catch {
39678
39836
  console.log(" brew install neovim failed.");
@@ -39686,7 +39844,7 @@ async function ensureNeovim() {
39686
39844
  if (hasCmd("choco")) {
39687
39845
  console.log(" Installing Neovim via Chocolatey...");
39688
39846
  try {
39689
- execSync24("choco install neovim -y", { stdio: "inherit", timeout: 12e4 });
39847
+ execSync25("choco install neovim -y", { stdio: "inherit", timeout: 12e4 });
39690
39848
  return "nvim";
39691
39849
  } catch {
39692
39850
  console.log(" choco install neovim failed.");
@@ -39695,7 +39853,7 @@ async function ensureNeovim() {
39695
39853
  if (hasCmd("winget")) {
39696
39854
  console.log(" Installing Neovim via winget...");
39697
39855
  try {
39698
- execSync24("winget install Neovim.Neovim --accept-source-agreements --accept-package-agreements", {
39856
+ execSync25("winget install Neovim.Neovim --accept-source-agreements --accept-package-agreements", {
39699
39857
  stdio: "inherit",
39700
39858
  timeout: 12e4
39701
39859
  });
@@ -40617,7 +40775,7 @@ var init_drop_panel = __esm({
40617
40775
  import { existsSync as existsSync38, unlinkSync as unlinkSync7 } from "node:fs";
40618
40776
  import { tmpdir as tmpdir8 } from "node:os";
40619
40777
  import { join as join53 } from "node:path";
40620
- import { execSync as execSync25 } from "node:child_process";
40778
+ import { execSync as execSync26 } from "node:child_process";
40621
40779
  function isNeovimActive() {
40622
40780
  return _state !== null && !_state.cleanedUp;
40623
40781
  }
@@ -40635,7 +40793,7 @@ async function startNeovimMode(opts) {
40635
40793
  }
40636
40794
  let nvimPath;
40637
40795
  try {
40638
- nvimPath = execSync25("which nvim 2>/dev/null", { encoding: "utf8" }).trim();
40796
+ nvimPath = execSync26("which nvim 2>/dev/null", { encoding: "utf8" }).trim();
40639
40797
  if (!nvimPath)
40640
40798
  throw new Error();
40641
40799
  } catch {
@@ -41096,7 +41254,7 @@ __export(voice_exports, {
41096
41254
  import { existsSync as existsSync39, mkdirSync as mkdirSync15, writeFileSync as writeFileSync16, readFileSync as readFileSync28, unlinkSync as unlinkSync8, readdirSync as readdirSync10, renameSync, statSync as statSync13 } from "node:fs";
41097
41255
  import { join as join54, dirname as dirname18 } from "node:path";
41098
41256
  import { homedir as homedir13, tmpdir as tmpdir9, platform as platform2 } from "node:os";
41099
- import { execSync as execSync26, spawn as nodeSpawn } from "node:child_process";
41257
+ import { execSync as execSync27, spawn as nodeSpawn } from "node:child_process";
41100
41258
  import { createRequire } from "node:module";
41101
41259
  function sanitizeForTTS(text) {
41102
41260
  return text.replace(/^#{1,6}\s+/gm, "").replace(/\*{1,3}([^*]+)\*{1,3}/g, "$1").replace(/_{1,3}([^_]+)_{1,3}/g, "$1").replace(/~~([^~]+)~~/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/```[\s\S]*?```/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/^[\s]*[-*+]\s+/gm, "").replace(/^[\s]*\d+\.\s+/gm, "").replace(/^>\s+/gm, "").replace(/^[-*_]{3,}$/gm, "").replace(/\[[ xX]\]\s*/g, "").replace(/[\u{1F600}-\u{1F64F}]/gu, "").replace(/[\u{1F300}-\u{1F5FF}]/gu, "").replace(/[\u{1F680}-\u{1F6FF}]/gu, "").replace(/[\u{1F1E0}-\u{1F1FF}]/gu, "").replace(/[\u{2600}-\u{26FF}]/gu, "").replace(/[\u{2700}-\u{27BF}]/gu, "").replace(/[\u{FE00}-\u{FE0F}]/gu, "").replace(/[\u{1F900}-\u{1F9FF}]/gu, "").replace(/[\u{1FA00}-\u{1FA6F}]/gu, "").replace(/[\u{1FA70}-\u{1FAFF}]/gu, "").replace(/[\u{200D}]/gu, "").replace(/[\u{20E3}]/gu, "").replace(/[✓✔✗✘✕✖⚠️⏸⏹⏵●○◆◇■□▪▫►▼▲◀⬆⬇⬅➡↑↓←→⇐⇒⇑⇓]/g, "").replace(/[─━│┃┌┐└┘├┤┬┴┼╔╗╚╝╠╣╦╩╬⎿⎾▕▏⏐░▒▓█⠀-⣿]/g, "").replace(/\s{2,}/g, " ").trim();
@@ -42837,7 +42995,7 @@ var init_voice = __esm({
42837
42995
  }
42838
42996
  for (const player of ["paplay", "pw-play", "aplay"]) {
42839
42997
  try {
42840
- execSync26(`which ${player}`, { stdio: "pipe" });
42998
+ execSync27(`which ${player}`, { stdio: "pipe" });
42841
42999
  return [player, path];
42842
43000
  } catch {
42843
43001
  }
@@ -42866,7 +43024,7 @@ var init_voice = __esm({
42866
43024
  return this.python3Path;
42867
43025
  for (const bin of ["python3", "python"]) {
42868
43026
  try {
42869
- const path = execSync26(`which ${bin}`, { stdio: "pipe", timeout: 5e3 }).toString().trim();
43027
+ const path = execSync27(`which ${bin}`, { stdio: "pipe", timeout: 5e3 }).toString().trim();
42870
43028
  if (path) {
42871
43029
  this.python3Path = path;
42872
43030
  return path;
@@ -42932,7 +43090,7 @@ var init_voice = __esm({
42932
43090
  return false;
42933
43091
  }
42934
43092
  try {
42935
- execSync26(`${py} -c "import mlx_audio"`, { stdio: "pipe", timeout: 1e4 });
43093
+ execSync27(`${py} -c "import mlx_audio"`, { stdio: "pipe", timeout: 1e4 });
42936
43094
  this.mlxInstalled = true;
42937
43095
  return true;
42938
43096
  } catch {
@@ -42993,11 +43151,11 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
42993
43151
  `tts_gen.main(["--model", ${JSON.stringify(mlxModelId)}, "--text", text, "--voice", ${JSON.stringify(mlxVoice)}, "--lang_code", ${JSON.stringify(mlxLangCode)}, "--audio_path", ${JSON.stringify(wavPath)}])`
42994
43152
  ].join("; ");
42995
43153
  try {
42996
- execSync26(`${py} -c ${JSON.stringify(pyScript)} ${JSON.stringify(JSON.stringify(cleaned))}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir9() });
43154
+ execSync27(`${py} -c ${JSON.stringify(pyScript)} ${JSON.stringify(JSON.stringify(cleaned))}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir9() });
42997
43155
  } catch (err) {
42998
43156
  try {
42999
43157
  const safeText = cleaned.replace(/'/g, "'\\''");
43000
- execSync26(`${py} -m mlx_audio.tts.generate --model ${mlxModelId} --text '${safeText}' --voice ${mlxVoice} --lang_code ${mlxLangCode} --audio_path ${JSON.stringify(wavPath)}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir9() });
43158
+ execSync27(`${py} -m mlx_audio.tts.generate --model ${mlxModelId} --text '${safeText}' --voice ${mlxVoice} --lang_code ${mlxLangCode} --audio_path ${JSON.stringify(wavPath)}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir9() });
43001
43159
  } catch (err2) {
43002
43160
  return;
43003
43161
  }
@@ -43061,11 +43219,11 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
43061
43219
  `tts_gen.main(["--model", ${JSON.stringify(mlxModelId)}, "--text", text, "--voice", ${JSON.stringify(mlxVoice)}, "--lang_code", ${JSON.stringify(mlxLangCode)}, "--audio_path", ${JSON.stringify(wavPath)}])`
43062
43220
  ].join("; ");
43063
43221
  try {
43064
- execSync26(`${py} -c ${JSON.stringify(pyScript)} ${JSON.stringify(JSON.stringify(cleaned))}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir9() });
43222
+ execSync27(`${py} -c ${JSON.stringify(pyScript)} ${JSON.stringify(JSON.stringify(cleaned))}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir9() });
43065
43223
  } catch {
43066
43224
  try {
43067
43225
  const safeText = cleaned.replace(/'/g, "'\\''");
43068
- execSync26(`${py} -m mlx_audio.tts.generate --model ${mlxModelId} --text '${safeText}' --voice ${mlxVoice} --lang_code ${mlxLangCode} --audio_path ${JSON.stringify(wavPath)}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir9() });
43226
+ execSync27(`${py} -m mlx_audio.tts.generate --model ${mlxModelId} --text '${safeText}' --voice ${mlxVoice} --lang_code ${mlxLangCode} --audio_path ${JSON.stringify(wavPath)}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir9() });
43069
43227
  } catch {
43070
43228
  return null;
43071
43229
  }
@@ -43803,6 +43961,109 @@ function safeLog(text) {
43803
43961
  process.stdout.write(text + "\n");
43804
43962
  }
43805
43963
  }
43964
+ async function destroyOrphanProcesses(ctx, global) {
43965
+ const myPid = process.pid;
43966
+ const myPpid = process.ppid;
43967
+ const cwd4 = ctx.repoRoot || process.cwd();
43968
+ const patterns = [
43969
+ "open-agents.*--non-interactive",
43970
+ // Stale eval/cron OA instances
43971
+ "nexus-daemon\\.mjs",
43972
+ // Detached nexus daemons
43973
+ "vitest",
43974
+ // Orphaned test runners
43975
+ "esbuild.*--service.*--ping",
43976
+ // Leaked esbuild services
43977
+ "python3 -u -i -q",
43978
+ // Orphaned REPL processes (ReplTool)
43979
+ "chromium.*--headless",
43980
+ // Headless Chrome from browser_action/web_crawl
43981
+ "chrome.*--headless",
43982
+ // Chrome from Puppeteer/Playwright scrapers
43983
+ "crawlee-scraper\\.py",
43984
+ // Python crawlee scraper
43985
+ "web_scrape\\.py",
43986
+ // Python web scraper
43987
+ "start-moondream\\.py",
43988
+ // Moondream vision server
43989
+ "live-whisper\\.py",
43990
+ // Whisper ASR server
43991
+ "jest.*--forceExit"
43992
+ // Orphaned Jest runners
43993
+ ];
43994
+ const patternArg = patterns.join("|");
43995
+ let killed = 0;
43996
+ try {
43997
+ const psOutput = nodeExecSync(`ps -eo pid,ppid,lstart,args --no-headers 2>/dev/null | grep -E "${patternArg}" | grep -v grep`, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
43998
+ if (!psOutput)
43999
+ return 0;
44000
+ const lines = psOutput.split("\n").filter((l) => l.trim());
44001
+ const candidates = [];
44002
+ for (const line of lines) {
44003
+ const parts = line.trim().split(/\s+/);
44004
+ const pid = parseInt(parts[0], 10);
44005
+ const ppid = parseInt(parts[1], 10);
44006
+ if (pid === myPid || pid === myPpid)
44007
+ continue;
44008
+ if (pid <= 10)
44009
+ continue;
44010
+ const cmd = parts.slice(6).join(" ");
44011
+ candidates.push({ pid, ppid, cmd });
44012
+ }
44013
+ if (!global) {
44014
+ const localCandidates = [];
44015
+ for (const c3 of candidates) {
44016
+ try {
44017
+ const procCwd = nodeExecSync(`readlink /proc/${c3.pid}/cwd 2>/dev/null`, { encoding: "utf-8", timeout: 2e3 }).trim();
44018
+ if (procCwd.startsWith(cwd4)) {
44019
+ localCandidates.push(c3);
44020
+ }
44021
+ } catch {
44022
+ if (/open-agents|nexus-daemon/.test(c3.cmd)) {
44023
+ localCandidates.push(c3);
44024
+ }
44025
+ }
44026
+ }
44027
+ candidates.length = 0;
44028
+ candidates.push(...localCandidates);
44029
+ }
44030
+ if (candidates.length === 0)
44031
+ return 0;
44032
+ renderInfo(`Found ${candidates.length} orphaned OA process(es)${global ? " (system-wide)" : ""}:`);
44033
+ for (const c3 of candidates) {
44034
+ const shortCmd = c3.cmd.length > 80 ? c3.cmd.slice(0, 77) + "..." : c3.cmd;
44035
+ process.stdout.write(` PID ${c3.pid}: ${shortCmd}
44036
+ `);
44037
+ }
44038
+ for (const c3 of candidates) {
44039
+ try {
44040
+ try {
44041
+ process.kill(-c3.pid, "SIGTERM");
44042
+ } catch {
44043
+ }
44044
+ process.kill(c3.pid, "SIGTERM");
44045
+ killed++;
44046
+ } catch {
44047
+ try {
44048
+ process.kill(c3.pid, "SIGKILL");
44049
+ killed++;
44050
+ } catch {
44051
+ }
44052
+ }
44053
+ }
44054
+ if (killed > 0) {
44055
+ await new Promise((r) => setTimeout(r, 2e3));
44056
+ for (const c3 of candidates) {
44057
+ try {
44058
+ process.kill(c3.pid, "SIGKILL");
44059
+ } catch {
44060
+ }
44061
+ }
44062
+ }
44063
+ } catch {
44064
+ }
44065
+ return killed;
44066
+ }
43806
44067
  async function handleSlashCommand(input, ctx) {
43807
44068
  const trimmed = input.trim();
43808
44069
  if (!trimmed.startsWith("/"))
@@ -45522,11 +45783,26 @@ async function handleSlashCommand(input, ctx) {
45522
45783
  return "handled";
45523
45784
  }
45524
45785
  case "destroy": {
45525
- if (ctx.hasActiveTask?.()) {
45526
- ctx.abortTask?.();
45786
+ const subCmd = (arg || "").toLowerCase().trim();
45787
+ if (subCmd.startsWith("processes") || subCmd.startsWith("procs")) {
45788
+ const isGlobal = subCmd.includes("--global") || subCmd.includes("-g");
45789
+ const killed = await destroyOrphanProcesses(ctx, isGlobal);
45790
+ if (killed === 0) {
45791
+ renderInfo("No orphaned OA processes found.");
45792
+ } else {
45793
+ renderInfo(`Killed ${killed} orphaned OA process(es).`);
45794
+ }
45795
+ return "handled";
45527
45796
  }
45528
- ctx.destroyProject?.();
45529
- return "exit";
45797
+ if (subCmd === "project" || subCmd === "") {
45798
+ if (ctx.hasActiveTask?.()) {
45799
+ ctx.abortTask?.();
45800
+ }
45801
+ ctx.destroyProject?.();
45802
+ return "exit";
45803
+ }
45804
+ renderWarning(`Unknown /destroy target: "${subCmd}". Use: /destroy processes [--global] or /destroy project`);
45805
+ return "handled";
45530
45806
  }
45531
45807
  case "context": {
45532
45808
  const subCmd = arg?.toLowerCase().split(/\s+/)[0] || "";
@@ -47035,7 +47311,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx, local) {
47035
47311
  }
47036
47312
  }
47037
47313
  async function handleParallel(arg, ctx) {
47038
- const { execSync: execSync31 } = await import("node:child_process");
47314
+ const { execSync: execSync32 } = await import("node:child_process");
47039
47315
  const baseUrl = ctx.config.backendUrl || "http://localhost:11434";
47040
47316
  const isRemote = ctx.config.backendType === "nexus";
47041
47317
  if (isRemote) {
@@ -47059,7 +47335,7 @@ async function handleParallel(arg, ctx) {
47059
47335
  }
47060
47336
  let systemdVal = "";
47061
47337
  try {
47062
- const out = execSync31("systemctl show ollama.service -p Environment 2>/dev/null || true", { encoding: "utf8" });
47338
+ const out = execSync32("systemctl show ollama.service -p Environment 2>/dev/null || true", { encoding: "utf8" });
47063
47339
  const match = out.match(/OLLAMA_NUM_PARALLEL=(\d+)/);
47064
47340
  if (match)
47065
47341
  systemdVal = match[1];
@@ -47088,7 +47364,7 @@ async function handleParallel(arg, ctx) {
47088
47364
  }
47089
47365
  const isSystemd = (() => {
47090
47366
  try {
47091
- const out = execSync31("systemctl is-active ollama.service 2>/dev/null", { encoding: "utf8" }).trim();
47367
+ const out = execSync32("systemctl is-active ollama.service 2>/dev/null", { encoding: "utf8" }).trim();
47092
47368
  return out === "active" || out === "inactive";
47093
47369
  } catch {
47094
47370
  return false;
@@ -47102,10 +47378,10 @@ async function handleParallel(arg, ctx) {
47102
47378
  const overrideContent = `[Service]
47103
47379
  Environment="OLLAMA_NUM_PARALLEL=${n}"
47104
47380
  `;
47105
- execSync31(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
47106
- execSync31(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
47107
- execSync31("sudo systemctl daemon-reload", { stdio: "pipe" });
47108
- execSync31("sudo systemctl restart ollama.service", { stdio: "pipe" });
47381
+ execSync32(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
47382
+ execSync32(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
47383
+ execSync32("sudo systemctl daemon-reload", { stdio: "pipe" });
47384
+ execSync32("sudo systemctl restart ollama.service", { stdio: "pipe" });
47109
47385
  let ready = false;
47110
47386
  for (let i = 0; i < 30 && !ready; i++) {
47111
47387
  await new Promise((r) => setTimeout(r, 500));
@@ -47132,7 +47408,7 @@ Environment="OLLAMA_NUM_PARALLEL=${n}"
47132
47408
  renderInfo(`Setting OLLAMA_NUM_PARALLEL=${n}...`);
47133
47409
  try {
47134
47410
  try {
47135
- execSync31("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
47411
+ execSync32("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
47136
47412
  } catch {
47137
47413
  }
47138
47414
  await new Promise((r) => setTimeout(r, 1e3));
@@ -47934,18 +48210,18 @@ async function showExposeDashboard(gateway, rl, ctx) {
47934
48210
  const cmd = `/endpoint ${id} --auth ${gateway.authKey ?? ""}`;
47935
48211
  let copied = false;
47936
48212
  try {
47937
- const { execSync: execSync31 } = __require("node:child_process");
48213
+ const { execSync: execSync32 } = __require("node:child_process");
47938
48214
  const platform5 = process.platform;
47939
48215
  if (platform5 === "darwin") {
47940
- execSync31("pbcopy", { input: cmd, timeout: 3e3 });
48216
+ execSync32("pbcopy", { input: cmd, timeout: 3e3 });
47941
48217
  copied = true;
47942
48218
  } else if (platform5 === "win32") {
47943
- execSync31("clip", { input: cmd, timeout: 3e3 });
48219
+ execSync32("clip", { input: cmd, timeout: 3e3 });
47944
48220
  copied = true;
47945
48221
  } else {
47946
48222
  for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
47947
48223
  try {
47948
- execSync31(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
48224
+ execSync32(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
47949
48225
  copied = true;
47950
48226
  break;
47951
48227
  } catch {
@@ -48034,7 +48310,7 @@ var init_commands = __esm({
48034
48310
  // packages/cli/dist/tui/project-context.js
48035
48311
  import { existsSync as existsSync41, readFileSync as readFileSync30, readdirSync as readdirSync12 } from "node:fs";
48036
48312
  import { join as join56, basename as basename11 } from "node:path";
48037
- import { execSync as execSync27 } from "node:child_process";
48313
+ import { execSync as execSync28 } from "node:child_process";
48038
48314
  import { homedir as homedir15, platform as platform3, release } from "node:os";
48039
48315
  function getModelTier(modelName) {
48040
48316
  const m = modelName.toLowerCase();
@@ -48080,19 +48356,19 @@ function loadProjectMap(repoRoot) {
48080
48356
  }
48081
48357
  function getGitInfo(repoRoot) {
48082
48358
  try {
48083
- execSync27("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
48359
+ execSync28("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
48084
48360
  } catch {
48085
48361
  return "";
48086
48362
  }
48087
48363
  const lines = [];
48088
48364
  try {
48089
- const branch = execSync27("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
48365
+ const branch = execSync28("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
48090
48366
  if (branch)
48091
48367
  lines.push(`Branch: ${branch}`);
48092
48368
  } catch {
48093
48369
  }
48094
48370
  try {
48095
- const status = execSync27("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
48371
+ const status = execSync28("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
48096
48372
  if (status) {
48097
48373
  const changed = status.split("\n").length;
48098
48374
  lines.push(`Working tree: ${changed} changed file(s)`);
@@ -48102,7 +48378,7 @@ function getGitInfo(repoRoot) {
48102
48378
  } catch {
48103
48379
  }
48104
48380
  try {
48105
- const log = execSync27("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
48381
+ const log = execSync28("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
48106
48382
  if (log)
48107
48383
  lines.push(`Recent commits:
48108
48384
  ${log}`);
@@ -50654,7 +50930,7 @@ var init_promptLoader3 = __esm({
50654
50930
  // packages/cli/dist/tui/dream-engine.js
50655
50931
  import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync19, readFileSync as readFileSync33, existsSync as existsSync44, cpSync, rmSync as rmSync2, readdirSync as readdirSync14 } from "node:fs";
50656
50932
  import { join as join60, basename as basename13 } from "node:path";
50657
- import { execSync as execSync28 } from "node:child_process";
50933
+ import { execSync as execSync29 } from "node:child_process";
50658
50934
  function loadAutoresearchMemory(repoRoot) {
50659
50935
  const memoryPath = join60(repoRoot, ".oa", "memory", "autoresearch.json");
50660
50936
  if (!existsSync44(memoryPath))
@@ -51018,7 +51294,7 @@ var init_dream_engine = __esm({
51018
51294
  }
51019
51295
  }
51020
51296
  try {
51021
- const output = execSync28(cmd, {
51297
+ const output = execSync29(cmd, {
51022
51298
  cwd: this.repoRoot,
51023
51299
  timeout: 3e4,
51024
51300
  encoding: "utf-8",
@@ -51795,17 +52071,17 @@ ${summaryResult}
51795
52071
  try {
51796
52072
  mkdirSync19(checkpointDir, { recursive: true });
51797
52073
  try {
51798
- const gitStatus = execSync28("git status --porcelain", {
52074
+ const gitStatus = execSync29("git status --porcelain", {
51799
52075
  cwd: this.repoRoot,
51800
52076
  encoding: "utf-8",
51801
52077
  timeout: 1e4
51802
52078
  });
51803
- const gitDiff = execSync28("git diff", {
52079
+ const gitDiff = execSync29("git diff", {
51804
52080
  cwd: this.repoRoot,
51805
52081
  encoding: "utf-8",
51806
52082
  timeout: 1e4
51807
52083
  });
51808
- const gitHash = execSync28("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
52084
+ const gitHash = execSync29("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
51809
52085
  cwd: this.repoRoot,
51810
52086
  encoding: "utf-8",
51811
52087
  timeout: 5e3
@@ -55615,7 +55891,7 @@ __export(text_selection_exports, {
55615
55891
  stripAnsi: () => stripAnsi3,
55616
55892
  visibleLength: () => visibleLength
55617
55893
  });
55618
- import { execSync as execSync29 } from "node:child_process";
55894
+ import { execSync as execSync30 } from "node:child_process";
55619
55895
  function stripAnsi3(s) {
55620
55896
  return s.replace(/\x1B\[[0-9;]*[A-Za-z]|\x1B\].*?(?:\x07|\x1B\\)/g, "");
55621
55897
  }
@@ -55626,16 +55902,16 @@ function copyText(text) {
55626
55902
  try {
55627
55903
  const platform5 = process.platform;
55628
55904
  if (platform5 === "darwin") {
55629
- execSync29("pbcopy", { input: text, timeout: 3e3 });
55905
+ execSync30("pbcopy", { input: text, timeout: 3e3 });
55630
55906
  return true;
55631
55907
  }
55632
55908
  if (platform5 === "win32") {
55633
- execSync29("clip", { input: text, timeout: 3e3 });
55909
+ execSync30("clip", { input: text, timeout: 3e3 });
55634
55910
  return true;
55635
55911
  }
55636
55912
  for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
55637
55913
  try {
55638
- execSync29(tool, { input: text, timeout: 3e3 });
55914
+ execSync30(tool, { input: text, timeout: 3e3 });
55639
55915
  return true;
55640
55916
  } catch {
55641
55917
  continue;
@@ -55644,10 +55920,10 @@ function copyText(text) {
55644
55920
  if (!_clipboardAutoInstallAttempted) {
55645
55921
  _clipboardAutoInstallAttempted = true;
55646
55922
  try {
55647
- execSync29("which apt-get", { timeout: 2e3, stdio: "pipe" });
55923
+ execSync30("which apt-get", { timeout: 2e3, stdio: "pipe" });
55648
55924
  try {
55649
- execSync29("sudo -n apt-get install -y xclip 2>/dev/null", { timeout: 15e3, stdio: "pipe" });
55650
- execSync29("xclip -selection clipboard", { input: text, timeout: 3e3 });
55925
+ execSync30("sudo -n apt-get install -y xclip 2>/dev/null", { timeout: 15e3, stdio: "pipe" });
55926
+ execSync30("xclip -selection clipboard", { input: text, timeout: 3e3 });
55651
55927
  return true;
55652
55928
  } catch {
55653
55929
  }
@@ -58088,7 +58364,7 @@ import { createRequire as createRequire2 } from "node:module";
58088
58364
  import { fileURLToPath as fileURLToPath12 } from "node:url";
58089
58365
  import { readFileSync as readFileSync37, writeFileSync as writeFileSync21, appendFileSync as appendFileSync4, rmSync as rmSync3, readdirSync as readdirSync18, mkdirSync as mkdirSync22 } from "node:fs";
58090
58366
  import { existsSync as existsSync48 } from "node:fs";
58091
- import { execSync as execSync30 } from "node:child_process";
58367
+ import { execSync as execSync31 } from "node:child_process";
58092
58368
  import { homedir as homedir16 } from "node:os";
58093
58369
  function formatTimeAgo(date) {
58094
58370
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -58245,6 +58521,7 @@ function buildTools(repoRoot, config, contextWindowSize) {
58245
58521
  new WorkingNotesTool(),
58246
58522
  new SemanticMapTool(repoRoot),
58247
58523
  new RepoMapTool(repoRoot),
58524
+ new ProcessHealthTool(),
58248
58525
  // Nexus P2P networking + x402 micropayments
58249
58526
  new NexusTool(repoRoot)
58250
58527
  ];
@@ -61360,7 +61637,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
61360
61637
  try {
61361
61638
  if (process.platform === "win32") {
61362
61639
  try {
61363
- execSync30(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
61640
+ execSync31(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
61364
61641
  } catch {
61365
61642
  }
61366
61643
  } else {
@@ -61387,7 +61664,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
61387
61664
  if (pid > 0) {
61388
61665
  if (process.platform === "win32") {
61389
61666
  try {
61390
- execSync30(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
61667
+ execSync31(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
61391
61668
  } catch {
61392
61669
  }
61393
61670
  } else {
@@ -61404,7 +61681,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
61404
61681
  } catch {
61405
61682
  }
61406
61683
  try {
61407
- execSync30(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
61684
+ execSync31(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
61408
61685
  } catch {
61409
61686
  }
61410
61687
  const oaPath = join64(repoRoot, OA_DIR);
@@ -61418,14 +61695,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
61418
61695
  } catch (err) {
61419
61696
  if (attempt < 2) {
61420
61697
  try {
61421
- execSync30(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
61698
+ execSync31(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
61422
61699
  } catch {
61423
61700
  }
61424
61701
  } else {
61425
61702
  writeContent(() => renderWarning(`Could not fully remove ${OA_DIR}/: ${err instanceof Error ? err.message : String(err)}`));
61426
61703
  if (process.platform === "win32") {
61427
61704
  try {
61428
- execSync30(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
61705
+ execSync31(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
61429
61706
  deleted = true;
61430
61707
  } catch {
61431
61708
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.150.0",
3
+ "version": "0.151.0",
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",