metheus-governance-mcp-cli 0.2.268 → 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.
|
@@ -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
|
|
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:
|
|
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,
|
|
@@ -6238,6 +6238,149 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
6238
6238
|
push("single_bot_conversational_greeting_request_skips_planner_and_worker", false, String(err?.message || err));
|
|
6239
6239
|
}
|
|
6240
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
|
+
|
|
6241
6384
|
try {
|
|
6242
6385
|
let aiCalls = 0;
|
|
6243
6386
|
let deliveryCalls = 0;
|