metheus-governance-mcp-cli 0.2.196 → 0.2.197

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
@@ -2363,6 +2363,16 @@ function buildRunnerRequestKey({
2363
2363
  ].join("::");
2364
2364
  }
2365
2365
 
2366
+ function buildSyntheticReplyChainConversationID(normalizedRoute, chatID, anchorMessageID) {
2367
+ const provider = String(normalizedRoute?.provider || "").trim() || "unknown";
2368
+ const normalizedChatID = String(chatID || "").trim() || "-";
2369
+ const normalizedAnchorMessageID = intFromRawAllowZero(anchorMessageID, 0);
2370
+ if (normalizedAnchorMessageID <= 0) {
2371
+ return "";
2372
+ }
2373
+ return `reply_chain:${provider}:${normalizedChatID}:${normalizedAnchorMessageID}`;
2374
+ }
2375
+
2366
2376
  function findRunnerRequestsForScope(state, normalizedRoute, selectors = {}) {
2367
2377
  const requests = normalizeBotRunnerRequests(state?.requests);
2368
2378
  const projectID = String(normalizedRoute?.projectID || "").trim();
@@ -2386,6 +2396,61 @@ function findRunnerRequestsForScope(state, normalizedRoute, selectors = {}) {
2386
2396
  });
2387
2397
  }
2388
2398
 
