open-agents-ai 0.103.54 → 0.103.56
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 +194 -48
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12962,23 +12962,16 @@ async function handleCmd(cmd) {
|
|
|
12962
12962
|
},
|
|
12963
12963
|
});
|
|
12964
12964
|
|
|
12965
|
-
//
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
outputBytes: (output || '').length,
|
|
12976
|
-
tokens: inputTokens + outputTokens,
|
|
12977
|
-
inputTokens: inputTokens,
|
|
12978
|
-
outputTokens: outputTokens,
|
|
12979
|
-
durationMs: Date.now() - logEntry.ts,
|
|
12980
|
-
}) + '\\n');
|
|
12981
|
-
} catch (mErr) { dlog('metering write error: ' + (mErr.message || mErr)); }
|
|
12965
|
+
// Stash token data for the metering hook (which fires after handler returns
|
|
12966
|
+
// and has the correct peerId from connection context)
|
|
12967
|
+
_tokensByRequest[request.requestId] = {
|
|
12968
|
+
model: model.name,
|
|
12969
|
+
inputBytes: prompt.length,
|
|
12970
|
+
outputBytes: (output || '').length,
|
|
12971
|
+
tokens: inputTokens + outputTokens,
|
|
12972
|
+
inputTokens: inputTokens,
|
|
12973
|
+
outputTokens: outputTokens,
|
|
12974
|
+
};
|
|
12982
12975
|
} catch (e) {
|
|
12983
12976
|
dlog('expose: inference error: ' + (e.message || e));
|
|
12984
12977
|
await swrite({
|
|
@@ -13205,9 +13198,37 @@ process.on('unhandledRejection', (reason) => {
|
|
|
13205
13198
|
dlog('WARNING: could not patch node.dialProtocol \u2014 node=' + !!_patchNode);
|
|
13206
13199
|
}
|
|
13207
13200
|
|
|
13208
|
-
// v1.5.0: Metering
|
|
13209
|
-
//
|
|
13210
|
-
//
|
|
13201
|
+
// v1.5.0: Metering hook that enriches nexus records with token counts.
|
|
13202
|
+
// The nexus library fires this hook AFTER handler returns, so it has the correct
|
|
13203
|
+
// peerId from connection context. We stash token data per requestId so the hook
|
|
13204
|
+
// can merge them into one complete record written to metering.jsonl.
|
|
13205
|
+
var _tokensByRequest = {};
|
|
13206
|
+
try {
|
|
13207
|
+
if (nexus.metering && typeof nexus.metering.addHook === 'function') {
|
|
13208
|
+
nexus.metering.addHook(function(record) {
|
|
13209
|
+
// Only enrich inference capability records
|
|
13210
|
+
if (!record.capability || !record.capability.startsWith('inference:')) return;
|
|
13211
|
+
var tokenData = _tokensByRequest[record.id];
|
|
13212
|
+
delete _tokensByRequest[record.id]; // consume
|
|
13213
|
+
try {
|
|
13214
|
+
appendFileSync(meteringFile, JSON.stringify({
|
|
13215
|
+
timestamp: record.timestamp || Date.now(),
|
|
13216
|
+
peerId: record.peerId || 'unknown',
|
|
13217
|
+
service: record.service || record.capability,
|
|
13218
|
+
capability: record.capability,
|
|
13219
|
+
model: tokenData ? tokenData.model : (record.capability || '').replace('inference:', ''),
|
|
13220
|
+
direction: record.direction || 'inbound',
|
|
13221
|
+
inputBytes: tokenData ? tokenData.inputBytes : (record.inputBytes || 0),
|
|
13222
|
+
outputBytes: tokenData ? tokenData.outputBytes : (record.outputBytes || 0),
|
|
13223
|
+
tokens: tokenData ? tokenData.tokens : 0,
|
|
13224
|
+
inputTokens: tokenData ? tokenData.inputTokens : 0,
|
|
13225
|
+
outputTokens: tokenData ? tokenData.outputTokens : 0,
|
|
13226
|
+
durationMs: record.durationMs || 0,
|
|
13227
|
+
}) + '\\n');
|
|
13228
|
+
} catch {}
|
|
13229
|
+
});
|
|
13230
|
+
}
|
|
13231
|
+
} catch {}
|
|
13211
13232
|
|
|
13212
13233
|
// Write payment events to ledger.jsonl
|
|
13213
13234
|
try {
|
|
@@ -28895,7 +28916,8 @@ ${this.formatConnectionInfo()}`);
|
|
|
28895
28916
|
user.tokensIn += tokIn;
|
|
28896
28917
|
user.tokensOut += tokOut;
|
|
28897
28918
|
if (record.model || record.capability || record.service) {
|
|
28898
|
-
|
|
28919
|
+
let modelName = record.model || (record.capability || record.service || "").replace("inference:", "") || "unknown";
|
|
28920
|
+
modelName = modelName.replace(/^open-agents-/, "").replace(/_latest$/, "").replace(/_/g, ":");
|
|
28899
28921
|
this._stats.modelUsage.set(modelName, (this._stats.modelUsage.get(modelName) ?? 0) + 1);
|
|
28900
28922
|
let mm = user.models.get(modelName);
|
|
28901
28923
|
if (!mm) {
|
|
@@ -37145,6 +37167,47 @@ function pick(key, variants) {
|
|
|
37145
37167
|
narration.lastVariantIdx[key] = idx;
|
|
37146
37168
|
return variants[idx];
|
|
37147
37169
|
}
|
|
37170
|
+
function extractSubAgentTopic(args) {
|
|
37171
|
+
const raw = String(args["description"] ?? args["prompt"] ?? "");
|
|
37172
|
+
if (!raw || raw.length < 5)
|
|
37173
|
+
return "";
|
|
37174
|
+
let text = raw.replace(/[#*`_~\[\]]/g, "").replace(/^\d+[.)]\s*/gm, "").trim();
|
|
37175
|
+
const sentEnd = text.search(/[.!?]\s/);
|
|
37176
|
+
if (sentEnd > 10 && sentEnd < 80) {
|
|
37177
|
+
text = text.slice(0, sentEnd);
|
|
37178
|
+
} else if (text.length > 80) {
|
|
37179
|
+
const cut = text.lastIndexOf(" ", 80);
|
|
37180
|
+
text = text.slice(0, cut > 20 ? cut : 80);
|
|
37181
|
+
}
|
|
37182
|
+
if (text.length > 0)
|
|
37183
|
+
text = text.charAt(0).toLowerCase() + text.slice(1);
|
|
37184
|
+
return text;
|
|
37185
|
+
}
|
|
37186
|
+
function extractShellEssence(cmd) {
|
|
37187
|
+
if (!cmd || cmd.length < 3)
|
|
37188
|
+
return "";
|
|
37189
|
+
const npmTest = cmd.match(/npm\s+test\b/);
|
|
37190
|
+
if (npmTest)
|
|
37191
|
+
return "running tests";
|
|
37192
|
+
const npmBuild = cmd.match(/npm\s+run\s+(\S+)/);
|
|
37193
|
+
if (npmBuild)
|
|
37194
|
+
return `running ${npmBuild[1]}`;
|
|
37195
|
+
const gitCmd = cmd.match(/git\s+(commit|push|pull|merge|rebase|checkout|diff|status|log|stash|add)\b/);
|
|
37196
|
+
if (gitCmd)
|
|
37197
|
+
return `git ${gitCmd[1]}`;
|
|
37198
|
+
if (/npm\s+publish/.test(cmd))
|
|
37199
|
+
return "publishing to npm";
|
|
37200
|
+
const nodeScript = cmd.match(/(?:node|tsx?)\s+(\S+)/);
|
|
37201
|
+
if (nodeScript)
|
|
37202
|
+
return `running ${nodeScript[1].split("/").pop()}`;
|
|
37203
|
+
const curlUrl = cmd.match(/curl\s+.*?(https?:\/\/\S{10,40})/);
|
|
37204
|
+
if (curlUrl)
|
|
37205
|
+
return `fetching ${curlUrl[1].replace(/https?:\/\//, "").split("/")[0]}`;
|
|
37206
|
+
const pnpm = cmd.match(/pnpm\s+(-r\s+)?(\S+)/);
|
|
37207
|
+
if (pnpm)
|
|
37208
|
+
return `pnpm ${pnpm[1] || ""}${pnpm[2]}`.trim();
|
|
37209
|
+
return "";
|
|
37210
|
+
}
|
|
37148
37211
|
function getShellCategory(cmd) {
|
|
37149
37212
|
if (/npm\s+test|vitest|jest|mocha/.test(cmd))
|
|
37150
37213
|
return "test";
|
|
@@ -37220,24 +37283,35 @@ function emotionColor(emotion, stark = false, autist = false) {
|
|
|
37220
37283
|
return "";
|
|
37221
37284
|
if (Math.random() > (stark ? 0.5 : 0.3))
|
|
37222
37285
|
return "";
|
|
37286
|
+
const lastEmoKey = narration.lastVariantIdx["_emo_quadrant"] ?? -1;
|
|
37223
37287
|
if (emotion.valence > 0.5 && emotion.arousal > 0.6) {
|
|
37288
|
+
narration.lastVariantIdx["_emo_quadrant"] = 1;
|
|
37289
|
+
if (lastEmoKey === 1 && Math.random() < 0.5)
|
|
37290
|
+
return "";
|
|
37224
37291
|
return pick("emo_excited", stark ? [
|
|
37225
37292
|
"Now we're cooking. ",
|
|
37226
37293
|
"This is what I'm talking about. ",
|
|
37227
|
-
"Brilliant.
|
|
37294
|
+
"Brilliant. ",
|
|
37228
37295
|
"Watch this. ",
|
|
37229
37296
|
"We're onto something here. ",
|
|
37230
|
-
"
|
|
37231
|
-
"
|
|
37297
|
+
"Oh this is good. ",
|
|
37298
|
+
"The pieces are falling into place. ",
|
|
37299
|
+
"This is where it gets interesting. ",
|
|
37300
|
+
"I can feel this coming together. "
|
|
37232
37301
|
] : [
|
|
37233
37302
|
"Feeling good about this. ",
|
|
37234
37303
|
"On a roll here. ",
|
|
37235
37304
|
"This is going great. ",
|
|
37236
37305
|
"Momentum building. ",
|
|
37237
|
-
"Loving this. "
|
|
37306
|
+
"Loving this. ",
|
|
37307
|
+
"Making real progress now. ",
|
|
37308
|
+
"Things are clicking. "
|
|
37238
37309
|
]);
|
|
37239
37310
|
}
|
|
37240
37311
|
if (emotion.valence < -0.3 && emotion.arousal > 0.5) {
|
|
37312
|
+
narration.lastVariantIdx["_emo_quadrant"] = 2;
|
|
37313
|
+
if (lastEmoKey === 2 && Math.random() < 0.5)
|
|
37314
|
+
return "";
|
|
37241
37315
|
return pick("emo_stressed", stark ? [
|
|
37242
37316
|
"Fine. The hard way then. ",
|
|
37243
37317
|
"This is getting personal. ",
|
|
@@ -37245,41 +37319,54 @@ function emotionColor(emotion, stark = false, autist = false) {
|
|
|
37245
37319
|
"Come on. Think. ",
|
|
37246
37320
|
"Not today. Not this bug. ",
|
|
37247
37321
|
"I've beaten worse. ",
|
|
37248
|
-
"
|
|
37322
|
+
"There's got to be a way. ",
|
|
37323
|
+
"Okay, new angle. "
|
|
37249
37324
|
] : [
|
|
37250
37325
|
"Pushing through. ",
|
|
37251
37326
|
"Staying focused. ",
|
|
37252
37327
|
"Not giving up. ",
|
|
37253
37328
|
"Determined to crack this. ",
|
|
37254
|
-
"Bear with me. "
|
|
37329
|
+
"Bear with me. ",
|
|
37330
|
+
"Working through this. ",
|
|
37331
|
+
"Let me rethink this. "
|
|
37255
37332
|
]);
|
|
37256
37333
|
}
|
|
37257
37334
|
if (emotion.valence > 0.3 && emotion.arousal < 0.3) {
|
|
37335
|
+
narration.lastVariantIdx["_emo_quadrant"] = 3;
|
|
37336
|
+
if (lastEmoKey === 3 && Math.random() < 0.5)
|
|
37337
|
+
return "";
|
|
37258
37338
|
return pick("emo_calm", stark ? [
|
|
37259
37339
|
"Clean. Just how I like it. ",
|
|
37260
37340
|
"Smooth. ",
|
|
37261
37341
|
"Textbook. ",
|
|
37262
37342
|
"That's the stuff. ",
|
|
37263
|
-
"Effortless. "
|
|
37343
|
+
"Effortless. ",
|
|
37344
|
+
"Exactly as expected. "
|
|
37264
37345
|
] : [
|
|
37265
37346
|
"Nice and steady. ",
|
|
37266
37347
|
"Smooth sailing. ",
|
|
37267
37348
|
"Easy does it. ",
|
|
37268
|
-
"Taking it easy. "
|
|
37349
|
+
"Taking it easy. ",
|
|
37350
|
+
"Going well. "
|
|
37269
37351
|
]);
|
|
37270
37352
|
}
|
|
37271
37353
|
if (emotion.valence < -0.2 && emotion.arousal < 0.3) {
|
|
37354
|
+
narration.lastVariantIdx["_emo_quadrant"] = 4;
|
|
37355
|
+
if (lastEmoKey === 4 && Math.random() < 0.5)
|
|
37356
|
+
return "";
|
|
37272
37357
|
return pick("emo_subdued", stark ? [
|
|
37273
37358
|
"Careful now. ",
|
|
37274
37359
|
"One wrong move and this unravels. ",
|
|
37275
37360
|
"Measured steps. ",
|
|
37276
37361
|
"Trust the process. ",
|
|
37277
|
-
"Precision matters here. "
|
|
37362
|
+
"Precision matters here. ",
|
|
37363
|
+
"Methodical. "
|
|
37278
37364
|
] : [
|
|
37279
37365
|
"Being careful here. ",
|
|
37280
37366
|
"Treading carefully. ",
|
|
37281
37367
|
"Let me be precise. ",
|
|
37282
|
-
"Taking extra care. "
|
|
37368
|
+
"Taking extra care. ",
|
|
37369
|
+
"One step at a time. "
|
|
37283
37370
|
]);
|
|
37284
37371
|
}
|
|
37285
37372
|
return "";
|
|
@@ -37312,9 +37399,16 @@ function describeToolCall(toolName, args, personality = 2, emotion, stark = fals
|
|
|
37312
37399
|
base = pick(poolKey, (FILE_EDIT_VARIANTS[tier] ?? FILE_EDIT_VARIANTS.terse)(file, args));
|
|
37313
37400
|
break;
|
|
37314
37401
|
case "shell": {
|
|
37315
|
-
const
|
|
37316
|
-
const
|
|
37317
|
-
|
|
37402
|
+
const cmd = String(args["command"] ?? "");
|
|
37403
|
+
const cat = getShellCategory(cmd);
|
|
37404
|
+
const essence = extractShellEssence(cmd);
|
|
37405
|
+
if (essence && tier !== "terse") {
|
|
37406
|
+
const essenceVariants = tier === "chatty" ? [`Alright, ${essence}`, `Let me handle ${essence}`, `Time to ${essence.startsWith("running") ? essence : "run " + essence}`] : [`Now ${essence}`, essence.charAt(0).toUpperCase() + essence.slice(1)];
|
|
37407
|
+
base = pick(`shell_ess_${tier}`, essenceVariants);
|
|
37408
|
+
} else {
|
|
37409
|
+
const pool = SHELL_VARIANTS[cat] ?? SHELL_VARIANTS.generic;
|
|
37410
|
+
base = pick(`shell_${cat}_${tier}`, pool[tier] ?? pool.terse);
|
|
37411
|
+
}
|
|
37318
37412
|
break;
|
|
37319
37413
|
}
|
|
37320
37414
|
case "grep_search":
|
|
@@ -37514,9 +37608,47 @@ function extractResultDigest(toolName, content) {
|
|
|
37514
37608
|
const exitMatch = text.match(/exit\s*(?:code)?[:\s]*(\d+)/i);
|
|
37515
37609
|
if (exitMatch && exitMatch[1] !== "0")
|
|
37516
37610
|
nuggets.push(`exit code ${exitMatch[1]}`);
|
|
37611
|
+
if (nuggets.length === 0) {
|
|
37612
|
+
const contentDigest = extractContentSummary(text, toolName);
|
|
37613
|
+
if (contentDigest)
|
|
37614
|
+
nuggets.push(contentDigest);
|
|
37615
|
+
}
|
|
37517
37616
|
const digest = nuggets.slice(0, 3).join(", ");
|
|
37518
37617
|
return digest.length > 100 ? digest.slice(0, 97) + "..." : digest;
|
|
37519
37618
|
}
|
|
37619
|
+
function extractContentSummary(text, toolName) {
|
|
37620
|
+
let cleaned = text.replace(/^\s*\d+[│|:→]\s*/gm, "").replace(/\d{4}-\d{2}-\d{2}T[\d:.]+Z?\s*/g, "").replace(/^[\s*#=-]+$/gm, "").replace(/```[\s\S]*?```/g, "").replace(/^\s*(import|export|const|let|var|function|class|interface|type)\s/gm, "").replace(/[{}()\[\];]/g, "").trim();
|
|
37621
|
+
const lines = cleaned.split("\n").map((l) => l.trim()).filter((l) => l.length > 10);
|
|
37622
|
+
for (const line of lines) {
|
|
37623
|
+
const wordChars = line.replace(/[^a-zA-Z\s]/g, "").trim();
|
|
37624
|
+
if (wordChars.length < line.length * 0.4)
|
|
37625
|
+
continue;
|
|
37626
|
+
if (/^(\/|\.\/)/.test(line))
|
|
37627
|
+
continue;
|
|
37628
|
+
if (/^\s*[{<]/.test(line))
|
|
37629
|
+
continue;
|
|
37630
|
+
let summary = line;
|
|
37631
|
+
if (summary.length > 80) {
|
|
37632
|
+
const cut = summary.lastIndexOf(" ", 80);
|
|
37633
|
+
summary = summary.slice(0, cut > 20 ? cut : 80);
|
|
37634
|
+
}
|
|
37635
|
+
if (summary.length > 0) {
|
|
37636
|
+
summary = summary.charAt(0).toLowerCase() + summary.slice(1);
|
|
37637
|
+
summary = summary.replace(/[.,;:!?]+$/, "");
|
|
37638
|
+
}
|
|
37639
|
+
return summary;
|
|
37640
|
+
}
|
|
37641
|
+
if (toolName === "sub_agent") {
|
|
37642
|
+
const summaryMatch = text.match(/(?:summary|created|completed|result)[:\s]+(.{10,80}?)(?:\n|$)/i);
|
|
37643
|
+
if (summaryMatch) {
|
|
37644
|
+
let s = summaryMatch[1].trim();
|
|
37645
|
+
if (s.length > 0)
|
|
37646
|
+
s = s.charAt(0).toLowerCase() + s.slice(1);
|
|
37647
|
+
return s;
|
|
37648
|
+
}
|
|
37649
|
+
}
|
|
37650
|
+
return "";
|
|
37651
|
+
}
|
|
37520
37652
|
function describeToolResult(toolName, success, personality = 2, resultContent, emotion, stark = false) {
|
|
37521
37653
|
if (toolName === "task_complete")
|
|
37522
37654
|
return "";
|
|
@@ -37532,6 +37664,16 @@ function describeToolResult(toolName, success, personality = 2, resultContent, e
|
|
|
37532
37664
|
return "";
|
|
37533
37665
|
const base = pick(`result_ok_${tier}`, RESULT_SUCCESS_VARIANTS[tier] ?? RESULT_SUCCESS_VARIANTS.terse);
|
|
37534
37666
|
if (digest && personality >= 2) {
|
|
37667
|
+
if (tier === "chatty" && digest.length > 15) {
|
|
37668
|
+
if (Math.random() < 0.5) {
|
|
37669
|
+
const contentLeads = [
|
|
37670
|
+
`${emo}Interesting, ${digest}`,
|
|
37671
|
+
`${emo}So we've got ${digest}`,
|
|
37672
|
+
`${emo}${digest.charAt(0).toUpperCase() + digest.slice(1)}, that's useful`
|
|
37673
|
+
];
|
|
37674
|
+
return pick("digest_lead", contentLeads);
|
|
37675
|
+
}
|
|
37676
|
+
}
|
|
37535
37677
|
const connectors = tier === "chatty" ? [` \u2014 ${digest}`, `, I see ${digest}`, `. Looks like ${digest}`, `, showing ${digest}`] : tier === "conv" ? [` \u2014 ${digest}`, `, ${digest}`, `. Shows ${digest}`] : [`: ${digest}`];
|
|
37536
37678
|
return emo + base + pick("digest_conn_ok", connectors);
|
|
37537
37679
|
}
|
|
@@ -38657,18 +38799,18 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
38657
38799
|
]
|
|
38658
38800
|
};
|
|
38659
38801
|
SUB_AGENT_VARIANTS = {
|
|
38660
|
-
terse: () =>
|
|
38661
|
-
|
|
38662
|
-
`
|
|
38663
|
-
|
|
38664
|
-
|
|
38665
|
-
|
|
38666
|
-
|
|
38667
|
-
|
|
38668
|
-
|
|
38669
|
-
|
|
38670
|
-
`This needs a
|
|
38671
|
-
|
|
38802
|
+
terse: (_f, a) => {
|
|
38803
|
+
const topic = extractSubAgentTopic(a);
|
|
38804
|
+
return topic ? [`Sub agent: ${topic}`, `Delegating: ${topic}`] : [`Delegating to sub agent`, `Spawning sub agent`];
|
|
38805
|
+
},
|
|
38806
|
+
conv: (_f, a) => {
|
|
38807
|
+
const topic = extractSubAgentTopic(a);
|
|
38808
|
+
return topic ? [`Handing off to a sub agent to ${topic}`, `Bringing in help to ${topic}`, `Delegating the ${topic} work`] : [`Handing this off to a sub agent`, `Bringing in some help for this`, `Delegating this part of the work`];
|
|
38809
|
+
},
|
|
38810
|
+
chatty: (_f, a) => {
|
|
38811
|
+
const topic = extractSubAgentTopic(a);
|
|
38812
|
+
return topic ? [`Spinning up a sub agent to ${topic}`, `This part needs a specialist, delegating to ${topic}`, `Calling in backup to handle ${topic}`] : [`Bringing in reinforcements for this one`, `Let me hand this off to a specialist`, `Calling in backup on this`];
|
|
38813
|
+
}
|
|
38672
38814
|
};
|
|
38673
38815
|
SHELL_VARIANTS = {
|
|
38674
38816
|
test: {
|
|
@@ -48255,8 +48397,12 @@ ${result.text}`;
|
|
|
48255
48397
|
if (parsed.summary)
|
|
48256
48398
|
steering = parsed.summary;
|
|
48257
48399
|
} catch {
|
|
48258
|
-
|
|
48259
|
-
|
|
48400
|
+
const raw = result.summary || "";
|
|
48401
|
+
const ackMatch = raw.match(/acknowledgment[:\s]*["']?([^"'\n]{5,80})["']?/i) || raw.match(/^([^.!?\n]{5,80}[.!?])/);
|
|
48402
|
+
if (ackMatch)
|
|
48403
|
+
acknowledgment = ackMatch[1].trim();
|
|
48404
|
+
if (raw.length > 10)
|
|
48405
|
+
steering = `USER STEERING: ${raw}`;
|
|
48260
48406
|
}
|
|
48261
48407
|
if (voiceEngine?.enabled) {
|
|
48262
48408
|
writeContent(() => renderVoiceText(acknowledgment));
|
package/package.json
CHANGED