metheus-governance-mcp-cli 0.2.294 → 0.2.296
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 +35 -12
- package/lib/runner-orchestration-entrypoints.mjs +338 -10
- package/lib/selftest-runner-scenarios.mjs +419 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -10632,10 +10632,10 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
10632
10632
|
latestRunnerState.consumedComments || latestRunnerState.consumed_comments,
|
|
10633
10633
|
);
|
|
10634
10634
|
const requests = normalizeBotRunnerRequests(latestRunnerState.requests);
|
|
10635
|
-
const commentsForPending = ensureArray(comments).filter((comment) => {
|
|
10636
|
-
const normalizedComment = normalizeArchiveCommentRecord(comment, parseArchivedChatComment);
|
|
10637
|
-
const commentID = String(normalizedComment.id || "").trim();
|
|
10638
|
-
const parsed = safeObject(normalizedComment.parsedArchive);
|
|
10635
|
+
const commentsForPending = ensureArray(comments).filter((comment) => {
|
|
10636
|
+
const normalizedComment = normalizeArchiveCommentRecord(comment, parseArchivedChatComment);
|
|
10637
|
+
const commentID = String(normalizedComment.id || "").trim();
|
|
10638
|
+
const parsed = safeObject(normalizedComment.parsedArchive);
|
|
10639
10639
|
const commentKind = String(parsed.kind || "").trim().toLowerCase();
|
|
10640
10640
|
if (commentID && safeObject(excludedComments)[commentID]) {
|
|
10641
10641
|
return false;
|
|
@@ -10663,22 +10663,45 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
10663
10663
|
}
|
|
10664
10664
|
const sessionMatch = findScopedConversationSessionState(latestRunnerState, normalizedRoute, conversationID);
|
|
10665
10665
|
return sessionAllowsConversationResponder(sessionMatch.session, currentBotSelector);
|
|
10666
|
-
}
|
|
10667
|
-
return true;
|
|
10668
|
-
});
|
|
10666
|
+
}
|
|
10667
|
+
return true;
|
|
10668
|
+
});
|
|
10669
|
+
const followupRequestsForPending = Object.values(requests).filter((entryRaw) => {
|
|
10670
|
+
const entry = safeObject(entryRaw);
|
|
10671
|
+
const nextExpectedResponders = ensureArray(entry.next_expected_responders)
|
|
10672
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
10673
|
+
.filter(Boolean);
|
|
10674
|
+
if (!nextExpectedResponders.includes(currentBotSelector)) {
|
|
10675
|
+
return false;
|
|
10676
|
+
}
|
|
10677
|
+
if (!isActiveRunnerRequestStatus(entry.status)) {
|
|
10678
|
+
return false;
|
|
10679
|
+
}
|
|
10680
|
+
if (
|
|
10681
|
+
String(entry.project_id || "").trim() !== String(normalizedRoute.projectID || "").trim()
|
|
10682
|
+
|| String(entry.provider || "").trim() !== String(normalizedRoute.provider || "").trim()
|
|
10683
|
+
|| String(entry.chat_id || "").trim() !== String(destination.chatID || "").trim()
|
|
10684
|
+
) {
|
|
10685
|
+
return false;
|
|
10686
|
+
}
|
|
10687
|
+
const lastReplyEnvelope = safeObject(entry.last_reply_message_envelope);
|
|
10688
|
+
return intFromRawAllowZero(lastReplyEnvelope.message_id || lastReplyEnvelope.messageID, 0) > 0;
|
|
10689
|
+
});
|
|
10669
10690
|
const pendingWork = selectRunnerPendingWork({
|
|
10670
10691
|
comments: commentsForPending,
|
|
10671
10692
|
importOutcome,
|
|
10672
10693
|
refreshedState,
|
|
10673
|
-
mode,
|
|
10694
|
+
mode,
|
|
10674
10695
|
parseArchivedChatComment,
|
|
10675
10696
|
pendingSelectionOptions: {
|
|
10676
10697
|
maxPendingAgeMs: BOT_RUNNER_PENDING_COMMENT_MAX_AGE_MS,
|
|
10677
10698
|
},
|
|
10678
|
-
deps: {
|
|
10679
|
-
applyPendingAgeSelection,
|
|
10680
|
-
normalizeArchiveCommentRecord,
|
|
10681
|
-
},
|
|
10699
|
+
deps: {
|
|
10700
|
+
applyPendingAgeSelection,
|
|
10701
|
+
normalizeArchiveCommentRecord,
|
|
10702
|
+
},
|
|
10703
|
+
followupRequests: followupRequestsForPending,
|
|
10704
|
+
currentBotSelector,
|
|
10682
10705
|
});
|
|
10683
10706
|
const pending = pendingWork.pending;
|
|
10684
10707
|
if (pending.staleSkippedLatest) {
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
dedupeProcessableArchiveComments,
|
|
6
6
|
isArchiveRecordAfterState,
|
|
7
7
|
isInboundArchiveKind,
|
|
8
|
+
normalizeTelegramMessageEnvelope,
|
|
8
9
|
selectPendingArchiveComments,
|
|
9
10
|
} from "./runner-helpers.mjs";
|
|
10
11
|
import {
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
} from "./runner-trigger.mjs";
|
|
14
15
|
import {
|
|
15
16
|
buildRunnerLocalInboundReceiptKey,
|
|
17
|
+
normalizeRunnerRecentLocalInboundReceiptMap,
|
|
16
18
|
} from "./runner-local-inbound-receipts.mjs";
|
|
17
19
|
|
|
18
20
|
function safeObject(value) {
|
|
@@ -130,6 +132,101 @@ function buildRunnerReceiptReplaySortTime(receiptRaw) {
|
|
|
130
132
|
return Number.isFinite(receiptTime) ? receiptTime : 0;
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
function extractReceiptMentionSelectors(text) {
|
|
136
|
+
const matches = Array.from(String(text || "").matchAll(/@([A-Za-z0-9_]+)/g));
|
|
137
|
+
return Array.from(new Set(matches.map((match) => normalizeMentionSelector(match[1] || "")).filter(Boolean)));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function buildRunnerReceiptReplayRouteState(routeStateRaw) {
|
|
141
|
+
const routeState = safeObject(routeStateRaw);
|
|
142
|
+
return {
|
|
143
|
+
...routeState,
|
|
144
|
+
last_processed_comment_id: String(routeState.active_comment_id || "").trim()
|
|
145
|
+
|| String(routeState.last_processed_comment_id || "").trim(),
|
|
146
|
+
last_processed_created_at: String(routeState.active_comment_created_at || "").trim()
|
|
147
|
+
|| String(routeState.last_processed_created_at || "").trim(),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function buildRunnerReceiptBackedSyntheticRecord(receiptRaw, receiptKey) {
|
|
152
|
+
const receipt = safeObject(receiptRaw);
|
|
153
|
+
const chatID = String(receipt.chat_id || "").trim();
|
|
154
|
+
const messageID = intFromRawAllowZero(receipt.message_id, 0);
|
|
155
|
+
const occurredAt = firstNonEmptyString([
|
|
156
|
+
receipt.occurred_at,
|
|
157
|
+
receipt.occurredAt,
|
|
158
|
+
receipt.received_at,
|
|
159
|
+
receipt.receivedAt,
|
|
160
|
+
]);
|
|
161
|
+
if (!chatID || !(messageID > 0) || !occurredAt) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
const body = String(receipt.body || receipt.text || "").trim();
|
|
165
|
+
const parsedArchive = {
|
|
166
|
+
kind: firstNonEmptyString([receipt.kind, "telegram_message"]),
|
|
167
|
+
chatID,
|
|
168
|
+
messageID,
|
|
169
|
+
sender: String(receipt.sender || "").trim(),
|
|
170
|
+
senderID: String(receipt.sender_id || "").trim(),
|
|
171
|
+
senderIsBot: receipt.sender_is_bot === true,
|
|
172
|
+
username: String(receipt.sender_username || "").trim(),
|
|
173
|
+
mentionUsernames: extractReceiptMentionSelectors(body),
|
|
174
|
+
occurredAt,
|
|
175
|
+
canonicalHumanMessageKey: String(receipt.canonical_human_message_key || "").trim(),
|
|
176
|
+
sourceOrigin: firstNonEmptyString([receipt.receipt_origin, "local_telegram_inbound"]).toLowerCase(),
|
|
177
|
+
sourceRouteKey: String(receipt.receipt_route_key || "").trim(),
|
|
178
|
+
sourceBotUsername: normalizeMentionSelector(receipt.receipt_bot_username || ""),
|
|
179
|
+
body,
|
|
180
|
+
};
|
|
181
|
+
const messageThreadID = intFromRawAllowZero(receipt.message_thread_id, 0);
|
|
182
|
+
if (messageThreadID > 0) {
|
|
183
|
+
parsedArchive.messageThreadID = messageThreadID;
|
|
184
|
+
}
|
|
185
|
+
const replyToMessageID = intFromRawAllowZero(receipt.reply_to_message_id, 0);
|
|
186
|
+
if (replyToMessageID > 0) {
|
|
187
|
+
parsedArchive.replyToMessageID = replyToMessageID;
|
|
188
|
+
}
|
|
189
|
+
const replyToUsername = String(receipt.reply_to_from_username || "").trim();
|
|
190
|
+
if (replyToUsername) {
|
|
191
|
+
parsedArchive.replyToUsername = replyToUsername;
|
|
192
|
+
}
|
|
193
|
+
if (receipt.reply_to_from_is_bot === true) {
|
|
194
|
+
parsedArchive.replyToSenderIsBot = true;
|
|
195
|
+
}
|
|
196
|
+
const chatType = String(receipt.chat_type || "").trim();
|
|
197
|
+
if (chatType) {
|
|
198
|
+
parsedArchive.chatType = chatType;
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
id: `receipt-replay:${receiptKey}`,
|
|
202
|
+
body,
|
|
203
|
+
createdAt: occurredAt,
|
|
204
|
+
updatedAt: occurredAt,
|
|
205
|
+
sourceOccurredAt: occurredAt,
|
|
206
|
+
parsedArchive,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function findRunnerReceiptArchiveCollisionRecord(orderedComments, receiptRaw, expectedReceiptKey = "") {
|
|
211
|
+
const receipt = safeObject(receiptRaw);
|
|
212
|
+
const chatID = String(receipt.chat_id || "").trim();
|
|
213
|
+
const messageID = intFromRawAllowZero(receipt.message_id, 0);
|
|
214
|
+
if (!chatID || !(messageID > 0)) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
return ensureArray(orderedComments).find((recordRaw) => {
|
|
218
|
+
const record = safeObject(recordRaw);
|
|
219
|
+
const parsed = safeObject(record.parsedArchive);
|
|
220
|
+
if (
|
|
221
|
+
firstNonEmptyString([parsed.chatID, parsed.chatId]) !== chatID
|
|
222
|
+
|| intFromRawAllowZero(parsed.messageID, 0) !== messageID
|
|
223
|
+
) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
return buildRunnerArchiveSourceMessageKey(record) !== String(expectedReceiptKey || "").trim();
|
|
227
|
+
}) || null;
|
|
228
|
+
}
|
|
229
|
+
|
|
133
230
|
function buildRunnerReceiptBackedReplayRecord(recordRaw, receiptRaw, pendingSelectionOptions = {}) {
|
|
134
231
|
const record = safeObject(recordRaw);
|
|
135
232
|
const receipt = safeObject(receiptRaw);
|
|
@@ -161,33 +258,75 @@ function buildRunnerReceiptBackedReplayRecord(recordRaw, receiptRaw, pendingSele
|
|
|
161
258
|
function buildRunnerReceiptBackedPendingArchiveComments({
|
|
162
259
|
orderedComments,
|
|
163
260
|
currentPollLocalInboundReceipts,
|
|
261
|
+
routeStateLocalInboundReceipts,
|
|
262
|
+
routeState,
|
|
164
263
|
existingPendingIDs,
|
|
165
264
|
pendingSelectionOptions,
|
|
166
265
|
}) {
|
|
167
266
|
const pendingIDs = new Set(ensureArray(existingPendingIDs).map((value) => String(value || "").trim()).filter(Boolean));
|
|
168
267
|
const latestReceiptsByKey = new Map();
|
|
169
|
-
|
|
268
|
+
const registerReceipt = (receiptRaw, source) => {
|
|
170
269
|
const receipt = safeObject(receiptRaw);
|
|
171
270
|
const receiptKey = buildRunnerLocalInboundReceiptKey(receipt, {
|
|
172
271
|
resolveTargetSelector: resolveRunnerLocalInboundReceiptTargetSelector,
|
|
173
272
|
});
|
|
174
273
|
if (!receiptKey) {
|
|
175
|
-
|
|
274
|
+
return;
|
|
176
275
|
}
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
276
|
+
const previousEntry = safeObject(latestReceiptsByKey.get(receiptKey));
|
|
277
|
+
const previousReceipt = safeObject(previousEntry.receipt);
|
|
278
|
+
const previousSource = String(previousEntry.source || "").trim();
|
|
279
|
+
const nextSortTime = buildRunnerReceiptReplaySortTime(receipt);
|
|
280
|
+
const previousSortTime = buildRunnerReceiptReplaySortTime(previousReceipt);
|
|
281
|
+
if (
|
|
282
|
+
nextSortTime > previousSortTime
|
|
283
|
+
|| (
|
|
284
|
+
nextSortTime === previousSortTime
|
|
285
|
+
&& source === "current_poll"
|
|
286
|
+
&& previousSource !== "current_poll"
|
|
287
|
+
)
|
|
288
|
+
) {
|
|
289
|
+
latestReceiptsByKey.set(receiptKey, { receipt, source });
|
|
180
290
|
}
|
|
291
|
+
};
|
|
292
|
+
for (const receiptRaw of ensureArray(currentPollLocalInboundReceipts)) {
|
|
293
|
+
registerReceipt(receiptRaw, "current_poll");
|
|
294
|
+
}
|
|
295
|
+
for (const receiptRaw of Object.values(normalizeRunnerRecentLocalInboundReceiptMap(routeStateLocalInboundReceipts))) {
|
|
296
|
+
registerReceipt(receiptRaw, "route_state");
|
|
181
297
|
}
|
|
298
|
+
const replayRouteState = buildRunnerReceiptReplayRouteState(routeState);
|
|
182
299
|
const replayCandidates = [];
|
|
183
|
-
for (const [receiptKey,
|
|
300
|
+
for (const [receiptKey, receiptEntryRaw] of latestReceiptsByKey.entries()) {
|
|
301
|
+
const receiptEntry = safeObject(receiptEntryRaw);
|
|
302
|
+
const receipt = safeObject(receiptEntry.receipt);
|
|
303
|
+
const receiptSource = String(receiptEntry.source || "").trim();
|
|
184
304
|
const matchedRecord = orderedComments.find((record) => buildRunnerArchiveSourceMessageKey(record) === receiptKey);
|
|
185
|
-
const
|
|
305
|
+
const collisionRecord = matchedRecord
|
|
306
|
+
? null
|
|
307
|
+
: findRunnerReceiptArchiveCollisionRecord(orderedComments, receipt, receiptKey);
|
|
308
|
+
const selectedRecord = matchedRecord || buildRunnerReceiptBackedSyntheticRecord(receipt, receiptKey);
|
|
309
|
+
if (
|
|
310
|
+
collisionRecord
|
|
311
|
+
|| !selectedRecord
|
|
312
|
+
|| (
|
|
313
|
+
!matchedRecord
|
|
314
|
+
&& !String(safeObject(selectedRecord.parsedArchive).canonicalHumanMessageKey || "").trim()
|
|
315
|
+
&& !String(safeObject(selectedRecord.parsedArchive).canonical_human_message_key || "").trim()
|
|
316
|
+
)
|
|
317
|
+
|| (
|
|
318
|
+
receiptSource !== "current_poll"
|
|
319
|
+
&& !isArchiveRecordAfterState(selectedRecord, replayRouteState)
|
|
320
|
+
)
|
|
321
|
+
) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const matchedID = String(safeObject(selectedRecord).id || "").trim();
|
|
186
325
|
if (!matchedID || pendingIDs.has(matchedID)) {
|
|
187
326
|
continue;
|
|
188
327
|
}
|
|
189
328
|
replayCandidates.push(buildRunnerReceiptBackedReplayRecord(
|
|
190
|
-
|
|
329
|
+
selectedRecord,
|
|
191
330
|
receipt,
|
|
192
331
|
pendingSelectionOptions,
|
|
193
332
|
));
|
|
@@ -195,6 +334,175 @@ function buildRunnerReceiptBackedPendingArchiveComments({
|
|
|
195
334
|
return replayCandidates.sort(compareArchiveCommentRecords);
|
|
196
335
|
}
|
|
197
336
|
|
|
337
|
+
function buildRunnerRequestFollowupSyntheticRecord(requestRaw, currentBotSelector, pendingSelectionOptions = {}) {
|
|
338
|
+
const request = safeObject(requestRaw);
|
|
339
|
+
const replyEnvelope = safeObject(normalizeTelegramMessageEnvelope(request.last_reply_message_envelope));
|
|
340
|
+
const chatID = String(request.chat_id || replyEnvelope.chat_id || "").trim();
|
|
341
|
+
const messageID = intFromRawAllowZero(replyEnvelope.message_id, 0);
|
|
342
|
+
const occurredAt = firstNonEmptyString([
|
|
343
|
+
replyEnvelope.occurred_at,
|
|
344
|
+
request.updated_at,
|
|
345
|
+
request.claimed_at,
|
|
346
|
+
request.created_at,
|
|
347
|
+
]);
|
|
348
|
+
if (!chatID || !(messageID > 0) || !occurredAt) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
const normalizedCurrentBotSelector = normalizeMentionSelector(currentBotSelector);
|
|
352
|
+
const senderSelector = normalizeMentionSelector(
|
|
353
|
+
replyEnvelope.sender_username
|
|
354
|
+
|| replyEnvelope.bot_username
|
|
355
|
+
|| replyEnvelope.username
|
|
356
|
+
|| replyEnvelope.sender,
|
|
357
|
+
);
|
|
358
|
+
if (normalizedCurrentBotSelector && senderSelector && normalizedCurrentBotSelector === senderSelector) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
const participants = Array.from(new Set(
|
|
362
|
+
ensureArray(request.conversation_participants)
|
|
363
|
+
.map((value) => normalizeMentionSelector(value))
|
|
364
|
+
.filter(Boolean),
|
|
365
|
+
));
|
|
366
|
+
const initialResponders = Array.from(new Set(
|
|
367
|
+
ensureArray(request.conversation_initial_responders)
|
|
368
|
+
.map((value) => normalizeMentionSelector(value))
|
|
369
|
+
.filter(Boolean),
|
|
370
|
+
));
|
|
371
|
+
const allowedResponders = Array.from(new Set(
|
|
372
|
+
ensureArray(request.conversation_allowed_responders)
|
|
373
|
+
.map((value) => normalizeMentionSelector(value))
|
|
374
|
+
.filter(Boolean),
|
|
375
|
+
));
|
|
376
|
+
const nextResponders = Array.from(new Set(
|
|
377
|
+
ensureArray(request.next_expected_responders)
|
|
378
|
+
.map((value) => normalizeMentionSelector(value))
|
|
379
|
+
.filter(Boolean),
|
|
380
|
+
));
|
|
381
|
+
const executionTargets = Array.from(new Set(
|
|
382
|
+
ensureArray(request.execution_contract_targets)
|
|
383
|
+
.map((value) => normalizeMentionSelector(value))
|
|
384
|
+
.filter(Boolean),
|
|
385
|
+
));
|
|
386
|
+
const normalizedExecutionContractType = String(
|
|
387
|
+
request.execution_contract_type || request.normalized_execution_contract_type || "",
|
|
388
|
+
).trim().toLowerCase() || "direct_result";
|
|
389
|
+
const normalizedSummaryBotSelector = normalizeMentionSelector(request.conversation_summary_bot);
|
|
390
|
+
const executionContract = {
|
|
391
|
+
type: normalizedExecutionContractType,
|
|
392
|
+
actionable: request.execution_contract_actionable === true,
|
|
393
|
+
assignments: executionTargets.map((targetBot) => ({
|
|
394
|
+
target_bot: targetBot,
|
|
395
|
+
})),
|
|
396
|
+
...(normalizedSummaryBotSelector ? { summary_bot: normalizedSummaryBotSelector } : {}),
|
|
397
|
+
...(nextResponders.length > 0 ? { next_responders: nextResponders } : {}),
|
|
398
|
+
};
|
|
399
|
+
const replyOccurredAtMs = Date.parse(occurredAt);
|
|
400
|
+
const maxPendingAgeMs = intFromRawAllowZero(safeObject(pendingSelectionOptions).maxPendingAgeMs, 0);
|
|
401
|
+
const staleAfterAt = maxPendingAgeMs > 0 && Number.isFinite(replyOccurredAtMs)
|
|
402
|
+
? new Date(replyOccurredAtMs + maxPendingAgeMs).toISOString()
|
|
403
|
+
: "";
|
|
404
|
+
const syntheticID = `request-followup:${String(request.request_key || "").trim()}:${messageID}:${normalizedCurrentBotSelector || "route"}`;
|
|
405
|
+
return {
|
|
406
|
+
id: syntheticID,
|
|
407
|
+
body: String(replyEnvelope.body || "").trim(),
|
|
408
|
+
createdAt: occurredAt,
|
|
409
|
+
updatedAt: occurredAt,
|
|
410
|
+
sourceOccurredAt: occurredAt,
|
|
411
|
+
...(staleAfterAt ? { staleAfterAt } : {}),
|
|
412
|
+
replayTriggeredByRequestFollowup: true,
|
|
413
|
+
parsedArchive: {
|
|
414
|
+
kind: "bot_reply",
|
|
415
|
+
chatID,
|
|
416
|
+
messageID,
|
|
417
|
+
...(intFromRawAllowZero(replyEnvelope.message_thread_id, 0) > 0
|
|
418
|
+
? { messageThreadID: intFromRawAllowZero(replyEnvelope.message_thread_id, 0) }
|
|
419
|
+
: {}),
|
|
420
|
+
...(intFromRawAllowZero(replyEnvelope.reply_to_message_id, 0) > 0
|
|
421
|
+
? { replyToMessageID: intFromRawAllowZero(replyEnvelope.reply_to_message_id, 0) }
|
|
422
|
+
: {}),
|
|
423
|
+
sender: String(replyEnvelope.sender || (senderSelector ? `@${senderSelector}` : "@bot")).trim(),
|
|
424
|
+
senderID: String(replyEnvelope.sender_id || "").trim(),
|
|
425
|
+
senderIsBot: true,
|
|
426
|
+
username: senderSelector,
|
|
427
|
+
botUsername: senderSelector,
|
|
428
|
+
mentionUsernames: uniqueOrdered([
|
|
429
|
+
...ensureArray(replyEnvelope.mention_usernames)
|
|
430
|
+
.map((value) => normalizeMentionSelector(value))
|
|
431
|
+
.filter(Boolean),
|
|
432
|
+
...nextResponders,
|
|
433
|
+
]),
|
|
434
|
+
occurredAt,
|
|
435
|
+
body: String(replyEnvelope.body || "").trim(),
|
|
436
|
+
conversationID: String(request.conversation_id || "").trim(),
|
|
437
|
+
conversationMode: "public_multi_bot",
|
|
438
|
+
conversationStage: "bot_reply",
|
|
439
|
+
conversationIntentMode: String(request.conversation_intent_mode || "").trim(),
|
|
440
|
+
conversationAllowBotToBot: request.conversation_allow_bot_to_bot === true,
|
|
441
|
+
conversationLeadBotUsername: normalizeMentionSelector(request.conversation_lead_bot),
|
|
442
|
+
conversationSummaryBotUsername: normalizedSummaryBotSelector,
|
|
443
|
+
conversationTargetBotUsername: nextResponders.length === 1 ? nextResponders[0] : "",
|
|
444
|
+
conversationParticipants: participants,
|
|
445
|
+
conversationInitialResponders: initialResponders,
|
|
446
|
+
conversationAllowedResponders: allowedResponders,
|
|
447
|
+
conversationReplyExpectation: String(request.conversation_reply_expectation || "").trim().toLowerCase(),
|
|
448
|
+
executionContract,
|
|
449
|
+
executionContractType: normalizedExecutionContractType,
|
|
450
|
+
executionContractActionable: request.execution_contract_actionable === true,
|
|
451
|
+
executionContractAssignments: executionTargets.map((targetBot) => ({
|
|
452
|
+
targetBot,
|
|
453
|
+
})),
|
|
454
|
+
executionContractSummaryBot: normalizedSummaryBotSelector,
|
|
455
|
+
executionContractNextResponders: nextResponders,
|
|
456
|
+
sourceOrigin: "request_followup_replay",
|
|
457
|
+
sourceRouteKey: String(request.claimed_by_route || "").trim(),
|
|
458
|
+
sourceBotUsername: senderSelector,
|
|
459
|
+
canonicalHumanMessageKey: String(request.canonical_human_message_key || "").trim(),
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function buildRunnerRequestFollowupPendingArchiveComments({
|
|
465
|
+
orderedComments,
|
|
466
|
+
routeState,
|
|
467
|
+
existingPendingIDs,
|
|
468
|
+
followupRequests,
|
|
469
|
+
currentBotSelector,
|
|
470
|
+
pendingSelectionOptions,
|
|
471
|
+
}) {
|
|
472
|
+
const pendingIDs = new Set(ensureArray(existingPendingIDs).map((value) => String(value || "").trim()).filter(Boolean));
|
|
473
|
+
const replayCandidates = [];
|
|
474
|
+
for (const requestRaw of ensureArray(followupRequests)) {
|
|
475
|
+
const request = safeObject(requestRaw);
|
|
476
|
+
const syntheticRecord = buildRunnerRequestFollowupSyntheticRecord(
|
|
477
|
+
request,
|
|
478
|
+
currentBotSelector,
|
|
479
|
+
pendingSelectionOptions,
|
|
480
|
+
);
|
|
481
|
+
if (!syntheticRecord) {
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (pendingIDs.has(String(syntheticRecord.id || "").trim())) {
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
const parsedSynthetic = safeObject(syntheticRecord.parsedArchive);
|
|
488
|
+
const archiveCollision = ensureArray(orderedComments).some((recordRaw) => {
|
|
489
|
+
const parsed = safeObject(safeObject(recordRaw).parsedArchive);
|
|
490
|
+
return String(parsed.kind || "").trim().toLowerCase() === "bot_reply"
|
|
491
|
+
&& String(parsed.chatID || parsed.chatId || "").trim() === String(parsedSynthetic.chatID || "").trim()
|
|
492
|
+
&& intFromRawAllowZero(parsed.messageID || parsed.messageId, 0) === intFromRawAllowZero(parsedSynthetic.messageID, 0);
|
|
493
|
+
});
|
|
494
|
+
if (archiveCollision) {
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (!isArchiveRecordAfterState(syntheticRecord, routeState)) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
pendingIDs.add(String(syntheticRecord.id || "").trim());
|
|
501
|
+
replayCandidates.push(syntheticRecord);
|
|
502
|
+
}
|
|
503
|
+
return replayCandidates.sort(compareArchiveCommentRecords);
|
|
504
|
+
}
|
|
505
|
+
|
|
198
506
|
function buildContextSpeakerType(parsedArchiveRaw) {
|
|
199
507
|
const parsed = safeObject(parsedArchiveRaw);
|
|
200
508
|
const kind = String(parsed.kind || "").trim();
|
|
@@ -944,6 +1252,8 @@ export function selectRunnerPendingWork({
|
|
|
944
1252
|
parseArchivedChatComment,
|
|
945
1253
|
deps,
|
|
946
1254
|
pendingSelectionOptions,
|
|
1255
|
+
followupRequests = [],
|
|
1256
|
+
currentBotSelector = "",
|
|
947
1257
|
}) {
|
|
948
1258
|
const normalizeArchiveCommentRecord = requireDependency(deps, "normalizeArchiveCommentRecord");
|
|
949
1259
|
const applyPendingAgeSelection = requireDependency(deps, "applyPendingAgeSelection");
|
|
@@ -1007,14 +1317,31 @@ export function selectRunnerPendingWork({
|
|
|
1007
1317
|
const receiptBackedPending = buildRunnerReceiptBackedPendingArchiveComments({
|
|
1008
1318
|
orderedComments,
|
|
1009
1319
|
currentPollLocalInboundReceipts: ensureArray(importOutcome?.currentPollLocalInboundReceipts),
|
|
1320
|
+
routeStateLocalInboundReceipts: safeObject(refreshedState).recent_local_inbound_receipts,
|
|
1321
|
+
routeState: refreshedState,
|
|
1010
1322
|
existingPendingIDs: ensureArray(safeObject(pending).pending).map((record) => String(safeObject(record).id || "").trim()),
|
|
1011
1323
|
pendingSelectionOptions,
|
|
1012
1324
|
});
|
|
1013
|
-
const
|
|
1325
|
+
const requestFollowupPending = buildRunnerRequestFollowupPendingArchiveComments({
|
|
1326
|
+
orderedComments,
|
|
1327
|
+
routeState: refreshedState,
|
|
1328
|
+
existingPendingIDs: [
|
|
1329
|
+
...ensureArray(safeObject(pending).pending).map((record) => String(safeObject(record).id || "").trim()),
|
|
1330
|
+
...ensureArray(receiptBackedPending).map((record) => String(safeObject(record).id || "").trim()),
|
|
1331
|
+
],
|
|
1332
|
+
followupRequests,
|
|
1333
|
+
currentBotSelector,
|
|
1334
|
+
pendingSelectionOptions,
|
|
1335
|
+
});
|
|
1336
|
+
const replayFallbackPending = [
|
|
1337
|
+
...ensureArray(receiptBackedPending),
|
|
1338
|
+
...ensureArray(requestFollowupPending),
|
|
1339
|
+
].sort(compareArchiveCommentRecords);
|
|
1340
|
+
const finalPending = ensureArray(safeObject(pending).pending).length === 0 && replayFallbackPending.length > 0
|
|
1014
1341
|
? applyPendingAgeSelection({
|
|
1015
1342
|
...safeObject(pending),
|
|
1016
1343
|
shouldPrime: false,
|
|
1017
|
-
pending:
|
|
1344
|
+
pending: replayFallbackPending,
|
|
1018
1345
|
}, pendingSelectionOptions)
|
|
1019
1346
|
: pending;
|
|
1020
1347
|
return {
|
|
@@ -1022,6 +1349,7 @@ export function selectRunnerPendingWork({
|
|
|
1022
1349
|
inboundComments,
|
|
1023
1350
|
importedRecords,
|
|
1024
1351
|
receiptBackedPending,
|
|
1352
|
+
requestFollowupPending,
|
|
1025
1353
|
pending: finalPending,
|
|
1026
1354
|
};
|
|
1027
1355
|
}
|
|
@@ -22062,6 +22062,425 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
22062
22062
|
push("runner_entrypoint_receipt_replay_skips_foreign_bot_archive_collision", false, String(err?.message || err));
|
|
22063
22063
|
}
|
|
22064
22064
|
|
|
22065
|
+
try {
|
|
22066
|
+
const pendingWork = selectRunnerPendingWorkEntrypoint({
|
|
22067
|
+
comments: [
|
|
22068
|
+
{
|
|
22069
|
+
id: "comment-earlier-cursor-1",
|
|
22070
|
+
createdAt: "2026-04-06T00:00:00.000Z",
|
|
22071
|
+
updatedAt: "2026-04-06T00:00:00.000Z",
|
|
22072
|
+
body: "@RyoAI_bot earlier",
|
|
22073
|
+
parsedArchive: {
|
|
22074
|
+
kind: "telegram_message",
|
|
22075
|
+
messageID: 485,
|
|
22076
|
+
chatID: "-100999",
|
|
22077
|
+
chatType: "supergroup",
|
|
22078
|
+
sender: "tester",
|
|
22079
|
+
senderIsBot: false,
|
|
22080
|
+
body: "@RyoAI_bot earlier",
|
|
22081
|
+
},
|
|
22082
|
+
},
|
|
22083
|
+
],
|
|
22084
|
+
importOutcome: {
|
|
22085
|
+
importedCommentIDs: [],
|
|
22086
|
+
importedComments: [],
|
|
22087
|
+
currentPollLocalInboundReceipts: [],
|
|
22088
|
+
},
|
|
22089
|
+
refreshedState: {
|
|
22090
|
+
last_processed_comment_id: "comment-earlier-cursor-1",
|
|
22091
|
+
last_processed_created_at: "2026-04-06T00:00:00.000Z",
|
|
22092
|
+
recent_local_inbound_receipts: {
|
|
22093
|
+
"-100999:486": {
|
|
22094
|
+
chat_id: "-100999",
|
|
22095
|
+
message_id: 486,
|
|
22096
|
+
body: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 하이",
|
|
22097
|
+
receipt_bot_username: "ryoai_bot",
|
|
22098
|
+
occurred_at: "2026-04-06T00:00:06.000Z",
|
|
22099
|
+
canonical_human_message_key: "shared-human-key-486",
|
|
22100
|
+
},
|
|
22101
|
+
},
|
|
22102
|
+
},
|
|
22103
|
+
mode: "continue",
|
|
22104
|
+
parseArchivedChatComment,
|
|
22105
|
+
deps: {
|
|
22106
|
+
normalizeArchiveCommentRecord: (record) => ({
|
|
22107
|
+
id: String(record?.id || "").trim(),
|
|
22108
|
+
body: String(record?.body || "").trim(),
|
|
22109
|
+
createdAt: String(record?.created_at || record?.createdAt || record?.updated_at || record?.updatedAt || "").trim(),
|
|
22110
|
+
updatedAt: String(record?.updated_at || record?.updatedAt || "").trim(),
|
|
22111
|
+
parsedArchive: safeObject(record?.parsedArchive),
|
|
22112
|
+
}),
|
|
22113
|
+
applyPendingAgeSelection: (selection) => selection,
|
|
22114
|
+
},
|
|
22115
|
+
pendingSelectionOptions: {},
|
|
22116
|
+
});
|
|
22117
|
+
push(
|
|
22118
|
+
"runner_entrypoint_replays_route_state_local_receipt_after_shared_human_fanout",
|
|
22119
|
+
ensureArray(pendingWork.receiptBackedPending).length === 1
|
|
22120
|
+
&& String(safeObject(ensureArray(pendingWork.receiptBackedPending)[0]).id || "").startsWith("receipt-replay:human:shared-human-key-486")
|
|
22121
|
+
&& safeObject(safeObject(ensureArray(pendingWork.receiptBackedPending)[0]).parsedArchive).messageID === 486
|
|
22122
|
+
&& ensureArray(safeObject(pendingWork.pending).pending).length === 1
|
|
22123
|
+
&& String(safeObject(ensureArray(safeObject(pendingWork.pending).pending)[0]).id || "").startsWith("receipt-replay:human:shared-human-key-486"),
|
|
22124
|
+
`replay=${ensureArray(pendingWork.receiptBackedPending).map((item) => String(safeObject(item).id || "")).join(",") || "(none)"} pending=${ensureArray(safeObject(pendingWork.pending).pending).map((item) => String(safeObject(item).id || "")).join(",") || "(none)"}`,
|
|
22125
|
+
);
|
|
22126
|
+
} catch (err) {
|
|
22127
|
+
push("runner_entrypoint_replays_route_state_local_receipt_after_shared_human_fanout", false, String(err?.message || err));
|
|
22128
|
+
}
|
|
22129
|
+
|
|
22130
|
+
try {
|
|
22131
|
+
const pendingWork = selectRunnerPendingWorkEntrypoint({
|
|
22132
|
+
comments: [],
|
|
22133
|
+
importOutcome: {
|
|
22134
|
+
importedCommentIDs: [],
|
|
22135
|
+
importedComments: [],
|
|
22136
|
+
currentPollLocalInboundReceipts: [],
|
|
22137
|
+
},
|
|
22138
|
+
refreshedState: {
|
|
22139
|
+
last_processed_comment_id: "comment-shared-human-opening-processed",
|
|
22140
|
+
last_processed_created_at: "2026-04-06T00:00:07.000Z",
|
|
22141
|
+
recent_local_inbound_receipts: {
|
|
22142
|
+
"-100999:487": {
|
|
22143
|
+
chat_id: "-100999",
|
|
22144
|
+
message_id: 487,
|
|
22145
|
+
body: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 하이",
|
|
22146
|
+
receipt_bot_username: "ryoai3_bot",
|
|
22147
|
+
occurred_at: "2026-04-06T00:00:06.000Z",
|
|
22148
|
+
canonical_human_message_key: "shared-human-key-487",
|
|
22149
|
+
},
|
|
22150
|
+
},
|
|
22151
|
+
},
|
|
22152
|
+
mode: "continue",
|
|
22153
|
+
parseArchivedChatComment,
|
|
22154
|
+
deps: {
|
|
22155
|
+
normalizeArchiveCommentRecord: (record) => ({
|
|
22156
|
+
id: String(record?.id || "").trim(),
|
|
22157
|
+
body: String(record?.body || "").trim(),
|
|
22158
|
+
createdAt: String(record?.created_at || record?.createdAt || record?.updated_at || record?.updatedAt || "").trim(),
|
|
22159
|
+
updatedAt: String(record?.updated_at || record?.updatedAt || "").trim(),
|
|
22160
|
+
parsedArchive: safeObject(record?.parsedArchive),
|
|
22161
|
+
}),
|
|
22162
|
+
applyPendingAgeSelection: (selection) => selection,
|
|
22163
|
+
},
|
|
22164
|
+
pendingSelectionOptions: {},
|
|
22165
|
+
});
|
|
22166
|
+
push(
|
|
22167
|
+
"runner_entrypoint_does_not_replay_route_state_local_receipt_after_route_cursor",
|
|
22168
|
+
ensureArray(pendingWork.receiptBackedPending).length === 0
|
|
22169
|
+
&& ensureArray(safeObject(pendingWork.pending).pending).length === 0,
|
|
22170
|
+
`replay=${ensureArray(pendingWork.receiptBackedPending).length} pending=${ensureArray(safeObject(pendingWork.pending).pending).length}`,
|
|
22171
|
+
);
|
|
22172
|
+
} catch (err) {
|
|
22173
|
+
push("runner_entrypoint_does_not_replay_route_state_local_receipt_after_route_cursor", false, String(err?.message || err));
|
|
22174
|
+
}
|
|
22175
|
+
|
|
22176
|
+
try {
|
|
22177
|
+
const pendingWork = selectRunnerPendingWorkEntrypoint({
|
|
22178
|
+
comments: [],
|
|
22179
|
+
importOutcome: {
|
|
22180
|
+
importedCommentIDs: [],
|
|
22181
|
+
importedComments: [],
|
|
22182
|
+
currentPollLocalInboundReceipts: [],
|
|
22183
|
+
},
|
|
22184
|
+
refreshedState: {
|
|
22185
|
+
last_processed_comment_id: "comment-before-followup-1",
|
|
22186
|
+
last_processed_created_at: "2026-04-06T00:00:00.000Z",
|
|
22187
|
+
},
|
|
22188
|
+
mode: "continue",
|
|
22189
|
+
parseArchivedChatComment,
|
|
22190
|
+
deps: {
|
|
22191
|
+
normalizeArchiveCommentRecord: (record) => ({
|
|
22192
|
+
id: String(record?.id || "").trim(),
|
|
22193
|
+
body: String(record?.body || "").trim(),
|
|
22194
|
+
createdAt: String(record?.created_at || record?.createdAt || record?.updated_at || record?.updatedAt || "").trim(),
|
|
22195
|
+
updatedAt: String(record?.updated_at || record?.updatedAt || "").trim(),
|
|
22196
|
+
parsedArchive: safeObject(record?.parsedArchive),
|
|
22197
|
+
}),
|
|
22198
|
+
applyPendingAgeSelection: (selection) => selection,
|
|
22199
|
+
},
|
|
22200
|
+
pendingSelectionOptions: {},
|
|
22201
|
+
followupRequests: [
|
|
22202
|
+
{
|
|
22203
|
+
request_key: "request-followup-1",
|
|
22204
|
+
status: "running",
|
|
22205
|
+
project_id: "project-followup-1",
|
|
22206
|
+
provider: "telegram",
|
|
22207
|
+
chat_id: "-100999",
|
|
22208
|
+
conversation_id: "conversation-followup-1",
|
|
22209
|
+
conversation_intent_mode: "multi_bot_direct",
|
|
22210
|
+
conversation_participants: ["ryoai_bot", "ryoai2_bot"],
|
|
22211
|
+
conversation_initial_responders: ["ryoai_bot", "ryoai2_bot"],
|
|
22212
|
+
conversation_allowed_responders: ["ryoai_bot", "ryoai2_bot"],
|
|
22213
|
+
execution_contract_type: "direct_result",
|
|
22214
|
+
execution_contract_actionable: true,
|
|
22215
|
+
execution_contract_targets: ["ryoai2_bot"],
|
|
22216
|
+
next_expected_responders: ["ryoai_bot"],
|
|
22217
|
+
updated_at: "2026-04-06T00:10:00.000Z",
|
|
22218
|
+
last_reply_message_envelope: {
|
|
22219
|
+
chat_id: "-100999",
|
|
22220
|
+
message_id: 498,
|
|
22221
|
+
reply_to_message_id: 497,
|
|
22222
|
+
body: "하이!",
|
|
22223
|
+
sender: "@RyoAI2_bot",
|
|
22224
|
+
sender_username: "ryoai2_bot",
|
|
22225
|
+
sender_is_bot: true,
|
|
22226
|
+
occurred_at: "2026-04-06T00:10:00.000Z",
|
|
22227
|
+
canonical_human_message_key: "shared-human-key-497",
|
|
22228
|
+
},
|
|
22229
|
+
},
|
|
22230
|
+
],
|
|
22231
|
+
currentBotSelector: "ryoai_bot",
|
|
22232
|
+
});
|
|
22233
|
+
push(
|
|
22234
|
+
"runner_entrypoint_replays_request_followup_for_next_expected_responder",
|
|
22235
|
+
ensureArray(pendingWork.requestFollowupPending).length === 1
|
|
22236
|
+
&& ensureArray(safeObject(pendingWork.pending).pending).length === 1
|
|
22237
|
+
&& String(safeObject(ensureArray(pendingWork.requestFollowupPending)[0]).id || "").startsWith("request-followup:request-followup-1:498:ryoai_bot")
|
|
22238
|
+
&& safeObject(safeObject(ensureArray(pendingWork.requestFollowupPending)[0]).parsedArchive).kind === "bot_reply"
|
|
22239
|
+
&& String(safeObject(safeObject(ensureArray(pendingWork.requestFollowupPending)[0]).parsedArchive).conversationMode || "") === "public_multi_bot"
|
|
22240
|
+
&& String(safeObject(safeObject(ensureArray(pendingWork.requestFollowupPending)[0]).parsedArchive).conversationIntentMode || "") === "multi_bot_direct"
|
|
22241
|
+
&& ensureArray(safeObject(safeObject(ensureArray(pendingWork.requestFollowupPending)[0]).parsedArchive).executionContractNextResponders).includes("ryoai_bot")
|
|
22242
|
+
&& ensureArray(safeObject(safeObject(safeObject(ensureArray(pendingWork.requestFollowupPending)[0]).parsedArchive).executionContract).next_responders).includes("ryoai_bot"),
|
|
22243
|
+
`replay=${ensureArray(pendingWork.requestFollowupPending).map((item) => String(safeObject(item).id || "")).join(",") || "(none)"} pending=${ensureArray(safeObject(pendingWork.pending).pending).map((item) => String(safeObject(item).id || "")).join(",") || "(none)"}`,
|
|
22244
|
+
);
|
|
22245
|
+
} catch (err) {
|
|
22246
|
+
push("runner_entrypoint_replays_request_followup_for_next_expected_responder", false, String(err?.message || err));
|
|
22247
|
+
}
|
|
22248
|
+
|
|
22249
|
+
try {
|
|
22250
|
+
const pendingWork = selectRunnerPendingWorkEntrypoint({
|
|
22251
|
+
comments: [],
|
|
22252
|
+
importOutcome: {
|
|
22253
|
+
importedCommentIDs: [],
|
|
22254
|
+
importedComments: [],
|
|
22255
|
+
currentPollLocalInboundReceipts: [],
|
|
22256
|
+
},
|
|
22257
|
+
refreshedState: {
|
|
22258
|
+
last_processed_comment_id: "request-followup:request-followup-2:499:ryoai_bot",
|
|
22259
|
+
last_processed_created_at: "2026-04-06T00:11:00.000Z",
|
|
22260
|
+
},
|
|
22261
|
+
mode: "continue",
|
|
22262
|
+
parseArchivedChatComment,
|
|
22263
|
+
deps: {
|
|
22264
|
+
normalizeArchiveCommentRecord: (record) => ({
|
|
22265
|
+
id: String(record?.id || "").trim(),
|
|
22266
|
+
body: String(record?.body || "").trim(),
|
|
22267
|
+
createdAt: String(record?.created_at || record?.createdAt || record?.updated_at || record?.updatedAt || "").trim(),
|
|
22268
|
+
updatedAt: String(record?.updated_at || record?.updatedAt || "").trim(),
|
|
22269
|
+
parsedArchive: safeObject(record?.parsedArchive),
|
|
22270
|
+
}),
|
|
22271
|
+
applyPendingAgeSelection: (selection) => selection,
|
|
22272
|
+
},
|
|
22273
|
+
pendingSelectionOptions: {},
|
|
22274
|
+
followupRequests: [
|
|
22275
|
+
{
|
|
22276
|
+
request_key: "request-followup-2",
|
|
22277
|
+
status: "running",
|
|
22278
|
+
project_id: "project-followup-1",
|
|
22279
|
+
provider: "telegram",
|
|
22280
|
+
chat_id: "-100999",
|
|
22281
|
+
conversation_id: "conversation-followup-2",
|
|
22282
|
+
conversation_intent_mode: "multi_bot_direct",
|
|
22283
|
+
conversation_participants: ["ryoai_bot", "ryoai2_bot"],
|
|
22284
|
+
conversation_initial_responders: ["ryoai_bot", "ryoai2_bot"],
|
|
22285
|
+
conversation_allowed_responders: ["ryoai_bot", "ryoai2_bot"],
|
|
22286
|
+
execution_contract_type: "direct_result",
|
|
22287
|
+
execution_contract_actionable: true,
|
|
22288
|
+
execution_contract_targets: ["ryoai2_bot"],
|
|
22289
|
+
next_expected_responders: ["ryoai_bot"],
|
|
22290
|
+
updated_at: "2026-04-06T00:11:00.000Z",
|
|
22291
|
+
last_reply_message_envelope: {
|
|
22292
|
+
chat_id: "-100999",
|
|
22293
|
+
message_id: 499,
|
|
22294
|
+
reply_to_message_id: 497,
|
|
22295
|
+
body: "하이!",
|
|
22296
|
+
sender: "@RyoAI2_bot",
|
|
22297
|
+
sender_username: "ryoai2_bot",
|
|
22298
|
+
sender_is_bot: true,
|
|
22299
|
+
occurred_at: "2026-04-06T00:11:00.000Z",
|
|
22300
|
+
canonical_human_message_key: "shared-human-key-499",
|
|
22301
|
+
},
|
|
22302
|
+
},
|
|
22303
|
+
],
|
|
22304
|
+
currentBotSelector: "ryoai_bot",
|
|
22305
|
+
});
|
|
22306
|
+
push(
|
|
22307
|
+
"runner_entrypoint_does_not_replay_request_followup_after_route_cursor",
|
|
22308
|
+
ensureArray(pendingWork.requestFollowupPending).length === 0
|
|
22309
|
+
&& ensureArray(safeObject(pendingWork.pending).pending).length === 0,
|
|
22310
|
+
`replay=${ensureArray(pendingWork.requestFollowupPending).length} pending=${ensureArray(safeObject(pendingWork.pending).pending).length}`,
|
|
22311
|
+
);
|
|
22312
|
+
} catch (err) {
|
|
22313
|
+
push("runner_entrypoint_does_not_replay_request_followup_after_route_cursor", false, String(err?.message || err));
|
|
22314
|
+
}
|
|
22315
|
+
|
|
22316
|
+
try {
|
|
22317
|
+
const pendingWork = selectRunnerPendingWorkEntrypoint({
|
|
22318
|
+
comments: [],
|
|
22319
|
+
importOutcome: {
|
|
22320
|
+
importedCommentIDs: [],
|
|
22321
|
+
importedComments: [],
|
|
22322
|
+
currentPollLocalInboundReceipts: [],
|
|
22323
|
+
},
|
|
22324
|
+
refreshedState: {
|
|
22325
|
+
last_processed_comment_id: "comment-before-followup-3",
|
|
22326
|
+
last_processed_created_at: "2026-04-06T00:12:00.000Z",
|
|
22327
|
+
},
|
|
22328
|
+
mode: "continue",
|
|
22329
|
+
parseArchivedChatComment,
|
|
22330
|
+
deps: {
|
|
22331
|
+
normalizeArchiveCommentRecord: (record) => ({
|
|
22332
|
+
id: String(record?.id || "").trim(),
|
|
22333
|
+
body: String(record?.body || "").trim(),
|
|
22334
|
+
createdAt: String(record?.created_at || record?.createdAt || record?.updated_at || record?.updatedAt || "").trim(),
|
|
22335
|
+
updatedAt: String(record?.updated_at || record?.updatedAt || "").trim(),
|
|
22336
|
+
parsedArchive: safeObject(record?.parsedArchive),
|
|
22337
|
+
}),
|
|
22338
|
+
applyPendingAgeSelection: (selection) => selection,
|
|
22339
|
+
},
|
|
22340
|
+
pendingSelectionOptions: {},
|
|
22341
|
+
followupRequests: [
|
|
22342
|
+
{
|
|
22343
|
+
request_key: "request-followup-3",
|
|
22344
|
+
status: "running",
|
|
22345
|
+
project_id: selftestProjectID,
|
|
22346
|
+
provider: "telegram",
|
|
22347
|
+
chat_id: "-100126",
|
|
22348
|
+
conversation_id: "conversation-followup-3",
|
|
22349
|
+
conversation_intent_mode: "multi_bot_direct",
|
|
22350
|
+
conversation_allow_bot_to_bot: true,
|
|
22351
|
+
conversation_participants: ["ryoai2_bot", "ryoai3_bot"],
|
|
22352
|
+
conversation_initial_responders: ["ryoai2_bot", "ryoai3_bot"],
|
|
22353
|
+
conversation_allowed_responders: ["ryoai2_bot", "ryoai3_bot"],
|
|
22354
|
+
execution_contract_type: "direct_result",
|
|
22355
|
+
execution_contract_actionable: true,
|
|
22356
|
+
execution_contract_targets: ["ryoai2_bot", "ryoai3_bot"],
|
|
22357
|
+
next_expected_responders: ["ryoai3_bot"],
|
|
22358
|
+
updated_at: "2026-04-06T00:12:00.000Z",
|
|
22359
|
+
last_reply_message_envelope: {
|
|
22360
|
+
chat_id: "-100126",
|
|
22361
|
+
message_id: 515,
|
|
22362
|
+
reply_to_message_id: 514,
|
|
22363
|
+
body: "안녕하세요, RyoAI2_bot입니다.",
|
|
22364
|
+
sender: "@RyoAI2_bot",
|
|
22365
|
+
sender_username: "ryoai2_bot",
|
|
22366
|
+
sender_is_bot: true,
|
|
22367
|
+
occurred_at: "2026-04-06T00:12:00.000Z",
|
|
22368
|
+
canonical_human_message_key: "shared-human-key-515",
|
|
22369
|
+
},
|
|
22370
|
+
},
|
|
22371
|
+
],
|
|
22372
|
+
currentBotSelector: "ryoai3_bot",
|
|
22373
|
+
});
|
|
22374
|
+
let aiCalls = 0;
|
|
22375
|
+
const deliveredConversation = [];
|
|
22376
|
+
const processed = await processRunnerSelectedRecord({
|
|
22377
|
+
routeKey: "runner-followup-public-multi-bot-peer-key",
|
|
22378
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
22379
|
+
name: "telegram-monitor-runner-followup-public-multi-bot-peer",
|
|
22380
|
+
project_id: selftestProjectID,
|
|
22381
|
+
provider: "telegram",
|
|
22382
|
+
role: "monitor",
|
|
22383
|
+
role_profile: "monitor",
|
|
22384
|
+
destination_id: "dest-1",
|
|
22385
|
+
destination_label: "Main Room",
|
|
22386
|
+
server_bot_name: "RyoAI3_bot",
|
|
22387
|
+
server_bot_id: "bot-peer-2",
|
|
22388
|
+
trigger_policy: {
|
|
22389
|
+
mentions_only: true,
|
|
22390
|
+
direct_messages: true,
|
|
22391
|
+
reply_to_bot_messages: true,
|
|
22392
|
+
},
|
|
22393
|
+
archive_policy: {
|
|
22394
|
+
mirror_replies: true,
|
|
22395
|
+
dedupe_inbound: true,
|
|
22396
|
+
dedupe_outbound: true,
|
|
22397
|
+
skip_bot_messages: true,
|
|
22398
|
+
},
|
|
22399
|
+
dry_run_delivery: true,
|
|
22400
|
+
}),
|
|
22401
|
+
selectedRecord: ensureArray(pendingWork.requestFollowupPending)[0],
|
|
22402
|
+
pendingOrdered: ensureArray(pendingWork.pending?.pending),
|
|
22403
|
+
bot: {
|
|
22404
|
+
id: "bot-peer-2",
|
|
22405
|
+
name: "RyoAI3_bot",
|
|
22406
|
+
username: "RyoAI3_bot",
|
|
22407
|
+
role: "monitor",
|
|
22408
|
+
provider: "telegram",
|
|
22409
|
+
},
|
|
22410
|
+
destination: {
|
|
22411
|
+
id: "dest-1",
|
|
22412
|
+
label: "Main Room",
|
|
22413
|
+
provider: "telegram",
|
|
22414
|
+
chatID: "-100126",
|
|
22415
|
+
},
|
|
22416
|
+
archiveThread: {
|
|
22417
|
+
threadID: "thread-1",
|
|
22418
|
+
workItemID: "work-item-1",
|
|
22419
|
+
},
|
|
22420
|
+
executionPlan: {
|
|
22421
|
+
mode: "role_profile",
|
|
22422
|
+
roleProfileName: "monitor",
|
|
22423
|
+
roleProfile: {
|
|
22424
|
+
client: "sample",
|
|
22425
|
+
model: "",
|
|
22426
|
+
permissionMode: "read_only",
|
|
22427
|
+
reasoningEffort: "low",
|
|
22428
|
+
},
|
|
22429
|
+
workspaceDir: path.join(os.tmpdir(), "metheus-runner-selftest-followup-public-multi-bot-peer"),
|
|
22430
|
+
workspaceSource: "selftest",
|
|
22431
|
+
usedCommandFallback: false,
|
|
22432
|
+
},
|
|
22433
|
+
runtime: {
|
|
22434
|
+
baseURL: "https://example.test",
|
|
22435
|
+
token: "selftest-token",
|
|
22436
|
+
timeoutSeconds: 30,
|
|
22437
|
+
actor: { user_id: "user-1" },
|
|
22438
|
+
},
|
|
22439
|
+
deps: {
|
|
22440
|
+
saveRunnerRouteState: () => {},
|
|
22441
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
22442
|
+
runRunnerAIExecution: async () => {
|
|
22443
|
+
aiCalls += 1;
|
|
22444
|
+
return {
|
|
22445
|
+
skip: false,
|
|
22446
|
+
reply: "안녕하세요, RyoAI3_bot입니다.",
|
|
22447
|
+
};
|
|
22448
|
+
},
|
|
22449
|
+
performLocalBotDelivery: async ({ archiveConversation, archiveConversationContext, archiveExecutionContract }) => {
|
|
22450
|
+
deliveredConversation.push(buildSelftestArchiveConversation({
|
|
22451
|
+
archiveConversation,
|
|
22452
|
+
archiveConversationContext,
|
|
22453
|
+
archiveExecutionContract,
|
|
22454
|
+
}));
|
|
22455
|
+
return {
|
|
22456
|
+
delivery: { dryRun: true, body: {} },
|
|
22457
|
+
archive: {},
|
|
22458
|
+
};
|
|
22459
|
+
},
|
|
22460
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
22461
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
22462
|
+
buildRunnerExecutionDeps: () => ({}),
|
|
22463
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
22464
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
22465
|
+
resolveConversationPeerBots: () => [
|
|
22466
|
+
{ id: "bot-peer-1", name: "RyoAI2_bot" },
|
|
22467
|
+
{ id: "bot-peer-2", name: "RyoAI3_bot" },
|
|
22468
|
+
],
|
|
22469
|
+
},
|
|
22470
|
+
});
|
|
22471
|
+
push(
|
|
22472
|
+
"runner_request_followup_public_multi_bot_continuation_is_authorized",
|
|
22473
|
+
processed.kind === "replied"
|
|
22474
|
+
&& aiCalls === 1
|
|
22475
|
+
&& String(deliveredConversation[0]?.mode || "") === "public_multi_bot"
|
|
22476
|
+
&& String(deliveredConversation[0]?.intentMode || "") === "multi_bot_direct"
|
|
22477
|
+
&& ensureArray(deliveredConversation[0]?.allowedResponderSelectors).includes("ryoai3_bot"),
|
|
22478
|
+
`kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} mode=${String(deliveredConversation[0]?.mode || "(none)")} intent=${String(deliveredConversation[0]?.intentMode || "(none)")} reason=${String(processed.skippedRecord?.reason || "(none)")}`,
|
|
22479
|
+
);
|
|
22480
|
+
} catch (err) {
|
|
22481
|
+
push("runner_request_followup_public_multi_bot_continuation_is_authorized", false, String(err?.message || err));
|
|
22482
|
+
}
|
|
22483
|
+
|
|
22065
22484
|
try {
|
|
22066
22485
|
const deliveryContext = await prepareLocalBotDeliveryContext({
|
|
22067
22486
|
siteBaseURL: "https://example.test",
|