metheus-governance-mcp-cli 0.2.267 → 0.2.269

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
@@ -6223,20 +6223,26 @@ function markRunnerRequestLifecycle({
6223
6223
  return String(existing.normalized_intent || "").trim().toLowerCase();
6224
6224
  })();
6225
6225
  const normalizedOutcome = String(outcome || "").trim().toLowerCase();
6226
- const nextStatus = (() => {
6227
- if (normalizedOutcome === "claimed") return "claimed";
6228
- if (normalizedOutcome === "running") return "running";
6229
- if (normalizedOutcome === "delivery_failed_after_generation") return "running";
6230
- if (normalizedOutcome === "replied") {
6231
- return shouldRemainRunningAfterReply ? "running" : "completed";
6232
- }
6233
- if (normalizedOutcome === "loop_closed") return "loop_closed";
6234
- if (normalizedOutcome === "expired") return "expired";
6235
- if (normalizedOutcome === "error" || normalizedOutcome === "skipped" || normalizedOutcome === "closed") {
6236
- return "closed";
6237
- }
6238
- return normalizeRunnerRequestStatus(existing.status);
6239
- })();
6226
+ const nextStatus = (() => {
6227
+ if (normalizedOutcome === "claimed") return "claimed";
6228
+ if (normalizedOutcome === "running") return "running";
6229
+ if (normalizedOutcome === "delivery_failed_after_generation") return "running";
6230
+ if (normalizedOutcome === "replied") {
6231
+ return shouldRemainRunningAfterReply ? "running" : "completed";
6232
+ }
6233
+ if (normalizedOutcome === "loop_closed") return "loop_closed";
6234
+ if (normalizedOutcome === "expired") return "expired";
6235
+ if (
6236
+ normalizedOutcome === "error"
6237
+ || normalizedOutcome === "skipped"
6238
+ || normalizedOutcome === "closed"
6239
+ || normalizedOutcome === "execution_failed"
6240
+ || normalizedOutcome === "policy_violation"
6241
+ ) {
6242
+ return "closed";
6243
+ }
6244
+ return normalizeRunnerRequestStatus(existing.status);
6245
+ })();
6240
6246
  const nowISO = new Date().toISOString();
6241
6247
  const commentKind = String(parsed.kind || "").trim().toLowerCase();
6242
6248
  const isRootHumanComment = ["telegram_message", "telegram_edited_message"].includes(commentKind);
@@ -1876,6 +1876,9 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
1876
1876
  `- Trigger hint reason: ${String(responseContract.candidate_reason || "-").trim() || "-"}`,
1877
1877
  `- Mentioned bot usernames: ${ensureArray(responseContract.candidate_bot_usernames).length ? ensureArray(responseContract.candidate_bot_usernames).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ") : "-"}`,
1878
1878
  `- Reply target bot username: ${String(responseContract.reply_to_bot_username || "").trim() ? `@${String(responseContract.reply_to_bot_username || "").trim().replace(/^@+/, "")}` : "-"}`,
1879
+ String(responseContract.reply_to_bot_username || "").trim()
1880
+ ? `- If you answer directly to that target bot, start the visible reply with @${String(responseContract.reply_to_bot_username || "").trim().replace(/^@+/, "")}.`
1881
+ : "",
1879
1882
  "",
1880
1883
  "Project background (context only):",
1881
1884
  `- Project ID: ${projectID || "-"}`,
@@ -17,6 +17,40 @@ function intFromRawAllowZero(raw, fallback = 0) {
17
17
  return Number.isFinite(parsed) ? parsed : fallback;
18
18
  }
19
19
 
