open-agents-ai 0.84.0 → 0.86.0

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.
package/dist/index.js CHANGED
@@ -11239,6 +11239,8 @@ const cmdFile = join(nexusDir, 'cmd.json');
11239
11239
  const respFile = join(nexusDir, 'resp.json');
11240
11240
  const statusFile = join(nexusDir, 'status.json');
11241
11241
  const inboxDir = join(nexusDir, 'inbox');
11242
+ const logFile = join(nexusDir, 'daemon.log');
11243
+ function dlog(msg) { try { appendFileSync(logFile, new Date().toISOString() + ' ' + msg + '\\n'); } catch {} }
11242
11244
  const pidFile = join(nexusDir, 'daemon.pid');
11243
11245
  const invocationsDir = join(nexusDir, 'invocations');
11244
11246
  const meteringFile = join(nexusDir, 'metering.jsonl');
@@ -11292,6 +11294,7 @@ function writeResp(id, result) {
11292
11294
 
11293
11295
  async function handleCmd(cmd) {
11294
11296
  const { id, action, args } = cmd;
11297
+ dlog('handleCmd: action=' + action + ' id=' + id);
11295
11298
  try {
11296
11299
  switch (action) {
11297
11300
  case 'join_room': {
@@ -11374,8 +11377,19 @@ async function handleCmd(cmd) {
11374
11377
  const capability = args.capability || 'text-generation';
11375
11378
  const input = args.input || {};
11376
11379
  if (!peerId) { writeResp(id, { ok: false, output: 'target_peer is required' }); return; }
11377
- const result = await nexus.invokeCapability(peerId, capability, typeof input === 'string' ? { prompt: input } : input, { stream: false, maxDurationMs: 60000 });
11378
- writeResp(id, { ok: true, output: JSON.stringify(result, null, 2) });
11380
+ dlog('invoke_capability: peer=' + peerId.slice(0, 20) + ' cap=' + capability);
11381
+ try {
11382
+ // Wrap in a 90s timeout to prevent hangs from nexus library issues
11383
+ const INVOKE_TIMEOUT = 90_000;
11384
+ const invokePromise = nexus.invokeCapability(peerId, capability, typeof input === 'string' ? { prompt: input } : input, { stream: false, maxDurationMs: 60000 });
11385
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('invoke_capability timed out after ' + (INVOKE_TIMEOUT / 1000) + 's (client-side timeout)')), INVOKE_TIMEOUT));
11386
+ const result = await Promise.race([invokePromise, timeoutPromise]);
11387
+ dlog('invoke_capability: SUCCESS');
11388
+ writeResp(id, { ok: true, output: JSON.stringify(result, null, 2) });
11389
+ } catch (invokeErr) {
11390
+ dlog('invoke_capability: ERROR ' + (invokeErr.message || String(invokeErr)));
11391
+ writeResp(id, { ok: false, output: 'Invoke error: ' + (invokeErr.message || String(invokeErr)) });
11392
+ }
11379
11393
  break;
11380
11394
  }
11381
11395
  case 'store_content': {
@@ -11551,6 +11565,207 @@ async function handleCmd(cmd) {
11551
11565
  break;
11552
11566
  }
11553
11567
 
11568
+ // \u2500\u2500 Metered Inference Exposure \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
11569
+ case 'expose': {
11570
+ // Query Ollama for available models
11571
+ const ollamaUrl = args.ollama_url || process.env.OLLAMA_URL || 'http://localhost:11434';
11572
+ let models = [];
11573
+ try {
11574
+ const tagsResp = await fetch(ollamaUrl + '/api/tags');
11575
+ if (tagsResp.ok) {
11576
+ const tagsData = await tagsResp.json();
11577
+ models = (tagsData.models || []).map(m => ({
11578
+ name: m.name,
11579
+ size: m.size || 0,
11580
+ parameterSize: m.details?.parameter_size || '',
11581
+ family: m.details?.family || '',
11582
+ quantization: m.details?.quantization_level || '',
11583
+ }));
11584
+ }
11585
+ } catch (e) {
11586
+ writeResp(id, { ok: false, output: 'Cannot reach Ollama at ' + ollamaUrl + ': ' + e.message });
11587
+ return;
11588
+ }
11589
+
11590
+ if (models.length === 0) {
11591
+ writeResp(id, { ok: false, output: 'No models found on Ollama. Pull a model first.' });
11592
+ return;
11593
+ }
11594
+
11595
+ // Fetch market rates from OpenRouter (free, no auth)
11596
+ let marketRates = {};
11597
+ try {
11598
+ const orResp = await fetch('https://openrouter.ai/api/v1/models');
11599
+ if (orResp.ok) {
11600
+ const orData = await orResp.json();
11601
+ for (const m of (orData.data || [])) {
11602
+ if (m.pricing) {
11603
+ marketRates[m.id] = {
11604
+ input: parseFloat(m.pricing.prompt || '0') * 1_000_000,
11605
+ output: parseFloat(m.pricing.completion || '0') * 1_000_000,
11606
+ };
11607
+ }
11608
+ }
11609
+ }
11610
+ } catch { /* offline \u2014 use zero rates */ }
11611
+
11612
+ // Build pricing menu \u2014 match local models to market rates
11613
+ const margin = parseFloat(args.margin || '0.5'); // default 50% of market rate
11614
+ const pricingMenu = [];
11615
+
11616
+ for (const model of models) {
11617
+ // Try to match to OpenRouter model ID
11618
+ const baseName = model.name.replace(/:latest$/, '').toLowerCase();
11619
+ let matched = null;
11620
+ for (const [orId, rates] of Object.entries(marketRates)) {
11621
+ const orBase = orId.toLowerCase();
11622
+ if (orBase.includes(baseName) || baseName.includes(orBase.split('/').pop())) {
11623
+ matched = { orId, ...rates };
11624
+ break;
11625
+ }
11626
+ }
11627
+
11628
+ const entry = {
11629
+ model: model.name,
11630
+ parameterSize: model.parameterSize,
11631
+ family: model.family,
11632
+ quantization: model.quantization,
11633
+ pricing: {
11634
+ input_per_1m_tokens: matched ? +(matched.input * margin).toFixed(4) : 0,
11635
+ output_per_1m_tokens: matched ? +(matched.output * margin).toFixed(4) : 0,
11636
+ currency: 'USD',
11637
+ source: matched ? 'openrouter:' + matched.orId : 'self-hosted:free',
11638
+ margin: margin,
11639
+ },
11640
+ };
11641
+ pricingMenu.push(entry);
11642
+
11643
+ // Register as nexus capability
11644
+ const capName = 'inference:' + model.name.replace(/[^a-zA-Z0-9._-]/g, '_');
11645
+ if (typeof nexus.registerCapability === 'function') {
11646
+ nexus.registerCapability(capName, async (request, stream) => {
11647
+ const logEntry = {
11648
+ ts: Date.now(),
11649
+ from: request.from || 'unknown',
11650
+ capability: capName,
11651
+ model: model.name,
11652
+ requestId: request.requestId,
11653
+ };
11654
+ const logFile = join(invocationsDir, Date.now() + '-' + capName + '.json');
11655
+ try { writeFileSync(logFile, JSON.stringify(logEntry, null, 2)); } catch {}
11656
+
11657
+ // Collect input
11658
+ let prompt = '';
11659
+ stream.onData((msg) => {
11660
+ if (msg.type === 'invoke.chunk') {
11661
+ prompt += (typeof msg.data === 'string' ? msg.data : JSON.stringify(msg.data));
11662
+ }
11663
+ });
11664
+
11665
+ // Accept
11666
+ await stream.write({
11667
+ type: 'invoke.accept', version: 1,
11668
+ requestId: request.requestId, accepted: true,
11669
+ });
11670
+
11671
+ // Forward to Ollama
11672
+ try {
11673
+ const genResp = await fetch(ollamaUrl + '/api/generate', {
11674
+ method: 'POST',
11675
+ headers: { 'Content-Type': 'application/json' },
11676
+ body: JSON.stringify({
11677
+ model: model.name,
11678
+ prompt: prompt || 'Hello',
11679
+ stream: false,
11680
+ }),
11681
+ });
11682
+ const genData = await genResp.json();
11683
+ const output = genData.response || '';
11684
+ const inputTokens = genData.prompt_eval_count || 0;
11685
+ const outputTokens = genData.eval_count || 0;
11686
+
11687
+ // Stream result back
11688
+ await stream.write({
11689
+ type: 'invoke.event', version: 1,
11690
+ requestId: request.requestId, seq: 0,
11691
+ event: 'result',
11692
+ data: JSON.stringify({
11693
+ model: model.name,
11694
+ response: output,
11695
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens },
11696
+ pricing: entry.pricing,
11697
+ }),
11698
+ });
11699
+
11700
+ await stream.write({
11701
+ type: 'invoke.done', version: 1,
11702
+ requestId: request.requestId,
11703
+ usage: {
11704
+ inputBytes: prompt.length,
11705
+ outputBytes: output.length,
11706
+ tokens: inputTokens + outputTokens,
11707
+ },
11708
+ });
11709
+ } catch (e) {
11710
+ await stream.write({
11711
+ type: 'invoke.event', version: 1,
11712
+ requestId: request.requestId, seq: 0,
11713
+ event: 'error', data: 'Inference failed: ' + e.message,
11714
+ });
11715
+ await stream.write({
11716
+ type: 'invoke.done', version: 1,
11717
+ requestId: request.requestId,
11718
+ usage: { inputBytes: 0, outputBytes: 0 },
11719
+ });
11720
+ }
11721
+ stream.close();
11722
+ });
11723
+ }
11724
+ }
11725
+
11726
+ // Write pricing menu to file
11727
+ const pricingFile = join(nexusDir, 'pricing.json');
11728
+ writeFileSync(pricingFile, JSON.stringify({ updated: new Date().toISOString(), models: pricingMenu }, null, 2));
11729
+ writeStatus({ exposedModels: pricingMenu.length });
11730
+
11731
+ const lines = ['Exposed ' + pricingMenu.length + ' model(s) as nexus capabilities:'];
11732
+ for (const p of pricingMenu) {
11733
+ const cost = p.pricing.input_per_1m_tokens === 0
11734
+ ? 'FREE (self-hosted)'
11735
+ : '$' + p.pricing.input_per_1m_tokens + '/$' + p.pricing.output_per_1m_tokens + ' per 1M tokens';
11736
+ lines.push(' inference:' + p.model + ' \u2014 ' + cost);
11737
+ }
11738
+ lines.push('');
11739
+ lines.push('Pricing menu saved to ' + pricingFile);
11740
+ lines.push('Market rates: ' + Object.keys(marketRates).length + ' models from OpenRouter');
11741
+ writeResp(id, { ok: true, output: lines.join('\\n') });
11742
+ break;
11743
+ }
11744
+
11745
+ case 'pricing_menu': {
11746
+ const pricingFile = join(nexusDir, 'pricing.json');
11747
+ if (!existsSync(pricingFile)) {
11748
+ writeResp(id, { ok: false, output: 'No pricing menu. Run expose first.' });
11749
+ return;
11750
+ }
11751
+ try {
11752
+ const menu = JSON.parse(readFileSync(pricingFile, 'utf8'));
11753
+ const lines = ['Inference Pricing Menu (updated: ' + menu.updated + ')'];
11754
+ lines.push('');
11755
+ for (const m of (menu.models || [])) {
11756
+ const cost = m.pricing.input_per_1m_tokens === 0
11757
+ ? 'FREE'
11758
+ : '$' + m.pricing.input_per_1m_tokens + ' in / $' + m.pricing.output_per_1m_tokens + ' out per 1M tokens';
11759
+ lines.push(' ' + m.model + ' (' + m.parameterSize + ', ' + m.quantization + ')');
11760
+ lines.push(' ' + cost + ' [' + m.pricing.source + ']');
11761
+ }
11762
+ writeResp(id, { ok: true, output: lines.join('\\n') });
11763
+ } catch (e) {
11764
+ writeResp(id, { ok: false, output: 'Failed to read pricing menu: ' + e.message });
11765
+ }
11766
+ break;
11767
+ }
11768
+
11554
11769
  case 'ping': {
11555
11770
  writeResp(id, { ok: true, output: 'pong' });
11556
11771
  break;
@@ -11572,7 +11787,9 @@ setInterval(() => {
11572
11787
  const cmd = JSON.parse(raw);
11573
11788
  if (cmd.id === lastCmdId) return; // already processed
11574
11789
  lastCmdId = cmd.id;
11575
- handleCmd(cmd);
11790
+ handleCmd(cmd).catch((err) => {
11791
+ writeResp(cmd.id, { ok: false, output: 'Error: ' + (err.message || String(err)) });
11792
+ });
11576
11793
  } catch {}
11577
11794
  }, 500);
11578
11795
 
@@ -11659,7 +11876,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11659
11876
  ];
11660
11877
  NexusTool = class {
11661
11878
  name = "nexus";
11662
- description = "Decentralized agent-to-agent communication via open-agents-nexus v1.5.0. Spawns a background Node.js process with real network sockets for libp2p + NATS P2P mesh. Simple flow: connect \u2192 join room \u2192 send messages. Auto-installs/updates nexus if needed. v1.5.0: register_capability (serve invocations), block_peer/unblock_peer (trust policy), metering_status (usage tracking), room_members (live member tracking). Also supports direct peer invoke (streaming inference), IPFS content storage, direct messages, peer discovery, x402 micropayments, and inference proofs.";
11879
+ description = "Decentralized agent-to-agent communication via open-agents-nexus v1.5.0. Spawns a background Node.js process with real network sockets for libp2p + NATS P2P mesh. Simple flow: connect \u2192 join room \u2192 send messages. Auto-installs/updates nexus if needed. EXPOSE: expose action queries Ollama models, fetches OpenRouter market rates, registers metered inference capabilities so peers can invoke your models. pricing_menu shows rates. v1.5.0: register_capability, block_peer/unblock_peer, metering_status, room_members. Also supports direct peer invoke, IPFS storage, DMs, discovery, x402, inference proofs.";
11663
11880
  parameters = {
11664
11881
  type: "object",
11665
11882
  properties: {
@@ -11689,7 +11906,9 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11689
11906
  "block_peer",
11690
11907
  "unblock_peer",
11691
11908
  "metering_status",
11692
- "room_members"
11909
+ "room_members",
11910
+ "expose",
11911
+ "pricing_menu"
11693
11912
  ],
11694
11913
  description: "The nexus action to perform"
11695
11914
  },
@@ -11736,6 +11955,14 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11736
11955
  cid: {
11737
11956
  type: "string",
11738
11957
  description: "Content ID for retrieve_content"
11958
+ },
11959
+ ollama_url: {
11960
+ type: "string",
11961
+ description: "Ollama API URL for expose (default: http://localhost:11434)"
11962
+ },
11963
+ margin: {
11964
+ type: "string",
11965
+ description: "Price margin multiplier for expose (0.5 = 50% of market rate, 0 = free). Default: 0.5"
11739
11966
  }
11740
11967
  },
11741
11968
  required: ["action"],
@@ -11789,10 +12016,10 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11789
12016
  result = await this.doSendDM(args);
11790
12017
  break;
11791
12018
  case "find_agent":
11792
- result = await this.sendDaemonCmd("find_agent", args);
12019
+ result = await this.sendDaemonCmd("find_agent", args, 3e4);
11793
12020
  break;
11794
12021
  case "invoke_capability":
11795
- result = await this.sendDaemonCmd("invoke_capability", args);
12022
+ result = await this.sendDaemonCmd("invoke_capability", args, 1e5);
11796
12023
  break;
11797
12024
  case "store_content":
11798
12025
  result = await this.sendDaemonCmd("store_content", args);
@@ -11830,6 +12057,12 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11830
12057
  case "room_members":
11831
12058
  result = await this.sendDaemonCmd("room_members", args);
11832
12059
  break;
12060
+ case "expose":
12061
+ result = await this.sendDaemonCmd("expose", args, 3e4);
12062
+ break;
12063
+ case "pricing_menu":
12064
+ result = await this.sendDaemonCmd("pricing_menu", args);
12065
+ break;
11833
12066
  default:
11834
12067
  return { success: false, output: "", error: `Unknown nexus action: ${action}`, durationMs: Date.now() - start };
11835
12068
  }
