metheus-governance-mcp-cli 0.2.209 → 0.2.211

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
@@ -3591,13 +3591,29 @@ function runnerRequestRequiresActionableContract(requestRaw) {
3591
3591
  const request = safeObject(requestRaw);
3592
3592
  const replyExpectation = String(request.conversation_reply_expectation || "").trim().toLowerCase();
3593
3593
  const executionContractType = String(request.execution_contract_type || "").trim().toLowerCase();
3594
+ const intentMode = String(request.conversation_intent_mode || "").trim().toLowerCase();
3595
+ const intentType = String(
3596
+ request.conversation_intent_type
3597
+ || request.intent_type
3598
+ || request.normalized_intent
3599
+ || "",
3600
+ ).trim().toLowerCase();
3601
+ const hasExecutionIntent = [
3602
+ "general_execution",
3603
+ "ctxpack_mutation",
3604
+ "workitem_mutation",
3605
+ ].includes(intentType);
3594
3606
  if (request.execution_contract_actionable === true) {
3595
3607
  return true;
3596
3608
  }
3597
- if (replyExpectation === "actionable") {
3609
+ if (["delegation", "direct_result", "summary_request", "final_summary"].includes(executionContractType)) {
3598
3610
  return true;
3599
3611
  }
3600
- if (["delegation", "direct_result"].includes(executionContractType)) {
3612
+ if (
3613
+ intentMode === "single_bot"
3614
+ && replyExpectation === "actionable"
3615
+ && hasExecutionIntent
3616
+ ) {
3601
3617
  return true;
3602
3618
  }
3603
3619
  return false;
@@ -1896,6 +1896,17 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
1896
1896
  responseContract.require_actionable_contract === true && Array.isArray(responseContract.allowed_contract_types) && responseContract.allowed_contract_types.length > 0
1897
1897
  ? `Allowed execution contract types for this reply: ${responseContract.allowed_contract_types.join(", ")}.`
1898
1898
  : "",
1899
+ "If you mention any other managed bot @username anywhere in the reply, you must include a contract for that mention.",
1900
+ "Do not mention another managed bot unless the contract explicitly names that bot in assignments or next_responders.",
1901
+ "Without a matching contract, mentioned bots will not act.",
1902
+ "When delegating to another managed bot, use contract.type=\"delegation\" with actionable=true, assignments, and next_responders.",
1903
+ "Delegation contract example: {\"type\":\"delegation\",\"actionable\":true,\"assignments\":[{\"target_bot\":\"ryoai2_bot\",\"task\":\"briefly greet in one line\"}],\"next_responders\":[\"ryoai2_bot\"]}.",
1904
+ ensureArray(responseContract.required_delegation_targets).length > 0
1905
+ ? `This reply must delegate to these exact managed bots now: ${ensureArray(responseContract.required_delegation_targets).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")}.`
1906
+ : "",
1907
+ String(responseContract.contract_hint || "").trim()
1908
+ ? `Contract requirement hint: ${String(responseContract.contract_hint || "").trim()}`
1909
+ : "",
1899
1910
  "",
1900
1911
  );
1901
1912
  }
@@ -2042,6 +2053,7 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
2042
2053
  lines.push(
2043
2054
  "As the lead bot, your first public reply must be immediately actionable.",
2044
2055
  "If you are delegating, include contract.type=\"delegation\" with concrete assignments for the exact bots you are tasking now.",
2056
+ "If you mention any allowed responder in the reply text, the contract must name that responder in assignments or next_responders.",
2045
2057
  "If you are completing the work yourself now, include contract.type=\"direct_result\".",
2046
2058
  "Do not say you will come back later, wait, or plan first without producing an actionable contract in this same reply.",
2047
2059
  );
@@ -1294,7 +1294,6 @@ export async function resolveHumanIntentContext({
1294
1294
  executionPlan,
1295
1295
  contractGuardrail: {
1296
1296
  require_complete_contract: true,
1297
- require_explicit_actionable_intent: true,
1298
1297
  reason: "Return a complete human intent contract for this managed-bot conversation.",
1299
1298
  },
1300
1299
  });
@@ -2584,6 +2583,11 @@ async function maybeExecuteDynamicRolePlan({
2584
2583
  safeObject(directHumanResponseContract).intentType
2585
2584
  || safeObject(safeObject(humanIntentContext).humanIntent).intentType,
2586
2585
  );
2586
+ const humanIntentMode = normalizeIntentMode(
2587
+ safeObject(directHumanResponseContract).intentMode
2588
+ || safeObject(safeObject(humanIntentContext).humanIntent).intentMode,
2589
+ );
2590
+ const actionableConversationRequiresContract = safeObject(directHumanResponseContract).requiresActionableContract === true;
2587
2591
  const plannerContextComments = ensureArray(contextWindow).length
2588
2592
  ? ensureArray(contextWindow)
2589
2593
  : buildRunnerContextWindow(
@@ -2595,6 +2599,13 @@ async function maybeExecuteDynamicRolePlan({
2595
2599
  if (!assignmentTasks.length && isInformationalHumanIntentType(humanIntentType)) {
2596
2600
  return null;
2597
2601
  }
2602
+ if (
2603
+ !assignmentTasks.length
2604
+ && actionableConversationRequiresContract
2605
+ && ["delegated_single_lead", "multi_bot_collab", "multi_bot_direct"].includes(humanIntentMode)
2606
+ ) {
2607
+ return null;
2608
+ }
2598
2609
  const executionIntentRequested = [
2599
2610
  "general_execution",
2600
2611
  "ctxpack_mutation",
@@ -4998,17 +5009,22 @@ export async function processRunnerSelectedRecord({
4998
5009
  const directHumanPeerMap = humanIntentContext?.peerMap instanceof Map
4999
5010
  ? humanIntentContext.peerMap
5000
5011
  : buildConversationPeerMap(bot, normalizedRoute, executionDeps);
5001
- const delegatedBotMentions = (
5002
- effectiveTriggerDecision.requiresDirectReply === true
5003
- && !conversationContext
5004
- && !safeObject(selectedRecord?.parsedArchive).senderIsBot
5005
- )
5006
- ? extractManagedPeerMentionSelectors(
5007
- String(aiResult.reply || "").trim(),
5008
- directHumanPeerMap,
5009
- currentBotSelector,
5012
+ const delegatedBotMentions = extractManagedPeerMentionSelectors(
5013
+ String(aiResult.reply || "").trim(),
5014
+ directHumanPeerMap,
5015
+ currentBotSelector,
5016
+ );
5017
+ const requiredDelegationTargets = uniqueOrdered(
5018
+ (
5019
+ delegatedBotMentions.length > 0
5020
+ ? delegatedBotMentions
5021
+ : requiresPeerDelegationContract
5022
+ ? peerAllowedResponders
5023
+ : []
5010
5024
  )
5011
- : [];
5025
+ .map((item) => normalizeMentionSelector(item))
5026
+ .filter(Boolean),
5027
+ );
5012
5028
  if (delegatedBotMentions.length > 0) {
5013
5029
  allowedActionableTypes = new Set(["delegation"]);
5014
5030
  }
@@ -5021,7 +5037,7 @@ export async function processRunnerSelectedRecord({
5021
5037
  && executionContract.actionable === true
5022
5038
  && (!allowedActionableTypes.size || allowedActionableTypes.has(String(executionContract.type || "").trim().toLowerCase())),
5023
5039
  );
5024
- if (!requiresActionableContract && delegatedBotMentions.length > 0) {
5040
+ if (!requiresActionableContract && requiredDelegationTargets.length > 0) {
5025
5041
  requiresActionableContract = true;
5026
5042
  }
5027
5043
  if (
@@ -5056,25 +5072,27 @@ export async function processRunnerSelectedRecord({
5056
5072
  allow_skip: false,
5057
5073
  must_reply: true,
5058
5074
  require_actionable_contract: true,
5059
- human_intent_mode: effectiveResponseContractPayload.human_intent_mode || ((delegatedBotMentions.length > 0 || requiresPeerDelegationContract) ? "delegated_single_lead" : ""),
5060
- human_lead_bot: delegatedBotMentions.length > 0
5061
- ? currentBotSelector
5062
- : String(effectiveResponseContractPayload.human_lead_bot || "").trim(),
5063
- human_summary_bot: delegatedBotMentions.length > 0
5064
- ? String(delegatedBotMentions[0] || "").trim()
5065
- : String(effectiveResponseContractPayload.human_summary_bot || "").trim(),
5075
+ human_intent_mode: String(effectiveResponseContractPayload.human_intent_mode || "").trim(),
5076
+ human_lead_bot: String(effectiveResponseContractPayload.human_lead_bot || "").trim(),
5077
+ human_summary_bot: String(effectiveResponseContractPayload.human_summary_bot || "").trim(),
5066
5078
  human_allowed_responders: ensureArray(effectiveResponseContractPayload.human_allowed_responders).length
5067
5079
  ? ensureArray(effectiveResponseContractPayload.human_allowed_responders)
5068
5080
  : uniqueOrdered([
5069
5081
  currentBotSelector,
5070
- ...(delegatedBotMentions.length > 0 ? delegatedBotMentions : peerAllowedResponders),
5082
+ ...requiredDelegationTargets,
5071
5083
  ].filter(Boolean)),
5072
- allow_bot_to_bot: delegatedBotMentions.length > 0 || requiresPeerDelegationContract || effectiveResponseContractPayload.human_intent_mode === "delegated_single_lead",
5073
- allowed_contract_types: delegatedBotMentions.length > 0 || requiresPeerDelegationContract
5074
- ? ["delegation", "summary_request", "final_summary"]
5084
+ allow_bot_to_bot: requiredDelegationTargets.length > 0
5085
+ || requiresPeerDelegationContract
5086
+ || effectiveResponseContractPayload.human_intent_mode === "delegated_single_lead",
5087
+ allowed_contract_types: requiredDelegationTargets.length > 0 || requiresPeerDelegationContract
5088
+ ? ["delegation"]
5075
5089
  : ensureArray(effectiveResponseContractPayload.allowed_contract_types).length
5076
5090
  ? ensureArray(effectiveResponseContractPayload.allowed_contract_types)
5077
5091
  : ["direct_result", "summary_request", "final_summary"],
5092
+ required_delegation_targets: requiredDelegationTargets,
5093
+ contract_hint: requiredDelegationTargets.length > 0
5094
+ ? `Your previous reply mentioned ${requiredDelegationTargets.map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")} without a delegation contract. Return contract.type=\"delegation\" with actionable=true, assignments for those exact bots, and matching next_responders.`
5095
+ : "",
5078
5096
  },
5079
5097
  };
5080
5098
  effectiveResponseContractPayload = safeObject(forcedContractPayload.response_contract);
@@ -5138,7 +5156,9 @@ export async function processRunnerSelectedRecord({
5138
5156
  if (requiresActionableContract) {
5139
5157
  const validContract = hasValidActionableContract;
5140
5158
  if (!validContract) {
5141
- const reason = "reply did not produce an actionable execution contract";
5159
+ const reason = requiredDelegationTargets.length > 0
5160
+ ? `reply mentioned managed peer bot(s) ${requiredDelegationTargets.map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")} without a required delegation contract`
5161
+ : "reply did not produce an actionable execution contract";
5142
5162
  const directHumanExecutionFailure = effectiveTriggerDecision.requiresDirectReply === true
5143
5163
  && !conversationContext
5144
5164
  && !safeObject(selectedRecord?.parsedArchive).senderIsBot;
@@ -5184,6 +5204,14 @@ export async function processRunnerSelectedRecord({
5184
5204
  }
5185
5205
  }
5186
5206
  const normalizedExecutionContractType = String(executionContract?.type || "").trim().toLowerCase();
5207
+ const effectiveResolvedIntentType = normalizeHumanIntentType(
5208
+ directHumanResponseContract.intentType || resolvedIntentType,
5209
+ );
5210
+ const effectiveResolvedIntentMode = normalizeIntentMode(
5211
+ directHumanResponseContract.intentMode
5212
+ || conversationContext?.intentMode
5213
+ || safeObject(safeObject(humanIntentContext).humanIntent).intentMode,
5214
+ );
5187
5215
  const shouldEnforceArtifactValidation = Boolean(
5188
5216
  (artifactValidationOverride && Object.keys(artifactValidationOverride).length > 0)
5189
5217
  || ensureArray(aiResult?.artifacts).length > 0
@@ -5193,7 +5221,8 @@ export async function processRunnerSelectedRecord({
5193
5221
  "general_execution",
5194
5222
  "ctxpack_mutation",
5195
5223
  "workitem_mutation",
5196
- ].includes(normalizeHumanIntentType(resolvedIntentType))
5224
+ ].includes(effectiveResolvedIntentType)
5225
+ && !["delegated_single_lead", "multi_bot_collab", "multi_bot_direct"].includes(effectiveResolvedIntentMode)
5197
5226
  );
5198
5227
  const artifactValidation = shouldEnforceArtifactValidation
5199
5228
  ? artifactValidationOverride && Object.keys(artifactValidationOverride).length > 0
@@ -96,12 +96,29 @@ function buildRunnerTaskName(humanIntentType, humanIntentContract = null) {
96
96
  const contract = safeObject(humanIntentContract);
97
97
  const replyExpectation = String(contract.replyExpectation || contract.reply_expectation || "").trim().toLowerCase();
98
98
  const executionContractType = String(contract.executionContractType || contract.execution_contract_type || "").trim().toLowerCase();
99
+ const intentMode = String(contract.intentMode || contract.intent_mode || "").trim().toLowerCase();
100
+ const executionIntentRequested = [
101
+ "general_execution",
102
+ "ctxpack_mutation",
103
+ "workitem_mutation",
104
+ ].includes(normalizedIntentType);
105
+ const collaborativeIntentMode = [
106
+ "delegated_single_lead",
107
+ "multi_bot_collab",
108
+ "multi_bot_direct",
109
+ ].includes(intentMode);
99
110
  if (normalizedIntentType === "status_query") return "answer_project_status_query";
100
111
  if (normalizedIntentType === "workspace_query") return "answer_project_workspace_query";
101
112
  if (normalizedIntentType === "bot_role_query") return "answer_project_bot_role_query";
102
113
  if (normalizedIntentType === "artifact_location_query") return "answer_project_artifact_location_query";
103
114
  if (normalizedIntentType === "small_talk") return "reply_to_project_chat_greeting";
104
- if (executionContractType || replyExpectation === "actionable") {
115
+ if (executionContractType) {
116
+ return "reply_to_project_actionable_message";
117
+ }
118
+ if (collaborativeIntentMode && replyExpectation === "actionable") {
119
+ return "reply_to_project_chat_message";
120
+ }
121
+ if (executionIntentRequested) {
105
122
  return "reply_to_project_actionable_message";
106
123
  }
107
124
  return "reply_to_project_chat_message";
@@ -4386,10 +4386,11 @@ export async function runSelftestRunnerScenarios(push, deps) {
4386
4386
  push("delegated_single_lead_human_opening_creates_expandable_contract", false, String(err?.message || err));
4387
4387
  }
4388
4388
 
4389
- try {
4390
- let aiCalls = 0;
4391
- const deliveredConversation = [];
4392
- const processed = await processRunnerSelectedRecord({
4389
+ try {
4390
+ let aiCalls = 0;
4391
+ const deliveredConversation = [];
4392
+ let forcedContractPayload = null;
4393
+ const processed = await processRunnerSelectedRecord({
4393
4394
  routeKey: "direct-human-delegation-contract-upgrade-key",
4394
4395
  normalizedRoute: normalizeRunnerRoute({
4395
4396
  name: "telegram-monitor-direct-human-delegation-contract-upgrade",
@@ -4464,15 +4465,16 @@ export async function runSelftestRunnerScenarios(push, deps) {
4464
4465
  timeoutSeconds: 30,
4465
4466
  actor: { user_id: "user-1" },
4466
4467
  },
4467
- deps: {
4468
- saveRunnerRouteState: () => {},
4469
- startRunnerTypingHeartbeat: () => ({ async stop() {} }),
4470
- runRunnerAIExecution: async ({ inputPayload }) => {
4471
- aiCalls += 1;
4472
- if (safeObject(inputPayload?.response_contract).require_actionable_contract === true) {
4473
- return {
4474
- skip: false,
4475
- reply: "@RyoAI3_bot briefly greet everyone in this room and state your role in one line.",
4468
+ deps: {
4469
+ saveRunnerRouteState: () => {},
4470
+ startRunnerTypingHeartbeat: () => ({ async stop() {} }),
4471
+ runRunnerAIExecution: async ({ inputPayload }) => {
4472
+ aiCalls += 1;
4473
+ if (safeObject(inputPayload?.response_contract).require_actionable_contract === true) {
4474
+ forcedContractPayload = inputPayload;
4475
+ return {
4476
+ skip: false,
4477
+ reply: "@RyoAI3_bot briefly greet everyone in this room and state your role in one line.",
4476
4478
  replyToMessageID: 0,
4477
4479
  contract: {
4478
4480
  type: "delegation",
@@ -4530,16 +4532,17 @@ export async function runSelftestRunnerScenarios(push, deps) {
4530
4532
  && Array.isArray(deliveredConversation[0]?.allowedResponderSelectors)
4531
4533
  && deliveredConversation[0].allowedResponderSelectors.includes("ryoai3_bot")
4532
4534
  && String(deliveredConversation[0]?.executionContract?.type || "") === "delegation",
4533
- `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} mode=${String(deliveredConversation[0]?.mode || "(none)")} contract=${String(deliveredConversation[0]?.executionContract?.type || "(none)")}`,
4534
- );
4535
- } catch (err) {
4536
- push("direct_human_public_delegate_reply_is_upgraded_to_actionable_contract", false, String(err?.message || err));
4537
- }
4535
+ `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} mode=${String(deliveredConversation[0]?.mode || "(none)")} contract=${String(deliveredConversation[0]?.executionContract?.type || "(none)")} required_targets=${JSON.stringify(safeObject(forcedContractPayload?.response_contract).required_delegation_targets || [])}`,
4536
+ );
4537
+ } catch (err) {
4538
+ push("direct_human_public_delegate_reply_is_upgraded_to_actionable_contract", false, String(err?.message || err));
4539
+ }
4538
4540
 
4539
- try {
4540
- let aiCalls = 0;
4541
- const deliveredConversation = [];
4542
- const processed = await processRunnerSelectedRecord({
4541
+ try {
4542
+ let aiCalls = 0;
4543
+ const deliveredConversation = [];
4544
+ let forcedContractPayload = null;
4545
+ const processed = await processRunnerSelectedRecord({
4543
4546
  routeKey: "direct-human-public-delegate-repairs-lead-bot-key",
4544
4547
  normalizedRoute: normalizeRunnerRoute({
4545
4548
  name: "telegram-monitor-direct-human-public-delegate-repairs-lead-bot",
@@ -4686,10 +4689,11 @@ export async function runSelftestRunnerScenarios(push, deps) {
4686
4689
  push("direct_human_public_delegate_repairs_lead_bot_selector_for_archive_contract", false, String(err?.message || err));
4687
4690
  }
4688
4691
 
4689
- try {
4690
- let aiCalls = 0;
4691
- const deliveredConversation = [];
4692
- const processed = await processRunnerSelectedRecord({
4692
+ try {
4693
+ let aiCalls = 0;
4694
+ const deliveredConversation = [];
4695
+ let forcedContractPayload = null;
4696
+ const processed = await processRunnerSelectedRecord({
4693
4697
  routeKey: "direct-human-public-delegate-fallback-uses-response-allowed-responders-key",
4694
4698
  normalizedRoute: normalizeRunnerRoute({
4695
4699
  name: "telegram-monitor-direct-human-public-delegate-fallback-uses-response-allowed-responders",
@@ -4830,10 +4834,11 @@ export async function runSelftestRunnerScenarios(push, deps) {
4830
4834
  push("direct_human_public_delegate_fallback_uses_response_allowed_responders", false, String(err?.message || err));
4831
4835
  }
4832
4836
 
4833
- try {
4834
- let aiCalls = 0;
4835
- const deliveredConversation = [];
4836
- const processed = await processRunnerSelectedRecord({
4837
+ try {
4838
+ let aiCalls = 0;
4839
+ const deliveredConversation = [];
4840
+ let forcedContractPayload = null;
4841
+ const processed = await processRunnerSelectedRecord({
4837
4842
  routeKey: "direct-human-public-summary-fallback-contract-key",
4838
4843
  normalizedRoute: normalizeRunnerRoute({
4839
4844
  name: "telegram-monitor-direct-human-public-summary-fallback-contract",
@@ -4910,13 +4915,14 @@ export async function runSelftestRunnerScenarios(push, deps) {
4910
4915
  },
4911
4916
  deps: {
4912
4917
  saveRunnerRouteState: () => {},
4913
- startRunnerTypingHeartbeat: () => ({ async stop() {} }),
4914
- runRunnerAIExecution: async ({ inputPayload }) => {
4915
- aiCalls += 1;
4916
- if (safeObject(inputPayload?.response_contract).require_actionable_contract === true) {
4917
- return {
4918
- skip: false,
4919
- reply: "@RyoAI3_bot briefly answer now.",
4918
+ startRunnerTypingHeartbeat: () => ({ async stop() {} }),
4919
+ runRunnerAIExecution: async ({ inputPayload }) => {
4920
+ aiCalls += 1;
4921
+ if (safeObject(inputPayload?.response_contract).require_actionable_contract === true) {
4922
+ forcedContractPayload = inputPayload;
4923
+ return {
4924
+ skip: false,
4925
+ reply: "@RyoAI3_bot briefly answer now.",
4920
4926
  replyToMessageID: 0,
4921
4927
  contract: {
4922
4928
  type: "summary_request",
@@ -4963,26 +4969,26 @@ export async function runSelftestRunnerScenarios(push, deps) {
4963
4969
  ],
4964
4970
  },
4965
4971
  });
4966
- push(
4967
- "direct_human_public_summary_request_fallback_builds_actionable_contract",
4968
- processed.kind === "replied"
4969
- && aiCalls >= 1
4970
- && String(deliveredConversation[0]?.mode || "") === "public_multi_bot"
4971
- && String(deliveredConversation[0]?.leadBotUsername || "") === "ryoai_bot"
4972
- && Array.isArray(deliveredConversation[0]?.allowedResponderSelectors)
4973
- && deliveredConversation[0].allowedResponderSelectors.includes("ryoai3_bot")
4974
- && String(deliveredConversation[0]?.executionContract?.type || "") === "summary_request",
4975
- `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} lead=${String(deliveredConversation[0]?.leadBotUsername || "(none)")} contract=${String(deliveredConversation[0]?.executionContract?.type || "(none)")} reason=${String(processed.skippedRecord?.reason || "(none)")}`,
4976
- );
4977
- } catch (err) {
4978
- push("direct_human_public_summary_request_fallback_builds_actionable_contract", false, String(err?.message || err));
4979
- }
4972
+ push(
4973
+ "direct_human_public_peer_mention_rejects_summary_request_without_delegation",
4974
+ processed.kind === "skipped"
4975
+ && aiCalls >= 1
4976
+ && deliveredConversation.length === 0
4977
+ && ensureArray(safeObject(forcedContractPayload?.response_contract).required_delegation_targets).includes("ryoai3_bot")
4978
+ && String(safeObject(forcedContractPayload?.response_contract).contract_hint || "").trim().length > 0
4979
+ && String(processed.skippedRecord?.reason || "").includes("delegation contract"),
4980
+ `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} required_targets=${JSON.stringify(safeObject(forcedContractPayload?.response_contract).required_delegation_targets || [])} reason=${String(processed.skippedRecord?.reason || "(none)")}`,
4981
+ );
4982
+ } catch (err) {
4983
+ push("direct_human_public_peer_mention_rejects_summary_request_without_delegation", false, String(err?.message || err));
4984
+ }
4980
4985
 
4981
- try {
4982
- let aiCalls = 0;
4983
- let deliveryCalls = 0;
4984
- let deliveredText = "";
4985
- const processed = await processRunnerSelectedRecord({
4986
+ try {
4987
+ let aiCalls = 0;
4988
+ let deliveryCalls = 0;
4989
+ let deliveredText = "";
4990
+ let forcedContractPayload = null;
4991
+ const processed = await processRunnerSelectedRecord({
4986
4992
  routeKey: "delegated-single-lead-dead-end-key",
4987
4993
  normalizedRoute: normalizeRunnerRoute({
4988
4994
  name: "telegram-monitor-delegated-single-lead-dead-end",
@@ -5057,14 +5063,17 @@ export async function runSelftestRunnerScenarios(push, deps) {
5057
5063
  timeoutSeconds: 30,
5058
5064
  actor: { user_id: "user-1" },
5059
5065
  },
5060
- deps: {
5061
- saveRunnerRouteState: () => {},
5062
- startRunnerTypingHeartbeat: () => ({ async stop() {} }),
5063
- runRunnerAIExecution: async () => {
5064
- aiCalls += 1;
5065
- return {
5066
- skip: false,
5067
- reply: "I will analyze first and come back later.",
5066
+ deps: {
5067
+ saveRunnerRouteState: () => {},
5068
+ startRunnerTypingHeartbeat: () => ({ async stop() {} }),
5069
+ runRunnerAIExecution: async ({ inputPayload }) => {
5070
+ aiCalls += 1;
5071
+ if (safeObject(inputPayload?.response_contract).require_actionable_contract === true) {
5072
+ forcedContractPayload = inputPayload;
5073
+ }
5074
+ return {
5075
+ skip: false,
5076
+ reply: "I will analyze first and come back later.",
5068
5077
  replyToMessageID: 0,
5069
5078
  };
5070
5079
  },
@@ -5100,16 +5109,19 @@ export async function runSelftestRunnerScenarios(push, deps) {
5100
5109
  ],
5101
5110
  },
5102
5111
  });
5103
- push(
5104
- "delegated_single_lead_lead_opening_requires_delegation_contract_for_peer_followup",
5105
- processed.kind === "skipped"
5106
- && aiCalls === 2
5107
- && !String(deliveredText || "").trim(),
5108
- `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} reply=${String(deliveredText || "(none)")}`,
5109
- );
5110
- } catch (err) {
5111
- push("delegated_single_lead_lead_opening_requires_delegation_contract_for_peer_followup", false, String(err?.message || err));
5112
- }
5112
+ push(
5113
+ "delegated_single_lead_lead_opening_requires_delegation_contract_for_peer_followup",
5114
+ processed.kind === "skipped"
5115
+ && aiCalls === 2
5116
+ && !String(deliveredText || "").trim()
5117
+ && ensureArray(safeObject(forcedContractPayload?.response_contract).required_delegation_targets).includes("ryoai2_bot")
5118
+ && ensureArray(safeObject(forcedContractPayload?.response_contract).required_delegation_targets).includes("ryoai3_bot")
5119
+ && String(safeObject(forcedContractPayload?.response_contract).contract_hint || "").trim().length > 0,
5120
+ `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} reply=${String(deliveredText || "(none)")} required_targets=${JSON.stringify(safeObject(forcedContractPayload?.response_contract).required_delegation_targets || [])} hint=${String(safeObject(forcedContractPayload?.response_contract).contract_hint || "(none)")}`,
5121
+ );
5122
+ } catch (err) {
5123
+ push("delegated_single_lead_lead_opening_requires_delegation_contract_for_peer_followup", false, String(err?.message || err));
5124
+ }
5113
5125
 
5114
5126
  try {
5115
5127
  let aiCalls = 0;
@@ -9966,14 +9978,51 @@ export async function runSelftestRunnerScenarios(push, deps) {
9966
9978
  && !prompt.includes("Archived Note"),
9967
9979
  `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} contexts=${projectContexts.map((item) => item.title).join(",") || "(none)"} prompt_has_active_context=${String(prompt.includes("Active project context"))}`,
9968
9980
  );
9969
- } catch (err) {
9970
- push("active_project_context_is_injected_into_runner_prompt", false, String(err?.message || err));
9971
- }
9972
-
9973
- try {
9974
- const createCalls = [];
9975
- let lastSavedState = null;
9976
- const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-context-suggest-"));
9981
+ } catch (err) {
9982
+ push("active_project_context_is_injected_into_runner_prompt", false, String(err?.message || err));
9983
+ }
9984
+
9985
+ try {
9986
+ const prompt = buildLocalBotPrompt({
9987
+ bot: {
9988
+ name: "RyoAI_bot",
9989
+ username: "RyoAI_bot",
9990
+ role: "monitor",
9991
+ },
9992
+ trigger: {
9993
+ body: "@RyoAI_bot ask @RyoAI2_bot to greet in one line",
9994
+ },
9995
+ response_contract: {
9996
+ is_current_bot_candidate: true,
9997
+ require_actionable_contract: true,
9998
+ allowed_contract_types: ["delegation"],
9999
+ required_delegation_targets: ["ryoai2_bot"],
10000
+ contract_hint: "Previous reply mentioned @ryoai2_bot without a delegation contract.",
10001
+ },
10002
+ destination: {
10003
+ label: "Main Room",
10004
+ chat_id: "-100123",
10005
+ },
10006
+ project: {
10007
+ id: selftestProjectID,
10008
+ },
10009
+ }, { terse: true });
10010
+ push(
10011
+ "local_bot_prompt_requires_contract_for_peer_mentions",
10012
+ prompt.includes("Without a matching contract, mentioned bots will not act.")
10013
+ && prompt.includes("@ryoai2_bot")
10014
+ && prompt.includes("contract.type=\"delegation\"")
10015
+ && prompt.includes("Previous reply mentioned @ryoai2_bot without a delegation contract."),
10016
+ prompt,
10017
+ );
10018
+ } catch (err) {
10019
+ push("local_bot_prompt_requires_contract_for_peer_mentions", false, String(err?.message || err));
10020
+ }
10021
+
10022
+ try {
10023
+ const createCalls = [];
10024
+ let lastSavedState = null;
10025
+ const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-selftest-context-suggest-"));
9977
10026
  const processed = await processRunnerSelectedRecord({
9978
10027
  routeKey: "context-suggest-human-rule-key",
9979
10028
  normalizedRoute: normalizeRunnerRoute({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.209",
3
+ "version": "0.2.211",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [