open-agents-ai 0.140.0 → 0.140.1

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 +259 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5645,6 +5645,86 @@ async function handleCmd(cmd) {
5645
5645
  break;
5646
5646
  }
5647
5647
 
5648
+ // \u2500\u2500 IPFS pin external CID \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5649
+ case 'ipfs_pin': {
5650
+ var _ipCid = args.cid || '';
5651
+ if (!_ipCid) { writeResp(id, { ok: false, output: 'cid required' }); break; }
5652
+ // Validate CID format
5653
+ if (!_ipCid.startsWith('bafy') && !_ipCid.startsWith('bafk') && !_ipCid.startsWith('Qm')) {
5654
+ writeResp(id, { ok: false, output: 'Invalid CID format. Must start with bafy, bafk, or Qm' });
5655
+ break;
5656
+ }
5657
+ var _ipReady = await _ensureHelia();
5658
+ if (!_ipReady || !_heliaNode) {
5659
+ writeResp(id, { ok: false, output: 'Helia not available \u2014 cannot pin external CIDs' });
5660
+ break;
5661
+ }
5662
+ try {
5663
+ var { CID: _ipCIDClass } = await import('multiformats/cid');
5664
+ var _ipParsed = _ipCIDClass.parse(_ipCid);
5665
+ for await (var _ipPin of _heliaNode.pins.add(_ipParsed)) { /* pinning */ }
5666
+ // Register in CID registry
5667
+ var _ipRegDir = join(nexusDir, 'ipfs', 'cid-registry');
5668
+ mkdirSync(_ipRegDir, { recursive: true });
5669
+ var _ipRegFile = join(_ipRegDir, 'learning-cids.json');
5670
+ var _ipReg = {};
5671
+ try { if (existsSync(_ipRegFile)) _ipReg = JSON.parse(readFileSync(_ipRegFile, 'utf8')); } catch {}
5672
+ _ipReg['pin-' + Date.now().toString(36)] = {
5673
+ cid: _ipCid,
5674
+ source: args.source || 'manual-pin',
5675
+ pinned: true,
5676
+ timestamp: Date.now(),
5677
+ };
5678
+ writeFileSync(_ipRegFile, JSON.stringify(_ipReg, null, 2));
5679
+ dlog('IPFS: pinned external CID ' + _ipCid.slice(0, 20) + '...');
5680
+ writeResp(id, { ok: true, output: JSON.stringify({ cid: _ipCid, pinned: true }) });
5681
+ } catch (_ipErr) {
5682
+ writeResp(id, { ok: false, output: 'Pin error: ' + (_ipErr.message || _ipErr) });
5683
+ }
5684
+ break;
5685
+ }
5686
+
5687
+ // \u2500\u2500 IPFS list pinned CIDs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5688
+ case 'ipfs_ls': {
5689
+ var _ilPins = [];
5690
+ // Merge from Helia pins (if available) + CID registry
5691
+ try {
5692
+ if (_heliaReady && _heliaNode) {
5693
+ for await (var _ilCid of _heliaNode.pins.ls()) {
5694
+ _ilPins.push({ cid: _ilCid.cid.toString(), source: 'helia', pinned: true });
5695
+ }
5696
+ }
5697
+ } catch {}
5698
+ // Merge CID registry metadata
5699
+ try {
5700
+ var _ilRegFile = join(nexusDir, 'ipfs', 'cid-registry', 'learning-cids.json');
5701
+ if (existsSync(_ilRegFile)) {
5702
+ var _ilReg = JSON.parse(readFileSync(_ilRegFile, 'utf8'));
5703
+ for (var _ilKey of Object.keys(_ilReg)) {
5704
+ var _ilEntry = _ilReg[_ilKey];
5705
+ if (!_ilPins.some(function(p) { return p.cid === _ilEntry.cid; })) {
5706
+ _ilPins.push({ cid: _ilEntry.cid, source: _ilEntry.source, pinned: _ilEntry.pinned, timestamp: _ilEntry.timestamp });
5707
+ }
5708
+ }
5709
+ }
5710
+ } catch {}
5711
+ // Also include identity CIDs
5712
+ try {
5713
+ var _ilIdFile = join(nexusDir, '..', 'identity', 'cids.json');
5714
+ if (existsSync(_ilIdFile)) {
5715
+ var _ilIdCids = JSON.parse(readFileSync(_ilIdFile, 'utf8'));
5716
+ for (var _ilIdKey of Object.keys(_ilIdCids)) {
5717
+ var _ilIdCid = _ilIdCids[_ilIdKey];
5718
+ if (_ilIdCid && !_ilPins.some(function(p) { return p.cid === _ilIdCid; })) {
5719
+ _ilPins.push({ cid: _ilIdCid, source: 'identity-' + _ilIdKey, pinned: true });
5720
+ }
5721
+ }
5722
+ }
5723
+ } catch {}
5724
+ writeResp(id, { ok: true, output: JSON.stringify({ pins: _ilPins, count: _ilPins.length }) });
5725
+ break;
5726
+ }
5727
+
5648
5728
  // \u2500\u2500 COHERE distributed learning \u2014 publish insight to mesh \u2500\u2500\u2500\u2500\u2500\u2500
