metheus-governance-mcp-cli 0.2.268 → 0.2.270
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.
|
@@ -212,6 +212,67 @@ function readOutputFile(filePath) {
|
|
|
212
212
|
return fs.readFileSync(filePath, "utf8");
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
export function looksLikeCodexCliSessionTranscript(rawText = "") {
|
|
216
|
+
const text = String(rawText || "").trim();
|
|
217
|
+
if (!text) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return /OpenAI Codex v|session id:|^workdir:|^model:|^provider:|^approval:|^sandbox:|^reasoning effort:|^--------|^user$|^codex$|mcp:.*\b(starting|ready)\b/i.test(text);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function summarizeCodexCliFailure({
|
|
224
|
+
status = 0,
|
|
225
|
+
stdoutText = "",
|
|
226
|
+
stderrText = "",
|
|
227
|
+
spawnError = null,
|
|
228
|
+
}) {
|
|
229
|
+
const genericMessage = status
|
|
230
|
+
? `Codex CLI exited before returning a final reply (status ${status})`
|
|
231
|
+
: "Codex CLI exited before returning a final reply";
|
|
232
|
+
if (spawnError) {
|
|
233
|
+
const spawnMessage = String(spawnError?.message || spawnError || "").trim();
|
|
234
|
+
return spawnMessage
|
|
235
|
+
? `${genericMessage}: ${spawnMessage}`
|
|
236
|
+
: genericMessage;
|
|
237
|
+
}
|
|
238
|
+
const candidateLines = `${String(stderrText || "")}\n${String(stdoutText || "")}`
|
|
239
|
+
.split(/\r?\n/)
|
|
240
|
+
.map((line) => String(line || "").trim())
|
|
241
|
+
.filter(Boolean)
|
|
242
|
+
.filter((line) => !looksLikeCodexCliSessionTranscript(line))
|
|
243
|
+
.filter((line) => /(error|failed|timeout|timed out|denied|missing|unavailable|not found|capacity)/i.test(line))
|
|
244
|
+
.slice(0, 2);
|
|
245
|
+
return candidateLines.length
|
|
246
|
+
? `${genericMessage}: ${candidateLines.join(" | ")}`
|
|
247
|
+
: genericMessage;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function resolveCodexRawTextProcessResult({
|
|
251
|
+
outputText = "",
|
|
252
|
+
stdoutText = "",
|
|
253
|
+
stderrText = "",
|
|
254
|
+
status = 0,
|
|
255
|
+
spawnError = null,
|
|
256
|
+
}) {
|
|
257
|
+
const normalizedOutputText = String(outputText || "").trim();
|
|
258
|
+
if (normalizedOutputText && !looksLikeCodexCliSessionTranscript(normalizedOutputText)) {
|
|
259
|
+
return normalizedOutputText;
|
|
260
|
+
}
|
|
261
|
+
const normalizedStdoutText = String(stdoutText || "").trim();
|
|
262
|
+
if (normalizedStdoutText && !looksLikeCodexCliSessionTranscript(normalizedStdoutText)) {
|
|
263
|
+
return normalizedStdoutText;
|
|
264
|
+
}
|
|
265
|
+
if (spawnError || status !== 0) {
|
|
266
|
+
throw new Error(summarizeCodexCliFailure({
|
|
267
|
+
status,
|
|
268
|
+
stdoutText: normalizedStdoutText,
|
|
269
|
+
stderrText,
|
|
270
|
+
spawnError,
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
throw new Error("Codex CLI did not return a usable final reply");
|
|
274
|
+
}
|
|
275
|
+
|
|
215
276
|
function tryParseEmbeddedJsonObject(text) {
|
|
216
277
|
const raw = String(text || "").trim();
|
|
217
278
|
if (!raw) return null;
|
|
@@ -686,13 +747,13 @@ function runCodexRawText({ promptText, workspaceDir, model, permissionMode, reas
|
|
|
686
747
|
maxBuffer: 8 * 1024 * 1024,
|
|
687
748
|
},
|
|
688
749
|
);
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
750
|
+
return resolveCodexRawTextProcessResult({
|
|
751
|
+
outputText: readOutputFile(outputPath),
|
|
752
|
+
stdoutText: String(result.stdout || ""),
|
|
753
|
+
stderrText: String(result.stderr || ""),
|
|
754
|
+
status: Number.isInteger(result.status) ? result.status : 0,
|
|
755
|
+
spawnError: result.error || null,
|
|
756
|
+
});
|
|
696
757
|
} finally {
|
|
697
758
|
if (fs.existsSync(outputPath)) {
|
|
698
759
|
fs.rmSync(outputPath, { force: true });
|
|
@@ -1876,6 +1937,9 @@ export function buildLocalBotPrompt(payload, { terse = true } = {}) {
|
|
|
1876
1937
|
`- Trigger hint reason: ${String(responseContract.candidate_reason || "-").trim() || "-"}`,
|
|
1877
1938
|
`- Mentioned bot usernames: ${ensureArray(responseContract.candidate_bot_usernames).length ? ensureArray(responseContract.candidate_bot_usernames).map((item) => `@${String(item || "").trim().replace(/^@+/, "")}`).join(", ") : "-"}`,
|
|
1878
1939
|
`- Reply target bot username: ${String(responseContract.reply_to_bot_username || "").trim() ? `@${String(responseContract.reply_to_bot_username || "").trim().replace(/^@+/, "")}` : "-"}`,
|
|
1940
|
+
String(responseContract.reply_to_bot_username || "").trim()
|
|
1941
|
+
? `- If you answer directly to that target bot, start the visible reply with @${String(responseContract.reply_to_bot_username || "").trim().replace(/^@+/, "")}.`
|
|
1942
|
+
: "",
|
|
1879
1943
|
"",
|
|
1880
1944
|
"Project background (context only):",
|
|
1881
1945
|
`- 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,
|
|
@@ -85,6 +85,9 @@ import {
|
|
|
85
85
|
import {
|
|
86
86
|
finalizeRunnerRequestRootReferenceRecorderState,
|
|
87
87
|
} from "./runner-recorder-request-root-reference-finalization-handoff.mjs";
|
|
88
|
+
import {
|
|
89
|
+
resolveCodexRawTextProcessResult,
|
|
90
|
+
} from "./local-ai-adapters.mjs";
|
|
88
91
|
import {
|
|
89
92
|
buildRunnerInheritedRootReferenceRecorderResult,
|
|
90
93
|
buildRunnerRootThreadRecorderFailure,
|
|
@@ -6238,6 +6241,149 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
6238
6241
|
push("single_bot_conversational_greeting_request_skips_planner_and_worker", false, String(err?.message || err));
|
|
6239
6242
|
}
|
|
6240
6243
|
|
|
6244
|
+
try {
|
|
6245
|
+
let aiCalls = 0;
|
|
6246
|
+
let plannerCalls = 0;
|
|
6247
|
+
let deliveryCalls = 0;
|
|
6248
|
+
let deliveredText = "";
|
|
6249
|
+
const processed = await processRunnerSelectedRecord({
|
|
6250
|
+
routeKey: "single-bot-conversation-greeting-target-prefix-key",
|
|
6251
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
6252
|
+
name: "telegram-monitor-single-bot-conversation-greeting-target-prefix",
|
|
6253
|
+
project_id: selftestProjectID,
|
|
6254
|
+
provider: "telegram",
|
|
6255
|
+
role: "monitor",
|
|
6256
|
+
role_profile: "monitor",
|
|
6257
|
+
destination_id: "dest-1",
|
|
6258
|
+
destination_label: "Main Room",
|
|
6259
|
+
server_bot_name: "RyoAI_bot",
|
|
6260
|
+
server_bot_id: "bot-lead-1",
|
|
6261
|
+
trigger_policy: {
|
|
6262
|
+
mentions_only: true,
|
|
6263
|
+
direct_messages: true,
|
|
6264
|
+
reply_to_bot_messages: true,
|
|
6265
|
+
},
|
|
6266
|
+
archive_policy: {
|
|
6267
|
+
mirror_replies: true,
|
|
6268
|
+
dedupe_inbound: true,
|
|
6269
|
+
dedupe_outbound: true,
|
|
6270
|
+
skip_bot_messages: true,
|
|
6271
|
+
},
|
|
6272
|
+
dry_run_delivery: true,
|
|
6273
|
+
}),
|
|
6274
|
+
selectedRecord: {
|
|
6275
|
+
id: "comment-single-bot-conversation-greeting-target-prefix",
|
|
6276
|
+
createdAt: "2026-03-31T08:38:56.000Z",
|
|
6277
|
+
parsedArchive: {
|
|
6278
|
+
kind: "telegram_message",
|
|
6279
|
+
chatID: "-100123",
|
|
6280
|
+
chatType: "supergroup",
|
|
6281
|
+
senderIsBot: false,
|
|
6282
|
+
body: "@RyoAI_bot 당신이 @SangHoon01_bot 에게 인사해보세요.",
|
|
6283
|
+
mentionUsernames: ["RyoAI_bot", "SangHoon01_bot"],
|
|
6284
|
+
messageID: 1058,
|
|
6285
|
+
},
|
|
6286
|
+
},
|
|
6287
|
+
pendingOrdered: [],
|
|
6288
|
+
bot: {
|
|
6289
|
+
id: "bot-lead-1",
|
|
6290
|
+
name: "RyoAI_bot",
|
|
6291
|
+
username: "RyoAI_bot",
|
|
6292
|
+
role: "monitor",
|
|
6293
|
+
provider: "telegram",
|
|
6294
|
+
},
|
|
6295
|
+
destination: {
|
|
6296
|
+
id: "dest-1",
|
|
6297
|
+
label: "Main Room",
|
|
6298
|
+
provider: "telegram",
|
|
6299
|
+
chatID: "-100123",
|
|
6300
|
+
},
|
|
6301
|
+
archiveThread: {
|
|
6302
|
+
threadID: "thread-1",
|
|
6303
|
+
workItemID: "work-item-1",
|
|
6304
|
+
},
|
|
6305
|
+
executionPlan: {
|
|
6306
|
+
mode: "role_profile",
|
|
6307
|
+
roleProfileName: "monitor",
|
|
6308
|
+
roleProfile: {
|
|
6309
|
+
client: "sample",
|
|
6310
|
+
model: "",
|
|
6311
|
+
permissionMode: "read_only",
|
|
6312
|
+
reasoningEffort: "low",
|
|
6313
|
+
},
|
|
6314
|
+
workspaceDir: path.join(os.tmpdir(), "metheus-runner-selftest-single-bot-conversation-greeting-target-prefix"),
|
|
6315
|
+
workspaceSource: "selftest",
|
|
6316
|
+
usedCommandFallback: false,
|
|
6317
|
+
},
|
|
6318
|
+
runtime: {
|
|
6319
|
+
baseURL: "https://example.test",
|
|
6320
|
+
token: "selftest-token",
|
|
6321
|
+
timeoutSeconds: 30,
|
|
6322
|
+
actor: { user_id: "user-1" },
|
|
6323
|
+
},
|
|
6324
|
+
deps: {
|
|
6325
|
+
saveRunnerRouteState: () => {},
|
|
6326
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
6327
|
+
runRunnerAIExecution: async () => {
|
|
6328
|
+
aiCalls += 1;
|
|
6329
|
+
return {
|
|
6330
|
+
skip: false,
|
|
6331
|
+
reply: "안녕하세요.",
|
|
6332
|
+
replyToMessageID: 0,
|
|
6333
|
+
};
|
|
6334
|
+
},
|
|
6335
|
+
performLocalBotDelivery: async ({ text }) => {
|
|
6336
|
+
deliveryCalls += 1;
|
|
6337
|
+
deliveredText = String(text || "").trim();
|
|
6338
|
+
return {
|
|
6339
|
+
delivery: { dryRun: true, body: {} },
|
|
6340
|
+
archive: {},
|
|
6341
|
+
};
|
|
6342
|
+
},
|
|
6343
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
6344
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
6345
|
+
buildRunnerExecutionDeps: () => ({
|
|
6346
|
+
analyzeHumanConversationIntentWithAI: async () => ({
|
|
6347
|
+
mode: "single_bot",
|
|
6348
|
+
lead_bot: "ryoai_bot",
|
|
6349
|
+
participants: ["ryoai_bot"],
|
|
6350
|
+
initial_responders: ["ryoai_bot"],
|
|
6351
|
+
allowed_responders: ["ryoai_bot"],
|
|
6352
|
+
summary_bot: "",
|
|
6353
|
+
allow_bot_to_bot: false,
|
|
6354
|
+
reply_expectation: "informational",
|
|
6355
|
+
intent_type: "small_talk",
|
|
6356
|
+
}),
|
|
6357
|
+
planRoleExecutionWithAI: async () => {
|
|
6358
|
+
plannerCalls += 1;
|
|
6359
|
+
return {
|
|
6360
|
+
requiresExecution: true,
|
|
6361
|
+
summaryRole: "worker",
|
|
6362
|
+
steps: [{ role: "worker", goal: "unexpected", artifactsRequired: true }],
|
|
6363
|
+
};
|
|
6364
|
+
},
|
|
6365
|
+
}),
|
|
6366
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
6367
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
6368
|
+
resolveConversationPeerBots: () => [
|
|
6369
|
+
{ id: "bot-lead-1", name: "RyoAI_bot" },
|
|
6370
|
+
{ id: "bot-peer-1", name: "SangHoon01_bot" },
|
|
6371
|
+
],
|
|
6372
|
+
},
|
|
6373
|
+
});
|
|
6374
|
+
push(
|
|
6375
|
+
"single_bot_direct_reply_target_prefixes_visible_target_when_ai_omits_mention",
|
|
6376
|
+
processed.kind === "replied"
|
|
6377
|
+
&& aiCalls === 1
|
|
6378
|
+
&& plannerCalls === 0
|
|
6379
|
+
&& deliveryCalls === 1
|
|
6380
|
+
&& deliveredText === "@sanghoon01_bot 안녕하세요.",
|
|
6381
|
+
`kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} planner_calls=${plannerCalls} delivery_calls=${deliveryCalls} delivered=${String(deliveredText || "(none)")}`,
|
|
6382
|
+
);
|
|
6383
|
+
} catch (err) {
|
|
6384
|
+
push("single_bot_direct_reply_target_prefixes_visible_target_when_ai_omits_mention", false, String(err?.message || err));
|
|
6385
|
+
}
|
|
6386
|
+
|
|
6241
6387
|
try {
|
|
6242
6388
|
let aiCalls = 0;
|
|
6243
6389
|
let deliveryCalls = 0;
|
|
@@ -18558,6 +18704,48 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
18558
18704
|
push("runner_selected_record_terminal_outcome_stays_within_module_4_boundary", false, String(err?.message || err));
|
|
18559
18705
|
}
|
|
18560
18706
|
|
|
18707
|
+
try {
|
|
18708
|
+
const replyText = resolveCodexRawTextProcessResult({
|
|
18709
|
+
outputText: "{\"reply\":\"하이\"}",
|
|
18710
|
+
stdoutText: "",
|
|
18711
|
+
stderrText: "OpenAI Codex v0.114.0\nsession id: test-session\nmcp: metheus ready",
|
|
18712
|
+
status: 1,
|
|
18713
|
+
spawnError: null,
|
|
18714
|
+
});
|
|
18715
|
+
push(
|
|
18716
|
+
"codex_nonzero_exit_uses_final_output_file_when_available",
|
|
18717
|
+
String(replyText || "").trim() === "{\"reply\":\"하이\"}",
|
|
18718
|
+
`reply=${String(replyText || "(none)")}`,
|
|
18719
|
+
);
|
|
18720
|
+
} catch (err) {
|
|
18721
|
+
push("codex_nonzero_exit_uses_final_output_file_when_available", false, String(err?.message || err));
|
|
18722
|
+
}
|
|
18723
|
+
|
|
18724
|
+
try {
|
|
18725
|
+
let errorMessage = "";
|
|
18726
|
+
try {
|
|
18727
|
+
resolveCodexRawTextProcessResult({
|
|
18728
|
+
outputText: "",
|
|
18729
|
+
stdoutText: "",
|
|
18730
|
+
stderrText: "OpenAI Codex v0.114.0\nworkdir: C:\\LUSH KOREA CLI\nsession id: test-session\nmcp: metheus starting\nmcp: metheus ready",
|
|
18731
|
+
status: 1,
|
|
18732
|
+
spawnError: null,
|
|
18733
|
+
});
|
|
18734
|
+
} catch (err) {
|
|
18735
|
+
errorMessage = String(err?.message || err);
|
|
18736
|
+
}
|
|
18737
|
+
push(
|
|
18738
|
+
"codex_failure_summary_does_not_leak_session_transcript",
|
|
18739
|
+
/Codex CLI exited before returning a final reply/i.test(errorMessage)
|
|
18740
|
+
&& !/OpenAI Codex v/i.test(errorMessage)
|
|
18741
|
+
&& !/session id:/i.test(errorMessage)
|
|
18742
|
+
&& !/mcp:/i.test(errorMessage),
|
|
18743
|
+
errorMessage || "(no error message)",
|
|
18744
|
+
);
|
|
18745
|
+
} catch (err) {
|
|
18746
|
+
push("codex_failure_summary_does_not_leak_session_transcript", false, String(err?.message || err));
|
|
18747
|
+
}
|
|
18748
|
+
|
|
18561
18749
|
try {
|
|
18562
18750
|
const prepared = await prepareRunnerSelectedRecordContractContext({
|
|
18563
18751
|
selectedRecord: {
|