metheus-governance-mcp-cli 0.2.111 → 0.2.113

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/README.md CHANGED
@@ -305,7 +305,7 @@ For direct Telegram adds, the CLI derives the local file name from the matched s
305
305
 
306
306
  For direct Telegram edits, prefer `--bot-name` or `--bot-id` because server bot identity is the source of truth. Use `--bot-key` only when you intentionally need the legacy local selector. If one server bot name expands to multiple roles such as `approval / worker / review / monitor`, prefer the guided `bot edit` flow so you can keep the current grouped settings, edit one role only, or walk every role in sequence instead of forcing one entry-level AI override.
307
307
 
308
- For runner commands, you can still use `--route-name` directly, but normal operator workflows can also use `--bot-name` or `--bot-id` when those identify one enabled route uniquely. Use `runner list` first if you are not sure which route belongs to which server bot.
308
+ For runner commands, routes are the executable unit. Use `--route-name` as the primary selector. `--bot-name` and `--bot-id` are convenience route aliases only when they identify one enabled route uniquely. Use `runner list` first if you are not sure which route belongs to which server bot, and use `runner show` to inspect the resolved route, server identity, workspace mapping, and execution profile together.
309
309
 
310
310
  Current support status:
311
311
 
@@ -388,6 +388,7 @@ Commands:
388
388
 
389
389
  ```bash
390
390
  metheus-governance-mcp-cli runner list
391
+ metheus-governance-mcp-cli runner show --route-name telegram-monitor
391
392
  metheus-governance-mcp-cli runner once --route-name telegram-monitor
392
393
  metheus-governance-mcp-cli runner start --route-name telegram-monitor
393
394
  ```
@@ -399,9 +400,16 @@ metheus-governance-mcp-cli runner once --route-name telegram-monitor --dry-run-d
399
400
  metheus-governance-mcp-cli runner start --route-name telegram-monitor
400
401
  ```
401
402
 
403
+ What `runner list` means:
404
+ - `server_bot_name` may come from `route.server_bot_name` or be resolved from the Telegram bot env file by `server_bot_id`
405
+ - `server_bot_name_source=route_config` means the route file already stores the name directly
406
+ - `server_bot_name_source=telegram_env_lookup` means the CLI resolved the name from the local Telegram bot file
407
+ - `route_alias_by_server_bot_name` is a convenience selector, not a separate execution target
408
+
402
409
  Debug/selection overrides:
403
410
 
404
411
  ```bash
412
+ metheus-governance-mcp-cli runner show --bot-name <server_bot_name>
405
413
  metheus-governance-mcp-cli runner once --project-id <project_uuid> --provider telegram --role monitor
406
414
  metheus-governance-mcp-cli runner start --project-id <project_uuid> --provider telegram --role monitor --poll-interval-ms 5000
407
415
  metheus-governance-mcp-cli runner once --project-id <project_uuid> --provider telegram --role monitor --role-profile review
package/cli.mjs CHANGED
@@ -255,8 +255,9 @@ function printUsage() {
255
255
  ` ${cmd} selftest [--json <true|false>]`,
256
256
  ` ${cmd} local-bot-bridge [--client <gpt|claude|gemini|sample>] [--cwd <path>] [--model <name>] [--permission-mode <read_only|workspace_write|danger_full_access>] [--reasoning-effort <low|medium|high>]`,
257
257
  ` ${cmd} runner list [--json <true|false>]`,
258
- ` ${cmd} runner once [--route-name <name> | --bot-name <server_name> | --bot-id <uuid>] [--project-id <uuid>] [--provider <telegram|slack|kakaotalk>] [--role <monitor|review|worker|approval>] [--role-profile <name>] [--mentions-only <true|false>] [--reply-to-bot-messages <true|false>] [--direct-messages <true|false>] [--ignore-edited-messages <true|false>] [--dry-run-delivery <true|false>] [--context-comments <n>] [--archive-replies <true|false>]`,
259
- ` ${cmd} runner start [--route-name <name> | --bot-name <server_name> | --bot-id <uuid>] [--project-id <uuid>] [--provider <telegram|slack|kakaotalk>] [--role <monitor|review|worker|approval>] [--role-profile <name>] [--mentions-only <true|false>] [--reply-to-bot-messages <true|false>] [--direct-messages <true|false>] [--ignore-edited-messages <true|false>] [--dry-run-delivery <true|false>] [--poll-interval-ms <n>] [--context-comments <n>] [--archive-replies <true|false>]`,
258
+ ` ${cmd} runner show [--route-name <name> | --bot-name <server_name> | --bot-id <uuid>] [--json <true|false>]`,
259
+ ` ${cmd} runner once [--route-name <name> | --bot-name <server_name when one enabled route matches> | --bot-id <uuid when one enabled route matches>] [--project-id <uuid>] [--provider <telegram|slack|kakaotalk>] [--role <monitor|review|worker|approval>] [--role-profile <name>] [--mentions-only <true|false>] [--reply-to-bot-messages <true|false>] [--direct-messages <true|false>] [--ignore-edited-messages <true|false>] [--dry-run-delivery <true|false>] [--context-comments <n>] [--archive-replies <true|false>]`,
260
+ ` ${cmd} runner start [--route-name <name> | --bot-name <server_name when one enabled route matches> | --bot-id <uuid when one enabled route matches>] [--project-id <uuid>] [--provider <telegram|slack|kakaotalk>] [--role <monitor|review|worker|approval>] [--role-profile <name>] [--mentions-only <true|false>] [--reply-to-bot-messages <true|false>] [--direct-messages <true|false>] [--ignore-edited-messages <true|false>] [--dry-run-delivery <true|false>] [--poll-interval-ms <n>] [--context-comments <n>] [--archive-replies <true|false>]`,
260
261
  ` ${cmd} ctxpack pull [--project-id <uuid>] [--base-url <url>] [--workspace-dir <path|auto>] [--paths <csv>] [--timeout-seconds <n>]`,
261
262
  ` ${cmd} auth status`,
262
263
  ` ${cmd} auth login [--base-url <url>] [--flow <auto|device|callback|manual>] [--keycloak-url <url>] [--realm <name>] [--client-id <id>] [--open-browser <true|false>] [--callback-port <n>] [--timeout-seconds <n>] [--manual <true|false>]`,
@@ -1269,10 +1270,21 @@ function normalizeBotRunnerConfigContents(parsed, filePath) {
1269
1270
  if (!binding.name || (!binding.botID && !binding.botName)) continue;
1270
1271
  if (!binding.roleProfile && !binding.client && !binding.model && !binding.permissionMode && !binding.reasoningEffort) continue;
1271
1272
  botBindings[binding.name] = binding;
1272
- const rawClient = String(safeObject(rawBinding).client || "").trim().toLowerCase();
1273
+ const rawBindingObject = safeObject(rawBinding);
1274
+ const rawClient = String(rawBindingObject.client || "").trim().toLowerCase();
1273
1275
  if (rawClient === "codex") {
1274
1276
  migrated = true;
1275
1277
  }
1278
+ if (
1279
+ (!Object.prototype.hasOwnProperty.call(rawBindingObject, "server_bot_id")
1280
+ && !Object.prototype.hasOwnProperty.call(rawBindingObject, "serverBotID")
1281
+ && (Object.prototype.hasOwnProperty.call(rawBindingObject, "bot_id") || Object.prototype.hasOwnProperty.call(rawBindingObject, "botId")))
1282
+ || (!Object.prototype.hasOwnProperty.call(rawBindingObject, "server_bot_name")
1283
+ && !Object.prototype.hasOwnProperty.call(rawBindingObject, "serverBotName")
1284
+ && (Object.prototype.hasOwnProperty.call(rawBindingObject, "bot_name") || Object.prototype.hasOwnProperty.call(rawBindingObject, "botName")))
1285
+ ) {
1286
+ migrated = true;
1287
+ }
1276
1288
  }
1277
1289
 
1278
1290
  const projectMappings = {};
@@ -1289,6 +1301,16 @@ function normalizeBotRunnerConfigContents(parsed, filePath) {
1289
1301
  const routeSource = safeObject(rawRoute);
1290
1302
  const route = normalizeRunnerRoute(routeSource);
1291
1303
  routes.push(route);
1304
+ if (
1305
+ (!Object.prototype.hasOwnProperty.call(routeSource, "server_bot_id")
1306
+ && !Object.prototype.hasOwnProperty.call(routeSource, "serverBotID")
1307
+ && (Object.prototype.hasOwnProperty.call(routeSource, "bot_id") || Object.prototype.hasOwnProperty.call(routeSource, "botID")))
1308
+ || (!Object.prototype.hasOwnProperty.call(routeSource, "server_bot_name")
1309
+ && !Object.prototype.hasOwnProperty.call(routeSource, "serverBotName")
1310
+ && (Object.prototype.hasOwnProperty.call(routeSource, "bot_name") || Object.prototype.hasOwnProperty.call(routeSource, "botName")))
1311
+ ) {
1312
+ migrated = true;
1313
+ }
1292
1314
  if (!Object.prototype.hasOwnProperty.call(routeSource, "trigger_policy") && !Object.prototype.hasOwnProperty.call(routeSource, "triggerPolicy")) {
1293
1315
  migrated = true;
1294
1316
  }
@@ -1797,8 +1819,27 @@ function matchesRunnerRouteText(candidate, expected) {
1797
1819
  return String(candidate || "").trim().toLowerCase() === expectedText.toLowerCase();
1798
1820
  }
1799
1821
 
1800
- function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags) {
1822
+ function resolveConfiguredRunnerRouteServerBotName(route, telegramEntries = []) {
1801
1823
  const candidate = normalizeRunnerRoute(route);
1824
+ if (candidate.botName) {
1825
+ return candidate.botName;
1826
+ }
1827
+ if (candidate.provider !== "telegram") {
1828
+ return "";
1829
+ }
1830
+ const matchedTelegramEntry = ensureArray(telegramEntries).find((entry) => {
1831
+ const current = safeObject(entry);
1832
+ return (
1833
+ (candidate.botID && current.serverBotID && current.serverBotID === candidate.botID)
1834
+ || (candidate.botName && current.serverBotName && current.serverBotName === candidate.botName)
1835
+ );
1836
+ });
1837
+ return String(matchedTelegramEntry?.serverBotName || "").trim();
1838
+ }
1839
+
1840
+ function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags, options = {}) {
1841
+ const candidate = normalizeRunnerRoute(route);
1842
+ const candidateBotName = resolveConfiguredRunnerRouteServerBotName(candidate, options.telegramEntries);
1802
1843
  if (hasRunnerFlag(flags, "route-name") && !matchesRunnerRouteText(candidate.name, flags["route-name"])) {
1803
1844
  return false;
1804
1845
  }
@@ -1817,7 +1858,7 @@ function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags)
1817
1858
  if (inlineRoute.botID && candidate.botID !== inlineRoute.botID) {
1818
1859
  return false;
1819
1860
  }
1820
- if (inlineRoute.botName && !matchesRunnerRouteText(candidate.botName, inlineRoute.botName)) {
1861
+ if (inlineRoute.botName && !matchesRunnerRouteText(candidateBotName, inlineRoute.botName)) {
1821
1862
  return false;
1822
1863
  }
1823
1864
  if (inlineRoute.destinationID && candidate.destinationID !== inlineRoute.destinationID) {
@@ -2014,9 +2055,10 @@ function resolveRunnerRoutes(flags, mode) {
2014
2055
  .map((rawRoute) => normalizeRunnerRoute(rawRoute))
2015
2056
  .filter((route) => route.enabled)
2016
2057
  .filter((route) => !routeNameFilter || String(route.name || "").trim().toLowerCase() === routeNameFilter);
2058
+ const telegramEntries = selectionRequested ? ensureArray(readTelegramEnvState().entries) : [];
2017
2059
 
2018
2060
  if (selectionRequested) {
2019
- const matchedRoutes = configuredRoutes.filter((route) => configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags));
2061
+ const matchedRoutes = configuredRoutes.filter((route) => configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags, { telegramEntries }));
2020
2062
  if (matchedRoutes.length === 1) {
2021
2063
  return [applyRunnerRouteFlagOverrides(matchedRoutes[0], flags)];
2022
2064
  }
@@ -2591,10 +2633,14 @@ function buildRunnerRouteListRows() {
2591
2633
  );
2592
2634
  })
2593
2635
  : null;
2636
+ const botNameSource = route.botName
2637
+ ? "route_config"
2638
+ : matchedTelegramEntry?.serverBotName
2639
+ ? "telegram_env_lookup"
2640
+ : "unresolved";
2594
2641
  const resolvedBotName = firstNonEmptyString([
2595
2642
  route.botName,
2596
2643
  matchedTelegramEntry?.serverBotName,
2597
- matchedTelegramEntry?.key,
2598
2644
  "-",
2599
2645
  ]);
2600
2646
  return {
@@ -2604,6 +2650,7 @@ function buildRunnerRouteListRows() {
2604
2650
  provider: route.provider || "-",
2605
2651
  projectID: route.projectID || "-",
2606
2652
  botName: resolvedBotName,
2653
+ botNameSource,
2607
2654
  botID: route.botID || "-",
2608
2655
  role: route.role || "-",
2609
2656
  roleProfile: route.roleProfile || "-",
@@ -2621,6 +2668,7 @@ async function runRunnerList(flags) {
2621
2668
  return;
2622
2669
  }
2623
2670
  process.stdout.write("Runner routes\n");
2671
+ process.stdout.write(" note: routes are the executable unit. --bot-name and --bot-id are convenience selectors that resolve one enabled route when the match is unique.\n");
2624
2672
  if (!rows.length) {
2625
2673
  process.stdout.write(" none configured\n");
2626
2674
  return;
@@ -2633,18 +2681,141 @@ async function runRunnerList(flags) {
2633
2681
  ` provider: ${row.provider}`,
2634
2682
  ` project_id: ${row.projectID}`,
2635
2683
  ` server_bot_name: ${row.botName}`,
2684
+ ` server_bot_name_source: ${row.botNameSource}`,
2636
2685
  ` server_bot_id: ${row.botID}`,
2637
2686
  ` role: ${row.role}`,
2638
2687
  ` role_profile: ${row.roleProfile}`,
2639
2688
  ` destination_label: ${row.destinationLabel}`,
2640
2689
  ` poll_interval_ms: ${row.pollIntervalMs}`,
2641
2690
  ` run_once: ${CLI_NAME} runner once --route-name ${row.name}`,
2642
- row.botName ? ` run_once_by_server_bot_name: ${CLI_NAME} runner once --bot-name "${row.botName}"` : "",
2691
+ row.botName && row.botNameSource !== "unresolved"
2692
+ ? ` route_alias_by_server_bot_name: ${CLI_NAME} runner once --bot-name "${row.botName}"`
2693
+ : "",
2643
2694
  ].join("\n") + "\n",
2644
2695
  );
2645
2696
  });
2646
2697
  }