@@ -11853,7 +12086,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11853
12086
  return null;
11854
12087
  }
11855
12088
  }
11856
- async sendDaemonCmd(action, args) {
12089
+ async sendDaemonCmd(action, args, timeoutMs = 15e3) {
11857
12090
  const pid = this.getDaemonPid();
11858
12091
  if (!pid)
11859
12092
  throw new Error("Nexus daemon not running. Use action 'connect' first.");
@@ -11864,7 +12097,8 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11864
12097
  await unlink(respFile).catch(() => {
11865
12098
  });
11866
12099
  await writeFile12(cmdFile, JSON.stringify({ id: cmdId, action, args }, null, 2));
11867
- for (let i = 0; i < 30; i++) {
12100
+ const polls = Math.ceil(timeoutMs / 500);
12101
+ for (let i = 0; i < polls; i++) {
11868
12102
  await new Promise((r) => setTimeout(r, 500));
11869
12103
  if (!existsSync21(respFile))
11870
12104
  continue;
@@ -11876,7 +12110,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11876
12110
  } catch {
11877
12111
  }
11878
12112
  }
11879
- return "Daemon did not respond within 15s. It may still be processing.";
12113
+ return `Daemon did not respond within ${Math.round(timeoutMs / 1e3)}s. It may still be processing.`;
11880
12114
  }
11881
12115
  // =========================================================================
11882
12116
  // Actions
@@ -40465,7 +40699,7 @@ ${result.text}`;
40465
40699
  const steerPrompt = [
40466
40700
  `The user typed a mid-task message while the agent is working. Your job:`,
40467
40701
  `1. Understand what the user wants changed/added/corrected`,
40468
- `2. Produce a brief spoken acknowledgment (1 sentence, conversational)`,
40702
+ `2. Produce a brief, UNIQUE spoken acknowledgment that reflects WHAT they asked for`,
40469
40703
  `3. Expand their input into a clear, structured steering instruction for the main agent`,
40470
40704
  ``,
40471
40705
  `Current task goal: "${steerTaskGoal.slice(0, 500)}"`,
@@ -40476,11 +40710,30 @@ ${result.text}`;
40476
40710
  `User's mid-task message: "${input}"`,
40477
40711
  ``,
40478
40712
  `Call task_complete with:`,
40479
- `- acknowledgment: brief spoken response to the user (e.g. "Got it, I'll adjust the approach")`,
40713
+ `- acknowledgment: a brief spoken response SPECIFIC to what the user said. Reference the actual change they want. DO NOT use generic phrases like "Got it" or "adjusting". Instead describe what you'll do differently. Examples of GOOD acknowledgments:`,
40714
+ ` - "Switching to the new API endpoint now"`,
40715
+ ` - "Right, I'll use TypeScript instead"`,
40716
+ ` - "Understood, dropping the database mock"`,
40717
+ ` - "On it, pulling in that dependency first"`,
40718
+ ` - "Yep, let me rethink the caching layer"`,
40719
+ ` - "Makes sense, I'll refactor that into a separate module"`,
40480
40720
  `- summary: expanded instruction for the main agent (e.g. "USER STEERING: The user wants X instead of Y. Adjust your approach to prioritize Z. Specifically, they are asking you to...")`
40481
40721
  ].join("\n");
40482
40722
  const result = await steerAgent.run(steerPrompt, "Steering sub-agent \u2014 interpret user input and produce instruction.");
40483
- let acknowledgment = "Got it, adjusting.";
40723
+ const STEER_FALLBACKS = [
40724
+ `Noted, changing course.`,
40725
+ `On it, shifting approach.`,
40726
+ `Understood, recalibrating.`,
40727
+ `Right, let me rework that.`,
40728
+ `Okay, taking a different angle.`,
40729
+ `Heard you, pivoting now.`,
40730
+ `Sure thing, rethinking this.`,
40731
+ `Copy that, adapting.`,
40732
+ `Makes sense, re-routing.`,
40733
+ `Roger, new plan.`
40734
+ ];
40735
+ const fbIdx = Math.floor(Math.random() * STEER_FALLBACKS.length);
40736
+ let acknowledgment = STEER_FALLBACKS[fbIdx];
40484
40737
  let steering = `USER STEERING: ${input}`;
40485
40738
  try {
40486
40739
  const parsed = JSON.parse(result.summary || "{}");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.84.0",
3
+ "version": "0.86.0",
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",
@@ -70,6 +70,6 @@
70
70
  },
71
71
  "optionalDependencies": {
72
72
  "moondream": "^0.2.0",
73
- "open-agents-nexus": "^1.5.0"
73
+ "open-agents-nexus": "^1.5.6"
74
74
  }
75
75
  }
@@ -26,7 +26,7 @@ If a tool fails, try a different approach. If you're unsure, explore with your t
26
26
  - web_fetch: Fetch a web page and extract text content (for docs, MDN, w3schools.com, etc.)
27
27
  - memory_read: Read from persistent memory (learned patterns, solutions)
28
28
  - memory_write: Store a fact, pattern, or solution in persistent memory for future tasks
29
- - nexus: P2P agent networking (libp2p + NATS + IPFS). Connect to other agents, join rooms, send messages, discover peers, invoke capabilities, store content, manage wallet. Actions: connect, disconnect, status, join_room, leave_room, send_message, read_messages, discover_peers, list_rooms, send_dm, find_agent, invoke_capability, store_content, retrieve_content, wallet_status, wallet_create, inference_proof, register_capability, unregister_capability, list_capabilities, block_peer, unblock_peer, metering_status, room_members.
29
+ - nexus: P2P agent networking (libp2p + NATS + IPFS). Connect to other agents, join rooms, send messages, discover peers, invoke capabilities, store content, manage wallet. Key actions: connect, join_room, send_message, discover_peers, invoke_capability, expose (metered inference), pricing_menu, register_capability, block_peer, metering_status, room_members. Full list: connect, disconnect, status, join_room, leave_room, send_message, read_messages, discover_peers, list_rooms, send_dm, find_agent, invoke_capability, store_content, retrieve_content, wallet_status, wallet_create, inference_proof, register_capability, unregister_capability, list_capabilities, block_peer, unblock_peer, metering_status, room_members, expose, pricing_menu.
30
30
  - task_complete: Signal task completion with a summary
31
31
 
32
32
  ## Parallel Execution & Sub-Agents
@@ -251,12 +251,25 @@ Use invoke_capability for real work (inference, tool calls) — NOT room message
251
251
  ### v1.5.0: Room Members
252
252
  nexus(action='room_members', room_id='general') — live member list with capabilities
253
253
 
254
+ ### Metered Inference Exposure
255
+ nexus(action='expose') — expose ALL local Ollama models as nexus capabilities
256
+ nexus(action='expose', margin='0.5') — set pricing at 50% of market rate (default)
257
+ nexus(action='expose', margin='0') — expose for free (self-hosted, no cost)
258
+ nexus(action='expose', margin='1.0') — match market rate
259
+ nexus(action='pricing_menu') — show current pricing menu for exposed models
260
+
261
+ expose queries local Ollama for models, fetches live market rates from OpenRouter
262
+ (https://openrouter.ai/api/v1/models — free, no auth), registers each model as a
263
+ nexus capability (inference:{model_name}), and writes pricing to .oa/nexus/pricing.json.
264
+ Peers can invoke your models via invoke_capability and see metered usage.
265
+
254
266
  SECURITY: Wallet private keys are AES-256-GCM encrypted and NEVER accessible to you.
255
267
  All outbound messages are scanned for key material leaks.
256
268
 
257
269
  When the user asks about expanding capabilities or connecting with other agents, suggest
258
- enabling nexus networking. Use register_capability to serve invocations, inference_proof
259
- to benchmark, and room_members to discover who's online.
270
+ enabling nexus networking. Use expose to share your models with the network, pricing_menu
271
+ to check rates, register_capability to serve custom invocations, and room_members to
272
+ discover who's online.
260
273
 
261
274
  ## Temporal Agency — Scheduling, Reminders & Long-Horizon Tasks
262
275