metheus-governance-mcp-cli 0.2.203 → 0.2.205

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
@@ -184,6 +184,7 @@ import {
184
184
  } from "./lib/runner-execution.mjs";
185
185
  import {
186
186
  processRunnerSelectedRecord,
187
+ resolveHumanIntentContext,
187
188
  resolveRunnerResponderAdjudication,
188
189
  resolveRunnerStartupLoopAdjudication,
189
190
  selectRunnerPendingWork,
@@ -2986,6 +2987,7 @@ async function claimRunnerRequestForHumanComment({
2986
2987
  selectedRecord,
2987
2988
  selectedBotUsernames = [],
2988
2989
  normalizedIntent = "",
2990
+ sharedHumanIntent = null,
2989
2991
  runtime = null,
2990
2992
  archiveThreadID = "",
2991
2993
  }) {
@@ -3039,6 +3041,7 @@ async function claimRunnerRequestForHumanComment({
3039
3041
  }
3040
3042
  const requests = normalizeBotRunnerRequests(stateForClaim.requests);
3041
3043
  const existing = safeObject(requests[requestKey]);
3044
+ const normalizedSharedHumanIntent = safeObject(sharedHumanIntent);
3042
3045
  const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
3043
3046
  let sharedConversationSource = currentMessageID > 0
3044
3047
  ? pickRunnerSharedConversationSourceRequest(
@@ -3097,20 +3100,22 @@ async function claimRunnerRequestForHumanComment({
3097
3100
  conversation_id: resolvedConversationID,
3098
3101
  selected_bot_usernames: uniqueOrderedStrings(selectedBotUsernames, normalizeTelegramMentionUsername),
3099
3102
  conversation_intent_mode: String(
3100
- existing.conversation_intent_mode || sharedConversationSource.conversation_intent_mode || referencedRequest.conversation_intent_mode || "",
3103
+ existing.conversation_intent_mode || sharedConversationSource.conversation_intent_mode || referencedRequest.conversation_intent_mode || normalizedSharedHumanIntent.intentMode || "",
3101
3104
  ).trim().toLowerCase(),
3102
3105
  conversation_lead_bot: normalizeTelegramMentionUsername(
3103
- existing.conversation_lead_bot || sharedConversationSource.conversation_lead_bot || referencedRequest.conversation_lead_bot,
3106
+ existing.conversation_lead_bot || sharedConversationSource.conversation_lead_bot || referencedRequest.conversation_lead_bot || normalizedSharedHumanIntent.leadBotSelector,
3104
3107
  ),
3105
3108
  conversation_summary_bot: normalizeTelegramMentionUsername(
3106
- existing.conversation_summary_bot || sharedConversationSource.conversation_summary_bot || referencedRequest.conversation_summary_bot,
3109
+ existing.conversation_summary_bot || sharedConversationSource.conversation_summary_bot || referencedRequest.conversation_summary_bot || normalizedSharedHumanIntent.summaryBotSelector,
3107
3110
  ),
3108
3111
  conversation_participants: uniqueOrderedStrings(
3109
3112
  ensureArray(existing.conversation_participants).length
3110
3113
  ? existing.conversation_participants
3111
3114
  : ensureArray(sharedConversationSource.conversation_participants).length
3112
3115
  ? sharedConversationSource.conversation_participants
3113
- : referencedRequest.conversation_participants,
3116
+ : ensureArray(referencedRequest.conversation_participants).length
3117
+ ? referencedRequest.conversation_participants
3118
+ : normalizedSharedHumanIntent.participantSelectors,
3114
3119
  normalizeTelegramMentionUsername,
3115
3120
  ),
3116
3121
  conversation_initial_responders: uniqueOrderedStrings(
@@ -3118,7 +3123,9 @@ async function claimRunnerRequestForHumanComment({
3118
3123
  ? existing.conversation_initial_responders
3119
3124
  : ensureArray(sharedConversationSource.conversation_initial_responders).length
3120
3125
  ? sharedConversationSource.conversation_initial_responders
3121
- : referencedRequest.conversation_initial_responders,
3126
+ : ensureArray(referencedRequest.conversation_initial_responders).length
3127
+ ? referencedRequest.conversation_initial_responders
3128
+ : normalizedSharedHumanIntent.initialResponderSelectors,
3122
3129
  normalizeTelegramMentionUsername,
3123
3130
  ),
3124
3131
  conversation_allowed_responders: uniqueOrderedStrings(
@@ -3126,14 +3133,17 @@ async function claimRunnerRequestForHumanComment({
3126
3133
  ? existing.conversation_allowed_responders
3127
3134
  : ensureArray(sharedConversationSource.conversation_allowed_responders).length
3128
3135
  ? sharedConversationSource.conversation_allowed_responders
3129
- : referencedRequest.conversation_allowed_responders,
3136
+ : ensureArray(referencedRequest.conversation_allowed_responders).length
3137
+ ? referencedRequest.conversation_allowed_responders
3138
+ : normalizedSharedHumanIntent.allowedResponderSelectors,
3130
3139
  normalizeTelegramMentionUsername,
3131
3140
  ),
3132
3141
  conversation_allow_bot_to_bot: existing.conversation_allow_bot_to_bot === true
3133
3142
  || sharedConversationSource.conversation_allow_bot_to_bot === true
3134
- || referencedRequest.conversation_allow_bot_to_bot === true,
3143
+ || referencedRequest.conversation_allow_bot_to_bot === true
3144
+ || normalizedSharedHumanIntent.allowBotToBot === true,
3135
3145
  conversation_reply_expectation: String(
3136
- existing.conversation_reply_expectation || sharedConversationSource.conversation_reply_expectation || referencedRequest.conversation_reply_expectation || "",
3146
+ existing.conversation_reply_expectation || sharedConversationSource.conversation_reply_expectation || referencedRequest.conversation_reply_expectation || normalizedSharedHumanIntent.replyExpectation || "",
3137
3147
  ).trim().toLowerCase(),
3138
3148
  execution_contract_type: String(
3139
3149
  existing.execution_contract_type || sharedConversationSource.execution_contract_type || referencedRequest.execution_contract_type || "",
@@ -3154,7 +3164,9 @@ async function claimRunnerRequestForHumanComment({
3154
3164
  ? existing.next_expected_responders
3155
3165
  : ensureArray(sharedConversationSource.next_expected_responders).length
3156
3166
  ? sharedConversationSource.next_expected_responders
3157
- : referencedRequest.next_expected_responders,
3167
+ : ensureArray(referencedRequest.next_expected_responders).length
3168
+ ? referencedRequest.next_expected_responders
3169
+ : normalizedSharedHumanIntent.initialResponderSelectors,
3158
3170
  normalizeTelegramMentionUsername,
3159
3171
  ),
3160
3172
  normalized_intent: resolvedNormalizedIntent,
@@ -7592,7 +7604,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7592
7604
  }
7593
7605
  return null;
7594
7606
  };
7595
- const prepareRunnerRequestClaim = async (selectedRecord, selectedResponderSelectors = []) => {
7607
+ const prepareRunnerRequestClaim = async (selectedRecord, selectedResponderSelectors = [], sharedHumanIntent = null) => {
7596
7608
  const parsed = safeObject(selectedRecord?.parsedArchive);
7597
7609
  const kind = String(parsed.kind || "").trim().toLowerCase();
7598
7610
  if (kind === "bot_reply") {
@@ -7622,6 +7634,8 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7622
7634
  routeKey,
7623
7635
  selectedRecord,
7624
7636
  selectedBotUsernames: selectedResponderSelectors,
7637
+ normalizedIntent: String(safeObject(sharedHumanIntent).intentType || "").trim().toLowerCase(),
7638
+ sharedHumanIntent,
7625
7639
  runtime,
7626
7640
  archiveThreadID: archiveThread.threadID,
7627
7641
  });
@@ -7679,17 +7693,26 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7679
7693
  skippedRecords.push(startupLoopSkipped.skippedRecord);
7680
7694
  continue;
7681
7695
  }
7696
+ const routingExecutionDeps = {
7697
+ ...buildRunnerExecutionDeps(),
7698
+ managedConversationBots,
7699
+ resolveConversationPeerBots: resolveRunnerConversationPeers,
7700
+ };
7701
+ const sharedHumanIntentContext = await resolveHumanIntentContext({
7702
+ selectedRecord,
7703
+ normalizedRoute,
7704
+ bot,
7705
+ executionPlan,
7706
+ deps: routingExecutionDeps,
7707
+ });
7682
7708
  const adjudication = await resolveRunnerResponderAdjudication({
7683
7709
  selectedRecord,
7684
7710
  pendingOrdered: pending.ordered,
7685
7711
  normalizedRoute,
7686
7712
  bot,
7687
7713
  executionPlan,
7688
- deps: {
7689
- ...buildRunnerExecutionDeps(),
7690
- managedConversationBots,
7691
- resolveConversationPeerBots: resolveRunnerConversationPeers,
7692
- },
7714
+ deps: routingExecutionDeps,
7715
+ precomputedHumanIntent: safeObject(sharedHumanIntentContext).humanIntent || null,
7693
7716
  });
7694
7717
  const currentBotSelected = ensureArray(adjudication.selected_bot_usernames)
7695
7718
  .map((value) => String(value || "").trim().replace(/^@+/, "").toLowerCase())
@@ -7710,7 +7733,11 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7710
7733
  });
7711
7734
  continue;
7712
7735
  }
7713
- const requestClaim = await prepareRunnerRequestClaim(selectedRecord, adjudication.selected_bot_usernames);
7736
+ const requestClaim = await prepareRunnerRequestClaim(
7737
+ selectedRecord,
7738
+ adjudication.selected_bot_usernames,
7739
+ safeObject(sharedHumanIntentContext).humanIntent || null,
7740
+ );
7714
7741
  if (!requestClaim.ok) {
7715
7742
  await syncRunnerRequestLedgerForProjectToServer({
7716
7743
  normalizedRoute,
@@ -7836,6 +7863,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7836
7863
  requestKey: String(requestClaim.requestKey || "").trim(),
7837
7864
  triggerDecision,
7838
7865
  responderAdjudication: adjudication,
7866
+ humanIntentContext: sharedHumanIntentContext,
7839
7867
  },
7840
7868
  };
7841
7869
  }
@@ -7918,17 +7946,26 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7918
7946
  skippedRecords.push(startupLoopSkipped.skippedRecord);
7919
7947
  continue;
7920
7948
  }
7949
+ const routingExecutionDeps = {
7950
+ ...buildRunnerExecutionDeps(),
7951
+ managedConversationBots,
7952
+ resolveConversationPeerBots: resolveRunnerConversationPeers,
7953
+ };
7954
+ const sharedHumanIntentContext = await resolveHumanIntentContext({
7955
+ selectedRecord,
7956
+ normalizedRoute,
7957
+ bot,
7958
+ executionPlan,
7959
+ deps: routingExecutionDeps,
7960
+ });
7921
7961
  const inlineAdjudication = await resolveRunnerResponderAdjudication({
7922
7962
  selectedRecord,
7923
7963
  pendingOrdered: pending.ordered,
7924
7964
  normalizedRoute,
7925
7965
  bot,
7926
7966
  executionPlan,
7927
- deps: {
7928
- ...buildRunnerExecutionDeps(),
7929
- managedConversationBots,
7930
- resolveConversationPeerBots: resolveRunnerConversationPeers,
7931
- },
7967
+ deps: routingExecutionDeps,
7968
+ precomputedHumanIntent: safeObject(sharedHumanIntentContext).humanIntent || null,
7932
7969
  });
7933
7970
  const inlineCurrentBotSelected = ensureArray(inlineAdjudication.selected_bot_usernames)
7934
7971
  .map((value) => String(value || "").trim().replace(/^@+/, "").toLowerCase())
@@ -7950,7 +7987,11 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
7950
7987
  continue;
7951
7988
  }
7952
7989
  const currentRouteState = safeObject(loadBotRunnerState().routes[routeKey]);
7953
- const requestClaim = await prepareRunnerRequestClaim(selectedRecord, inlineAdjudication.selected_bot_usernames);
7990
+ const requestClaim = await prepareRunnerRequestClaim(
7991
+ selectedRecord,
7992
+ inlineAdjudication.selected_bot_usernames,
7993
+ safeObject(sharedHumanIntentContext).humanIntent || null,
7994
+ );
7954
7995
  if (!requestClaim.ok) {
7955
7996
  await syncRunnerRequestLedgerForProjectToServer({
7956
7997
  normalizedRoute,
@@ -8096,6 +8137,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
8096
8137
  triggerDecision,
8097
8138
  responderAdjudication: inlineAdjudication,
8098
8139
  persistedHumanIntentRequest: claimedRequest,
8140
+ precomputedHumanIntentContext: sharedHumanIntentContext,
8099
8141
  deps: {
8100
8142
  saveRunnerRouteState,
8101
8143
  startRunnerTypingHeartbeat,
@@ -10465,6 +10507,7 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
10465
10507
  triggerDecision: deferredExecution.triggerDecision,
10466
10508
  responderAdjudication: deferredExecution.responderAdjudication,
10467
10509
  persistedHumanIntentRequest: loadRunnerRequestByKey(deferredExecution.requestKey),
10510
+ precomputedHumanIntentContext: safeObject(deferredExecution.humanIntentContext),
10468
10511
  deps: {
10469
10512
  saveRunnerRouteState,
10470
10513
  startRunnerTypingHeartbeat,
@@ -14951,6 +14994,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
14951
14994
  safeObject,
14952
14995
  normalizeRunnerTriggerPolicy,
14953
14996
  evaluateTelegramRunnerTrigger,
14997
+ resolveRunnerResponderAdjudication,
14954
14998
  selectPendingArchiveComments,
14955
14999
  selectRunnerPendingWork,
14956
15000
  processRunnerSelectedRecord,
@@ -2067,7 +2067,7 @@ function buildConversationIntentAnalysisPrompt({
2067
2067
  "You are a conversation intent contract parser for a public Telegram room with managed bots.",
2068
2068
  "Infer the human's intended bot participation contract from the human message only.",
2069
2069
  "Do not infer from prior bot replies. Do not invent bots outside managed_bots.",
2070
- "Be conservative. If the request is ambiguous, choose single_bot and keep only the directly addressed bot as the responder.",
2070
+ "Be conservative only when the human truly does not indicate collaboration. If the message clearly asks multiple managed bots to discuss, review, brainstorm, compare perspectives, or talk together, do not collapse it to single_bot.",
2071
2071
  "Also decide whether the human is asking for immediate execution/work now or only asking for information/explanation.",
2072
2072
  "Also classify the current message intent type.",
2073
2073
  "",
@@ -2092,12 +2092,13 @@ function buildConversationIntentAnalysisPrompt({
2092
2092
  "- intent_type=bot_role_query for asking about bot roles, bot ownership, or how managed bots are meant to collaborate.",
2093
2093
  "- intent_type=workspace_query for asking about the current workspace, project folder, or working directory.",
2094
2094
  "- intent_type=artifact_location_query for asking where a file, document, pdf, guide, or artifact is located.",
2095
- "- intent_type=explanation_query for explanation, clarification, or informational questions that do not require execution.",
2095
+ "- intent_type=explanation_query for explanation, clarification, informational questions, discussion requests, review requests, brainstorming requests, or requests for bots to talk together about a topic when no concrete work execution is explicitly requested.",
2096
2096
  "- intent_type=ctxpack_mutation for requests to create/update project guidance, instructions, rules, policies, or other ctxpack-backed source documents now.",
2097
2097
  "- intent_type=workitem_mutation for requests to create/update actionable work items, backlog tasks, or task breakdowns now.",
2098
- "- intent_type=general_execution for other concrete work requests that should execute now.",
2098
+ "- intent_type=general_execution only for concrete work requests that should execute now, such as creating/updating files, running tools/commands, modifying project artifacts, or producing validated deliverables.",
2099
+ "- If the human asks bots to discuss, debate, review, brainstorm, compare opinions, or hold a conversation about a topic, default to reply_expectation=informational unless they explicitly ask for concrete execution/output now.",
2099
2100
  "- reply_expectation=actionable when the human is asking the bot(s) to actually do work now, produce concrete results, create/update files, delegate concrete tasks, or otherwise execute immediately.",
2100
- "- reply_expectation=informational when the human is only asking for explanation, status, location, clarification, or other non-execution information.",
2101
+ "- reply_expectation=informational when the human is only asking for explanation, status, location, clarification, discussion, review, brainstorming, or other non-execution information.",
2101
2102
  "",
2102
2103
  `managed_bots=${JSON.stringify(bots)}`,
2103
2104
  `human_message=${JSON.stringify(String(messageText || "").trim())}`,
@@ -973,9 +973,8 @@ function buildHumanIntentFromPersistedRunnerRequest({
973
973
  const allowedResponderSelectors = normalizeManagedSelectors(persistedRequest.conversation_allowed_responders);
974
974
  const leadBotSelector = normalizeMentionSelector(persistedRequest.conversation_lead_bot);
975
975
  const summaryBotSelector = normalizeMentionSelector(persistedRequest.conversation_summary_bot);
976
- const hasContract = Boolean(
976
+ const hasConversationContract = Boolean(
977
977
  intentMode
978
- || intentType
979
978
  || replyExpectation
980
979
  || participantSelectors.length
981
980
  || initialResponderSelectors.length
@@ -983,7 +982,13 @@ function buildHumanIntentFromPersistedRunnerRequest({
983
982
  || leadBotSelector
984
983
  || summaryBotSelector,
985
984
  );
986
- if (!hasContract) {
985
+ const hasExecutionContract = Boolean(
986
+ String(persistedRequest.execution_contract_type || "").trim()
987
+ || persistedRequest.execution_contract_actionable === true
988
+ || ensureArray(persistedRequest.execution_contract_targets).length
989
+ || ensureArray(persistedRequest.next_expected_responders).length,
990
+ );
991
+ if (!hasConversationContract && !hasExecutionContract) {
987
992
  return null;
988
993
  }
989
994
  return {
@@ -999,7 +1004,7 @@ function buildHumanIntentFromPersistedRunnerRequest({
999
1004
  };
1000
1005
  }
1001
1006
 
1002
- async function resolveHumanIntentContext({
1007
+ export async function resolveHumanIntentContext({
1003
1008
  selectedRecord,
1004
1009
  normalizedRoute,
1005
1010
  bot,
@@ -3952,6 +3957,7 @@ export async function resolveRunnerResponderAdjudication({
3952
3957
  bot,
3953
3958
  executionPlan,
3954
3959
  deps,
3960
+ precomputedHumanIntent = null,
3955
3961
  }) {
3956
3962
  const cacheKey = buildRunnerResponderAdjudicationCacheKey({ normalizedRoute, selectedRecord });
3957
3963
  if (runnerResponderAdjudicationPromises.has(cacheKey)) {
@@ -3967,6 +3973,11 @@ export async function resolveRunnerResponderAdjudication({
3967
3973
  bot,
3968
3974
  deps,
3969
3975
  });
3976
+ const managedBotSelectors = new Set(
3977
+ managedBots
3978
+ .map((entry) => normalizeMentionSelector(entry.username))
3979
+ .filter(Boolean),
3980
+ );
3970
3981
  const triggerFacts = {
3971
3982
  message_kind: String(safeObject(selectedRecord?.parsedArchive).kind || "").trim(),
3972
3983
  chat_type: String(safeObject(selectedRecord?.parsedArchive).chatType || "").trim(),
@@ -3982,6 +3993,47 @@ export async function resolveRunnerResponderAdjudication({
3982
3993
  trigger_reason: String(entry.trigger_reason || "").trim(),
3983
3994
  })),
3984
3995
  };
3996
+ const humanIntent = safeObject(precomputedHumanIntent);
3997
+ const contractInitialResponders = uniqueOrdered(
3998
+ ensureArray(humanIntent.initialResponderSelectors)
3999
+ .map((value) => normalizeMentionSelector(value))
4000
+ .filter((value) => value && managedBotSelectors.has(value)),
4001
+ );
4002
+ const contractAllowedResponders = uniqueOrdered(
4003
+ ensureArray(humanIntent.allowedResponderSelectors)
4004
+ .map((value) => normalizeMentionSelector(value))
4005
+ .filter((value) => value && managedBotSelectors.has(value)),
4006
+ );
4007
+ const contractParticipants = uniqueOrdered(
4008
+ ensureArray(humanIntent.participantSelectors)
4009
+ .map((value) => normalizeMentionSelector(value))
4010
+ .filter((value) => value && managedBotSelectors.has(value)),
4011
+ );
4012
+ const contractLeadBot = normalizeMentionSelector(humanIntent.leadBotSelector);
4013
+ const selectedFromContract = uniqueOrdered(
4014
+ (
4015
+ contractInitialResponders.length
4016
+ ? contractInitialResponders
4017
+ : contractAllowedResponders.length
4018
+ ? contractAllowedResponders
4019
+ : contractParticipants.length
4020
+ ? contractParticipants
4021
+ : contractLeadBot && managedBotSelectors.has(contractLeadBot)
4022
+ ? [contractLeadBot]
4023
+ : []
4024
+ ).filter(Boolean),
4025
+ );
4026
+ if (selectedFromContract.length > 0) {
4027
+ return {
4028
+ decision: selectedFromContract.length > 1 ? "multiple_responders" : "single_responder",
4029
+ selected_bot_usernames: selectedFromContract,
4030
+ referenced_bot_usernames: triggerFacts.mentioned_bot_usernames,
4031
+ confidence: "high",
4032
+ reason_code: "precomputed_human_intent_contract",
4033
+ clarification: "",
4034
+ managed_bots: managedBots,
4035
+ };
4036
+ }
3985
4037
  if (!adjudicator) {
3986
4038
  const fallbackSelected = managedBots
3987
4039
  .filter((entry) => entry.trigger_eligible === true)
@@ -4104,6 +4156,7 @@ export async function processRunnerSelectedRecord({
4104
4156
  triggerDecision: precomputedTriggerDecision = null,
4105
4157
  responderAdjudication: precomputedResponderAdjudication = null,
4106
4158
  persistedHumanIntentRequest = null,
4159
+ precomputedHumanIntentContext = null,
4107
4160
  deps,
4108
4161
  }) {
4109
4162
  const saveRunnerRouteState = requireDependency(deps, "saveRunnerRouteState");
@@ -4123,6 +4176,8 @@ export async function processRunnerSelectedRecord({
4123
4176
  ...safeObject(buildRunnerExecutionDeps()),
4124
4177
  ...safeObject(deps),
4125
4178
  };
4179
+ const normalizedPrecomputedHumanIntentContext = safeObject(precomputedHumanIntentContext);
4180
+ const normalizedPrecomputedHumanIntent = safeObject(normalizedPrecomputedHumanIntentContext.humanIntent);
4126
4181
  const validateWorkspaceArtifacts = typeof executionDeps.validateWorkspaceArtifacts === "function"
4127
4182
  ? executionDeps.validateWorkspaceArtifacts
4128
4183
  : null;
@@ -4167,6 +4222,7 @@ export async function processRunnerSelectedRecord({
4167
4222
  bot,
4168
4223
  executionPlan,
4169
4224
  deps: executionDeps,
4225
+ precomputedHumanIntent: normalizedPrecomputedHumanIntent,
4170
4226
  });
4171
4227
  const selectedResponderSelectors = ensureArray(responderAdjudication.selected_bot_usernames)
4172
4228
  .map((value) => normalizeMentionSelector(value))
@@ -4196,14 +4252,16 @@ export async function processRunnerSelectedRecord({
4196
4252
  };
4197
4253
  }
4198
4254
 
4199
- const humanIntentContext = await resolveHumanIntentContext({
4200
- selectedRecord,
4201
- normalizedRoute,
4202
- bot,
4203
- executionPlan,
4204
- deps: executionDeps,
4205
- persistedRequest: persistedHumanIntentRequest,
4206
- });
4255
+ const humanIntentContext = Object.keys(normalizedPrecomputedHumanIntentContext).length > 0
4256
+ ? normalizedPrecomputedHumanIntentContext
4257
+ : await resolveHumanIntentContext({
4258
+ selectedRecord,
4259
+ normalizedRoute,
4260
+ bot,
4261
+ executionPlan,
4262
+ deps: executionDeps,
4263
+ persistedRequest: persistedHumanIntentRequest,
4264
+ });
4207
4265
  const precomputedIntentType = normalizeHumanIntentType(
4208
4266
  safeObject(safeObject(humanIntentContext).humanIntent).intentType,
4209
4267
  );
@@ -118,6 +118,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
118
118
  const safeObject = requireDependency(deps, "safeObject");
119
119
  const normalizeRunnerTriggerPolicy = requireDependency(deps, "normalizeRunnerTriggerPolicy");
120
120
  const evaluateTelegramRunnerTrigger = requireDependency(deps, "evaluateTelegramRunnerTrigger");
121
+ const resolveRunnerResponderAdjudication = requireDependency(deps, "resolveRunnerResponderAdjudication");
121
122
  const selectPendingArchiveComments = requireDependency(deps, "selectPendingArchiveComments");
122
123
  const selectRunnerPendingWork = requireDependency(deps, "selectRunnerPendingWork");
123
124
  const processRunnerSelectedRecord = requireDependency(deps, "processRunnerSelectedRecord");
@@ -2935,6 +2936,108 @@ export async function runSelftestRunnerScenarios(push, deps) {
2935
2936
  push("public_multi_bot_human_intent_is_computed_once_per_message", false, String(err?.message || err));
2936
2937
  }
2937
2938
 
2939
+ try {
2940
+ let adjudicatorCalls = 0;
2941
+ const adjudication = await resolveRunnerResponderAdjudication({
2942
+ selectedRecord: {
2943
+ id: "comment-precomputed-human-intent-adjudication",
2944
+ parsedArchive: {
2945
+ kind: "telegram_message",
2946
+ chatID: "-100123",
2947
+ chatType: "supergroup",
2948
+ senderIsBot: false,
2949
+ body: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot discuss this project together.",
2950
+ mentionUsernames: ["RyoAI_bot", "RyoAI2_bot", "RyoAI3_bot"],
2951
+ messageID: 1193,
2952
+ },
2953
+ },
2954
+ pendingOrdered: [],
2955
+ normalizedRoute: normalizeRunnerRoute({
2956
+ name: "telegram-monitor-precomputed-human-intent-adjudication",
2957
+ project_id: selftestProjectID,
2958
+ provider: "telegram",
2959
+ role: "monitor",
2960
+ role_profile: "monitor",
2961
+ destination_id: "dest-1",
2962
+ destination_label: "Main Room",
2963
+ server_bot_name: "RyoAI_bot",
2964
+ server_bot_id: "bot-lead-1",
2965
+ trigger_policy: {
2966
+ mentions_only: true,
2967
+ direct_messages: true,
2968
+ reply_to_bot_messages: true,
2969
+ },
2970
+ archive_policy: {
2971
+ mirror_replies: true,
2972
+ dedupe_inbound: true,
2973
+ dedupe_outbound: true,
2974
+ skip_bot_messages: true,
2975
+ },
2976
+ dry_run_delivery: true,
2977
+ }),
2978
+ bot: {
2979
+ id: "bot-lead-1",
2980
+ name: "RyoAI_bot",
2981
+ username: "RyoAI_bot",
2982
+ role: "monitor",
2983
+ provider: "telegram",
2984
+ },
2985
+ executionPlan: {
2986
+ mode: "role_profile",
2987
+ roleProfileName: "monitor",
2988
+ roleProfile: {
2989
+ client: "sample",
2990
+ model: "",
2991
+ permissionMode: "read_only",
2992
+ reasoningEffort: "low",
2993
+ },
2994
+ workspaceDir: path.join(os.tmpdir(), "metheus-runner-selftest-precomputed-human-intent-adjudication"),
2995
+ workspaceSource: "selftest",
2996
+ usedCommandFallback: false,
2997
+ },
2998
+ precomputedHumanIntent: {
2999
+ intentMode: "delegated_single_lead",
3000
+ intentType: "explanation_query",
3001
+ participantSelectors: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
3002
+ initialResponderSelectors: ["ryoai_bot"],
3003
+ allowedResponderSelectors: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
3004
+ leadBotSelector: "ryoai_bot",
3005
+ summaryBotSelector: "ryoai_bot",
3006
+ allowBotToBot: true,
3007
+ replyExpectation: "informational",
3008
+ },
3009
+ deps: {
3010
+ adjudicateRunnerRespondersWithAI: async () => {
3011
+ adjudicatorCalls += 1;
3012
+ return {
3013
+ decision: "multiple_responders",
3014
+ selected_bot_usernames: ["ryoai2_bot", "ryoai3_bot"],
3015
+ };
3016
+ },
3017
+ managedConversationBots: [
3018
+ { route: { serverBotName: "RyoAI_bot" }, bot: { username: "RyoAI_bot", name: "RyoAI_bot" } },
3019
+ { route: { serverBotName: "RyoAI2_bot" }, bot: { username: "RyoAI2_bot", name: "RyoAI2_bot" } },
3020
+ { route: { serverBotName: "RyoAI3_bot" }, bot: { username: "RyoAI3_bot", name: "RyoAI3_bot" } },
3021
+ ],
3022
+ resolveConversationPeerBots: () => [
3023
+ { id: "bot-lead-1", name: "RyoAI_bot" },
3024
+ { id: "bot-peer-1", name: "RyoAI2_bot" },
3025
+ { id: "bot-peer-2", name: "RyoAI3_bot" },
3026
+ ],
3027
+ },
3028
+ });
3029
+ push(
3030
+ "precomputed_human_intent_contract_drives_responder_selection",
3031
+ adjudicatorCalls === 0
3032
+ && ensureArray(adjudication.selected_bot_usernames).length === 1
3033
+ && String(ensureArray(adjudication.selected_bot_usernames)[0] || "") === "ryoai_bot"
3034
+ && String(adjudication.reason_code || "") === "precomputed_human_intent_contract",
3035
+ `adjudicator_calls=${adjudicatorCalls} selected=${JSON.stringify(ensureArray(adjudication.selected_bot_usernames))} reason=${String(adjudication.reason_code || "(none)")}`,
3036
+ );
3037
+ } catch (err) {
3038
+ push("precomputed_human_intent_contract_drives_responder_selection", false, String(err?.message || err));
3039
+ }
3040
+
2938
3041
  try {
2939
3042
  let humanIntentCalls = 0;
2940
3043
  const processed = await processRunnerSelectedRecord({
@@ -3074,6 +3177,142 @@ export async function runSelftestRunnerScenarios(push, deps) {
3074
3177
  push("public_multi_bot_persisted_human_intent_skips_reanalysis", false, String(err?.message || err));
3075
3178
  }
3076
3179
 
3180
+ try {
3181
+ let humanIntentCalls = 0;
3182
+ const processed = await processRunnerSelectedRecord({
3183
+ routeKey: "public-persisted-intent-only-key",
3184
+ normalizedRoute: normalizeRunnerRoute({
3185
+ name: "telegram-monitor-public-persisted-intent-only",
3186
+ project_id: selftestProjectID,
3187
+ provider: "telegram",
3188
+ role: "monitor",
3189
+ role_profile: "monitor",
3190
+ destination_id: "dest-1",
3191
+ destination_label: "Main Room",
3192
+ server_bot_name: "RyoAI_bot",
3193
+ server_bot_id: "bot-lead-1",
3194
+ trigger_policy: {
3195
+ mentions_only: true,
3196
+ direct_messages: true,
3197
+ reply_to_bot_messages: true,
3198
+ },
3199
+ archive_policy: {
3200
+ mirror_replies: true,
3201
+ dedupe_inbound: true,
3202
+ dedupe_outbound: true,
3203
+ skip_bot_messages: true,
3204
+ },
3205
+ dry_run_delivery: true,
3206
+ }),
3207
+ selectedRecord: {
3208
+ id: "comment-public-persisted-intent-only",
3209
+ createdAt: "2026-03-16T00:07:00.000Z",
3210
+ parsedArchive: {
3211
+ kind: "telegram_message",
3212
+ chatID: "-100123",
3213
+ chatType: "supergroup",
3214
+ senderIsBot: false,
3215
+ body: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot continue the discussion.",
3216
+ mentionUsernames: ["RyoAI_bot", "RyoAI2_bot", "RyoAI3_bot"],
3217
+ messageID: 1192,
3218
+ },
3219
+ },
3220
+ pendingOrdered: [],
3221
+ bot: {
3222
+ id: "bot-lead-1",
3223
+ name: "RyoAI_bot",
3224
+ username: "RyoAI_bot",
3225
+ role: "monitor",
3226
+ provider: "telegram",
3227
+ },
3228
+ destination: {
3229
+ id: "dest-1",
3230
+ label: "Main Room",
3231
+ provider: "telegram",
3232
+ chatID: "-100123",
3233
+ },
3234
+ archiveThread: {
3235
+ threadID: "thread-1",
3236
+ workItemID: "work-item-1",
3237
+ },
3238
+ executionPlan: {
3239
+ mode: "role_profile",
3240
+ roleProfileName: "monitor",
3241
+ roleProfile: {
3242
+ client: "sample",
3243
+ model: "",
3244
+ permissionMode: "read_only",
3245
+ reasoningEffort: "low",
3246
+ },
3247
+ workspaceDir: path.join(os.tmpdir(), "metheus-runner-selftest-persisted-human-intent-only"),
3248
+ workspaceSource: "selftest",
3249
+ usedCommandFallback: false,
3250
+ },
3251
+ runtime: {
3252
+ baseURL: "https://example.test",
3253
+ token: "selftest-token",
3254
+ timeoutSeconds: 30,
3255
+ actor: { user_id: "user-1" },
3256
+ },
3257
+ responderAdjudication: {
3258
+ selected_bot_usernames: ["ryoai_bot"],
3259
+ },
3260
+ persistedHumanIntentRequest: {
3261
+ request_key: "persisted-request-1192",
3262
+ normalized_intent: "general_execution",
3263
+ },
3264
+ deps: {
3265
+ saveRunnerRouteState: () => {},
3266
+ startRunnerTypingHeartbeat: () => ({ async stop() {} }),
3267
+ runRunnerAIExecution: async () => ({
3268
+ skip: false,
3269
+ reply: "bot replied",
3270
+ replyToMessageID: 0,
3271
+ contract: {
3272
+ type: "direct_result",
3273
+ actionable: false,
3274
+ },
3275
+ }),
3276
+ performLocalBotDelivery: async () => ({
3277
+ delivery: { dryRun: true, body: {} },
3278
+ archive: {},
3279
+ }),
3280
+ serializeRunnerTriggerPolicy: (value) => value,
3281
+ serializeRunnerArchivePolicy: (value) => value,
3282
+ buildRunnerExecutionDeps: () => ({
3283
+ analyzeHumanConversationIntentWithAI: async () => {
3284
+ humanIntentCalls += 1;
3285
+ return {
3286
+ mode: "multi_bot_collab",
3287
+ lead_bot: "ryoai_bot",
3288
+ summary_bot: "ryoai_bot",
3289
+ participants: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
3290
+ initial_responders: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
3291
+ allowed_responders: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
3292
+ allow_bot_to_bot: true,
3293
+ reply_expectation: "informational",
3294
+ intent_type: "explanation_query",
3295
+ };
3296
+ },
3297
+ }),
3298
+ buildRunnerDeliveryDeps: () => ({}),
3299
+ buildRunnerRuntimeDeps: () => ({}),
3300
+ resolveConversationPeerBots: () => [
3301
+ { id: "bot-lead-1", name: "RyoAI_bot" },
3302
+ { id: "bot-peer-1", name: "RyoAI2_bot" },
3303
+ { id: "bot-peer-2", name: "RyoAI3_bot" },
3304
+ ],
3305
+ },
3306
+ });
3307
+ push(
3308
+ "public_multi_bot_persisted_intent_only_reanalyzes_human_message",
3309
+ processed.kind === "replied" && humanIntentCalls === 1,
3310
+ `kind=${String(processed.kind || "(none)")} human_intent_calls=${humanIntentCalls}`,
3311
+ );
3312
+ } catch (err) {
3313
+ push("public_multi_bot_persisted_intent_only_reanalyzes_human_message", false, String(err?.message || err));
3314
+ }
3315
+
3077
3316
  try {
3078
3317
  const deliveredTexts = [];
3079
3318
  const deliveredConversation = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.203",
3
+ "version": "0.2.205",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [