metheus-governance-mcp-cli 0.2.221 → 0.2.223

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
@@ -11085,8 +11085,10 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
11085
11085
  route_name: normalizedRoute.name,
11086
11086
  logical_signature: runnerRouteLogicalSignature(normalizedRoute),
11087
11087
  });
11088
+ let cycleOutcome = "polling";
11088
11089
  try {
11089
11090
  const result = await processRunnerRouteOnce(normalizedRoute, runtime, "start", { deferExecution: true });
11091
+ cycleOutcome = String(result?.outcome || "").trim().toLowerCase() || "idle";
11090
11092
  const deferredExecution = safeObject(result.deferred_execution);
11091
11093
  if (deferredExecution.routeKey) {
11092
11094
  tui?.reportExecutionStage({
@@ -11392,6 +11394,7 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
11392
11394
  ...result,
11393
11395
  deferred_execution: undefined,
11394
11396
  };
11397
+ cycleOutcome = String(acceptedResult?.outcome || "accepted").trim().toLowerCase() || "accepted";
11395
11398
  runnerLogger?.append("route_result", {
11396
11399
  route_key: String(acceptedResult?.route_key || routeKey).trim(),
11397
11400
  route_name: String(acceptedResult?.route_name || normalizedRoute.name || "").trim(),
@@ -11430,6 +11433,7 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
11430
11433
  const errorText = String(err?.message || err);
11431
11434
  const fatalArchiveBootstrapError = errorText.includes("Archive thread is missing")
11432
11435
  && errorText.includes("write access is denied");
11436
+ cycleOutcome = fatalArchiveBootstrapError ? "blocked" : "error";
11433
11437
  saveRunnerRouteState(routeKey, {
11434
11438
  ...safeObject(loadBotRunnerState().routes[routeKey]),
11435
11439
  last_error: errorText,
@@ -11453,8 +11457,25 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
11453
11457
  printRunnerResult("start", result, jsonMode);
11454
11458
  }
11455
11459
  } finally {
11456
- const routeState = safeObject(loadBotRunnerState().routes[routeKey]);
11457
- const activeExecutionState = resolveRunnerActiveExecutionState(routeState);
11460
+ let routeState = safeObject(loadBotRunnerState().routes[routeKey]);
11461
+ let activeExecutionState = resolveRunnerActiveExecutionState(routeState);
11462
+ const shouldClearStaleRouteError = !activeExecutionState.active
11463
+ && [
11464
+ "idle",
11465
+ "primed",
11466
+ "skipped",
11467
+ "replied",
11468
+ "dry_run",
11469
+ "delivery_failed_after_generation",
11470
+ ].includes(cycleOutcome);
11471
+ if (shouldClearStaleRouteError && (String(routeState.last_error || "").trim() || String(routeState.active_comment_id || "").trim())) {
11472
+ saveRunnerRouteState(routeKey, {
11473
+ ...emptyRunnerActiveExecutionPatch(),
11474
+ last_error: "",
11475
+ });
11476
+ routeState = safeObject(loadBotRunnerState().routes[routeKey]);
11477
+ activeExecutionState = resolveRunnerActiveExecutionState(routeState);
11478
+ }
11458
11479
  tui?.setRouteState(routeKey, {
11459
11480
  intent_type: String(routeState.last_intent_type || "").trim(),
11460
11481
  source_message_id: intFromRawAllowZero(routeState.last_source_message_id, 0),
@@ -1939,6 +1939,18 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
1939
1939
  && selfIsLeadBot
1940
1940
  ? "The human asked this bot to coordinate other bots publicly. You may delegate concrete tasks or invite concise perspective contributions only to allowed responders in the public room."
1941
1941
  : "Do not delegate the answer to another bot.",
1942
+ responseContract.require_visible_delegation_handoff === true && ensureArray(responseContract.required_delegation_targets).length > 0
1943
+ ? `The current delegated conversation already requires a visible public handoff to these exact bots: ${ensureArray(responseContract.required_delegation_targets).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")}.`
1944
+ : "",
1945
+ responseContract.require_visible_delegation_handoff === true
1946
+ ? "Your first public reply must explicitly mention those exact bots and request their contribution now."
1947
+ : "",
1948
+ responseContract.require_visible_delegation_handoff === true
1949
+ ? "Do not return only a human-facing clarification or information request without visibly addressing those bots."
1950
+ : "",
1951
+ responseContract.require_visible_delegation_handoff === true
1952
+ ? "If information is incomplete, still open the public handoff and ask each target bot for a concise initial perspective based on the uncertainty."
1953
+ : "",
1942
1954
  "Mentions and reply targets are routing hints, not proof that this bot should definitely answer.",
1943
1955
  conversation?.mode === "public_multi_bot"
1944
1956
  ? "This is a public multi-bot room conversation. Other bots and humans can read your reply."
@@ -2117,6 +2129,15 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
2117
2129
  if (selfIsLeadBot) {
2118
2130
  lines.push(
2119
2131
  "As the lead bot, your first public reply must be immediately actionable.",
2132
+ responseContract.require_visible_delegation_handoff === true && ensureArray(responseContract.required_delegation_targets).length > 0
2133
+ ? `Because this delegated conversation already targets ${ensureArray(responseContract.required_delegation_targets).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")}, your first public reply must visibly call those exact bots in the room now.`
2134
+ : "",
2135
+ responseContract.require_visible_delegation_handoff === true
2136
+ ? "A contract-only delegation is incomplete. The visible reply text must enact the handoff."
2137
+ : "",
2138
+ responseContract.require_visible_delegation_handoff === true
2139
+ ? "Do not stop at asking only the human for more information. Open the bot handoff first, then note what remains unknown."
2140
+ : "",
2120
2141
  "If you are delegating, include contract.type=\"delegation\" with explicit assignments or perspective handoffs for the exact bots you are tasking now.",
2121
2142
  "If you mention any allowed responder in the reply text, the contract must name that responder in assignments or next_responders.",
2122
2143
  "If you are completing the work yourself now, include contract.type=\"direct_result\".",
@@ -5103,7 +5103,7 @@ export async function processRunnerSelectedRecord({
5103
5103
  && executionContract.actionable === true
5104
5104
  && (!allowedActionableTypes.size || allowedActionableTypes.has(String(executionContract.type || "").trim().toLowerCase())),
5105
5105
  );
5106
- const responseContractValidation = (() => {
5106
+ let responseContractValidation = (() => {
5107
5107
  if (requiresActionableContract && !hasValidActionableContract) {
5108
5108
  return {
5109
5109
  ok: false,
@@ -5286,6 +5286,34 @@ export async function processRunnerSelectedRecord({
5286
5286
  triggerDecision: effectiveTriggerDecision,
5287
5287
  conversationContext: effectiveConversationContext,
5288
5288
  });
5289
+ const visibleDelegationTargets = uniqueOrdered(
5290
+ effectiveConversationContext?.mode === "public_multi_bot"
5291
+ && String(effectiveConversationContext?.stage || "").trim() === "human_opening"
5292
+ && effectiveResolvedIntentMode === "delegated_single_lead"
5293
+ && currentBotSelector
5294
+ && currentBotSelector === normalizeMentionSelector(effectiveConversationContext?.leadBotUsername)
5295
+ && normalizedExecutionContractType === "delegation"
5296
+ ? ensureArray(executionContract?.assignments)
5297
+ .map((item) => normalizeMentionSelector(item?.targetBot))
5298
+ .filter(Boolean)
5299
+ : [],
5300
+ );
5301
+ if (responseContractValidation.ok && visibleDelegationTargets.length > 0) {
5302
+ const visibleReplyTargets = extractManagedPeerMentionSelectors(
5303
+ sanitizedReplyText,
5304
+ directHumanPeerMap,
5305
+ currentBotSelector,
5306
+ );
5307
+ const missingVisibleDelegationTargets = visibleDelegationTargets.filter((item) => !visibleReplyTargets.includes(item));
5308
+ if (missingVisibleDelegationTargets.length > 0) {
5309
+ responseContractValidation = {
5310
+ ok: false,
5311
+ status: "delegation_targets_not_visible_in_reply",
5312
+ reason: `delegation contract targeted ${missingVisibleDelegationTargets.map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ")} but the public reply did not visibly hand off to them`,
5313
+ targets: missingVisibleDelegationTargets,
5314
+ };
5315
+ }
5316
+ }
5289
5317
  if (effectiveConversationContext?.mode === "public_multi_bot") {
5290
5318
  const currentBotSelector = normalizeMentionSelector(bot?.username || bot?.name);
5291
5319
  const currentSession = safeObject(effectiveConversationContext.session);
@@ -11,6 +11,18 @@ function ensureArray(value) {
11
11
  return Array.isArray(value) ? value : [];
12
12
  }
13
13
 
14
+ function uniqueOrdered(items) {
15
+ const seen = new Set();
16
+ const result = [];
17
+ for (const item of ensureArray(items)) {
18
+ const text = String(item || "").trim();
19
+ if (!text || seen.has(text)) continue;
20
+ seen.add(text);
21
+ result.push(text);
22
+ }
23
+ return result;
24
+ }
25
+
14
26
  function intFromRawAllowZero(raw, fallback = 0) {
15
27
  const text = String(raw ?? "").trim();
16
28
  if (!text) return fallback;
@@ -177,6 +189,31 @@ export function buildRunnerInputPayload({
177
189
  const executionContractType = String(
178
190
  directHumanIntent.executionContractType || directHumanIntent.execution_contract_type || "",
179
191
  ).trim().toLowerCase();
192
+ const currentBotSelector = normalizeTelegramMentionUsername(bot?.username || bot?.name);
193
+ const normalizedLeadBot = normalizeTelegramMentionUsername(
194
+ directHumanIntent.leadBotSelector || directHumanIntent.lead_bot,
195
+ );
196
+ const normalizedInitialResponders = uniqueOrdered(
197
+ ensureArray(directHumanIntent.initialResponderSelectors || directHumanIntent.initial_responders)
198
+ .map((value) => normalizeTelegramMentionUsername(value))
199
+ .filter(Boolean),
200
+ );
201
+ const normalizedAllowedResponders = uniqueOrdered(
202
+ ensureArray(directHumanIntent.allowedResponderSelectors || directHumanIntent.allowed_responders)
203
+ .map((value) => normalizeTelegramMentionUsername(value))
204
+ .filter(Boolean),
205
+ );
206
+ const requiredDelegationTargets = uniqueOrdered(
207
+ String(directHumanIntent.intentMode || directHumanIntent.intent_mode || "").trim() === "delegated_single_lead"
208
+ && currentBotSelector
209
+ && normalizedLeadBot
210
+ && currentBotSelector === normalizedLeadBot
211
+ && normalizedInitialResponders.length === 1
212
+ && normalizedInitialResponders.includes(currentBotSelector)
213
+ ? normalizedAllowedResponders.filter((value) => value && value !== currentBotSelector)
214
+ : [],
215
+ );
216
+ const requireVisibleDelegationHandoff = requiredDelegationTargets.length > 0;
180
217
  const taskMetadata = buildRunnerTaskName(directHumanIntent.intentType, directHumanIntent);
181
218
  return {
182
219
  task: taskMetadata,
@@ -262,6 +299,8 @@ export function buildRunnerInputPayload({
262
299
  human_reply_expectation: String(directHumanIntent.replyExpectation || "").trim(),
263
300
  require_actionable_contract: directHumanIntent.requiresActionableContract === true,
264
301
  allowed_contract_types: ensureArray(directHumanIntent.allowedContractTypes),
302
+ require_visible_delegation_handoff: requireVisibleDelegationHandoff,
303
+ required_delegation_targets: requiredDelegationTargets,
265
304
  },
266
305
  agent_context: {
267
306
  bot_identity: {
@@ -294,6 +333,8 @@ export function buildRunnerInputPayload({
294
333
  require_actionable_contract: directHumanIntent.requiresActionableContract === true,
295
334
  allowed_contract_types: ensureArray(directHumanIntent.allowedContractTypes),
296
335
  candidate_bot_usernames: candidateBotUsernames,
336
+ require_visible_delegation_handoff: requireVisibleDelegationHandoff,
337
+ required_delegation_targets: requiredDelegationTargets,
297
338
  },
298
339
  },
299
340
  conversation: conversationContext
@@ -10156,6 +10156,54 @@ export async function runSelftestRunnerScenarios(push, deps) {
10156
10156
  push("local_bot_prompt_treats_expected_followup_as_contract_authority", false, String(err?.message || err));
10157
10157
  }
10158
10158
 
10159
+ try {
10160
+ const prompt = buildLocalBotPrompt({
10161
+ bot: {
10162
+ name: "RyoAI_bot",
10163
+ username: "RyoAI_bot",
10164
+ role: "monitor",
10165
+ },
10166
+ trigger: {
10167
+ body: "@RyoAI_bot lead the room and bring in @RyoAI2_bot and @RyoAI3_bot now",
10168
+ },
10169
+ response_contract: {
10170
+ is_current_bot_candidate: true,
10171
+ require_actionable_contract: true,
10172
+ allowed_contract_types: ["delegation"],
10173
+ require_visible_delegation_handoff: true,
10174
+ required_delegation_targets: ["ryoai2_bot", "ryoai3_bot"],
10175
+ },
10176
+ conversation: {
10177
+ mode: "public_multi_bot",
10178
+ id: "conversation-lead-visible-handoff",
10179
+ stage: "human_opening",
10180
+ intent_mode: "delegated_single_lead",
10181
+ allow_bot_to_bot: true,
10182
+ lead_bot_username: "ryoai_bot",
10183
+ allowed_responders: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
10184
+ initial_responders: ["ryoai_bot"],
10185
+ },
10186
+ destination: {
10187
+ label: "Main Room",
10188
+ chat_id: "-100123",
10189
+ },
10190
+ project: {
10191
+ id: selftestProjectID,
10192
+ },
10193
+ }, { terse: true });
10194
+ push(
10195
+ "local_bot_prompt_requires_visible_lead_handoff_for_delegated_opening",
10196
+ prompt.includes("requires a visible public handoff")
10197
+ && prompt.includes("must explicitly mention those exact bots")
10198
+ && prompt.includes("Do not return only a human-facing clarification or information request without visibly addressing those bots.")
10199
+ && prompt.includes("The visible reply text must enact the handoff.")
10200
+ && prompt.includes("Do not stop at asking only the human for more information."),
10201
+ prompt,
10202
+ );
10203
+ } catch (err) {
10204
+ push("local_bot_prompt_requires_visible_lead_handoff_for_delegated_opening", false, String(err?.message || err));
10205
+ }
10206
+
10159
10207
  try {
10160
10208
  const createCalls = [];
10161
10209
  let lastSavedState = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.221",
3
+ "version": "0.2.223",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [