metheus-governance-mcp-cli 0.2.277 → 0.2.279
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 +108 -55
- package/lib/runner-helpers.mjs +160 -0
- package/lib/runner-orchestration-entrypoints.mjs +20 -0
- package/lib/runner-orchestration-selected-record-contract-context.mjs +113 -2
- package/lib/runner-runtime.mjs +24 -14
- package/lib/selftest-runner-scenarios.mjs +337 -21
- package/lib/selftest-telegram-e2e.mjs +117 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -135,6 +135,7 @@ import {
|
|
|
135
135
|
} from "./lib/client-registration.mjs";
|
|
136
136
|
import {
|
|
137
137
|
applyPendingAgeSelection,
|
|
138
|
+
buildCanonicalHumanInboundKey as buildRunnerCanonicalHumanInboundKey,
|
|
138
139
|
buildTelegramBotReplyEnvelope,
|
|
139
140
|
buildTelegramMessageEnvelopeFromParsedArchive as buildRunnerTelegramMessageEnvelopeFromParsedArchive,
|
|
140
141
|
buildRunnerRouteDuplicateStateFromComment,
|
|
@@ -3522,30 +3523,35 @@ function uniqueOrderedStrings(values, normalizer = (value) => String(value || ""
|
|
|
3522
3523
|
return output;
|
|
3523
3524
|
}
|
|
3524
3525
|
|
|
3525
|
-
function buildRunnerRequestKey({
|
|
3526
|
-
normalizedRoute,
|
|
3527
|
-
selectedRecord,
|
|
3528
|
-
selectedBotUsernames = [],
|
|
3529
|
-
normalizedIntent = "",
|
|
3530
|
-
conversationID = "",
|
|
3531
|
-
}) {
|
|
3532
|
-
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
3533
|
-
const chatID = String(parsed.chatID || parsed.chatId || "").trim() || "-";
|
|
3534
|
-
const messageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
3535
|
-
const kind = String(parsed.kind || "").trim().toLowerCase() || "-";
|
|
3536
|
-
const
|
|
3537
|
-
const
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3526
|
+
function buildRunnerRequestKey({
|
|
3527
|
+
normalizedRoute,
|
|
3528
|
+
selectedRecord,
|
|
3529
|
+
selectedBotUsernames = [],
|
|
3530
|
+
normalizedIntent = "",
|
|
3531
|
+
conversationID = "",
|
|
3532
|
+
}) {
|
|
3533
|
+
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
3534
|
+
const chatID = String(parsed.chatID || parsed.chatId || "").trim() || "-";
|
|
3535
|
+
const messageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
3536
|
+
const kind = String(parsed.kind || "").trim().toLowerCase() || "-";
|
|
3537
|
+
const canonicalHumanKey = buildRunnerCanonicalHumanInboundKey(parsed);
|
|
3538
|
+
const responders = canonicalHumanKey
|
|
3539
|
+
? "-"
|
|
3540
|
+
: uniqueOrderedStrings(selectedBotUsernames, normalizeTelegramMentionUsername).join(",") || "-";
|
|
3541
|
+
const intent = canonicalHumanKey
|
|
3542
|
+
? "-"
|
|
3543
|
+
: String(normalizedIntent || "").trim().toLowerCase() || "-";
|
|
3544
|
+
const resolvedConversationID = String(conversationID || parsed.conversationID || "").trim() || "-";
|
|
3545
|
+
return [
|
|
3546
|
+
String(normalizedRoute?.projectID || "").trim() || "-",
|
|
3547
|
+
String(normalizedRoute?.provider || "").trim() || "-",
|
|
3548
|
+
chatID,
|
|
3549
|
+
canonicalHumanKey ? `human:${canonicalHumanKey}` : String(messageID || "-"),
|
|
3550
|
+
kind,
|
|
3551
|
+
responders,
|
|
3552
|
+
intent,
|
|
3553
|
+
resolvedConversationID,
|
|
3554
|
+
].join("::");
|
|
3549
3555
|
}
|
|
3550
3556
|
|
|
3551
3557
|
function resolveRunnerRequestClaimIntent({
|
|
@@ -3614,15 +3620,19 @@ function buildSyntheticReplyChainConversationID(normalizedRoute, chatID, anchorM
|
|
|
3614
3620
|
return `reply_chain:${provider}:${normalizedChatID}:${normalizedAnchorMessageID}`;
|
|
3615
3621
|
}
|
|
3616
3622
|
|
|
3617
|
-
function buildSyntheticHumanOpeningConversationID(normalizedRoute, chatID, messageID) {
|
|
3618
|
-
const provider = String(normalizedRoute?.provider || "").trim() || "unknown";
|
|
3619
|
-
const
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3623
|
+
function buildSyntheticHumanOpeningConversationID(normalizedRoute, chatID, messageID, canonicalHumanMessageKey = "") {
|
|
3624
|
+
const provider = String(normalizedRoute?.provider || "").trim() || "unknown";
|
|
3625
|
+
const canonicalKey = String(canonicalHumanMessageKey || "").trim();
|
|
3626
|
+
if (canonicalKey) {
|
|
3627
|
+
return `human_opening:${provider}:${canonicalKey}`;
|
|
3628
|
+
}
|
|
3629
|
+
const normalizedChatID = String(chatID || "").trim() || "-";
|
|
3630
|
+
const normalizedMessageID = intFromRawAllowZero(messageID, 0);
|
|
3631
|
+
if (normalizedMessageID <= 0) {
|
|
3632
|
+
return "";
|
|
3633
|
+
}
|
|
3634
|
+
return `human_opening:${provider}:${normalizedChatID}:${normalizedMessageID}`;
|
|
3635
|
+
}
|
|
3626
3636
|
|
|
3627
3637
|
function findRunnerRequestsForScope(state, normalizedRoute, selectors = {}) {
|
|
3628
3638
|
const requests = normalizeBotRunnerRequests(state?.requests);
|
|
@@ -4910,24 +4920,26 @@ function resolveRunnerHumanCommentAuthorityContext({
|
|
|
4910
4920
|
const referenced = safeObject(referencedRequest);
|
|
4911
4921
|
const sharedSource = safeObject(sharedConversationSource);
|
|
4912
4922
|
const sharedIntent = safeObject(normalizedSharedHumanIntent);
|
|
4913
|
-
const authoritySource = Object.keys(referenced).length
|
|
4914
|
-
? referenced
|
|
4915
|
-
: Object.keys(sharedSource).length
|
|
4916
|
-
? sharedSource
|
|
4917
|
-
: {};
|
|
4918
|
-
const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
4919
|
-
const
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4923
|
+
const authoritySource = Object.keys(referenced).length
|
|
4924
|
+
? referenced
|
|
4925
|
+
: Object.keys(sharedSource).length
|
|
4926
|
+
? sharedSource
|
|
4927
|
+
: {};
|
|
4928
|
+
const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
4929
|
+
const canonicalHumanMessageKey = buildRunnerCanonicalHumanInboundKey(parsed);
|
|
4930
|
+
const resolvedConversationID = firstNonEmptyString([
|
|
4931
|
+
parsed.conversationID,
|
|
4932
|
+
replyChain.conversationID,
|
|
4933
|
+
authoritySource.conversation_id,
|
|
4934
|
+
Object.keys(sharedIntent).length > 0
|
|
4935
|
+
? buildSyntheticHumanOpeningConversationID(
|
|
4936
|
+
normalizedRoute,
|
|
4937
|
+
String(parsed.chatID || parsed.chatId || "").trim(),
|
|
4938
|
+
currentMessageID,
|
|
4939
|
+
canonicalHumanMessageKey,
|
|
4940
|
+
)
|
|
4941
|
+
: "",
|
|
4942
|
+
]);
|
|
4931
4943
|
const explicitSelectedBotUsernames = uniqueOrderedStrings(
|
|
4932
4944
|
selectedBotUsernames,
|
|
4933
4945
|
normalizeTelegramMentionUsername,
|
|
@@ -8139,6 +8151,16 @@ function buildTelegramArchiveStructuredPayload(normalized) {
|
|
|
8139
8151
|
.map((value) => normalizeTelegramMentionUsername(value))
|
|
8140
8152
|
.filter(Boolean),
|
|
8141
8153
|
occurredAt: String(normalized?.occurredAt || "").trim(),
|
|
8154
|
+
canonicalHumanMessageKey: buildRunnerCanonicalHumanInboundKey({
|
|
8155
|
+
chatID: normalized?.chatID,
|
|
8156
|
+
kind,
|
|
8157
|
+
senderID: normalized?.fromID,
|
|
8158
|
+
senderIsBot: normalized?.fromIsBot === true,
|
|
8159
|
+
occurredAt: normalized?.occurredAt,
|
|
8160
|
+
messageThreadID: normalized?.messageThreadID,
|
|
8161
|
+
replyToMessageID: normalized?.replyToMessageID,
|
|
8162
|
+
body: normalized?.text,
|
|
8163
|
+
}),
|
|
8142
8164
|
sourceOrigin: String(normalized?.archiveSourceOrigin || normalized?.sourceOrigin || "").trim().toLowerCase(),
|
|
8143
8165
|
sourceRouteKey: String(normalized?.archiveSourceRouteKey || normalized?.sourceRouteKey || "").trim(),
|
|
8144
8166
|
sourceBotUsername: normalizeTelegramMentionUsername(normalized?.archiveSourceBotUsername || normalized?.sourceBotUsername || ""),
|
|
@@ -8275,6 +8297,10 @@ function parseArchivedChatComment(rawBody) {
|
|
|
8275
8297
|
.map((value) => normalizeTelegramMentionUsername(value))
|
|
8276
8298
|
.filter(Boolean),
|
|
8277
8299
|
occurredAt: firstNonEmptyString([safeObject(structuredPayload).occurredAt, metadata.occurred_at]),
|
|
8300
|
+
canonicalHumanMessageKey: firstNonEmptyString([
|
|
8301
|
+
safeObject(structuredPayload).canonicalHumanMessageKey,
|
|
8302
|
+
metadata.canonical_human_message_key,
|
|
8303
|
+
]),
|
|
8278
8304
|
sourceOrigin: firstNonEmptyString([safeObject(structuredPayload).sourceOrigin, metadata.archive_source_origin, metadata.source_origin]).toLowerCase(),
|
|
8279
8305
|
sourceRouteKey: firstNonEmptyString([safeObject(structuredPayload).sourceRouteKey, metadata.archive_source_route_key, metadata.source_route_key]),
|
|
8280
8306
|
sourceBotUsername: normalizeTelegramMentionUsername(
|
|
@@ -8609,9 +8635,35 @@ function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
|
8609
8635
|
};
|
|
8610
8636
|
}
|
|
8611
8637
|
|
|
8612
|
-
function buildArchivedInboundMessageKey(
|
|
8613
|
-
const
|
|
8614
|
-
|
|
8638
|
+
function buildArchivedInboundMessageKey(chatIDOrEnvelope, messageID = undefined, sourceBotUsername = "") {
|
|
8639
|
+
const rawEnvelope = messageID === undefined && typeof chatIDOrEnvelope === "object" && chatIDOrEnvelope !== null
|
|
8640
|
+
? safeObject(chatIDOrEnvelope)
|
|
8641
|
+
: {
|
|
8642
|
+
chatID: chatIDOrEnvelope,
|
|
8643
|
+
messageID,
|
|
8644
|
+
sourceBotUsername,
|
|
8645
|
+
};
|
|
8646
|
+
const canonicalHumanKey = buildRunnerCanonicalHumanInboundKey(rawEnvelope);
|
|
8647
|
+
if (canonicalHumanKey) {
|
|
8648
|
+
return `human:${canonicalHumanKey}`;
|
|
8649
|
+
}
|
|
8650
|
+
const chatID = String(
|
|
8651
|
+
rawEnvelope.chatID
|
|
8652
|
+
|| rawEnvelope.chatId
|
|
8653
|
+
|| rawEnvelope.chat_id
|
|
8654
|
+
|| "",
|
|
8655
|
+
).trim();
|
|
8656
|
+
const normalizedMessageID = intFromRawAllowZero(
|
|
8657
|
+
rawEnvelope.messageID
|
|
8658
|
+
|| rawEnvelope.message_id,
|
|
8659
|
+
0,
|
|
8660
|
+
);
|
|
8661
|
+
const baseKey = `${chatID}:${normalizedMessageID}`;
|
|
8662
|
+
const normalizedBotUsername = normalizeTelegramMentionUsername(
|
|
8663
|
+
rawEnvelope.sourceBotUsername
|
|
8664
|
+
|| rawEnvelope.source_bot_username
|
|
8665
|
+
|| sourceBotUsername,
|
|
8666
|
+
);
|
|
8615
8667
|
return normalizedBotUsername ? `${baseKey}::${normalizedBotUsername}` : baseKey;
|
|
8616
8668
|
}
|
|
8617
8669
|
|
|
@@ -17931,7 +17983,8 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
17931
17983
|
saveRunnerRouteState,
|
|
17932
17984
|
mergeServerRunnerRequestLedgerIntoLocalState,
|
|
17933
17985
|
buildRunnerStatusQueryLookup,
|
|
17934
|
-
|
|
17986
|
+
buildArchivedInboundMessageKey,
|
|
17987
|
+
tryJsonParse,
|
|
17935
17988
|
safeObject,
|
|
17936
17989
|
normalizeRunnerTriggerPolicy,
|
|
17937
17990
|
evaluateTelegramRunnerTrigger,
|
package/lib/runner-helpers.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
|
|
3
4
|
function safeObject(value) {
|
|
4
5
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -11,6 +12,24 @@ function ensureArray(value) {
|
|
|
11
12
|
return Array.isArray(value) ? value : [];
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
function normalizeBodyForCanonicalKey(value) {
|
|
16
|
+
return String(value || "")
|
|
17
|
+
.replace(/\s+/g, " ")
|
|
18
|
+
.trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeOccurredAtForCanonicalKey(value) {
|
|
22
|
+
const text = String(value || "").trim();
|
|
23
|
+
if (!text) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
const parsed = Date.parse(text);
|
|
27
|
+
if (!Number.isFinite(parsed)) {
|
|
28
|
+
return text;
|
|
29
|
+
}
|
|
30
|
+
return new Date(Math.floor(parsed / 1000) * 1000).toISOString();
|
|
31
|
+
}
|
|
32
|
+
|
|
14
33
|
function firstNonEmptyString(values) {
|
|
15
34
|
for (const value of ensureArray(values)) {
|
|
16
35
|
const text = String(value ?? "").trim();
|
|
@@ -31,6 +50,93 @@ function intFromRaw(raw, fallback) {
|
|
|
31
50
|
return parsed > 0 ? parsed : fallback;
|
|
32
51
|
}
|
|
33
52
|
|
|
53
|
+
function buildCanonicalHumanInboundSource(rawValue) {
|
|
54
|
+
const value = safeObject(rawValue);
|
|
55
|
+
const kind = String(value.kind || "").trim().toLowerCase();
|
|
56
|
+
const senderIsBotRaw = value.sender_is_bot
|
|
57
|
+
?? value.senderIsBot
|
|
58
|
+
?? value.fromIsBot
|
|
59
|
+
?? value.from_is_bot;
|
|
60
|
+
const senderIsBot = senderIsBotRaw === true
|
|
61
|
+
|| senderIsBotRaw === "true"
|
|
62
|
+
|| senderIsBotRaw === 1
|
|
63
|
+
|| senderIsBotRaw === "1"
|
|
64
|
+
|| kind === "bot_reply";
|
|
65
|
+
if (senderIsBot) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const chatID = String(
|
|
69
|
+
value.chat_id
|
|
70
|
+
|| value.chatID
|
|
71
|
+
|| value.chatId
|
|
72
|
+
|| "",
|
|
73
|
+
).trim();
|
|
74
|
+
const senderID = String(
|
|
75
|
+
value.sender_id
|
|
76
|
+
|| value.senderID
|
|
77
|
+
|| value.fromID
|
|
78
|
+
|| value.from_id
|
|
79
|
+
|| value.user_id
|
|
80
|
+
|| value.userID
|
|
81
|
+
|| value.sender_username
|
|
82
|
+
|| value.senderUsername
|
|
83
|
+
|| value.fromUsername
|
|
84
|
+
|| value.from_username
|
|
85
|
+
|| value.username
|
|
86
|
+
|| "",
|
|
87
|
+
).trim().toLowerCase();
|
|
88
|
+
const occurredAt = String(
|
|
89
|
+
value.occurred_at
|
|
90
|
+
|| value.occurredAt
|
|
91
|
+
|| "",
|
|
92
|
+
).trim();
|
|
93
|
+
const body = normalizeBodyForCanonicalKey(
|
|
94
|
+
value.body
|
|
95
|
+
|| value.text
|
|
96
|
+
|| "",
|
|
97
|
+
);
|
|
98
|
+
if (!chatID || !senderID || !occurredAt || !body) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const messageThreadID = intFromRawAllowZero(
|
|
102
|
+
value.message_thread_id
|
|
103
|
+
|| value.messageThreadID,
|
|
104
|
+
0,
|
|
105
|
+
);
|
|
106
|
+
const replyToMessageID = intFromRawAllowZero(
|
|
107
|
+
value.reply_to_message_id
|
|
108
|
+
|| value.replyToMessageID,
|
|
109
|
+
0,
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
chatID,
|
|
113
|
+
senderID,
|
|
114
|
+
occurredAt: normalizeOccurredAtForCanonicalKey(occurredAt),
|
|
115
|
+
body,
|
|
116
|
+
messageThreadID: messageThreadID > 0 ? messageThreadID : 0,
|
|
117
|
+
replyToMessageID: replyToMessageID > 0 ? replyToMessageID : 0,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function buildCanonicalHumanInboundKey(rawValue) {
|
|
122
|
+
const value = safeObject(rawValue);
|
|
123
|
+
const explicitKey = String(
|
|
124
|
+
value.canonical_human_message_key
|
|
125
|
+
|| value.canonicalHumanMessageKey
|
|
126
|
+
|| "",
|
|
127
|
+
).trim();
|
|
128
|
+
if (explicitKey) {
|
|
129
|
+
return explicitKey;
|
|
130
|
+
}
|
|
131
|
+
const source = buildCanonicalHumanInboundSource(value);
|
|
132
|
+
if (!source) {
|
|
133
|
+
return "";
|
|
134
|
+
}
|
|
135
|
+
return createHash("sha1")
|
|
136
|
+
.update(JSON.stringify(source))
|
|
137
|
+
.digest("hex");
|
|
138
|
+
}
|
|
139
|
+
|
|
34
140
|
export function normalizeTelegramMessageEnvelope(rawEnvelope) {
|
|
35
141
|
const envelope = safeObject(rawEnvelope);
|
|
36
142
|
const chatID = String(
|
|
@@ -61,6 +167,13 @@ export function normalizeTelegramMessageEnvelope(rawEnvelope) {
|
|
|
61
167
|
envelope.sender,
|
|
62
168
|
senderUsername ? `@${senderUsername}` : "",
|
|
63
169
|
]);
|
|
170
|
+
const senderID = String(
|
|
171
|
+
envelope.sender_id
|
|
172
|
+
|| envelope.senderID
|
|
173
|
+
|| envelope.fromID
|
|
174
|
+
|| envelope.from_id
|
|
175
|
+
|| "",
|
|
176
|
+
).trim();
|
|
64
177
|
const rawSenderIsBot = envelope.sender_is_bot ?? envelope.senderIsBot;
|
|
65
178
|
const senderIsBot = rawSenderIsBot === true
|
|
66
179
|
|| rawSenderIsBot === "true"
|
|
@@ -68,6 +181,15 @@ export function normalizeTelegramMessageEnvelope(rawEnvelope) {
|
|
|
68
181
|
|| rawSenderIsBot === "1"
|
|
69
182
|
|| kind === "bot_reply";
|
|
70
183
|
const body = String(envelope.body || envelope.text || "").trim();
|
|
184
|
+
const occurredAt = String(
|
|
185
|
+
envelope.occurred_at
|
|
186
|
+
|| envelope.occurredAt
|
|
187
|
+
|| "",
|
|
188
|
+
).trim();
|
|
189
|
+
const mentionUsernames = uniqueNormalizedSelectors(
|
|
190
|
+
envelope.mention_usernames
|
|
191
|
+
|| envelope.mentionUsernames,
|
|
192
|
+
);
|
|
71
193
|
const sourceOrigin = String(
|
|
72
194
|
envelope.source_origin
|
|
73
195
|
|| envelope.sourceOrigin
|
|
@@ -90,14 +212,29 @@ export function normalizeTelegramMessageEnvelope(rawEnvelope) {
|
|
|
90
212
|
&& !(replyToMessageID > 0)
|
|
91
213
|
&& !kind
|
|
92
214
|
&& !sender
|
|
215
|
+
&& !senderID
|
|
93
216
|
&& !senderUsername
|
|
94
217
|
&& !body
|
|
218
|
+
&& !occurredAt
|
|
219
|
+
&& mentionUsernames.length === 0
|
|
95
220
|
&& !sourceOrigin
|
|
96
221
|
&& !sourceRouteKey
|
|
97
222
|
&& !sourceBotUsername
|
|
98
223
|
) {
|
|
99
224
|
return {};
|
|
100
225
|
}
|
|
226
|
+
const canonicalHumanMessageKey = buildCanonicalHumanInboundKey({
|
|
227
|
+
chat_id: chatID,
|
|
228
|
+
message_id: messageID,
|
|
229
|
+
message_thread_id: messageThreadID,
|
|
230
|
+
reply_to_message_id: replyToMessageID,
|
|
231
|
+
kind,
|
|
232
|
+
sender_id: senderID,
|
|
233
|
+
sender_username: senderUsername,
|
|
234
|
+
sender_is_bot: senderIsBot === true,
|
|
235
|
+
body,
|
|
236
|
+
occurred_at: occurredAt,
|
|
237
|
+
});
|
|
101
238
|
return {
|
|
102
239
|
...(chatID ? { chat_id: chatID } : {}),
|
|
103
240
|
...(messageID > 0 ? { message_id: messageID } : {}),
|
|
@@ -105,9 +242,13 @@ export function normalizeTelegramMessageEnvelope(rawEnvelope) {
|
|
|
105
242
|
...(replyToMessageID > 0 ? { reply_to_message_id: replyToMessageID } : {}),
|
|
106
243
|
...(kind ? { kind } : {}),
|
|
107
244
|
...(sender ? { sender } : {}),
|
|
245
|
+
...(senderID ? { sender_id: senderID } : {}),
|
|
108
246
|
...(senderUsername ? { sender_username: senderUsername } : {}),
|
|
109
247
|
sender_is_bot: senderIsBot === true,
|
|
110
248
|
...(body ? { body } : {}),
|
|
249
|
+
...(occurredAt ? { occurred_at: occurredAt } : {}),
|
|
250
|
+
...(mentionUsernames.length > 0 ? { mention_usernames: mentionUsernames } : {}),
|
|
251
|
+
...(canonicalHumanMessageKey ? { canonical_human_message_key: canonicalHumanMessageKey } : {}),
|
|
111
252
|
...(sourceOrigin ? { source_origin: sourceOrigin } : {}),
|
|
112
253
|
...(sourceRouteKey ? { source_route_key: sourceRouteKey } : {}),
|
|
113
254
|
...(sourceBotUsername ? { source_bot_username: sourceBotUsername } : {}),
|
|
@@ -126,6 +267,10 @@ export function buildTelegramMessageEnvelopeFromParsedArchive(parsedArchiveRaw,
|
|
|
126
267
|
message_thread_id: overrides.message_thread_id ?? parsedArchive.messageThreadID,
|
|
127
268
|
reply_to_message_id: overrides.reply_to_message_id ?? parsedArchive.replyToMessageID,
|
|
128
269
|
kind: firstNonEmptyString([overrides.kind, parsedArchive.kind]),
|
|
270
|
+
sender_id: firstNonEmptyString([
|
|
271
|
+
overrides.sender_id,
|
|
272
|
+
parsedArchive.senderID,
|
|
273
|
+
]),
|
|
129
274
|
sender: firstNonEmptyString([
|
|
130
275
|
overrides.sender,
|
|
131
276
|
parsedArchive.sender,
|
|
@@ -140,6 +285,17 @@ export function buildTelegramMessageEnvelopeFromParsedArchive(parsedArchiveRaw,
|
|
|
140
285
|
]),
|
|
141
286
|
sender_is_bot: overrides.sender_is_bot ?? parsedArchive.senderIsBot,
|
|
142
287
|
body: firstNonEmptyString([overrides.body, parsedArchive.body]),
|
|
288
|
+
occurred_at: firstNonEmptyString([overrides.occurred_at, parsedArchive.occurredAt]),
|
|
289
|
+
mention_usernames: ensureArray(
|
|
290
|
+
overrides.mention_usernames
|
|
291
|
+
|| overrides.mentionUsernames
|
|
292
|
+
|| parsedArchive.mentionUsernames,
|
|
293
|
+
),
|
|
294
|
+
canonical_human_message_key: firstNonEmptyString([
|
|
295
|
+
overrides.canonical_human_message_key,
|
|
296
|
+
overrides.canonicalHumanMessageKey,
|
|
297
|
+
parsedArchive.canonicalHumanMessageKey,
|
|
298
|
+
]),
|
|
143
299
|
source_origin: firstNonEmptyString([
|
|
144
300
|
overrides.source_origin,
|
|
145
301
|
parsedArchive.sourceOrigin,
|
|
@@ -451,6 +607,10 @@ export function buildProcessableArchiveLogicalKey(recordRaw) {
|
|
|
451
607
|
if (!isRunnerProcessableArchiveKind(kind)) {
|
|
452
608
|
return "";
|
|
453
609
|
}
|
|
610
|
+
const canonicalHumanKey = buildCanonicalHumanInboundKey(parsed);
|
|
611
|
+
if (canonicalHumanKey) {
|
|
612
|
+
return `human:${canonicalHumanKey}`;
|
|
613
|
+
}
|
|
454
614
|
const chatID = firstNonEmptyString([parsed.chatID, parsed.chatId]);
|
|
455
615
|
const messageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
456
616
|
if (!chatID || !(messageID > 0)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildCanonicalHumanInboundKey,
|
|
2
3
|
buildRunnerContextWindow,
|
|
3
4
|
compareArchiveCommentRecords,
|
|
4
5
|
dedupeProcessableArchiveComments,
|
|
@@ -103,6 +104,10 @@ function resolveRunnerLocalInboundReceiptTargetSelector(receiptRaw) {
|
|
|
103
104
|
|
|
104
105
|
function buildRunnerArchiveSourceMessageKey(recordRaw) {
|
|
105
106
|
const parsed = safeObject(safeObject(recordRaw).parsedArchive);
|
|
107
|
+
const canonicalHumanKey = buildCanonicalHumanInboundKey(parsed);
|
|
108
|
+
if (canonicalHumanKey) {
|
|
109
|
+
return `human:${canonicalHumanKey}`;
|
|
110
|
+
}
|
|
106
111
|
const chatID = String(parsed.chatID || parsed.chatId || "").trim();
|
|
107
112
|
const messageID = intFromRawAllowZero(parsed.messageID || parsed.messageId, 0);
|
|
108
113
|
if (!chatID || !(messageID > 0)) {
|
|
@@ -113,6 +118,21 @@ function buildRunnerArchiveSourceMessageKey(recordRaw) {
|
|
|
113
118
|
|
|
114
119
|
function buildRunnerLocalInboundReceiptKey(receiptRaw) {
|
|
115
120
|
const receipt = safeObject(receiptRaw);
|
|
121
|
+
const canonicalHumanKey = buildCanonicalHumanInboundKey({
|
|
122
|
+
chat_id: receipt.chat_id,
|
|
123
|
+
message_id: receipt.message_id,
|
|
124
|
+
message_thread_id: receipt.message_thread_id,
|
|
125
|
+
reply_to_message_id: receipt.reply_to_message_id,
|
|
126
|
+
kind: receipt.kind,
|
|
127
|
+
sender_id: receipt.sender_id,
|
|
128
|
+
sender_username: receipt.sender_username,
|
|
129
|
+
sender_is_bot: receipt.sender_is_bot === true,
|
|
130
|
+
body: receipt.body,
|
|
131
|
+
occurred_at: receipt.occurred_at,
|
|
132
|
+
});
|
|
133
|
+
if (canonicalHumanKey) {
|
|
134
|
+
return `human:${canonicalHumanKey}`;
|
|
135
|
+
}
|
|
116
136
|
const chatID = String(receipt.chat_id || receipt.chatID || "").trim();
|
|
117
137
|
const messageID = intFromRawAllowZero(receipt.message_id || receipt.messageID, 0);
|
|
118
138
|
if (!chatID || !(messageID > 0)) {
|
|
@@ -9,6 +9,14 @@ function ensureArray(value) {
|
|
|
9
9
|
return Array.isArray(value) ? value : [];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function intFromRawAllowZero(raw, fallback = 0) {
|
|
13
|
+
if (raw === null || raw === undefined || raw === "") {
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
const parsed = Number.parseInt(String(raw).trim(), 10);
|
|
17
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
function uniqueOrdered(values) {
|
|
13
21
|
const seen = new Set();
|
|
14
22
|
const ordered = [];
|
|
@@ -21,6 +29,104 @@ function uniqueOrdered(values) {
|
|
|
21
29
|
return ordered;
|
|
22
30
|
}
|
|
23
31
|
|
|
32
|
+
function normalizeSelectorList(values, normalizeMentionSelector) {
|
|
33
|
+
return uniqueOrdered(
|
|
34
|
+
ensureArray(values)
|
|
35
|
+
.map((item) => normalizeMentionSelector(item))
|
|
36
|
+
.filter(Boolean),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildImplicitPublicMultiBotExecutionContract({
|
|
41
|
+
conversationContext = null,
|
|
42
|
+
executionContract = null,
|
|
43
|
+
currentBotSelector = "",
|
|
44
|
+
normalizeMentionSelector,
|
|
45
|
+
}) {
|
|
46
|
+
const context = safeObject(conversationContext);
|
|
47
|
+
const normalizedCurrentBotSelector = normalizeMentionSelector(currentBotSelector);
|
|
48
|
+
if (
|
|
49
|
+
String(context.mode || "").trim() !== "public_multi_bot"
|
|
50
|
+
|| String(context.intentMode || "").trim() !== "multi_bot_collab"
|
|
51
|
+
|| context.allowBotToBot !== true
|
|
52
|
+
|| !normalizedCurrentBotSelector
|
|
53
|
+
) {
|
|
54
|
+
return safeObject(executionContract);
|
|
55
|
+
}
|
|
56
|
+
const normalizedAllowedResponders = normalizeSelectorList(
|
|
57
|
+
context.allowedResponderSelectors,
|
|
58
|
+
normalizeMentionSelector,
|
|
59
|
+
);
|
|
60
|
+
if (
|
|
61
|
+
normalizedAllowedResponders.length < 2
|
|
62
|
+
|| !normalizedAllowedResponders.includes(normalizedCurrentBotSelector)
|
|
63
|
+
) {
|
|
64
|
+
return safeObject(executionContract);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const explicitContract = safeObject(executionContract);
|
|
68
|
+
const explicitAssignments = normalizeSelectorList(
|
|
69
|
+
ensureArray(explicitContract.assignments).map((item) => (
|
|
70
|
+
safeObject(item).targetBot || safeObject(item).target_bot
|
|
71
|
+
)),
|
|
72
|
+
normalizeMentionSelector,
|
|
73
|
+
).filter((item) => item !== normalizedCurrentBotSelector);
|
|
74
|
+
const explicitNextResponders = normalizeSelectorList(
|
|
75
|
+
explicitContract.nextResponders || explicitContract.next_responders || explicitContract.responders,
|
|
76
|
+
normalizeMentionSelector,
|
|
77
|
+
).filter((item) => item !== normalizedCurrentBotSelector);
|
|
78
|
+
const explicitSummaryBot = normalizeMentionSelector(
|
|
79
|
+
explicitContract.summaryBot || explicitContract.summary_bot || context.summaryBotUsername,
|
|
80
|
+
);
|
|
81
|
+
const explicitType = String(explicitContract.type || "").trim().toLowerCase();
|
|
82
|
+
const hasExplicitFollowup = explicitAssignments.length > 0
|
|
83
|
+
|| explicitNextResponders.length > 0
|
|
84
|
+
|| (explicitType === "summary_request" && explicitSummaryBot && explicitSummaryBot !== normalizedCurrentBotSelector);
|
|
85
|
+
if (hasExplicitFollowup) {
|
|
86
|
+
return explicitContract;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const sessionSpeakerCounts = safeObject(safeObject(context.session).speaker_counts);
|
|
90
|
+
const projectedSpeakerCounts = {
|
|
91
|
+
...sessionSpeakerCounts,
|
|
92
|
+
[normalizedCurrentBotSelector]: intFromRawAllowZero(sessionSpeakerCounts[normalizedCurrentBotSelector], 0) + 1,
|
|
93
|
+
};
|
|
94
|
+
const remainingPeerResponders = normalizedAllowedResponders.filter((selector) => (
|
|
95
|
+
selector
|
|
96
|
+
&& selector !== normalizedCurrentBotSelector
|
|
97
|
+
&& intFromRawAllowZero(projectedSpeakerCounts[selector], 0) <= 0
|
|
98
|
+
));
|
|
99
|
+
if (remainingPeerResponders.length > 0) {
|
|
100
|
+
return {
|
|
101
|
+
type: "delegation",
|
|
102
|
+
actionable: true,
|
|
103
|
+
assignments: remainingPeerResponders.map((targetBot) => ({ targetBot })),
|
|
104
|
+
summaryBot: explicitSummaryBot,
|
|
105
|
+
nextResponders: remainingPeerResponders,
|
|
106
|
+
implicitFollowup: true,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const stage = String(context.stage || "").trim().toLowerCase();
|
|
111
|
+
if (
|
|
112
|
+
stage === "bot_reply"
|
|
113
|
+
&& explicitSummaryBot
|
|
114
|
+
&& explicitSummaryBot !== normalizedCurrentBotSelector
|
|
115
|
+
&& normalizedAllowedResponders.includes(explicitSummaryBot)
|
|
116
|
+
) {
|
|
117
|
+
return {
|
|
118
|
+
type: "summary_request",
|
|
119
|
+
actionable: true,
|
|
120
|
+
assignments: [],
|
|
121
|
+
summaryBot: explicitSummaryBot,
|
|
122
|
+
nextResponders: [explicitSummaryBot],
|
|
123
|
+
implicitFollowup: true,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return explicitContract;
|
|
128
|
+
}
|
|
129
|
+
|
|
24
130
|
export async function prepareRunnerSelectedRecordContractContext({
|
|
25
131
|
selectedRecord,
|
|
26
132
|
routeState,
|
|
@@ -64,7 +170,7 @@ export async function prepareRunnerSelectedRecordContractContext({
|
|
|
64
170
|
}
|
|
65
171
|
|
|
66
172
|
const effectiveResponseContractPayload = safeObject(aiPayload?.response_contract);
|
|
67
|
-
const
|
|
173
|
+
const normalizedExecutionContract = conversationContext?.mode === "public_multi_bot"
|
|
68
174
|
? normalizeConversationExecutionContract(
|
|
69
175
|
aiResult?.contract,
|
|
70
176
|
{
|
|
@@ -78,6 +184,12 @@ export async function prepareRunnerSelectedRecordContractContext({
|
|
|
78
184
|
effectiveResponseContractPayload,
|
|
79
185
|
{ currentBotSelector },
|
|
80
186
|
);
|
|
187
|
+
const executionContract = buildImplicitPublicMultiBotExecutionContract({
|
|
188
|
+
conversationContext,
|
|
189
|
+
executionContract: normalizedExecutionContract,
|
|
190
|
+
currentBotSelector,
|
|
191
|
+
normalizeMentionSelector,
|
|
192
|
+
});
|
|
81
193
|
let allowedActionableTypes = new Set(
|
|
82
194
|
ensureArray(effectiveResponseContractPayload.allowed_contract_types)
|
|
83
195
|
.map((item) => String(item || "").trim().toLowerCase())
|
|
@@ -99,7 +211,6 @@ export async function prepareRunnerSelectedRecordContractContext({
|
|
|
99
211
|
&& String(conversationContext?.stage || "").trim() === "human_opening"
|
|
100
212
|
&& conversationContext?.allowBotToBot === true
|
|
101
213
|
&& Boolean(currentBotSelector)
|
|
102
|
-
&& normalizedConversationInitialResponders.length === 1
|
|
103
214
|
&& normalizedConversationInitialResponders.includes(currentBotSelector)
|
|
104
215
|
&& peerAllowedResponders.length > 0;
|
|
105
216
|
const directHumanPeerMap = humanIntentContext?.peerMap instanceof Map
|
package/lib/runner-runtime.mjs
CHANGED
|
@@ -539,10 +539,12 @@ function buildRunnerLocalInboundEnvelopeFromReceipt(rawReceipt) {
|
|
|
539
539
|
message_thread_id: receipt.message_thread_id,
|
|
540
540
|
reply_to_message_id: receipt.reply_to_message_id,
|
|
541
541
|
kind: receipt.kind,
|
|
542
|
+
sender_id: receipt.sender_id,
|
|
542
543
|
sender: receipt.sender,
|
|
543
544
|
sender_username: receipt.sender_username,
|
|
544
545
|
sender_is_bot: receipt.sender_is_bot === true,
|
|
545
546
|
body: receipt.body,
|
|
547
|
+
occurred_at: receipt.occurred_at,
|
|
546
548
|
source_origin: receipt.receipt_origin,
|
|
547
549
|
source_route_key: receipt.receipt_route_key,
|
|
548
550
|
source_bot_username: receipt.receipt_bot_username,
|
|
@@ -670,16 +672,13 @@ function buildRunnerRecentLocalInboundReceipts(routeStateRaw, localInboundArtifa
|
|
|
670
672
|
const RUNNER_INBOUND_ARCHIVE_RESERVATION_TTL_MS = 10 * 60 * 1000;
|
|
671
673
|
const runnerInboundArchiveReservations = new Map();
|
|
672
674
|
|
|
673
|
-
function buildRunnerInboundArchiveReservationKey(threadID,
|
|
675
|
+
function buildRunnerInboundArchiveReservationKey(threadID, archiveMessageKey = "") {
|
|
674
676
|
const normalizedThreadID = String(threadID || "").trim();
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
const normalizedSourceBotUsername = normalizeMentionSelector(sourceBotUsername);
|
|
678
|
-
if (!normalizedThreadID || !normalizedChatID || !(normalizedMessageID > 0)) {
|
|
677
|
+
const normalizedArchiveMessageKey = String(archiveMessageKey || "").trim();
|
|
678
|
+
if (!normalizedThreadID || !normalizedArchiveMessageKey) {
|
|
679
679
|
return "";
|
|
680
680
|
}
|
|
681
|
-
|
|
682
|
-
return normalizedSourceBotUsername ? `${baseKey}::${normalizedSourceBotUsername}` : baseKey;
|
|
681
|
+
return `${normalizedThreadID}::${normalizedArchiveMessageKey}`;
|
|
683
682
|
}
|
|
684
683
|
|
|
685
684
|
function cleanupRunnerInboundArchiveReservations(nowMs = Date.now()) {
|
|
@@ -690,8 +689,8 @@ function cleanupRunnerInboundArchiveReservations(nowMs = Date.now()) {
|
|
|
690
689
|
}
|
|
691
690
|
}
|
|
692
691
|
|
|
693
|
-
function reserveRunnerInboundArchiveMessage(threadID,
|
|
694
|
-
const reservationKey = buildRunnerInboundArchiveReservationKey(threadID,
|
|
692
|
+
function reserveRunnerInboundArchiveMessage(threadID, archiveMessageKey = "") {
|
|
693
|
+
const reservationKey = buildRunnerInboundArchiveReservationKey(threadID, archiveMessageKey);
|
|
695
694
|
if (!reservationKey) {
|
|
696
695
|
return {
|
|
697
696
|
ok: false,
|
|
@@ -821,7 +820,7 @@ async function loadRunnerExistingInboundArchiveKeys({
|
|
|
821
820
|
.map((record) => normalizeArchiveCommentRecord(record, parseArchivedChatComment))
|
|
822
821
|
.map((record) => record.parsedArchive)
|
|
823
822
|
.filter((parsed) => parsed && isInboundArchiveKind(parsed.kind) && parsed.chatID)
|
|
824
|
-
.map((parsed) => buildArchivedInboundMessageKey(parsed
|
|
823
|
+
.map((parsed) => buildArchivedInboundMessageKey(parsed)),
|
|
825
824
|
);
|
|
826
825
|
}
|
|
827
826
|
|
|
@@ -884,16 +883,27 @@ async function archiveRunnerTelegramInboundUpdates({
|
|
|
884
883
|
route,
|
|
885
884
|
bot,
|
|
886
885
|
});
|
|
887
|
-
const dedupeKey = buildArchivedInboundMessageKey(
|
|
886
|
+
const dedupeKey = buildArchivedInboundMessageKey({
|
|
887
|
+
chatID: update.chatID,
|
|
888
|
+
messageID: update.messageID,
|
|
889
|
+
messageThreadID: update.messageThreadID,
|
|
890
|
+
kind: update.fromIsBot ? "bot_reply" : "telegram_message",
|
|
891
|
+
senderID: update.fromID,
|
|
892
|
+
sender: update.fromName,
|
|
893
|
+
senderUsername: update.fromUsername,
|
|
894
|
+
senderIsBot: update.fromIsBot === true,
|
|
895
|
+
body: update.text,
|
|
896
|
+
occurredAt: update.occurredAt,
|
|
897
|
+
replyToMessageID: update.replyToMessageID,
|
|
898
|
+
sourceBotUsername: archiveSourceBotUsername,
|
|
899
|
+
});
|
|
888
900
|
if (boolFromRaw(archivePolicy.dedupeInbound, true) && existingKeys.has(dedupeKey)) {
|
|
889
901
|
continue;
|
|
890
902
|
}
|
|
891
903
|
const reservation = boolFromRaw(archivePolicy.dedupeInbound, true)
|
|
892
904
|
? reserveRunnerInboundArchiveMessage(
|
|
893
905
|
archiveThread?.threadID,
|
|
894
|
-
|
|
895
|
-
update.messageID,
|
|
896
|
-
archiveSourceBotUsername,
|
|
906
|
+
dedupeKey,
|
|
897
907
|
)
|
|
898
908
|
: { ok: true, reservationKey: "" };
|
|
899
909
|
if (!reservation.ok) {
|
|
@@ -299,6 +299,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
299
299
|
const mergeServerRunnerRequestLedgerIntoLocalState = requireDependency(deps, "mergeServerRunnerRequestLedgerIntoLocalState");
|
|
300
300
|
const buildRunnerStatusQueryLookup = requireDependency(deps, "buildRunnerStatusQueryLookup");
|
|
301
301
|
const claimRunnerRequestForHumanComment = requireDependency(deps, "claimRunnerRequestForHumanComment");
|
|
302
|
+
const buildArchivedInboundMessageKey = requireDependency(deps, "buildArchivedInboundMessageKey");
|
|
302
303
|
const markRunnerRequestLifecycle = requireDependency(deps, "markRunnerRequestLifecycle");
|
|
303
304
|
const resolveRunnerContinuationRequestForBotReply = requireDependency(deps, "resolveRunnerContinuationRequestForBotReply");
|
|
304
305
|
const prepareRunnerSelectedRecordRecoveryContext = requireDependency(deps, "prepareRunnerSelectedRecordRecoveryContext");
|
|
@@ -2180,17 +2181,142 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
2180
2181
|
const authoritativeSourceRequest = safeObject(
|
|
2181
2182
|
safeObject(loadBotRunnerState().requests)[authoritativeSourceClaim.requestKey],
|
|
2182
2183
|
);
|
|
2183
|
-
push(
|
|
2184
|
-
"runner_request_claim_preserves_authoritative_source_message_envelope",
|
|
2185
|
-
authoritativeSourceClaim.ok === true
|
|
2186
|
-
&& String(authoritativeSourceRequest.source_message_origin || "") === "local_telegram_inbound"
|
|
2187
|
-
&& String(authoritativeSourceRequest.source_message_route_key || "") === requestRouteKey
|
|
2188
|
-
&& String(authoritativeSourceRequest.source_message_bot_username || "") === "ryoai_bot"
|
|
2189
|
-
&& Number(safeObject(authoritativeSourceRequest.source_message_envelope).message_id || 0) === 503,
|
|
2190
|
-
`origin=${String(authoritativeSourceRequest.source_message_origin || "(none)")} route=${String(authoritativeSourceRequest.source_message_route_key || "(none)")} bot=${String(authoritativeSourceRequest.source_message_bot_username || "(none)")} message=${String(safeObject(authoritativeSourceRequest.source_message_envelope).message_id || "(none)")}`,
|
|
2191
|
-
);
|
|
2192
|
-
|
|
2193
|
-
const
|
|
2184
|
+
push(
|
|
2185
|
+
"runner_request_claim_preserves_authoritative_source_message_envelope",
|
|
2186
|
+
authoritativeSourceClaim.ok === true
|
|
2187
|
+
&& String(authoritativeSourceRequest.source_message_origin || "") === "local_telegram_inbound"
|
|
2188
|
+
&& String(authoritativeSourceRequest.source_message_route_key || "") === requestRouteKey
|
|
2189
|
+
&& String(authoritativeSourceRequest.source_message_bot_username || "") === "ryoai_bot"
|
|
2190
|
+
&& Number(safeObject(authoritativeSourceRequest.source_message_envelope).message_id || 0) === 503,
|
|
2191
|
+
`origin=${String(authoritativeSourceRequest.source_message_origin || "(none)")} route=${String(authoritativeSourceRequest.source_message_route_key || "(none)")} bot=${String(authoritativeSourceRequest.source_message_bot_username || "(none)")} message=${String(safeObject(authoritativeSourceRequest.source_message_envelope).message_id || "(none)")}`,
|
|
2192
|
+
);
|
|
2193
|
+
|
|
2194
|
+
const canonicalHumanArchiveKeyPrimary = buildArchivedInboundMessageKey({
|
|
2195
|
+
chatID: "-100123",
|
|
2196
|
+
messageID: 1189,
|
|
2197
|
+
messageThreadID: 0,
|
|
2198
|
+
kind: "telegram_message",
|
|
2199
|
+
senderID: "7001",
|
|
2200
|
+
senderIsBot: false,
|
|
2201
|
+
occurredAt: "2026-04-01T06:00:00.000Z",
|
|
2202
|
+
body: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 같이 논의 해죠",
|
|
2203
|
+
sourceBotUsername: "ryoai_bot",
|
|
2204
|
+
});
|
|
2205
|
+
const canonicalHumanArchiveKeyPeer = buildArchivedInboundMessageKey({
|
|
2206
|
+
chatID: "-100123",
|
|
2207
|
+
messageID: 294,
|
|
2208
|
+
messageThreadID: 0,
|
|
2209
|
+
kind: "telegram_message",
|
|
2210
|
+
senderID: "7001",
|
|
2211
|
+
senderIsBot: false,
|
|
2212
|
+
occurredAt: "2026-04-01T06:00:00.000Z",
|
|
2213
|
+
body: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 같이 논의 해죠",
|
|
2214
|
+
sourceBotUsername: "ryoai3_bot",
|
|
2215
|
+
});
|
|
2216
|
+
push(
|
|
2217
|
+
"runner_human_inbound_archive_identity_is_canonical_across_routes",
|
|
2218
|
+
canonicalHumanArchiveKeyPrimary === canonicalHumanArchiveKeyPeer
|
|
2219
|
+
&& canonicalHumanArchiveKeyPrimary.startsWith("human:"),
|
|
2220
|
+
`primary=${canonicalHumanArchiveKeyPrimary} peer=${canonicalHumanArchiveKeyPeer}`,
|
|
2221
|
+
);
|
|
2222
|
+
|
|
2223
|
+
const sharedHumanRouteRyoai1 = normalizeRunnerRoute({
|
|
2224
|
+
...requestRoute,
|
|
2225
|
+
name: "telegram-monitor-request-ledger-shared-human-ryoai1",
|
|
2226
|
+
});
|
|
2227
|
+
const sharedHumanRouteRyoai3 = normalizeRunnerRoute({
|
|
2228
|
+
...requestRoute,
|
|
2229
|
+
name: "telegram-monitor-request-ledger-shared-human-ryoai3",
|
|
2230
|
+
});
|
|
2231
|
+
const sharedHumanRouteRyoai1Key = runnerRouteKey(sharedHumanRouteRyoai1);
|
|
2232
|
+
const sharedHumanRouteRyoai3Key = runnerRouteKey(sharedHumanRouteRyoai3);
|
|
2233
|
+
const sharedHumanBody = "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 같이 논의 해죠";
|
|
2234
|
+
const sharedHumanRecordPrimary = {
|
|
2235
|
+
id: "comment-request-shared-human-1",
|
|
2236
|
+
createdAt: "2026-04-01T06:00:00.000Z",
|
|
2237
|
+
updatedAt: "2026-04-01T06:00:00.000Z",
|
|
2238
|
+
parsedArchive: {
|
|
2239
|
+
kind: "telegram_message",
|
|
2240
|
+
chatID: "-100123",
|
|
2241
|
+
chatType: "supergroup",
|
|
2242
|
+
body: sharedHumanBody,
|
|
2243
|
+
messageID: 1189,
|
|
2244
|
+
senderID: "7001",
|
|
2245
|
+
senderIsBot: false,
|
|
2246
|
+
occurredAt: "2026-04-01T06:00:00.000Z",
|
|
2247
|
+
mentionUsernames: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
2248
|
+
},
|
|
2249
|
+
};
|
|
2250
|
+
const sharedHumanRecordPeer = {
|
|
2251
|
+
id: "comment-request-shared-human-2",
|
|
2252
|
+
createdAt: "2026-04-01T06:00:00.000Z",
|
|
2253
|
+
updatedAt: "2026-04-01T06:00:00.000Z",
|
|
2254
|
+
parsedArchive: {
|
|
2255
|
+
kind: "telegram_message",
|
|
2256
|
+
chatID: "-100123",
|
|
2257
|
+
chatType: "supergroup",
|
|
2258
|
+
body: sharedHumanBody,
|
|
2259
|
+
messageID: 294,
|
|
2260
|
+
senderID: "7001",
|
|
2261
|
+
senderIsBot: false,
|
|
2262
|
+
occurredAt: "2026-04-01T06:00:00.000Z",
|
|
2263
|
+
mentionUsernames: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
2264
|
+
},
|
|
2265
|
+
};
|
|
2266
|
+
const sharedHumanClaimPrimary = await claimRunnerRequestForHumanComment({
|
|
2267
|
+
normalizedRoute: sharedHumanRouteRyoai1,
|
|
2268
|
+
routeKey: sharedHumanRouteRyoai1Key,
|
|
2269
|
+
selectedRecord: sharedHumanRecordPrimary,
|
|
2270
|
+
selectedBotUsernames: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
2271
|
+
normalizedIntent: "discussion_request",
|
|
2272
|
+
authoritativeSourceMessageEnvelope: {
|
|
2273
|
+
chat_id: "-100123",
|
|
2274
|
+
message_id: 1189,
|
|
2275
|
+
sender_id: "7001",
|
|
2276
|
+
sender_is_bot: false,
|
|
2277
|
+
occurred_at: "2026-04-01T06:00:00.000Z",
|
|
2278
|
+
body: sharedHumanBody,
|
|
2279
|
+
source_origin: "local_telegram_inbound",
|
|
2280
|
+
source_route_key: sharedHumanRouteRyoai1Key,
|
|
2281
|
+
source_bot_username: "ryoai_bot",
|
|
2282
|
+
},
|
|
2283
|
+
});
|
|
2284
|
+
const sharedHumanClaimPeer = await claimRunnerRequestForHumanComment({
|
|
2285
|
+
normalizedRoute: sharedHumanRouteRyoai3,
|
|
2286
|
+
routeKey: sharedHumanRouteRyoai3Key,
|
|
2287
|
+
selectedRecord: sharedHumanRecordPeer,
|
|
2288
|
+
selectedBotUsernames: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
2289
|
+
normalizedIntent: "discussion_request",
|
|
2290
|
+
authoritativeSourceMessageEnvelope: {
|
|
2291
|
+
chat_id: "-100123",
|
|
2292
|
+
message_id: 294,
|
|
2293
|
+
sender_id: "7001",
|
|
2294
|
+
sender_is_bot: false,
|
|
2295
|
+
occurred_at: "2026-04-01T06:00:00.000Z",
|
|
2296
|
+
body: sharedHumanBody,
|
|
2297
|
+
source_origin: "local_telegram_inbound",
|
|
2298
|
+
source_route_key: sharedHumanRouteRyoai3Key,
|
|
2299
|
+
source_bot_username: "ryoai3_bot",
|
|
2300
|
+
},
|
|
2301
|
+
});
|
|
2302
|
+
const sharedHumanState = loadBotRunnerState();
|
|
2303
|
+
const sharedHumanRequestCount = Object.values(safeObject(sharedHumanState.requests))
|
|
2304
|
+
.filter((entryRaw) => {
|
|
2305
|
+
const entry = safeObject(entryRaw);
|
|
2306
|
+
return String(entry.chat_id || "") === "-100123"
|
|
2307
|
+
&& String(entry.source_message_body || "") === sharedHumanBody;
|
|
2308
|
+
})
|
|
2309
|
+
.length;
|
|
2310
|
+
push(
|
|
2311
|
+
"runner_human_opening_request_identity_is_canonical_across_routes",
|
|
2312
|
+
sharedHumanClaimPrimary.ok === true
|
|
2313
|
+
&& (sharedHumanClaimPeer.ok === true || String(sharedHumanClaimPeer.reason || "") === "request_already_claimed")
|
|
2314
|
+
&& String(sharedHumanClaimPrimary.requestKey || "") === String(sharedHumanClaimPeer.requestKey || "")
|
|
2315
|
+
&& sharedHumanRequestCount === 1,
|
|
2316
|
+
`primary=${String(sharedHumanClaimPrimary.requestKey || "(none)")} peer=${String(sharedHumanClaimPeer.requestKey || "(none)")} peer_reason=${String(sharedHumanClaimPeer.reason || "(none)")} count=${sharedHumanRequestCount}`,
|
|
2317
|
+
);
|
|
2318
|
+
|
|
2319
|
+
const rootTaskRecord = {
|
|
2194
2320
|
id: "comment-request-root-task-1",
|
|
2195
2321
|
createdAt: "2026-03-22T00:05:00.000Z",
|
|
2196
2322
|
updatedAt: "2026-03-22T00:05:00.000Z",
|
|
@@ -2825,6 +2951,50 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
2825
2951
|
`status=${String(continuedRequest?.status || "(none)")}`,
|
|
2826
2952
|
);
|
|
2827
2953
|
|
|
2954
|
+
saveBotRunnerState({
|
|
2955
|
+
routes: {
|
|
2956
|
+
[requestRouteKey]: {},
|
|
2957
|
+
},
|
|
2958
|
+
sharedInboxes: {},
|
|
2959
|
+
excludedComments: {},
|
|
2960
|
+
requests: {
|
|
2961
|
+
"request-key-2c": {
|
|
2962
|
+
request_key: "request-key-2c",
|
|
2963
|
+
project_id: selftestProjectID,
|
|
2964
|
+
provider: "telegram",
|
|
2965
|
+
chat_id: "-100123",
|
|
2966
|
+
source_message_id: 721,
|
|
2967
|
+
conversation_id: "conv-request-2c",
|
|
2968
|
+
status: "running",
|
|
2969
|
+
claimed_by_route: requestRouteKey,
|
|
2970
|
+
},
|
|
2971
|
+
},
|
|
2972
|
+
consumedComments: {},
|
|
2973
|
+
});
|
|
2974
|
+
const continuedByNextExpected = markRunnerRequestLifecycle({
|
|
2975
|
+
normalizedRoute: requestRoute,
|
|
2976
|
+
requestKey: "request-key-2c",
|
|
2977
|
+
selectedRecord: {
|
|
2978
|
+
id: "comment-request-finish-2c",
|
|
2979
|
+
parsedArchive: {
|
|
2980
|
+
kind: "bot_reply",
|
|
2981
|
+
chatID: "-100123",
|
|
2982
|
+
messageID: 722,
|
|
2983
|
+
conversationID: "conv-request-2c",
|
|
2984
|
+
},
|
|
2985
|
+
},
|
|
2986
|
+
routeKey: requestRouteKey,
|
|
2987
|
+
outcome: "replied",
|
|
2988
|
+
currentBotSelector: "@RyoAI_bot",
|
|
2989
|
+
executionContractTargets: [],
|
|
2990
|
+
nextExpectedResponders: ["@RyoAI2_bot", "@RyoAI3_bot"],
|
|
2991
|
+
});
|
|
2992
|
+
push(
|
|
2993
|
+
"runner_request_lifecycle_reply_remains_running_with_next_expected_responders",
|
|
2994
|
+
String(continuedByNextExpected?.status || "") === "running",
|
|
2995
|
+
`status=${String(continuedByNextExpected?.status || "(none)")}`,
|
|
2996
|
+
);
|
|
2997
|
+
|
|
2828
2998
|
saveBotRunnerState({
|
|
2829
2999
|
routes: {
|
|
2830
3000
|
[requestRouteKey]: {},
|
|
@@ -5324,16 +5494,162 @@ export async function runSelftestRunnerScenarios(push, deps) {
|
|
|
5324
5494
|
&& String(deliveredConversation[0]?.summaryBotUsername || "") === "ryoai_bot",
|
|
5325
5495
|
`kind=${String(processed.kind || "(none)")} intent=${String(deliveredConversation[0]?.intentMode || "(none)")} summary=${String(deliveredConversation[0]?.summaryBotUsername || "(none)")}`,
|
|
5326
5496
|
);
|
|
5327
|
-
} catch (err) {
|
|
5328
|
-
push("intent_fallback_uses_repeated_managed_mention_for_summary_target", false, String(err?.message || err));
|
|
5329
|
-
}
|
|
5330
|
-
|
|
5331
|
-
try {
|
|
5332
|
-
|
|
5333
|
-
const
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5497
|
+
} catch (err) {
|
|
5498
|
+
push("intent_fallback_uses_repeated_managed_mention_for_summary_target", false, String(err?.message || err));
|
|
5499
|
+
}
|
|
5500
|
+
|
|
5501
|
+
try {
|
|
5502
|
+
let aiCalls = 0;
|
|
5503
|
+
const deliveredConversation = [];
|
|
5504
|
+
const processed = await processRunnerSelectedRecord({
|
|
5505
|
+
routeKey: "multi-bot-collab-human-opening-synthesizes-next-turn-contract-key",
|
|
5506
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
5507
|
+
name: "telegram-monitor-multi-bot-collab-human-opening-synthesizes-next-turn-contract",
|
|
5508
|
+
project_id: selftestProjectID,
|
|
5509
|
+
provider: "telegram",
|
|
5510
|
+
role: "monitor",
|
|
5511
|
+
role_profile: "monitor",
|
|
5512
|
+
destination_id: "dest-1",
|
|
5513
|
+
destination_label: "Main Room",
|
|
5514
|
+
server_bot_name: "RyoAI_bot",
|
|
5515
|
+
server_bot_id: "bot-lead-1",
|
|
5516
|
+
trigger_policy: {
|
|
5517
|
+
mentions_only: true,
|
|
5518
|
+
direct_messages: true,
|
|
5519
|
+
reply_to_bot_messages: true,
|
|
5520
|
+
},
|
|
5521
|
+
archive_policy: {
|
|
5522
|
+
mirror_replies: true,
|
|
5523
|
+
dedupe_inbound: true,
|
|
5524
|
+
dedupe_outbound: true,
|
|
5525
|
+
skip_bot_messages: true,
|
|
5526
|
+
},
|
|
5527
|
+
dry_run_delivery: true,
|
|
5528
|
+
}),
|
|
5529
|
+
selectedRecord: {
|
|
5530
|
+
id: "comment-multi-bot-collab-human-opening",
|
|
5531
|
+
body: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 같이 논의해줘.",
|
|
5532
|
+
threadID: "thread-1",
|
|
5533
|
+
createdAt: "2026-03-17T00:00:00.000Z",
|
|
5534
|
+
updatedAt: "2026-03-17T00:00:00.000Z",
|
|
5535
|
+
parsedArchive: {
|
|
5536
|
+
kind: "telegram_message",
|
|
5537
|
+
body: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 같이 논의해줘.",
|
|
5538
|
+
sender: "User",
|
|
5539
|
+
senderName: "User",
|
|
5540
|
+
senderIsBot: false,
|
|
5541
|
+
username: "user1",
|
|
5542
|
+
chatType: "supergroup",
|
|
5543
|
+
mentionUsernames: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
5544
|
+
messageID: 1901,
|
|
5545
|
+
chatID: "-100123",
|
|
5546
|
+
},
|
|
5547
|
+
},
|
|
5548
|
+
routeState: {},
|
|
5549
|
+
pendingOrdered: [],
|
|
5550
|
+
bot: {
|
|
5551
|
+
id: "bot-lead-1",
|
|
5552
|
+
name: "RyoAI_bot",
|
|
5553
|
+
username: "RyoAI_bot",
|
|
5554
|
+
},
|
|
5555
|
+
destination: {
|
|
5556
|
+
id: "dest-1",
|
|
5557
|
+
label: "Main Room",
|
|
5558
|
+
provider: "telegram",
|
|
5559
|
+
chatID: "-100123",
|
|
5560
|
+
},
|
|
5561
|
+
archiveThread: {
|
|
5562
|
+
threadID: "thread-1",
|
|
5563
|
+
workItemID: "work-item-1",
|
|
5564
|
+
},
|
|
5565
|
+
executionPlan: {
|
|
5566
|
+
mode: "role_profile",
|
|
5567
|
+
roleProfileName: "monitor",
|
|
5568
|
+
roleProfile: {
|
|
5569
|
+
client: "sample",
|
|
5570
|
+
model: "",
|
|
5571
|
+
permissionMode: "read_only",
|
|
5572
|
+
reasoningEffort: "low",
|
|
5573
|
+
},
|
|
5574
|
+
workspaceDir: path.join(os.tmpdir(), "metheus-runner-selftest-multi-bot-collab-kickoff"),
|
|
5575
|
+
workspaceSource: "selftest",
|
|
5576
|
+
usedCommandFallback: false,
|
|
5577
|
+
},
|
|
5578
|
+
runtime: {
|
|
5579
|
+
baseURL: "https://example.test",
|
|
5580
|
+
token: "selftest-token",
|
|
5581
|
+
timeoutSeconds: 30,
|
|
5582
|
+
actor: { user_id: "user-1" },
|
|
5583
|
+
},
|
|
5584
|
+
deps: {
|
|
5585
|
+
saveRunnerRouteState: () => {},
|
|
5586
|
+
startRunnerTypingHeartbeat: () => ({ async stop() {} }),
|
|
5587
|
+
runRunnerAIExecution: async () => {
|
|
5588
|
+
aiCalls += 1;
|
|
5589
|
+
return {
|
|
5590
|
+
skip: false,
|
|
5591
|
+
reply: "좋아요. 먼저 제 관점을 짧게 정리하고, 다른 봇 의견도 이어서 받겠습니다.",
|
|
5592
|
+
replyToMessageID: 0,
|
|
5593
|
+
};
|
|
5594
|
+
},
|
|
5595
|
+
performLocalBotDelivery: async ({ archiveConversation, archiveConversationContext, archiveExecutionContract }) => {
|
|
5596
|
+
deliveredConversation.push(buildSelftestArchiveConversation({
|
|
5597
|
+
archiveConversation,
|
|
5598
|
+
archiveConversationContext,
|
|
5599
|
+
archiveExecutionContract,
|
|
5600
|
+
}));
|
|
5601
|
+
return {
|
|
5602
|
+
delivery: { dryRun: true, body: {} },
|
|
5603
|
+
archive: {},
|
|
5604
|
+
};
|
|
5605
|
+
},
|
|
5606
|
+
serializeRunnerTriggerPolicy: (value) => value,
|
|
5607
|
+
serializeRunnerArchivePolicy: (value) => value,
|
|
5608
|
+
buildRunnerExecutionDeps: () => ({
|
|
5609
|
+
analyzeHumanConversationIntentWithAI: async () => ({
|
|
5610
|
+
mode: "multi_bot_collab",
|
|
5611
|
+
intent_type: "explanation_query",
|
|
5612
|
+
lead_bot: "ryoai_bot",
|
|
5613
|
+
participants: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
5614
|
+
initial_responders: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
5615
|
+
allowed_responders: ["ryoai_bot", "ryoai2_bot", "ryoai3_bot"],
|
|
5616
|
+
summary_bot: "ryoai_bot",
|
|
5617
|
+
allow_bot_to_bot: true,
|
|
5618
|
+
reply_expectation: "informational",
|
|
5619
|
+
}),
|
|
5620
|
+
}),
|
|
5621
|
+
buildRunnerDeliveryDeps: () => ({}),
|
|
5622
|
+
buildRunnerRuntimeDeps: () => ({}),
|
|
5623
|
+
resolveConversationPeerBots: () => [
|
|
5624
|
+
{ id: "bot-lead-1", name: "RyoAI_bot" },
|
|
5625
|
+
{ id: "bot-peer-1", name: "RyoAI2_bot" },
|
|
5626
|
+
{ id: "bot-peer-2", name: "RyoAI3_bot" },
|
|
5627
|
+
],
|
|
5628
|
+
},
|
|
5629
|
+
});
|
|
5630
|
+
push(
|
|
5631
|
+
"multi_bot_collab_human_opening_synthesizes_shared_next_turn_contract",
|
|
5632
|
+
processed.kind === "replied"
|
|
5633
|
+
&& aiCalls === 1
|
|
5634
|
+
&& String(deliveredConversation[0]?.mode || "") === "public_multi_bot"
|
|
5635
|
+
&& String(deliveredConversation[0]?.executionContract?.type || "") === "delegation"
|
|
5636
|
+
&& ensureArray(deliveredConversation[0]?.executionContract?.nextResponders).includes("ryoai2_bot")
|
|
5637
|
+
&& ensureArray(deliveredConversation[0]?.executionContract?.nextResponders).includes("ryoai3_bot")
|
|
5638
|
+
&& ensureArray(processed.result?.next_expected_responders).includes("ryoai2_bot")
|
|
5639
|
+
&& ensureArray(processed.result?.next_expected_responders).includes("ryoai3_bot")
|
|
5640
|
+
&& String(processed.result?.response_contract_validation_status || "") === "valid",
|
|
5641
|
+
`kind=${String(processed.kind || "(none)")} ai_calls=${aiCalls} contract=${String(deliveredConversation[0]?.executionContract?.type || "(none)")} next=${JSON.stringify(processed.result?.next_expected_responders || [])} validation=${String(processed.result?.response_contract_validation_status || "(none)")}`,
|
|
5642
|
+
);
|
|
5643
|
+
} catch (err) {
|
|
5644
|
+
push("multi_bot_collab_human_opening_synthesizes_shared_next_turn_contract", false, String(err?.message || err));
|
|
5645
|
+
}
|
|
5646
|
+
|
|
5647
|
+
try {
|
|
5648
|
+
const deliveredConversation = [];
|
|
5649
|
+
const processed = await processRunnerSelectedRecord({
|
|
5650
|
+
routeKey: "delegated-single-lead-human-open-key",
|
|
5651
|
+
normalizedRoute: normalizeRunnerRoute({
|
|
5652
|
+
name: "telegram-monitor-delegated-single-lead-open",
|
|
5337
5653
|
project_id: selftestProjectID,
|
|
5338
5654
|
provider: "telegram",
|
|
5339
5655
|
role: "monitor",
|
|
@@ -1599,6 +1599,123 @@ export async function runSelftestTelegramE2E(push, deps) {
|
|
|
1599
1599
|
});
|
|
1600
1600
|
const routeRyoai2Key = runnerRouteKey(routeRyoai2);
|
|
1601
1601
|
const routeRyoai3Key = runnerRouteKey(routeRyoai3);
|
|
1602
|
+
|
|
1603
|
+
telegramE2EServer.state.comments = [];
|
|
1604
|
+
telegramE2EServer.state.updates = [
|
|
1605
|
+
{
|
|
1606
|
+
update_id: 402,
|
|
1607
|
+
message: {
|
|
1608
|
+
message_id: 84,
|
|
1609
|
+
date: Math.floor(Date.now() / 1000),
|
|
1610
|
+
chat: {
|
|
1611
|
+
id: Number(e2eDestination.chat_id),
|
|
1612
|
+
type: "supergroup",
|
|
1613
|
+
title: e2eDestination.label,
|
|
1614
|
+
},
|
|
1615
|
+
from: {
|
|
1616
|
+
id: 7001,
|
|
1617
|
+
is_bot: false,
|
|
1618
|
+
first_name: "Operator",
|
|
1619
|
+
username: "operator_user",
|
|
1620
|
+
},
|
|
1621
|
+
text: "@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 같이 논의 해죠",
|
|
1622
|
+
entities: buildTelegramMentionEntities("@RyoAI_bot @RyoAI2_bot @RyoAI3_bot 같이 논의 해죠"),
|
|
1623
|
+
},
|
|
1624
|
+
},
|
|
1625
|
+
];
|
|
1626
|
+
const sharedHumanRouteRuntime = {
|
|
1627
|
+
baseURL: telegramE2EServer.baseURL,
|
|
1628
|
+
timeoutSeconds: 10,
|
|
1629
|
+
token: e2eToken,
|
|
1630
|
+
actor: {
|
|
1631
|
+
user_id: e2eActorUserID,
|
|
1632
|
+
},
|
|
1633
|
+
};
|
|
1634
|
+
const sharedHumanManagedBots = [
|
|
1635
|
+
{
|
|
1636
|
+
username: "RyoAI_bot",
|
|
1637
|
+
route: routeRyoai1,
|
|
1638
|
+
bot: { username: "RyoAI_bot", name: "RyoAI_bot" },
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
username: "RyoAI2_bot",
|
|
1642
|
+
route: routeRyoai2,
|
|
1643
|
+
bot: { username: "RyoAI2_bot", name: "RyoAI2_bot" },
|
|
1644
|
+
},
|
|
1645
|
+
{
|
|
1646
|
+
username: "RyoAI3_bot",
|
|
1647
|
+
route: routeRyoai3,
|
|
1648
|
+
bot: { username: "RyoAI3_bot", name: "RyoAI3_bot" },
|
|
1649
|
+
},
|
|
1650
|
+
];
|
|
1651
|
+
await archiveLocalTelegramMessagesForRoute({
|
|
1652
|
+
routeKey: runnerRouteKey(routeRyoai1),
|
|
1653
|
+
route: routeRyoai1,
|
|
1654
|
+
routeState: {},
|
|
1655
|
+
runtime: sharedHumanRouteRuntime,
|
|
1656
|
+
bot: {
|
|
1657
|
+
id: "88888888-8888-4888-8888-888888888881",
|
|
1658
|
+
name: "RyoAI_bot",
|
|
1659
|
+
username: "RyoAI_bot",
|
|
1660
|
+
role: "monitor",
|
|
1661
|
+
},
|
|
1662
|
+
destination: {
|
|
1663
|
+
chatID: e2eDestination.chat_id,
|
|
1664
|
+
},
|
|
1665
|
+
archiveThread: {
|
|
1666
|
+
threadID: e2eThreadID,
|
|
1667
|
+
},
|
|
1668
|
+
managedConversationBots: sharedHumanManagedBots,
|
|
1669
|
+
deps: buildRunnerRuntimeDeps(),
|
|
1670
|
+
});
|
|
1671
|
+
await archiveLocalTelegramMessagesForRoute({
|
|
1672
|
+
routeKey: routeRyoai2Key,
|
|
1673
|
+
route: routeRyoai2,
|
|
1674
|
+
routeState: {},
|
|
1675
|
+
runtime: sharedHumanRouteRuntime,
|
|
1676
|
+
bot: {
|
|
1677
|
+
id: "88888888-8888-4888-8888-888888888882",
|
|
1678
|
+
name: "RyoAI2_bot",
|
|
1679
|
+
username: "RyoAI2_bot",
|
|
1680
|
+
role: "monitor",
|
|
1681
|
+
},
|
|
1682
|
+
destination: {
|
|
1683
|
+
chatID: e2eDestination.chat_id,
|
|
1684
|
+
},
|
|
1685
|
+
archiveThread: {
|
|
1686
|
+
threadID: e2eThreadID,
|
|
1687
|
+
},
|
|
1688
|
+
managedConversationBots: sharedHumanManagedBots,
|
|
1689
|
+
deps: buildRunnerRuntimeDeps(),
|
|
1690
|
+
});
|
|
1691
|
+
await archiveLocalTelegramMessagesForRoute({
|
|
1692
|
+
routeKey: routeRyoai3Key,
|
|
1693
|
+
route: routeRyoai3,
|
|
1694
|
+
routeState: {},
|
|
1695
|
+
runtime: sharedHumanRouteRuntime,
|
|
1696
|
+
bot: {
|
|
1697
|
+
id: "88888888-8888-4888-8888-888888888883",
|
|
1698
|
+
name: "RyoAI3_bot",
|
|
1699
|
+
username: "RyoAI3_bot",
|
|
1700
|
+
role: "monitor",
|
|
1701
|
+
},
|
|
1702
|
+
destination: {
|
|
1703
|
+
chatID: e2eDestination.chat_id,
|
|
1704
|
+
},
|
|
1705
|
+
archiveThread: {
|
|
1706
|
+
threadID: e2eThreadID,
|
|
1707
|
+
},
|
|
1708
|
+
managedConversationBots: sharedHumanManagedBots,
|
|
1709
|
+
deps: buildRunnerRuntimeDeps(),
|
|
1710
|
+
});
|
|
1711
|
+
const sharedHumanBodies = telegramE2EServer.state.comments.map((item) => String(item.body || ""));
|
|
1712
|
+
push(
|
|
1713
|
+
"telegram_multi_bot_human_opening_archives_once_across_routes",
|
|
1714
|
+
sharedHumanBodies.filter((item) => item.includes("message_id: 84")).length === 1,
|
|
1715
|
+
`count=${sharedHumanBodies.filter((item) => item.includes("message_id: 84")).length} bodies=${sharedHumanBodies.join(" || ")}`,
|
|
1716
|
+
);
|
|
1717
|
+
|
|
1718
|
+
telegramE2EServer.state.comments = [];
|
|
1602
1719
|
await archiveLocalTelegramMessagesForRoute({
|
|
1603
1720
|
routeKey: routeRyoai3Key,
|
|
1604
1721
|
route: routeRyoai3,
|