metheus-governance-mcp-cli 0.2.206 → 0.2.207
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 +3 -0
- package/lib/local-ai-adapters.mjs +117 -0
- package/lib/runner-orchestration.mjs +180 -1
- package/lib/selftest-runner-scenarios.mjs +142 -0
- 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,
|
|
@@ -8463,6 +8464,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8463
8464
|
saveRunnerRouteState,
|
|
8464
8465
|
startRunnerTypingHeartbeat,
|
|
8465
8466
|
runRunnerAIExecution,
|
|
8467
|
+
explainExecutionFailureWithAI,
|
|
8466
8468
|
performLocalBotDelivery,
|
|
8467
8469
|
serializeRunnerTriggerPolicy,
|
|
8468
8470
|
serializeRunnerArchivePolicy,
|
|
@@ -10833,6 +10835,7 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
10833
10835
|
saveRunnerRouteState,
|
|
10834
10836
|
startRunnerTypingHeartbeat,
|
|
10835
10837
|
runRunnerAIExecution,
|
|
10838
|
+
explainExecutionFailureWithAI,
|
|
10836
10839
|
performLocalBotDelivery,
|
|
10837
10840
|
serializeRunnerTriggerPolicy,
|
|
10838
10841
|
serializeRunnerArchivePolicy,
|
|
@@ -1269,6 +1269,29 @@ export function resolveResponderAdjudicatorModelDisplayName({
|
|
|
1269
1269
|
).trim();
|
|
1270
1270
|
}
|
|
1271
1271
|
|
|
1272
|
+
export function resolveFailureExplainerModelDisplayName({
|
|
1273
|
+
client = "",
|
|
1274
|
+
model = "",
|
|
1275
|
+
env = process.env,
|
|
1276
|
+
} = {}) {
|
|
1277
|
+
const explainerClient = normalizeLocalAIClientName(
|
|
1278
|
+
String(
|
|
1279
|
+
client
|
|
1280
|
+
|| env?.METHEUS_FAILURE_EXPLAINER_CLIENT
|
|
1281
|
+
|| env?.METHEUS_INTENT_PARSER_CLIENT
|
|
1282
|
+
|| "",
|
|
1283
|
+
).trim(),
|
|
1284
|
+
"gpt",
|
|
1285
|
+
);
|
|
1286
|
+
return String(
|
|
1287
|
+
model
|
|
1288
|
+
|| env?.METHEUS_FAILURE_EXPLAINER_MODEL
|
|
1289
|
+
|| env?.METHEUS_INTENT_PARSER_MODEL
|
|
1290
|
+
|| defaultAdjudicationModelForClient(explainerClient, env)
|
|
1291
|
+
|| "",
|
|
1292
|
+
).trim();
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1272
1295
|
export function resolveLocalAIExecutionModel(clientName, rawModelValue = "") {
|
|
1273
1296
|
const modelValue = String(rawModelValue || "").trim();
|
|
1274
1297
|
if (!modelValue) return "";
|
|
@@ -2492,6 +2515,100 @@ export function analyzeHumanConversationIntentWithAI({
|
|
|
2492
2515
|
};
|
|
2493
2516
|
}
|
|
2494
2517
|
|
|
2518
|
+
function buildExecutionFailureExplanationPrompt({
|
|
2519
|
+
botName = "",
|
|
2520
|
+
userMessageText = "",
|
|
2521
|
+
failureFacts = null,
|
|
2522
|
+
}) {
|
|
2523
|
+
const compactUserMessage = String(userMessageText || "").trim() || "(not available)";
|
|
2524
|
+
const compactBotName = String(botName || "").trim() || "the bot";
|
|
2525
|
+
return [
|
|
2526
|
+
"You explain runner execution failures to the user.",
|
|
2527
|
+
`The active bot name is: ${compactBotName}`,
|
|
2528
|
+
"Use only the supplied failure facts. Do not invent success, recovered work items, or missing facts.",
|
|
2529
|
+
"Classify the outcome and explain it briefly in the same language as the user's latest message when possible.",
|
|
2530
|
+
"If retryable is true, mention that a retry is reasonable.",
|
|
2531
|
+
"Return a JSON object only with keys:",
|
|
2532
|
+
'{"classification":"failed|retryable_failure|partial_success|needs_user_input|blocked","reply":"string","next_action":"string"}',
|
|
2533
|
+
"",
|
|
2534
|
+
"Latest user message:",
|
|
2535
|
+
compactUserMessage,
|
|
2536
|
+
"",
|
|
2537
|
+
"Failure facts JSON:",
|
|
2538
|
+
JSON.stringify(safeObject(failureFacts), null, 2),
|
|
2539
|
+
].join("\n");
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
export function explainExecutionFailureWithAI({
|
|
2543
|
+
failureFacts = null,
|
|
2544
|
+
userMessageText = "",
|
|
2545
|
+
botName = "",
|
|
2546
|
+
workspaceDir,
|
|
2547
|
+
client = "",
|
|
2548
|
+
model = "",
|
|
2549
|
+
env = process.env,
|
|
2550
|
+
}) {
|
|
2551
|
+
const explainerClient = normalizeLocalAIClientName(
|
|
2552
|
+
String(
|
|
2553
|
+
client
|
|
2554
|
+
|| env?.METHEUS_FAILURE_EXPLAINER_CLIENT
|
|
2555
|
+
|| env?.METHEUS_INTENT_PARSER_CLIENT
|
|
2556
|
+
|| "",
|
|
2557
|
+
).trim(),
|
|
2558
|
+
"gpt",
|
|
2559
|
+
);
|
|
2560
|
+
const explainerModel = resolveFailureExplainerModelDisplayName({
|
|
2561
|
+
client: explainerClient,
|
|
2562
|
+
model,
|
|
2563
|
+
env,
|
|
2564
|
+
});
|
|
2565
|
+
const rawText = runLocalAIPromptRawText({
|
|
2566
|
+
client: explainerClient,
|
|
2567
|
+
promptText: buildExecutionFailureExplanationPrompt({
|
|
2568
|
+
botName,
|
|
2569
|
+
userMessageText,
|
|
2570
|
+
failureFacts,
|
|
2571
|
+
}),
|
|
2572
|
+
workspaceDir,
|
|
2573
|
+
model: explainerModel,
|
|
2574
|
+
permissionMode: "read_only",
|
|
2575
|
+
reasoningEffort: String(env?.METHEUS_FAILURE_EXPLAINER_REASONING_EFFORT || "low").trim() || "low",
|
|
2576
|
+
env,
|
|
2577
|
+
});
|
|
2578
|
+
const parsed = tryJsonParse(rawText) || tryParseEmbeddedJsonObject(rawText);
|
|
2579
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2580
|
+
const classificationRaw = String(parsed.classification || "").trim().toLowerCase();
|
|
2581
|
+
const classification = [
|
|
2582
|
+
"failed",
|
|
2583
|
+
"retryable_failure",
|
|
2584
|
+
"partial_success",
|
|
2585
|
+
"needs_user_input",
|
|
2586
|
+
"blocked",
|
|
2587
|
+
].includes(classificationRaw) ? classificationRaw : "failed";
|
|
2588
|
+
const reply = String(parsed.reply || parsed.message || "").trim();
|
|
2589
|
+
const nextAction = String(parsed.next_action || parsed.nextAction || "").trim();
|
|
2590
|
+
if (!reply) {
|
|
2591
|
+
throw new Error("failure explainer did not return reply text");
|
|
2592
|
+
}
|
|
2593
|
+
return {
|
|
2594
|
+
classification,
|
|
2595
|
+
reply,
|
|
2596
|
+
next_action: nextAction,
|
|
2597
|
+
raw: parsed,
|
|
2598
|
+
};
|
|
2599
|
+
}
|
|
2600
|
+
const plainReply = String(rawText || "").trim();
|
|
2601
|
+
if (!plainReply) {
|
|
2602
|
+
throw new Error("failure explainer returned empty output");
|
|
2603
|
+
}
|
|
2604
|
+
return {
|
|
2605
|
+
classification: "failed",
|
|
2606
|
+
reply: plainReply,
|
|
2607
|
+
next_action: "",
|
|
2608
|
+
raw: null,
|
|
2609
|
+
};
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2495
2612
|
function normalizeResponderAdjudicationSelectorList(values, allowedSelectors) {
|
|
2496
2613
|
const allowed = new Set(ensureArray(allowedSelectors).map((value) => String(value || "").trim().replace(/^@+/, "").toLowerCase()).filter(Boolean));
|
|
2497
2614
|
return uniqueOrdered(
|
|
@@ -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);
|
|
@@ -4176,6 +4325,9 @@ export async function processRunnerSelectedRecord({
|
|
|
4176
4325
|
...safeObject(buildRunnerExecutionDeps()),
|
|
4177
4326
|
...safeObject(deps),
|
|
4178
4327
|
};
|
|
4328
|
+
const explainExecutionFailureWithAI = typeof executionDeps.explainExecutionFailureWithAI === "function"
|
|
4329
|
+
? executionDeps.explainExecutionFailureWithAI
|
|
4330
|
+
: null;
|
|
4179
4331
|
const normalizedPrecomputedHumanIntentContext = safeObject(precomputedHumanIntentContext);
|
|
4180
4332
|
const normalizedPrecomputedHumanIntent = safeObject(normalizedPrecomputedHumanIntentContext.humanIntent);
|
|
4181
4333
|
const validateWorkspaceArtifacts = typeof executionDeps.validateWorkspaceArtifacts === "function"
|
|
@@ -4413,9 +4565,36 @@ export async function processRunnerSelectedRecord({
|
|
|
4413
4565
|
if (!shouldSendExecutionFailureReply({ triggerDecision: effectiveTriggerDecision, selectedRecord })) {
|
|
4414
4566
|
return null;
|
|
4415
4567
|
}
|
|
4416
|
-
const
|
|
4568
|
+
const failureFacts = buildExecutionFailureFacts(detail, {
|
|
4417
4569
|
intentType: resolvedIntentType,
|
|
4570
|
+
executionMode: effectiveExecutionPlan.mode,
|
|
4571
|
+
roleProfileName: effectiveExecutionPlan.roleProfileName,
|
|
4572
|
+
conversationID: String(conversationContext?.id || "").trim(),
|
|
4573
|
+
rootWorkItemID: firstNonEmptyString([
|
|
4574
|
+
routeState?.active_root_work_item_id,
|
|
4575
|
+
routeState?.last_root_work_item_id,
|
|
4576
|
+
]),
|
|
4577
|
+
messageID: selectedRecord?.parsedArchive?.messageID,
|
|
4578
|
+
userMessageText: selectedRecord?.parsedArchive?.body,
|
|
4418
4579
|
});
|
|
4580
|
+
let replyText = "";
|
|
4581
|
+
if (explainExecutionFailureWithAI) {
|
|
4582
|
+
try {
|
|
4583
|
+
const explanation = await Promise.resolve(explainExecutionFailureWithAI({
|
|
4584
|
+
failureFacts,
|
|
4585
|
+
userMessageText: String(selectedRecord?.parsedArchive?.body || "").trim(),
|
|
4586
|
+
botName: String(bot?.name || bot?.username || bot?.id || "").trim(),
|
|
4587
|
+
workspaceDir: String(effectiveExecutionPlan.workspaceDir || "").trim(),
|
|
4588
|
+
env: process.env,
|
|
4589
|
+
}));
|
|
4590
|
+
replyText = String(safeObject(explanation).reply || safeObject(explanation).message || "").trim();
|
|
4591
|
+
} catch {}
|
|
4592
|
+
}
|
|
4593
|
+
if (!replyText) {
|
|
4594
|
+
replyText = buildExecutionFailureReplyText(detail, {
|
|
4595
|
+
intentType: resolvedIntentType,
|
|
4596
|
+
});
|
|
4597
|
+
}
|
|
4419
4598
|
if (!replyText) {
|
|
4420
4599
|
return null;
|
|
4421
4600
|
}
|
|
@@ -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({
|