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 +266 -13
- package/package.json +2 -2
- package/prompts/agentic/system-large.md +16 -3
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
|
-
|
|
11378
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
259
|
-
to
|
|
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
|
|