5649
5729
  case 'cohere_publish_insight': {
5650
5730
  if (!cohereActive) { writeResp(id, { ok: false, output: 'COHERE not active' }); break; }
@@ -7214,6 +7294,8 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
7214
7294
  "cohere_deny_model",
7215
7295
  "cohere_list_models",
7216
7296
  "ipfs_add",
7297
+ "ipfs_pin",
7298
+ "ipfs_ls",
7217
7299
  "cohere_publish_insight"
7218
7300
  ],
7219
7301
  description: "The nexus action. MUST call 'connect' first (spawns daemon). Then: join_room, send_message, discover_peers, expose, status, cohere_stats, cohere_enable, cohere_disable, cohere_allow_model, cohere_deny_model, cohere_list_models, ipfs_add, etc."
@@ -7454,6 +7536,12 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
7454
7536
  case "ipfs_add":
7455
7537
  result = await this.sendDaemonCmd("ipfs_add", { content: String(args.content ?? args.message ?? "") });
7456
7538
  break;
7539
+ case "ipfs_pin":
7540
+ result = await this.sendDaemonCmd("ipfs_pin", { cid: String(args.cid ?? args.content ?? ""), source: String(args.source ?? "agent") });
7541
+ break;
7542
+ case "ipfs_ls":
7543
+ result = await this.sendDaemonCmd("ipfs_ls", {});
7544
+ break;
7457
7545
  case "cohere_publish_insight":
