metheus-governance-mcp-cli 0.2.282 → 0.2.283

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.mjs CHANGED
@@ -25,9 +25,10 @@ import {
25
25
  resolveRolePlannerAuditorModelDisplayName,
26
26
  resolveRolePlannerModelDisplayName,
27
27
  resolveRolePlannerRepairModelDisplayName,
28
- resolveResponderAdjudicatorModelDisplayName,
29
- resolveGeminiReasoningConfig,
30
- suggestLocalAIModelDisplayName,
28
+ resolveResponderAdjudicatorModelDisplayName,
29
+ resolveGeminiHeadlessExecutionModel,
30
+ resolveGeminiReasoningConfig,
31
+ suggestLocalAIModelDisplayName,
31
32
  SUPPORTED_LOCAL_AI_CLIENTS,
32
33
  normalizeLocalAIClientName,
33
34
  normalizeLocalAIPermissionMode,
@@ -4144,12 +4145,14 @@ function buildRunnerValidationAndDeliverySummary({
4144
4145
  responseContractValidationStatus = "",
4145
4146
  responseContractValidationReason = "",
4146
4147
  responseContractValidationTargets = [],
4147
- assignmentValidationStatus = "",
4148
- assignmentValidationReason = "",
4149
- assignmentValidationModes = [],
4150
- deliveryStatus = "",
4151
- archiveStatus = "",
4152
- transportError = "",
4148
+ assignmentValidationStatus = "",
4149
+ assignmentValidationReason = "",
4150
+ assignmentValidationModes = [],
4151
+ failureReplyClassification = "",
4152
+ failureFacts = {},
4153
+ deliveryStatus = "",
4154
+ archiveStatus = "",
4155
+ transportError = "",
4153
4156
  archiveError = "",
4154
4157
  sourceMessageEnvelope = {},
4155
4158
  lastReplyMessageEnvelope = {},
@@ -6381,12 +6384,14 @@ function markRunnerRequestLifecycle({
6381
6384
  responseContractValidationStatus = "",
6382
6385
  responseContractValidationReason = "",
6383
6386
  responseContractValidationTargets = [],
6384
- assignmentValidationStatus = "",
6385
- assignmentValidationReason = "",
6386
- assignmentValidationModes = [],
6387
- deliveryStatus = "",
6388
- archiveStatus = "",
6389
- transportError = "",
6387
+ assignmentValidationStatus = "",
6388
+ assignmentValidationReason = "",
6389
+ assignmentValidationModes = [],
6390
+ failureReplyClassification = "",
6391
+ failureFacts = {},
6392
+ deliveryStatus = "",
6393
+ archiveStatus = "",
6394
+ transportError = "",
6390
6395
  archiveError = "",
6391
6396
  lastReplyMessageID = 0,
6392
6397
  lastReplyMessageThreadID = 0,
@@ -6497,6 +6502,8 @@ function markRunnerRequestLifecycle({
6497
6502
  || "",
6498
6503
  ).trim().toLowerCase();
6499
6504
  const normalizedOutcome = String(outcome || "").trim().toLowerCase();
6505
+ const normalizedFailureReplyClassification = String(failureReplyClassification || "").trim().toLowerCase();
6506
+ const normalizedFailureFacts = safeObject(failureFacts);
6500
6507
  const shouldRemainRunningAfterReply = authoritativeDecisionBundle.should_close_after_reply === true
6501
6508
  ? false
6502
6509
  : authoritativeDecisionBundle.should_close_after_reply === false
@@ -6511,6 +6518,18 @@ function markRunnerRequestLifecycle({
6511
6518
  || rootEffectiveNextExpectedResponders.length > 0
6512
6519
  || continuationSelectors.length > 0
6513
6520
  );
6521
+ const shouldRemainRunningAfterError = ["error", "execution_failed"].includes(normalizedOutcome)
6522
+ && (
6523
+ normalizedFailureFacts.retryable === true
6524
+ || normalizedFailureReplyClassification === "retryable_failure"
6525
+ )
6526
+ && authoritativeDecisionBundle.should_close_after_reply !== true
6527
+ && (
6528
+ nextExecutionContractType === "delegation"
6529
+ || rootEffectiveExecutionContractTargets.length > 0
6530
+ || rootEffectiveNextExpectedResponders.length > 0
6531
+ || continuationSelectors.length > 0
6532
+ );
6514
6533
  const nextConversationIntentMode = String(
6515
6534
  authoritativeDecisionBundle.conversation_intent_mode
6516
6535
  || conversationIntentMode
@@ -6547,7 +6566,7 @@ function markRunnerRequestLifecycle({
6547
6566
  || normalizedOutcome === "execution_failed"
6548
6567
  || normalizedOutcome === "policy_violation"
6549
6568
  ) {
6550
- return "closed";
6569
+ return shouldRemainRunningAfterError ? "running" : "closed";
6551
6570
  }
6552
6571
  return normalizeRunnerRequestStatus(existing.status);
6553
6572
  })();
@@ -19554,12 +19573,13 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
19554
19573
  push("runner_tui_frame_renders_route_statuses", false, String(err?.message || err));
19555
19574
  }
19556
19575
 
19557
- await runSelftestBotCommands(push, {
19558
- cliPath: fileURLToPath(import.meta.url),
19559
- parseSimpleEnvText,
19560
- resolveLocalAIExecutionModel,
19561
- suggestLocalAIModelDisplayName,
19562
- resolveGeminiReasoningConfig,
19576
+ await runSelftestBotCommands(push, {
19577
+ cliPath: fileURLToPath(import.meta.url),
19578
+ parseSimpleEnvText,
19579
+ resolveLocalAIExecutionModel,
19580
+ resolveGeminiHeadlessExecutionModel,
19581
+ suggestLocalAIModelDisplayName,
19582
+ resolveGeminiReasoningConfig,
19563
19583
  stripLocalOnlyToolArgs: (requestObj, toolName) =>
19564
19584
  stripLocalOnlyToolArgs(requestObj, toolName),
19565
19585
  applyProxyResponsePatches: (params, deps = buildProxyResponsePipelineDeps()) =>
@@ -19,6 +19,7 @@ const GEMINI_HOME_SYNC_FILES = [
19
19
  ];
20
20
  const GEMINI_STDIN_BRIDGE_PROMPT = "Use the full task provided on standard input as the authoritative prompt. Follow it exactly and output only the final answer.";
21
21
  const GEMINI_CLI_TIMEOUT_MS = 90 * 1000;
22
+ const GEMINI_RUNNER_STABLE_EXECUTION_MODEL = "gemini-3-flash-preview";
22
23
  const LOCAL_AI_MODEL_MAPPINGS = {
23
24
  gpt: [
24
25
  {
@@ -880,7 +881,12 @@ function runLocalAIPromptRawText({
880
881
  const normalizedClient = normalizeLocalAIClientName(client);
881
882
  const normalizedPermissionMode = normalizeLocalAIPermissionMode(permissionMode);
882
883
  const normalizedReasoningEffort = normalizeLocalAIReasoningEffort(reasoningEffort, "low");
883
- const resolvedExecutionModel = resolveLocalAIExecutionModel(normalizedClient, model);
884
+ const resolvedExecutionModel = normalizedClient === "gemini"
885
+ ? resolveGeminiHeadlessExecutionModel(model, {
886
+ permissionMode: normalizedPermissionMode,
887
+ reasoningEffort: normalizedReasoningEffort,
888
+ })
889
+ : resolveLocalAIExecutionModel(normalizedClient, model);
884
890
  const resolvedWorkspaceDir = ensureWorkspaceDir(workspaceDir);
885
891
  const nextEnv = {
886
892
  ...process.env,
@@ -1386,6 +1392,23 @@ export function resolveLocalAIExecutionModel(clientName, rawModelValue = "") {
1386
1392
  return match ? String(match.execution || "").trim() : modelValue;
1387
1393
  }
1388
1394
 
1395
+ export function resolveGeminiHeadlessExecutionModel(
1396
+ rawModelValue = "",
1397
+ { permissionMode = "read_only", reasoningEffort = "low" } = {},
1398
+ ) {
1399
+ const resolvedExecutionModel = resolveLocalAIExecutionModel("gemini", rawModelValue);
1400
+ const normalizedExecutionModel = normalizeModelAliasText(resolvedExecutionModel);
1401
+ void normalizeLocalAIPermissionMode(permissionMode);
1402
+ void normalizeLocalAIReasoningEffort(reasoningEffort, "low");
1403
+ if (normalizedExecutionModel !== "auto-gemini-3") {
1404
+ return resolvedExecutionModel;
1405
+ }
1406
+ // Headless runner turns should not depend on Gemini CLI's internal auto-router.
1407
+ // Under heavier prompts it can escalate to capacity-constrained preview models,
1408
+ // which makes one bot path look flaky even though the routing logic is correct.
1409
+ return GEMINI_RUNNER_STABLE_EXECUTION_MODEL;
1410
+ }
1411
+
1389
1412
  function buildCodexArgs({ workspaceDir, model, permissionMode, reasoningEffort, outputPath }) {
1390
1413
  const args = ["exec"];
1391
1414
  if (model) {
@@ -1513,7 +1536,10 @@ function buildGeminiThinkingConfig(model, reasoningEffort) {
1513
1536
  }
1514
1537
 
1515
1538
  export function resolveGeminiReasoningConfig(rawModelValue = "", reasoningEffort = "medium") {
1516
- const executionModel = resolveLocalAIExecutionModel("gemini", rawModelValue);
1539
+ const executionModel = resolveGeminiHeadlessExecutionModel(rawModelValue, {
1540
+ permissionMode: "read_only",
1541
+ reasoningEffort,
1542
+ });
1517
1543
  if (!executionModel) {
1518
1544
  return null;
1519
1545
  }
@@ -3256,7 +3282,12 @@ export function runLocalAIClient({
3256
3282
  const normalizedClient = normalizeLocalAIClientName(client);
3257
3283
  const normalizedPermissionMode = normalizeLocalAIPermissionMode(permissionMode);
3258
3284
  const normalizedReasoningEffort = normalizeLocalAIReasoningEffort(reasoningEffort);
3259
- const resolvedExecutionModel = resolveLocalAIExecutionModel(normalizedClient, model);
3285
+ const resolvedExecutionModel = normalizedClient === "gemini"
3286
+ ? resolveGeminiHeadlessExecutionModel(model, {
3287
+ permissionMode: normalizedPermissionMode,
3288
+ reasoningEffort: normalizedReasoningEffort,
3289
+ })
3290
+ : resolveLocalAIExecutionModel(normalizedClient, model);
3260
3291
  const resolvedWorkspaceDir = ensureWorkspaceDir(workspaceDir);
3261
3292
  const promptText = buildLocalBotPrompt(inputPayload);
3262
3293
  if (normalizedClient === "sample") {
@@ -26,11 +26,14 @@ export function classifyExecutionFailureFacts(detail) {
26
26
  const normalizedDetail = String(detail || "").trim();
27
27
  const networkReset = /ECONNRESET|socket hang up|read ECONNRESET/i.test(normalizedDetail);
28
28
  const networkTimeout = /ETIMEDOUT|http timeout|ECONNABORTED|aborted/i.test(normalizedDetail);
29
- const retryable = networkReset || networkTimeout;
29
+ const providerCapacityExhausted = /MODEL_CAPACITY_EXHAUSTED|RESOURCE_EXHAUSTED|No capacity available for model|rateLimitExceeded/i.test(normalizedDetail);
30
+ const retryable = networkReset || networkTimeout || providerCapacityExhausted;
30
31
  const base = {
31
32
  stage: "execution",
32
33
  operation: "runner_execution",
33
- errorType: retryable
34
+ errorType: providerCapacityExhausted
35
+ ? "provider_capacity_exhausted"
36
+ : retryable
34
37
  ? (networkTimeout ? "network_timeout" : "network_reset")
35
38
  : "execution_failed",
36
39
  retryable,
@@ -42,6 +45,15 @@ export function classifyExecutionFailureFacts(detail) {
42
45
  if (!normalizedDetail) {
43
46
  return base;
44
47
  }
48
+ if (providerCapacityExhausted) {
49
+ return {
50
+ ...base,
51
+ stage: "provider_call",
52
+ operation: "local_ai_model_request",
53
+ errorType: "provider_capacity_exhausted",
54
+ retryable: true,
55
+ };
56
+ }
45
57
  if (/permission_mode=read_only|read[_ -]?only/i.test(normalizedDetail)) {
46
58
  return {
47
59
  ...base,
@@ -88,6 +88,7 @@ export function buildRunnerProcessedLifecycleInput({
88
88
  const processed = safeObject(processedRaw);
89
89
  const result = safeObject(processed.result);
90
90
  const normalizedOutcome = normalizeRunnerProcessedLifecycleOutcome(processed);
91
+ const normalizedFailureFacts = safeObject(result.failure_facts);
91
92
  return {
92
93
  requestKey,
93
94
  selectedRecord,
@@ -95,6 +96,8 @@ export function buildRunnerProcessedLifecycleInput({
95
96
  outcome: normalizedOutcome,
96
97
  closedReason: normalizedOutcome === "skipped"
97
98
  ? String(processed.skippedRecord?.reason || result.detail || "skipped").trim() || "skipped"
99
+ : ["error", "execution_failed", "policy_violation"].includes(normalizedOutcome)
100
+ ? String(result.detail || "execution_error").trim() || "execution_error"
98
101
  : "",
99
102
  conversationIDRaw: String(result.conversation_id || "").trim(),
100
103
  conversationParticipants: ensureArray(result.conversation_participants),
@@ -126,6 +129,8 @@ export function buildRunnerProcessedLifecycleInput({
126
129
  assignmentValidationStatus: String(result.assignment_validation_status || "").trim(),
127
130
  assignmentValidationReason: String(result.assignment_validation_reason || "").trim(),
128
131
  assignmentValidationModes: ensureArray(result.assignment_validation_modes),
132
+ failureReplyClassification: String(result.failure_reply_classification || "").trim(),
133
+ failureFacts: normalizedFailureFacts,
129
134
  deliveryStatus: String(result.delivery_status || "").trim(),
130
135
  archiveStatus: String(result.archive_status || "").trim(),
131
136
  transportError: String(result.transport_error || "").trim(),
@@ -361,6 +361,7 @@ export async function runSelftestBotCommands(push, deps) {
361
361
  const cliPath = String(requireDependency(deps, "cliPath") || "").trim();
362
362
  const parseSimpleEnvText = requireDependency(deps, "parseSimpleEnvText");
363
363
  const resolveLocalAIExecutionModel = requireDependency(deps, "resolveLocalAIExecutionModel");
364
+ const resolveGeminiHeadlessExecutionModel = requireDependency(deps, "resolveGeminiHeadlessExecutionModel");
364
365
  const suggestLocalAIModelDisplayName = requireDependency(deps, "suggestLocalAIModelDisplayName");
365
366
  const resolveGeminiReasoningConfig = requireDependency(deps, "resolveGeminiReasoningConfig");
366
367
  const stripLocalOnlyToolArgs = requireDependency(deps, "stripLocalOnlyToolArgs");
@@ -392,6 +393,18 @@ export async function runSelftestBotCommands(push, deps) {
392
393
  ].join(" "),
393
394
  );
394
395
 
396
+ push(
397
+ "gemini_headless_runner_uses_explicit_stable_execution_model",
398
+ resolveGeminiHeadlessExecutionModel("gemini-3.1-pro", {
399
+ permissionMode: "read_only",
400
+ reasoningEffort: "low",
401
+ }) === "gemini-3-flash-preview",
402
+ `gemini_headless=${resolveGeminiHeadlessExecutionModel("gemini-3.1-pro", {
403
+ permissionMode: "read_only",
404
+ reasoningEffort: "low",
405
+ })}`,
406
+ );
407
+
395
408
  push(
396
409
  "blank_model_defaults_to_first_display_model_for_each_client",
397
410
  suggestLocalAIModelDisplayName("gpt", "") === "gpt-5.4"
@@ -409,7 +422,7 @@ export async function runSelftestBotCommands(push, deps) {
409
422
  const geminiHighReasoning = resolveGeminiReasoningConfig("gemini-3.1-pro", "high");
410
423
  push(
411
424
  "gemini_reasoning_effort_maps_to_runtime_settings_override",
412
- String(geminiLowReasoning?.model || "") === "auto-gemini-3"
425
+ String(geminiLowReasoning?.model || "") === "gemini-3-flash-preview"
413
426
  && String(safeObject(geminiLowReasoning?.thinkingConfig).thinkingLevel || "") === "LOW"
414
427
  && String(safeObject(geminiMediumReasoning?.thinkingConfig).thinkingLevel || "") === "THINKING_LEVEL_UNSPECIFIED"
415
428
  && String(safeObject(geminiHighReasoning?.thinkingConfig).thinkingLevel || "") === "HIGH",
@@ -3245,6 +3245,73 @@ export async function runSelftestRunnerScenarios(push, deps) {
3245
3245
  `status=${String(failedRequest?.status || "(none)")} reason=${String(failedRequest?.closed_reason || "(none)")} closed_at=${String(failedRequest?.closed_at || "(none)")}`,
3246
3246
  );
3247
3247
 
3248
+ saveBotRunnerState({
3249
+ routes: {
3250
+ [requestRouteKey]: {},
3251
+ },
3252
+ sharedInboxes: {},
3253
+ excludedComments: {},
3254
+ requests: {
3255
+ "request-key-2f": {
3256
+ request_key: "request-key-2f",
3257
+ project_id: selftestProjectID,
3258
+ provider: "telegram",
3259
+ chat_id: "-100123",
3260
+ source_message_id: 751,
3261
+ conversation_id: "conv-request-2f",
3262
+ execution_contract_type: "delegation",
3263
+ execution_contract_targets: ["ryoai3_bot"],
3264
+ next_expected_responders: ["ryoai3_bot"],
3265
+ authoritative_decision_bundle: {
3266
+ schema_version: "runner_conversation_decision.v1",
3267
+ decision_type: "reply_outcome",
3268
+ conversation_intent_mode: "delegated_single_lead",
3269
+ allowed_responders: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
3270
+ initial_responders: ["ryoai_bot"],
3271
+ selected_bot_usernames: ["ryoai2_bot"],
3272
+ allow_bot_to_bot: true,
3273
+ execution_contract_type: "delegation",
3274
+ execution_contract_targets: ["ryoai3_bot"],
3275
+ next_expected_responders: ["ryoai3_bot"],
3276
+ should_close_after_reply: false,
3277
+ },
3278
+ decision_bundle_validation_status: "valid",
3279
+ status: "running",
3280
+ claimed_by_route: requestRouteKey,
3281
+ },
3282
+ },
3283
+ consumedComments: {},
3284
+ });
3285
+ const retryableFailedDelegation = markRunnerRequestLifecycle({
3286
+ normalizedRoute: requestRoute,
3287
+ requestKey: "request-key-2f",
3288
+ selectedRecord: {
3289
+ id: "comment-request-finish-2f",
3290
+ parsedArchive: {
3291
+ kind: "bot_reply",
3292
+ chatID: "-100123",
3293
+ messageID: 752,
3294
+ conversationID: "conv-request-2f",
3295
+ },
3296
+ },
3297
+ routeKey: requestRouteKey,
3298
+ outcome: "error",
3299
+ closedReason: "Gemini CLI timed out after 90s while waiting for a model response (No capacity available for model gemini-3.1-pro-preview on the server)",
3300
+ currentBotSelector: "@RyoAI2_bot",
3301
+ failureReplyClassification: "retryable_failure",
3302
+ failureFacts: {
3303
+ retryable: true,
3304
+ error_type: "provider_capacity_exhausted",
3305
+ },
3306
+ });
3307
+ push(
3308
+ "runner_request_lifecycle_retryable_delegated_error_stays_running",
3309
+ String(retryableFailedDelegation?.status || "") === "running"
3310
+ && ensureArray(retryableFailedDelegation?.next_expected_responders).includes("ryoai3_bot")
3311
+ && String(retryableFailedDelegation?.closed_reason || "").trim() === "",
3312
+ `status=${String(retryableFailedDelegation?.status || "(none)")} next=${ensureArray(retryableFailedDelegation?.next_expected_responders).join(",")} closed_reason=${String(retryableFailedDelegation?.closed_reason || "(none)")}`,
3313
+ );
3314
+
3248
3315
  saveBotRunnerState({
3249
3316
  routes: {
3250
3317
  [requestRouteKey]: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.282",
3
+ "version": "0.2.283",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [