metheus-governance-mcp-cli 0.2.198 → 0.2.200
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 +253 -21
- package/lib/runner-data.mjs +7 -6
- package/lib/selftest-runner-scenarios.mjs +55 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -2386,6 +2386,66 @@ function buildRunnerRequestKey({
|
|
|
2386
2386
|
].join("::");
|
|
2387
2387
|
}
|
|
2388
2388
|
|
|
2389
|
+
function looksLikeRunnerClaimQuestion(rawText) {
|
|
2390
|
+
return /[??]/.test(String(rawText || ""));
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
function inferRunnerRequestClaimIntent(selectedRecord) {
|
|
2394
|
+
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
2395
|
+
const commentKind = String(parsed.kind || "").trim().toLowerCase();
|
|
2396
|
+
if (!isInboundArchiveKind(commentKind)) {
|
|
2397
|
+
return "";
|
|
2398
|
+
}
|
|
2399
|
+
const rawText = String(parsed.body || "").trim();
|
|
2400
|
+
const normalizedText = rawText.toLowerCase();
|
|
2401
|
+
const replyToMessageID = intFromRawAllowZero(parsed.replyToMessageID, 0);
|
|
2402
|
+
if (!rawText) {
|
|
2403
|
+
return "";
|
|
2404
|
+
}
|
|
2405
|
+
if (/^(hi|hello|hey|thanks|thank you|good morning|good afternoon|good evening)\b/.test(normalizedText)) {
|
|
2406
|
+
return "small_talk";
|
|
2407
|
+
}
|
|
2408
|
+
if (/\b(bot role|your role|what do you do|who are you|which bot|who should respond)\b/.test(normalizedText)) {
|
|
2409
|
+
return "bot_role_query";
|
|
2410
|
+
}
|
|
2411
|
+
if (/\b(workspace|working directory|workdir|project folder|local folder)\b/.test(normalizedText)) {
|
|
2412
|
+
return "workspace_query";
|
|
2413
|
+
}
|
|
2414
|
+
if (
|
|
2415
|
+
(/\b(where|path|locate|find)\b/.test(normalizedText) || /\.[a-z0-9]{1,8}\b/.test(normalizedText))
|
|
2416
|
+
&& /\b(file|folder|path|doc|guide|readme|workspace)\b/.test(normalizedText)
|
|
2417
|
+
) {
|
|
2418
|
+
return "artifact_location_query";
|
|
2419
|
+
}
|
|
2420
|
+
if (/\b(why|explain|what is|what does|how does|describe)\b/.test(normalizedText) && looksLikeRunnerClaimQuestion(rawText)) {
|
|
2421
|
+
return "explanation_query";
|
|
2422
|
+
}
|
|
2423
|
+
if (
|
|
2424
|
+
/\b(status|progress|done|finished|complete|completed|working on|current work|did you|handled|handle it|what are you working|check status)\b/
|
|
2425
|
+
.test(normalizedText)
|
|
2426
|
+
) {
|
|
2427
|
+
return "status_query";
|
|
2428
|
+
}
|
|
2429
|
+
if (replyToMessageID > 0 && looksLikeRunnerClaimQuestion(rawText)) {
|
|
2430
|
+
return "status_query";
|
|
2431
|
+
}
|
|
2432
|
+
if (looksLikeRunnerClaimQuestion(rawText) && normalizedText.split(/\s+/).filter(Boolean).length <= 8) {
|
|
2433
|
+
return "status_query";
|
|
2434
|
+
}
|
|
2435
|
+
return "general_execution";
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
function resolveRunnerRequestClaimIntent({
|
|
2439
|
+
normalizedIntent = "",
|
|
2440
|
+
selectedRecord,
|
|
2441
|
+
}) {
|
|
2442
|
+
const explicitIntent = String(normalizedIntent || "").trim().toLowerCase();
|
|
2443
|
+
if (explicitIntent) {
|
|
2444
|
+
return explicitIntent;
|
|
2445
|
+
}
|
|
2446
|
+
return inferRunnerRequestClaimIntent(selectedRecord);
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2389
2449
|
function buildSyntheticReplyChainConversationID(normalizedRoute, chatID, anchorMessageID) {
|
|
2390
2450
|
const provider = String(normalizedRoute?.provider || "").trim() || "unknown";
|
|
2391
2451
|
const normalizedChatID = String(chatID || "").trim() || "-";
|
|
@@ -2658,12 +2718,6 @@ async function claimRunnerRequestForHumanComment({
|
|
|
2658
2718
|
reason: "non_human_comment_cannot_create_request",
|
|
2659
2719
|
};
|
|
2660
2720
|
}
|
|
2661
|
-
const requestKey = buildRunnerRequestKey({
|
|
2662
|
-
normalizedRoute,
|
|
2663
|
-
selectedRecord,
|
|
2664
|
-
selectedBotUsernames,
|
|
2665
|
-
normalizedIntent,
|
|
2666
|
-
});
|
|
2667
2721
|
const currentState = loadBotRunnerState();
|
|
2668
2722
|
const replyChainResolution = await resolveRunnerReplyChainConversationContextWithServerFallback({
|
|
2669
2723
|
state: currentState,
|
|
@@ -2674,6 +2728,16 @@ async function claimRunnerRequestForHumanComment({
|
|
|
2674
2728
|
const replyChainContext = safeObject(replyChainResolution.replyChainContext);
|
|
2675
2729
|
const referencedRequest = safeObject(replyChainContext.referencedRequest);
|
|
2676
2730
|
const conversationID = String(parsed.conversationID || replyChainContext.conversationID || "").trim();
|
|
2731
|
+
const resolvedNormalizedIntent = resolveRunnerRequestClaimIntent({
|
|
2732
|
+
normalizedIntent,
|
|
2733
|
+
selectedRecord,
|
|
2734
|
+
});
|
|
2735
|
+
const requestKey = buildRunnerRequestKey({
|
|
2736
|
+
normalizedRoute,
|
|
2737
|
+
selectedRecord,
|
|
2738
|
+
selectedBotUsernames,
|
|
2739
|
+
normalizedIntent: resolvedNormalizedIntent,
|
|
2740
|
+
});
|
|
2677
2741
|
let stateForClaim = safeObject(replyChainResolution.state);
|
|
2678
2742
|
if (
|
|
2679
2743
|
Object.keys(referencedRequest).length > 0
|
|
@@ -2719,7 +2783,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
2719
2783
|
root_comment_kind: commentKind,
|
|
2720
2784
|
conversation_id: conversationID,
|
|
2721
2785
|
selected_bot_usernames: uniqueOrderedStrings(selectedBotUsernames, normalizeTelegramMentionUsername),
|
|
2722
|
-
normalized_intent:
|
|
2786
|
+
normalized_intent: resolvedNormalizedIntent,
|
|
2723
2787
|
status: "claimed",
|
|
2724
2788
|
claimed_by_route: String(routeKey || "").trim(),
|
|
2725
2789
|
claimed_at: firstNonEmptyString([existing.claimed_at, nowISO]) || nowISO,
|
|
@@ -2767,6 +2831,22 @@ function isActionableRunnerRequestIntent(rawIntent) {
|
|
|
2767
2831
|
return Boolean(normalizedIntent) && !isInformationalRunnerRequestIntent(normalizedIntent);
|
|
2768
2832
|
}
|
|
2769
2833
|
|
|
2834
|
+
function loadRunnerRequestByKey(requestKey) {
|
|
2835
|
+
const key = String(requestKey || "").trim();
|
|
2836
|
+
if (!key) {
|
|
2837
|
+
return {};
|
|
2838
|
+
}
|
|
2839
|
+
return safeObject(normalizeBotRunnerRequests(loadBotRunnerState().requests)[key]);
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
function actionableRunnerRequestMissingRootWorkItem(requestRaw) {
|
|
2843
|
+
const request = safeObject(requestRaw);
|
|
2844
|
+
return (
|
|
2845
|
+
isActionableRunnerRequestIntent(request.normalized_intent)
|
|
2846
|
+
&& !String(request.root_work_item_id || "").trim()
|
|
2847
|
+
);
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2770
2850
|
function truncateRunnerWorkItemTitleText(rawText, maxLength = 96) {
|
|
2771
2851
|
const text = String(rawText || "").replace(/\s+/g, " ").trim();
|
|
2772
2852
|
if (!text) {
|
|
@@ -3234,6 +3314,81 @@ function buildRunnerRootWorkItemTransitionPath(currentStatusRaw, targetStatusRaw
|
|
|
3234
3314
|
return [];
|
|
3235
3315
|
}
|
|
3236
3316
|
|
|
3317
|
+
function buildRunnerRequestRecoveryPatchFromRouteState(currentStateRaw, requestRaw, routeKeyHint = "") {
|
|
3318
|
+
const currentState = safeObject(currentStateRaw);
|
|
3319
|
+
const request = safeObject(requestRaw);
|
|
3320
|
+
const requestKey = String(request.request_key || "").trim();
|
|
3321
|
+
if (!requestKey) {
|
|
3322
|
+
return {};
|
|
3323
|
+
}
|
|
3324
|
+
const requestSourceMessageID = intFromRawAllowZero(request.last_source_message_id, 0)
|
|
3325
|
+
|| intFromRawAllowZero(request.source_message_id, 0);
|
|
3326
|
+
const routeKeys = uniqueOrderedStrings(
|
|
3327
|
+
[
|
|
3328
|
+
routeKeyHint,
|
|
3329
|
+
String(request.claimed_by_route || "").trim(),
|
|
3330
|
+
],
|
|
3331
|
+
(value) => String(value || "").trim(),
|
|
3332
|
+
).filter(Boolean);
|
|
3333
|
+
for (const routeKey of routeKeys) {
|
|
3334
|
+
const routeState = safeObject(safeObject(currentState.routes)[routeKey]);
|
|
3335
|
+
if (!Object.keys(routeState).length) {
|
|
3336
|
+
continue;
|
|
3337
|
+
}
|
|
3338
|
+
const routeSourceMessageID = intFromRawAllowZero(routeState.last_source_message_id, 0)
|
|
3339
|
+
|| intFromRawAllowZero(routeState.source_message_id, 0);
|
|
3340
|
+
if (requestSourceMessageID && routeSourceMessageID && requestSourceMessageID !== routeSourceMessageID) {
|
|
3341
|
+
continue;
|
|
3342
|
+
}
|
|
3343
|
+
const patch = {};
|
|
3344
|
+
const recoveredRootWorkItemID = firstNonEmptyString([
|
|
3345
|
+
routeState.active_root_work_item_id,
|
|
3346
|
+
routeState.last_root_work_item_id,
|
|
3347
|
+
]);
|
|
3348
|
+
if (!String(request.root_work_item_id || "").trim() && recoveredRootWorkItemID) {
|
|
3349
|
+
patch.root_work_item_id = recoveredRootWorkItemID;
|
|
3350
|
+
patch.root_work_item_title = firstNonEmptyString([
|
|
3351
|
+
request.root_work_item_title,
|
|
3352
|
+
routeState.active_root_work_item_title,
|
|
3353
|
+
routeState.last_root_work_item_title,
|
|
3354
|
+
]);
|
|
3355
|
+
patch.root_work_item_status = normalizeRunnerWorkItemStatus(
|
|
3356
|
+
request.root_work_item_status
|
|
3357
|
+
|| routeState.active_root_work_item_status
|
|
3358
|
+
|| routeState.last_root_work_item_status,
|
|
3359
|
+
);
|
|
3360
|
+
}
|
|
3361
|
+
const requestStatus = normalizeRunnerRequestStatus(request.status);
|
|
3362
|
+
if (!isFinalRunnerRequestStatus(requestStatus)) {
|
|
3363
|
+
const routeAction = String(routeState.last_action || "").trim().toLowerCase();
|
|
3364
|
+
if (routeAction === "replied") {
|
|
3365
|
+
patch.status = "completed";
|
|
3366
|
+
patch.completed_at = firstNonEmptyString([
|
|
3367
|
+
request.completed_at,
|
|
3368
|
+
request.updated_at,
|
|
3369
|
+
new Date().toISOString(),
|
|
3370
|
+
]);
|
|
3371
|
+
} else if (routeAction === "error" || routeAction === "skipped" || routeAction === "closed") {
|
|
3372
|
+
patch.status = "closed";
|
|
3373
|
+
patch.closed_at = firstNonEmptyString([
|
|
3374
|
+
request.closed_at,
|
|
3375
|
+
request.updated_at,
|
|
3376
|
+
new Date().toISOString(),
|
|
3377
|
+
]);
|
|
3378
|
+
patch.closed_reason = firstNonEmptyString([
|
|
3379
|
+
request.closed_reason,
|
|
3380
|
+
routeState.last_reason,
|
|
3381
|
+
routeAction,
|
|
3382
|
+
]);
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
if (Object.keys(patch).length) {
|
|
3386
|
+
return patch;
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
return {};
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3237
3392
|
async function syncRunnerRequestRootWorkItemForOutcome({
|
|
3238
3393
|
normalizedRoute,
|
|
3239
3394
|
runtime,
|
|
@@ -3247,7 +3402,19 @@ async function syncRunnerRequestRootWorkItemForOutcome({
|
|
|
3247
3402
|
};
|
|
3248
3403
|
}
|
|
3249
3404
|
const currentState = loadBotRunnerState();
|
|
3250
|
-
|
|
3405
|
+
let request = safeObject(normalizeBotRunnerRequests(currentState.requests)[key]);
|
|
3406
|
+
const recoveryPatch = buildRunnerRequestRecoveryPatchFromRouteState(currentState, request);
|
|
3407
|
+
if (Object.keys(recoveryPatch).length) {
|
|
3408
|
+
const recovered = upsertRunnerRequest(currentState, key, recoveryPatch);
|
|
3409
|
+
request = safeObject(recovered.request);
|
|
3410
|
+
saveBotRunnerState({
|
|
3411
|
+
routes: currentState.routes,
|
|
3412
|
+
sharedInboxes: currentState.sharedInboxes || currentState.shared_inboxes,
|
|
3413
|
+
excludedComments: currentState.excludedComments || currentState.excluded_comments,
|
|
3414
|
+
requests: recovered.requests,
|
|
3415
|
+
consumedComments: currentState.consumedComments || currentState.consumed_comments,
|
|
3416
|
+
});
|
|
3417
|
+
}
|
|
3251
3418
|
const rootWorkItemID = String(request.root_work_item_id || "").trim();
|
|
3252
3419
|
if (!rootWorkItemID) {
|
|
3253
3420
|
return {
|
|
@@ -3666,6 +3833,20 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
|
|
|
3666
3833
|
preserveLocalStringWhenServerBlank("root_thread_id");
|
|
3667
3834
|
preserveLocalStringWhenServerBlank("root_work_item_created_at");
|
|
3668
3835
|
preserveLocalStringWhenServerBlank("root_work_item_last_error");
|
|
3836
|
+
const localStatus = normalizeRunnerRequestStatus(localEntry.status);
|
|
3837
|
+
const serverStatus = normalizeRunnerRequestStatus(serverEntry.status);
|
|
3838
|
+
if (isFinalRunnerRequestStatus(localStatus) && !isFinalRunnerRequestStatus(serverStatus)) {
|
|
3839
|
+
merged.status = localStatus;
|
|
3840
|
+
if (String(localEntry.completed_at || "").trim()) {
|
|
3841
|
+
merged.completed_at = String(localEntry.completed_at || "").trim();
|
|
3842
|
+
}
|
|
3843
|
+
if (String(localEntry.closed_at || "").trim()) {
|
|
3844
|
+
merged.closed_at = String(localEntry.closed_at || "").trim();
|
|
3845
|
+
}
|
|
3846
|
+
if (String(localEntry.closed_reason || "").trim()) {
|
|
3847
|
+
merged.closed_reason = String(localEntry.closed_reason || "").trim();
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3669
3850
|
return merged;
|
|
3670
3851
|
}
|
|
3671
3852
|
|
|
@@ -3850,13 +4031,28 @@ async function syncRunnerRequestLedgerForProjectToServer({ normalizedRoute, runt
|
|
|
3850
4031
|
const commentStates = buildProjectRunnerRequestCommentStatesForSync(state, normalizedRoute);
|
|
3851
4032
|
|
|
3852
4033
|
for (const request of requests) {
|
|
4034
|
+
const recoveryPatch = buildRunnerRequestRecoveryPatchFromRouteState(state, request);
|
|
4035
|
+
const recoveredRequest = Object.keys(recoveryPatch).length
|
|
4036
|
+
? normalizeBotRunnerRequests({
|
|
4037
|
+
[String(request.request_key || "").trim()]: {
|
|
4038
|
+
...safeObject(request),
|
|
4039
|
+
...recoveryPatch,
|
|
4040
|
+
},
|
|
4041
|
+
})[String(request.request_key || "").trim()]
|
|
4042
|
+
: request;
|
|
4043
|
+
if (
|
|
4044
|
+
isActionableRunnerRequestIntent(recoveredRequest.normalized_intent)
|
|
4045
|
+
&& !String(recoveredRequest.root_work_item_id || "").trim()
|
|
4046
|
+
) {
|
|
4047
|
+
continue;
|
|
4048
|
+
}
|
|
3853
4049
|
await upsertProjectRunnerRequest({
|
|
3854
4050
|
siteBaseURL: runtime.baseURL,
|
|
3855
4051
|
projectID,
|
|
3856
4052
|
token: runtime.token,
|
|
3857
4053
|
timeoutSeconds: runtime.timeoutSeconds,
|
|
3858
4054
|
actorUserID: runtime.actor?.user_id,
|
|
3859
|
-
request,
|
|
4055
|
+
request: recoveredRequest,
|
|
3860
4056
|
});
|
|
3861
4057
|
}
|
|
3862
4058
|
for (const commentState of commentStates) {
|
|
@@ -7070,7 +7266,19 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
7070
7266
|
runtime,
|
|
7071
7267
|
requestKey: requestClaim.requestKey,
|
|
7072
7268
|
});
|
|
7073
|
-
|
|
7269
|
+
const claimedRequest = safeObject(
|
|
7270
|
+
loadRunnerRequestByKey(requestClaim.requestKey)
|
|
7271
|
+
|| rootWorkItemClaim.request
|
|
7272
|
+
|| inheritedRootReference.request
|
|
7273
|
+
|| requestClaim.request,
|
|
7274
|
+
);
|
|
7275
|
+
const missingRequiredRootWorkItem = actionableRunnerRequestMissingRootWorkItem(claimedRequest);
|
|
7276
|
+
if (!rootWorkItemClaim.ok || missingRequiredRootWorkItem) {
|
|
7277
|
+
const rootWorkItemFailure = String(
|
|
7278
|
+
rootWorkItemClaim.error
|
|
7279
|
+
|| rootWorkItemClaim.reason
|
|
7280
|
+
|| (missingRequiredRootWorkItem ? "root_work_item_missing" : "root_work_item_create_failed"),
|
|
7281
|
+
).trim() || "root_work_item_create_failed";
|
|
7074
7282
|
if (String(requestClaim.requestKey || "").trim()) {
|
|
7075
7283
|
markRunnerRequestLifecycle({
|
|
7076
7284
|
normalizedRoute,
|
|
@@ -7078,7 +7286,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
7078
7286
|
selectedRecord,
|
|
7079
7287
|
routeKey,
|
|
7080
7288
|
outcome: "closed",
|
|
7081
|
-
closedReason:
|
|
7289
|
+
closedReason: rootWorkItemFailure,
|
|
7082
7290
|
});
|
|
7083
7291
|
await syncRunnerRequestLedgerForProjectToServer({
|
|
7084
7292
|
normalizedRoute,
|
|
@@ -7089,22 +7297,21 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
7089
7297
|
routeKey,
|
|
7090
7298
|
buildRunnerRouteStateFromComment(selectedRecord, {
|
|
7091
7299
|
last_action: "request_skipped",
|
|
7092
|
-
last_reason:
|
|
7300
|
+
last_reason: rootWorkItemFailure,
|
|
7093
7301
|
last_trigger: "work_item_root",
|
|
7094
7302
|
last_request_key: String(requestClaim.requestKey || "").trim(),
|
|
7095
7303
|
}),
|
|
7096
7304
|
);
|
|
7097
7305
|
skippedRecords.push({
|
|
7098
7306
|
id: selectedRecord.id,
|
|
7099
|
-
reason:
|
|
7307
|
+
reason: rootWorkItemFailure,
|
|
7100
7308
|
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
7101
7309
|
diagnosticType: "skip",
|
|
7102
7310
|
action: "skip_missing_root_work_item",
|
|
7103
|
-
closedReason:
|
|
7311
|
+
closedReason: rootWorkItemFailure,
|
|
7104
7312
|
});
|
|
7105
7313
|
continue;
|
|
7106
7314
|
}
|
|
7107
|
-
const claimedRequest = safeObject(rootWorkItemClaim.request || inheritedRootReference.request || requestClaim.request);
|
|
7108
7315
|
saveRunnerRouteState(routeKey, {
|
|
7109
7316
|
...buildRunnerActiveExecutionPatch(selectedRecord, requestClaim.requestKey, {
|
|
7110
7317
|
id: String(claimedRequest.root_work_item_id || "").trim(),
|
|
@@ -7299,7 +7506,19 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
7299
7506
|
runtime,
|
|
7300
7507
|
requestKey: requestClaim.requestKey,
|
|
7301
7508
|
});
|
|
7302
|
-
|
|
7509
|
+
const claimedRequest = safeObject(
|
|
7510
|
+
loadRunnerRequestByKey(requestClaim.requestKey)
|
|
7511
|
+
|| rootWorkItemClaim.request
|
|
7512
|
+
|| inheritedRootReference.request
|
|
7513
|
+
|| requestClaim.request,
|
|
7514
|
+
);
|
|
7515
|
+
const missingRequiredRootWorkItem = actionableRunnerRequestMissingRootWorkItem(claimedRequest);
|
|
7516
|
+
if (!rootWorkItemClaim.ok || missingRequiredRootWorkItem) {
|
|
7517
|
+
const rootWorkItemFailure = String(
|
|
7518
|
+
rootWorkItemClaim.error
|
|
7519
|
+
|| rootWorkItemClaim.reason
|
|
7520
|
+
|| (missingRequiredRootWorkItem ? "root_work_item_missing" : "root_work_item_create_failed"),
|
|
7521
|
+
).trim() || "root_work_item_create_failed";
|
|
7303
7522
|
if (String(requestClaim.requestKey || "").trim()) {
|
|
7304
7523
|
markRunnerRequestLifecycle({
|
|
7305
7524
|
normalizedRoute,
|
|
@@ -7307,7 +7526,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
7307
7526
|
selectedRecord,
|
|
7308
7527
|
routeKey,
|
|
7309
7528
|
outcome: "closed",
|
|
7310
|
-
closedReason:
|
|
7529
|
+
closedReason: rootWorkItemFailure,
|
|
7311
7530
|
});
|
|
7312
7531
|
await syncRunnerRequestLedgerForProjectToServer({
|
|
7313
7532
|
normalizedRoute,
|
|
@@ -7318,22 +7537,21 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
7318
7537
|
routeKey,
|
|
7319
7538
|
buildRunnerRouteStateFromComment(selectedRecord, {
|
|
7320
7539
|
last_action: "request_skipped",
|
|
7321
|
-
last_reason:
|
|
7540
|
+
last_reason: rootWorkItemFailure,
|
|
7322
7541
|
last_trigger: "work_item_root",
|
|
7323
7542
|
last_request_key: String(requestClaim.requestKey || "").trim(),
|
|
7324
7543
|
}),
|
|
7325
7544
|
);
|
|
7326
7545
|
skippedRecords.push({
|
|
7327
7546
|
id: selectedRecord.id,
|
|
7328
|
-
reason:
|
|
7547
|
+
reason: rootWorkItemFailure,
|
|
7329
7548
|
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
7330
7549
|
diagnosticType: "skip",
|
|
7331
7550
|
action: "skip_missing_root_work_item",
|
|
7332
|
-
closedReason:
|
|
7551
|
+
closedReason: rootWorkItemFailure,
|
|
7333
7552
|
});
|
|
7334
7553
|
continue;
|
|
7335
7554
|
}
|
|
7336
|
-
const claimedRequest = safeObject(rootWorkItemClaim.request || inheritedRootReference.request || requestClaim.request);
|
|
7337
7555
|
saveRunnerRouteState(routeKey, {
|
|
7338
7556
|
...buildRunnerActiveExecutionPatch(selectedRecord, requestClaim.requestKey, {
|
|
7339
7557
|
id: String(claimedRequest.root_work_item_id || "").trim(),
|
|
@@ -9891,6 +10109,12 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
9891
10109
|
last_error: errorText,
|
|
9892
10110
|
});
|
|
9893
10111
|
if (String(deferredExecution.requestKey || "").trim()) {
|
|
10112
|
+
const currentRequest = safeObject(loadRunnerRequestByKey(deferredExecution.requestKey));
|
|
10113
|
+
const resolvedIntentType = String(
|
|
10114
|
+
safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]).last_intent_type
|
|
10115
|
+
|| currentRequest.normalized_intent
|
|
10116
|
+
|| "",
|
|
10117
|
+
).trim();
|
|
9894
10118
|
markRunnerRequestLifecycle({
|
|
9895
10119
|
normalizedRoute: deferredExecution.normalizedRoute,
|
|
9896
10120
|
requestKey: deferredExecution.requestKey,
|
|
@@ -9898,6 +10122,14 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
9898
10122
|
routeKey: deferredExecution.routeKey,
|
|
9899
10123
|
outcome: "error",
|
|
9900
10124
|
closedReason: errorText || "execution_error",
|
|
10125
|
+
normalizedIntent: resolvedIntentType,
|
|
10126
|
+
});
|
|
10127
|
+
await ensureRunnerRootWorkItemForRequest({
|
|
10128
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
10129
|
+
routeKey: deferredExecution.routeKey,
|
|
10130
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
10131
|
+
runtime: deferredExecution.runtime,
|
|
10132
|
+
requestKey: deferredExecution.requestKey,
|
|
9901
10133
|
});
|
|
9902
10134
|
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
9903
10135
|
normalizedRoute: deferredExecution.normalizedRoute,
|
package/lib/runner-data.mjs
CHANGED
|
@@ -379,6 +379,7 @@ export async function upsertProjectRunnerRequest(
|
|
|
379
379
|
const postJSONWithAuthHeaders = requireDependency(deps, "postJSONWithAuthHeaders");
|
|
380
380
|
const parseJSONText = requireDependency(deps, "parseJSONText");
|
|
381
381
|
const raw = safeObject(request);
|
|
382
|
+
const rootWorkItemID = String(raw.root_work_item_id || "").trim();
|
|
382
383
|
const payload = {
|
|
383
384
|
request_key: String(raw.request_key || "").trim(),
|
|
384
385
|
project_id: String(raw.project_id || "").trim(),
|
|
@@ -398,12 +399,12 @@ export async function upsertProjectRunnerRequest(
|
|
|
398
399
|
completed_at: String(raw.completed_at || "").trim() || undefined,
|
|
399
400
|
closed_at: String(raw.closed_at || "").trim() || undefined,
|
|
400
401
|
closed_reason: String(raw.closed_reason || "").trim(),
|
|
401
|
-
root_work_item_id:
|
|
402
|
-
root_work_item_title: String(raw.root_work_item_title || "").trim(),
|
|
403
|
-
root_work_item_status: String(raw.root_work_item_status || "").trim(),
|
|
404
|
-
root_thread_id: String(raw.root_thread_id || "").trim(),
|
|
405
|
-
root_work_item_created_at: String(raw.root_work_item_created_at || "").trim() || undefined,
|
|
406
|
-
root_work_item_last_error: String(raw.root_work_item_last_error || "").trim(),
|
|
402
|
+
root_work_item_id: rootWorkItemID,
|
|
403
|
+
root_work_item_title: rootWorkItemID ? String(raw.root_work_item_title || "").trim() : "",
|
|
404
|
+
root_work_item_status: rootWorkItemID ? String(raw.root_work_item_status || "").trim() : "",
|
|
405
|
+
root_thread_id: rootWorkItemID ? String(raw.root_thread_id || "").trim() : "",
|
|
406
|
+
root_work_item_created_at: rootWorkItemID ? String(raw.root_work_item_created_at || "").trim() || undefined : undefined,
|
|
407
|
+
root_work_item_last_error: rootWorkItemID ? String(raw.root_work_item_last_error || "").trim() : "",
|
|
407
408
|
last_comment_id: String(raw.last_comment_id || "").trim(),
|
|
408
409
|
last_comment_kind: String(raw.last_comment_kind || "").trim(),
|
|
409
410
|
last_source_message_id: Number.isFinite(Number(raw.last_source_message_id)) ? Number(raw.last_source_message_id) : undefined,
|
|
@@ -1426,6 +1426,33 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
1426
1426
|
`request=${String(claimed.requestKey || "(none)")} status=${String(claimedRequest.status || "(none)")} consumed=${String(claimedConsumed.request_key || "(none)")}`,
|
|
1427
1427
|
);
|
|
1428
1428
|
|
|
1429
|
+
const inferredExecutionRecord = {
|
|
1430
|
+
id: "comment-request-human-2",
|
|
1431
|
+
createdAt: "2026-03-22T00:01:00.000Z",
|
|
1432
|
+
updatedAt: "2026-03-22T00:01:00.000Z",
|
|
1433
|
+
parsedArchive: {
|
|
1434
|
+
kind: "telegram_message",
|
|
1435
|
+
chatID: "-100123",
|
|
1436
|
+
chatType: "supergroup",
|
|
1437
|
+
body: "@RyoAI_bot draft the project charter and outline the deliverables",
|
|
1438
|
+
messageID: 502,
|
|
1439
|
+
senderIsBot: false,
|
|
1440
|
+
},
|
|
1441
|
+
};
|
|
1442
|
+
const inferredExecutionClaim = await claimRunnerRequestForHumanComment({
|
|
1443
|
+
normalizedRoute: requestRoute,
|
|
1444
|
+
routeKey: requestRouteKey,
|
|
1445
|
+
selectedRecord: inferredExecutionRecord,
|
|
1446
|
+
selectedBotUsernames: ["ryoai_bot"],
|
|
1447
|
+
});
|
|
1448
|
+
const inferredExecutionRequest = safeObject(safeObject(loadBotRunnerState().requests)[inferredExecutionClaim.requestKey]);
|
|
1449
|
+
push(
|
|
1450
|
+
"runner_request_claim_defaults_actionable_messages_to_general_execution",
|
|
1451
|
+
inferredExecutionClaim.ok === true
|
|
1452
|
+
&& String(inferredExecutionRequest.normalized_intent || "") === "general_execution",
|
|
1453
|
+
`intent=${String(inferredExecutionRequest.normalized_intent || "(none)")} request=${String(inferredExecutionClaim.requestKey || "(none)")}`,
|
|
1454
|
+
);
|
|
1455
|
+
|
|
1429
1456
|
const rootTaskRecord = {
|
|
1430
1457
|
id: "comment-request-root-task-1",
|
|
1431
1458
|
createdAt: "2026-03-22T00:05:00.000Z",
|
|
@@ -1468,6 +1495,34 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
1468
1495
|
selectedBotUsernames: ["ryoai_bot"],
|
|
1469
1496
|
normalizedIntent: "status_query",
|
|
1470
1497
|
});
|
|
1498
|
+
const inferredReplyTaskRecord = {
|
|
1499
|
+
id: "comment-request-root-task-2b",
|
|
1500
|
+
createdAt: "2026-03-22T00:06:30.000Z",
|
|
1501
|
+
updatedAt: "2026-03-22T00:06:30.000Z",
|
|
1502
|
+
parsedArchive: {
|
|
1503
|
+
kind: "telegram_message",
|
|
1504
|
+
chatID: "-100123",
|
|
1505
|
+
chatType: "supergroup",
|
|
1506
|
+
body: "@RyoAI_bot did you handle it?",
|
|
1507
|
+
messageID: 6021,
|
|
1508
|
+
replyToMessageID: 601,
|
|
1509
|
+
senderIsBot: false,
|
|
1510
|
+
mentionUsernames: ["ryoai_bot"],
|
|
1511
|
+
},
|
|
1512
|
+
};
|
|
1513
|
+
const inferredReplyTaskClaim = await claimRunnerRequestForHumanComment({
|
|
1514
|
+
normalizedRoute: requestRoute,
|
|
1515
|
+
routeKey: requestRouteKey,
|
|
1516
|
+
selectedRecord: inferredReplyTaskRecord,
|
|
1517
|
+
selectedBotUsernames: ["ryoai_bot"],
|
|
1518
|
+
});
|
|
1519
|
+
const inferredReplyTaskRequest = safeObject(safeObject(loadBotRunnerState().requests)[inferredReplyTaskClaim.requestKey]);
|
|
1520
|
+
push(
|
|
1521
|
+
"runner_request_claim_infers_status_query_for_short_reply_questions",
|
|
1522
|
+
inferredReplyTaskClaim.ok === true
|
|
1523
|
+
&& String(inferredReplyTaskRequest.normalized_intent || "") === "status_query",
|
|
1524
|
+
`intent=${String(inferredReplyTaskRequest.normalized_intent || "(none)")} request=${String(inferredReplyTaskClaim.requestKey || "(none)")}`,
|
|
1525
|
+
);
|
|
1471
1526
|
const syntheticConversationID = "reply_chain:telegram:-100123:601";
|
|
1472
1527
|
const replyChainState = loadBotRunnerState();
|
|
1473
1528
|
const rootTaskRequest = safeObject(safeObject(replyChainState.requests)[rootTaskClaim.requestKey]);
|