metheus-governance-mcp-cli 0.2.178 → 0.2.180

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
@@ -4269,6 +4269,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
4269
4269
  bot,
4270
4270
  destination,
4271
4271
  archiveThread,
4272
+ managedConversationBots,
4272
4273
  deps: buildRunnerRuntimeDeps(),
4273
4274
  });
4274
4275
  saveRunnerRouteState(routeKey, {
@@ -42,6 +42,10 @@ function firstNonEmptyString(values) {
42
42
  return "";
43
43
  }
44
44
 
45
+ function normalizeMentionSelector(rawValue) {
46
+ return String(rawValue || "").trim().replace(/^@+/, "").toLowerCase();
47
+ }
48
+
45
49
  function normalizeRunnerSharedInboxKey({ provider, bot, route }) {
46
50
  const normalizedProvider = String(provider || route?.provider || "").trim().toLowerCase() || "telegram";
47
51
  const botSelector = firstNonEmptyString([
@@ -60,6 +64,66 @@ function normalizeRunnerSharedInboxKey({ provider, bot, route }) {
60
64
  return `${normalizedProvider}::${botSelector}`;
61
65
  }
62
66
 
67
+ function buildRouteBotUsernameCandidates(bot, route) {
68
+ return Array.from(new Set(
69
+ [
70
+ bot?.username,
71
+ bot?.name,
72
+ route?.botName,
73
+ route?.serverBotName,
74
+ ]
75
+ .map((value) => normalizeMentionSelector(value))
76
+ .filter(Boolean),
77
+ ));
78
+ }
79
+
80
+ function shouldImportManagedBotMessageForRoute({
81
+ update,
82
+ bot,
83
+ route,
84
+ managedConversationBots,
85
+ }) {
86
+ const normalizedUpdate = safeObject(update);
87
+ if (normalizedUpdate.fromIsBot !== true) {
88
+ return false;
89
+ }
90
+ const managedSelectors = new Set(
91
+ ensureArray(managedConversationBots)
92
+ .flatMap((entry) => [
93
+ safeObject(entry).username,
94
+ safeObject(entry).display_name,
95
+ safeObject(safeObject(entry).bot).username,
96
+ safeObject(safeObject(entry).bot).name,
97
+ safeObject(safeObject(entry).route).botName,
98
+ safeObject(safeObject(entry).route).serverBotName,
99
+ ])
100
+ .map((value) => normalizeMentionSelector(value))
101
+ .filter(Boolean),
102
+ );
103
+ const senderSelector = normalizeMentionSelector(normalizedUpdate.fromUsername);
104
+ if (!senderSelector || !managedSelectors.has(senderSelector)) {
105
+ return false;
106
+ }
107
+ const currentBotSelectors = new Set(buildRouteBotUsernameCandidates(bot, route));
108
+ if (!currentBotSelectors.size) {
109
+ return false;
110
+ }
111
+ if (currentBotSelectors.has(senderSelector)) {
112
+ return false;
113
+ }
114
+ const explicitMentions = ensureArray(normalizedUpdate.mentionUsernames)
115
+ .map((value) => normalizeMentionSelector(value))
116
+ .filter(Boolean);
117
+ if (explicitMentions.some((value) => currentBotSelectors.has(value))) {
118
+ return true;
119
+ }
120
+ const replyTargetSelector = normalizeMentionSelector(normalizedUpdate.replyToFromUsername);
121
+ if (normalizedUpdate.replyToFromIsBot === true && replyTargetSelector && currentBotSelectors.has(replyTargetSelector)) {
122
+ return true;
123
+ }
124
+ return false;
125
+ }
126
+
63
127
  function normalizeSharedInboxUpdate(rawUpdate) {
64
128
  const update = safeObject(rawUpdate);
65
129
  const updateID = intFromRawAllowZero(update.updateID ?? update.update_id, 0);
@@ -270,6 +334,7 @@ export async function archiveLocalTelegramMessagesForRoute({
270
334
  bot,
271
335
  destination,
272
336
  archiveThread,
337
+ managedConversationBots = [],
273
338
  deps,
274
339
  }) {
275
340
  const loadProviderEnvConfig = requireDependency(deps, "loadProviderEnvConfig");
@@ -407,7 +472,13 @@ export async function archiveLocalTelegramMessagesForRoute({
407
472
  handledUpdateID = Math.max(handledUpdateID, updateID);
408
473
  continue;
409
474
  }
410
- if (boolFromRaw(archivePolicy.skipBotMessages, true) && update.fromIsBot) {
475
+ const allowManagedBotImport = shouldImportManagedBotMessageForRoute({
476
+ update,
477
+ bot,
478
+ route,
479
+ managedConversationBots,
480
+ });
481
+ if (boolFromRaw(archivePolicy.skipBotMessages, true) && update.fromIsBot && !allowManagedBotImport) {
411
482
  handledUpdateID = Math.max(handledUpdateID, updateID);
412
483
  continue;
413
484
  }
@@ -602,15 +602,145 @@ export async function runSelftestTelegramE2E(push, deps) {
602
602
  deps: buildRunnerRuntimeDeps(),
603
603
  });
604
604
  const sharedInboxBodies = telegramE2EServer.state.comments.map((item) => String(item.body || ""));
605
- push(
606
- "telegram_shared_inbox_fans_out_updates_across_routes_for_same_bot",
607
- sharedInboxBodies.some((item) => item.includes("message_id: 71"))
608
- && sharedInboxBodies.some((item) => item.includes("message_id: 72")),
609
- `comments=${sharedInboxBodies.length} bodies=${sharedInboxBodies.join(" || ")}`,
610
- );
611
- } catch (err) {
612
- push("telegram_runner_e2e_local_mock", false, String(err?.message || err));
613
- } finally {
605
+ push(
606
+ "telegram_shared_inbox_fans_out_updates_across_routes_for_same_bot",
607
+ sharedInboxBodies.some((item) => item.includes("message_id: 71"))
608
+ && sharedInboxBodies.some((item) => item.includes("message_id: 72")),
609
+ `comments=${sharedInboxBodies.length} bodies=${sharedInboxBodies.join(" || ")}`,
610
+ );
611
+
612
+ telegramE2EServer.state.comments = [];
613
+ telegramE2EServer.state.updates = [
614
+ {
615
+ update_id: 401,
616
+ message: {
617
+ message_id: 81,
618
+ date: Math.floor(Date.now() / 1000),
619
+ chat: {
620
+ id: Number(e2eDestination.chat_id),
621
+ type: "supergroup",
622
+ title: e2eDestination.label,
623
+ },
624
+ from: {
625
+ id: 6001,
626
+ is_bot: true,
627
+ first_name: "Lead Bot",
628
+ username: "lead_monitor_bot",
629
+ },
630
+ text: `@${String("PeerMonitorBot").replace(/^@+/, "")} please summarize the project`,
631
+ },
632
+ },
633
+ ];
634
+ await archiveLocalTelegramMessagesForRoute({
635
+ routeKey: runnerRouteKey(normalizeRunnerRoute({
636
+ ...e2eRoute,
637
+ name: "selftest-runner-e2e-managed-bot-mention-import",
638
+ server_bot_name: "PeerMonitorBot",
639
+ })),
640
+ route: {
641
+ ...e2eRoute,
642
+ name: "selftest-runner-e2e-managed-bot-mention-import",
643
+ server_bot_name: "PeerMonitorBot",
644
+ },
645
+ routeState: {},
646
+ runtime: {
647
+ baseURL: telegramE2EServer.baseURL,
648
+ timeoutSeconds: 10,
649
+ token: e2eToken,
650
+ actor: {
651
+ user_id: e2eActorUserID,
652
+ },
653
+ },
654
+ bot: {
655
+ id: "66666666-6666-4666-8666-666666666666",
656
+ name: "PeerMonitorBot",
657
+ role: "monitor",
658
+ },
659
+ destination: {
660
+ chatID: e2eDestination.chat_id,
661
+ },
662
+ archiveThread: {
663
+ threadID: e2eThreadID,
664
+ },
665
+ managedConversationBots: [
666
+ { username: "lead_monitor_bot" },
667
+ { username: "PeerMonitorBot" },
668
+ ],
669
+ deps: buildRunnerRuntimeDeps(),
670
+ });
671
+ push(
672
+ "telegram_managed_bot_public_delegate_is_imported_even_when_skip_bot_messages_is_true",
673
+ telegramE2EServer.state.comments.some((item) => String(item.body || "").includes("message_id: 81")),
674
+ `comments=${telegramE2EServer.state.comments.length}`,
675
+ );
676
+
677
+ telegramE2EServer.state.comments = [];
678
+ telegramE2EServer.state.updates = [
679
+ {
680
+ update_id: 402,
681
+ message: {
682
+ message_id: 82,
683
+ date: Math.floor(Date.now() / 1000),
684
+ chat: {
685
+ id: Number(e2eDestination.chat_id),
686
+ type: "supergroup",
687
+ title: e2eDestination.label,
688
+ },
689
+ from: {
690
+ id: 6002,
691
+ is_bot: true,
692
+ first_name: "External Bot",
693
+ username: "external_random_bot",
694
+ },
695
+ text: `@${String("PeerMonitorBot").replace(/^@+/, "")} hello from outside`,
696
+ },
697
+ },
698
+ ];
699
+ await archiveLocalTelegramMessagesForRoute({
700
+ routeKey: runnerRouteKey(normalizeRunnerRoute({
701
+ ...e2eRoute,
702
+ name: "selftest-runner-e2e-unmanaged-bot-skip",
703
+ server_bot_name: "PeerMonitorBot",
704
+ })),
705
+ route: {
706
+ ...e2eRoute,
707
+ name: "selftest-runner-e2e-unmanaged-bot-skip",
708
+ server_bot_name: "PeerMonitorBot",
709
+ },
710
+ routeState: {},
711
+ runtime: {
712
+ baseURL: telegramE2EServer.baseURL,
713
+ timeoutSeconds: 10,
714
+ token: e2eToken,
715
+ actor: {
716
+ user_id: e2eActorUserID,
717
+ },
718
+ },
719
+ bot: {
720
+ id: "77777777-7777-4777-8777-777777777777",
721
+ name: "PeerMonitorBot",
722
+ role: "monitor",
723
+ },
724
+ destination: {
725
+ chatID: e2eDestination.chat_id,
726
+ },
727
+ archiveThread: {
728
+ threadID: e2eThreadID,
729
+ },
730
+ managedConversationBots: [
731
+ { username: "lead_monitor_bot" },
732
+ { username: "PeerMonitorBot" },
733
+ ],
734
+ deps: buildRunnerRuntimeDeps(),
735
+ });
736
+ push(
737
+ "telegram_unmanaged_bot_message_remains_skipped_when_skip_bot_messages_is_true",
738
+ telegramE2EServer.state.comments.length === 0,
739
+ `comments=${telegramE2EServer.state.comments.length}`,
740
+ );
741
+ } catch (err) {
742
+ push("telegram_runner_e2e_local_mock", false, String(err?.message || err));
743
+ } finally {
614
744
  try {
615
745
  await telegramE2EServer?.close?.();
616
746
  } catch {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.178",
3
+ "version": "0.2.180",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [