@ouro.bot/cli 0.1.0-alpha.759 → 0.1.0-alpha.760
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/changelog.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.760",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Force exact repeated requests to retry unresolved matching effects and fail closed instead of accepting stale text."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
4
10
|
{
|
|
5
11
|
"version": "0.1.0-alpha.759",
|
|
6
12
|
"changes": [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0"?>
|
|
2
2
|
<Container version="2">
|
|
3
3
|
<Name>Mendelow Cloud Butler</Name>
|
|
4
|
-
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.
|
|
4
|
+
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.760</Repository>
|
|
5
5
|
<Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
|
|
6
6
|
<Network>host</Network>
|
|
7
7
|
<Shell>sh</Shell>
|
package/dist/heart/core.js
CHANGED
|
@@ -558,14 +558,35 @@ function isHarnessCorrectiveUserText(text) {
|
|
|
558
558
|
|| text.startsWith("private-return acknowledgement claimed work was queued, but no ponder packet was created this turn.")
|
|
559
559
|
|| text.startsWith("this exact request previously reached a tool that is advertised again now.");
|
|
560
560
|
}
|
|
561
|
-
function
|
|
562
|
-
const normalized =
|
|
563
|
-
return
|
|
564
|
-
||
|
|
565
|
-
||
|
|
566
|
-
||
|
|
561
|
+
function toolResultIndicatesFailure(content) {
|
|
562
|
+
const normalized = content.trim().toLowerCase();
|
|
563
|
+
return normalized.startsWith("error:")
|
|
564
|
+
|| normalized.startsWith("invalid tool arguments:")
|
|
565
|
+
|| normalized.startsWith("unknown:")
|
|
566
|
+
|| normalized.startsWith("relationship authorization required:")
|
|
567
|
+
|| normalized.startsWith("orientation hold:")
|
|
568
|
+
|| normalized.startsWith("commerce authority required:")
|
|
569
|
+
|| normalized.startsWith("blocked:")
|
|
570
|
+
|| normalized.startsWith("rejected:");
|
|
567
571
|
}
|
|
568
|
-
function
|
|
572
|
+
function effectFingerprint(name, rawArguments) {
|
|
573
|
+
let args;
|
|
574
|
+
try {
|
|
575
|
+
const parsed = JSON.parse(rawArguments);
|
|
576
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
577
|
+
return null;
|
|
578
|
+
args = { ...parsed };
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
const profile = (0, tools_1.riskProfileForToolName)(name, args);
|
|
584
|
+
if (!profile || profile.mutates === "none")
|
|
585
|
+
return null;
|
|
586
|
+
delete args.expectedVersion;
|
|
587
|
+
return (0, tool_arguments_1.digestJson)({ name, args });
|
|
588
|
+
}
|
|
589
|
+
function historicalFailedEffectsForExactRepeatedRequest(messages) {
|
|
569
590
|
const userRequests = [];
|
|
570
591
|
for (let index = 0; index < messages.length; index += 1) {
|
|
571
592
|
const message = messages[index];
|
|
@@ -582,32 +603,34 @@ function historicalFailedToolsForExactRepeatedRequest(messages) {
|
|
|
582
603
|
const prior = userRequests.slice(0, -1).find((request) => request.text === current.text);
|
|
583
604
|
if (!prior)
|
|
584
605
|
return [];
|
|
585
|
-
const
|
|
586
|
-
const
|
|
606
|
+
const effectsByCallId = new Map();
|
|
607
|
+
const failedEffects = new Map();
|
|
587
608
|
for (let index = prior.index + 1; index < current.index; index += 1) {
|
|
588
609
|
const message = messages[index];
|
|
589
610
|
if (message?.role === "assistant" && Array.isArray(message.tool_calls)) {
|
|
590
611
|
for (const call of message.tool_calls) {
|
|
591
|
-
if (call.type
|
|
592
|
-
|
|
593
|
-
|
|
612
|
+
if (call.type !== "function" || !call.id || !call.function.name)
|
|
613
|
+
continue;
|
|
614
|
+
const fingerprint = effectFingerprint(call.function.name, call.function.arguments);
|
|
615
|
+
if (fingerprint)
|
|
616
|
+
effectsByCallId.set(call.id, { name: call.function.name, fingerprint });
|
|
594
617
|
}
|
|
595
618
|
continue;
|
|
596
619
|
}
|
|
597
620
|
if (message?.role !== "tool")
|
|
598
621
|
continue;
|
|
599
|
-
const content = messageContentText(message.content)
|
|
600
|
-
const
|
|
601
|
-
if (!
|
|
622
|
+
const content = messageContentText(message.content);
|
|
623
|
+
const effect = effectsByCallId.get(message.tool_call_id);
|
|
624
|
+
if (!effect)
|
|
602
625
|
continue;
|
|
603
|
-
if (
|
|
604
|
-
|
|
626
|
+
if (toolResultIndicatesFailure(content)) {
|
|
627
|
+
failedEffects.set(effect.fingerprint, effect);
|
|
605
628
|
}
|
|
606
629
|
else {
|
|
607
|
-
|
|
630
|
+
failedEffects.delete(effect.fingerprint);
|
|
608
631
|
}
|
|
609
632
|
}
|
|
610
|
-
return [...
|
|
633
|
+
return [...failedEffects.values()];
|
|
611
634
|
}
|
|
612
635
|
function latestUserMessageText(messages) {
|
|
613
636
|
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
@@ -1094,8 +1117,8 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1094
1117
|
// a ponder packet created the return obligation in this turn.
|
|
1095
1118
|
let noToolCallRetries = 0;
|
|
1096
1119
|
const NO_TOOL_CALL_MAX_RETRIES = 2;
|
|
1097
|
-
|
|
1098
|
-
let
|
|
1120
|
+
let unresolvedHistoricalEffects = historicalFailedEffectsForExactRepeatedRequest(messages);
|
|
1121
|
+
let historicalToolFailureRetries = 0;
|
|
1099
1122
|
let providerIterations = 0;
|
|
1100
1123
|
const toolLoopState = (0, tool_loop_1.createToolLoopState)();
|
|
1101
1124
|
const toolFrictionLedger = (0, tool_friction_1.createToolFrictionLedger)();
|
|
@@ -1200,7 +1223,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1200
1223
|
const ordinaryActiveTools = relationshipToolNames
|
|
1201
1224
|
? unscopedOrdinaryActiveTools.filter((tool) => relationshipToolNames.includes(tool.function.name))
|
|
1202
1225
|
: unscopedOrdinaryActiveTools;
|
|
1203
|
-
const
|
|
1226
|
+
const candidateActiveTools = options?.toolProfile === "sanctuary-health-private"
|
|
1204
1227
|
? (() => {
|
|
1205
1228
|
const sendTools = baseTools.filter((tool) => tool.function.name === "send_message");
|
|
1206
1229
|
if (channel !== "inner" || sendTools.length !== 1 || baseTools.length !== 1) {
|
|
@@ -1209,6 +1232,14 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1209
1232
|
return [sendTools[0], tools_1.restTool];
|
|
1210
1233
|
})()
|
|
1211
1234
|
: ordinaryActiveTools;
|
|
1235
|
+
const candidateToolNames = new Set(candidateActiveTools.map((tool) => tool.function.name));
|
|
1236
|
+
const forcedHistoricalToolNames = new Set(unresolvedHistoricalEffects
|
|
1237
|
+
.map((effect) => effect.name)
|
|
1238
|
+
.filter((name) => candidateToolNames.has(name)));
|
|
1239
|
+
const forcingHistoricalEffect = forcedHistoricalToolNames.size > 0;
|
|
1240
|
+
const activeTools = forcingHistoricalEffect
|
|
1241
|
+
? candidateActiveTools.filter((tool) => forcedHistoricalToolNames.has(tool.function.name))
|
|
1242
|
+
: candidateActiveTools;
|
|
1212
1243
|
const activeToolNames = new Set(activeTools.map((tool) => tool.function.name));
|
|
1213
1244
|
const steeringFollowUps = options?.drainSteeringFollowUps?.() ?? [];
|
|
1214
1245
|
if (steeringFollowUps.length > 0) {
|
|
@@ -1262,7 +1293,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1262
1293
|
callbacks: turnCallbackBufferRef.current?.callbacks ?? callbacks,
|
|
1263
1294
|
signal,
|
|
1264
1295
|
traceId,
|
|
1265
|
-
toolChoiceRequired,
|
|
1296
|
+
toolChoiceRequired: forcingHistoricalEffect || toolChoiceRequired,
|
|
1266
1297
|
reasoningEffort: currentReasoningEffort,
|
|
1267
1298
|
eagerSettleStreaming: true,
|
|
1268
1299
|
systemPrompt: structuredSystemPrompt,
|
|
@@ -1445,29 +1476,45 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1445
1476
|
})
|
|
1446
1477
|
: null;
|
|
1447
1478
|
if (!result.toolCalls.length) {
|
|
1448
|
-
|
|
1449
|
-
? []
|
|
1450
|
-
: historicalFailedToolNames.filter((name) => activeToolNames.has(name));
|
|
1451
|
-
const responseClaimsCurrentToolFailure = claimsCurrentToolFailure(messageContentText(msg.content));
|
|
1452
|
-
if (retryableHistoricalTools.length > 0 && responseClaimsCurrentToolFailure) {
|
|
1453
|
-
historicalToolFailureRetryIssued = true;
|
|
1479
|
+
if (forcingHistoricalEffect) {
|
|
1454
1480
|
streamCallbackBuffer?.discard();
|
|
1455
1481
|
callbacks.onClearText?.();
|
|
1482
|
+
const retryableHistoricalTools = [...forcedHistoricalToolNames];
|
|
1483
|
+
if (historicalToolFailureRetries >= NO_TOOL_CALL_MAX_RETRIES) {
|
|
1484
|
+
const blockedAnswer = `I could not complete the unresolved ${retryableHistoricalTools.join(", ")} effect because the provider repeatedly returned no tool call. I did not treat the requested change as complete.`;
|
|
1485
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1486
|
+
level: "warn",
|
|
1487
|
+
component: "engine",
|
|
1488
|
+
event: "engine.historical_tool_failure_retry",
|
|
1489
|
+
message: "unresolved historical effect exhausted deterministic tool-call retries; failing closed",
|
|
1490
|
+
meta: { provider: providerRuntime.id, model: providerRuntime.model, toolNames: retryableHistoricalTools, cap: NO_TOOL_CALL_MAX_RETRIES },
|
|
1491
|
+
});
|
|
1492
|
+
msg.content = blockedAnswer;
|
|
1493
|
+
pushGenerated(msg);
|
|
1494
|
+
callbacks.onTextChunk(blockedAnswer);
|
|
1495
|
+
completion = { answer: blockedAnswer, intent: "blocked" };
|
|
1496
|
+
outcome = "blocked";
|
|
1497
|
+
done = true;
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
historicalToolFailureRetries += 1;
|
|
1456
1501
|
(0, runtime_1.emitNervesEvent)({
|
|
1457
1502
|
level: "warn",
|
|
1458
1503
|
component: "engine",
|
|
1459
1504
|
event: "engine.historical_tool_failure_retry",
|
|
1460
|
-
message: "
|
|
1505
|
+
message: "unresolved historical effect returned no tool call; forcing another exact-effect attempt",
|
|
1461
1506
|
meta: {
|
|
1462
1507
|
provider: providerRuntime.id,
|
|
1463
1508
|
model: providerRuntime.model,
|
|
1464
1509
|
toolNames: retryableHistoricalTools,
|
|
1510
|
+
attempt: historicalToolFailureRetries,
|
|
1511
|
+
cap: NO_TOOL_CALL_MAX_RETRIES,
|
|
1465
1512
|
},
|
|
1466
1513
|
});
|
|
1467
1514
|
pushGenerated(msg);
|
|
1468
1515
|
messages.push({
|
|
1469
1516
|
role: "user",
|
|
1470
|
-
content: `this exact request previously reached a tool that is advertised again now.
|
|
1517
|
+
content: `this exact request previously reached a tool that is advertised again now. Its unresolved historical effect is through ${retryableHistoricalTools.join(", ")}. Only that tool is available now. Read current state if needed, then retry the exact failed effect. Do not report completion until the effect succeeds.`,
|
|
1471
1518
|
});
|
|
1472
1519
|
continue;
|
|
1473
1520
|
}
|
|
@@ -1939,6 +1986,17 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1939
1986
|
continue;
|
|
1940
1987
|
}
|
|
1941
1988
|
const args = validatedCallArguments.get(tc);
|
|
1989
|
+
const currentEffectFingerprint = effectFingerprint(tc.name, tc.arguments);
|
|
1990
|
+
if (forcingHistoricalEffect
|
|
1991
|
+
&& currentEffectFingerprint
|
|
1992
|
+
&& !unresolvedHistoricalEffects.some((effect) => effect.fingerprint === currentEffectFingerprint)) {
|
|
1993
|
+
const rejection = "rejected: this turn is retrying an unresolved historical effect, and these mutation arguments do not match it. Read current state or retry the exact failed effect.";
|
|
1994
|
+
callbacks.onToolStart(tc.name, args);
|
|
1995
|
+
callbacks.onToolEnd(tc.name, (0, tools_1.summarizeArgs)(tc.name, args), false);
|
|
1996
|
+
pushGenerated({ role: "tool", tool_call_id: tc.id, content: rejection });
|
|
1997
|
+
providerRuntime.appendToolOutput(tc.id, rejection);
|
|
1998
|
+
continue;
|
|
1999
|
+
}
|
|
1942
2000
|
if (tc.name === "send_message" && args.friendId === "self") {
|
|
1943
2001
|
const latestUserText = latestUserMessageText(messages);
|
|
1944
2002
|
if (!isPrivateRuntimeChannel && looksLikePrivateReturnRequest(latestUserText)) {
|
|
@@ -2196,6 +2254,9 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
2196
2254
|
success = false;
|
|
2197
2255
|
augmentedToolContext?.habitSession?.recordError?.(toolResult);
|
|
2198
2256
|
}
|
|
2257
|
+
if (success && currentEffectFingerprint && !toolResultIndicatesFailure(toolResult)) {
|
|
2258
|
+
unresolvedHistoricalEffects = unresolvedHistoricalEffects.filter((effect) => effect.fingerprint !== currentEffectFingerprint);
|
|
2259
|
+
}
|
|
2199
2260
|
const resolvedRiskProfile = (0, tools_1.resolveToolDefinition)(tc.name)?.riskProfile;
|
|
2200
2261
|
const toolRiskProfile = typeof resolvedRiskProfile === "function" ? resolvedRiskProfile(args) : resolvedRiskProfile;
|
|
2201
2262
|
options?.toolBoundaryObserver?.({
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.760",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@ouro.bot/cli",
|
|
9
|
-
"version": "0.1.0-alpha.
|
|
9
|
+
"version": "0.1.0-alpha.760",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
12
12
|
"@azure/identity": "^4.13.0",
|