2647
2698
 
2699
+ function resolveRunnerShowSelection(flags) {
2700
+ const routes = resolveRunnerRoutes(flags, "once");
2701
+ if (!routes.length) {
2702
+ throw new Error("no runner route matched the provided filters");
2703
+ }
2704
+ if (routes.length > 1) {
2705
+ const names = routes.map((route) => normalizeRunnerRoute(route).name || runnerRouteKey(route)).join(", ");
2706
+ throw new Error(`Multiple enabled runner routes matched. Narrow with --route-name, --bot-name, or --bot-id. Matches: ${names}`);
2707
+ }
2708
+ return normalizeRunnerRoute(routes[0]);
2709
+ }
2710
+
2711
+ function findRunnerTelegramEntryForRoute(route, telegramEntries) {
2712
+ const normalizedRoute = normalizeRunnerRoute(route);
2713
+ if (normalizedRoute.provider !== "telegram") return null;
2714
+ return ensureArray(telegramEntries).find((entry) => {
2715
+ const current = safeObject(entry);
2716
+ return (
2717
+ (normalizedRoute.botID && current.serverBotID && current.serverBotID === normalizedRoute.botID)
2718
+ || (normalizedRoute.botName && current.serverBotName && current.serverBotName === normalizedRoute.botName)
2719
+ );
2720
+ }) || null;
2721
+ }
2722
+
2723
+ function buildRunnerShowPayload(route, flags = {}) {
2724
+ const normalizedRoute = normalizeRunnerRoute(route);
2725
+ const runnerConfig = loadBotRunnerConfig({ persistIfNeeded: true });
2726
+ const diagnostics = collectRunnerRouteDiagnostics(normalizedRoute, runnerConfig);
2727
+ const telegramState = readTelegramEnvState();
2728
+ const telegramEntries = ensureArray(telegramState.entries);
2729
+ const matchedTelegramEntry = findRunnerTelegramEntryForRoute(normalizedRoute, telegramEntries);
2730
+ const botNameSource = normalizedRoute.botName
2731
+ ? "route_config"
2732
+ : matchedTelegramEntry?.serverBotName
2733
+ ? "telegram_env_lookup"
2734
+ : "unresolved";
2735
+ const resolvedServerBotName = firstNonEmptyString([
2736
+ normalizedRoute.botName,
2737
+ matchedTelegramEntry?.serverBotName,
2738
+ "-",
2739
+ ]);
2740
+ const envConfig = normalizedRoute.provider
2741
+ ? loadProviderEnvConfig(normalizedRoute.provider, {
2742
+ botID: normalizedRoute.botID,
2743
+ botName: resolvedServerBotName !== "-" ? resolvedServerBotName : "",
2744
+ route: normalizedRoute,
2745
+ })
2746
+ : null;
2747
+ return {
2748
+ ok: diagnostics.errors.length === 0,
2749
+ route_name: normalizedRoute.name || runnerRouteKey(normalizedRoute),
2750
+ route_key: runnerRouteKey(normalizedRoute),
2751
+ route_config_file: runnerConfig.filePath,
2752
+ route_config: serializeRunnerRoute(normalizedRoute),
2753
+ resolved_server_identity: {
2754
+ server_bot_name: resolvedServerBotName,
2755
+ server_bot_name_source: botNameSource,
2756
+ server_bot_id: normalizedRoute.botID || "-",
2757
+ telegram_entry_file: String(envConfig?.entryFilePath || "").trim() || "-",
2758
+ },
2759
+ workspace_mapping: {
2760
+ workspace_dir: diagnostics.workspaceDir || "-",
2761
+ workspace_source: diagnostics.workspaceSource || "-",
2762
+ },
2763
+ execution_profile: {
2764
+ route_role: normalizedRoute.role || "-",
2765
+ role_profile_name: diagnostics.roleProfileName || "-",
2766
+ client: String(diagnostics.roleProfile?.client || "").trim() || "-",
2767
+ model: String(diagnostics.roleProfile?.model || "").trim() || "-",
2768
+ permission_mode: String(diagnostics.roleProfile?.permissionMode || "").trim() || "-",
2769
+ reasoning_effort: String(diagnostics.roleProfile?.reasoningEffort || "").trim() || "-",
2770
+ },
2771
+ route_selection_note: "Routes are the executable unit. --bot-name and --bot-id are convenience selectors that resolve one enabled route when the match is unique.",
2772
+ warnings: diagnostics.warnings,
2773
+ errors: diagnostics.errors,
2774
+ };
2775
+ }
2776
+
2777
+ async function runRunnerShow(flags) {
2778
+ const route = resolveRunnerShowSelection(flags);
2779
+ const payload = buildRunnerShowPayload(route, flags);
2780
+ if (boolFromRaw(flags.json, false)) {
2781
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
2782
+ return;
2783
+ }
2784
+ process.stdout.write("Runner route details\n");
2785
+ process.stdout.write(" note: routes are the executable unit. --bot-name and --bot-id are convenience selectors that resolve one enabled route when the match is unique.\n");
2786
+ process.stdout.write(
2787
+ [
2788
+ ` route_name: ${payload.route_name}`,
2789
+ ` route_key: ${payload.route_key}`,
2790
+ ` route_config_file: ${payload.route_config_file}`,
2791
+ " route_config:",
2792
+ ` provider: ${payload.route_config.provider || "-"}`,
2793
+ ` project_id: ${payload.route_config.project_id || "-"}`,
2794
+ ` role: ${payload.route_config.role || "-"}`,
2795
+ ` role_profile: ${payload.route_config.role_profile || "-"}`,
2796
+ ` destination_label: ${payload.route_config.destination_label || "-"}`,
2797
+ ` poll_interval_ms: ${payload.route_config.poll_interval_ms || 0}`,
2798
+ " resolved_server_identity:",
2799
+ ` server_bot_name: ${payload.resolved_server_identity.server_bot_name}`,
2800
+ ` server_bot_name_source: ${payload.resolved_server_identity.server_bot_name_source}`,
2801
+ ` server_bot_id: ${payload.resolved_server_identity.server_bot_id}`,
2802
+ ` telegram_entry_file: ${payload.resolved_server_identity.telegram_entry_file}`,
2803
+ " workspace_mapping:",
2804
+ ` workspace_dir: ${payload.workspace_mapping.workspace_dir}`,
2805
+ ` workspace_source: ${payload.workspace_mapping.workspace_source}`,
2806
+ " execution_profile:",
2807
+ ` route_role: ${payload.execution_profile.route_role}`,
2808
+ ` role_profile_name: ${payload.execution_profile.role_profile_name}`,
2809
+ ` client: ${payload.execution_profile.client}`,
2810
+ ` model: ${payload.execution_profile.model}`,
2811
+ ` permission_mode: ${payload.execution_profile.permission_mode}`,
2812
+ ` reasoning_effort: ${payload.execution_profile.reasoning_effort}`,
2813
+ payload.warnings.length ? ` warnings: ${payload.warnings.join("; ")}` : " warnings: -",
2814
+ payload.errors.length ? ` errors: ${payload.errors.join("; ")}` : " errors: -",
2815
+ ].join("\n") + "\n",
2816
+ );
2817
+ }
2818
+
2648
2819
  async function runRunnerStart(flags) {
2649
2820
  const jsonMode = boolFromRaw(flags.json, false);
2650
2821
  const routes = resolveRunnerRoutes(flags, "start");
@@ -2709,6 +2880,10 @@ async function runRunner(argv) {
2709
2880
  await runRunnerList(flags);
2710
2881
  return;
2711
2882
  }
2883
+ if (subcommand === "show") {
2884
+ await runRunnerShow(flags);
2885
+ return;
2886
+ }
2712
2887
  if (subcommand === "once") {
2713
2888
  await runRunnerOnce(flags);
2714
2889
  return;
@@ -3938,6 +4113,7 @@ function buildBotCommandDeps() {
3938
4113
  normalizeBotProvider,
3939
4114
  normalizeTelegramBotEnvKey,
3940
4115
  normalizeTelegramBotUsername,
4116
+ normalizeRunnerRoute,
3941
4117
  normalizeRunnerRoleProfileName,
3942
4118
  normalizeLocalAIClientName,
3943
4119
  normalizeLocalAIPermissionMode,
@@ -1254,6 +1254,7 @@ function summarizeTelegramRouteLinks(parsedEnv, entry, serverBinding, deps) {
1254
1254
  const selectedEntry = safeObject(entry);
1255
1255
  const binding = safeObject(serverBinding);
1256
1256
  const config = safeObject(requireDependency(deps, "loadBotRunnerConfig")({ persistIfNeeded: true }));
1257
+ const normalizeRunnerRoute = requireDependency(deps, "normalizeRunnerRoute");
1257
1258
  const routes = ensureArray(config.routes);
1258
1259
  const entryKey = String(selectedEntry.key || "").trim();
1259
1260
  const defaultBotKey = String(safeObject(parsedEnv).TELEGRAM_DEFAULT_BOT_KEY || "").trim();
@@ -1267,12 +1268,12 @@ function summarizeTelegramRouteLinks(parsedEnv, entry, serverBinding, deps) {
1267
1268
  );
1268
1269
  const linkedRoutes = [];
1269
1270
  routes.forEach((rawRoute, index) => {
1270
- const route = safeObject(rawRoute);
1271
+ const route = normalizeRunnerRoute(rawRoute);
1271
1272
  if (route.enabled === false) return;
1272
1273
  if (String(route.provider || "").trim().toLowerCase() !== "telegram") return;
1273
1274
  const routeName = String(route.name || route.route_name || `telegram-route-${index + 1}`).trim();
1274
- const routeBotID = String(route.bot_id || route.botID || "").trim();
1275
- const routeBotName = normalizeServerBotIdentityText(route.bot_name || route.botName || "");
1275
+ const routeBotID = String(route.server_bot_id || route.serverBotID || route.bot_id || route.botID || "").trim();
1276
+ const routeBotName = normalizeServerBotIdentityText(route.server_bot_name || route.serverBotName || route.bot_name || route.botName || "");
1276
1277
  const routeRole = String(route.role_profile || route.roleProfile || route.role || "").trim();
1277
1278
  let matchedBy = "";
1278
1279
  if (serverBotID && routeBotID === serverBotID) {
@@ -565,7 +565,7 @@ export async function runSelftestBotCommands(push, deps) {
565
565
  enabled: true,
566
566
  project_id: "03c586a2-006d-4051-83b4-f353a5813176",
567
567
  provider: "telegram",
568
- bot_id: mock.bots[0].id,
568
+ server_bot_id: mock.bots[0].id,
569
569
  role_profile: "monitor",
570
570
  },
571
571
  {
@@ -415,7 +415,26 @@ export async function runSelftestRunnerScenarios(push, deps) {
415
415
  originalResolveUserProfile = process.env.USERPROFILE;
416
416
  const resolveHome = path.join(runnerRouteResolveTempRoot, "home");
417
417
  const resolveMetheusDir = path.join(resolveHome, ".metheus");
418
+ const resolveTelegramDir = path.join(resolveMetheusDir, "telegram-bots");
418
419
  fs.mkdirSync(resolveMetheusDir, { recursive: true });
420
+ fs.mkdirSync(resolveTelegramDir, { recursive: true });
421
+ fs.writeFileSync(
422
+ path.join(resolveTelegramDir, "global.env"),
423
+ "TELEGRAM_DEFAULT_BOT_KEY=serverprotocolmonitorbot\n",
424
+ "utf8",
425
+ );
426
+ fs.writeFileSync(
427
+ path.join(resolveTelegramDir, "ServerProtocolMonitorBot.env"),
428
+ [
429
+ "TELEGRAM_BOT_SERVER_BOT_ID=22222222-2222-2222-2222-222222222222",
430
+ "TELEGRAM_BOT_SERVER_NAME=ServerProtocolMonitorBot",
431
+ "TELEGRAM_BOT_SERVER_ROLES=monitor",
432
+ "TELEGRAM_BOT_SERVER_ROLE_IDS=monitor:22222222-2222-2222-2222-222222222222",
433
+ "TELEGRAM_BOT_TOKEN=test-token",
434
+ "",
435
+ ].join("\n"),
436
+ "utf8",
437
+ );
419
438
  fs.writeFileSync(
420
439
  path.join(resolveMetheusDir, "bot-runner.json"),
421
440
  `${JSON.stringify({
@@ -435,9 +454,21 @@ export async function runSelftestRunnerScenarios(push, deps) {
435
454
  provider: "telegram",
436
455
  role: "monitor",
437
456
  role_profile: "monitor",
438
- bot_name: "ServerProtocolMonitorBot",
457
+ bot_id: "22222222-2222-2222-2222-222222222222",
439
458
  destination_label: "AI incubating CHAT ROOM",
440
459
  archive_work_item_id: "304ce77a-7032-421c-aeda-bc54daf088dd",
460
+ trigger_policy: {
461
+ mentions_only: true,
462
+ direct_messages: true,
463
+ reply_to_bot_messages: true,
464
+ ignore_edited_messages: true,
465
+ },
466
+ archive_policy: {
467
+ mirror_replies: true,
468
+ dedupe_inbound: true,
469
+ dedupe_outbound: true,
470
+ skip_bot_messages: true,
471
+ },
441
472
  },
442
473
  ],
443
474
  }, null, 2)}\n`,
@@ -457,14 +488,18 @@ export async function runSelftestRunnerScenarios(push, deps) {
457
488
  "once",
458
489
  );
459
490
  const resolvedRunnerRoute = normalizeRunnerRoute(resolvedRunnerRoutes[0]);
491
+ const persistedRunnerConfig = JSON.parse(fs.readFileSync(path.join(resolveMetheusDir, "bot-runner.json"), "utf8"));
492
+ const persistedRoute = Array.isArray(persistedRunnerConfig.routes) ? persistedRunnerConfig.routes[0] || {} : {};
460
493
  push(
461
494
  "bot_runner_inline_filters_reuse_configured_route",
462
495
  resolvedRunnerRoutes.length === 1
463
496
  && resolvedRunnerRoute.name === "telegram-monitor"
464
497
  && resolvedRunnerRoute.destinationLabel === "AI incubating CHAT ROOM"
465
498
  && resolvedRunnerRoute.dryRunDelivery === true
466
- && runnerRouteKey(resolvedRunnerRoute) === "telegram-monitor::11111111-1111-1111-1111-111111111111::telegram::monitor::ServerProtocolMonitorBot::AI incubating CHAT ROOM",
467
- `name=${resolvedRunnerRoute.name || "(none)"} destination=${resolvedRunnerRoute.destinationLabel || "(none)"} key=${runnerRouteKey(resolvedRunnerRoute)}`,
499
+ && runnerRouteKey(resolvedRunnerRoute) === "telegram-monitor::11111111-1111-1111-1111-111111111111::telegram::monitor::22222222-2222-2222-2222-222222222222::AI incubating CHAT ROOM"
500
+ && String(persistedRoute.server_bot_id || "") === "22222222-2222-2222-2222-222222222222"
501
+ && !Object.prototype.hasOwnProperty.call(persistedRoute, "bot_id"),
502
+ `name=${resolvedRunnerRoute.name || "(none)"} destination=${resolvedRunnerRoute.destinationLabel || "(none)"} key=${runnerRouteKey(resolvedRunnerRoute)} persisted=${JSON.stringify(persistedRoute)}`,
468
503
  );
469
504
  } catch (err) {
470
505
  push("bot_runner_inline_filters_reuse_configured_route", false, String(err?.message || err));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.111",
3
+ "version": "0.2.113",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [