open-agents-ai 0.186.14 → 0.186.15

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 +126 -38
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5343,7 +5343,7 @@ var init_nexus = __esm({
5343
5343
  * Usage: node nexus-daemon.mjs <nexus-dir> <agent-name> [agent-type]
5344
5344
  */
5345
5345
 
5346
- import { NexusClient } from 'open-agents-nexus';
5346
+ import { NexusClient, buildNknEnvelope, parseNknEnvelope, deriveNknSeed } from 'open-agents-nexus';
5347
5347
  import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, appendFileSync, watch as fsWatch } from 'node:fs';
5348
5348
  import { join } from 'node:path';
5349
5349
  import { homedir, hostname } from 'node:os';
@@ -5639,12 +5639,33 @@ async function handleCmd(cmd) {
5639
5639
  writeResp(id, { ok: false, output: 'NATS not connected \u2014 cannot announce sponsorship' });
5640
5640
  return;
5641
5641
  }
5642
+ // NX-07: Fetch per-model metadata from Ollama for capacity announcements
5643
+ var _saModels = args.models || [];
5644
+ var _saModelDetails = [];
5645
+ try {
5646
+ var _saOllamaUrl = process.env.OLLAMA_HOST || 'http://localhost:11434';
5647
+ var _saTagsResp = await fetch(_saOllamaUrl + '/api/tags');
5648
+ var _saTags = await _saTagsResp.json();
5649
+ for (var _saM of (_saTags.models || [])) {
5650
+ var _saName = _saM.name || '';
5651
+ if (_saModels.length > 0 && !_saModels.includes(_saName)) continue;
5652
+ _saModelDetails.push({
5653
+ name: _saName,
5654
+ sizeGB: Math.round((_saM.size || 0) / (1024*1024*1024) * 10) / 10,
5655
+ paramCount: _saM.details?.parameter_size || '',
5656
+ quantization: _saM.details?.quantization_level || '',
5657
+ family: _saM.details?.family || '',
5658
+ contextLength: 0, // would need /api/show per model \u2014 too slow for announce
5659
+ });
5660
+ }
5661
+ } catch {}
5642
5662
  var sponsorData = {
5643
5663
  type: 'sponsor.announce',
5644
5664
  peerId: globalThis._daemonPeerId || 'unknown',
5645
5665
  libp2pPeerId: globalThis._daemonPeerId || '',
5646
5666
  name: args.name || 'Anonymous Sponsor',
5647
- models: args.models || [],
5667
+ models: _saModels.length > 0 ? _saModels : _saModelDetails.map(function(m) { return m.name; }),
5668
+ modelDetails: _saModelDetails, // NX-07: per-model capacity
5648
5669
  tunnelUrl: args.tunnel_url || null,
5649
5670
  authKey: args.auth_key || '',
5650
5671
  limits: {
@@ -5690,9 +5711,101 @@ async function handleCmd(cmd) {
5690
5711
  await rooms.get('sponsors').send(JSON.stringify(sponsorData), { format: 'application/json' });
5691
5712
  } catch {}
5692
5713
  }
5693
- writeResp(id, { ok: true, output: 'Sponsor announced: ' + sponsorData.name + ' (' + sponsorData.models.length + ' models)' });
5714
+ // NX-04: GossipSub meta topic announcement (multi-layer discovery)
5715
+ try {
5716
+ var _node = nexus.network ? nexus.network.node : nexus.node;
5717
+ if (_node && _node.services && _node.services.pubsub) {
5718
+ var _metaTopic = '/nexus/meta';
5719
+ var _metaMsg = JSON.stringify({
5720
+ version: 1,
5721
+ type: 'capability',
5722
+ id: Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
5723
+ timestamp: Date.now(),
5724
+ sender: nexus.peerId || 'unknown',
5725
+ topic: _metaTopic,
5726
+ payload: {
5727
+ capabilities: [{
5728
+ name: 'sponsor_inference',
5729
+ protocol: '/nexus/invoke/1.1.0',
5730
+ description: 'Sponsored inference via ' + sponsorData.name,
5731
+ pricing: 'free',
5732
+ rateLimit: String(sponsorData.limits.maxRequestsPerMinute) + '/min',
5733
+ }],
5734
+ sponsor: sponsorData,
5735
+ },
5736
+ });
5737
+ try { await _node.services.pubsub.subscribe(_metaTopic); } catch {}
5738
+ await _node.services.pubsub.publish(_metaTopic, new TextEncoder().encode(_metaMsg));
5739
+ dlog('sponsor_announce: published to GossipSub /nexus/meta');
5740
+ }
5741
+ } catch (gsErr) {
5742
+ dlog('sponsor_announce: GossipSub meta failed: ' + (gsErr.message || gsErr));
5743
+ }
5744
+ writeResp(id, { ok: true, output: 'Sponsor announced: ' + sponsorData.name + ' (' + sponsorData.models.length + ' models) [NATS+KV+Room+GossipSub]' });
5694
5745
  break;
5695
5746
  }
5747
+ // NX-05 + NX-06: Register sponsor_inference capability for P2P inference relay.
5748
+ // When a sponsor activates, this registers an invoke handler that proxies
5749
+ // chat completions to local Ollama \u2014 no tunnel needed for P2P inference.
5750
+ case 'register_sponsor_inference': {
5751
+ var _rsiOllamaUrl = args.ollama_url || process.env.OLLAMA_HOST || 'http://localhost:11434';
5752
+ var _rsiAllowedModels = args.models ? String(args.models).split(',') : null;
5753
+ if (typeof nexus.registerCapability !== 'function') {
5754
+ writeResp(id, { ok: false, output: 'registerCapability not available' });
5755
+ break;
5756
+ }
5757
+ nexus.registerCapability('sponsor_inference', async (request, stream) => {
5758
+ var _rsiInput = '';
5759
+ stream.onData(function(msg) {
5760
+ if (msg.type === 'invoke.chunk') {
5761
+ _rsiInput += (typeof msg.data === 'string' ? msg.data : JSON.stringify(msg.data));
5762
+ }
5763
+ });
5764
+ // Wait for input to finish (accept fires when sender signals done)
5765
+ await stream.accept();
5766
+ try {
5767
+ var _rsiReq = JSON.parse(_rsiInput);
5768
+ var _rsiModel = _rsiReq.model || '';
5769
+ // Check model allowlist
5770
+ if (_rsiAllowedModels && !_rsiAllowedModels.includes(_rsiModel)) {
5771
+ stream.write({ type: 'invoke.error', error: 'Model not allowed: ' + _rsiModel });
5772
+ return;
5773
+ }
5774
+ // Proxy to local Ollama /v1/chat/completions
5775
+ var _rsiBody = JSON.stringify({
5776
+ model: _rsiModel,
5777
+ messages: _rsiReq.messages || [],
5778
+ stream: true,
5779
+ });
5780
+ var _rsiResp = await fetch(_rsiOllamaUrl + '/v1/chat/completions', {
5781
+ method: 'POST',
5782
+ headers: { 'Content-Type': 'application/json' },
5783
+ body: _rsiBody,
5784
+ });
5785
+ if (!_rsiResp.ok) {
5786
+ stream.write({ type: 'invoke.error', error: 'Ollama returned HTTP ' + _rsiResp.status });
5787
+ return;
5788
+ }
5789
+ // Stream response back via invoke protocol
5790
+ var _rsiReader = _rsiResp.body?.getReader();
5791
+ if (_rsiReader) {
5792
+ var _rsiDecoder = new TextDecoder();
5793
+ while (true) {
5794
+ var _rsiChunk = await _rsiReader.read();
5795
+ if (_rsiChunk.done) break;
5796
+ stream.write({ type: 'invoke.event', data: _rsiDecoder.decode(_rsiChunk.value, { stream: true }) });
5797
+ }
5798
+ }
5799
+ stream.write({ type: 'invoke.done' });
5800
+ dlog('sponsor_inference: served ' + _rsiModel + ' to ' + (request.from || '?').slice(0, 16));
5801
+ } catch (rsiErr) {
5802
+ stream.write({ type: 'invoke.error', error: String(rsiErr.message || rsiErr) });
5803
+ }
5804
+ });
5805
+ writeResp(id, { ok: true, output: 'Registered sponsor_inference capability (P2P relay active)' });
5806
+ break;
5807
+ }
5808
+
5696
5809
  case 'sponsor_discover': {
5697
5810
  var foundSponsors = [];
5698
5811
  var discoverTimeout = parseInt(args.timeout_ms || '5000', 10);
@@ -7328,32 +7441,29 @@ async function handleCmd(cmd) {
7328
7441
  writeResp(id, { ok: false, output: 'Required: to (NKN address) and message' });
7329
7442
  break;
7330
7443
  }
7331
- // Lazy NKN init
7444
+ // Lazy NKN init \u2014 uses open-agents-nexus API (deriveNknSeed, NknFallback)
7332
7445
  if (!nexus.nkn || !nexus.nkn.isConnected) {
7333
7446
  try {
7334
- // Load or generate NKN seed from global identity
7335
7447
  var _nsSeedFile = join(nexusDir, 'nkn-seed.hex');
7336
7448
  var _nsSeed = undefined;
7337
7449
  if (existsSync(_nsSeedFile)) {
7338
7450
  _nsSeed = readFileSync(_nsSeedFile, 'utf8').trim();
7339
7451
  } else {
7340
- // Derive NKN seed from agent name + identity key (deterministic)
7452
+ // Use open-agents-nexus deriveNknSeed() for deterministic address
7341
7453
  var _nsKeyData = existsSync(keyPath) ? readFileSync(keyPath) : Buffer.from(agentName);
7342
- _nsSeed = createHash('sha256').update(_nsKeyData).update('nkn-seed-v1').digest('hex');
7454
+ _nsSeed = deriveNknSeed(_nsKeyData);
7343
7455
  writeFileSync(_nsSeedFile, _nsSeed, { mode: 0o600 });
7344
7456
  }
7345
- // Connect NKN via nexus client if available
7346
7457
  if (nexus.nkn && typeof nexus.nkn.connect === 'function') {
7347
7458
  await nexus.nkn.connect(_nsSeed);
7348
7459
  } else {
7349
- // Direct NKN init if nexus client doesn't expose nkn
7460
+ // Direct NKN init via nkn-sdk
7350
7461
  var nknSdk = await import('nkn-sdk');
7351
7462
  var _nknMC = new nknSdk.default.MultiClient({ seed: _nsSeed, identifier: 'oa-' + agentName.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 20), numSubClients: 3 });
7352
7463
  await new Promise(function(resolve, reject) {
7353
7464
  _nknMC.onConnect(resolve);
7354
7465
  setTimeout(function() { reject(new Error('NKN connect timeout (15s)')); }, 15000);
7355
7466
  });
7356
- // Store address
7357
7467
  var _nsIdFile = join(nexusDir, 'nkn-identity.json');
7358
7468
  writeFileSync(_nsIdFile, JSON.stringify({
7359
7469
  address: _nknMC.addr,
@@ -7363,28 +7473,14 @@ async function handleCmd(cmd) {
7363
7473
  createdAt: new Date().toISOString(),
7364
7474
  }, null, 2), { mode: 0o600 });
7365
7475
  dlog('NKN connected: ' + _nknMC.addr);
7366
- // Receive handler \u2014 store in inbox
7476
+ // Receive handler \u2014 uses open-agents-nexus parseNknEnvelope() for HMAC verification
7367
7477
  _nknMC.onMessage(function({ src, payload }) {
7368
7478
  var _body = typeof payload === 'string' ? payload : new TextDecoder().decode(payload);
7369
7479
  var _niDir2 = join(inboxDir, '_nkn');
7370
7480
  mkdirSync(_niDir2, { recursive: true });
7371
- var _entry = { sender: src, raw: _body, receivedAt: Date.now(), verified: false };
7372
- // Verify HMAC if present
7373
- try {
7374
- var _parsed = JSON.parse(_body);
7375
- if (_parsed._hmac && _parsed._agentName && _parsed.content) {
7376
- // Verify: HMAC(agentName + content + timestamp, shared_secret)
7377
- var _sharedSecret = createHash('sha256').update(src).update(_nknMC.addr).update('oa-nkn-v1').digest('hex');
7378
- var _expectedHmac = createHmac('sha256', _sharedSecret).update(_parsed._agentName + _parsed.content + String(_parsed.timestamp || 0)).digest('hex').slice(0, 16);
7379
- _entry.verified = (_parsed._hmac === _expectedHmac);
7380
- _entry.agentName = _parsed._agentName;
7381
- _entry.content = _parsed.content;
7382
- _entry.timestamp = _parsed.timestamp;
7383
- }
7384
- } catch { /* raw text message \u2014 not verified */ }
7481
+ var _entry = parseNknEnvelope(src, _nknMC.addr, _body);
7385
7482
  try { writeFileSync(join(_niDir2, Date.now() + '.json'), JSON.stringify(_entry, null, 2)); } catch {}
7386
7483
  });
7387
- // Stash the client for reuse
7388
7484
  nexus._nknDirect = _nknMC;
7389
7485
  }
7390
7486
  } catch (nknErr) {
@@ -7392,20 +7488,11 @@ async function handleCmd(cmd) {
7392
7488
  break;
7393
7489
  }
7394
7490
  }
7395
- // Build verified message with HMAC
7491
+ // Build verified message using open-agents-nexus buildNknEnvelope()
7396
7492
  var _nsClient = nexus._nknDirect || (nexus.nkn && nexus.nkn.client);
7397
7493
  if (!_nsClient) { writeResp(id, { ok: false, output: 'NKN not connected' }); break; }
7398
7494
  var _nsMyAddr = _nsClient.addr || (nexus.nkn ? nexus.nkn.address : '');
7399
- var _nsTimestamp = Date.now();
7400
- var _nsSharedSecret = createHash('sha256').update(_nsMyAddr).update(_nsTo).update('oa-nkn-v1').digest('hex');
7401
- var _nsHmac = createHmac('sha256', _nsSharedSecret).update(agentName + _nsMsg + String(_nsTimestamp)).digest('hex').slice(0, 16);
7402
- var _nsEnvelope = JSON.stringify({
7403
- _agentName: agentName,
7404
- content: _nsMsg,
7405
- timestamp: _nsTimestamp,
7406
- libp2pPeerId: nexus.peerId || null,
7407
- _hmac: _nsHmac,
7408
- });
7495
+ var _nsEnvelope = buildNknEnvelope(_nsMyAddr, _nsTo, agentName, _nsMsg, nexus.peerId || undefined);
7409
7496
  try {
7410
7497
  await _nsClient.send(_nsTo, _nsEnvelope, { msgHoldingSeconds: 3600 });
7411
7498
  // Add to peer list if not already there
@@ -8396,7 +8483,8 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
8396
8483
  "nkn_peers",
8397
8484
  "nkn_inbox",
8398
8485
  "nkn_send",
8399
- "nkn_add_peer"
8486
+ "nkn_add_peer",
8487
+ "register_sponsor_inference"
8400
8488
  ],
8401
8489
  description: "The nexus action. MUST call 'connect' first (spawns daemon). Then: join_room, send_message, discover_peers, expose, status, nkn_status (NKN backup comms), nkn_peers (address book), nkn_inbox (read messages), nkn_send (DM via NKN with HMAC verification), nkn_add_peer (add to address book), cohere_stats, ipfs_add, etc."
8402
8490
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.186.14",
3
+ "version": "0.186.15",
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",