metheus-governance-mcp-cli 0.2.104 → 0.2.106

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
@@ -58,9 +58,9 @@ TELEGRAM_BOT_TOKEN=
58
58
  ```
59
59
 
60
60
  ```env
61
- # ~/.metheus/telegram-bots/RyoAI_bot.env
61
+ # ~/.metheus/telegram-bots/<ServerBotName>.env
62
62
  TELEGRAM_BOT_SERVER_BOT_ID=<server_bot_uuid>
63
- TELEGRAM_BOT_SERVER_NAME=RyoAI_bot
63
+ TELEGRAM_BOT_SERVER_NAME=<ServerBotName>
64
64
  TELEGRAM_BOT_SERVER_ROLES=monitor,review,worker,approval
65
65
  TELEGRAM_BOT_SERVER_ROLE_IDS=monitor:<uuid>,review:<uuid>,worker:<uuid>,approval:<uuid>
66
66
  # Optional fallback only when server bot binding is unavailable
@@ -95,7 +95,7 @@ Runner template:
95
95
  },
96
96
  "role_profiles": {
97
97
  "monitor": {
98
- "client": "codex",
98
+ "client": "gpt",
99
99
  "model": "",
100
100
  "permission_mode": "read_only",
101
101
  "reasoning_effort": "low"
@@ -106,7 +106,7 @@ Runner template:
106
106
  "bot_id": "<server_bot_uuid>",
107
107
  "bot_name": "<bot_name>",
108
108
  "role_profile": "monitor",
109
- "client": "codex",
109
+ "client": "gpt",
110
110
  "model": "",
111
111
  "permission_mode": "read_only",
112
112
  "reasoning_effort": "low"
@@ -194,15 +194,18 @@ Fill provider bot secrets and provider-local transport options locally. Project
194
194
  Built-in helper command for legacy fallback/testing:
195
195
 
196
196
  ```bash
197
- metheus-governance-mcp-cli local-bot-bridge --client codex --no-update
197
+ metheus-governance-mcp-cli local-bot-bridge --client gpt --no-update
198
198
  ```
199
199
 
200
200
  Supported `--client` values:
201
- - `codex`
201
+ - `gpt`
202
202
  - `claude`
203
203
  - `gemini`
204
204
  - `sample`
205
205
 
206
+ Compatibility note:
207
+ - `codex` is still accepted as an alias for `gpt` because the GPT adapter runs through the Codex CLI binary.
208
+
206
209
  Use `sample` first for smoke tests before switching to a real AI client.
207
210
 
208
211
  Gemini CLI note:
@@ -230,22 +233,22 @@ Direct commands:
230
233
 
231
234
  ```bash
232
235
  metheus-governance-mcp-cli bot list
233
- metheus-governance-mcp-cli bot show --provider telegram --bot-name RyoAI_bot
236
+ metheus-governance-mcp-cli bot show --provider telegram --bot-name <server_bot_name>
234
237
  metheus-governance-mcp-cli bot add --provider telegram
235
238
  metheus-governance-mcp-cli bot edit
236
239
  metheus-governance-mcp-cli bot edit --provider telegram
237
240
  metheus-governance-mcp-cli bot remove --provider telegram
238
- metheus-governance-mcp-cli bot set-default --provider telegram --bot-name RyoAI_bot
239
- metheus-governance-mcp-cli bot migrate --provider telegram --bot-name RyoAI_bot
241
+ metheus-governance-mcp-cli bot set-default --provider telegram --bot-name <server_bot_name>
242
+ metheus-governance-mcp-cli bot migrate --provider telegram --bot-name <server_bot_name>
240
243
  metheus-governance-mcp-cli bot global --provider telegram
241
- metheus-governance-mcp-cli bot verify --provider telegram --bot-name RyoAI_bot
244
+ metheus-governance-mcp-cli bot verify --provider telegram --bot-name <server_bot_name>
242
245
  ```
243
246
 
244
247
  Behavior:
245
248
 
246
249
  - `bot setup` asks for `Telegram / Slack / KakaoTalk` first, then prompts with numbered actions.
247
250
  - `bot add` without flags now uses the shortest practical guided flow: provider -> token -> verify -> optional save-anyway only when verify fails -> optional username fallback only when verify cannot discover it -> optional AI model selection when the resolved defaults leave it blank -> optional default bot.
248
- - In the normal Telegram path, `bot add` does not ask for a local bot key, a server bot UUID, or an approval / worker / review / monitor choice.
251
+ - In the normal Telegram path, `bot add` does not ask for an advanced local selector, a server bot UUID, or an approval / worker / review / monitor choice.
249
252
  - For Telegram, the local bot file stem is auto-generated from the matched server bot name or verified username, so you do not have to invent a separate local nickname first.
250
253
  - For Telegram, the CLI tries to match the verified bot identity against the server `me/bots` list first. If the server exposes one logical bot name with multiple roles such as `approval`, `worker`, `review`, and `monitor`, the CLI does not ask you to choose one UUID. It binds by server bot name and keeps the local role/AI fields empty so runtime can use the server bot role for each route.
251
254
  - When the Telegram username matches exactly one server bot role, the CLI still auto-fills the local `role_profile` and blank AI defaults from your local `bot-runner.json` `role_profiles` mapping.
@@ -281,7 +284,7 @@ Behavior:
281
284
  - `bot verify` checks the configured local token, cross-checks the server bot binding, prints the effective runtime role profile summary that the runner will use, and shows which local runner routes currently point at this bot entry.
282
285
  - `bot show` prints one local bot entry in detail, including grouped role summaries when one server bot name expands to multiple roles and any linked local runner routes.
283
286
  - In `bot show` and `bot verify`, `saved_local_file` means the values already stored on disk under `~/.metheus/telegram-bots/*.env`, while `current_server_protocol` means the fresh `/api/v1/me/bots` response from the server right now.
284
- - `bot global` edits Telegram-wide local settings such as API base URL, allowed updates, and default bot key.
287
+ - `bot global` edits Telegram-wide local settings such as API base URL, allowed updates, and the default Telegram bot selector.
285
288
  - `bot set-default` updates `TELEGRAM_DEFAULT_BOT_KEY`.
286
289
  - `bot migrate` moves legacy `TELEGRAM_BOT_TOKEN` into a named Telegram bot entry.
287
290
 
@@ -291,11 +294,11 @@ Non-interactive examples:
291
294
  metheus-governance-mcp-cli bot global --provider telegram --non-interactive true --api-base-url http://127.0.0.1:8999/telegram --auto-clear-webhook false --allowed-updates message,edited_message,channel_post
292
295
  metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --server-bot-id <server_bot_uuid> --token <telegram_bot_token> --default true
293
296
  metheus-governance-mcp-cli bot add --provider telegram --non-interactive true --server-bot-id <server_bot_uuid> --token <telegram_bot_token> --ai-client gpt --ai-model "gpt-5.4" --ai-permission-mode read_only --ai-reasoning-effort low
294
- metheus-governance-mcp-cli bot edit --provider telegram --bot-name RyoAI_bot --non-interactive true --ai-client claude --ai-model "Sonnet 4.6r" --ai-permission-mode danger_full_access --ai-reasoning-effort high
295
- metheus-governance-mcp-cli bot set-default --provider telegram --bot-name RyoAI_bot --non-interactive true
296
- metheus-governance-mcp-cli bot migrate --provider telegram --bot-name RyoAI_bot --server-bot-id <server_bot_uuid> --role-profile monitor --ai-client gpt --ai-permission-mode read_only --ai-reasoning-effort low --non-interactive true
297
- metheus-governance-mcp-cli bot remove --provider telegram --bot-name RyoAI_bot --non-interactive true
298
- metheus-governance-mcp-cli bot verify --provider telegram --bot-name RyoAI_bot --json true
297
+ metheus-governance-mcp-cli bot edit --provider telegram --bot-name <server_bot_name> --non-interactive true --ai-client claude --ai-model "Sonnet 4.6r" --ai-permission-mode danger_full_access --ai-reasoning-effort high
298
+ metheus-governance-mcp-cli bot set-default --provider telegram --bot-name <server_bot_name> --non-interactive true
299
+ metheus-governance-mcp-cli bot migrate --provider telegram --bot-name <server_bot_name> --server-bot-id <server_bot_uuid> --role-profile monitor --ai-client gpt --ai-permission-mode read_only --ai-reasoning-effort low --non-interactive true
300
+ metheus-governance-mcp-cli bot remove --provider telegram --bot-name <server_bot_name> --non-interactive true
301
+ metheus-governance-mcp-cli bot verify --provider telegram --bot-name <server_bot_name> --json true
299
302
  ```
300
303
 
301
304
  For direct Telegram adds, the CLI derives the local file name from the matched server bot name. You normally do not need `--bot-key` or `--username`. Treat `--bot-key` as an advanced compatibility selector only when you intentionally need to target an older local selector directly.
@@ -378,7 +381,7 @@ Execution model:
378
381
  - local runner stores workspace mapping and role profiles
379
382
  - runner resolves `project_id -> workspace_dir`
380
383
  - runner resolves server bot role to a local `role_profile`
381
- - runner executes the mapped client adapter (`codex` / `claude` / `gemini`)
384
+ - runner executes the mapped client adapter (`gpt` / `claude` / `gemini`)
382
385
  - legacy `command` remains readable for migration, but execution is disabled by default unless `METHEUS_ALLOW_LEGACY_RUNNER_COMMAND=1`
383
386
 
384
387
  Commands:
@@ -421,7 +424,8 @@ Why `workspace_dir` matters:
421
424
  - without a valid project mapping, bots may answer with a generic local root instead of the real project workspace
422
425
 
423
426
  Role profile fields:
424
- - `client`: `codex`, `claude`, `gemini`, or `sample`
427
+ - `client`: `gpt`, `claude`, `gemini`, or `sample`
428
+ - `codex` is still accepted as a compatibility alias for `gpt`
425
429
  - `model`: optional CLI-specific model name
426
430
  - `permission_mode`: `read_only`, `workspace_write`, `danger_full_access`
427
431
  - `reasoning_effort`: `low`, `medium`, `high`
package/cli.mjs CHANGED
@@ -185,7 +185,7 @@ const CTXPACK_PUSH_TOOL_NAMES = ["ctxpack.push", "ctxpack.update", "ctxpack.save
185
185
  const CLI_META = loadCLIMeta();
186
186
  const CLI_NAME = CLI_META.name || "metheus-governance-mcp-cli";
187
187
  const CLI_VERSION = CLI_META.version || "0.0.0";
188
- const DEFAULT_BOT_RUNNER_CLIENT = DEFAULT_LOCAL_AI_CLIENT;
188
+ const DEFAULT_BOT_RUNNER_CLIENT = "gpt";
189
189
  const SELF_UPDATE_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
190
190
  const SELF_UPDATE_TIMEOUT_SECONDS = 6;
191
191
  const SELF_UPDATE_ENV_KEY = "METHEUS_CLI_AUTO_UPDATE";
@@ -253,7 +253,7 @@ function printUsage() {
253
253
  ` ${cmd} doctor [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--timeout-seconds <n>] [--strict <true|false>]`,
254
254
  ` ${cmd} proxy [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path|auto>] [--include-drafts <true|false>] [--auto-pull-on-conflict <true|false>] [--timeout-seconds <n>]`,
255
255
  ` ${cmd} selftest [--json <true|false>]`,
256
- ` ${cmd} local-bot-bridge [--client <codex|claude|gemini|sample>] [--cwd <path>] [--model <name>] [--permission-mode <read_only|workspace_write|danger_full_access>] [--reasoning-effort <low|medium|high>]`,
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
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
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>]`,
@@ -832,7 +832,7 @@ function defaultLocalBotBridgeCommand(client = DEFAULT_BOT_RUNNER_CLIENT) {
832
832
  function defaultBotRunnerRoleProfiles() {
833
833
  return {
834
834
  monitor: {
835
- client: "codex",
835
+ client: "gpt",
836
836
  model: "",
837
837
  permission_mode: "read_only",
838
838
  reasoning_effort: "low",
@@ -844,7 +844,7 @@ function defaultBotRunnerRoleProfiles() {
844
844
  reasoning_effort: "medium",
845
845
  },
846
846
  worker: {
847
- client: "codex",
847
+ client: "gpt",
848
848
  model: "",
849
849
  permission_mode: "danger_full_access",
850
850
  reasoning_effort: "medium",
@@ -1243,6 +1243,7 @@ function normalizeBotRunnerConfigContents(parsed, filePath) {
1243
1243
  const defaults = defaultBotRunnerRoleProfiles();
1244
1244
  const rawRoleProfiles = safeObject(raw.role_profiles || raw.roleProfiles);
1245
1245
  const roleProfiles = {};
1246
+ let migrated = rawVersion < BOT_RUNNER_CONFIG_VERSION;
1246
1247
  const roleProfileNames = new Set([
1247
1248
  ...Object.keys(defaults),
1248
1249
  ...Object.keys(rawRoleProfiles).map((name) => normalizeRunnerRoleProfileName(name)).filter(Boolean),
@@ -1253,6 +1254,10 @@ function normalizeBotRunnerConfigContents(parsed, filePath) {
1253
1254
  rawRoleProfiles[profileName],
1254
1255
  defaults[profileName] || {},
1255
1256
  );
1257
+ const rawClient = String(safeObject(rawRoleProfiles[profileName]).client || safeObject(rawRoleProfiles[profileName]).cli || "").trim().toLowerCase();
1258
+ if (rawClient === "codex") {
1259
+ migrated = true;
1260
+ }
1256
1261
  }
1257
1262
 
1258
1263
  const botBindings = {};
@@ -1262,6 +1267,10 @@ function normalizeBotRunnerConfigContents(parsed, filePath) {
1262
1267
  if (!binding.name || (!binding.botID && !binding.botName)) continue;
1263
1268
  if (!binding.roleProfile && !binding.client && !binding.model && !binding.permissionMode && !binding.reasoningEffort) continue;
1264
1269
  botBindings[binding.name] = binding;
1270
+ const rawClient = String(safeObject(rawBinding).client || "").trim().toLowerCase();
1271
+ if (rawClient === "codex") {
1272
+ migrated = true;
1273
+ }
1265
1274
  }
1266
1275
 
1267
1276
  const projectMappings = {};
@@ -1274,7 +1283,6 @@ function normalizeBotRunnerConfigContents(parsed, filePath) {
1274
1283
  }
1275
1284
 
1276
1285
  const routes = [];
1277
- let migrated = rawVersion < BOT_RUNNER_CONFIG_VERSION;
1278
1286
  for (const rawRoute of ensureArray(raw.routes)) {
1279
1287
  const routeSource = safeObject(rawRoute);
1280
1288
  const route = normalizeRunnerRoute(routeSource);
@@ -228,7 +228,7 @@ function printBotUsage(deps) {
228
228
  `Behavior:`,
229
229
  ` - bot setup asks for provider first, then shows a numbered action menu.`,
230
230
  ` - bot add without flags uses the shortest practical guided flow: provider -> token -> verify -> optional username fallback -> optional default bot.`,
231
- ` - in the normal Telegram path, bot add does not ask for a local bot key or a server role/profile choice.`,
231
+ ` - in the normal Telegram path, bot add does not ask for an advanced local selector or a server role/profile choice.`,
232
232
  ` - server bot name/UUID is the source of truth; --bot-key is an advanced compatibility selector only.`,
233
233
  ` - runner commands can auto-select by --bot-name or --bot-id when one enabled route matches. Use runner list first if you are unsure.`,
234
234
  ` - bot edit without flags asks for provider, existing entry, then walks field-by-field in a numbered flow.`,
@@ -813,7 +813,7 @@ function telegramEntryDisplayName(entry) {
813
813
 
814
814
  function displayLocalAIClientName(clientName) {
815
815
  const normalized = String(clientName || "").trim().toLowerCase();
816
- if (normalized === "codex" || normalized === "gpt") return "GPT";
816
+ if (normalized === "gpt" || normalized === "codex") return "GPT";
817
817
  if (normalized === "claude") return "Claude";
818
818
  if (normalized === "gemini") return "Gemini";
819
819
  if (normalized === "sample") return "Sample";
@@ -1568,12 +1568,12 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
1568
1568
  {
1569
1569
  value: "__manual__",
1570
1570
  label: "Manual entry",
1571
- description: "type the server bot UUID yourself",
1571
+ description: "enter a server bot UUID manually",
1572
1572
  },
1573
1573
  {
1574
1574
  value: "__blank__",
1575
1575
  label: "Leave empty",
1576
- description: "skip server bot UUID binding",
1576
+ description: "continue without a server bot binding",
1577
1577
  },
1578
1578
  ],
1579
1579
  { defaultIndex: 0 },
@@ -1618,12 +1618,12 @@ async function chooseServerBot(ui, provider, baseURL, timeoutSeconds, deps, opti
1618
1618
  {
1619
1619
  value: "__manual__",
1620
1620
  label: "Manual entry",
1621
- description: "type the server bot UUID yourself",
1621
+ description: "enter a server bot UUID manually",
1622
1622
  },
1623
1623
  {
1624
1624
  value: "__blank__",
1625
1625
  label: "Leave empty",
1626
- description: "skip server bot UUID binding",
1626
+ description: "continue without a server bot binding",
1627
1627
  },
1628
1628
  ],
1629
1629
  { defaultIndex: 0 },
@@ -1710,7 +1710,7 @@ async function resolveServerBotForNonInteractive(provider, flags, deps, options
1710
1710
  (bot) => normalizeServerBotIdentityText(bot.name) === requestedName,
1711
1711
  );
1712
1712
  if (!matchedByName.length) {
1713
- throw new Error(`no server bot matched ${requestedName}`);
1713
+ throw new Error(`no server bot matched the requested server bot name "${requestedName}"`);
1714
1714
  }
1715
1715
  if (matchedByName.length === 1) {
1716
1716
  const match = matchedByName[0] || {};
@@ -1758,7 +1758,7 @@ async function promptAIClient(ui, deps, defaultValue = "") {
1758
1758
 
1759
1759
  function suggestedAIModelsForClient(clientName) {
1760
1760
  const normalizedClient = String(clientName || "").trim().toLowerCase();
1761
- if (normalizedClient === "codex") {
1761
+ if (normalizedClient === "gpt") {
1762
1762
  return [
1763
1763
  { value: "gpt-5.4", label: "gpt-5.4", description: "recommended GPT model" },
1764
1764
  { value: "gpt-5.3-codex", label: "gpt-5.3-codex", description: "stable GPT codex model" },
@@ -1789,23 +1789,26 @@ async function promptAIModel(ui, deps, clientName, defaultValue = "", title = "S
1789
1789
  const normalizedClient = requireDependency(deps, "normalizeLocalAIClientName")(clientName || "", "");
1790
1790
  const currentValue = String(defaultValue || "").trim();
1791
1791
  const suggestions = suggestedAIModelsForClient(normalizedClient);
1792
+ const currentMatchesSuggestions = currentValue && suggestions.some((item) => item.value === currentValue);
1792
1793
  const options = [
1793
1794
  { value: "", label: "(blank)", description: "leave AI model empty" },
1794
1795
  ];
1795
- if (currentValue && !suggestions.some((item) => item.value === currentValue)) {
1796
+ options.push(...suggestions);
1797
+ if (currentValue && !currentMatchesSuggestions) {
1796
1798
  options.push({
1797
1799
  value: currentValue,
1798
- label: currentValue,
1799
- description: "current saved model",
1800
+ label: `Keep current custom model (${currentValue})`,
1801
+ description: "reuse the model already saved on disk",
1800
1802
  });
1801
1803
  }
1802
- options.push(...suggestions);
1803
1804
  options.push({
1804
1805
  value: "__manual__",
1805
1806
  label: "Manual entry",
1806
1807
  description: "type the model name yourself",
1807
1808
  });
1808
- const selectedIndex = Math.max(0, options.findIndex((item) => item.value === currentValue));
1809
+ const selectedIndex = currentMatchesSuggestions
1810
+ ? Math.max(0, options.findIndex((item) => item.value === currentValue))
1811
+ : (suggestions.length ? 1 : Math.max(0, options.findIndex((item) => item.value === currentValue)));
1809
1812
  const selected = await promptChoice(ui, title, options, { defaultIndex: selectedIndex >= 0 ? selectedIndex : 0 });
1810
1813
  if (String(selected?.value || "") === "__manual__") {
1811
1814
  return String(await promptRequiredLine(ui, "AI model", currentValue)).trim();
@@ -2154,6 +2157,11 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
2154
2157
  const normalizedServerIdentity = normalizeServerBotIdentityText(current.serverBotName || current.botUsername || current.botKey);
2155
2158
  const matches = bots.filter((bot) => normalizeServerBotIdentityText(bot.name) === normalizedServerIdentity);
2156
2159
  if (!matches.length) {
2160
+ const unmatchedIdentity = String(current.botUsername || "").trim()
2161
+ ? `@${String(current.botUsername || "").trim()}`
2162
+ : String(current.serverBotName || "").trim()
2163
+ ? `"${String(current.serverBotName || "").trim()}"`
2164
+ : "this local Telegram bot";
2157
2165
  return {
2158
2166
  ok: false,
2159
2167
  mode: "missing",
@@ -2165,7 +2173,7 @@ function resolveTelegramServerBindingDetailsFromBots(envConfig, bots, deps) {
2165
2173
  serverBotID: "",
2166
2174
  effectiveRoleProfile: null,
2167
2175
  effectiveRoleProfiles: {},
2168
- detail: `no server bot matched ${current.botUsername ? `@${current.botUsername}` : current.botKey}`,
2176
+ detail: `no server bot matched ${unmatchedIdentity}`,
2169
2177
  };
2170
2178
  }
2171
2179
  if (matches.length > 1) {
@@ -2422,12 +2430,12 @@ async function addTelegramBot(ui, flags, deps) {
2422
2430
  defaultKeyHint,
2423
2431
  );
2424
2432
  if (nonInteractive && existingKeys.has(botKey)) {
2425
- throw new Error(`Telegram bot key "${botKey}" already exists`);
2433
+ throw new Error(`Telegram local selector "${botKey}" already exists`);
2426
2434
  }
2427
2435
  const serverGroupMatched = serverBot.matchMode === "group";
2428
2436
  if (serverGroupMatched && !nonInteractive) {
2429
2437
  process.stdout.write(
2430
- `Matched server Telegram bot "${serverBot.name || username || botKey}" with roles: ${ensureArray(serverBot.roles).join(", ")}. Runtime will use the server role, so local role/AI fields can stay empty.\n`,
2438
+ `Matched server Telegram bot "${serverBot.name || username || "current Telegram bot"}" with roles: ${ensureArray(serverBot.roles).join(", ")}. Runtime will use the server role, so local role/AI fields can stay empty.\n`,
2431
2439
  );
2432
2440
  }
2433
2441
  const aiFlagOverrides = hasOwnFlag(flags, "client")
@@ -2528,7 +2536,7 @@ async function addTelegramBot(ui, flags, deps) {
2528
2536
  }
2529
2537
 
2530
2538
  const filePath = writeProviderEnvState("telegram", nextParsed, deps);
2531
- process.stdout.write(`Saved Telegram bot entry "${botKey}" to ${filePath}\n`);
2539
+ process.stdout.write(`Saved Telegram bot file for "${String(serverBot.name || username || botKey).trim() || "telegram"}" to ${filePath}\n`);
2532
2540
  }
2533
2541
 
2534
2542
  async function editTelegramGlobalSettings(ui, flags, deps) {
@@ -2559,7 +2567,7 @@ async function editTelegramGlobalSettings(ui, flags, deps) {
2559
2567
  { value: "api", label: "API base URL", description: String(parsed.TELEGRAM_API_BASE_URL || "").trim() || "(empty)" },
2560
2568
  { value: "webhook", label: "Auto clear webhook", description: boolFromRaw(parsed.TELEGRAM_AUTO_CLEAR_WEBHOOK, true) ? "true" : "false" },
2561
2569
  { value: "updates", label: "Allowed updates", description: String(parsed.TELEGRAM_ALLOWED_UPDATES || "message,edited_message").trim() },
2562
- { value: "default", label: "Default bot key", description: String(parsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim() || "(empty)" },
2570
+ { value: "default", label: "Default Telegram bot", description: String(parsed.TELEGRAM_DEFAULT_BOT_KEY || "").trim() || "(empty)" },
2563
2571
  ],
2564
2572
  { defaultIndex: 0 },
2565
2573
  );
@@ -2669,7 +2677,7 @@ async function removeTelegramBot(ui, flags, deps) {
2669
2677
  nextParsed.TELEGRAM_DEFAULT_BOT_KEY = remaining[0]?.key || "";
2670
2678
  }
2671
2679
  const filePath = writeProviderEnvState("telegram", nextParsed, deps);
2672
- process.stdout.write(`Removed Telegram bot "${selected.key}" from ${filePath}\n`);
2680
+ process.stdout.write(`Removed Telegram bot file for "${String(selected.serverBotName || selected.username || selected.key).trim() || "telegram"}" from ${filePath}\n`);
2673
2681
  }
2674
2682
 
2675
2683
  async function verifyProviderEntry(ui, provider, flags, deps) {
@@ -3105,7 +3113,7 @@ async function runBotRemove(ui, flags, deps) {
3105
3113
  nextParsed.TELEGRAM_DEFAULT_BOT_KEY = remaining[0]?.key || "";
3106
3114
  }
3107
3115
  const filePath = writeProviderEnvState("telegram", nextParsed, deps);
3108
- process.stdout.write(`Removed Telegram bot "${selected.key}" from ${filePath}\n`);
3116
+ process.stdout.write(`Removed Telegram bot file for "${String(selected.serverBotName || selected.username || selected.key).trim() || "telegram"}" from ${filePath}\n`);
3109
3117
  return;
3110
3118
  }
3111
3119
  await removeTelegramBot(ui, flags, deps);
@@ -3178,11 +3186,11 @@ async function runBotMigrate(ui, flags, deps) {
3178
3186
  const botKey = normalizeBotKey(
3179
3187
  nonInteractive
3180
3188
  ? String(flags["bot-key"] || defaultKeyHint).trim()
3181
- : await promptRequiredLine(ui, "Local Telegram bot key for migrated entry", defaultKeyHint),
3189
+ : await promptRequiredLine(ui, "Advanced local selector for migrated entry", defaultKeyHint),
3182
3190
  defaultKeyHint,
3183
3191
  );
3184
3192
  if (existingKeys.has(botKey)) {
3185
- throw new Error(`Telegram bot key "${botKey}" already exists`);
3193
+ throw new Error(`Telegram local selector "${botKey}" already exists`);
3186
3194
  }
3187
3195
  const nextParsed = upsertTelegramEntry(parsed, {
3188
3196
  key: botKey,
@@ -3203,7 +3211,7 @@ async function runBotMigrate(ui, flags, deps) {
3203
3211
  }
3204
3212
  const filePath = writeProviderEnvState("telegram", nextParsed, deps);
3205
3213
  process.stdout.write(
3206
- `Migrated TELEGRAM_BOT_TOKEN to named entry "${botKey}" in ${filePath}${boolFromRaw(flags["keep-legacy-token"], false) ? " (legacy token preserved)" : ""}\n`,
3214
+ `Migrated TELEGRAM_BOT_TOKEN to named entry selector "${botKey}" in ${filePath}${boolFromRaw(flags["keep-legacy-token"], false) ? " (legacy token preserved)" : ""}\n`,
3207
3215
  );
3208
3216
  }
3209
3217
 
@@ -63,7 +63,7 @@ export async function runDoctorProjectDestinationChecks({
63
63
  }
64
64
  const tokenDetailParts = [`configured (${envConfig.filePath})`];
65
65
  if (envConfig.botKey) {
66
- tokenDetailParts.push(`bot=${envConfig.botKey}`);
66
+ tokenDetailParts.push(`local_selector=${envConfig.botKey}`);
67
67
  }
68
68
  if (envConfig.botUsername) {
69
69
  tokenDetailParts.push(`@${envConfig.botUsername}`);
@@ -3,8 +3,8 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { spawnSync } from "node:child_process";
5
5
 
6
- export const SUPPORTED_LOCAL_AI_CLIENTS = ["codex", "claude", "gemini", "sample"];
7
- export const DEFAULT_LOCAL_AI_CLIENT = "codex";
6
+ export const SUPPORTED_LOCAL_AI_CLIENTS = ["gpt", "claude", "gemini", "sample"];
7
+ export const DEFAULT_LOCAL_AI_CLIENT = "gpt";
8
8
 
9
9
  const SUPPORTED_PERMISSION_MODES = ["read_only", "workspace_write", "danger_full_access"];
10
10
  const SUPPORTED_REASONING_EFFORTS = ["low", "medium", "high"];
@@ -18,7 +18,7 @@ const GEMINI_HOME_SYNC_FILES = [
18
18
  "settings.json",
19
19
  ];
20
20
  const LOCAL_AI_MODEL_MAPPINGS = {
21
- codex: [
21
+ gpt: [
22
22
  {
23
23
  display: "gpt-5.4",
24
24
  execution: "gpt-5.4",
@@ -589,8 +589,8 @@ function runSampleAdapter(payload) {
589
589
 
590
590
  export function normalizeLocalAIClientName(rawValue, fallback = DEFAULT_LOCAL_AI_CLIENT) {
591
591
  const value = String(rawValue || "").trim().toLowerCase();
592
- if (["gpt", "openai-gpt", "openai"].includes(value)) {
593
- return "codex";
592
+ if (["gpt", "openai-gpt", "openai", "codex"].includes(value)) {
593
+ return "gpt";
594
594
  }
595
595
  if (SUPPORTED_LOCAL_AI_CLIENTS.includes(value)) {
596
596
  return value;
@@ -734,7 +734,7 @@ export function runLocalAIClient({
734
734
  METHEUS_AI_RUNNER_REASONING_EFFORT: normalizedReasoningEffort,
735
735
  METHEUS_RUNNER_WORKSPACE_DIR: resolvedWorkspaceDir,
736
736
  };
737
- if (normalizedClient === "codex") {
737
+ if (normalizedClient === "gpt") {
738
738
  return runCodexAdapter({
739
739
  promptText,
740
740
  workspaceDir: resolvedWorkspaceDir,
@@ -95,7 +95,7 @@ function createMockServer(options = {}) {
95
95
  : [
96
96
  {
97
97
  id: "11111111-2222-3333-4444-555555555555",
98
- name: "MonitorSelftestBot",
98
+ name: "ServerProtocolMonitorBot",
99
99
  provider: "telegram",
100
100
  bot_role: "monitor",
101
101
  is_active: true,
@@ -105,8 +105,8 @@ function createMockServer(options = {}) {
105
105
  ensureArray(options.telegramUsersByToken).length
106
106
  ? ensureArray(options.telegramUsersByToken)
107
107
  : [
108
- ["selftest-main-token", "MonitorSelftestBot"],
109
- ["selftest-edited-token", "MonitorSelftestBot"],
108
+ ["selftest-main-token", "ServerProtocolMonitorBot"],
109
+ ["selftest-edited-token", "ServerProtocolMonitorBot"],
110
110
  ],
111
111
  );
112
112
 
@@ -236,20 +236,20 @@ export async function runSelftestBotCommands(push, deps) {
236
236
  try {
237
237
  push(
238
238
  "display_model_labels_map_to_tested_execution_ids",
239
- resolveLocalAIExecutionModel("codex", "gpt-5.4") === "gpt-5.4"
240
- && resolveLocalAIExecutionModel("codex", "gpt-5.3-codex") === "gpt-5.3-codex"
241
- && resolveLocalAIExecutionModel("codex", "gpt-5.3-codex-spark") === "gpt-5.3-codex-spark"
242
- && resolveLocalAIExecutionModel("codex", "GPT-5.4") === "gpt-5.4"
243
- && resolveLocalAIExecutionModel("codex", "GPT-5.3-CODEX") === "gpt-5.3-codex"
244
- && resolveLocalAIExecutionModel("codex", "GPT-5.3-CODEX-Spark") === "gpt-5.3-codex-spark"
239
+ resolveLocalAIExecutionModel("gpt", "gpt-5.4") === "gpt-5.4"
240
+ && resolveLocalAIExecutionModel("gpt", "gpt-5.3-codex") === "gpt-5.3-codex"
241
+ && resolveLocalAIExecutionModel("gpt", "gpt-5.3-codex-spark") === "gpt-5.3-codex-spark"
242
+ && resolveLocalAIExecutionModel("gpt", "GPT-5.4") === "gpt-5.4"
243
+ && resolveLocalAIExecutionModel("gpt", "GPT-5.3-CODEX") === "gpt-5.3-codex"
244
+ && resolveLocalAIExecutionModel("gpt", "GPT-5.3-CODEX-Spark") === "gpt-5.3-codex-spark"
245
245
  && resolveLocalAIExecutionModel("claude", "Sonnet 4.6r") === "sonnet"
246
246
  && resolveLocalAIExecutionModel("claude", "Haiku 4.5") === "haiku"
247
247
  && resolveLocalAIExecutionModel("claude", "Opus 4.6") === "opus"
248
248
  && resolveLocalAIExecutionModel("gemini", "gemini-3.1-pro") === "auto-gemini-3",
249
249
  [
250
- `gpt54=${resolveLocalAIExecutionModel("codex", "gpt-5.4")}`,
251
- `gpt53codex=${resolveLocalAIExecutionModel("codex", "gpt-5.3-codex")}`,
252
- `spark=${resolveLocalAIExecutionModel("codex", "gpt-5.3-codex-spark")}`,
250
+ `gpt54=${resolveLocalAIExecutionModel("gpt", "gpt-5.4")}`,
251
+ `gpt53codex=${resolveLocalAIExecutionModel("gpt", "gpt-5.3-codex")}`,
252
+ `spark=${resolveLocalAIExecutionModel("gpt", "gpt-5.3-codex-spark")}`,
253
253
  `claude=${resolveLocalAIExecutionModel("claude", "Sonnet 4.6r")}`,
254
254
  `gemini=${resolveLocalAIExecutionModel("gemini", "gemini-3.1-pro")}`,
255
255
  ].join(" "),
@@ -257,11 +257,11 @@ export async function runSelftestBotCommands(push, deps) {
257
257
 
258
258
  push(
259
259
  "blank_model_defaults_to_first_display_model_for_each_client",
260
- suggestLocalAIModelDisplayName("codex", "") === "gpt-5.4"
260
+ suggestLocalAIModelDisplayName("gpt", "") === "gpt-5.4"
261
261
  && suggestLocalAIModelDisplayName("claude", "") === "Sonnet 4.6r"
262
262
  && suggestLocalAIModelDisplayName("gemini", "") === "gemini-3.1-pro",
263
263
  [
264
- `codex=${suggestLocalAIModelDisplayName("codex", "")}`,
264
+ `gpt=${suggestLocalAIModelDisplayName("gpt", "")}`,
265
265
  `claude=${suggestLocalAIModelDisplayName("claude", "")}`,
266
266
  `gemini=${suggestLocalAIModelDisplayName("gemini", "")}`,
267
267
  ].join(" "),
@@ -290,6 +290,7 @@ export async function runSelftestBotCommands(push, deps) {
290
290
  const telegramApiBaseURL = `${baseURL}/telegram`;
291
291
  const telegramBotEntriesDir = path.join(tempHome, ".metheus", "telegram-bots");
292
292
  const telegramEnvPath = path.join(telegramBotEntriesDir, "global.env");
293
+ const botRunnerConfigPath = path.join(tempHome, ".metheus", "bot-runner.json");
293
294
  const readTelegramGlobals = () => parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
294
295
  const readTelegramBotEntry = (selector) => {
295
296
  const requested = String(selector || "").trim().toLowerCase();
@@ -309,6 +310,42 @@ export async function runSelftestBotCommands(push, deps) {
309
310
  }
310
311
  return parseSimpleEnvText(fs.readFileSync(filePath, "utf8"));
311
312
  };
313
+ fs.mkdirSync(path.join(tempHome, ".metheus"), { recursive: true });
314
+ fs.writeFileSync(
315
+ botRunnerConfigPath,
316
+ `${JSON.stringify({
317
+ version: 2,
318
+ project_mappings: {},
319
+ role_profiles: {
320
+ monitor: {
321
+ client: "gpt",
322
+ model: "",
323
+ permission_mode: "read_only",
324
+ reasoning_effort: "low",
325
+ },
326
+ review: {
327
+ client: "claude",
328
+ model: "Sonnet 4.6r",
329
+ permission_mode: "danger_full_access",
330
+ reasoning_effort: "high",
331
+ },
332
+ worker: {
333
+ client: "gpt",
334
+ model: "gpt-5.4",
335
+ permission_mode: "danger_full_access",
336
+ reasoning_effort: "high",
337
+ },
338
+ approval: {
339
+ client: "claude",
340
+ model: "Sonnet 4.6r",
341
+ permission_mode: "danger_full_access",
342
+ reasoning_effort: "high",
343
+ },
344
+ },
345
+ routes: [],
346
+ }, null, 2)}\n`,
347
+ "utf8",
348
+ );
312
349
 
313
350
  const setupResult = await runCLI({
314
351
  cliPath,
@@ -359,29 +396,29 @@ export async function runSelftestBotCommands(push, deps) {
359
396
  ]),
360
397
  },
361
398
  });
362
- const addState = readTelegramBotEntry("monitorselftestbot");
399
+ const addState = readTelegramBotEntry("serverprotocolmonitorbot");
363
400
  const addGlobals = readTelegramGlobals();
364
401
  push(
365
402
  "bot_add_guided_creates_named_telegram_entry",
366
- String(addState.TELEGRAM_BOT_SERVER_NAME || "") === "MonitorSelftestBot"
403
+ String(addState.TELEGRAM_BOT_SERVER_NAME || "") === "ServerProtocolMonitorBot"
367
404
  && String(addState.TELEGRAM_BOT_SERVER_ROLES || "") === "monitor"
368
405
  && String(addState.TELEGRAM_BOT_TOKEN || "") === "selftest-main-token"
369
406
  && String(addState.TELEGRAM_BOT_ROLE_PROFILE || "") === "monitor"
370
- && String(addState.TELEGRAM_BOT_AI_CLIENT || "") === "codex"
407
+ && String(addState.TELEGRAM_BOT_AI_CLIENT || "") === "gpt"
371
408
  && String(addState.TELEGRAM_BOT_AI_PERMISSION_MODE || "") === "read_only"
372
409
  && String(addState.TELEGRAM_BOT_AI_REASONING_EFFORT || "") === "low"
373
- && String(addGlobals.TELEGRAM_DEFAULT_BOT_KEY || "") === "monitorselftestbot",
410
+ && String(addGlobals.TELEGRAM_DEFAULT_BOT_KEY || "") === "serverprotocolmonitorbot",
374
411
  `default=${String(addGlobals.TELEGRAM_DEFAULT_BOT_KEY || "")} token=${String(addState.TELEGRAM_BOT_TOKEN || "")} server_name=${String(addState.TELEGRAM_BOT_SERVER_NAME || "")} roles=${String(addState.TELEGRAM_BOT_SERVER_ROLES || "")} role=${String(addState.TELEGRAM_BOT_ROLE_PROFILE || "")} client=${String(addState.TELEGRAM_BOT_AI_CLIENT || "")}`,
375
412
  );
376
413
 
377
414
  const groupedMock = await createMockServer({
378
415
  serverBots: [
379
- { id: "977ef999-c40b-4cf6-a142-ade246f1b9cf", name: "RyoAI_bot", provider: "telegram", bot_role: "approval", is_active: true },
380
- { id: "d74b57de-a635-4bac-a20f-f8ada188cf97", name: "RyoAI_bot", provider: "telegram", bot_role: "worker", is_active: true },
381
- { id: "bbae9450-4236-487c-9a05-61dc4a626215", name: "RyoAI_bot", provider: "telegram", bot_role: "review", is_active: true },
382
- { id: "353896c5-3b6b-422f-9eeb-5dc9354f77b3", name: "RyoAI_bot", provider: "telegram", bot_role: "monitor", is_active: true },
416
+ { id: "977ef999-c40b-4cf6-a142-ade246f1b9cf", name: "GroupedServerProtocolBot", provider: "telegram", bot_role: "approval", is_active: true },
417
+ { id: "d74b57de-a635-4bac-a20f-f8ada188cf97", name: "GroupedServerProtocolBot", provider: "telegram", bot_role: "worker", is_active: true },
418
+ { id: "bbae9450-4236-487c-9a05-61dc4a626215", name: "GroupedServerProtocolBot", provider: "telegram", bot_role: "review", is_active: true },
419
+ { id: "353896c5-3b6b-422f-9eeb-5dc9354f77b3", name: "GroupedServerProtocolBot", provider: "telegram", bot_role: "monitor", is_active: true },
383
420
  ],
384
- telegramUsersByToken: [["selftest-group-token", "RyoAI_bot"]],
421
+ telegramUsersByToken: [["selftest-group-token", "GroupedServerProtocolBot"]],
385
422
  }).listen();
386
423
  try {
387
424
  await runCLI({
@@ -410,11 +447,11 @@ export async function runSelftestBotCommands(push, deps) {
410
447
  ]),
411
448
  },
412
449
  });
413
- const groupedState = readTelegramBotEntry("ryoai_bot");
450
+ const groupedState = readTelegramBotEntry("groupedserverprotocolbot");
414
451
  push(
415
452
  "bot_add_guided_autoresolves_server_bot_group_without_role_prompt",
416
453
  String(groupedState.TELEGRAM_BOT_SERVER_BOT_ID || "") === ""
417
- && String(groupedState.TELEGRAM_BOT_SERVER_NAME || "") === "RyoAI_bot"
454
+ && String(groupedState.TELEGRAM_BOT_SERVER_NAME || "") === "GroupedServerProtocolBot"
418
455
  && String(groupedState.TELEGRAM_BOT_SERVER_ROLES || "") === "monitor,review,worker,approval"
419
456
  && String(groupedState.TELEGRAM_BOT_ROLE_PROFILE || "") === ""
420
457
  && String(groupedState.TELEGRAM_BOT_AI_CLIENT || "") === ""
@@ -433,13 +470,13 @@ export async function runSelftestBotCommands(push, deps) {
433
470
  ...env,
434
471
  METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
435
472
  "1", // provider: telegram
436
- "2", // bot entry: ryoai_bot
473
+ "1", // bot entry: GroupedServerProtocolBot
437
474
  "1", // keep token
438
475
  "2", // grouped role settings: edit one role
439
476
  "3", // select role to edit: worker
440
477
  "2", // worker: edit settings
441
478
  "3", // worker AI client: claude
442
- "3", // worker AI model: Sonnet 4.6r
479
+ "2", // worker AI model: Sonnet 4.6r
443
480
  "4", // worker permission: danger_full_access
444
481
  "4", // worker reasoning: high
445
482
  "y", // edit another role
@@ -462,15 +499,16 @@ export async function runSelftestBotCommands(push, deps) {
462
499
  );
463
500
  const groupedRunnerConfigPath = path.join(tempHome, ".metheus", "bot-runner.json");
464
501
  const groupedRunnerConfig = readJSON(fs.readFileSync(groupedRunnerConfigPath, "utf8"));
502
+ const groupedRoleProfiles = safeObject(groupedRunnerConfig.role_profiles || groupedRunnerConfig.roleProfiles);
465
503
  push(
466
504
  "bot_edit_grouped_server_roles_updates_role_profiles",
467
- String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).client || "") === "claude"
468
- && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).model || "") === "Sonnet 4.6r"
469
- && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).permission_mode || "") === "danger_full_access"
470
- && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).reasoning_effort || "") === "high"
471
- && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval).client || "") === "gemini"
472
- && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval).model || "") === "gemini-3.1-pro",
473
- `worker=${JSON.stringify(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker))} approval=${JSON.stringify(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval))}`,
505
+ String(safeObject(safeObject(groupedRoleProfiles).worker).client || "") === "claude"
506
+ && String(safeObject(safeObject(groupedRoleProfiles).worker).model || "") === "Sonnet 4.6r"
507
+ && String(safeObject(safeObject(groupedRoleProfiles).worker).permission_mode || "") === "danger_full_access"
508
+ && String(safeObject(safeObject(groupedRoleProfiles).worker).reasoning_effort || "") === "high"
509
+ && String(safeObject(safeObject(groupedRoleProfiles).approval).client || "") === "gemini"
510
+ && String(safeObject(safeObject(groupedRoleProfiles).approval).model || "") === "gemini-3.1-pro",
511
+ `worker=${JSON.stringify(safeObject(safeObject(groupedRoleProfiles).worker))} approval=${JSON.stringify(safeObject(safeObject(groupedRoleProfiles).approval))}`,
474
512
  );
475
513
 
476
514
  const groupedShowResult = await runCLI({
@@ -478,7 +516,7 @@ export async function runSelftestBotCommands(push, deps) {
478
516
  args: [
479
517
  "bot", "show",
480
518
  "--provider", "telegram",
481
- "--bot-key", "ryoai_bot",
519
+ "--bot-key", "groupedserverprotocolbot",
482
520
  "--base-url", `http://127.0.0.1:${groupedMock.port}`,
483
521
  "--json", "true",
484
522
  ],
@@ -512,7 +550,7 @@ export async function runSelftestBotCommands(push, deps) {
512
550
  args: [
513
551
  "bot", "show",
514
552
  "--provider", "telegram",
515
- "--bot-key", "monitorselftestbot",
553
+ "--bot-key", "serverprotocolmonitorbot",
516
554
  "--base-url", baseURL,
517
555
  "--json", "true",
518
556
  ],
@@ -543,7 +581,7 @@ export async function runSelftestBotCommands(push, deps) {
543
581
  args: [
544
582
  "bot", "show",
545
583
  "--provider", "telegram",
546
- "--bot-key", "monitorselftestbot",
584
+ "--bot-key", "serverprotocolmonitorbot",
547
585
  "--base-url", baseURL,
548
586
  "--json", "true",
549
587
  ],
@@ -552,8 +590,8 @@ export async function runSelftestBotCommands(push, deps) {
552
590
  const showWithRoutesPayload = readJSON(showWithRoutesResult.stdout);
553
591
  push(
554
592
  "bot_show_returns_selected_telegram_entry",
555
- safeObject(showPayload.entry).key === "monitorselftestbot"
556
- && safeObject(showPayload.entry).client === "codex"
593
+ safeObject(showPayload.entry).key === "serverprotocolmonitorbot"
594
+ && safeObject(showPayload.entry).client === "gpt"
557
595
  && safeObject(showPayload.serverBinding).mode === "single",
558
596
  `key=${String(safeObject(showPayload.entry).key || "")} client=${String(safeObject(showPayload.entry).client || "")} mode=${String(safeObject(showPayload.serverBinding).mode || "")}`,
559
597
  );
@@ -574,12 +612,12 @@ export async function runSelftestBotCommands(push, deps) {
574
612
  ...env,
575
613
  METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
576
614
  "1", // provider: telegram
577
- "1", // bot entry: @monitorselftestbot
615
+ "2", // bot entry: @serverprotocolmonitorbot
578
616
  "1", // keep token
579
617
  "2", // change AI client
580
618
  "4", // gemini
581
619
  "2", // change AI model
582
- "3", // gemini model: gemini-3.1-pro
620
+ "2", // gemini model: gemini-3.1-pro
583
621
  "2", // change permission mode
584
622
  "3", // workspace_write
585
623
  "2", // change reasoning effort
@@ -594,7 +632,7 @@ export async function runSelftestBotCommands(push, deps) {
594
632
  !String(guidedEditResult.stdout || "").includes("Telegram username (without @)"),
595
633
  String(guidedEditResult.stdout || "").split(/\r?\n/).filter((line) => line.includes("Telegram username")).join(" | ") || "username prompt skipped",
596
634
  );
597
- const guidedState = readTelegramBotEntry("monitorselftestbot");
635
+ const guidedState = readTelegramBotEntry("serverprotocolmonitorbot");
598
636
  push(
599
637
  "bot_edit_guided_prompts_update_ai_binding_fields",
600
638
  String(guidedState.TELEGRAM_BOT_AI_CLIENT || "") === "gemini"
@@ -609,7 +647,7 @@ export async function runSelftestBotCommands(push, deps) {
609
647
  args: [
610
648
  "bot", "edit",
611
649
  "--provider", "telegram",
612
- "--bot-key", "monitorselftestbot",
650
+ "--bot-key", "serverprotocolmonitorbot",
613
651
  "--non-interactive", "true",
614
652
  "--token", "selftest-edited-token",
615
653
  "--client", "claude",
@@ -619,7 +657,7 @@ export async function runSelftestBotCommands(push, deps) {
619
657
  ],
620
658
  env,
621
659
  });
622
- const editedState = readTelegramBotEntry("monitorselftestbot");
660
+ const editedState = readTelegramBotEntry("serverprotocolmonitorbot");
623
661
  push(
624
662
  "bot_edit_updates_ai_binding_fields",
625
663
  String(editedState.TELEGRAM_BOT_TOKEN || "") === "selftest-edited-token"
@@ -637,7 +675,7 @@ export async function runSelftestBotCommands(push, deps) {
637
675
  ...env,
638
676
  METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
639
677
  "1", // provider: telegram
640
- "1", // bot entry: @monitorselftestbot
678
+ "2", // bot entry: @serverprotocolmonitorbot
641
679
  "1", // confirm set default
642
680
  ]),
643
681
  },
@@ -645,7 +683,7 @@ export async function runSelftestBotCommands(push, deps) {
645
683
  const guidedDefaultState = readTelegramGlobals();
646
684
  push(
647
685
  "bot_set_default_guided_selects_entry",
648
- String(guidedDefaultState.TELEGRAM_DEFAULT_BOT_KEY || "") === "monitorselftestbot",
686
+ String(guidedDefaultState.TELEGRAM_DEFAULT_BOT_KEY || "") === "serverprotocolmonitorbot",
649
687
  `default=${String(guidedDefaultState.TELEGRAM_DEFAULT_BOT_KEY || "")}`,
650
688
  );
651
689
 
@@ -660,7 +698,7 @@ export async function runSelftestBotCommands(push, deps) {
660
698
  ...env,
661
699
  METHEUS_SCRIPTED_PROMPT_ANSWERS: JSON.stringify([
662
700
  "1", // provider: telegram
663
- "1", // bot entry: @monitorselftestbot
701
+ "2", // bot entry: @serverprotocolmonitorbot
664
702
  "2", // output format: json
665
703
  ]),
666
704
  },
@@ -680,7 +718,7 @@ export async function runSelftestBotCommands(push, deps) {
680
718
  args: [
681
719
  "bot", "set-default",
682
720
  "--provider", "telegram",
683
- "--bot-key", "monitorselftestbot",
721
+ "--bot-key", "serverprotocolmonitorbot",
684
722
  "--non-interactive", "true",
685
723
  ],
686
724
  env,
@@ -688,7 +726,7 @@ export async function runSelftestBotCommands(push, deps) {
688
726
  const defaultState = readTelegramGlobals();
689
727
  push(
690
728
  "bot_set_default_updates_default_bot_key",
691
- String(defaultState.TELEGRAM_DEFAULT_BOT_KEY || "") === "monitorselftestbot",
729
+ String(defaultState.TELEGRAM_DEFAULT_BOT_KEY || "") === "serverprotocolmonitorbot",
692
730
  `default=${String(defaultState.TELEGRAM_DEFAULT_BOT_KEY || "")}`,
693
731
  );
694
732
 
@@ -697,7 +735,7 @@ export async function runSelftestBotCommands(push, deps) {
697
735
  args: [
698
736
  "bot", "verify",
699
737
  "--provider", "telegram",
700
- "--bot-key", "monitorselftestbot",
738
+ "--bot-key", "serverprotocolmonitorbot",
701
739
  "--base-url", baseURL,
702
740
  "--timeout-seconds", "5",
703
741
  "--json", "true",
@@ -727,7 +765,7 @@ export async function runSelftestBotCommands(push, deps) {
727
765
  args: [
728
766
  "bot", "remove",
729
767
  "--provider", "telegram",
730
- "--bot-key", "monitorselftestbot",
768
+ "--bot-key", "serverprotocolmonitorbot",
731
769
  "--non-interactive", "true",
732
770
  ],
733
771
  env,
@@ -745,7 +783,7 @@ export async function runSelftestBotCommands(push, deps) {
745
783
  const telegramEntry = safeObject(listPayload[0]);
746
784
  push(
747
785
  "bot_remove_deletes_named_telegram_entry",
748
- !ensureArray(telegramEntry.entries).some((entry) => String(safeObject(entry).key || "") === "monitorselftestbot"),
786
+ !ensureArray(telegramEntry.entries).some((entry) => String(safeObject(entry).key || "") === "serverprotocolmonitorbot"),
749
787
  `entries=${String(ensureArray(telegramEntry.entries).length)}`,
750
788
  );
751
789
 
@@ -767,13 +805,13 @@ export async function runSelftestBotCommands(push, deps) {
767
805
  ],
768
806
  env,
769
807
  });
770
- const aliasAddState = readTelegramBotEntry("monitorselftestbot");
808
+ const aliasAddState = readTelegramBotEntry("serverprotocolmonitorbot");
771
809
  push(
772
810
  "bot_add_accepts_ai_prefixed_option_aliases",
773
811
  String(aliasAddState.TELEGRAM_BOT_SERVER_BOT_ID || "") === mock.bots[0].id
774
- && String(aliasAddState.TELEGRAM_BOT_SERVER_NAME || "") === "MonitorSelftestBot"
812
+ && String(aliasAddState.TELEGRAM_BOT_SERVER_NAME || "") === "ServerProtocolMonitorBot"
775
813
  && String(aliasAddState.TELEGRAM_BOT_SERVER_ROLES || "") === "monitor"
776
- && String(aliasAddState.TELEGRAM_BOT_AI_CLIENT || "") === "codex"
814
+ && String(aliasAddState.TELEGRAM_BOT_AI_CLIENT || "") === "gpt"
777
815
  && String(aliasAddState.TELEGRAM_BOT_AI_MODEL || "") === "gpt-5.4"
778
816
  && String(aliasAddState.TELEGRAM_BOT_AI_PERMISSION_MODE || "") === "read_only"
779
817
  && String(aliasAddState.TELEGRAM_BOT_AI_REASONING_EFFORT || "") === "low",
@@ -792,7 +830,7 @@ export async function runSelftestBotCommands(push, deps) {
792
830
  const guidedRemoveBeforePayload = ensureArray(readJSON(guidedRemoveBeforeList.stdout));
793
831
  const guidedRemoveBeforeEntry = safeObject(guidedRemoveBeforePayload[0]);
794
832
  const guidedRemoveEntries = ensureArray(guidedRemoveBeforeEntry.entries);
795
- const explicitEntryIndex = guidedRemoveEntries.findIndex((entry) => String(safeObject(entry).key || "") === "monitorselftestbot");
833
+ const explicitEntryIndex = guidedRemoveEntries.findIndex((entry) => String(safeObject(entry).key || "") === "serverprotocolmonitorbot");
796
834
 
797
835
  await runCLI({
798
836
  cliPath,
@@ -838,7 +876,7 @@ export async function runSelftestBotCommands(push, deps) {
838
876
  "--provider", "telegram",
839
877
  "--bot-key", "legacy_main",
840
878
  "--bot-id", mock.bots[0].id,
841
- "--bot-name", "MonitorSelftestBot",
879
+ "--bot-name", "ServerProtocolMonitorBot",
842
880
  "--role-profile", "monitor",
843
881
  "--client", "codex",
844
882
  "--permission-mode", "read_only",
@@ -186,9 +186,9 @@ export async function runSelftestRunnerScenarios(push, deps) {
186
186
  project_id: selftestProjectID,
187
187
  provider: "telegram",
188
188
  role: "monitor",
189
- bot_name: "RyoAI_bot",
189
+ bot_name: "ServerProtocolMonitorBot",
190
190
  }),
191
- { name: "RyoAI_bot", role: "review" },
191
+ { name: "ServerProtocolMonitorBot", role: "review" },
192
192
  normalizeBotRunnerConfigContents(
193
193
  {
194
194
  version: 2,
@@ -211,7 +211,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
211
211
  },
212
212
  },
213
213
  bot_bindings: {
214
- RyoAI_bot: {
214
+ ServerProtocolMonitorBot: {
215
215
  role_profile: "approval",
216
216
  },
217
217
  },
@@ -237,9 +237,9 @@ export async function runSelftestRunnerScenarios(push, deps) {
237
237
  project_id: selftestProjectID,
238
238
  provider: "telegram",
239
239
  role: "monitor",
240
- bot_name: "RyoAI_bot",
240
+ bot_name: "ServerProtocolMonitorBot",
241
241
  }),
242
- { name: "RyoAI_bot", role: "monitor" },
242
+ { name: "ServerProtocolMonitorBot", role: "monitor" },
243
243
  normalizeBotRunnerConfigContents(
244
244
  {
245
245
  version: 2,
@@ -257,7 +257,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
257
257
  },
258
258
  },
259
259
  bot_bindings: {
260
- RyoAI_bot: {
260
+ ServerProtocolMonitorBot: {
261
261
  role_profile: "monitor",
262
262
  client: "claude",
263
263
  reasoning_effort: "high",
@@ -435,7 +435,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
435
435
  provider: "telegram",
436
436
  role: "monitor",
437
437
  role_profile: "monitor",
438
- bot_name: "RyoAI_bot",
438
+ bot_name: "ServerProtocolMonitorBot",
439
439
  destination_label: "AI incubating CHAT ROOM",
440
440
  archive_work_item_id: "304ce77a-7032-421c-aeda-bc54daf088dd",
441
441
  },
@@ -451,7 +451,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
451
451
  provider: "telegram",
452
452
  role: "monitor",
453
453
  "role-profile": "monitor",
454
- "bot-name": "RyoAI_bot",
454
+ "bot-name": "ServerProtocolMonitorBot",
455
455
  "dry-run-delivery": true,
456
456
  },
457
457
  "once",
@@ -463,7 +463,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
463
463
  && resolvedRunnerRoute.name === "telegram-monitor"
464
464
  && resolvedRunnerRoute.destinationLabel === "AI incubating CHAT ROOM"
465
465
  && resolvedRunnerRoute.dryRunDelivery === true
466
- && runnerRouteKey(resolvedRunnerRoute) === "telegram-monitor::11111111-1111-1111-1111-111111111111::telegram::monitor::RyoAI_bot::AI incubating CHAT ROOM",
466
+ && runnerRouteKey(resolvedRunnerRoute) === "telegram-monitor::11111111-1111-1111-1111-111111111111::telegram::monitor::ServerProtocolMonitorBot::AI incubating CHAT ROOM",
467
467
  `name=${resolvedRunnerRoute.name || "(none)"} destination=${resolvedRunnerRoute.destinationLabel || "(none)"} key=${runnerRouteKey(resolvedRunnerRoute)}`,
468
468
  );
469
469
  } catch (err) {
@@ -515,7 +515,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
515
515
  provider: "telegram",
516
516
  role: "monitor",
517
517
  role_profile: "monitor",
518
- bot_name: "RyoAI_bot",
518
+ bot_name: "ServerProtocolMonitorBot",
519
519
  destination_label: "AI incubating CHAT ROOM",
520
520
  },
521
521
  ],
@@ -527,7 +527,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
527
527
  `${JSON.stringify({
528
528
  version: 1,
529
529
  routes: {
530
- "-::11111111-1111-1111-1111-111111111111::telegram::monitor::RyoAI_bot::-": {
530
+ "-::11111111-1111-1111-1111-111111111111::telegram::monitor::ServerProtocolMonitorBot::-": {
531
531
  last_processed_comment_id: "comment-a",
532
532
  last_processed_created_at: "2026-03-13T06:00:00.000Z",
533
533
  last_source_message_id: 41,
@@ -543,7 +543,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
543
543
  process.env.HOME = migrationHome;
544
544
  process.env.USERPROFILE = migrationHome;
545
545
  const migratedRunnerState = loadBotRunnerState();
546
- const canonicalRunnerStateKey = "telegram-monitor::11111111-1111-1111-1111-111111111111::telegram::monitor::RyoAI_bot::AI incubating CHAT ROOM";
546
+ const canonicalRunnerStateKey = "telegram-monitor::11111111-1111-1111-1111-111111111111::telegram::monitor::ServerProtocolMonitorBot::AI incubating CHAT ROOM";
547
547
  const persistedRunnerState = tryJsonParse(
548
548
  fs.readFileSync(path.join(migrationMetheusDir, "bot-runner-state.json"), "utf8"),
549
549
  );
@@ -552,7 +552,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
552
552
  migratedRunnerState.migrated === true
553
553
  && migratedRunnerState.migratedKeys.length === 1
554
554
  && Boolean(migratedRunnerState.routes[canonicalRunnerStateKey])
555
- && !migratedRunnerState.routes["-::11111111-1111-1111-1111-111111111111::telegram::monitor::RyoAI_bot::-"]
555
+ && !migratedRunnerState.routes["-::11111111-1111-1111-1111-111111111111::telegram::monitor::ServerProtocolMonitorBot::-"]
556
556
  && Boolean(safeObject(persistedRunnerState.routes)[canonicalRunnerStateKey]),
557
557
  `migrated=${String(migratedRunnerState.migrated)} key=${canonicalRunnerStateKey}`,
558
558
  );
@@ -619,7 +619,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
619
619
  },
620
620
  },
621
621
  mentionOnlyRoute,
622
- { name: "RyoAI_bot", role: "monitor" },
622
+ { name: "ServerProtocolMonitorBot", role: "monitor" },
623
623
  );
624
624
  push(
625
625
  "telegram_trigger_mentions_only_skips_unmentioned_group_message",
@@ -634,13 +634,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
634
634
  kind: "telegram_message",
635
635
  chatID: "-100123",
636
636
  chatType: "supergroup",
637
- body: "hello @RyoAI_bot",
637
+ body: "hello @ServerProtocolMonitorBot",
638
638
  mentionUsernames: ["ryoai_bot"],
639
639
  replyToSenderIsBot: false,
640
640
  },
641
641
  },
642
642
  mentionOnlyRoute,
643
- { name: "RyoAI_bot", role: "monitor" },
643
+ { name: "ServerProtocolMonitorBot", role: "monitor" },
644
644
  );
645
645
  push(
646
646
  "telegram_trigger_mentions_only_accepts_bot_mention",
@@ -650,7 +650,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
650
650
 
651
651
  const botReplyArchiveComment = formatBotReplyArchiveComment({
652
652
  provider: "telegram",
653
- bot: { id: "bot-1", name: "RyoAI_bot", role: "monitor" },
653
+ bot: { id: "bot-1", name: "ServerProtocolMonitorBot", role: "monitor" },
654
654
  destination: { chatID: "-100123", label: "Main Room" },
655
655
  replyText: "hello",
656
656
  messageID: 4321,
@@ -234,7 +234,7 @@ export async function runSelftestTelegramE2E(push, deps) {
234
234
  const e2eTelegramToken = "123456:test-token";
235
235
  const e2eBot = {
236
236
  id: "55555555-5555-4555-8555-555555555555",
237
- name: "RyoAI_bot",
237
+ name: "ServerProtocolMonitorBot",
238
238
  role: "monitor",
239
239
  };
240
240
  const e2eDestination = {
@@ -356,7 +356,7 @@ export async function runSelftestTelegramE2E(push, deps) {
356
356
  && e2eResult.execution_mode === "role_profile"
357
357
  && e2eResult.role_profile === "monitor"
358
358
  && telegramE2EServer.state.sentMessages.length === 1
359
- && String(telegramE2EServer.state.sentMessages[0]?.text || "").trim() === "Acknowledged: hello @RyoAI_bot"
359
+ && String(telegramE2EServer.state.sentMessages[0]?.text || "").trim() === "Acknowledged: hello @ServerProtocolMonitorBot"
360
360
  && intFromRawAllowZero(telegramE2EServer.state.sentMessages[0]?.reply_to_message_id, 0) === 41
361
361
  && telegramE2EServer.state.chatActions.length >= 1
362
362
  && Boolean(mirroredReply)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.104",
3
+ "version": "0.2.106",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [
package/postinstall.mjs CHANGED
@@ -75,7 +75,7 @@ function botRunnerTemplate() {
75
75
  },
76
76
  role_profiles: {
77
77
  monitor: {
78
- client: "codex",
78
+ client: "gpt",
79
79
  model: "",
80
80
  permission_mode: "read_only",
81
81
  reasoning_effort: "low",
@@ -87,7 +87,7 @@ function botRunnerTemplate() {
87
87
  reasoning_effort: "medium",
88
88
  },
89
89
  worker: {
90
- client: "codex",
90
+ client: "gpt",
91
91
  model: "",
92
92
  permission_mode: "danger_full_access",
93
93
  reasoning_effort: "medium",
@@ -104,7 +104,7 @@ function botRunnerTemplate() {
104
104
  bot_id: "<server_bot_uuid>",
105
105
  bot_name: "<bot_name>",
106
106
  role_profile: "monitor",
107
- client: "codex",
107
+ client: "gpt",
108
108
  model: "",
109
109
  permission_mode: "read_only",
110
110
  reasoning_effort: "low",