2399
+ function findRunnerRequestsForMessageID(state, normalizedRoute, selectors = {}) {
2400
+ const chatID = String(selectors.chatID || "").trim();
2401
+ const messageID = intFromRawAllowZero(selectors.messageID, 0);
2402
+ if (!chatID || messageID <= 0) {
2403
+ return [];
2404
+ }
2405
+ return findRunnerRequestsForScope(state, normalizedRoute, { chatID })
2406
+ .filter((entry) => (
2407
+ intFromRawAllowZero(entry.source_message_id, 0) === messageID
2408
+ || intFromRawAllowZero(entry.last_source_message_id, 0) === messageID
2409
+ ));
2410
+ }
2411
+
2412
+ function resolveRunnerReplyChainConversationContext(state, normalizedRoute, selectedRecord) {
2413
+ const parsed = safeObject(selectedRecord?.parsedArchive);
2414
+ const explicitConversationID = String(parsed.conversationID || "").trim();
2415
+ if (explicitConversationID) {
2416
+ return {
2417
+ conversationID: explicitConversationID,
2418
+ replyToMessageID: intFromRawAllowZero(parsed.replyToMessageID, 0),
2419
+ anchorMessageID: 0,
2420
+ reason: "archive_conversation",
2421
+ referencedRequest: null,
2422
+ };
2423
+ }
2424
+ const chatID = String(parsed.chatID || parsed.chatId || "").trim();
2425
+ const replyToMessageID = intFromRawAllowZero(parsed.replyToMessageID, 0);
2426
+ if (!chatID || replyToMessageID <= 0) {
2427
+ return {
2428
+ conversationID: "",
2429
+ replyToMessageID,
2430
+ anchorMessageID: 0,
2431
+ reason: "",
2432
+ referencedRequest: null,
2433
+ };
2434
+ }
2435
+ const referencedRequest = safeObject(findRunnerRequestsForMessageID(state, normalizedRoute, {
2436
+ chatID,
2437
+ messageID: replyToMessageID,
2438
+ })[0]);
2439
+ const referencedConversationID = String(referencedRequest.conversation_id || "").trim();
2440
+ const anchorMessageID = intFromRawAllowZero(referencedRequest.source_message_id, 0) || replyToMessageID;
2441
+ return {
2442
+ conversationID: referencedConversationID || buildSyntheticReplyChainConversationID(normalizedRoute, chatID, anchorMessageID),
2443
+ replyToMessageID,
2444
+ anchorMessageID,
2445
+ reason: referencedConversationID
2446
+ ? "reply_request_conversation"
2447
+ : Object.keys(referencedRequest).length > 0
2448
+ ? "reply_request_synthetic"
2449
+ : "reply_message_synthetic",
2450
+ referencedRequest: Object.keys(referencedRequest).length > 0 ? referencedRequest : null,
2451
+ };
2452
+ }
2453
+
2389
2454
  function upsertRunnerRequest(state, requestKey, patch = {}) {
2390
2455
  const currentState = safeObject(state);
2391
2456
  const requests = normalizeBotRunnerRequests(currentState.requests);
@@ -2447,7 +2512,24 @@ function claimRunnerRequestForHumanComment({
2447
2512
  normalizedIntent,
2448
2513
  });
2449
2514
  const currentState = loadBotRunnerState();
2450
- const requests = normalizeBotRunnerRequests(currentState.requests);
2515
+ const replyChainContext = resolveRunnerReplyChainConversationContext(currentState, normalizedRoute, selectedRecord);
2516
+ const conversationID = String(parsed.conversationID || replyChainContext.conversationID || "").trim();
2517
+ let stateForClaim = currentState;
2518
+ if (
2519
+ replyChainContext.referencedRequest
2520
+ && conversationID
2521
+ && !String(replyChainContext.referencedRequest.conversation_id || "").trim()
2522
+ && String(replyChainContext.referencedRequest.request_key || "").trim()
2523
+ ) {
2524
+ const backfilled = upsertRunnerRequest(stateForClaim, replyChainContext.referencedRequest.request_key, {
2525
+ conversation_id: conversationID,
2526
+ });
2527
+ stateForClaim = {
2528
+ ...stateForClaim,
2529
+ requests: backfilled.requests,
2530
+ };
2531
+ }
2532
+ const requests = normalizeBotRunnerRequests(stateForClaim.requests);
2451
2533
  const existing = safeObject(requests[requestKey]);
2452
2534
  if (isFinalRunnerRequestStatus(existing.status)) {
2453
2535
  return {
@@ -2468,14 +2550,14 @@ function claimRunnerRequestForHumanComment({
2468
2550
  };
2469
2551
  }
2470
2552
  const nowISO = new Date().toISOString();
2471
- const { requests: nextRequests, request } = upsertRunnerRequest(currentState, requestKey, {
2553
+ const { requests: nextRequests, request } = upsertRunnerRequest(stateForClaim, requestKey, {
2472
2554
  project_id: String(normalizedRoute?.projectID || "").trim(),
2473
2555
  provider: String(normalizedRoute?.provider || "").trim(),
2474
2556
  chat_id: String(parsed.chatID || parsed.chatId || "").trim(),
2475
2557
  source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
2476
2558
  root_comment_id: String(selectedRecord?.id || "").trim(),
2477
2559
  root_comment_kind: commentKind,
2478
- conversation_id: String(parsed.conversationID || "").trim(),
2560
+ conversation_id: conversationID,
2479
2561
  selected_bot_usernames: uniqueOrderedStrings(selectedBotUsernames, normalizeTelegramMentionUsername),
2480
2562
  normalized_intent: String(normalizedIntent || "").trim().toLowerCase(),
2481
2563
  status: "claimed",
@@ -2485,20 +2567,20 @@ function claimRunnerRequestForHumanComment({
2485
2567
  last_comment_kind: commentKind,
2486
2568
  last_source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
2487
2569
  });
2488
- const { consumedComments: nextConsumedComments } = upsertRunnerConsumedComment(currentState, selectedRecord?.id, {
2570
+ const { consumedComments: nextConsumedComments } = upsertRunnerConsumedComment(stateForClaim, selectedRecord?.id, {
2489
2571
  project_id: String(normalizedRoute?.projectID || "").trim(),
2490
2572
  provider: String(normalizedRoute?.provider || "").trim(),
2491
2573
  request_key: requestKey,
2492
2574
  route_key: String(routeKey || "").trim(),
2493
- conversation_id: String(parsed.conversationID || "").trim(),
2575
+ conversation_id: conversationID,
2494
2576
  source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
2495
2577
  comment_kind: commentKind,
2496
2578
  request_status: "claimed",
2497
2579
  });
2498
2580
  saveBotRunnerState({
2499
- routes: currentState.routes,
2500
- sharedInboxes: currentState.sharedInboxes || currentState.shared_inboxes,
2501
- excludedComments: currentState.excludedComments || currentState.excluded_comments,
2581
+ routes: stateForClaim.routes,
2582
+ sharedInboxes: stateForClaim.sharedInboxes || stateForClaim.shared_inboxes,
2583
+ excludedComments: stateForClaim.excludedComments || stateForClaim.excluded_comments,
2502
2584
  requests: nextRequests,
2503
2585
  consumedComments: nextConsumedComments,
2504
2586
  });
@@ -5149,20 +5231,77 @@ function summarizeRunnerRequestForStatusLookup(entryRaw) {
5149
5231
  return {
5150
5232
  status: String(entry.status || "").trim(),
5151
5233
  normalized_intent: String(entry.normalized_intent || "").trim(),
5234
+ conversation_id: String(entry.conversation_id || "").trim(),
5235
+ closed_reason: String(entry.closed_reason || "").trim(),
5152
5236
  claimed_at: firstNonEmptyString([entry.claimed_at, entry.started_at]),
5153
5237
  started_at: firstNonEmptyString([entry.started_at, entry.claimed_at]),
5154
5238
  updated_at: String(entry.updated_at || "").trim(),
5155
5239
  source_message_id: intFromRawAllowZero(entry.source_message_id, 0) || undefined,
5240
+ last_source_message_id: intFromRawAllowZero(entry.last_source_message_id, 0) || undefined,
5156
5241
  selected_bot_usernames: ensureArray(entry.selected_bot_usernames)
5157
5242
  .map((value) => normalizeTelegramMentionUsername(value))
5158
5243
  .filter(Boolean),
5159
5244
  };
5160
5245
  }
5161
5246
 
5247
+ function isInformationalRunnerRequestIntent(intentType) {
5248
+ return new Set([
5249
+ "status_query",
5250
+ "bot_role_query",
5251
+ "workspace_query",
5252
+ "explanation_query",
5253
+ "small_talk",
5254
+ ]).has(String(intentType || "").trim().toLowerCase());
5255
+ }
5256
+
5257
+ function requestEligibleForStatusLookup(entryRaw, routeKey, selfBotUsername, currentMessageID) {
5258
+ const entry = safeObject(entryRaw);
5259
+ const sourceMessageID = intFromRawAllowZero(entry.source_message_id, 0);
5260
+ const lastSourceMessageID = intFromRawAllowZero(entry.last_source_message_id, 0);
5261
+ if (
5262
+ (currentMessageID > 0 && sourceMessageID === currentMessageID)
5263
+ || (currentMessageID > 0 && lastSourceMessageID === currentMessageID)
5264
+ ) {
5265
+ return false;
5266
+ }
5267
+ if (String(entry.claimed_by_route || "").trim() === routeKey) {
5268
+ return true;
5269
+ }
5270
+ if (!selfBotUsername) {
5271
+ return true;
5272
+ }
5273
+ return ensureArray(entry.selected_bot_usernames)
5274
+ .map((value) => normalizeTelegramMentionUsername(value))
5275
+ .filter(Boolean)
5276
+ .includes(selfBotUsername);
5277
+ }
5278
+
5279
+ function pickPreferredStatusLookupRequest(entries = []) {
5280
+ const candidates = ensureArray(entries).map((entry) => safeObject(entry)).filter((entry) => Object.keys(entry).length > 0);
5281
+ if (!candidates.length) {
5282
+ return null;
5283
+ }
5284
+ const activeNonInformational = candidates.filter((entry) => (
5285
+ isActiveRunnerRequestStatus(entry.status)
5286
+ && !isInformationalRunnerRequestIntent(entry.normalized_intent)
5287
+ ));
5288
+ if (activeNonInformational.length) {
5289
+ return activeNonInformational[0];
5290
+ }
5291
+ const nonInformational = candidates.filter((entry) => !isInformationalRunnerRequestIntent(entry.normalized_intent));
5292
+ if (nonInformational.length) {
5293
+ return nonInformational[0];
5294
+ }
5295
+ const activeAny = candidates.filter((entry) => isActiveRunnerRequestStatus(entry.status));
5296
+ if (activeAny.length) {
5297
+ return activeAny[0];
5298
+ }
5299
+ return candidates[0];
5300
+ }
5301
+
5162
5302
  function buildRunnerStatusQueryLookup({ route, routeState, selectedRecord }) {
5163
5303
  const parsed = safeObject(selectedRecord?.parsedArchive);
5164
5304
  const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
5165
- const currentConversationID = String(parsed.conversationID || "").trim();
5166
5305
  const currentChatID = String(parsed.chatID || parsed.chatId || "").trim();
5167
5306
  const routeKey = runnerRouteKey(route);
5168
5307
  const selfBotUsername = normalizeTelegramMentionUsername(firstNonEmptyString([
@@ -5179,34 +5318,52 @@ function buildRunnerStatusQueryLookup({ route, routeState, selectedRecord }) {
5179
5318
  && activeSourceMessageID > 0
5180
5319
  && currentMessageID === activeSourceMessageID
5181
5320
  );
5182
- let relatedActiveRequest = null;
5321
+ let runnerState = { requests: {} };
5183
5322
  try {
5184
- const runnerState = loadBotRunnerState();
5185
- const selectors = currentConversationID
5186
- ? { conversationID: currentConversationID, chatID: currentChatID }
5187
- : { chatID: currentChatID };
5188
- let scopedRequests = findRunnerRequestsForScope(runnerState, route, selectors);
5189
- if (!scopedRequests.length && currentConversationID) {
5190
- scopedRequests = findRunnerRequestsForScope(runnerState, route, { chatID: currentChatID });
5191
- }
5192
- relatedActiveRequest = scopedRequests
5193
- .filter((entry) => isActiveRunnerRequestStatus(entry.status))
5194
- .filter((entry) => intFromRawAllowZero(entry.source_message_id, 0) !== currentMessageID)
5195
- .filter((entry) => {
5196
- if (String(entry.claimed_by_route || "").trim() === routeKey) return true;
5197
- if (!selfBotUsername) return true;
5198
- return ensureArray(entry.selected_bot_usernames)
5199
- .map((value) => normalizeTelegramMentionUsername(value))
5200
- .filter(Boolean)
5201
- .includes(selfBotUsername);
5202
- })[0] || null;
5323
+ runnerState = loadBotRunnerState();
5203
5324
  } catch {}
5325
+ const replyChainContext = resolveRunnerReplyChainConversationContext(runnerState, route, selectedRecord);
5326
+ const currentConversationID = String(parsed.conversationID || replyChainContext.conversationID || "").trim();
5327
+ const requestMatchesCurrentRoute = (entry) => requestEligibleForStatusLookup(
5328
+ entry,
5329
+ routeKey,
5330
+ selfBotUsername,
5331
+ currentMessageID,
5332
+ );
5333
+ let relatedActiveRequest = null;
5334
+ let relatedRequest = null;
5335
+ const selectors = currentConversationID
5336
+ ? { conversationID: currentConversationID, chatID: currentChatID }
5337
+ : { chatID: currentChatID };
5338
+ let scopedRequests = findRunnerRequestsForScope(runnerState, route, selectors);
5339
+ if (!scopedRequests.length && currentConversationID) {
5340
+ scopedRequests = findRunnerRequestsForScope(runnerState, route, { chatID: currentChatID });
5341
+ }
5342
+ const eligibleScopedRequests = scopedRequests.filter(requestMatchesCurrentRoute);
5343
+ relatedActiveRequest = eligibleScopedRequests
5344
+ .filter((entry) => isActiveRunnerRequestStatus(entry.status))[0] || null;
5345
+ relatedRequest = pickPreferredStatusLookupRequest(
5346
+ eligibleScopedRequests.length
5347
+ ? eligibleScopedRequests
5348
+ : (replyChainContext.referencedRequest ? [replyChainContext.referencedRequest] : []).filter(requestMatchesCurrentRoute),
5349
+ );
5204
5350
  const lastAction = String(safeObject(routeState).last_action || "").trim();
5205
5351
  const lastReason = String(safeObject(routeState).last_reason || "").trim();
5206
5352
  const lastIntentType = String(safeObject(routeState).last_intent_type || "").trim();
5353
+ const routeConversationID = String(safeObject(routeState).last_conversation_id || "").trim();
5354
+ const routeWorkItemIDs = ensureArray(safeObject(routeState).last_work_item_ids).map((item) => String(item || "").trim()).filter(Boolean);
5355
+ const routeWorkItemTitles = ensureArray(safeObject(routeState).last_work_item_titles).map((item) => String(item || "").trim()).filter(Boolean);
5207
5356
  return {
5208
5357
  kind: "runner_status",
5209
- status: (!selfBusyFiltered && activeExecution.active) || relatedActiveRequest ? "running" : "idle",
5358
+ status: (!selfBusyFiltered && activeExecution.active) || relatedActiveRequest
5359
+ ? "running"
5360
+ : String(safeObject(relatedRequest).status || "").trim() || "idle",
5361
+ resolved_conversation_id: currentConversationID,
5362
+ reply_chain_resolution: {
5363
+ reason: String(replyChainContext.reason || "").trim(),
5364
+ reply_to_message_id: intFromRawAllowZero(replyChainContext.replyToMessageID, 0) || undefined,
5365
+ anchor_message_id: intFromRawAllowZero(replyChainContext.anchorMessageID, 0) || undefined,
5366
+ },
5210
5367
  self_busy_filtered: selfBusyFiltered,
5211
5368
  active_execution: activeExecution.active && !selfBusyFiltered
5212
5369
  ? {
@@ -5218,6 +5375,13 @@ function buildRunnerStatusQueryLookup({ route, routeState, selectedRecord }) {
5218
5375
  }
5219
5376
  : null,
5220
5377
  related_active_request: relatedActiveRequest ? summarizeRunnerRequestForStatusLookup(relatedActiveRequest) : null,
5378
+ related_request: relatedRequest ? summarizeRunnerRequestForStatusLookup(relatedRequest) : null,
5379
+ route_work_items: currentConversationID && routeConversationID === currentConversationID && (routeWorkItemIDs.length > 0 || routeWorkItemTitles.length > 0)
5380
+ ? {
5381
+ ids: routeWorkItemIDs,
5382
+ titles: routeWorkItemTitles,
5383
+ }
5384
+ : null,
5221
5385
  last_route_result: {
5222
5386
  action: lastAction,
5223
5387
  reason: lastReason,
@@ -12966,6 +13130,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
12966
13130
  runnerRouteLogicalSignature,
12967
13131
  loadBotRunnerState,
12968
13132
  saveBotRunnerState,
13133
+ buildRunnerStatusQueryLookup,
12969
13134
  tryJsonParse,
12970
13135
  safeObject,
12971
13136
  normalizeRunnerTriggerPolicy,
@@ -46,6 +46,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
46
46
  const runnerRouteLogicalSignature = requireDependency(deps, "runnerRouteLogicalSignature");
47
47
  const loadBotRunnerState = requireDependency(deps, "loadBotRunnerState");
48
48
  const saveBotRunnerState = requireDependency(deps, "saveBotRunnerState");
49
+ const buildRunnerStatusQueryLookup = requireDependency(deps, "buildRunnerStatusQueryLookup");
49
50
  const claimRunnerRequestForHumanComment = requireDependency(deps, "claimRunnerRequestForHumanComment");
50
51
  const markRunnerRequestLifecycle = requireDependency(deps, "markRunnerRequestLifecycle");
51
52
  const resolveRunnerContinuationRequestForBotReply = requireDependency(deps, "resolveRunnerContinuationRequestForBotReply");
@@ -1424,10 +1425,106 @@ export async function runSelftestRunnerScenarios(push, deps) {
1424
1425
  `request=${String(claimed.requestKey || "(none)")} status=${String(claimedRequest.status || "(none)")} consumed=${String(claimedConsumed.request_key || "(none)")}`,
1425
1426
  );
1426
1427
 
1428
+ const rootTaskRecord = {
1429
+ id: "comment-request-root-task-1",
1430
+ createdAt: "2026-03-22T00:05:00.000Z",
1431
+ updatedAt: "2026-03-22T00:05:00.000Z",
1432
+ parsedArchive: {
1433
+ kind: "telegram_message",
1434
+ chatID: "-100123",
1435
+ chatType: "supergroup",
1436
+ body: "@RyoAI_bot remove the implementation guide entry",
1437
+ messageID: 601,
1438
+ senderIsBot: false,
1439
+ },
1440
+ };
1441
+ const rootTaskClaim = claimRunnerRequestForHumanComment({
1442
+ normalizedRoute: requestRoute,
1443
+ routeKey: requestRouteKey,
1444
+ selectedRecord: rootTaskRecord,
1445
+ selectedBotUsernames: ["ryoai_bot"],
1446
+ normalizedIntent: "ctxpack_mutation",
1447
+ });
1448
+ const replyTaskRecord = {
1449
+ id: "comment-request-root-task-2",
1450
+ createdAt: "2026-03-22T00:06:00.000Z",
1451
+ updatedAt: "2026-03-22T00:06:00.000Z",
1452
+ parsedArchive: {
1453
+ kind: "telegram_message",
1454
+ chatID: "-100123",
1455
+ chatType: "supergroup",
1456
+ body: "@RyoAI_bot did you handle it?",
1457
+ messageID: 602,
1458
+ replyToMessageID: 601,
1459
+ senderIsBot: false,
1460
+ mentionUsernames: ["ryoai_bot"],
1461
+ },
1462
+ };
1463
+ const replyTaskClaim = claimRunnerRequestForHumanComment({
1464
+ normalizedRoute: requestRoute,
1465
+ routeKey: requestRouteKey,
1466
+ selectedRecord: replyTaskRecord,
1467
+ selectedBotUsernames: ["ryoai_bot"],
1468
+ normalizedIntent: "status_query",
1469
+ });
1470
+ const syntheticConversationID = "reply_chain:telegram:-100123:601";
1471
+ const replyChainState = loadBotRunnerState();
1472
+ const rootTaskRequest = safeObject(safeObject(replyChainState.requests)[rootTaskClaim.requestKey]);
1473
+ const replyTaskRequest = safeObject(safeObject(replyChainState.requests)[replyTaskClaim.requestKey]);
1474
+ push(
1475
+ "runner_request_reply_chain_assigns_synthetic_conversation",
1476
+ replyTaskClaim.ok === true
1477
+ && String(rootTaskRequest.conversation_id || "") === syntheticConversationID
1478
+ && String(replyTaskRequest.conversation_id || "") === syntheticConversationID,
1479
+ `root_conversation=${String(rootTaskRequest.conversation_id || "(none)")} reply_conversation=${String(replyTaskRequest.conversation_id || "(none)")}`,
1480
+ );
1481
+
1482
+ markRunnerRequestLifecycle({
1483
+ normalizedRoute: requestRoute,
1484
+ requestKey: rootTaskClaim.requestKey,
1485
+ selectedRecord: rootTaskRecord,
1486
+ routeKey: requestRouteKey,
1487
+ outcome: "replied",
1488
+ conversationIDRaw: syntheticConversationID,
1489
+ normalizedIntent: "ctxpack_mutation",
1490
+ });
1491
+ const replyChainLookup = buildRunnerStatusQueryLookup({
1492
+ route: requestRoute,
1493
+ routeState: {
1494
+ last_conversation_id: syntheticConversationID,
1495
+ last_work_item_ids: ["work-item-601"],
1496
+ last_work_item_titles: ["Remove implementation guide entry"],
1497
+ },
1498
+ selectedRecord: {
1499
+ id: "comment-request-root-task-3",
1500
+ parsedArchive: {
1501
+ kind: "telegram_message",
1502
+ chatID: "-100123",
1503
+ chatType: "supergroup",
1504
+ body: "@RyoAI_bot was that task done?",
1505
+ messageID: 603,
1506
+ replyToMessageID: 602,
1507
+ senderIsBot: false,
1508
+ mentionUsernames: ["ryoai_bot"],
1509
+ },
1510
+ },
1511
+ });
1512
+ push(
1513
+ "status_query_lookup_resolves_reply_chain_conversation_context",
1514
+ String(replyChainLookup.resolved_conversation_id || "") === syntheticConversationID
1515
+ && String(safeObject(replyChainLookup.related_request).normalized_intent || "") === "ctxpack_mutation"
1516
+ && String(safeObject(replyChainLookup.related_request).source_message_id || "") === "601"
1517
+ && String(safeObject(replyChainLookup.route_work_items).ids?.[0] || "") === "work-item-601",
1518
+ `conversation=${String(replyChainLookup.resolved_conversation_id || "(none)")} intent=${String(safeObject(replyChainLookup.related_request).normalized_intent || "(none)")} source_message=${String(safeObject(replyChainLookup.related_request).source_message_id || "(none)")} work_item=${String(safeObject(replyChainLookup.route_work_items).ids?.[0] || "(none)")}`,
1519
+ );
1520
+
1521
+ const stateBeforeCleanup = loadBotRunnerState();
1427
1522
  saveBotRunnerState({
1428
- ...claimedState,
1523
+ ...stateBeforeCleanup,
1429
1524
  routes: {
1525
+ ...safeObject(stateBeforeCleanup.routes),
1430
1526
  [requestRouteKey]: {
1527
+ ...safeObject(safeObject(stateBeforeCleanup.routes)[requestRouteKey]),
1431
1528
  conversation_sessions: {
1432
1529
  "conv-stale-1": {
1433
1530
  status: "open",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.196",
3
+ "version": "0.2.197",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [