open-agents-ai 0.103.82 → 0.103.83

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 +124 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1343,10 +1343,22 @@ var init_shell = __esm({
1343
1343
  workingDir;
1344
1344
  defaultTimeout;
1345
1345
  _sudoPassword = null;
1346
+ /** Active child processes — killed on abort for instant /stop */
1347
+ _activeChildren = /* @__PURE__ */ new Set();
1346
1348
  constructor(workingDir, defaultTimeout = 3e4) {
1347
1349
  this.workingDir = workingDir;
1348
1350
  this.defaultTimeout = defaultTimeout;
1349
1351
  }
1352
+ /** Kill all active child processes immediately (called by /stop) */
1353
+ killAll() {
1354
+ for (const child of this._activeChildren) {
1355
+ try {
1356
+ child.kill("SIGKILL");
1357
+ } catch {
1358
+ }
1359
+ }
1360
+ this._activeChildren.clear();
1361
+ }
1350
1362
  /** Set a cached sudo password for the session. The shell tool will
1351
1363
  * automatically retry permission-denied commands with `sudo -S`. */
1352
1364
  setSudoPassword(password) {
@@ -1397,17 +1409,14 @@ ${stdinInput ?? ""}`);
1397
1409
  cwd: this.workingDir,
1398
1410
  env: {
1399
1411
  ...process.env,
1400
- // Non-interactive mode: libraries like prompts, inquirer,
1401
- // create-next-app, and many Node CLI tools check CI to skip
1402
- // interactive prompts and use defaults instead.
1403
1412
  CI: "true",
1404
1413
  NONINTERACTIVE: "1",
1405
- // Disable color in most tools to get clean output
1406
1414
  NO_COLOR: "1",
1407
1415
  FORCE_COLOR: "0"
1408
1416
  },
1409
1417
  stdio: ["pipe", "pipe", "pipe"]
1410
1418
  });
1419
+ this._activeChildren.add(child);
1411
1420
  let stdout = "";
1412
1421
  let stderr = "";
1413
1422
  let killed = false;
@@ -1450,6 +1459,7 @@ ${stdinInput ?? ""}`);
1450
1459
  }
1451
1460
  child.stdin.end();
1452
1461
  child.on("exit", (code) => {
1462
+ this._activeChildren.delete(child);
1453
1463
  exitFlushTimer = setTimeout(() => {
1454
1464
  const durationMs = performance.now() - start;
1455
1465
  if (killed) {
@@ -18628,6 +18638,7 @@ var init_agenticRunner = __esm({
18628
18638
  handlers = [];
18629
18639
  pendingUserMessages = [];
18630
18640
  aborted = false;
18641
+ _abortController = new AbortController();
18631
18642
  _paused = false;
18632
18643
  _pauseResolve = null;
18633
18644
  _sudoPassword = null;
@@ -18837,14 +18848,23 @@ ${this.options.dynamicContext}`,
18837
18848
  injectUserMessage(content) {
18838
18849
  this.pendingUserMessages.push(content);
18839
18850
  }
18840
- /** Abort the current task run */
18851
+ /** Abort the current task run — cancels in-flight requests and kills child processes */
18841
18852
  abort() {
18842
18853
  this.aborted = true;
18854
+ this._abortController.abort();
18855
+ const shellTool = this.tools.get("shell");
18856
+ if (shellTool && typeof shellTool.killAll === "function") {
18857
+ shellTool.killAll();
18858
+ }
18843
18859
  if (this._pauseResolve) {
18844
18860
  this._pauseResolve();
18845
18861
  this._pauseResolve = null;
18846
18862
  }
18847
18863
  }
18864
+ /** Get the current abort signal for passing to fetch/spawn */
18865
+ get abortSignal() {
18866
+ return this._abortController.signal;
18867
+ }
18848
18868
  /**
18849
18869
  * Pause the current task gracefully. The run loop will suspend at the next
18850
18870
  * turn boundary (between tool calls / model requests) without destroying state.
@@ -18955,6 +18975,11 @@ Respond with your assessment, then take action.`;
18955
18975
  }
18956
18976
  /** Run a task through the agentic loop */
18957
18977
  async run(task, context) {
18978
+ this.aborted = false;
18979
+ this._abortController = new AbortController();
18980
+ if (typeof this.backend.setAbortSignal === "function") {
18981
+ this.backend.setAbortSignal(this._abortController.signal);
18982
+ }
18958
18983
  const start = Date.now();
18959
18984
  const taskTimeoutMs = this.options.taskTimeoutMs;
18960
18985
  const selfEvalInterval = taskTimeoutMs;
@@ -19615,6 +19640,10 @@ Integrate this guidance into your current approach. Continue working on the task
19615
19640
  try {
19616
19641
  response = this.options.streamEnabled && this.hasStreamingSupport() ? await this.streamingRequest(chatRequest, turn) : await this.backend.chatCompletion(chatRequest);
19617
19642
  } catch (reqErr) {
19643
+ if (this.aborted || reqErr instanceof Error && reqErr.name === "AbortError") {
19644
+ this.emit({ type: "error", content: "Task aborted by user", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
19645
+ break;
19646
+ }
19618
19647
  const recovered = await this.retryOnTransient(reqErr, chatRequest, turn);
19619
19648
  if (!recovered) {
19620
19649
  const errMsg2 = reqErr instanceof Error ? reqErr.message : String(reqErr);
@@ -21004,12 +21033,18 @@ ${transcript}`
21004
21033
  model;
21005
21034
  apiKey;
21006
21035
  thinking;
21036
+ /** Abort signal — set by the runner so /stop can cancel in-flight requests */
21037
+ _abortSignal = null;
21007
21038
  constructor(baseUrl, model, apiKey, thinking) {
21008
21039
  this.baseUrl = normalizeBaseUrl(baseUrl);
21009
21040
  this.model = model;
21010
21041
  this.apiKey = apiKey ?? "";
21011
21042
  this.thinking = thinking ?? true;
21012
21043
  }
21044
+ /** Set the abort signal from the runner (called at run start) */
21045
+ setAbortSignal(signal) {
21046
+ this._abortSignal = signal;
21047
+ }
21013
21048
  /** Build auth headers — all providers use standard Bearer token auth. */
21014
21049
  authHeaders() {
21015
21050
  const headers = { "Content-Type": "application/json" };
@@ -21027,11 +21062,14 @@ ${transcript}`
21027
21062
  max_tokens: request.maxTokens,
21028
21063
  think: this.thinking
21029
21064
  };
21030
- const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
21065
+ const fetchOpts = {
21031
21066
  method: "POST",
21032
21067
  headers: this.authHeaders(),
21033
21068
  body: JSON.stringify(body)
21034
- });
21069
+ };
21070
+ if (this._abortSignal)
21071
+ fetchOpts.signal = this._abortSignal;
21072
+ const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, fetchOpts);
21035
21073
  if (!resp.ok) {
21036
21074
  const text = await resp.text().catch(() => "");
21037
21075
  const isHtml = text.trimStart().startsWith("<!") || text.trimStart().startsWith("<html");
@@ -21089,11 +21127,14 @@ ${transcript}`
21089
21127
  stream_options: { include_usage: true },
21090
21128
  think: this.thinking
21091
21129
  };
21092
- const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
21130
+ const streamFetchOpts = {
21093
21131
  method: "POST",
21094
21132
  headers: this.authHeaders(),
21095
21133
  body: JSON.stringify(body)
21096
- });
21134
+ };
21135
+ if (this._abortSignal)
21136
+ streamFetchOpts.signal = this._abortSignal;
21137
+ const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, streamFetchOpts);
21097
21138
  if (!resp.ok) {
21098
21139
  const text = await resp.text().catch(() => "");
21099
21140
  const isHtml = text.trimStart().startsWith("<!") || text.trimStart().startsWith("<html");
@@ -38670,6 +38711,8 @@ var init_voice = __esm({
38670
38711
  await this.ensureLuxtts();
38671
38712
  this.luxttsActive = true;
38672
38713
  this.mlxActive = false;
38714
+ this.ensureLuxttsDaemon().catch(() => {
38715
+ });
38673
38716
  } else if (isMlx) {
38674
38717
  await this.ensureMlxAudio();
38675
38718
  this.mlxActive = true;
@@ -38722,6 +38765,8 @@ var init_voice = __esm({
38722
38765
  await this.ensureLuxtts();
38723
38766
  this.luxttsActive = true;
38724
38767
  this.mlxActive = false;
38768
+ this.ensureLuxttsDaemon().catch(() => {
38769
+ });
38725
38770
  } else if (isMlx) {
38726
38771
  await this.ensureMlxAudio();
38727
38772
  this.mlxActive = true;
@@ -39308,7 +39353,7 @@ var init_voice = __esm({
39308
39353
  isMlxCapable() {
39309
39354
  return platform3() === "darwin" && process.arch === "arm64";
39310
39355
  }
39311
- /** Resolve python3 binary path */
39356
+ /** Resolve python3 binary path (cached after first call) */
39312
39357
  findPython3() {
39313
39358
  if (this.python3Path)
39314
39359
  return this.python3Path;
@@ -39324,6 +39369,52 @@ var init_voice = __esm({
39324
39369
  }
39325
39370
  return null;
39326
39371
  }
39372
+ /** Async version of findPython3 — non-blocking */
39373
+ async findPython3Async() {
39374
+ if (this.python3Path)
39375
+ return this.python3Path;
39376
+ for (const bin of ["python3", "python"]) {
39377
+ try {
39378
+ const path = await this.asyncShell(`which ${bin}`, 5e3);
39379
+ if (path) {
39380
+ this.python3Path = path;
39381
+ return path;
39382
+ }
39383
+ } catch {
39384
+ }
39385
+ }
39386
+ return null;
39387
+ }
39388
+ /** Non-blocking shell execution — async alternative to execSync.
39389
+ * Returns stdout string on exit 0, rejects otherwise. */
39390
+ asyncShell(command, timeoutMs = 3e4) {
39391
+ return new Promise((resolve31, reject) => {
39392
+ const proc = nodeSpawn("sh", ["-c", command], {
39393
+ stdio: ["ignore", "pipe", "pipe"],
39394
+ cwd: tmpdir6()
39395
+ });
39396
+ let stdout = "";
39397
+ let stderr = "";
39398
+ proc.stdout.on("data", (d) => {
39399
+ stdout += d.toString();
39400
+ });
39401
+ proc.stderr.on("data", (d) => {
39402
+ stderr += d.toString();
39403
+ });
39404
+ proc.on("error", reject);
39405
+ const timer = setTimeout(() => {
39406
+ proc.kill("SIGKILL");
39407
+ reject(new Error(`Timeout after ${timeoutMs}ms`));
39408
+ }, timeoutMs);
39409
+ proc.on("close", (code) => {
39410
+ clearTimeout(timer);
39411
+ if (code === 0)
39412
+ resolve31(stdout.trim());
39413
+ else
39414
+ reject(new Error(stderr.slice(0, 300) || `Exit code ${code}`));
39415
+ });
39416
+ });
39417
+ }
39327
39418
  /** Check if mlx-audio is installed */
39328
39419
  checkMlxInstalled() {
39329
39420
  if (this.mlxInstalled !== null)
@@ -39496,9 +39587,10 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39496
39587
  * Ensure LuxTTS venv is created with all dependencies.
39497
39588
  * On first run: creates venv, installs PyTorch + LuxTTS + deps (~5-15 min).
39498
39589
  * Subsequent runs: checks venv exists and import works.
39590
+ * All shell commands use asyncShell — never blocks the event loop.
39499
39591
  */
39500
39592
  async ensureLuxtts() {
39501
- const py = this.findPython3();
39593
+ const py = await this.findPython3Async();
39502
39594
  if (!py) {
39503
39595
  throw new Error("python3 not found. LuxTTS requires Python 3.10+. Try: apt install python3 / brew install python3");
39504
39596
  }
@@ -39506,23 +39598,25 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39506
39598
  const venvPy = luxttsVenvPy();
39507
39599
  if (existsSync34(venvPy)) {
39508
39600
  try {
39509
- execSync27(`${venvPy} -c "import sys; sys.path.insert(0, '${luxttsRepoDir()}'); from zipvoice.luxvoice import LuxTTS; print('ok')"`, { stdio: "pipe", timeout: 3e4 });
39601
+ await this.asyncShell(`${venvPy} -c "import sys; sys.path.insert(0, '${luxttsRepoDir()}'); from zipvoice.luxvoice import LuxTTS; print('ok')"`, 3e4);
39510
39602
  let hasCudaSys = false;
39511
39603
  try {
39512
- execSync27("nvidia-smi", { stdio: "pipe", timeout: 5e3 });
39604
+ await this.asyncShell("nvidia-smi", 5e3);
39513
39605
  hasCudaSys = true;
39514
39606
  } catch {
39515
39607
  }
39516
39608
  if (hasCudaSys) {
39517
- try {
39518
- const torchCheck = execSync27(`${venvPy} -c "import torch; print('cuda' if torch.cuda.is_available() else 'cpu')"`, { stdio: "pipe", timeout: 15e3 }).toString().trim();
39609
+ this.asyncShell(`${venvPy} -c "import torch; print('cuda' if torch.cuda.is_available() else 'cpu')"`, 15e3).then(async (torchCheck) => {
39519
39610
  if (torchCheck === "cpu") {
39520
- renderWarning("GPU detected but PyTorch is CPU-only. Reinstalling with CUDA support...");
39521
- execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet --force-reinstall torch torchaudio --index-url https://download.pytorch.org/whl/cu124`, { stdio: "pipe", timeout: 6e5 });
39522
- renderInfo("PyTorch reinstalled with CUDA GPU support.");
39611
+ renderWarning("GPU detected but PyTorch is CPU-only. Reinstalling with CUDA support in background...");
39612
+ try {
39613
+ await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet --force-reinstall torch torchaudio --index-url https://download.pytorch.org/whl/cu124`, 6e5);
39614
+ renderInfo("PyTorch reinstalled with CUDA GPU support.");
39615
+ } catch {
39616
+ }
39523
39617
  }
39524
- } catch {
39525
- }
39618
+ }).catch(() => {
39619
+ });
39526
39620
  }
39527
39621
  this.writeLuxttsInferScript();
39528
39622
  this.autoDetectCloneRef();
@@ -39535,27 +39629,24 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39535
39629
  if (!existsSync34(venvDir)) {
39536
39630
  renderInfo(" Creating Python virtual environment...");
39537
39631
  try {
39538
- execSync27(`${py} -m venv ${JSON.stringify(venvDir)}`, {
39539
- stdio: "pipe",
39540
- timeout: 6e4
39541
- });
39632
+ await this.asyncShell(`${py} -m venv ${JSON.stringify(venvDir)}`, 6e4);
39542
39633
  } catch (err) {
39543
39634
  throw new Error(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
39544
39635
  }
39545
39636
  }
39546
39637
  let hasCuda = false;
39547
39638
  try {
39548
- execSync27("nvidia-smi", { stdio: "pipe", timeout: 5e3 });
39639
+ await this.asyncShell("nvidia-smi", 5e3);
39549
39640
  hasCuda = true;
39550
39641
  } catch {
39551
39642
  }
39552
39643
  if (hasCuda) {
39553
39644
  renderInfo(" Installing PyTorch (CUDA GPU)...");
39554
39645
  try {
39555
- execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cu124`, { stdio: "pipe", timeout: 6e5 });
39646
+ await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cu124`, 6e5);
39556
39647
  } catch {
39557
39648
  try {
39558
- execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, { stdio: "pipe", timeout: 6e5 });
39649
+ await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, 6e5);
39559
39650
  } catch (err2) {
39560
39651
  throw new Error(`Failed to install PyTorch (CUDA): ${err2 instanceof Error ? err2.message : String(err2)}`);
39561
39652
  }
@@ -39563,10 +39654,10 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39563
39654
  } else {
39564
39655
  renderInfo(" Installing PyTorch (CPU \u2014 no NVIDIA GPU detected)...");
39565
39656
  try {
39566
- execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cpu`, { stdio: "pipe", timeout: 6e5 });
39657
+ await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cpu`, 6e5);
39567
39658
  } catch {
39568
39659
  try {
39569
- execSync27(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, { stdio: "pipe", timeout: 6e5 });
39660
+ await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, 6e5);
39570
39661
  } catch (err2) {
39571
39662
  throw new Error(`Failed to install PyTorch: ${err2 instanceof Error ? err2.message : String(err2)}`);
39572
39663
  }
@@ -39577,9 +39668,9 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39577
39668
  renderInfo(" Cloning LuxTTS repository...");
39578
39669
  try {
39579
39670
  if (existsSync34(repoDir)) {
39580
- execSync27(`rm -rf ${JSON.stringify(repoDir)}`, { stdio: "pipe" });
39671
+ await this.asyncShell(`rm -rf ${JSON.stringify(repoDir)}`, 3e4);
39581
39672
  }
39582
- execSync27(`git clone --depth 1 https://github.com/ysharma3501/LuxTTS.git ${JSON.stringify(repoDir)}`, { stdio: "pipe", timeout: 12e4 });
39673
+ await this.asyncShell(`git clone --depth 1 https://github.com/ysharma3501/LuxTTS.git ${JSON.stringify(repoDir)}`, 12e4);
39583
39674
  } catch (err) {
39584
39675
  throw new Error(`Failed to clone LuxTTS: ${err instanceof Error ? err.message : String(err)}`);
39585
39676
  }
@@ -39587,19 +39678,15 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39587
39678
  renderInfo(" Installing LuxTTS dependencies...");
39588
39679
  const pipCmd = JSON.stringify(venvPy);
39589
39680
  const installCmds = [
39590
- // Core deps from requirements.txt
39591
39681
  `${pipCmd} -m pip install --quiet lhotse huggingface_hub safetensors pydub onnxruntime librosa "transformers<=4.57.6" inflect numpy vocos "setuptools<81"`,
39592
- // Tokenization deps (piper_phonemize from custom index, plus CJK support)
39593
39682
  `${pipCmd} -m pip install --quiet piper-phonemize --find-links https://k2-fsa.github.io/icefall/piper_phonemize.html`,
39594
39683
  `${pipCmd} -m pip install --quiet jieba pypinyin cn2an`,
39595
- // LinaCodec custom vocoder (required by Zipvoice)
39596
39684
  `${pipCmd} -m pip install --quiet "git+https://github.com/ysharma3501/LinaCodec.git"`,
39597
- // Zipvoice package from the cloned repo (installs as 'zipvoice')
39598
39685
  `${pipCmd} -m pip install --quiet -e ${JSON.stringify(repoDir)}`
39599
39686
  ];
39600
39687
  for (const cmd of installCmds) {
39601
39688
  try {
39602
- execSync27(cmd, { stdio: "pipe", timeout: 3e5 });
39689
+ await this.asyncShell(cmd, 3e5);
39603
39690
  } catch (err) {
39604
39691
  const msg = err instanceof Error ? err.message : String(err);
39605
39692
  if (msg.includes("piper") || msg.includes("LinaCodec")) {
@@ -39610,7 +39697,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
39610
39697
  }
39611
39698
  }
39612
39699
  try {
39613
- execSync27(`${venvPy} -c "import sys; sys.path.insert(0, '${repoDir}'); from zipvoice.luxvoice import LuxTTS; print('ok')"`, { stdio: "pipe", timeout: 3e4 });
39700
+ await this.asyncShell(`${venvPy} -c "import sys; sys.path.insert(0, '${repoDir}'); from zipvoice.luxvoice import LuxTTS; print('ok')"`, 3e4);
39614
39701
  } catch (err) {
39615
39702
  throw new Error(`LuxTTS installed but import failed: ${err instanceof Error ? err.message : String(err)}`);
39616
39703
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.82",
3
+ "version": "0.103.83",
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",