metheus-governance-mcp-cli 0.2.274 → 0.2.276
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 +29 -21
- package/lib/runner-helpers.mjs +10 -0
- package/lib/runner-orchestration-entrypoints.mjs +52 -6
- package/lib/runner-recorder-active-execution-handoff.mjs +2 -0
- package/lib/runner-runtime.mjs +63 -7
- package/lib/selftest-runner-scenarios.mjs +87 -1
- package/lib/selftest-telegram-e2e.mjs +90 -3
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -133,15 +133,16 @@ import {
|
|
|
133
133
|
tryRegister,
|
|
134
134
|
withWorkspaceDirArg,
|
|
135
135
|
} from "./lib/client-registration.mjs";
|
|
136
|
-
import {
|
|
137
|
-
applyPendingAgeSelection,
|
|
138
|
-
buildTelegramBotReplyEnvelope,
|
|
139
|
-
buildTelegramMessageEnvelopeFromParsedArchive as buildRunnerTelegramMessageEnvelopeFromParsedArchive,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
136
|
+
import {
|
|
137
|
+
applyPendingAgeSelection,
|
|
138
|
+
buildTelegramBotReplyEnvelope,
|
|
139
|
+
buildTelegramMessageEnvelopeFromParsedArchive as buildRunnerTelegramMessageEnvelopeFromParsedArchive,
|
|
140
|
+
buildRunnerRouteDuplicateStateFromComment,
|
|
141
|
+
buildRunnerRouteStateFromComment,
|
|
142
|
+
buildProcessableArchiveLogicalKey,
|
|
143
|
+
findEarlierProcessableArchiveDuplicate,
|
|
144
|
+
findRecentTelegramMessageEnvelope,
|
|
145
|
+
isTelegramLocalInboundEnvelopeForRoute,
|
|
145
146
|
isInboundArchiveKind,
|
|
146
147
|
normalizeTelegramMessageEnvelope as normalizeRunnerTelegramMessageEnvelope,
|
|
147
148
|
normalizeArchiveCommentRecord,
|
|
@@ -2100,12 +2101,15 @@ function mergeRunnerStateRecords(preferred, fallback) {
|
|
|
2100
2101
|
}
|
|
2101
2102
|
return safeObject(secondary[key]);
|
|
2102
2103
|
};
|
|
2103
|
-
return {
|
|
2104
|
-
last_processed_comment_id: pickStringField("last_processed_comment_id"),
|
|
2105
|
-
last_processed_created_at: pickStringField("last_processed_created_at"),
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2104
|
+
return {
|
|
2105
|
+
last_processed_comment_id: pickStringField("last_processed_comment_id"),
|
|
2106
|
+
last_processed_created_at: pickStringField("last_processed_created_at"),
|
|
2107
|
+
last_dedupe_comment_id: pickStringField("last_dedupe_comment_id"),
|
|
2108
|
+
last_source_message_id: pickNumberField("last_source_message_id", { allowUndefined: true }),
|
|
2109
|
+
last_source_kind: pickStringField("last_source_kind"),
|
|
2110
|
+
last_dedupe_source_message_id: pickNumberField("last_dedupe_source_message_id", { allowUndefined: true }),
|
|
2111
|
+
last_dedupe_source_kind: pickStringField("last_dedupe_source_kind"),
|
|
2112
|
+
last_error: pickStringField("last_error", { allowBlank: true }),
|
|
2109
2113
|
updated_at: firstNonEmptyString([primary.updated_at, secondary.updated_at, new Date().toISOString()]),
|
|
2110
2114
|
last_action: pickStringField("last_action"),
|
|
2111
2115
|
last_reason: pickStringField("last_reason"),
|
|
@@ -5856,11 +5860,11 @@ function maybeBuildDuplicateArchivedSkipForRoute({
|
|
|
5856
5860
|
};
|
|
5857
5861
|
}
|
|
5858
5862
|
const duplicateState = safeObject(routeStateForDuplicate);
|
|
5859
|
-
const lastSourceMessageID = intFromRawAllowZero(duplicateState.
|
|
5860
|
-
const lastSourceKind = String(duplicateState.
|
|
5863
|
+
const lastSourceMessageID = intFromRawAllowZero(duplicateState.last_dedupe_source_message_id, 0);
|
|
5864
|
+
const lastSourceKind = String(duplicateState.last_dedupe_source_kind || "").trim().toLowerCase();
|
|
5861
5865
|
const selectedMessageID = intFromRawAllowZero(selectedParsed.messageID, 0);
|
|
5862
5866
|
const selectedKind = String(selectedParsed.kind || "").trim().toLowerCase();
|
|
5863
|
-
const lastCommentID = String(duplicateState.
|
|
5867
|
+
const lastCommentID = String(duplicateState.last_dedupe_comment_id || "").trim();
|
|
5864
5868
|
if (
|
|
5865
5869
|
selectedMessageID > 0
|
|
5866
5870
|
&& lastSourceMessageID > 0
|
|
@@ -8591,9 +8595,11 @@ function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
|
8591
8595
|
};
|
|
8592
8596
|
}
|
|
8593
8597
|
|
|
8594
|
-
function buildArchivedInboundMessageKey(chatID, messageID) {
|
|
8595
|
-
|
|
8596
|
-
|
|
8598
|
+
function buildArchivedInboundMessageKey(chatID, messageID, sourceBotUsername = "") {
|
|
8599
|
+
const baseKey = `${String(chatID || "").trim()}:${intFromRawAllowZero(messageID, 0)}`;
|
|
8600
|
+
const normalizedBotUsername = normalizeTelegramMentionUsername(sourceBotUsername);
|
|
8601
|
+
return normalizedBotUsername ? `${baseKey}::${normalizedBotUsername}` : baseKey;
|
|
8602
|
+
}
|
|
8597
8603
|
|
|
8598
8604
|
function formatTelegramInboundArchiveComment(normalized) {
|
|
8599
8605
|
const archiveSourceOrigin = String(normalized.archiveSourceOrigin || normalized.sourceOrigin || "").trim();
|
|
@@ -10575,6 +10581,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
10575
10581
|
}),
|
|
10576
10582
|
requestKey: requestClaim.requestKey,
|
|
10577
10583
|
claimedRequest,
|
|
10584
|
+
dedupeStatePatch: buildRunnerRouteDuplicateStateFromComment(selectedRecord),
|
|
10578
10585
|
visibilityStatePatch,
|
|
10579
10586
|
});
|
|
10580
10587
|
saveRunnerRouteState(routeKey, acceptedExecutionRoutePatch);
|
|
@@ -10739,6 +10746,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
10739
10746
|
}),
|
|
10740
10747
|
requestKey: requestClaim.requestKey,
|
|
10741
10748
|
claimedRequest,
|
|
10749
|
+
dedupeStatePatch: buildRunnerRouteDuplicateStateFromComment(selectedRecord),
|
|
10742
10750
|
visibilityStatePatch,
|
|
10743
10751
|
});
|
|
10744
10752
|
saveRunnerRouteState(routeKey, acceptedExecutionRoutePatch);
|
package/lib/runner-helpers.mjs
CHANGED
|
@@ -534,6 +534,16 @@ export function buildRunnerRouteStateFromComment(record, patch = {}) {
|
|
|
534
534
|
};
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
export function buildRunnerRouteDuplicateStateFromComment(record, patch = {}) {
|
|
538
|
+
const parsed = safeObject(record?.parsedArchive);
|
|
539
|
+
return {
|
|
540
|
+
last_dedupe_comment_id: String(record?.id || "").trim(),
|
|
541
|
+
last_dedupe_source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
|
|
542
|
+
last_dedupe_source_kind: String(parsed.kind || "").trim(),
|
|
543
|
+
...safeObject(patch),
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
537
547
|
export function normalizeArchiveCommentRecord(rawComment, parseArchivedChatComment) {
|
|
538
548
|
const comment = safeObject(rawComment);
|
|
539
549
|
const body = String(comment.body || "").trim();
|
|
@@ -51,6 +51,56 @@ function intFromRawAllowZero(raw, fallback = 0) {
|
|
|
51
51
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function escapeRegex(text) {
|
|
55
|
+
return String(text || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizeMentionSelector(value) {
|
|
59
|
+
return String(value || "").trim().replace(/^@+/, "").toLowerCase();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildRunnerInboundTargetSelectorSuffix(selector) {
|
|
63
|
+
const normalizedSelector = normalizeMentionSelector(selector);
|
|
64
|
+
return normalizedSelector ? `::${normalizedSelector}` : "";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function resolveRunnerArchiveSourceTargetSelector(recordRaw) {
|
|
68
|
+
const parsed = safeObject(safeObject(recordRaw).parsedArchive);
|
|
69
|
+
return normalizeMentionSelector(parsed.sourceBotUsername || parsed.source_bot_username);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveRunnerLocalInboundReceiptTargetSelector(receiptRaw) {
|
|
73
|
+
const receipt = safeObject(receiptRaw);
|
|
74
|
+
const receiptBotSelector = normalizeMentionSelector(
|
|
75
|
+
receipt.receipt_bot_username
|
|
76
|
+
|| receipt.receiptBotUsername
|
|
77
|
+
|| receipt.source_bot_username
|
|
78
|
+
|| receipt.sourceBotUsername,
|
|
79
|
+
);
|
|
80
|
+
if (!receiptBotSelector) {
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
const replyTargetSelector = normalizeMentionSelector(
|
|
84
|
+
receipt.reply_to_from_username
|
|
85
|
+
|| receipt.replyToFromUsername
|
|
86
|
+
|| receipt.reply_to_username
|
|
87
|
+
|| receipt.replyToUsername,
|
|
88
|
+
);
|
|
89
|
+
const replyTargetIsBot = receipt.reply_to_from_is_bot === true
|
|
90
|
+
|| receipt.replyToFromIsBot === true
|
|
91
|
+
|| receipt.reply_to_sender_is_bot === true
|
|
92
|
+
|| receipt.replyToSenderIsBot === true;
|
|
93
|
+
if (replyTargetIsBot && replyTargetSelector && replyTargetSelector === receiptBotSelector) {
|
|
94
|
+
return receiptBotSelector;
|
|
95
|
+
}
|
|
96
|
+
const body = String(receipt.body || receipt.text || "").trim();
|
|
97
|
+
if (!body) {
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
const mentionPattern = new RegExp(`(^|\\s)@${escapeRegex(receiptBotSelector)}(?=\\b|\\s|$)`, "i");
|
|
101
|
+
return mentionPattern.test(body) ? receiptBotSelector : "";
|
|
102
|
+
}
|
|
103
|
+
|
|
54
104
|
function buildRunnerArchiveSourceMessageKey(recordRaw) {
|
|
55
105
|
const parsed = safeObject(safeObject(recordRaw).parsedArchive);
|
|
56
106
|
const chatID = String(parsed.chatID || parsed.chatId || "").trim();
|
|
@@ -58,7 +108,7 @@ function buildRunnerArchiveSourceMessageKey(recordRaw) {
|
|
|
58
108
|
if (!chatID || !(messageID > 0)) {
|
|
59
109
|
return "";
|
|
60
110
|
}
|
|
61
|
-
return `${chatID}:${messageID}`;
|
|
111
|
+
return `${chatID}:${messageID}${buildRunnerInboundTargetSelectorSuffix(resolveRunnerArchiveSourceTargetSelector(recordRaw))}`;
|
|
62
112
|
}
|
|
63
113
|
|
|
64
114
|
function buildRunnerLocalInboundReceiptKey(receiptRaw) {
|
|
@@ -68,7 +118,7 @@ function buildRunnerLocalInboundReceiptKey(receiptRaw) {
|
|
|
68
118
|
if (!chatID || !(messageID > 0)) {
|
|
69
119
|
return "";
|
|
70
120
|
}
|
|
71
|
-
return `${chatID}:${messageID}`;
|
|
121
|
+
return `${chatID}:${messageID}${buildRunnerInboundTargetSelectorSuffix(resolveRunnerLocalInboundReceiptTargetSelector(receiptRaw))}`;
|
|
72
122
|
}
|
|
73
123
|
|
|
74
124
|
function buildRunnerReceiptReplaySortTime(receiptRaw) {
|
|
@@ -157,10 +207,6 @@ function buildContextSpeakerType(parsedArchiveRaw) {
|
|
|
157
207
|
return parsed.senderIsBot === true ? "bot" : "system";
|
|
158
208
|
}
|
|
159
209
|
|
|
160
|
-
function normalizeMentionSelector(value) {
|
|
161
|
-
return String(value || "").trim().replace(/^@+/, "").toLowerCase();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
210
|
function uniqueOrdered(values) {
|
|
165
211
|
const seen = new Set();
|
|
166
212
|
const output = [];
|
|
@@ -10,6 +10,7 @@ export function buildRunnerAcceptedExecutionRoutePatch({
|
|
|
10
10
|
requestKey = "",
|
|
11
11
|
claimedRequest = {},
|
|
12
12
|
visibilityStatePatch = {},
|
|
13
|
+
dedupeStatePatch = {},
|
|
13
14
|
}) {
|
|
14
15
|
const normalizedActiveExecutionPatch = safeObject(activeExecutionPatch);
|
|
15
16
|
const normalizedClaimedRequest = safeObject(claimedRequest);
|
|
@@ -19,6 +20,7 @@ export function buildRunnerAcceptedExecutionRoutePatch({
|
|
|
19
20
|
last_root_work_item_id: String(normalizedClaimedRequest.root_work_item_id || "").trim(),
|
|
20
21
|
last_root_work_item_title: String(normalizedClaimedRequest.root_work_item_title || "").trim(),
|
|
21
22
|
last_root_work_item_status: String(normalizedClaimedRequest.root_work_item_status || "").trim(),
|
|
23
|
+
...safeObject(dedupeStatePatch),
|
|
22
24
|
...safeObject(visibilityStatePatch),
|
|
23
25
|
};
|
|
24
26
|
}
|
package/lib/runner-runtime.mjs
CHANGED
|
@@ -280,6 +280,34 @@ function currentRouteOwnsRunnerInboundUpdate({
|
|
|
280
280
|
}).some((owner) => String(safeObject(owner).routeKey || "").trim() === normalizedRouteKey);
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
function resolveRunnerInboundArchiveSourceBotUsername({
|
|
284
|
+
update,
|
|
285
|
+
route,
|
|
286
|
+
bot,
|
|
287
|
+
}) {
|
|
288
|
+
const normalizedUpdate = safeObject(update);
|
|
289
|
+
const currentBotSelectors = buildRouteBotUsernameCandidates(bot, route);
|
|
290
|
+
const currentBotSelector = ensureArray(currentBotSelectors)[0] || "";
|
|
291
|
+
if (!currentBotSelector) {
|
|
292
|
+
return "";
|
|
293
|
+
}
|
|
294
|
+
const explicitMentions = ensureArray(normalizedUpdate.mentionUsernames)
|
|
295
|
+
.map((value) => normalizeMentionSelector(value))
|
|
296
|
+
.filter(Boolean);
|
|
297
|
+
if (explicitMentions.includes(currentBotSelector)) {
|
|
298
|
+
return currentBotSelector;
|
|
299
|
+
}
|
|
300
|
+
const replyTargetSelector = normalizeMentionSelector(normalizedUpdate.replyToFromUsername);
|
|
301
|
+
if (
|
|
302
|
+
normalizedUpdate.replyToFromIsBot === true
|
|
303
|
+
&& replyTargetSelector
|
|
304
|
+
&& currentBotSelectors.includes(replyTargetSelector)
|
|
305
|
+
) {
|
|
306
|
+
return replyTargetSelector;
|
|
307
|
+
}
|
|
308
|
+
return "";
|
|
309
|
+
}
|
|
310
|
+
|
|
283
311
|
function managedConversationBotTargetsCurrentRoute({
|
|
284
312
|
update,
|
|
285
313
|
bot,
|
|
@@ -480,6 +508,18 @@ function normalizeRunnerRecentLocalInboundReceipt(rawReceipt, fallbackKey = "")
|
|
|
480
508
|
if (replyToMessageID > 0) {
|
|
481
509
|
normalized.reply_to_message_id = replyToMessageID;
|
|
482
510
|
}
|
|
511
|
+
const replyToFromUsername = firstNonEmptyString([
|
|
512
|
+
receipt.reply_to_from_username,
|
|
513
|
+
receipt.replyToFromUsername,
|
|
514
|
+
receipt.reply_to_username,
|
|
515
|
+
receipt.replyToUsername,
|
|
516
|
+
]);
|
|
517
|
+
if (replyToFromUsername) {
|
|
518
|
+
normalized.reply_to_from_username = replyToFromUsername;
|
|
519
|
+
}
|
|
520
|
+
if (receipt.reply_to_from_is_bot === true || receipt.replyToFromIsBot === true) {
|
|
521
|
+
normalized.reply_to_from_is_bot = true;
|
|
522
|
+
}
|
|
483
523
|
const chatType = firstNonEmptyString([receipt.chat_type, receipt.chatType]);
|
|
484
524
|
if (chatType) {
|
|
485
525
|
normalized.chat_type = chatType;
|
|
@@ -535,6 +575,8 @@ function buildRunnerLocalInboundArtifacts(updates, routeKey, route, bot, destina
|
|
|
535
575
|
message_id: messageID,
|
|
536
576
|
message_thread_id: intFromRawAllowZero(update.messageThreadID, 0),
|
|
537
577
|
reply_to_message_id: intFromRawAllowZero(update.replyToMessageID, 0),
|
|
578
|
+
reply_to_from_username: update.replyToFromUsername,
|
|
579
|
+
reply_to_from_is_bot: update.replyToFromIsBot === true,
|
|
538
580
|
kind: update.fromIsBot ? "bot_reply" : "telegram_message",
|
|
539
581
|
sender_id: update.fromID,
|
|
540
582
|
sender: update.fromName,
|
|
@@ -628,14 +670,16 @@ function buildRunnerRecentLocalInboundReceipts(routeStateRaw, localInboundArtifa
|
|
|
628
670
|
const RUNNER_INBOUND_ARCHIVE_RESERVATION_TTL_MS = 10 * 60 * 1000;
|
|
629
671
|
const runnerInboundArchiveReservations = new Map();
|
|
630
672
|
|
|
631
|
-
function buildRunnerInboundArchiveReservationKey(threadID, chatID, messageID) {
|
|
673
|
+
function buildRunnerInboundArchiveReservationKey(threadID, chatID, messageID, sourceBotUsername = "") {
|
|
632
674
|
const normalizedThreadID = String(threadID || "").trim();
|
|
633
675
|
const normalizedChatID = String(chatID || "").trim();
|
|
634
676
|
const normalizedMessageID = intFromRawAllowZero(messageID, 0);
|
|
677
|
+
const normalizedSourceBotUsername = normalizeMentionSelector(sourceBotUsername);
|
|
635
678
|
if (!normalizedThreadID || !normalizedChatID || !(normalizedMessageID > 0)) {
|
|
636
679
|
return "";
|
|
637
680
|
}
|
|
638
|
-
|
|
681
|
+
const baseKey = `${normalizedThreadID}::${normalizedChatID}:${normalizedMessageID}`;
|
|
682
|
+
return normalizedSourceBotUsername ? `${baseKey}::${normalizedSourceBotUsername}` : baseKey;
|
|
639
683
|
}
|
|
640
684
|
|
|
641
685
|
function cleanupRunnerInboundArchiveReservations(nowMs = Date.now()) {
|
|
@@ -646,8 +690,8 @@ function cleanupRunnerInboundArchiveReservations(nowMs = Date.now()) {
|
|
|
646
690
|
}
|
|
647
691
|
}
|
|
648
692
|
|
|
649
|
-
function reserveRunnerInboundArchiveMessage(threadID, chatID, messageID) {
|
|
650
|
-
const reservationKey = buildRunnerInboundArchiveReservationKey(threadID, chatID, messageID);
|
|
693
|
+
function reserveRunnerInboundArchiveMessage(threadID, chatID, messageID, sourceBotUsername = "") {
|
|
694
|
+
const reservationKey = buildRunnerInboundArchiveReservationKey(threadID, chatID, messageID, sourceBotUsername);
|
|
651
695
|
if (!reservationKey) {
|
|
652
696
|
return {
|
|
653
697
|
ok: false,
|
|
@@ -777,7 +821,7 @@ async function loadRunnerExistingInboundArchiveKeys({
|
|
|
777
821
|
.map((record) => normalizeArchiveCommentRecord(record, parseArchivedChatComment))
|
|
778
822
|
.map((record) => record.parsedArchive)
|
|
779
823
|
.filter((parsed) => parsed && isInboundArchiveKind(parsed.kind) && parsed.chatID)
|
|
780
|
-
.map((parsed) => buildArchivedInboundMessageKey(parsed.chatID, parsed.messageID)),
|
|
824
|
+
.map((parsed) => buildArchivedInboundMessageKey(parsed.chatID, parsed.messageID, parsed.sourceBotUsername)),
|
|
781
825
|
);
|
|
782
826
|
}
|
|
783
827
|
|
|
@@ -835,12 +879,22 @@ async function archiveRunnerTelegramInboundUpdates({
|
|
|
835
879
|
) {
|
|
836
880
|
continue;
|
|
837
881
|
}
|
|
838
|
-
const
|
|
882
|
+
const archiveSourceBotUsername = resolveRunnerInboundArchiveSourceBotUsername({
|
|
883
|
+
update,
|
|
884
|
+
route,
|
|
885
|
+
bot,
|
|
886
|
+
});
|
|
887
|
+
const dedupeKey = buildArchivedInboundMessageKey(update.chatID, update.messageID, archiveSourceBotUsername);
|
|
839
888
|
if (boolFromRaw(archivePolicy.dedupeInbound, true) && existingKeys.has(dedupeKey)) {
|
|
840
889
|
continue;
|
|
841
890
|
}
|
|
842
891
|
const reservation = boolFromRaw(archivePolicy.dedupeInbound, true)
|
|
843
|
-
? reserveRunnerInboundArchiveMessage(
|
|
892
|
+
? reserveRunnerInboundArchiveMessage(
|
|
893
|
+
archiveThread?.threadID,
|
|
894
|
+
update.chatID,
|
|
895
|
+
update.messageID,
|
|
896
|
+
archiveSourceBotUsername,
|
|
897
|
+
)
|
|
844
898
|
: { ok: true, reservationKey: "" };
|
|
845
899
|
if (!reservation.ok) {
|
|
846
900
|
continue;
|
|
@@ -851,6 +905,8 @@ async function archiveRunnerTelegramInboundUpdates({
|
|
|
851
905
|
archiveBody = formatTelegramInboundArchiveComment({
|
|
852
906
|
...update,
|
|
853
907
|
archiveSourceOrigin: "telegram_archive_context",
|
|
908
|
+
archiveSourceRouteKey: archiveSourceBotUsername ? String(routeKey || "").trim() : "",
|
|
909
|
+
archiveSourceBotUsername,
|
|
854
910
|
});
|
|
855
911
|
createdComment = await createThreadComment({
|
|
856
912
|
siteBaseURL: runtime.baseURL,
|
|
@@ -88,6 +88,10 @@ import {
|
|
|
88
88
|
import {
|
|
89
89
|
resolveCodexRawTextProcessResult,
|
|
90
90
|
} from "./local-ai-adapters.mjs";
|
|
91
|
+
import {
|
|
92
|
+
buildRunnerRouteDuplicateStateFromComment,
|
|
93
|
+
buildRunnerRouteStateFromComment,
|
|
94
|
+
} from "./runner-helpers.mjs";
|
|
91
95
|
import {
|
|
92
96
|
buildRunnerInheritedRootReferenceRecorderResult,
|
|
93
97
|
buildRunnerRootThreadRecorderFailure,
|
|
@@ -17735,6 +17739,17 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
17735
17739
|
}
|
|
17736
17740
|
|
|
17737
17741
|
try {
|
|
17742
|
+
const foreignSkipState = buildRunnerRouteStateFromComment({
|
|
17743
|
+
id: "comment-foreign-skip-1",
|
|
17744
|
+
createdAt: "2026-04-01T00:00:00.000Z",
|
|
17745
|
+
parsedArchive: {
|
|
17746
|
+
kind: "telegram_message",
|
|
17747
|
+
messageID: 1178,
|
|
17748
|
+
},
|
|
17749
|
+
}, {
|
|
17750
|
+
last_action: "trigger_skipped",
|
|
17751
|
+
last_reason: "group message mentioned a different bot",
|
|
17752
|
+
});
|
|
17738
17753
|
const acceptedRoutePatch = buildRunnerAcceptedExecutionRoutePatch({
|
|
17739
17754
|
activeExecutionPatch: {
|
|
17740
17755
|
active_comment_id: "comment-active-handoff-1",
|
|
@@ -17747,6 +17762,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
17747
17762
|
root_work_item_title: "Runner Accepted Handoff",
|
|
17748
17763
|
root_work_item_status: "doing",
|
|
17749
17764
|
},
|
|
17765
|
+
dedupeStatePatch: buildRunnerRouteDuplicateStateFromComment({
|
|
17766
|
+
id: "comment-active-handoff-1",
|
|
17767
|
+
parsedArchive: {
|
|
17768
|
+
kind: "telegram_message",
|
|
17769
|
+
messageID: 1180,
|
|
17770
|
+
},
|
|
17771
|
+
}),
|
|
17750
17772
|
visibilityStatePatch: {
|
|
17751
17773
|
last_visibility_mode: "reply",
|
|
17752
17774
|
last_visibility_status: "visible",
|
|
@@ -17757,11 +17779,15 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
17757
17779
|
String(acceptedRoutePatch.active_comment_id || "") === "comment-active-handoff-1"
|
|
17758
17780
|
&& String(acceptedRoutePatch.last_request_key || "") === "request-active-handoff-1"
|
|
17759
17781
|
&& String(acceptedRoutePatch.last_root_work_item_title || "") === "Runner Accepted Handoff"
|
|
17782
|
+
&& String(acceptedRoutePatch.last_dedupe_comment_id || "") === "comment-active-handoff-1"
|
|
17783
|
+
&& String(acceptedRoutePatch.last_dedupe_source_kind || "") === "telegram_message"
|
|
17784
|
+
&& Number(acceptedRoutePatch.last_dedupe_source_message_id || 0) === 1180
|
|
17785
|
+
&& String(foreignSkipState.last_dedupe_comment_id || "") === ""
|
|
17760
17786
|
&& String(acceptedRoutePatch.last_visibility_status || "") === "visible"
|
|
17761
17787
|
&& !Object.prototype.hasOwnProperty.call(acceptedRoutePatch, "text")
|
|
17762
17788
|
&& !Object.prototype.hasOwnProperty.call(acceptedRoutePatch, "archiveConversation")
|
|
17763
17789
|
&& !Object.prototype.hasOwnProperty.call(acceptedRoutePatch, "delivery"),
|
|
17764
|
-
`request=${String(acceptedRoutePatch.last_request_key || "(none)")} root=${String(acceptedRoutePatch.last_root_work_item_id || "(none)")} visibility=${String(acceptedRoutePatch.last_visibility_status || "(none)")}`,
|
|
17790
|
+
`request=${String(acceptedRoutePatch.last_request_key || "(none)")} root=${String(acceptedRoutePatch.last_root_work_item_id || "(none)")} dedupe=${String(acceptedRoutePatch.last_dedupe_source_message_id || "(none)")} foreign_skip_dedupe=${String(foreignSkipState.last_dedupe_comment_id || "(none)")} visibility=${String(acceptedRoutePatch.last_visibility_status || "(none)")}`,
|
|
17765
17791
|
);
|
|
17766
17792
|
} catch (err) {
|
|
17767
17793
|
push("runner_recorder_active_execution_handoff_stays_within_module_5_boundary", false, String(err?.message || err));
|
|
@@ -19691,6 +19717,66 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
19691
19717
|
push("runner_entrypoint_pending_selection_stays_within_module_4_7_boundary", false, String(err?.message || err));
|
|
19692
19718
|
}
|
|
19693
19719
|
|
|
19720
|
+
try {
|
|
19721
|
+
const foreignArchiveComment = {
|
|
19722
|
+
id: "comment-foreign-owner-1",
|
|
19723
|
+
createdAt: "2026-03-30T00:00:00.000Z",
|
|
19724
|
+
updatedAt: "2026-03-30T00:00:00.000Z",
|
|
19725
|
+
body: "@woobn_bot hi",
|
|
19726
|
+
parsedArchive: {
|
|
19727
|
+
kind: "telegram_message",
|
|
19728
|
+
messageID: 280,
|
|
19729
|
+
chatID: "-100999",
|
|
19730
|
+
chatType: "supergroup",
|
|
19731
|
+
sender: "tester",
|
|
19732
|
+
senderIsBot: false,
|
|
19733
|
+
body: "@woobn_bot hi",
|
|
19734
|
+
sourceBotUsername: "woobn_bot",
|
|
19735
|
+
},
|
|
19736
|
+
};
|
|
19737
|
+
const pendingWork = selectRunnerPendingWorkEntrypoint({
|
|
19738
|
+
comments: [foreignArchiveComment],
|
|
19739
|
+
importOutcome: {
|
|
19740
|
+
importedCommentIDs: [],
|
|
19741
|
+
importedComments: [],
|
|
19742
|
+
currentPollLocalInboundReceipts: [
|
|
19743
|
+
{
|
|
19744
|
+
chat_id: "-100999",
|
|
19745
|
+
message_id: 280,
|
|
19746
|
+
body: "@RyoAI3_bot 하이",
|
|
19747
|
+
receipt_bot_username: "ryoai3_bot",
|
|
19748
|
+
occurred_at: "2026-03-31T00:10:00.000Z",
|
|
19749
|
+
},
|
|
19750
|
+
],
|
|
19751
|
+
},
|
|
19752
|
+
refreshedState: {
|
|
19753
|
+
last_processed_comment_id: "comment-foreign-owner-1",
|
|
19754
|
+
last_processed_created_at: "2026-03-30T00:00:00.000Z",
|
|
19755
|
+
},
|
|
19756
|
+
mode: "continue",
|
|
19757
|
+
parseArchivedChatComment,
|
|
19758
|
+
deps: {
|
|
19759
|
+
normalizeArchiveCommentRecord: (record) => ({
|
|
19760
|
+
id: String(record?.id || "").trim(),
|
|
19761
|
+
body: String(record?.body || "").trim(),
|
|
19762
|
+
createdAt: String(record?.created_at || record?.createdAt || record?.updated_at || record?.updatedAt || "").trim(),
|
|
19763
|
+
updatedAt: String(record?.updated_at || record?.updatedAt || "").trim(),
|
|
19764
|
+
parsedArchive: safeObject(record?.parsedArchive),
|
|
19765
|
+
}),
|
|
19766
|
+
applyPendingAgeSelection: (selection) => selection,
|
|
19767
|
+
},
|
|
19768
|
+
pendingSelectionOptions: {},
|
|
19769
|
+
});
|
|
19770
|
+
push(
|
|
19771
|
+
"runner_entrypoint_receipt_replay_skips_foreign_bot_archive_collision",
|
|
19772
|
+
ensureArray(pendingWork.receiptBackedPending).length === 0
|
|
19773
|
+
&& ensureArray(safeObject(pendingWork.pending).pending).length === 0,
|
|
19774
|
+
`replay=${String(ensureArray(pendingWork.receiptBackedPending).length)} pending=${String(ensureArray(safeObject(pendingWork.pending).pending).length)}`,
|
|
19775
|
+
);
|
|
19776
|
+
} catch (err) {
|
|
19777
|
+
push("runner_entrypoint_receipt_replay_skips_foreign_bot_archive_collision", false, String(err?.message || err));
|
|
19778
|
+
}
|
|
19779
|
+
|
|
19694
19780
|
try {
|
|
19695
19781
|
const deliveryContext = await prepareLocalBotDeliveryContext({
|
|
19696
19782
|
siteBaseURL: "https://example.test",
|
|
@@ -923,10 +923,10 @@ export async function runSelftestTelegramE2E(push, deps) {
|
|
|
923
923
|
`envelope=${JSON.stringify(progressEnvelope)}`,
|
|
924
924
|
);
|
|
925
925
|
push(
|
|
926
|
-
"
|
|
926
|
+
"telegram_archive_comment_preserves_route_ownership_fields_for_explicit_targeting",
|
|
927
927
|
progressCommentBody.includes("archive_source_origin: telegram_archive_context")
|
|
928
|
-
&&
|
|
929
|
-
&&
|
|
928
|
+
&& progressCommentBody.includes("archive_source_route_key:")
|
|
929
|
+
&& progressCommentBody.includes("archive_source_bot_username:"),
|
|
930
930
|
`body=${progressCommentBody}`,
|
|
931
931
|
);
|
|
932
932
|
const progressParsedArchive = parseArchivedChatComment(progressCommentBody);
|
|
@@ -1657,6 +1657,93 @@ export async function runSelftestTelegramE2E(push, deps) {
|
|
|
1657
1657
|
&& ownershipBodies.length === 0,
|
|
1658
1658
|
`owner=${JSON.stringify(ryoai2Receipt)} foreign=${JSON.stringify(ryoai3Receipt)} comments=${ownershipBodies.join(" || ")}`,
|
|
1659
1659
|
);
|
|
1660
|
+
|
|
1661
|
+
telegramE2EServer.state.comments = [
|
|
1662
|
+
{
|
|
1663
|
+
id: "foreign-owner-archive-280",
|
|
1664
|
+
body: buildRunnerRuntimeDeps().formatTelegramInboundArchiveComment({
|
|
1665
|
+
eventName: "telegram.message.created",
|
|
1666
|
+
chatID: e2eDestination.chat_id,
|
|
1667
|
+
chatType: "supergroup",
|
|
1668
|
+
messageID: 280,
|
|
1669
|
+
fromID: "7001",
|
|
1670
|
+
fromName: "Operator",
|
|
1671
|
+
fromUsername: "operator_user",
|
|
1672
|
+
fromIsBot: false,
|
|
1673
|
+
mentionUsernames: ["woobn_bot"],
|
|
1674
|
+
text: "@WooBN_bot 하이",
|
|
1675
|
+
occurredAt: "2026-03-31T00:00:00.000Z",
|
|
1676
|
+
archiveSourceOrigin: "telegram_archive_context",
|
|
1677
|
+
archiveSourceRouteKey: "telegram-monitor-woobn-bot::project::telegram::monitor::dest::actor",
|
|
1678
|
+
archiveSourceBotUsername: "woobn_bot",
|
|
1679
|
+
}),
|
|
1680
|
+
created_at: "2026-03-31T00:00:01.000Z",
|
|
1681
|
+
updated_at: "2026-03-31T00:00:01.000Z",
|
|
1682
|
+
author_user_id: e2eActorUserID,
|
|
1683
|
+
},
|
|
1684
|
+
];
|
|
1685
|
+
telegramE2EServer.state.updates = [
|
|
1686
|
+
{
|
|
1687
|
+
update_id: 404,
|
|
1688
|
+
message: {
|
|
1689
|
+
message_id: 280,
|
|
1690
|
+
date: Math.floor(Date.now() / 1000),
|
|
1691
|
+
chat: {
|
|
1692
|
+
id: Number(e2eDestination.chat_id),
|
|
1693
|
+
type: "supergroup",
|
|
1694
|
+
title: e2eDestination.label,
|
|
1695
|
+
},
|
|
1696
|
+
from: {
|
|
1697
|
+
id: 7002,
|
|
1698
|
+
is_bot: false,
|
|
1699
|
+
first_name: "Operator",
|
|
1700
|
+
username: "operator_user",
|
|
1701
|
+
},
|
|
1702
|
+
text: "@RyoAI3_bot 하이",
|
|
1703
|
+
entities: buildTelegramMentionEntities("@RyoAI3_bot 하이"),
|
|
1704
|
+
},
|
|
1705
|
+
},
|
|
1706
|
+
];
|
|
1707
|
+
await archiveLocalTelegramMessagesForRoute({
|
|
1708
|
+
routeKey: routeRyoai3Key,
|
|
1709
|
+
route: routeRyoai3,
|
|
1710
|
+
routeState: {},
|
|
1711
|
+
runtime: {
|
|
1712
|
+
baseURL: telegramE2EServer.baseURL,
|
|
1713
|
+
timeoutSeconds: 10,
|
|
1714
|
+
token: e2eToken,
|
|
1715
|
+
actor: {
|
|
1716
|
+
user_id: e2eActorUserID,
|
|
1717
|
+
},
|
|
1718
|
+
},
|
|
1719
|
+
bot: {
|
|
1720
|
+
id: "88888888-8888-4888-8888-888888888883",
|
|
1721
|
+
name: "RyoAI3_bot",
|
|
1722
|
+
username: "RyoAI3_bot",
|
|
1723
|
+
role: "monitor",
|
|
1724
|
+
},
|
|
1725
|
+
destination: {
|
|
1726
|
+
chatID: e2eDestination.chat_id,
|
|
1727
|
+
},
|
|
1728
|
+
archiveThread: {
|
|
1729
|
+
threadID: e2eThreadID,
|
|
1730
|
+
},
|
|
1731
|
+
managedConversationBots: [
|
|
1732
|
+
{
|
|
1733
|
+
username: "RyoAI3_bot",
|
|
1734
|
+
route: routeRyoai3,
|
|
1735
|
+
bot: { username: "RyoAI3_bot", name: "RyoAI3_bot" },
|
|
1736
|
+
},
|
|
1737
|
+
],
|
|
1738
|
+
deps: buildRunnerRuntimeDeps(),
|
|
1739
|
+
});
|
|
1740
|
+
const foreignCollisionBodies = telegramE2EServer.state.comments.map((item) => String(item.body || ""));
|
|
1741
|
+
push(
|
|
1742
|
+
"telegram_explicit_self_mention_reimports_when_same_message_id_exists_for_foreign_bot_archive",
|
|
1743
|
+
foreignCollisionBodies.filter((item) => item.includes("message_id: 280")).length === 2
|
|
1744
|
+
&& foreignCollisionBodies.some((item) => item.includes("archive_source_bot_username: @ryoai3_bot")),
|
|
1745
|
+
`count=${foreignCollisionBodies.filter((item) => item.includes("message_id: 280")).length} bodies=${foreignCollisionBodies.join(" || ")}`,
|
|
1746
|
+
);
|
|
1660
1747
|
} catch (err) {
|
|
1661
1748
|
push("telegram_runner_e2e_local_mock", false, String(err?.message || err));
|
|
1662
1749
|
} finally {
|