7458
7546
  result = await this.sendDaemonCmd("cohere_publish_insight", {
7459
7547
  insight: String(args.insight ?? args.content ?? ""),
@@ -40445,14 +40533,49 @@ var init_voice = __esm({
40445
40533
  async drainQueue() {
40446
40534
  this.speaking = true;
40447
40535
  let isFirst = true;
40536
+ let prefetchedWav = null;
40448
40537
  while (this.speakQueue.length > 0) {
40449
40538
  const item = this.speakQueue.shift();
40450
40539
  if (!isFirst) {
40451
- await this.sleep(250);
40540
+ await this.sleep(120);
40452
40541
  }
40453
40542
  isFirst = false;
40454
40543
  try {
40455
- await this.synthesizeAndPlay(item.text, item.volume, item.pitchFactor, item.speedFactor, item.stereoDelayMs);
40544
+ if (this.luxttsActive) {
40545
+ let wavPath = null;
40546
+ if (prefetchedWav && prefetchedWav.item === item) {
40547
+ wavPath = prefetchedWav.path;
40548
+ prefetchedWav = null;
40549
+ }
40550
+ const nextItem = this.speakQueue[0] ?? null;
40551
+ let prefetchPromise = null;
40552
+ if (nextItem && this.luxttsActive) {
40553
+ prefetchPromise = this.synthesizeLuxttsWav(nextItem.text, nextItem.speedFactor);
40554
+ }
40555
+ if (!wavPath) {
40556
+ wavPath = await this.synthesizeLuxttsWav(item.text, item.speedFactor);
40557
+ }
40558
+ if (wavPath) {
40559
+ await this.postProcessAndPlayLuxtts(wavPath, item.volume, item.pitchFactor, item.stereoDelayMs);
40560
+ }
40561
+ if (prefetchPromise && nextItem) {
40562
+ try {
40563
+ const nextWav = await prefetchPromise;
40564
+ if (nextWav) {
40565
+ prefetchedWav = { path: nextWav, item: nextItem };
40566
+ }
40567
+ } catch {
40568
+ }
40569
+ }
40570
+ } else {
40571
+ await this.synthesizeAndPlay(item.text, item.volume, item.pitchFactor, item.speedFactor, item.stereoDelayMs);
40572
+ }
40573
+ } catch {
40574
+ }
40575
+ }
40576
+ if (prefetchedWav) {
40577
+ try {
40578
+ unlinkSync8(prefetchedWav.path);
40456
40579
  } catch {
40457
40580
  }
40458
40581
  }
@@ -41151,10 +41274,19 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
41151
41274
  renderWarning(` Could not install system build deps: ${err instanceof Error ? err.message : String(err)}`);
41152
41275
  }
41153
41276
  }
41277
+ const isJetson = isArm && (existsSync35("/etc/nv_tegra_release") || existsSync35("/usr/local/cuda/targets/aarch64-linux") || (process.env.JETSON_L4T_VERSION ?? "") !== "");
41154
41278
  const installSteps = isArm ? [
41155
41279
  // ARM: install individually so we get clear error messages per package.
41156
41280
  // ALL are fatal because LuxTTS hard-imports them (no lazy/optional imports).
41157
41281
  { cmd: `${pipCmd} -m pip install --quiet "setuptools<81" wheel`, fatal: true, label: "setuptools" },
41282
+ // Jetson: try NVIDIA's prebuilt PyTorch wheel first (has CUDA support)
41283
+ ...isJetson ? [
41284
+ { cmd: `${pipCmd} -m pip install --quiet torch --index-url https://developer.download.nvidia.com/compute/redist/jp/v60/pytorch/ 2>/dev/null || ${pipCmd} -m pip install --quiet torch`, fatal: true, label: "PyTorch (Jetson L4T)" },
41285
+ { cmd: `${pipCmd} -m pip install --quiet onnxruntime-gpu 2>/dev/null || true`, fatal: false, label: "onnxruntime-gpu (Jetson, optional)" }
41286
+ ] : [
41287
+ // Non-Jetson ARM: standard PyTorch (CPU)
41288
+ { cmd: `${pipCmd} -m pip install --quiet torch --index-url https://download.pytorch.org/whl/cpu`, fatal: true, label: "PyTorch (ARM CPU)" }
41289
+ ],
41158
41290
  { cmd: `${pipCmd} -m pip install --quiet numpy`, fatal: true, label: "numpy" },
41159
41291
  { cmd: `${pipCmd} -m pip install --quiet huggingface_hub safetensors`, fatal: true, label: "huggingface_hub + safetensors" },
41160
41292
  { cmd: `${pipCmd} -m pip install --quiet "transformers<=4.57.6"`, fatal: true, label: "transformers" },
@@ -41397,17 +41529,21 @@ if __name__ == '__main__':
41397
41529
  * Speed is passed natively to LuxTTS. Pitch is post-processed via resampling.
41398
41530
  * Volume is applied via WAV sample scaling. Stereo ITD applied for depth.
41399
41531
  */
41400
- async synthesizeWithLuxtts(text, volume = 1, pitchFactor = 1, speedFactor = 1, stereoDelayMs = 0.6) {
41401
- if (!this.luxttsCloneRef || !existsSync35(this.luxttsCloneRef)) {
41402
- return;
41403
- }
41532
+ /**
41533
+ * Synthesize text to a WAV file via LuxTTS daemon (synthesis only, no playback).
41534
+ * Returns the WAV file path or null on failure.
41535
+ * Used by drainQueue's pre-fetch pipeline for gapless back-to-back playback.
41536
+ */
41537
+ async synthesizeLuxttsWav(text, speedFactor = 1) {
41538
+ if (!this.luxttsCloneRef || !existsSync35(this.luxttsCloneRef))
41539
+ return null;
41404
41540
  const cleaned = text.replace(/\*/g, "").trim();
41405
41541
  if (!cleaned)
41406
- return;
41542
+ return null;
41407
41543
  const ready = await this.ensureLuxttsDaemon();
41408
41544
  if (!ready)
41409
- return;
41410
- const wavPath = join50(tmpdir9(), `oa-luxtts-${Date.now()}.wav`);
41545
+ return null;
41546
+ const wavPath = join50(tmpdir9(), `oa-luxtts-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.wav`);
41411
41547
  try {
41412
41548
  await this.luxttsRequest({
41413
41549
  action: "synthesize",
@@ -41417,8 +41553,15 @@ if __name__ == '__main__':
41417
41553
  speed: speedFactor
41418
41554
  });
41419
41555
  } catch {
41420
- return;
41556
+ return null;
41421
41557
  }
41558
+ return existsSync35(wavPath) ? wavPath : null;
41559
+ }
41560
+ /**
41561
+ * Post-process (fade-in, volume, pitch, stereo) and play a LuxTTS WAV file.
41562
+ * Cleans up the WAV file after playback.
41563
+ */
41564
+ async postProcessAndPlayLuxtts(wavPath, volume = 1, pitchFactor = 1, stereoDelayMs = 0.6) {
41422
41565
  if (!existsSync35(wavPath))
41423
41566
  return;
41424
41567
  try {
@@ -41493,6 +41636,13 @@ if __name__ == '__main__':
41493
41636
  } catch {
41494
41637
  }
41495
41638
  }
41639
+ /** Combined synthesize + post-process + play for LuxTTS (used by synthesizeAndPlay) */
41640
+ async synthesizeWithLuxtts(text, volume = 1, pitchFactor = 1, speedFactor = 1, stereoDelayMs = 0.6) {
41641
+ const wavPath = await this.synthesizeLuxttsWav(text, speedFactor);
41642
+ if (!wavPath)
41643
+ return;
41644
+ await this.postProcessAndPlayLuxtts(wavPath, volume, pitchFactor, stereoDelayMs);
41645
+ }
41496
41646
  /**
41497
41647
  * Synthesize text to WAV buffer using LuxTTS (no playback).
41498
41648
  * Used for Telegram voice messages and WebSocket streaming.
@@ -42124,6 +42274,85 @@ async function handleSlashCommand(input, ctx) {
42124
42274
  return "handled";
42125
42275
  }
42126
42276
  case "ipfs": {
42277
+ const ipfsSubCmd = (arg || "").trim().split(/\s+/);
42278
+ const ipfsAction = ipfsSubCmd[0]?.toLowerCase() || "";
42279
+ const ipfsArg = ipfsSubCmd.slice(1).join(" ");
42280
+ if (ipfsAction === "pin" && ipfsArg) {
42281
+ const cidToPin = ipfsArg.trim();
42282
+ if (!cidToPin.startsWith("bafy") && !cidToPin.startsWith("bafk") && !cidToPin.startsWith("Qm")) {
42283
+ renderWarning("Invalid CID format. Must start with bafy, bafk, or Qm");
42284
+ return "handled";
42285
+ }
42286
+ try {
42287
+ const { NexusTool: NexusTool2 } = __require("@open-agents/execution");
42288
+ const nexus = new NexusTool2(ctx.repoRoot);
42289
+ const result = await nexus.execute({ action: "ipfs_pin", cid: cidToPin, source: "user-pin" });
42290
+ if (result.success) {
42291
+ renderInfo(`Pinned: ${cidToPin}`);
42292
+ } else {
42293
+ renderWarning(`Pin failed: ${result.output}`);
42294
+ }
42295
+ } catch (e) {
42296
+ renderWarning(`Pin failed: ${e.message ?? e}`);
42297
+ }
42298
+ return "handled";
42299
+ }
42300
+ if (ipfsAction === "publish") {
42301
+ try {
42302
+ const { IdentityKernelTool: IdentityKernelTool2 } = __require("@open-agents/execution");
42303
+ const ik = new IdentityKernelTool2(ctx.repoRoot);
42304
+ const result = await ik.execute({ operation: "publish_snapshot" });
42305
+ if (result.success) {
42306
+ const cidMatch = result.output.match(/CID:\s*(bafy\S+|bafk\S+)/);
42307
+ if (cidMatch) {
42308
+ safeLog(`
42309
+ ${c2.bold("Identity Published")}`);
42310
+ safeLog(` CID: ${c2.bold(cidMatch[1])}`);
42311
+ safeLog(` ${c2.dim("Share this CID with other agents: /ipfs pin " + cidMatch[1])}
42312
+ `);
42313
+ } else {
42314
+ safeLog(result.output);
42315
+ }
42316
+ } else {
42317
+ renderWarning(result.output);
42318
+ }
42319
+ } catch (e) {
42320
+ renderWarning(`Publish failed: ${e.message ?? e}`);
42321
+ }
42322
+ return "handled";
42323
+ }
42324
+ if (ipfsAction === "cids" || ipfsAction === "ls" || ipfsAction === "pins") {
42325
+ try {
42326
+ const { NexusTool: NexusTool2 } = __require("@open-agents/execution");
42327
+ const nexus = new NexusTool2(ctx.repoRoot);
42328
+ const result = await nexus.execute({ action: "ipfs_ls" });
42329
+ if (result.success) {
42330
+ const data = JSON.parse(result.output);
42331
+ const pins = data.pins || [];
42332
+ if (pins.length === 0) {
42333
+ renderInfo("No pinned CIDs. Use /ipfs publish to publish your identity.");
42334
+ } else {
42335
+ const lines2 = [`
42336
+ ${c2.bold("Pinned CIDs")} (${pins.length})
42337
+ `];
42338
+ for (const p of pins.slice(0, 20)) {
42339
+ const src = p.source ? c2.dim(` (${p.source})`) : "";
42340
+ const pin = p.pinned ? c2.green("pinned") : c2.yellow("registered");
42341
+ lines2.push(` ${pin} ${c2.bold(p.cid.slice(0, 30))}...${src}`);
42342
+ }
42343
+ if (pins.length > 20)
42344
+ lines2.push(` ${c2.dim(`... and ${pins.length - 20} more`)}`);
42345
+ lines2.push("");
42346
+ safeLog(lines2.join("\n"));
42347
+ }
42348
+ } else {
42349
+ renderWarning(result.output);
42350
+ }
42351
+ } catch (e) {
42352
+ renderWarning(`List failed: ${e.message ?? e}`);
42353
+ }
42354
+ return "handled";
42355
+ }
42127
42356
  const lines = [];
42128
42357
  lines.push(`
42129
42358
  ${c2.bold("IPFS / Helia Status")}
@@ -42169,6 +42398,26 @@ async function handleSlashCommand(input, ctx) {
42169
42398
  lines.push(` ${c2.dim("Helia blocks:")}`);
42170
42399
  lines.push(` Blocks: ${c2.bold(String(heliaBlocks))} Size: ${c2.bold(formatFileSize(heliaBytes))}`);
42171
42400
  lines.push(` Backend: ${heliaBlocks > 0 ? c2.green("helia-ipfs") : c2.yellow("sha256-local (Helia not initialized)")}`);
42401
+ try {
42402
+ const statusFile = join51(ctx.repoRoot, ".oa", "nexus", "status.json");
42403
+ if (existsSync36(statusFile)) {
42404
+ const status = JSON.parse(readFileSync25(statusFile, "utf8"));
42405
+ if (status.peerId) {
42406
+ lines.push(`
42407
+ ${c2.bold("Peer Info")}`);
42408
+ lines.push(` Peer ID: ${c2.bold(status.peerId.slice(0, 20))}...`);
42409
+ if (status.multiaddrs && status.multiaddrs.length > 0) {
42410
+ lines.push(` Addresses: ${status.multiaddrs.length}`);
42411
+ for (const addr of status.multiaddrs.slice(0, 3)) {
42412
+ lines.push(` ${c2.dim(addr)}`);
42413
+ }
42414
+ }
42415
+ }
42416
+ }
42417
+ } catch {
42418
+ }
42419
+ lines.push(`
42420
+ ${c2.dim("Commands: /ipfs pin <CID> /ipfs publish /ipfs cids")}`);
42172
42421
  lines.push(`
42173
42422
  ${c2.bold("Identity Kernel")}`);
42174
42423
  const idDir = join51(ctx.repoRoot, ".oa", "identity");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.140.0",
3
+ "version": "0.140.1",
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",