metheus-governance-mcp-cli 0.2.84 → 0.2.86

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
@@ -241,7 +241,7 @@ Behavior:
241
241
  - `Sonnet 4.6r` -> `sonnet`
242
242
  - `Haiku 4.5` -> `haiku`
243
243
  - `Opus 4.6` -> `opus`
244
- - `Gemini 3.1 Pro` -> `gemini-2.5-pro`
244
+ - `gemini-3.1-pro` -> `auto-gemini-3`
245
245
  - if one server bot name maps to multiple server roles, `bot edit` keeps the Telegram env entry bound to the server identity and lets you review the local `role_profiles` for each detected role instead of forcing one role/profile UUID choice up front.
246
246
  - In the normal Telegram edit path, the CLI keeps or re-resolves the server bot binding automatically. It no longer asks you to pick `approval / worker / review / monitor` or a server bot UUID first.
247
247
  - `bot set-default` without flags starts a guided numbered flow: provider -> bot entry -> confirm default change.
@@ -410,7 +410,8 @@ Recommended role mapping:
410
410
  Role profile note:
411
411
  - Claude maps `reasoning_effort` to `--effort`.
412
412
  - Codex maps `reasoning_effort` to `-c model_reasoning_effort="..."`.
413
- - Gemini CLI still has no dedicated effort flag, so the runner keeps the value in env/prompt context for policy parity.
413
+ - Gemini applies `reasoning_effort` by injecting a temporary `~/.gemini/settings.json` override for the invocation.
414
+ - For Gemini 3 models, `low -> thinkingLevel LOW`, `medium -> THINKING_LEVEL_UNSPECIFIED` (Gemini default), and `high -> thinkingLevel HIGH`.
414
415
 
415
416
  Trigger policy fields:
416
417
  - `mentions_only`: in groups, react only when the bot is mentioned or when a message replies to the bot
package/cli.mjs CHANGED
@@ -11,6 +11,7 @@ import https from "node:https";
11
11
  import {
12
12
  DEFAULT_LOCAL_AI_CLIENT,
13
13
  resolveLocalAIExecutionModel,
14
+ resolveGeminiReasoningConfig,
14
15
  SUPPORTED_LOCAL_AI_CLIENTS,
15
16
  normalizeLocalAIClientName,
16
17
  normalizeLocalAIPermissionMode,
@@ -3358,6 +3359,7 @@ function buildBotCommandDeps() {
3358
3359
  normalizeLocalAIClientName,
3359
3360
  normalizeLocalAIPermissionMode,
3360
3361
  normalizeLocalAIReasoningEffort,
3362
+ resolveGeminiReasoningConfig,
3361
3363
  supportedLocalAIClients: SUPPORTED_LOCAL_AI_CLIENTS,
3362
3364
  summarizeProviderSupport,
3363
3365
  loadProviderEnvConfig,
@@ -5209,6 +5211,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
5209
5211
  cliPath: fileURLToPath(import.meta.url),
5210
5212
  parseSimpleEnvText,
5211
5213
  resolveLocalAIExecutionModel,
5214
+ resolveGeminiReasoningConfig,
5212
5215
  });
5213
5216
 
5214
5217
  const payload = buildSelftestPayload(checks);
@@ -1473,12 +1473,12 @@ function suggestedAIModelsForClient(clientName) {
1473
1473
  { value: "Haiku 4.5", label: "Haiku 4.5", description: "display label; runs as haiku" },
1474
1474
  { value: "Opus 4.6", label: "Opus 4.6", description: "display label; runs as opus" },
1475
1475
  ];
1476
- }
1477
- if (normalizedClient === "gemini") {
1478
- return [
1479
- { value: "Gemini 3.1 Pro", label: "Gemini 3.1 Pro", description: "display label; runs as gemini-2.5-pro" },
1480
- ];
1481
- }
1476
+ }
1477
+ if (normalizedClient === "gemini") {
1478
+ return [
1479
+ { value: "gemini-3.1-pro", label: "gemini-3.1-pro", description: "display label; runs as auto-gemini-3" },
1480
+ ];
1481
+ }
1482
1482
  if (normalizedClient === "sample") {
1483
1483
  return [
1484
1484
  { value: "sample", label: "sample", description: "sample adapter model placeholder" },
@@ -8,6 +8,15 @@ export const DEFAULT_LOCAL_AI_CLIENT = "codex";
8
8
 
9
9
  const SUPPORTED_PERMISSION_MODES = ["read_only", "workspace_write", "danger_full_access"];
10
10
  const SUPPORTED_REASONING_EFFORTS = ["low", "medium", "high"];
11
+ const GEMINI_HOME_SYNC_FILES = [
12
+ "google_accounts.json",
13
+ "oauth_creds.json",
14
+ "installation_id",
15
+ "state.json",
16
+ "trustedFolders.json",
17
+ "projects.json",
18
+ "settings.json",
19
+ ];
11
20
  const LOCAL_AI_MODEL_MAPPINGS = {
12
21
  codex: [
13
22
  {
@@ -45,9 +54,16 @@ const LOCAL_AI_MODEL_MAPPINGS = {
45
54
  ],
46
55
  gemini: [
47
56
  {
48
- display: "Gemini 3.1 Pro",
49
- execution: "gemini-2.5-pro",
50
- aliases: ["gemini 3.1 pro", "gemini-3.1-pro", "gemini-2.5-pro", "gemini 2.5 pro"],
57
+ display: "gemini-3.1-pro",
58
+ execution: "auto-gemini-3",
59
+ aliases: [
60
+ "gemini-3.1-pro",
61
+ "gemini 3.1 pro",
62
+ "Gemini 3.1 Pro",
63
+ "gemini-3.1-pro-preview",
64
+ "auto-gemini-3",
65
+ "pro",
66
+ ],
51
67
  },
52
68
  ],
53
69
  sample: [],
@@ -73,6 +89,10 @@ function ensureArray(value) {
73
89
  return Array.isArray(value) ? value : [];
74
90
  }
75
91
 
92
+ function safeObject(value) {
93
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
94
+ }
95
+
76
96
  function intFromRawAllowZero(rawValue, fallback = 0) {
77
97
  const parsed = Number.parseInt(String(rawValue ?? "").trim(), 10);
78
98
  if (!Number.isFinite(parsed) || parsed < 0) {
@@ -323,6 +343,122 @@ function buildGeminiArgs({ promptText, model, permissionMode }) {
323
343
  return args;
324
344
  }
325
345
 
346
+ function isGemini3ExecutionModel(model) {
347
+ const normalizedModel = normalizeModelAliasText(model);
348
+ return normalizedModel === "auto-gemini-3" || normalizedModel.startsWith("gemini-3");
349
+ }
350
+
351
+ function readJsonFileIfExists(filePath) {
352
+ if (!filePath || !fs.existsSync(filePath)) {
353
+ return null;
354
+ }
355
+ try {
356
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
357
+ } catch {
358
+ return null;
359
+ }
360
+ }
361
+
362
+ function copyFileIfExists(sourcePath, targetPath) {
363
+ if (!sourcePath || !targetPath || !fs.existsSync(sourcePath)) {
364
+ return;
365
+ }
366
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
367
+ fs.copyFileSync(sourcePath, targetPath);
368
+ }
369
+
370
+ function buildGeminiThinkingConfig(model, reasoningEffort) {
371
+ const normalizedReasoningEffort = normalizeLocalAIReasoningEffort(reasoningEffort, "");
372
+ if (!normalizedReasoningEffort) {
373
+ return null;
374
+ }
375
+ if (isGemini3ExecutionModel(model)) {
376
+ if (normalizedReasoningEffort === "low") {
377
+ return { thinkingLevel: "LOW" };
378
+ }
379
+ if (normalizedReasoningEffort === "high") {
380
+ return { thinkingLevel: "HIGH" };
381
+ }
382
+ return { thinkingLevel: "THINKING_LEVEL_UNSPECIFIED" };
383
+ }
384
+ const thinkingBudgetMap = {
385
+ low: 0,
386
+ medium: 4096,
387
+ high: 8192,
388
+ };
389
+ return {
390
+ thinkingBudget: thinkingBudgetMap[normalizedReasoningEffort] ?? thinkingBudgetMap.medium,
391
+ };
392
+ }
393
+
394
+ export function resolveGeminiReasoningConfig(rawModelValue = "", reasoningEffort = "medium") {
395
+ const executionModel = resolveLocalAIExecutionModel("gemini", rawModelValue);
396
+ const thinkingConfig = buildGeminiThinkingConfig(executionModel, reasoningEffort);
397
+ if (!thinkingConfig) {
398
+ return null;
399
+ }
400
+ return {
401
+ model: executionModel,
402
+ thinkingConfig,
403
+ };
404
+ }
405
+
406
+ function buildGeminiSettingsOverride(existingSettings, model, reasoningEffort) {
407
+ const resolved = resolveGeminiReasoningConfig(model, reasoningEffort);
408
+ if (!resolved) {
409
+ return safeObject(existingSettings);
410
+ }
411
+ const baseSettings = safeObject(existingSettings);
412
+ const baseModelConfigs = safeObject(baseSettings.modelConfigs);
413
+ const nextOverrides = ensureArray(baseModelConfigs.overrides).slice();
414
+ nextOverrides.push({
415
+ match: { model: resolved.model },
416
+ modelConfig: {
417
+ generateContentConfig: {
418
+ thinkingConfig: resolved.thinkingConfig,
419
+ },
420
+ },
421
+ });
422
+ return {
423
+ ...baseSettings,
424
+ modelConfigs: {
425
+ ...baseModelConfigs,
426
+ overrides: nextOverrides,
427
+ },
428
+ };
429
+ }
430
+
431
+ function prepareGeminiRuntimeEnv({ model, reasoningEffort, env }) {
432
+ const sourceHome = String(env?.GEMINI_CLI_HOME || os.homedir() || "").trim();
433
+ const sourceGeminiDir = sourceHome ? path.join(sourceHome, ".gemini") : "";
434
+ const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-gemini-home-"));
435
+ const tempGeminiDir = path.join(tempHome, ".gemini");
436
+ fs.mkdirSync(tempGeminiDir, { recursive: true });
437
+ for (const fileName of GEMINI_HOME_SYNC_FILES) {
438
+ copyFileIfExists(
439
+ sourceGeminiDir ? path.join(sourceGeminiDir, fileName) : "",
440
+ path.join(tempGeminiDir, fileName),
441
+ );
442
+ }
443
+ const mergedSettings = buildGeminiSettingsOverride(
444
+ readJsonFileIfExists(path.join(tempGeminiDir, "settings.json")),
445
+ model,
446
+ reasoningEffort,
447
+ );
448
+ fs.writeFileSync(path.join(tempGeminiDir, "settings.json"), `${JSON.stringify(mergedSettings, null, 2)}\n`, "utf8");
449
+ return {
450
+ env: {
451
+ ...env,
452
+ GEMINI_CLI_HOME: tempHome,
453
+ },
454
+ cleanup() {
455
+ if (fs.existsSync(tempHome)) {
456
+ fs.rmSync(tempHome, { recursive: true, force: true });
457
+ }
458
+ },
459
+ };
460
+ }
461
+
326
462
  function runCodexAdapter({ promptText, workspaceDir, model, permissionMode, reasoningEffort, env }) {
327
463
  const outputPath = path.join(os.tmpdir(), `metheus-runner-codex-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`);
328
464
  const codexCommand = resolveLocalCliCommand("codex");
@@ -385,29 +521,34 @@ function runClaudeAdapter({ promptText, workspaceDir, model, permissionMode, rea
385
521
  return normalizeCliOutput(result.stdout);
386
522
  }
387
523
 
388
- function runGeminiAdapter({ promptText, workspaceDir, model, permissionMode, env }) {
524
+ function runGeminiAdapter({ promptText, workspaceDir, model, permissionMode, reasoningEffort, env }) {
389
525
  const geminiCommand = resolveLocalCliCommand("gemini");
390
- const result = spawnCli(
391
- geminiCommand,
392
- buildGeminiArgs({
393
- promptText,
394
- model,
395
- permissionMode,
396
- }),
397
- {
398
- cwd: workspaceDir,
399
- encoding: "utf8",
400
- env,
401
- maxBuffer: 8 * 1024 * 1024,
402
- },
403
- );
404
- if (result.error) {
405
- throw new Error(String(result.error?.message || result.error));
406
- }
407
- if (result.status !== 0) {
408
- throw new Error(String(result.stderr || result.stdout || `gemini exited with status ${result.status}`));
526
+ const runtime = prepareGeminiRuntimeEnv({ model, reasoningEffort, env });
527
+ try {
528
+ const result = spawnCli(
529
+ geminiCommand,
530
+ buildGeminiArgs({
531
+ promptText,
532
+ model,
533
+ permissionMode,
534
+ }),
535
+ {
536
+ cwd: workspaceDir,
537
+ encoding: "utf8",
538
+ env: runtime.env,
539
+ maxBuffer: 8 * 1024 * 1024,
540
+ },
541
+ );
542
+ if (result.error) {
543
+ throw new Error(String(result.error?.message || result.error));
544
+ }
545
+ if (result.status !== 0) {
546
+ throw new Error(String(result.stderr || result.stdout || `gemini exited with status ${result.status}`));
547
+ }
548
+ return normalizeCliOutput(result.stdout);
549
+ } finally {
550
+ runtime.cleanup();
409
551
  }
410
- return normalizeCliOutput(result.stdout);
411
552
  }
412
553
 
413
554
  function runSampleAdapter(payload) {
@@ -601,6 +742,7 @@ export function runLocalAIClient({
601
742
  workspaceDir: resolvedWorkspaceDir,
602
743
  model: resolvedExecutionModel,
603
744
  permissionMode: normalizedPermissionMode,
745
+ reasoningEffort: normalizedReasoningEffort,
604
746
  env: nextEnv,
605
747
  });
606
748
  }
@@ -229,6 +229,7 @@ export async function runSelftestBotCommands(push, deps) {
229
229
  const cliPath = String(requireDependency(deps, "cliPath") || "").trim();
230
230
  const parseSimpleEnvText = requireDependency(deps, "parseSimpleEnvText");
231
231
  const resolveLocalAIExecutionModel = requireDependency(deps, "resolveLocalAIExecutionModel");
232
+ const resolveGeminiReasoningConfig = requireDependency(deps, "resolveGeminiReasoningConfig");
232
233
  let tempHome = "";
233
234
  let mock = null;
234
235
  try {
@@ -243,13 +244,29 @@ export async function runSelftestBotCommands(push, deps) {
243
244
  && resolveLocalAIExecutionModel("claude", "Sonnet 4.6r") === "sonnet"
244
245
  && resolveLocalAIExecutionModel("claude", "Haiku 4.5") === "haiku"
245
246
  && resolveLocalAIExecutionModel("claude", "Opus 4.6") === "opus"
246
- && resolveLocalAIExecutionModel("gemini", "Gemini 3.1 Pro") === "gemini-2.5-pro",
247
+ && resolveLocalAIExecutionModel("gemini", "gemini-3.1-pro") === "auto-gemini-3",
247
248
  [
248
249
  `gpt54=${resolveLocalAIExecutionModel("codex", "gpt-5.4")}`,
249
250
  `gpt53codex=${resolveLocalAIExecutionModel("codex", "gpt-5.3-codex")}`,
250
251
  `spark=${resolveLocalAIExecutionModel("codex", "gpt-5.3-codex-spark")}`,
251
252
  `claude=${resolveLocalAIExecutionModel("claude", "Sonnet 4.6r")}`,
252
- `gemini=${resolveLocalAIExecutionModel("gemini", "Gemini 3.1 Pro")}`,
253
+ `gemini=${resolveLocalAIExecutionModel("gemini", "gemini-3.1-pro")}`,
254
+ ].join(" "),
255
+ );
256
+
257
+ const geminiLowReasoning = resolveGeminiReasoningConfig("gemini-3.1-pro", "low");
258
+ const geminiMediumReasoning = resolveGeminiReasoningConfig("gemini-3.1-pro", "medium");
259
+ const geminiHighReasoning = resolveGeminiReasoningConfig("gemini-3.1-pro", "high");
260
+ push(
261
+ "gemini_reasoning_effort_maps_to_runtime_settings_override",
262
+ String(geminiLowReasoning?.model || "") === "auto-gemini-3"
263
+ && String(safeObject(geminiLowReasoning?.thinkingConfig).thinkingLevel || "") === "LOW"
264
+ && String(safeObject(geminiMediumReasoning?.thinkingConfig).thinkingLevel || "") === "THINKING_LEVEL_UNSPECIFIED"
265
+ && String(safeObject(geminiHighReasoning?.thinkingConfig).thinkingLevel || "") === "HIGH",
266
+ [
267
+ `low=${JSON.stringify(geminiLowReasoning?.thinkingConfig || {})}`,
268
+ `medium=${JSON.stringify(geminiMediumReasoning?.thinkingConfig || {})}`,
269
+ `high=${JSON.stringify(geminiHighReasoning?.thinkingConfig || {})}`,
253
270
  ].join(" "),
254
271
  );
255
272
 
@@ -393,7 +410,7 @@ export async function runSelftestBotCommands(push, deps) {
393
410
  "3", // select role to edit: approval
394
411
  "2", // approval: edit settings
395
412
  "4", // approval AI client: gemini
396
- "2", // approval AI model: Gemini 3.1 Pro
413
+ "2", // approval AI model: gemini-3.1-pro
397
414
  "4", // approval permission: danger_full_access
398
415
  "4", // approval reasoning: high
399
416
  "n", // stop editing roles
@@ -416,7 +433,7 @@ export async function runSelftestBotCommands(push, deps) {
416
433
  && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).permission_mode || "") === "danger_full_access"
417
434
  && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker).reasoning_effort || "") === "high"
418
435
  && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval).client || "") === "gemini"
419
- && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval).model || "") === "Gemini 3.1 Pro",
436
+ && String(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval).model || "") === "gemini-3.1-pro",
420
437
  `worker=${JSON.stringify(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).worker))} approval=${JSON.stringify(safeObject(safeObject(groupedRunnerConfig.role_profiles || {}).approval))}`,
421
438
  );
422
439
 
@@ -525,7 +542,7 @@ export async function runSelftestBotCommands(push, deps) {
525
542
  "2", // change AI client
526
543
  "4", // gemini
527
544
  "2", // change AI model
528
- "2", // gemini model: Gemini 3.1 Pro
545
+ "2", // gemini model: gemini-3.1-pro
529
546
  "2", // change permission mode
530
547
  "3", // workspace_write
531
548
  "2", // change reasoning effort
@@ -544,7 +561,7 @@ export async function runSelftestBotCommands(push, deps) {
544
561
  push(
545
562
  "bot_edit_guided_prompts_update_ai_binding_fields",
546
563
  String(guidedState.TELEGRAM_BOT_MONITORSELFTESTBOT_AI_CLIENT || "") === "gemini"
547
- && String(guidedState.TELEGRAM_BOT_MONITORSELFTESTBOT_AI_MODEL || "") === "Gemini 3.1 Pro"
564
+ && String(guidedState.TELEGRAM_BOT_MONITORSELFTESTBOT_AI_MODEL || "") === "gemini-3.1-pro"
548
565
  && String(guidedState.TELEGRAM_BOT_MONITORSELFTESTBOT_AI_PERMISSION_MODE || "") === "workspace_write"
549
566
  && String(guidedState.TELEGRAM_BOT_MONITORSELFTESTBOT_AI_REASONING_EFFORT || "") === "medium",
550
567
  `client=${String(guidedState.TELEGRAM_BOT_MONITORSELFTESTBOT_AI_CLIENT || "")} model=${String(guidedState.TELEGRAM_BOT_MONITORSELFTESTBOT_AI_MODEL || "")}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.84",
3
+ "version": "0.2.86",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [