open-agents-ai 0.186.13 → 0.186.14

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 +180 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7270,6 +7270,179 @@ async function handleCmd(cmd) {
7270
7270
  writeResp(id, { ok: true, output: 'pong' });
7271
7271
  break;
7272
7272
  }
7273
+
7274
+ // \u2500\u2500 NKN Backup Comms Layer \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\u2500
7275
+ // Persistent NKN addresses for fallback agent-to-agent communication.
7276
+ // Each OA instance gets a deterministic NKN address derived from the
7277
+ // mnemonic agent name. Messages are signed with HMAC to verify the
7278
+ // AGENT sent them (not a human spoofing).
7279
+
7280
+ case 'nkn_status': {
7281
+ var _nknIdFile = join(nexusDir, 'nkn-identity.json');
7282
+ var _nknPeersFile = join(nexusDir, 'nkn-peers.json');
7283
+ var _nknId = existsSync(_nknIdFile) ? JSON.parse(readFileSync(_nknIdFile, 'utf8')) : null;
7284
+ var _nknPeers = existsSync(_nknPeersFile) ? JSON.parse(readFileSync(_nknPeersFile, 'utf8')) : { peers: [] };
7285
+ var _nknConnected = nexus.nkn && nexus.nkn.isConnected;
7286
+ writeResp(id, { ok: true, output: JSON.stringify({
7287
+ enabled: !!nexus.nkn,
7288
+ connected: _nknConnected,
7289
+ address: _nknId ? _nknId.address : (nexus.nkn ? nexus.nkn.address : null),
7290
+ publicKey: _nknId ? _nknId.publicKey : null,
7291
+ peerCount: _nknPeers.peers ? _nknPeers.peers.length : 0,
7292
+ libp2pPeerId: nexus.peerId || null,
7293
+ agentName: agentName,
7294
+ }) });
7295
+ break;
7296
+ }
7297
+
7298
+ case 'nkn_peers': {
7299
+ // List all known NKN peer addresses (agents we've discovered)
7300
+ var _npFile = join(nexusDir, 'nkn-peers.json');
7301
+ var _npData = existsSync(_npFile) ? JSON.parse(readFileSync(_npFile, 'utf8')) : { peers: [] };
7302
+ writeResp(id, { ok: true, output: JSON.stringify(_npData) });
7303
+ break;
7304
+ }
7305
+
7306
+ case 'nkn_inbox': {
7307
+ // Read NKN inbox messages (most recent first, capped)
7308
+ var _niDir = join(inboxDir, '_nkn');
7309
+ var _niMessages = [];
7310
+ try {
7311
+ if (existsSync(_niDir)) {
7312
+ var _niFiles = readdirSync(_niDir).filter(f => f.endsWith('.json')).sort().reverse().slice(0, 50);
7313
+ for (var _niF of _niFiles) {
7314
+ try { _niMessages.push(JSON.parse(readFileSync(join(_niDir, _niF), 'utf8'))); } catch {}
7315
+ }
7316
+ }
7317
+ } catch {}
7318
+ writeResp(id, { ok: true, output: JSON.stringify({ messages: _niMessages, count: _niMessages.length }) });
7319
+ break;
7320
+ }
7321
+
7322
+ case 'nkn_send': {
7323
+ // Send a DM to another agent via NKN
7324
+ // args: { to: "nkn-address", message: "text", verify: true }
7325
+ var _nsTo = args.to || args.address;
7326
+ var _nsMsg = args.message || args.content || '';
7327
+ if (!_nsTo || !_nsMsg) {
7328
+ writeResp(id, { ok: false, output: 'Required: to (NKN address) and message' });
7329
+ break;
7330
+ }
7331
+ // Lazy NKN init
7332
+ if (!nexus.nkn || !nexus.nkn.isConnected) {
7333
+ try {
7334
+ // Load or generate NKN seed from global identity
7335
+ var _nsSeedFile = join(nexusDir, 'nkn-seed.hex');
7336
+ var _nsSeed = undefined;
7337
+ if (existsSync(_nsSeedFile)) {
7338
+ _nsSeed = readFileSync(_nsSeedFile, 'utf8').trim();
7339
+ } else {
7340
+ // Derive NKN seed from agent name + identity key (deterministic)
7341
+ var _nsKeyData = existsSync(keyPath) ? readFileSync(keyPath) : Buffer.from(agentName);
7342
+ _nsSeed = createHash('sha256').update(_nsKeyData).update('nkn-seed-v1').digest('hex');
7343
+ writeFileSync(_nsSeedFile, _nsSeed, { mode: 0o600 });
7344
+ }
7345
+ // Connect NKN via nexus client if available
7346
+ if (nexus.nkn && typeof nexus.nkn.connect === 'function') {
7347
+ await nexus.nkn.connect(_nsSeed);
7348
+ } else {
7349
+ // Direct NKN init if nexus client doesn't expose nkn
7350
+ var nknSdk = await import('nkn-sdk');
7351
+ var _nknMC = new nknSdk.default.MultiClient({ seed: _nsSeed, identifier: 'oa-' + agentName.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 20), numSubClients: 3 });
7352
+ await new Promise(function(resolve, reject) {
7353
+ _nknMC.onConnect(resolve);
7354
+ setTimeout(function() { reject(new Error('NKN connect timeout (15s)')); }, 15000);
7355
+ });
7356
+ // Store address
7357
+ var _nsIdFile = join(nexusDir, 'nkn-identity.json');
7358
+ writeFileSync(_nsIdFile, JSON.stringify({
7359
+ address: _nknMC.addr,
7360
+ publicKey: _nknMC.getPublicKey(),
7361
+ agentName: agentName,
7362
+ libp2pPeerId: nexus.peerId || null,
7363
+ createdAt: new Date().toISOString(),
7364
+ }, null, 2), { mode: 0o600 });
7365
+ dlog('NKN connected: ' + _nknMC.addr);
7366
+ // Receive handler \u2014 store in inbox
7367
+ _nknMC.onMessage(function({ src, payload }) {
7368
+ var _body = typeof payload === 'string' ? payload : new TextDecoder().decode(payload);
7369
+ var _niDir2 = join(inboxDir, '_nkn');
7370
+ 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 */ }
7385
+ try { writeFileSync(join(_niDir2, Date.now() + '.json'), JSON.stringify(_entry, null, 2)); } catch {}
7386
+ });
7387
+ // Stash the client for reuse
7388
+ nexus._nknDirect = _nknMC;
7389
+ }
7390
+ } catch (nknErr) {
7391
+ writeResp(id, { ok: false, output: 'NKN connect failed: ' + (nknErr.message || nknErr) });
7392
+ break;
7393
+ }
7394
+ }
7395
+ // Build verified message with HMAC
7396
+ var _nsClient = nexus._nknDirect || (nexus.nkn && nexus.nkn.client);
7397
+ if (!_nsClient) { writeResp(id, { ok: false, output: 'NKN not connected' }); break; }
7398
+ 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
+ });
7409
+ try {
7410
+ await _nsClient.send(_nsTo, _nsEnvelope, { msgHoldingSeconds: 3600 });
7411
+ // Add to peer list if not already there
7412
+ var _nsPeersFile = join(nexusDir, 'nkn-peers.json');
7413
+ var _nsPeers = existsSync(_nsPeersFile) ? JSON.parse(readFileSync(_nsPeersFile, 'utf8')) : { peers: [] };
7414
+ if (!_nsPeers.peers.find(function(p) { return p.nknAddress === _nsTo; })) {
7415
+ _nsPeers.peers.push({ nknAddress: _nsTo, lastContact: _nsTimestamp, agentName: args.to_agent || 'unknown' });
7416
+ writeFileSync(_nsPeersFile, JSON.stringify(_nsPeers, null, 2));
7417
+ }
7418
+ writeResp(id, { ok: true, output: 'Sent to ' + _nsTo + ' (HMAC verified, held 1h if offline)' });
7419
+ } catch (sendErr) {
7420
+ writeResp(id, { ok: false, output: 'NKN send failed: ' + (sendErr.message || sendErr) });
7421
+ }
7422
+ break;
7423
+ }
7424
+
7425
+ case 'nkn_add_peer': {
7426
+ // Manually add a peer's NKN address to the address book
7427
+ var _napAddr = args.nkn_address || args.address;
7428
+ var _napName = args.agent_name || args.name || 'unknown';
7429
+ var _napPeerId = args.libp2p_peer_id || args.peer_id || '';
7430
+ if (!_napAddr) { writeResp(id, { ok: false, output: 'Required: nkn_address' }); break; }
7431
+ var _napFile = join(nexusDir, 'nkn-peers.json');
7432
+ var _napData = existsSync(_napFile) ? JSON.parse(readFileSync(_napFile, 'utf8')) : { peers: [] };
7433
+ var _napExisting = _napData.peers.findIndex(function(p) { return p.nknAddress === _napAddr; });
7434
+ if (_napExisting >= 0) {
7435
+ _napData.peers[_napExisting].agentName = _napName;
7436
+ _napData.peers[_napExisting].libp2pPeerId = _napPeerId || _napData.peers[_napExisting].libp2pPeerId;
7437
+ _napData.peers[_napExisting].lastContact = Date.now();
7438
+ } else {
7439
+ _napData.peers.push({ nknAddress: _napAddr, agentName: _napName, libp2pPeerId: _napPeerId, addedAt: Date.now(), lastContact: Date.now() });
7440
+ }
7441
+ writeFileSync(_napFile, JSON.stringify(_napData, null, 2));
7442
+ writeResp(id, { ok: true, output: 'Added peer: ' + _napName + ' (' + _napAddr.slice(0, 30) + '...)' });
7443
+ break;
7444
+ }
7445
+
7273
7446
  default:
7274
7447
  writeResp(id, { ok: false, output: 'Unknown daemon command: ' + action });
7275
7448
  }
@@ -8218,9 +8391,14 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
8218
8391
  "ipfs_add",
8219
8392
  "ipfs_pin",
8220
8393
  "ipfs_ls",
8221
- "cohere_publish_insight"
8394
+ "cohere_publish_insight",
8395
+ "nkn_status",
8396
+ "nkn_peers",
8397
+ "nkn_inbox",
8398
+ "nkn_send",
8399
+ "nkn_add_peer"
8222
8400
  ],
8223
- 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."
8401
+ 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."
8224
8402
  },
8225
8403
  room_id: {
8226
8404
  type: "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.186.13",
3
+ "version": "0.186.14",
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",