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 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: String(normalizedIntent || "").trim().toLowerCase(),
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
- const request = safeObject(normalizeBotRunnerRequests(currentState.requests)[key]);
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
- if (!rootWorkItemClaim.ok) {
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: String(rootWorkItemClaim.error || rootWorkItemClaim.reason || "root_work_item_create_failed").trim(),
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: String(rootWorkItemClaim.error || rootWorkItemClaim.reason || "root_work_item_create_failed").trim() || "root_work_item_create_failed",
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: String(rootWorkItemClaim.error || rootWorkItemClaim.reason || "root_work_item_create_failed").trim() || "root_work_item_create_failed",
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: String(rootWorkItemClaim.reason || "").trim(),
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
- if (!rootWorkItemClaim.ok) {
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: String(rootWorkItemClaim.error || rootWorkItemClaim.reason || "root_work_item_create_failed").trim(),
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: String(rootWorkItemClaim.error || rootWorkItemClaim.reason || "root_work_item_create_failed").trim() || "root_work_item_create_failed",
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: String(rootWorkItemClaim.error || rootWorkItemClaim.reason || "root_work_item_create_failed").trim() || "root_work_item_create_failed",
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: String(rootWorkItemClaim.reason || "").trim(),
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,
@@ -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: String(raw.root_work_item_id || "").trim(),
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]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.198",
3
+ "version": "0.2.200",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [