open-agents-ai 0.184.49 → 0.184.51

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 +77 -15
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5292,6 +5292,8 @@ var init_nexus = __esm({
5292
5292
  import { NexusClient } from 'open-agents-nexus';
5293
5293
  import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, appendFileSync, watch as fsWatch } from 'node:fs';
5294
5294
  import { join } from 'node:path';
5295
+ import { homedir, hostname } from 'node:os';
5296
+ import { createHash, createHmac } from 'node:crypto';
5295
5297
 
5296
5298
  const nexusDir = process.argv[2];
5297
5299
  const agentName = process.argv[3] || ('oa-node-' + process.pid);
@@ -5316,7 +5318,7 @@ writeFileSync(pidFile, String(process.pid));
5316
5318
 
5317
5319
  // Use GLOBAL identity key so all OA instances on this machine share one peerId.
5318
5320
  // Fallback to project-scoped key if global doesn't exist.
5319
- const globalKeyDir = join(require('os').homedir(), '.open-agents');
5321
+ const globalKeyDir = join(homedir(), '.open-agents');
5320
5322
  const globalKeyPath = join(globalKeyDir, 'identity.key');
5321
5323
  const projectKeyPath = join(nexusDir, 'identity.key');
5322
5324
  const keyPath = existsSync(globalKeyPath) ? globalKeyPath : projectKeyPath;
@@ -7248,19 +7250,33 @@ try {
7248
7250
  try { process.stdout.on('error', function(e) { if (e.code !== 'EPIPE') throw e; }); } catch {}
7249
7251
  try { process.stderr.on('error', function(e) { if (e.code !== 'EPIPE') throw e; }); } catch {}
7250
7252
 
7251
- // Crash protection \u2014 prevent unhandled errors from killing the daemon
7253
+ // Crash protection \u2014 prevent unhandled errors from killing the daemon.
7254
+ // EPIPE/ECONNRESET/ETIMEDOUT are transient network errors \u2014 always swallow.
7255
+ // The daemon MUST stay alive through any non-fatal error.
7256
+ var TRANSIENT_CODES = ['EPIPE', 'ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EHOSTUNREACH', 'ENETUNREACH', 'EAI_AGAIN'];
7257
+ function isTransientError(err) {
7258
+ if (!err) return false;
7259
+ var code = err.code || '';
7260
+ var msg = err.message || String(err);
7261
+ return TRANSIENT_CODES.some(function(c) { return code === c || msg.includes(c); });
7262
+ }
7252
7263
  process.on('uncaughtException', (err) => {
7264
+ if (isTransientError(err)) {
7265
+ dlog('Swallowed transient uncaughtException: ' + (err.code || err.message || err));
7266
+ return; // keep daemon alive
7267
+ }
7253
7268
  var errMsg = err && err.message ? err.message : String(err);
7254
- // EPIPE means parent closed the pipe \u2014 not a real error, just ignore
7255
- if (err && err.code === 'EPIPE') return;
7256
- if (errMsg.includes('EPIPE')) return;
7257
- try { console.error('Nexus daemon uncaughtException:', errMsg); } catch {}
7269
+ try { dlog('uncaughtException (non-transient): ' + errMsg); } catch {}
7258
7270
  writeStatus({ error: 'uncaughtException: ' + errMsg });
7271
+ // Do NOT exit \u2014 keep daemon alive even for non-transient errors
7259
7272
  });
7260
7273
  process.on('unhandledRejection', (reason) => {
7274
+ if (isTransientError(reason)) {
7275
+ dlog('Swallowed transient unhandledRejection: ' + (reason?.code || reason?.message || reason));
7276
+ return;
7277
+ }
7261
7278
  var msg = reason instanceof Error ? reason.message : String(reason);
7262
- if (msg.includes('EPIPE')) return;
7263
- try { console.error('Nexus daemon unhandledRejection:', msg); } catch {}
7279
+ try { dlog('unhandledRejection (non-transient): ' + msg); } catch {}
7264
7280
  // Do NOT exit \u2014 keep daemon alive
7265
7281
  });
7266
7282
 
@@ -7705,7 +7721,7 @@ process.on('unhandledRejection', (reason) => {
7705
7721
  const _cLatency = Date.now() - _cStart;
7706
7722
  // Sign response for accountability (HMAC-SHA256 with peerId)
7707
7723
  const _cSigData = _cData.queryId + ':' + _cContent.slice(0, 200) + ':' + _cModel + ':' + _cLatency;
7708
- const _cSig = require('crypto').createHmac('sha256', nexus.peerId).update(_cSigData).digest('hex').slice(0, 32);
7724
+ const _cSig = createHmac('sha256', nexus.peerId).update(_cSigData).digest('hex').slice(0, 32);
7709
7725
  // Scan inbound query for leaked secrets (defense-in-depth)
7710
7726
  const _cSecretPatterns = [/sk-[a-zA-Z0-9]{20,}/g, /ghp_[a-zA-Z0-9]{36,}/g, /AKIA[0-9A-Z]{16}/g];
7711
7727
  for (const _cPat of _cSecretPatterns) {
@@ -7718,7 +7734,7 @@ process.on('unhandledRejection', (reason) => {
7718
7734
  content: _cContent,
7719
7735
  model: _cModel,
7720
7736
  provider: nexus.peerId,
7721
- agentName: nexus.agentName || ('oa-' + require('os').hostname().slice(0, 12)),
7737
+ agentName: nexus.agentName || ('oa-' + hostname().slice(0, 12)),
7722
7738
  latencyMs: _cLatency,
7723
7739
  usage: _cResult.eval_count ? { inputTokens: _cResult.prompt_eval_count || 0, outputTokens: _cResult.eval_count || 0 } : undefined,
7724
7740
  signature: _cSig,
@@ -8023,7 +8039,7 @@ process.on('unhandledRejection', (reason) => {
8023
8039
  var _esTopIds = _esFiltered.map(function(m) { return m.id; }).sort().join(',');
8024
8040
 
8025
8041
  // SHA-256 hash of top-10 IDs for compact comparison
8026
- var _esHash = require('crypto').createHash('sha256').update(_esTopIds).digest('hex').slice(0, 16);
8042
+ var _esHash = createHash('sha256').update(_esTopIds).digest('hex').slice(0, 16);
8027
8043
 
8028
8044
  _epochCounter++;
8029
8045
  var _esAnnouncement = {
@@ -8078,6 +8094,9 @@ process.on('unhandledRejection', (reason) => {
8078
8094
  }
8079
8095
  })();
8080
8096
 
8097
+ // Ignore SIGPIPE \u2014 broken pipe from network writes should NOT kill daemon
8098
+ try { process.on('SIGPIPE', function() { dlog('SIGPIPE received \u2014 ignored'); }); } catch {}
8099
+
8081
8100
  // Graceful shutdown
8082
8101
  process.on('SIGTERM', async () => {
8083
8102
  for (const [, room] of rooms) { try { await room.leave(); } catch {} }
@@ -8511,8 +8530,36 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
8511
8530
  }
8512
8531
  return `Daemon did not respond within ${Math.round(timeoutMs / 1e3)}s. It may still be processing.`;
8513
8532
  }
8533
+ /** Check if daemon process is alive. Returns PID if alive, 0 if dead. */
8534
+ isDaemonAlive() {
8535
+ const pid = this.getDaemonPid();
8536
+ if (!pid)
8537
+ return 0;
8538
+ try {
8539
+ process.kill(pid, 0);
8540
+ return pid;
8541
+ } catch {
8542
+ return 0;
8543
+ }
8544
+ }
8545
+ /** Auto-restart daemon if it crashed. Called before every command. */
8546
+ async ensureDaemonAlive() {
8547
+ if (this.isDaemonAlive())
8548
+ return;
8549
+ const statusFile = join14(this.nexusDir, "status.json");
8550
+ if (!existsSync11(statusFile))
8551
+ return;
8552
+ try {
8553
+ const status = JSON.parse(readFileSync8(statusFile, "utf8"));
8554
+ if (status.agentName) {
8555
+ await this.doConnect({ agent_name: status.agentName, agent_type: status.agentType || "general" });
8556
+ }
8557
+ } catch {
8558
+ }
8559
+ }
8514
8560
  /** Public interface for daemon commands — used by NexusAgenticBackend */
8515
8561
  async sendCommand(action, args, timeoutMs) {
8562
+ await this.ensureDaemonAlive();
8516
8563
  return this.sendDaemonCmd(action, args, timeoutMs);
8517
8564
  }
8518
8565
  /** Get the nexus data directory path */
@@ -61966,12 +62013,27 @@ import { randomBytes as randomBytes16 } from "node:crypto";
61966
62013
  function getVersion3() {
61967
62014
  try {
61968
62015
  const require2 = createRequire2(import.meta.url);
61969
- const pkgPath = join69(dirname20(fileURLToPath12(import.meta.url)), "..", "..", "package.json");
61970
- const pkg = require2(pkgPath);
61971
- return pkg.version;
62016
+ const thisDir = dirname20(fileURLToPath12(import.meta.url));
62017
+ const candidates = [
62018
+ join69(thisDir, "..", "package.json"),
62019
+ join69(thisDir, "..", "..", "package.json"),
62020
+ join69(thisDir, "..", "..", "..", "package.json")
62021
+ ];
62022
+ for (const pkgPath of candidates) {
62023
+ try {
62024
+ if (!existsSync52(pkgPath))
62025
+ continue;
62026
+ const pkg = require2(pkgPath);
62027
+ if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
62028
+ return pkg.version ?? "0.0.0";
62029
+ }
62030
+ } catch {
62031
+ continue;
62032
+ }
62033
+ }
61972
62034
  } catch {
61973
- return "0.0.0";
61974
62035
  }
62036
+ return "0.0.0";
61975
62037
  }
61976
62038
  function recordMetric(method, path, status) {
61977
62039
  const key = `${method}|${path}|${status}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.184.49",
3
+ "version": "0.184.51",
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",