metheus-governance-mcp-cli 0.2.206 → 0.2.208
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/cli.mjs +49 -11
- package/lib/local-ai-adapters.mjs +125 -1
- package/lib/runner-delivery.mjs +8 -1
- package/lib/runner-orchestration.mjs +284 -33
- package/lib/selftest-runner-scenarios.mjs +149 -7
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
analyzeHumanConversationIntentWithAI,
|
|
17
17
|
auditRoleExecutionPlanWithAI,
|
|
18
18
|
auditDirectHumanReplyWithAI,
|
|
19
|
+
explainExecutionFailureWithAI,
|
|
19
20
|
normalizeExecutionArtifacts,
|
|
20
21
|
planRoleExecutionWithAI,
|
|
21
22
|
repairRoleExecutionPlanWithAI,
|
|
@@ -2622,7 +2623,7 @@ function inferRunnerRequestClaimIntent(selectedRecord) {
|
|
|
2622
2623
|
if (looksLikeRunnerClaimQuestion(rawText) && normalizedText.split(/\s+/).filter(Boolean).length <= 8) {
|
|
2623
2624
|
return "status_query";
|
|
2624
2625
|
}
|
|
2625
|
-
return "
|
|
2626
|
+
return "";
|
|
2626
2627
|
}
|
|
2627
2628
|
|
|
2628
2629
|
function resolveRunnerRequestClaimIntent({
|
|
@@ -2646,6 +2647,16 @@ function buildSyntheticReplyChainConversationID(normalizedRoute, chatID, anchorM
|
|
|
2646
2647
|
return `reply_chain:${provider}:${normalizedChatID}:${normalizedAnchorMessageID}`;
|
|
2647
2648
|
}
|
|
2648
2649
|
|
|
2650
|
+
function buildSyntheticHumanOpeningConversationID(normalizedRoute, chatID, messageID) {
|
|
2651
|
+
const provider = String(normalizedRoute?.provider || "").trim() || "unknown";
|
|
2652
|
+
const normalizedChatID = String(chatID || "").trim() || "-";
|
|
2653
|
+
const normalizedMessageID = intFromRawAllowZero(messageID, 0);
|
|
2654
|
+
if (normalizedMessageID <= 0) {
|
|
2655
|
+
return "";
|
|
2656
|
+
}
|
|
2657
|
+
return `human_opening:${provider}:${normalizedChatID}:${normalizedMessageID}`;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2649
2660
|
function findRunnerRequestsForScope(state, normalizedRoute, selectors = {}) {
|
|
2650
2661
|
const requests = normalizeBotRunnerRequests(state?.requests);
|
|
2651
2662
|
const projectID = String(normalizedRoute?.projectID || "").trim();
|
|
@@ -3258,13 +3269,17 @@ async function claimRunnerRequestForHumanComment({
|
|
|
3258
3269
|
normalizedIntent,
|
|
3259
3270
|
selectedRecord,
|
|
3260
3271
|
});
|
|
3261
|
-
|
|
3272
|
+
let stateForClaim = safeObject(replyChainResolution.state);
|
|
3273
|
+
const normalizedSharedHumanIntent = safeObject(sharedHumanIntent);
|
|
3274
|
+
const provisionalNormalizedIntent = String(
|
|
3275
|
+
normalizedSharedHumanIntent.intentType || resolvedNormalizedIntent || "",
|
|
3276
|
+
).trim().toLowerCase();
|
|
3277
|
+
const provisionalRequestKey = buildRunnerRequestKey({
|
|
3262
3278
|
normalizedRoute,
|
|
3263
3279
|
selectedRecord,
|
|
3264
3280
|
selectedBotUsernames,
|
|
3265
|
-
normalizedIntent:
|
|
3281
|
+
normalizedIntent: provisionalNormalizedIntent,
|
|
3266
3282
|
});
|
|
3267
|
-
let stateForClaim = safeObject(replyChainResolution.state);
|
|
3268
3283
|
const baseConversationID = String(
|
|
3269
3284
|
parsed.conversationID
|
|
3270
3285
|
|| replyChainContext.conversationID
|
|
@@ -3284,9 +3299,6 @@ async function claimRunnerRequestForHumanComment({
|
|
|
3284
3299
|
requests: backfilled.requests,
|
|
3285
3300
|
};
|
|
3286
3301
|
}
|
|
3287
|
-
const requests = normalizeBotRunnerRequests(stateForClaim.requests);
|
|
3288
|
-
const existing = safeObject(requests[requestKey]);
|
|
3289
|
-
const normalizedSharedHumanIntent = safeObject(sharedHumanIntent);
|
|
3290
3302
|
const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
3291
3303
|
let sharedConversationSource = currentMessageID > 0
|
|
3292
3304
|
? pickRunnerSharedConversationSourceRequest(
|
|
@@ -3294,7 +3306,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
3294
3306
|
chatID: String(parsed.chatID || parsed.chatId || "").trim(),
|
|
3295
3307
|
messageID: currentMessageID,
|
|
3296
3308
|
}),
|
|
3297
|
-
|
|
3309
|
+
provisionalRequestKey,
|
|
3298
3310
|
)
|
|
3299
3311
|
: {};
|
|
3300
3312
|
if (
|
|
@@ -3308,14 +3320,38 @@ async function claimRunnerRequestForHumanComment({
|
|
|
3308
3320
|
runtime,
|
|
3309
3321
|
chatID: String(parsed.chatID || parsed.chatId || "").trim(),
|
|
3310
3322
|
messageID: currentMessageID,
|
|
3311
|
-
excludeRequestKey:
|
|
3323
|
+
excludeRequestKey: provisionalRequestKey,
|
|
3312
3324
|
}));
|
|
3313
3325
|
}
|
|
3314
3326
|
const resolvedConversationID = String(
|
|
3315
3327
|
baseConversationID
|
|
3316
3328
|
|| sharedConversationSource.conversation_id
|
|
3329
|
+
|| (
|
|
3330
|
+
Object.keys(normalizedSharedHumanIntent).length > 0
|
|
3331
|
+
? buildSyntheticHumanOpeningConversationID(
|
|
3332
|
+
normalizedRoute,
|
|
3333
|
+
String(parsed.chatID || parsed.chatId || "").trim(),
|
|
3334
|
+
currentMessageID,
|
|
3335
|
+
)
|
|
3336
|
+
: ""
|
|
3337
|
+
)
|
|
3317
3338
|
|| "",
|
|
3318
3339
|
).trim();
|
|
3340
|
+
const preferredNormalizedIntent = String(
|
|
3341
|
+
normalizedSharedHumanIntent.intentType
|
|
3342
|
+
|| sharedConversationSource.normalized_intent
|
|
3343
|
+
|| referencedRequest.normalized_intent
|
|
3344
|
+
|| resolvedNormalizedIntent
|
|
3345
|
+
|| "",
|
|
3346
|
+
).trim().toLowerCase();
|
|
3347
|
+
const requestKey = buildRunnerRequestKey({
|
|
3348
|
+
normalizedRoute,
|
|
3349
|
+
selectedRecord,
|
|
3350
|
+
selectedBotUsernames,
|
|
3351
|
+
normalizedIntent: preferredNormalizedIntent,
|
|
3352
|
+
});
|
|
3353
|
+
const requests = normalizeBotRunnerRequests(stateForClaim.requests);
|
|
3354
|
+
const existing = safeObject(requests[requestKey]);
|
|
3319
3355
|
if (isFinalRunnerRequestStatus(existing.status)) {
|
|
3320
3356
|
return {
|
|
3321
3357
|
ok: false,
|
|
@@ -3411,10 +3447,10 @@ async function claimRunnerRequestForHumanComment({
|
|
|
3411
3447
|
? sharedConversationSource.next_expected_responders
|
|
3412
3448
|
: ensureArray(referencedRequest.next_expected_responders).length
|
|
3413
3449
|
? referencedRequest.next_expected_responders
|
|
3414
|
-
:
|
|
3450
|
+
: [],
|
|
3415
3451
|
normalizeTelegramMentionUsername,
|
|
3416
3452
|
),
|
|
3417
|
-
normalized_intent:
|
|
3453
|
+
normalized_intent: preferredNormalizedIntent,
|
|
3418
3454
|
status: "claimed",
|
|
3419
3455
|
claimed_by_route: String(routeKey || "").trim(),
|
|
3420
3456
|
claimed_at: firstNonEmptyString([existing.claimed_at, nowISO]) || nowISO,
|
|
@@ -8463,6 +8499,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8463
8499
|
saveRunnerRouteState,
|
|
8464
8500
|
startRunnerTypingHeartbeat,
|
|
8465
8501
|
runRunnerAIExecution,
|
|
8502
|
+
explainExecutionFailureWithAI,
|
|
8466
8503
|
performLocalBotDelivery,
|
|
8467
8504
|
serializeRunnerTriggerPolicy,
|
|
8468
8505
|
serializeRunnerArchivePolicy,
|
|
@@ -10833,6 +10870,7 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
10833
10870
|
saveRunnerRouteState,
|
|
10834
10871
|
startRunnerTypingHeartbeat,
|
|
10835
10872
|
runRunnerAIExecution,
|
|
10873
|
+
explainExecutionFailureWithAI,
|
|
10836
10874
|
performLocalBotDelivery,
|
|
10837
10875
|
serializeRunnerTriggerPolicy,
|
|
10838
10876
|
serializeRunnerArchivePolicy,
|
|
@@ -949,7 +949,7 @@ function normalizeExecutionContract(rawContract) {
|
|
|
949
949
|
|| contract.target_summary_bot
|
|
950
950
|
|| "",
|
|
951
951
|
).trim().replace(/^@+/, "").toLowerCase();
|
|
952
|
-
const
|
|
952
|
+
const explicitNextResponders = uniqueOrdered(
|
|
953
953
|
ensureArray(contract.next_responders || contract.nextResponders || contract.responders)
|
|
954
954
|
.map((item) => String(item || "").trim().replace(/^@+/, "").toLowerCase())
|
|
955
955
|
.filter(Boolean),
|
|
@@ -962,6 +962,13 @@ function normalizeExecutionContract(rawContract) {
|
|
|
962
962
|
].includes(type)
|
|
963
963
|
? type
|
|
964
964
|
: "";
|
|
965
|
+
const nextResponders = explicitNextResponders.length > 0
|
|
966
|
+
? explicitNextResponders
|
|
967
|
+
: normalizedType === "delegation"
|
|
968
|
+
? uniqueOrdered(assignments.map((item) => String(item.target_bot || item.targetBot || "").trim().replace(/^@+/, "").toLowerCase()).filter(Boolean))
|
|
969
|
+
: normalizedType === "summary_request" && summaryBot
|
|
970
|
+
? [summaryBot]
|
|
971
|
+
: [];
|
|
965
972
|
const actionable = contract.actionable === true
|
|
966
973
|
|| (normalizedType === "delegation" && assignments.length > 0)
|
|
967
974
|
|| (normalizedType === "summary_request" && Boolean(summaryBot))
|
|
@@ -1269,6 +1276,29 @@ export function resolveResponderAdjudicatorModelDisplayName({
|
|
|
1269
1276
|
).trim();
|
|
1270
1277
|
}
|
|
1271
1278
|
|
|
1279
|
+
export function resolveFailureExplainerModelDisplayName({
|
|
1280
|
+
client = "",
|
|
1281
|
+
model = "",
|
|
1282
|
+
env = process.env,
|
|
1283
|
+
} = {}) {
|
|
1284
|
+
const explainerClient = normalizeLocalAIClientName(
|
|
1285
|
+
String(
|
|
1286
|
+
client
|
|
1287
|
+
|| env?.METHEUS_FAILURE_EXPLAINER_CLIENT
|
|
1288
|
+
|| env?.METHEUS_INTENT_PARSER_CLIENT
|
|
1289
|
+
|| "",
|
|
1290
|
+
).trim(),
|
|
1291
|
+
"gpt",
|
|
1292
|
+
);
|
|
1293
|
+
return String(
|
|
1294
|
+
model
|
|
1295
|
+
|| env?.METHEUS_FAILURE_EXPLAINER_MODEL
|
|
1296
|
+
|| env?.METHEUS_INTENT_PARSER_MODEL
|
|
1297
|
+
|| defaultAdjudicationModelForClient(explainerClient, env)
|
|
1298
|
+
|| "",
|
|
1299
|
+
).trim();
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1272
1302
|
export function resolveLocalAIExecutionModel(clientName, rawModelValue = "") {
|
|
1273
1303
|
const modelValue = String(rawModelValue || "").trim();
|
|
1274
1304
|
if (!modelValue) return "";
|
|
@@ -2492,6 +2522,100 @@ export function analyzeHumanConversationIntentWithAI({
|
|
|
2492
2522
|
};
|
|
2493
2523
|
}
|
|
2494
2524
|
|
|
2525
|
+
function buildExecutionFailureExplanationPrompt({
|
|
2526
|
+
botName = "",
|
|
2527
|
+
userMessageText = "",
|
|
2528
|
+
failureFacts = null,
|
|
2529
|
+
}) {
|
|
2530
|
+
const compactUserMessage = String(userMessageText || "").trim() || "(not available)";
|
|
2531
|
+
const compactBotName = String(botName || "").trim() || "the bot";
|
|
2532
|
+
return [
|
|
2533
|
+
"You explain runner execution failures to the user.",
|
|
2534
|
+
`The active bot name is: ${compactBotName}`,
|
|
2535
|
+
"Use only the supplied failure facts. Do not invent success, recovered work items, or missing facts.",
|
|
2536
|
+
"Classify the outcome and explain it briefly in the same language as the user's latest message when possible.",
|
|
2537
|
+
"If retryable is true, mention that a retry is reasonable.",
|
|
2538
|
+
"Return a JSON object only with keys:",
|
|
2539
|
+
'{"classification":"failed|retryable_failure|partial_success|needs_user_input|blocked","reply":"string","next_action":"string"}',
|
|
2540
|
+
"",
|
|
2541
|
+
"Latest user message:",
|
|
2542
|
+
compactUserMessage,
|
|
2543
|
+
"",
|
|
2544
|
+
"Failure facts JSON:",
|
|
2545
|
+
JSON.stringify(safeObject(failureFacts), null, 2),
|
|
2546
|
+
].join("\n");
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
export function explainExecutionFailureWithAI({
|
|
2550
|
+
failureFacts = null,
|
|
2551
|
+
userMessageText = "",
|
|
2552
|
+
botName = "",
|
|
2553
|
+
workspaceDir,
|
|
2554
|
+
client = "",
|
|
2555
|
+
model = "",
|
|
2556
|
+
env = process.env,
|
|
2557
|
+
}) {
|
|
2558
|
+
const explainerClient = normalizeLocalAIClientName(
|
|
2559
|
+
String(
|
|
2560
|
+
client
|
|
2561
|
+
|| env?.METHEUS_FAILURE_EXPLAINER_CLIENT
|
|
2562
|
+
|| env?.METHEUS_INTENT_PARSER_CLIENT
|
|
2563
|
+
|| "",
|
|
2564
|
+
).trim(),
|
|
2565
|
+
"gpt",
|
|
2566
|
+
);
|
|
2567
|
+
const explainerModel = resolveFailureExplainerModelDisplayName({
|
|
2568
|
+
client: explainerClient,
|
|
2569
|
+
model,
|
|
2570
|
+
env,
|
|
2571
|
+
});
|
|
2572
|
+
const rawText = runLocalAIPromptRawText({
|
|
2573
|
+
client: explainerClient,
|
|
2574
|
+
promptText: buildExecutionFailureExplanationPrompt({
|
|
2575
|
+
botName,
|
|
2576
|
+
userMessageText,
|
|
2577
|
+
failureFacts,
|
|
2578
|
+
}),
|
|
2579
|
+
workspaceDir,
|
|
2580
|
+
model: explainerModel,
|
|
2581
|
+
permissionMode: "read_only",
|
|
2582
|
+
reasoningEffort: String(env?.METHEUS_FAILURE_EXPLAINER_REASONING_EFFORT || "low").trim() || "low",
|
|
2583
|
+
env,
|
|
2584
|
+
});
|
|
2585
|
+
const parsed = tryJsonParse(rawText) || tryParseEmbeddedJsonObject(rawText);
|
|
2586
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2587
|
+
const classificationRaw = String(parsed.classification || "").trim().toLowerCase();
|
|
2588
|
+
const classification = [
|
|
2589
|
+
"failed",
|
|
2590
|
+
"retryable_failure",
|
|
2591
|
+
"partial_success",
|
|
2592
|
+
"needs_user_input",
|
|
2593
|
+
"blocked",
|
|
2594
|
+
].includes(classificationRaw) ? classificationRaw : "failed";
|
|
2595
|
+
const reply = String(parsed.reply || parsed.message || "").trim();
|
|
2596
|
+
const nextAction = String(parsed.next_action || parsed.nextAction || "").trim();
|
|
2597
|
+
if (!reply) {
|
|
2598
|
+
throw new Error("failure explainer did not return reply text");
|
|
2599
|
+
}
|
|
2600
|
+
return {
|
|
2601
|
+
classification,
|
|
2602
|
+
reply,
|
|
2603
|
+
next_action: nextAction,
|
|
2604
|
+
raw: parsed,
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
const plainReply = String(rawText || "").trim();
|
|
2608
|
+
if (!plainReply) {
|
|
2609
|
+
throw new Error("failure explainer returned empty output");
|
|
2610
|
+
}
|
|
2611
|
+
return {
|
|
2612
|
+
classification: "failed",
|
|
2613
|
+
reply: plainReply,
|
|
2614
|
+
next_action: "",
|
|
2615
|
+
raw: null,
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2495
2619
|
function normalizeResponderAdjudicationSelectorList(values, allowedSelectors) {
|
|
2496
2620
|
const allowed = new Set(ensureArray(allowedSelectors).map((value) => String(value || "").trim().replace(/^@+/, "").toLowerCase()).filter(Boolean));
|
|
2497
2621
|
return uniqueOrdered(
|
package/lib/runner-delivery.mjs
CHANGED
|
@@ -101,11 +101,18 @@ function normalizeExecutionContractForArchive(rawContract) {
|
|
|
101
101
|
})
|
|
102
102
|
.filter(Boolean);
|
|
103
103
|
const summaryBot = normalizeMentionUsername(contract.summary_bot || contract.summaryBot || "");
|
|
104
|
-
const
|
|
104
|
+
const explicitNextResponders = Array.from(new Set(
|
|
105
105
|
ensureArray(contract.next_responders || contract.nextResponders || contract.responders)
|
|
106
106
|
.map((item) => normalizeMentionUsername(item))
|
|
107
107
|
.filter(Boolean),
|
|
108
108
|
));
|
|
109
|
+
const nextResponders = explicitNextResponders.length > 0
|
|
110
|
+
? explicitNextResponders
|
|
111
|
+
: type === "delegation"
|
|
112
|
+
? Array.from(new Set(assignments.map((item) => normalizeMentionUsername(item.target_bot)).filter(Boolean)))
|
|
113
|
+
: type === "summary_request" && summaryBot
|
|
114
|
+
? [summaryBot]
|
|
115
|
+
: [];
|
|
109
116
|
const actionable = contract.actionable === true;
|
|
110
117
|
if (!type && !assignments.length && !summaryBot && !nextResponders.length && !actionable) {
|
|
111
118
|
return null;
|
|
@@ -431,6 +431,155 @@ function shouldSendExecutionFailureReply({ triggerDecision, selectedRecord }) {
|
|
|
431
431
|
&& safeObject(selectedRecord?.parsedArchive).senderIsBot !== true;
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
function classifyExecutionFailureFacts(detail) {
|
|
435
|
+
const normalizedDetail = String(detail || "").trim();
|
|
436
|
+
const networkReset = /ECONNRESET|socket hang up|read ECONNRESET/i.test(normalizedDetail);
|
|
437
|
+
const networkTimeout = /ETIMEDOUT|http timeout|ECONNABORTED|aborted/i.test(normalizedDetail);
|
|
438
|
+
const retryable = networkReset || networkTimeout;
|
|
439
|
+
const base = {
|
|
440
|
+
stage: "execution",
|
|
441
|
+
operation: "runner_execution",
|
|
442
|
+
errorType: retryable
|
|
443
|
+
? (networkTimeout ? "network_timeout" : "network_reset")
|
|
444
|
+
: "execution_failed",
|
|
445
|
+
retryable,
|
|
446
|
+
artifactCreated: null,
|
|
447
|
+
workItemCreated: null,
|
|
448
|
+
ctxpackUpdated: null,
|
|
449
|
+
partialSuccess: false,
|
|
450
|
+
};
|
|
451
|
+
if (!normalizedDetail) {
|
|
452
|
+
return base;
|
|
453
|
+
}
|
|
454
|
+
if (/permission_mode=read_only|read[_ -]?only/i.test(normalizedDetail)) {
|
|
455
|
+
return {
|
|
456
|
+
...base,
|
|
457
|
+
stage: "permission_check",
|
|
458
|
+
operation: "route_permission_check",
|
|
459
|
+
errorType: "read_only_route",
|
|
460
|
+
retryable: false,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (/reply did not produce an actionable execution contract/i.test(normalizedDetail)) {
|
|
464
|
+
return {
|
|
465
|
+
...base,
|
|
466
|
+
stage: "execution_contract",
|
|
467
|
+
operation: "response_contract_validation",
|
|
468
|
+
errorType: "missing_actionable_contract",
|
|
469
|
+
retryable: false,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
if (/failed to create work item/i.test(normalizedDetail)) {
|
|
473
|
+
return {
|
|
474
|
+
...base,
|
|
475
|
+
stage: "work_item_create",
|
|
476
|
+
operation: "workitem.push",
|
|
477
|
+
errorType: retryable
|
|
478
|
+
? (networkTimeout ? "network_timeout" : "network_reset")
|
|
479
|
+
: "work_item_create_failed",
|
|
480
|
+
workItemCreated: false,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
if (/governance work items/i.test(normalizedDetail)) {
|
|
484
|
+
return {
|
|
485
|
+
...base,
|
|
486
|
+
stage: "work_item_create",
|
|
487
|
+
operation: "governance_work_item_validation",
|
|
488
|
+
errorType: "governance_work_items_missing",
|
|
489
|
+
retryable: false,
|
|
490
|
+
workItemCreated: false,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
if (/validated project artifacts|reported project artifacts that were not observed|artifact path does not exist/i.test(normalizedDetail)) {
|
|
494
|
+
return {
|
|
495
|
+
...base,
|
|
496
|
+
stage: "artifact_validation",
|
|
497
|
+
operation: "workspace_artifact_validation",
|
|
498
|
+
errorType: "artifact_validation_failed",
|
|
499
|
+
retryable: false,
|
|
500
|
+
artifactCreated: false,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
if (/thread not found/i.test(normalizedDetail)) {
|
|
504
|
+
return {
|
|
505
|
+
...base,
|
|
506
|
+
stage: "archive_thread",
|
|
507
|
+
operation: "archive_thread_lookup",
|
|
508
|
+
errorType: "archive_thread_missing",
|
|
509
|
+
retryable,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
if (/ctxpack version_id is missing/i.test(normalizedDetail)) {
|
|
513
|
+
return {
|
|
514
|
+
...base,
|
|
515
|
+
stage: "ctxpack_update",
|
|
516
|
+
operation: "ctxpack.update",
|
|
517
|
+
errorType: "ctxpack_version_missing",
|
|
518
|
+
retryable: false,
|
|
519
|
+
ctxpackUpdated: false,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (/ctxpack\.update requires project ctxpack write access|ctxpack write access|ctxpack update permission|forbidden/i.test(normalizedDetail) && /ctxpack/i.test(normalizedDetail)) {
|
|
523
|
+
return {
|
|
524
|
+
...base,
|
|
525
|
+
stage: "ctxpack_update",
|
|
526
|
+
operation: "ctxpack.update",
|
|
527
|
+
errorType: "ctxpack_permission_denied",
|
|
528
|
+
retryable: false,
|
|
529
|
+
ctxpackUpdated: false,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
if (/ctxpack/i.test(normalizedDetail) && retryable) {
|
|
533
|
+
return {
|
|
534
|
+
...base,
|
|
535
|
+
stage: "ctxpack_update",
|
|
536
|
+
operation: "ctxpack.update",
|
|
537
|
+
errorType: networkTimeout ? "network_timeout" : "network_reset",
|
|
538
|
+
retryable: true,
|
|
539
|
+
ctxpackUpdated: false,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (/ctxpack/i.test(normalizedDetail)) {
|
|
543
|
+
return {
|
|
544
|
+
...base,
|
|
545
|
+
stage: "ctxpack_update",
|
|
546
|
+
operation: "ctxpack.update",
|
|
547
|
+
errorType: "ctxpack_update_failed",
|
|
548
|
+
retryable: false,
|
|
549
|
+
ctxpackUpdated: false,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
return base;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function buildExecutionFailureFacts(detail, options = {}) {
|
|
556
|
+
const normalizedDetail = String(detail || "").trim();
|
|
557
|
+
const compactDetail = normalizedDetail.replace(/\s+/g, " ").trim();
|
|
558
|
+
const normalizedOptions = safeObject(options);
|
|
559
|
+
const intentType = normalizeHumanIntentType(normalizedOptions.intentType);
|
|
560
|
+
const classified = classifyExecutionFailureFacts(compactDetail);
|
|
561
|
+
return {
|
|
562
|
+
stage: classified.stage,
|
|
563
|
+
operation: classified.operation,
|
|
564
|
+
status: "failed",
|
|
565
|
+
error_type: classified.errorType,
|
|
566
|
+
error_message: compactDetail.slice(0, 400),
|
|
567
|
+
retryable: classified.retryable === true,
|
|
568
|
+
artifact_created: classified.artifactCreated,
|
|
569
|
+
work_item_created: classified.workItemCreated,
|
|
570
|
+
ctxpack_updated: classified.ctxpackUpdated,
|
|
571
|
+
partial_success: classified.partialSuccess === true,
|
|
572
|
+
intent_type: intentType,
|
|
573
|
+
informational_request: isInformationalHumanIntentType(intentType),
|
|
574
|
+
execution_mode: String(normalizedOptions.executionMode || "").trim(),
|
|
575
|
+
role_profile: String(normalizedOptions.roleProfileName || "").trim(),
|
|
576
|
+
conversation_id: String(normalizedOptions.conversationID || "").trim(),
|
|
577
|
+
root_work_item_id: String(normalizedOptions.rootWorkItemID || "").trim(),
|
|
578
|
+
request_message_id: intFromRawAllowZero(normalizedOptions.messageID, 0),
|
|
579
|
+
request_text: String(normalizedOptions.userMessageText || "").trim(),
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
434
583
|
function buildExecutionFailureReplyText(detail, options = {}) {
|
|
435
584
|
const normalizedDetail = String(detail || "").trim();
|
|
436
585
|
const intentType = normalizeHumanIntentType(safeObject(options).intentType);
|
|
@@ -960,14 +1109,26 @@ function buildHumanIntentFromPersistedRunnerRequest({
|
|
|
960
1109
|
.filter((item) => item && (managedSelectors.size === 0 || managedSelectors.has(item))),
|
|
961
1110
|
);
|
|
962
1111
|
const intentMode = String(persistedRequest.conversation_intent_mode || "").trim();
|
|
963
|
-
const
|
|
1112
|
+
const persistedReplyExpectation = normalizeReplyExpectation(
|
|
1113
|
+
persistedRequest.conversation_reply_expectation,
|
|
1114
|
+
"",
|
|
1115
|
+
);
|
|
1116
|
+
const persistedExecutionActionable = persistedRequest.execution_contract_actionable === true;
|
|
1117
|
+
const persistedNormalizedIntent = normalizeHumanIntentType(
|
|
964
1118
|
persistedRequest.normalized_intent || persistedRequest.intent_type,
|
|
965
1119
|
"",
|
|
966
1120
|
);
|
|
967
|
-
const
|
|
968
|
-
|
|
1121
|
+
const intentType = normalizeHumanIntentType(
|
|
1122
|
+
(
|
|
1123
|
+
persistedNormalizedIntent === "general_execution"
|
|
1124
|
+
&& persistedReplyExpectation !== "actionable"
|
|
1125
|
+
&& !persistedExecutionActionable
|
|
1126
|
+
)
|
|
1127
|
+
? ""
|
|
1128
|
+
: persistedNormalizedIntent,
|
|
969
1129
|
"",
|
|
970
1130
|
);
|
|
1131
|
+
const replyExpectation = persistedReplyExpectation;
|
|
971
1132
|
const participantSelectors = normalizeManagedSelectors(persistedRequest.conversation_participants);
|
|
972
1133
|
const initialResponderSelectors = normalizeManagedSelectors(persistedRequest.conversation_initial_responders);
|
|
973
1134
|
const allowedResponderSelectors = normalizeManagedSelectors(persistedRequest.conversation_allowed_responders);
|
|
@@ -1112,7 +1273,7 @@ function buildDirectHumanResponseContract({
|
|
|
1112
1273
|
}
|
|
1113
1274
|
}
|
|
1114
1275
|
const requiresActionableContract = (
|
|
1115
|
-
|
|
1276
|
+
replyExpectation === "actionable"
|
|
1116
1277
|
&& Boolean(currentBotSelector)
|
|
1117
1278
|
&& initialResponderSelectors.includes(currentBotSelector)
|
|
1118
1279
|
);
|
|
@@ -3158,11 +3319,18 @@ function normalizeConversationExecutionContract(
|
|
|
3158
3319
|
|| contract.summary_bot
|
|
3159
3320
|
|| conversationContext?.summaryBotUsername,
|
|
3160
3321
|
);
|
|
3161
|
-
const
|
|
3162
|
-
ensureArray(contract.nextResponders || contract.next_responders || contract.responders)
|
|
3163
|
-
.map((item) => normalizeMentionSelector(item))
|
|
3164
|
-
.filter((item) => !allowedResponderSet.size || allowedResponderSet.has(item)),
|
|
3165
|
-
);
|
|
3322
|
+
const explicitNextResponders = uniqueOrdered(
|
|
3323
|
+
ensureArray(contract.nextResponders || contract.next_responders || contract.responders)
|
|
3324
|
+
.map((item) => normalizeMentionSelector(item))
|
|
3325
|
+
.filter((item) => !allowedResponderSet.size || allowedResponderSet.has(item)),
|
|
3326
|
+
);
|
|
3327
|
+
const nextResponders = explicitNextResponders.length > 0
|
|
3328
|
+
? explicitNextResponders
|
|
3329
|
+
: normalizedType === "delegation"
|
|
3330
|
+
? uniqueOrdered(assignments.map((item) => normalizeMentionSelector(item.targetBot)).filter(Boolean))
|
|
3331
|
+
: normalizedType === "summary_request" && summaryBot
|
|
3332
|
+
? [summaryBot]
|
|
3333
|
+
: [];
|
|
3166
3334
|
const actionable = contract.actionable === true
|
|
3167
3335
|
|| (normalizedType === "delegation" && assignments.length > 0)
|
|
3168
3336
|
|| (normalizedType === "summary_request" && Boolean(summaryBot || nextResponders.length))
|
|
@@ -3237,11 +3405,18 @@ function normalizeResponseExecutionContract(rawContract, responseContract, { cur
|
|
|
3237
3405
|
contract.summaryBot
|
|
3238
3406
|
|| contract.summary_bot,
|
|
3239
3407
|
);
|
|
3240
|
-
const
|
|
3408
|
+
const explicitNextResponders = uniqueOrdered(
|
|
3241
3409
|
ensureArray(contract.nextResponders || contract.next_responders || contract.responders)
|
|
3242
3410
|
.map((item) => normalizeMentionSelector(item))
|
|
3243
3411
|
.filter(Boolean),
|
|
3244
3412
|
);
|
|
3413
|
+
const nextResponders = explicitNextResponders.length > 0
|
|
3414
|
+
? explicitNextResponders
|
|
3415
|
+
: normalizedType === "delegation"
|
|
3416
|
+
? uniqueOrdered(assignments.map((item) => normalizeMentionSelector(item.targetBot)).filter(Boolean))
|
|
3417
|
+
: normalizedType === "summary_request" && summaryBot
|
|
3418
|
+
? [summaryBot]
|
|
3419
|
+
: [];
|
|
3245
3420
|
const actionable = contract.actionable === true
|
|
3246
3421
|
|| (normalizedType === "delegation" && assignments.length > 0)
|
|
3247
3422
|
|| (normalizedType === "summary_request" && Boolean(summaryBot || nextResponders.length))
|
|
@@ -3262,7 +3437,7 @@ function normalizeResponseExecutionContract(rawContract, responseContract, { cur
|
|
|
3262
3437
|
};
|
|
3263
3438
|
}
|
|
3264
3439
|
|
|
3265
|
-
function buildImplicitSummaryRequestContract(conversationContext, currentBotSelector) {
|
|
3440
|
+
function buildImplicitSummaryRequestContract(conversationContext, currentBotSelector) {
|
|
3266
3441
|
if (String(conversationContext?.mode || "").trim() !== "public_multi_bot") {
|
|
3267
3442
|
return null;
|
|
3268
3443
|
}
|
|
@@ -3277,15 +3452,45 @@ function buildImplicitSummaryRequestContract(conversationContext, currentBotSele
|
|
|
3277
3452
|
if (!summaryBotSelector || !currentSelector || summaryBotSelector === currentSelector) {
|
|
3278
3453
|
return null;
|
|
3279
3454
|
}
|
|
3280
|
-
return {
|
|
3281
|
-
type: "summary_request",
|
|
3282
|
-
actionable: true,
|
|
3283
|
-
assignments: [],
|
|
3284
|
-
summaryBot: summaryBotSelector,
|
|
3285
|
-
nextResponders: [summaryBotSelector],
|
|
3286
|
-
implicit: true,
|
|
3287
|
-
};
|
|
3288
|
-
}
|
|
3455
|
+
return {
|
|
3456
|
+
type: "summary_request",
|
|
3457
|
+
actionable: true,
|
|
3458
|
+
assignments: [],
|
|
3459
|
+
summaryBot: summaryBotSelector,
|
|
3460
|
+
nextResponders: [summaryBotSelector],
|
|
3461
|
+
implicit: true,
|
|
3462
|
+
};
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3465
|
+
function collectExecutionContractNextResponders(executionContract) {
|
|
3466
|
+
const contract = safeObject(executionContract);
|
|
3467
|
+
const normalizedType = String(
|
|
3468
|
+
contract.type
|
|
3469
|
+
|| contract.contract_type
|
|
3470
|
+
|| contract.contractType
|
|
3471
|
+
|| "",
|
|
3472
|
+
).trim().toLowerCase();
|
|
3473
|
+
const explicitNextResponders = uniqueOrdered(
|
|
3474
|
+
ensureArray(contract.nextResponders || contract.next_responders || contract.responders)
|
|
3475
|
+
.map((item) => normalizeMentionSelector(item))
|
|
3476
|
+
.filter(Boolean),
|
|
3477
|
+
);
|
|
3478
|
+
if (explicitNextResponders.length > 0) {
|
|
3479
|
+
return explicitNextResponders;
|
|
3480
|
+
}
|
|
3481
|
+
if (normalizedType === "delegation") {
|
|
3482
|
+
return uniqueOrdered(
|
|
3483
|
+
ensureArray(contract.assignments)
|
|
3484
|
+
.map((item) => normalizeMentionSelector(safeObject(item).targetBot || safeObject(item).target_bot))
|
|
3485
|
+
.filter(Boolean),
|
|
3486
|
+
);
|
|
3487
|
+
}
|
|
3488
|
+
const summaryBot = normalizeMentionSelector(contract.summaryBot || contract.summary_bot);
|
|
3489
|
+
if (normalizedType === "summary_request" && summaryBot) {
|
|
3490
|
+
return [summaryBot];
|
|
3491
|
+
}
|
|
3492
|
+
return [];
|
|
3493
|
+
}
|
|
3289
3494
|
|
|
3290
3495
|
function conversationSessionIsExpired(session, policy, nowMs = Date.now()) {
|
|
3291
3496
|
const expiresAtMs = parseISOStringMs(safeObject(session).expires_at);
|
|
@@ -4176,6 +4381,9 @@ export async function processRunnerSelectedRecord({
|
|
|
4176
4381
|
...safeObject(buildRunnerExecutionDeps()),
|
|
4177
4382
|
...safeObject(deps),
|
|
4178
4383
|
};
|
|
4384
|
+
const explainExecutionFailureWithAI = typeof executionDeps.explainExecutionFailureWithAI === "function"
|
|
4385
|
+
? executionDeps.explainExecutionFailureWithAI
|
|
4386
|
+
: null;
|
|
4179
4387
|
const normalizedPrecomputedHumanIntentContext = safeObject(precomputedHumanIntentContext);
|
|
4180
4388
|
const normalizedPrecomputedHumanIntent = safeObject(normalizedPrecomputedHumanIntentContext.humanIntent);
|
|
4181
4389
|
const validateWorkspaceArtifacts = typeof executionDeps.validateWorkspaceArtifacts === "function"
|
|
@@ -4413,9 +4621,36 @@ export async function processRunnerSelectedRecord({
|
|
|
4413
4621
|
if (!shouldSendExecutionFailureReply({ triggerDecision: effectiveTriggerDecision, selectedRecord })) {
|
|
4414
4622
|
return null;
|
|
4415
4623
|
}
|
|
4416
|
-
const
|
|
4624
|
+
const failureFacts = buildExecutionFailureFacts(detail, {
|
|
4417
4625
|
intentType: resolvedIntentType,
|
|
4626
|
+
executionMode: effectiveExecutionPlan.mode,
|
|
4627
|
+
roleProfileName: effectiveExecutionPlan.roleProfileName,
|
|
4628
|
+
conversationID: String(conversationContext?.id || "").trim(),
|
|
4629
|
+
rootWorkItemID: firstNonEmptyString([
|
|
4630
|
+
routeState?.active_root_work_item_id,
|
|
4631
|
+
routeState?.last_root_work_item_id,
|
|
4632
|
+
]),
|
|
4633
|
+
messageID: selectedRecord?.parsedArchive?.messageID,
|
|
4634
|
+
userMessageText: selectedRecord?.parsedArchive?.body,
|
|
4418
4635
|
});
|
|
4636
|
+
let replyText = "";
|
|
4637
|
+
if (explainExecutionFailureWithAI) {
|
|
4638
|
+
try {
|
|
4639
|
+
const explanation = await Promise.resolve(explainExecutionFailureWithAI({
|
|
4640
|
+
failureFacts,
|
|
4641
|
+
userMessageText: String(selectedRecord?.parsedArchive?.body || "").trim(),
|
|
4642
|
+
botName: String(bot?.name || bot?.username || bot?.id || "").trim(),
|
|
4643
|
+
workspaceDir: String(effectiveExecutionPlan.workspaceDir || "").trim(),
|
|
4644
|
+
env: process.env,
|
|
4645
|
+
}));
|
|
4646
|
+
replyText = String(safeObject(explanation).reply || safeObject(explanation).message || "").trim();
|
|
4647
|
+
} catch {}
|
|
4648
|
+
}
|
|
4649
|
+
if (!replyText) {
|
|
4650
|
+
replyText = buildExecutionFailureReplyText(detail, {
|
|
4651
|
+
intentType: resolvedIntentType,
|
|
4652
|
+
});
|
|
4653
|
+
}
|
|
4419
4654
|
if (!replyText) {
|
|
4420
4655
|
return null;
|
|
4421
4656
|
}
|
|
@@ -4687,6 +4922,24 @@ export async function processRunnerSelectedRecord({
|
|
|
4687
4922
|
.filter(Boolean),
|
|
4688
4923
|
);
|
|
4689
4924
|
let requiresActionableContract = safeObject(aiPayload.response_contract).require_actionable_contract === true;
|
|
4925
|
+
const normalizedConversationInitialResponders = uniqueOrdered(
|
|
4926
|
+
ensureArray(conversationContext?.initialResponderSelectors)
|
|
4927
|
+
.map((item) => normalizeMentionSelector(item))
|
|
4928
|
+
.filter(Boolean),
|
|
4929
|
+
);
|
|
4930
|
+
const normalizedConversationAllowedResponders = uniqueOrdered(
|
|
4931
|
+
ensureArray(conversationContext?.allowedResponderSelectors)
|
|
4932
|
+
.map((item) => normalizeMentionSelector(item))
|
|
4933
|
+
.filter(Boolean),
|
|
4934
|
+
);
|
|
4935
|
+
const peerAllowedResponders = normalizedConversationAllowedResponders.filter((item) => item && item !== currentBotSelector);
|
|
4936
|
+
const requiresPeerDelegationContract = conversationContext?.mode === "public_multi_bot"
|
|
4937
|
+
&& String(conversationContext?.stage || "").trim() === "human_opening"
|
|
4938
|
+
&& conversationContext?.allowBotToBot === true
|
|
4939
|
+
&& Boolean(currentBotSelector)
|
|
4940
|
+
&& normalizedConversationInitialResponders.length === 1
|
|
4941
|
+
&& normalizedConversationInitialResponders.includes(currentBotSelector)
|
|
4942
|
+
&& peerAllowedResponders.length > 0;
|
|
4690
4943
|
const directHumanPeerMap = humanIntentContext?.peerMap instanceof Map
|
|
4691
4944
|
? humanIntentContext.peerMap
|
|
4692
4945
|
: buildConversationPeerMap(bot, normalizedRoute, executionDeps);
|
|
@@ -4704,6 +4957,10 @@ export async function processRunnerSelectedRecord({
|
|
|
4704
4957
|
if (delegatedBotMentions.length > 0) {
|
|
4705
4958
|
allowedActionableTypes = new Set(["delegation"]);
|
|
4706
4959
|
}
|
|
4960
|
+
if (requiresPeerDelegationContract) {
|
|
4961
|
+
allowedActionableTypes = new Set(["delegation"]);
|
|
4962
|
+
requiresActionableContract = true;
|
|
4963
|
+
}
|
|
4707
4964
|
let hasValidActionableContract = Boolean(
|
|
4708
4965
|
executionContract
|
|
4709
4966
|
&& executionContract.actionable === true
|
|
@@ -4744,7 +5001,7 @@ export async function processRunnerSelectedRecord({
|
|
|
4744
5001
|
allow_skip: false,
|
|
4745
5002
|
must_reply: true,
|
|
4746
5003
|
require_actionable_contract: true,
|
|
4747
|
-
human_intent_mode: effectiveResponseContractPayload.human_intent_mode || (delegatedBotMentions.length > 0 ? "delegated_single_lead" : ""),
|
|
5004
|
+
human_intent_mode: effectiveResponseContractPayload.human_intent_mode || ((delegatedBotMentions.length > 0 || requiresPeerDelegationContract) ? "delegated_single_lead" : ""),
|
|
4748
5005
|
human_lead_bot: delegatedBotMentions.length > 0
|
|
4749
5006
|
? currentBotSelector
|
|
4750
5007
|
: String(effectiveResponseContractPayload.human_lead_bot || "").trim(),
|
|
@@ -4755,10 +5012,10 @@ export async function processRunnerSelectedRecord({
|
|
|
4755
5012
|
? ensureArray(effectiveResponseContractPayload.human_allowed_responders)
|
|
4756
5013
|
: uniqueOrdered([
|
|
4757
5014
|
currentBotSelector,
|
|
4758
|
-
...delegatedBotMentions,
|
|
5015
|
+
...(delegatedBotMentions.length > 0 ? delegatedBotMentions : peerAllowedResponders),
|
|
4759
5016
|
].filter(Boolean)),
|
|
4760
|
-
allow_bot_to_bot: delegatedBotMentions.length > 0 || effectiveResponseContractPayload.human_intent_mode === "delegated_single_lead",
|
|
4761
|
-
allowed_contract_types: delegatedBotMentions.length > 0
|
|
5017
|
+
allow_bot_to_bot: delegatedBotMentions.length > 0 || requiresPeerDelegationContract || effectiveResponseContractPayload.human_intent_mode === "delegated_single_lead",
|
|
5018
|
+
allowed_contract_types: delegatedBotMentions.length > 0 || requiresPeerDelegationContract
|
|
4762
5019
|
? ["delegation", "summary_request", "final_summary"]
|
|
4763
5020
|
: ensureArray(effectiveResponseContractPayload.allowed_contract_types).length
|
|
4764
5021
|
? ensureArray(effectiveResponseContractPayload.allowed_contract_types)
|
|
@@ -5077,10 +5334,7 @@ export async function processRunnerSelectedRecord({
|
|
|
5077
5334
|
last_execution_contract_type: String(executionContract?.type || "").trim(),
|
|
5078
5335
|
last_execution_contract_actionable: executionContract?.actionable === true,
|
|
5079
5336
|
last_execution_contract_targets: ensureArray(executionContract?.assignments).map((item) => normalizeMentionSelector(item.targetBot)).filter(Boolean),
|
|
5080
|
-
next_expected_responders:
|
|
5081
|
-
...ensureArray(executionContract?.nextResponders).map((item) => normalizeMentionSelector(item)).filter(Boolean),
|
|
5082
|
-
normalizeMentionSelector(executionContract?.summaryBot),
|
|
5083
|
-
]),
|
|
5337
|
+
next_expected_responders: collectExecutionContractNextResponders(executionContract),
|
|
5084
5338
|
last_speaker_bot_username: currentBotSelector,
|
|
5085
5339
|
speaker_counts: speakerCounts,
|
|
5086
5340
|
last_sender_bot_username: String(effectiveConversationContext?.senderBotUsername || "").trim(),
|
|
@@ -5176,10 +5430,7 @@ export async function processRunnerSelectedRecord({
|
|
|
5176
5430
|
execution_contract_type: String(executionContract?.type || "").trim(),
|
|
5177
5431
|
execution_contract_actionable: executionContract?.actionable === true,
|
|
5178
5432
|
execution_contract_targets: ensureArray(executionContract?.assignments).map((item) => normalizeMentionSelector(item.targetBot)).filter(Boolean),
|
|
5179
|
-
next_expected_responders:
|
|
5180
|
-
...ensureArray(executionContract?.nextResponders).map((item) => normalizeMentionSelector(item)).filter(Boolean),
|
|
5181
|
-
normalizeMentionSelector(executionContract?.summaryBot),
|
|
5182
|
-
]),
|
|
5433
|
+
next_expected_responders: collectExecutionContractNextResponders(executionContract),
|
|
5183
5434
|
artifact_validation: String(artifactValidation.status || "").trim() || "none",
|
|
5184
5435
|
artifact_paths: summarizeValidatedArtifactPaths(artifactValidation),
|
|
5185
5436
|
ctxpack_version_id: String(safeObject(aiResult?.ctxpackUpdate).version_id || "").trim(),
|
|
@@ -1569,9 +1569,9 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
1569
1569
|
});
|
|
1570
1570
|
const inferredExecutionRequest = safeObject(safeObject(loadBotRunnerState().requests)[inferredExecutionClaim.requestKey]);
|
|
1571
1571
|
push(
|
|
1572
|
-
"
|
|
1572
|
+
"runner_request_claim_leaves_unclassified_messages_unresolved",
|
|
1573
1573
|
inferredExecutionClaim.ok === true
|
|
1574
|
-
&& String(inferredExecutionRequest.normalized_intent || "") === "
|
|
1574
|
+
&& String(inferredExecutionRequest.normalized_intent || "") === "",
|
|
1575
1575
|
`intent=${String(inferredExecutionRequest.normalized_intent || "(none)")} request=${String(inferredExecutionClaim.requestKey || "(none)")}`,
|
|
1576
1576
|
);
|
|
1577
1577
|
|
|
@@ -4785,7 +4785,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
4785
4785
|
push(
|
|
4786
4786
|
"direct_human_public_delegate_fallback_uses_response_allowed_responders",
|
|
4787
4787
|
processed.kind === "replied"
|
|
4788
|
-
&& aiCalls ===
|
|
4788
|
+
&& aiCalls === 2
|
|
4789
4789
|
&& String(deliveredConversation[0]?.mode || "") === "public_multi_bot"
|
|
4790
4790
|
&& Array.isArray(deliveredConversation[0]?.allowedResponderSelectors)
|
|
4791
4791
|
&& deliveredConversation[0].allowedResponderSelectors.includes("ryoai3_bot")
|
|
@@ -5064,14 +5064,14 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5064
5064
|
},
|
|
5065
5065
|
});
|
|
5066
5066
|
push(
|
|
5067
|
-
"
|
|
5067
|
+
"delegated_single_lead_lead_opening_requires_delegation_contract_for_peer_followup",
|
|
5068
5068
|
processed.kind === "skipped"
|
|
5069
5069
|
&& aiCalls === 2
|
|
5070
|
-
&&
|
|
5071
|
-
`kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls}
|
|
5070
|
+
&& !String(deliveredText || "").trim(),
|
|
5071
|
+
`kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} reply=${String(deliveredText || "(none)")}`,
|
|
5072
5072
|
);
|
|
5073
5073
|
} catch (err) {
|
|
5074
|
-
push("
|
|
5074
|
+
push("delegated_single_lead_lead_opening_requires_delegation_contract_for_peer_followup", false, String(err?.message || err));
|
|
5075
5075
|
}
|
|
5076
5076
|
|
|
5077
5077
|
try {
|
|
@@ -5207,6 +5207,148 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5207
5207
|
push("single_bot_human_work_request_requires_actionable_contract", false, String(err?.message || err));
|
|
5208
5208
|
}
|
|
5209
5209
|
|
|
5210
|
+
try {
|
|
5211
|
+
let aiCalls = 0;
|
|
5212
|
+
let deliveryCalls = 0;
|
|
5213
|
+
let deliveredText = "";
|
|
5214
|
+
let capturedFailureFacts = null;
|
|
5215
|
+
const processed = await processRunnerSelectedRecord({
|
|
5216
|
+
routeKey: "single-bot-human-work-request-ai-failure-explainer-key",
|
|
5217
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
5218
|
+
name: "telegram-monitor-single-bot-human-work-request-ai-failure-explainer",
|
|
5219
|
+
project_id: selftestProjectID,
|
|
5220
|
+
provider: "telegram",
|
|
5221
|
+
role: "monitor",
|
|
5222
|
+
role_profile: "monitor",
|
|
5223
|
+
destination_id: "dest-1",
|
|
5224
|
+
destination_label: "Main Room",
|
|
5225
|
+
server_bot_name: "RyoAI_bot",
|
|
5226
|
+
server_bot_id: "bot-lead-1",
|
|
5227
|
+
trigger_policy: {
|
|
5228
|
+
mentions_only: true,
|
|
5229
|
+
direct_messages: true,
|
|
5230
|
+
reply_to_bot_messages: true,
|
|
5231
|
+
},
|
|
5232
|
+
archive_policy: {
|
|
5233
|
+
mirror_replies: true,
|
|
5234
|
+
dedupe_inbound: true,
|
|
5235
|
+
dedupe_outbound: true,
|
|
5236
|
+
skip_bot_messages: true,
|
|
5237
|
+
},
|
|
5238
|
+
dry_run_delivery: true,
|
|
5239
|
+
}),
|
|
5240
|
+
selectedRecord: {
|
|
5241
|
+
id: "comment-single-bot-human-work-request-ai-failure-explainer",
|
|
5242
|
+
createdAt: "2026-03-16T00:02:05.500Z",
|
|
5243
|
+
parsedArchive: {
|
|
5244
|
+
kind: "telegram_message",
|
|
5245
|
+
chatID: "-100123",
|
|
5246
|
+
chatType: "supergroup",
|
|
5247
|
+
senderIsBot: false,
|
|
5248
|
+
body: "@RyoAI_bot update the implementation guide now.",
|
|
5249
|
+
mentionUsernames: ["RyoAI_bot"],
|
|
5250
|
+
messageID: 1206,
|
|
5251
|
+
},
|
|
5252
|
+
},
|
|
5253
|
+
pendingOrdered: [],
|
|
5254
|
+
bot: {
|
|
5255
|
+
id: "bot-lead-1",
|
|
5256
|
+
name: "RyoAI_bot",
|
|
5257
|
+
username: "RyoAI_bot",
|
|
5258
|
+
role: "monitor",
|
|
5259
|
+
provider: "telegram",
|
|
5260
|
+
},
|
|
5261
|
+
destination: {
|
|
5262
|
+
id: "dest-1",
|
|
5263
|
+
label: "Main Room",
|
|
5264
|
+
provider: "telegram",
|
|
5265
|
+
chatID: "-100123",
|
|
5266
|
+
},
|
|
5267
|
+
archiveThread: {
|
|
5268
|
+
threadID: "thread-1",
|
|
5269
|
+
workItemID: "work-item-1",
|
|
5270
|
+
},
|
|
5271
|
+
executionPlan: {
|
|
5272
|
+
mode: "role_profile",
|
|
5273
|
+
roleProfileName: "monitor",
|
|
5274
|
+
roleProfile: {
|
|
5275
|
+
client: "sample",
|
|
5276
|
+
model: "",
|
|
5277
|
+
permissionMode: "read_only",
|
|
5278
|
+
reasoningEffort: "low",
|
|
5279
|
+
},
|
|
5280
|
+
workspaceDir: process.cwd(),
|
|
5281
|
+
workspaceSource: "selftest",
|
|
5282
|
+
usedCommandFallback: false,
|
|
5283
|
+
},
|
|
5284
|
+
runtime: {
|
|
5285
|
+
baseURL: "https://example.test",
|
|
5286
|
+
token: "selftest-token",
|
|
5287
|
+
timeoutSeconds: 30,
|
|
5288
|
+
actor: { user_id: "user-1" },
|
|
5289
|
+
},
|
|
5290
|
+
deps: {
|
|
5291
|
+
saveRunnerRouteState: () => {},
|
|
5292
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
5293
|
+
runRunnerAIExecution: async () => {
|
|
5294
|
+
aiCalls += 1;
|
|
5295
|
+
return {
|
|
5296
|
+
skip: false,
|
|
5297
|
+
reply: "I reviewed the request but did not produce a contract.",
|
|
5298
|
+
contract: null,
|
|
5299
|
+
};
|
|
5300
|
+
},
|
|
5301
|
+
explainExecutionFailureWithAI: ({ failureFacts }) => {
|
|
5302
|
+
capturedFailureFacts = safeObject(failureFacts);
|
|
5303
|
+
return {
|
|
5304
|
+
classification: "failed",
|
|
5305
|
+
reply: "AI failure summary",
|
|
5306
|
+
next_action: "retry",
|
|
5307
|
+
};
|
|
5308
|
+
},
|
|
5309
|
+
performLocalBotDelivery: async ({ text }) => {
|
|
5310
|
+
deliveryCalls += 1;
|
|
5311
|
+
deliveredText = String(text || "");
|
|
5312
|
+
return {
|
|
5313
|
+
delivery: { dryRun: true, body: {} },
|
|
5314
|
+
archive: {},
|
|
5315
|
+
};
|
|
5316
|
+
},
|
|
5317
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
5318
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
5319
|
+
buildRunnerExecutionDeps: () => ({
|
|
5320
|
+
analyzeHumanConversationIntentWithAI: async () => ({
|
|
5321
|
+
mode: "single_bot",
|
|
5322
|
+
lead_bot: "ryoai_bot",
|
|
5323
|
+
participants: ["ryoai_bot"],
|
|
5324
|
+
initial_responders: ["ryoai_bot"],
|
|
5325
|
+
allowed_responders: ["ryoai_bot"],
|
|
5326
|
+
summary_bot: "",
|
|
5327
|
+
allow_bot_to_bot: false,
|
|
5328
|
+
reply_expectation: "actionable",
|
|
5329
|
+
}),
|
|
5330
|
+
}),
|
|
5331
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
5332
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
5333
|
+
resolveConversationPeerBots: () => [
|
|
5334
|
+
{ id: "bot-lead-1", name: "RyoAI_bot" },
|
|
5335
|
+
],
|
|
5336
|
+
},
|
|
5337
|
+
});
|
|
5338
|
+
push(
|
|
5339
|
+
"single_bot_execution_failure_uses_ai_failure_explainer_when_available",
|
|
5340
|
+
processed.kind === "error"
|
|
5341
|
+
&& aiCalls >= 1
|
|
5342
|
+
&& deliveryCalls === 1
|
|
5343
|
+
&& deliveredText === "AI failure summary"
|
|
5344
|
+
&& String(capturedFailureFacts?.error_type || "") === "missing_actionable_contract"
|
|
5345
|
+
&& String(capturedFailureFacts?.stage || "") === "execution_contract",
|
|
5346
|
+
`kind=${String(processed.kind || "(none)")} outcome=${String(processed.result?.outcome || "(none)")} ai_calls=${aiCalls} delivery_calls=${deliveryCalls} delivered=${deliveredText} failure_type=${String(capturedFailureFacts?.error_type || "(none)")} stage=${String(capturedFailureFacts?.stage || "(none)")}`,
|
|
5347
|
+
);
|
|
5348
|
+
} catch (err) {
|
|
5349
|
+
push("single_bot_execution_failure_uses_ai_failure_explainer_when_available", false, String(err?.message || err));
|
|
5350
|
+
}
|
|
5351
|
+
|
|
5210
5352
|
try {
|
|
5211
5353
|
let aiCalls = 0;
|
|
5212
5354
|
const processed = await processRunnerSelectedRecord({
|