20
+ function escapeRegexText(value) {
21
+ return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22
+ }
23
+
24
+ function detectDirectedManagedReplyTarget({
25
+ text,
26
+ currentBotSelector = "",
27
+ managedMentions = [],
28
+ }) {
29
+ const normalizedText = String(text || "").trim();
30
+ const currentSelector = String(currentBotSelector || "").trim().toLowerCase();
31
+ if (!normalizedText || !currentSelector) {
32
+ return "";
33
+ }
34
+ const instructionPattern = /(?:\b(?:say|tell|greet|reply|answer|introduce|mention)\b|인사(?:해|하)|말(?:해|하)|얘기(?:해|하)|전달(?:해|하)|전해|알려|소개(?:해|하)|답(?:해|하)|응답(?:해|하)|말씀(?:해|하))/i;
35
+ if (!instructionPattern.test(normalizedText)) {
36
+ return "";
37
+ }
38
+ const candidates = ensureArray(managedMentions)
39
+ .map((item) => String(item || "").trim().toLowerCase())
40
+ .filter((item) => item && item !== currentSelector);
41
+ for (const selector of candidates) {
42
+ const escapedSelector = escapeRegexText(String(selector || "").replace(/^@+/, ""));
43
+ const targetPattern = new RegExp(
44
+ `(?:@${escapedSelector}\\s*(?:에게|한테|께|보고)?|(?:to|for)\\s+@${escapedSelector}\\b)`,
45
+ "i",
46
+ );
47
+ if (targetPattern.test(normalizedText)) {
48
+ return selector;
49
+ }
50
+ }
51
+ return "";
52
+ }
53
+
20
54
  export async function resolveHumanIntentContext({
21
55
  selectedRecord,
22
56
  normalizedRoute,
@@ -84,6 +118,17 @@ export async function resolveHumanIntentContext({
84
118
  runnerHumanIntentPromises.set(cacheKey, promise);
85
119
  }
86
120
  let humanIntent = await runnerHumanIntentPromises.get(cacheKey);
121
+ const directedReplyTargetSelector = detectDirectedManagedReplyTarget({
122
+ text: parsed.body,
123
+ currentBotSelector,
124
+ managedMentions,
125
+ });
126
+ if (directedReplyTargetSelector) {
127
+ humanIntent = {
128
+ ...safeObject(humanIntent),
129
+ replyTargetBotSelector: directedReplyTargetSelector,
130
+ };
131
+ }
87
132
  if (!isCompleteHumanIntentContract(humanIntent)) {
88
133
  return {
89
134
  currentBotSelector,
@@ -152,6 +197,12 @@ export function buildDirectHumanResponseContract({
152
197
  );
153
198
  const requiresActionableContract = source.requiresActionableContract === true
154
199
  || source.require_actionable_contract === true;
200
+ const replyTargetBotSelector = normalizeMentionSelector(
201
+ source.replyTargetBotSelector
202
+ || source.reply_target_bot
203
+ || source.reply_to_bot_username
204
+ || "",
205
+ );
155
206
  return {
156
207
  intentMode,
157
208
  allowBotToBot,
@@ -163,6 +214,7 @@ export function buildDirectHumanResponseContract({
163
214
  intentType,
164
215
  requiresActionableContract,
165
216
  allowedContractTypes,
217
+ replyTargetBotSelector,
166
218
  };
167
219
  }
168
220
 
@@ -213,6 +265,7 @@ export function mergeDirectHumanResponseContract({
213
265
  const explicitAllowBotToBot = readExplicit(["allow_bot_to_bot", "allowBotToBot"]);
214
266
  const explicitLeadBot = readExplicit(["human_lead_bot"]);
215
267
  const explicitSummaryBot = readExplicit(["human_summary_bot"]);
268
+ const explicitReplyTargetBot = readExplicit(["human_reply_target_bot", "reply_to_bot_username"]);
216
269
  const intentMode = String(
217
270
  explicitIntentMode
218
271
  || base.intentMode
@@ -255,6 +308,11 @@ export function mergeDirectHumanResponseContract({
255
308
  || base.summaryBotSelector
256
309
  || "",
257
310
  );
311
+ const replyTargetBotSelector = normalizeMentionSelector(
312
+ explicitReplyTargetBot
313
+ || base.replyTargetBotSelector
314
+ || "",
315
+ );
258
316
  const requiresActionableContract = base.requiresActionableContract === true;
259
317
  const allowedContractTypes = ensureArray(base.allowedContractTypes)
260
318
  .map((item) => String(item || "").trim().toLowerCase())
@@ -271,6 +329,7 @@ export function mergeDirectHumanResponseContract({
271
329
  intentType,
272
330
  requiresActionableContract,
273
331
  allowedContractTypes,
332
+ replyTargetBotSelector,
274
333
  };
275
334
  }
276
335
 
@@ -26,6 +26,7 @@ export async function prepareRunnerSelectedRecordDeliveryContext({
26
26
  bot,
27
27
  selectedRecord,
28
28
  currentBotSelector = "",
29
+ replyTargetBotSelector = "",
29
30
  executionContract = null,
30
31
  effectiveTriggerDecision,
31
32
  effectiveConversationContext = null,
@@ -55,13 +56,28 @@ export async function prepareRunnerSelectedRecordDeliveryContext({
55
56
  }
56
57
 
57
58
  const normalizedExecutionContractType = String(executionContract?.type || "").trim().toLowerCase();
58
- const sanitizedReplyText = sanitizeForcedDirectReplyText({
59
+ const normalizedReplyTargetBotSelector = normalizeMentionSelector(replyTargetBotSelector);
60
+ let sanitizedReplyText = sanitizeForcedDirectReplyText({
59
61
  replyText: aiResult?.reply,
60
62
  bot,
61
63
  selectedRecord,
62
64
  triggerDecision: effectiveTriggerDecision,
63
65
  conversationContext: effectiveConversationContext,
64
66
  });
67
+ if (
68
+ sanitizedReplyText
69
+ && normalizedReplyTargetBotSelector
70
+ && normalizedReplyTargetBotSelector !== currentBotSelector
71
+ ) {
72
+ const visibleReplyTargets = extractManagedPeerMentionSelectors(
73
+ sanitizedReplyText,
74
+ directHumanPeerMap,
75
+ currentBotSelector,
76
+ );
77
+ if (!visibleReplyTargets.includes(normalizedReplyTargetBotSelector)) {
78
+ sanitizedReplyText = `@${normalizedReplyTargetBotSelector} ${String(sanitizedReplyText || "").trim()}`.trim();
79
+ }
80
+ }
65
81
  const attemptedDeliveryEnvelope = buildTelegramBotReplyEnvelope({
66
82
  sourceEnvelope: sourceMessageEnvelope,
67
83
  messageThreadID: replyMessageThreadID,
@@ -258,6 +258,12 @@ export function buildRunnerInputPayload({
258
258
  .map((value) => normalizeMentionSelector(value))
259
259
  .filter(Boolean),
260
260
  );
261
+ const normalizedReplyTargetBot = normalizeMentionSelector(
262
+ directHumanIntent.replyTargetBotSelector
263
+ || directHumanIntent.reply_target_bot
264
+ || directHumanIntent.reply_to_bot_username
265
+ || parsed.replyToUsername,
266
+ );
261
267
  const requiredDelegationTargets = uniqueOrdered(
262
268
  String(directHumanIntent.intentMode || directHumanIntent.intent_mode || "").trim() === "delegated_single_lead"
263
269
  && currentBotSelector
@@ -353,11 +359,12 @@ export function buildRunnerInputPayload({
353
359
  self_bot_username: String(bot?.username || "").trim(),
354
360
  mentioned_usernames: ensureArray(parsed.mentionUsernames).map((value) => String(value || "").trim()).filter(Boolean),
355
361
  candidate_bot_usernames: candidateBotUsernames,
356
- reply_to_bot_username: String(parsed.replyToUsername || "").trim(),
362
+ reply_to_bot_username: normalizedReplyTargetBot,
357
363
  human_intent_mode: String(directHumanIntent.intentMode || "").trim(),
358
364
  human_intent_type: String(directHumanIntent.intentType || "").trim(),
359
365
  human_lead_bot: String(directHumanIntent.leadBotSelector || "").trim(),
360
366
  human_summary_bot: String(directHumanIntent.summaryBotSelector || "").trim(),
367
+ human_reply_target_bot: normalizedReplyTargetBot,
361
368
  human_allowed_responders: ensureArray(directHumanIntent.allowedResponderSelectors),
362
369
  human_reply_expectation: String(directHumanIntent.replyExpectation || "").trim(),
363
370
  require_actionable_contract: directHumanIntent.requiresActionableContract === true,
@@ -378,6 +385,7 @@ export function buildRunnerInputPayload({
378
385
  body: String(parsed.body || "").trim(),
379
386
  message_id: Number.parseInt(String(parsed.messageID ?? 0), 10) || 0,
380
387
  reply_to_message_id: Number.parseInt(String(parsed.replyToMessageID ?? 0), 10) || 0,
388
+ reply_to_bot_username: normalizedReplyTargetBot,
381
389
  },
382
390
  conversation_contract: {
383
391
  conversation_id: String(directHumanIntent.conversationID || directHumanIntent.conversation_id || parsed.conversationID || "").trim(),
@@ -388,6 +396,7 @@ export function buildRunnerInputPayload({
388
396
  participants: ensureArray(directHumanIntent.participantSelectors || directHumanIntent.participants),
389
397
  initial_responders: ensureArray(directHumanIntent.initialResponderSelectors || directHumanIntent.initial_responders),
390
398
  allowed_responders: ensureArray(directHumanIntent.allowedResponderSelectors || directHumanIntent.allowed_responders),
399
+ reply_target_bot: normalizedReplyTargetBot,
391
400
  reply_expectation: String(directHumanIntent.replyExpectation || directHumanIntent.reply_expectation || "").trim(),
392
401
  execution_contract_type: executionContractType,
393
402
  },
@@ -3477,6 +3486,7 @@ export async function processRunnerSelectedRecord({
3477
3486
  bot,
3478
3487
  selectedRecord,
3479
3488
  currentBotSelector,
3489
+ replyTargetBotSelector: directHumanResponseContract?.replyTargetBotSelector,
3480
3490
  executionContract,
3481
3491
  effectiveTriggerDecision,
3482
3492
  effectiveConversationContext,
@@ -2615,16 +2615,61 @@ export async function runSelftestRunnerScenarios(push, deps) {
2615
2615
  executionContractTargets: ["@RyoAI3_bot"],
2616
2616
  nextExpectedResponders: [],
2617
2617
  });
2618
- push(
2619
- "runner_request_lifecycle_reply_ignores_session_state_when_continuation_targets_remain",
2620
- String(continuedRequest?.status || "") === "running",
2621
- `status=${String(continuedRequest?.status || "(none)")}`,
2622
- );
2623
-
2624
- saveBotRunnerState({
2625
- routes: {
2626
- [requestRouteKey]: {},
2627
- },
2618
+ push(
2619
+ "runner_request_lifecycle_reply_ignores_session_state_when_continuation_targets_remain",
2620
+ String(continuedRequest?.status || "") === "running",
2621
+ `status=${String(continuedRequest?.status || "(none)")}`,
2622
+ );
2623
+
2624
+ saveBotRunnerState({
2625
+ routes: {
2626
+ [requestRouteKey]: {},
2627
+ },
2628
+ sharedInboxes: {},
2629
+ excludedComments: {},
2630
+ requests: {
2631
+ "request-key-2b": {
2632
+ request_key: "request-key-2b",
2633
+ project_id: selftestProjectID,
2634
+ provider: "telegram",
2635
+ chat_id: "-100123",
2636
+ source_message_id: 711,
2637
+ conversation_id: "conv-request-2b",
2638
+ status: "running",
2639
+ claimed_by_route: requestRouteKey,
2640
+ },
2641
+ },
2642
+ consumedComments: {},
2643
+ });
2644
+ const failedRequest = markRunnerRequestLifecycle({
2645
+ normalizedRoute: requestRoute,
2646
+ requestKey: "request-key-2b",
2647
+ selectedRecord: {
2648
+ id: "comment-request-finish-2b",
2649
+ parsedArchive: {
2650
+ kind: "bot_reply",
2651
+ chatID: "-100123",
2652
+ messageID: 712,
2653
+ conversationID: "conv-request-2b",
2654
+ },
2655
+ },
2656
+ routeKey: requestRouteKey,
2657
+ outcome: "execution_failed",
2658
+ closedReason: "worker step completed without any validated project artifacts",
2659
+ currentBotSelector: "@RyoAI_bot",
2660
+ });
2661
+ push(
2662
+ "runner_request_lifecycle_execution_failed_closes_request",
2663
+ String(failedRequest?.status || "") === "closed"
2664
+ && String(failedRequest?.closed_reason || "") === "worker step completed without any validated project artifacts"
2665
+ && String(failedRequest?.closed_at || "").trim().length > 0,
2666
+ `status=${String(failedRequest?.status || "(none)")} reason=${String(failedRequest?.closed_reason || "(none)")} closed_at=${String(failedRequest?.closed_at || "(none)")}`,
2667
+ );
2668
+
2669
+ saveBotRunnerState({
2670
+ routes: {
2671
+ [requestRouteKey]: {},
2672
+ },
2628
2673
  sharedInboxes: {},
2629
2674
  excludedComments: {},
2630
2675
  requests: {
@@ -2679,12 +2724,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
2679
2724
  );
2680
2725
  } catch (err) {
2681
2726
  push("runner_request_claim_persists_request_and_consumed_comment", false, String(err?.message || err));
2682
- push("runner_stale_open_session_cleanup_excludes_bot_reply_replay", false, String(err?.message || err));
2683
- push("runner_request_lifecycle_reply_completes_without_continuation_targets", false, String(err?.message || err));
2684
- push("runner_request_lifecycle_reply_ignores_session_state_when_continuation_targets_remain", false, String(err?.message || err));
2685
- push("runner_request_claim_preserves_authoritative_source_message_envelope", false, String(err?.message || err));
2686
- push("runner_request_lifecycle_preserves_stronger_existing_source_ownership", false, String(err?.message || err));
2687
- } finally {
2727
+ push("runner_stale_open_session_cleanup_excludes_bot_reply_replay", false, String(err?.message || err));
2728
+ push("runner_request_lifecycle_reply_completes_without_continuation_targets", false, String(err?.message || err));
2729
+ push("runner_request_lifecycle_reply_ignores_session_state_when_continuation_targets_remain", false, String(err?.message || err));
2730
+ push("runner_request_lifecycle_execution_failed_closes_request", false, String(err?.message || err));
2731
+ push("runner_request_claim_preserves_authoritative_source_message_envelope", false, String(err?.message || err));
2732
+ push("runner_request_lifecycle_preserves_stronger_existing_source_ownership", false, String(err?.message || err));
2733
+ } finally {
2688
2734
  if (typeof originalRequestLedgerHome === "string") {
2689
2735
  process.env.HOME = originalRequestLedgerHome;
2690
2736
  } else {
@@ -6192,6 +6238,149 @@ export async function runSelftestRunnerScenarios(push, deps) {
6192
6238
  push("single_bot_conversational_greeting_request_skips_planner_and_worker", false, String(err?.message || err));
6193
6239
  }
6194
6240
 
6241
+ try {
6242
+ let aiCalls = 0;
6243
+ let plannerCalls = 0;
6244
+ let deliveryCalls = 0;
6245
+ let deliveredText = "";
6246
+ const processed = await processRunnerSelectedRecord({
6247
+ routeKey: "single-bot-conversation-greeting-target-prefix-key",
6248
+ normalizedRoute: normalizeRunnerRoute({
6249
+ name: "telegram-monitor-single-bot-conversation-greeting-target-prefix",
6250
+ project_id: selftestProjectID,
6251
+ provider: "telegram",
6252
+ role: "monitor",
6253
+ role_profile: "monitor",
6254
+ destination_id: "dest-1",
6255
+ destination_label: "Main Room",
6256
+ server_bot_name: "RyoAI_bot",
6257
+ server_bot_id: "bot-lead-1",
6258
+ trigger_policy: {
6259
+ mentions_only: true,
6260
+ direct_messages: true,
6261
+ reply_to_bot_messages: true,
6262
+ },
6263
+ archive_policy: {
6264
+ mirror_replies: true,
6265
+ dedupe_inbound: true,
6266
+ dedupe_outbound: true,
6267
+ skip_bot_messages: true,
6268
+ },
6269
+ dry_run_delivery: true,
6270
+ }),
6271
+ selectedRecord: {
6272
+ id: "comment-single-bot-conversation-greeting-target-prefix",
6273
+ createdAt: "2026-03-31T08:38:56.000Z",
6274
+ parsedArchive: {
6275
+ kind: "telegram_message",
6276
+ chatID: "-100123",
6277
+ chatType: "supergroup",
6278
+ senderIsBot: false,
6279
+ body: "@RyoAI_bot 당신이 @SangHoon01_bot 에게 인사해보세요.",
6280
+ mentionUsernames: ["RyoAI_bot", "SangHoon01_bot"],
6281
+ messageID: 1058,
6282
+ },
6283
+ },
6284
+ pendingOrdered: [],
6285
+ bot: {
6286
+ id: "bot-lead-1",
6287
+ name: "RyoAI_bot",
6288
+ username: "RyoAI_bot",
6289
+ role: "monitor",
6290
+ provider: "telegram",
6291
+ },
6292
+ destination: {
6293
+ id: "dest-1",
6294
+ label: "Main Room",
6295
+ provider: "telegram",
6296
+ chatID: "-100123",
6297
+ },
6298
+ archiveThread: {
6299
+ threadID: "thread-1",
6300
+ workItemID: "work-item-1",
6301
+ },
6302
+ executionPlan: {
6303
+ mode: "role_profile",
6304
+ roleProfileName: "monitor",
6305
+ roleProfile: {
6306
+ client: "sample",
6307
+ model: "",
6308
+ permissionMode: "read_only",
6309
+ reasoningEffort: "low",
6310
+ },
6311
+ workspaceDir: path.join(os.tmpdir(), "metheus-runner-selftest-single-bot-conversation-greeting-target-prefix"),
6312
+ workspaceSource: "selftest",
6313
+ usedCommandFallback: false,
6314
+ },
6315
+ runtime: {
6316
+ baseURL: "https://example.test",
6317
+ token: "selftest-token",
6318
+ timeoutSeconds: 30,
6319
+ actor: { user_id: "user-1" },
6320
+ },
6321
+ deps: {
6322
+ saveRunnerRouteState: () => {},
6323
+ startRunnerTypingHeartbeat: () => ({ async stop() {} }),
6324
+ runRunnerAIExecution: async () => {
6325
+ aiCalls += 1;
6326
+ return {
6327
+ skip: false,
6328
+ reply: "안녕하세요.",
6329
+ replyToMessageID: 0,
6330
+ };
6331
+ },
6332
+ performLocalBotDelivery: async ({ text }) => {
6333
+ deliveryCalls += 1;
6334
+ deliveredText = String(text || "").trim();
6335
+ return {
6336
+ delivery: { dryRun: true, body: {} },
6337
+ archive: {},
6338
+ };
6339
+ },
6340
+ serializeRunnerTriggerPolicy: (value) => value,
6341
+ serializeRunnerArchivePolicy: (value) => value,
6342
+ buildRunnerExecutionDeps: () => ({
6343
+ analyzeHumanConversationIntentWithAI: async () => ({
6344
+ mode: "single_bot",
6345
+ lead_bot: "ryoai_bot",
6346
+ participants: ["ryoai_bot"],
6347
+ initial_responders: ["ryoai_bot"],
6348
+ allowed_responders: ["ryoai_bot"],
6349
+ summary_bot: "",
6350
+ allow_bot_to_bot: false,
6351
+ reply_expectation: "informational",
6352
+ intent_type: "small_talk",
6353
+ }),
6354
+ planRoleExecutionWithAI: async () => {
6355
+ plannerCalls += 1;
6356
+ return {
6357
+ requiresExecution: true,
6358
+ summaryRole: "worker",
6359
+ steps: [{ role: "worker", goal: "unexpected", artifactsRequired: true }],
6360
+ };
6361
+ },
6362
+ }),
6363
+ buildRunnerDeliveryDeps: () => ({}),
6364
+ buildRunnerRuntimeDeps: () => ({}),
6365
+ resolveConversationPeerBots: () => [
6366
+ { id: "bot-lead-1", name: "RyoAI_bot" },
6367
+ { id: "bot-peer-1", name: "SangHoon01_bot" },
6368
+ ],
6369
+ },
6370
+ });
6371
+ push(
6372
+ "single_bot_direct_reply_target_prefixes_visible_target_when_ai_omits_mention",
6373
+ processed.kind === "replied"
6374
+ && aiCalls === 1
6375
+ && plannerCalls === 0
6376
+ && deliveryCalls === 1
6377
+ && deliveredText === "@sanghoon01_bot 안녕하세요.",
6378
+ `kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} planner_calls=${plannerCalls} delivery_calls=${deliveryCalls} delivered=${String(deliveredText || "(none)")}`,
6379
+ );
6380
+ } catch (err) {
6381
+ push("single_bot_direct_reply_target_prefixes_visible_target_when_ai_omits_mention", false, String(err?.message || err));
6382
+ }
6383
+
6195
6384
  try {
6196
6385
  let aiCalls = 0;
6197
6386
  let deliveryCalls = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.267",
3
+ "version": "0.2.269",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [