metheus-governance-mcp-cli 0.2.298 → 0.2.304
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 +2512 -678
- package/lib/local-ai-adapters.mjs +96 -9
- package/lib/provider-local-transport.mjs +56 -0
- package/lib/runner-data.mjs +155 -3
- package/lib/runner-delivery-archive-handoff.mjs +20 -0
- package/lib/runner-delivery-context.mjs +49 -8
- package/lib/runner-delivery.mjs +2 -0
- package/lib/runner-execution.mjs +46 -6
- package/lib/runner-helpers.mjs +210 -2
- package/lib/runner-local-inbound-receipts.mjs +23 -0
- package/lib/runner-media-transcription.mjs +232 -0
- package/lib/runner-media-vision.mjs +268 -0
- package/lib/runner-orchestration-adjudication.mjs +145 -5
- package/lib/runner-orchestration-decision-bundle.mjs +111 -22
- package/lib/runner-orchestration-entrypoints.mjs +229 -53
- package/lib/runner-orchestration-selected-record-context.mjs +438 -8
- package/lib/runner-orchestration-selected-record-delivery-handoff.mjs +2 -0
- package/lib/runner-orchestration-selected-record-outcome.mjs +1 -1
- package/lib/runner-orchestration-selected-record-preparation.mjs +63 -9
- package/lib/runner-orchestration-selected-record-reply-outcome.mjs +169 -4
- package/lib/runner-orchestration-selected-record-terminal-outcome.mjs +17 -1
- package/lib/runner-orchestration-step-inputs.mjs +87 -3
- package/lib/runner-orchestration-step-requirements.mjs +274 -0
- package/lib/runner-orchestration-visibility.mjs +9 -3
- package/lib/runner-orchestration.mjs +399 -50
- package/lib/runner-runtime.mjs +299 -9
- package/lib/selftest-runner-scenarios.mjs +3565 -280
- package/lib/selftest-telegram-e2e.mjs +173 -24
- package/package.json +2 -2
package/cli.mjs
CHANGED
|
@@ -137,6 +137,7 @@ import {
|
|
|
137
137
|
import {
|
|
138
138
|
applyPendingAgeSelection,
|
|
139
139
|
buildCanonicalHumanInboundKey as buildRunnerCanonicalHumanInboundKey,
|
|
140
|
+
buildTelegramMediaPlaceholder,
|
|
140
141
|
buildTelegramBotReplyEnvelope,
|
|
141
142
|
buildTelegramMessageEnvelopeFromParsedArchive as buildRunnerTelegramMessageEnvelopeFromParsedArchive,
|
|
142
143
|
buildRunnerRouteDuplicateStateFromComment,
|
|
@@ -147,11 +148,13 @@ import {
|
|
|
147
148
|
isTelegramLocalInboundEnvelopeForRoute,
|
|
148
149
|
isInboundArchiveKind,
|
|
149
150
|
normalizeTelegramMessageEnvelope as normalizeRunnerTelegramMessageEnvelope,
|
|
151
|
+
normalizeTelegramMessageAttachments,
|
|
150
152
|
normalizeArchiveCommentRecord,
|
|
151
153
|
selectPendingArchiveComments,
|
|
152
154
|
printRunnerResult,
|
|
153
155
|
} from "./lib/runner-helpers.mjs";
|
|
154
156
|
import {
|
|
157
|
+
normalizeRunnerRecentLocalInboundReceiptEntry,
|
|
155
158
|
normalizeRunnerRecentLocalInboundReceiptMap,
|
|
156
159
|
} from "./lib/runner-local-inbound-receipts.mjs";
|
|
157
160
|
import {
|
|
@@ -159,30 +162,40 @@ import {
|
|
|
159
162
|
validateRunnerConversationDecisionBundle,
|
|
160
163
|
} from "./lib/runner-orchestration-decision-bundle.mjs";
|
|
161
164
|
import {
|
|
165
|
+
transcribeRunnerTelegramAudioAttachments,
|
|
166
|
+
} from "./lib/runner-media-transcription.mjs";
|
|
167
|
+
import {
|
|
168
|
+
analyzeRunnerTelegramImageAttachments,
|
|
169
|
+
} from "./lib/runner-media-vision.mjs";
|
|
170
|
+
import {
|
|
171
|
+
createProjectPrivateChat as createProjectPrivateChatImpl,
|
|
162
172
|
createProjectContextItem as createProjectContextItemImpl,
|
|
163
|
-
createProjectEvidence as createProjectEvidenceImpl,
|
|
164
|
-
createProjectWorkItem as createProjectWorkItemImpl,
|
|
165
|
-
createThreadComment as createThreadCommentImpl,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
createProjectEvidence as createProjectEvidenceImpl,
|
|
174
|
+
createProjectWorkItem as createProjectWorkItemImpl,
|
|
175
|
+
createThreadComment as createThreadCommentImpl,
|
|
176
|
+
getProjectRunnerSettings as getProjectRunnerSettingsImpl,
|
|
177
|
+
createWorkItemThread as createWorkItemThreadImpl,
|
|
178
|
+
deleteProjectContextItem as deleteProjectContextItemImpl,
|
|
179
|
+
discoverArchiveThreadForDestination as discoverArchiveThreadForDestinationImpl,
|
|
180
|
+
linkWorkItemEvidence as linkWorkItemEvidenceImpl,
|
|
181
|
+
listProjectChatDestinations as listProjectChatDestinationsImpl,
|
|
182
|
+
listProjectContextItems as listProjectContextItemsImpl,
|
|
183
|
+
listProjectCtxpackFiles as listProjectCtxpackFilesImpl,
|
|
184
|
+
listProjectPrivateChats as listProjectPrivateChatsImpl,
|
|
185
|
+
listProjectRunnerRequestCommentStates as listProjectRunnerRequestCommentStatesImpl,
|
|
186
|
+
listProjectRunnerRequests as listProjectRunnerRequestsImpl,
|
|
187
|
+
listWorkItemThreads as listWorkItemThreadsImpl,
|
|
188
|
+
listThreadComments as listThreadCommentsImpl,
|
|
189
|
+
listThreadCommentsTail as listThreadCommentsTailImpl,
|
|
178
190
|
listUserBotsForRunner as listUserBotsForRunnerImpl,
|
|
179
191
|
selectProjectChatDestination as selectProjectChatDestinationImpl,
|
|
180
192
|
selectRunnerBot as selectRunnerBotImpl,
|
|
181
|
-
transitionProjectWorkItem as transitionProjectWorkItemImpl,
|
|
182
|
-
upsertProjectRunnerRequest as upsertProjectRunnerRequestImpl,
|
|
183
|
-
upsertProjectRunnerRequestCommentState as upsertProjectRunnerRequestCommentStateImpl,
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
transitionProjectWorkItem as transitionProjectWorkItemImpl,
|
|
194
|
+
upsertProjectRunnerRequest as upsertProjectRunnerRequestImpl,
|
|
195
|
+
upsertProjectRunnerRequestCommentState as upsertProjectRunnerRequestCommentStateImpl,
|
|
196
|
+
updateProjectPrivateChat as updateProjectPrivateChatImpl,
|
|
197
|
+
updateProjectContextItem as updateProjectContextItemImpl,
|
|
198
|
+
} from "./lib/runner-data.mjs";
|
|
186
199
|
import {
|
|
187
200
|
evaluateTelegramRunnerTrigger,
|
|
188
201
|
resolveRunnerPrecomputedTriggerDecision,
|
|
@@ -239,13 +252,14 @@ import {
|
|
|
239
252
|
formatBotReplyArchiveComment,
|
|
240
253
|
performLocalBotDelivery,
|
|
241
254
|
} from "./lib/runner-delivery.mjs";
|
|
242
|
-
import {
|
|
243
|
-
resolveRunnerExecutionPlan,
|
|
244
|
-
resolveRunnerExecutionPlanForRole,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
255
|
+
import {
|
|
256
|
+
resolveRunnerExecutionPlan,
|
|
257
|
+
resolveRunnerExecutionPlanForRole,
|
|
258
|
+
resolveRunnerLocalAIExecutionProfile,
|
|
259
|
+
resolveRunnerRoleProfile,
|
|
260
|
+
resolveRunnerWorkspaceSelection,
|
|
261
|
+
runRunnerAIExecution,
|
|
262
|
+
} from "./lib/runner-execution.mjs";
|
|
249
263
|
import {
|
|
250
264
|
buildRunnerResponderAdjudicationFromConversationContext,
|
|
251
265
|
buildRunnerResponderAdjudicationFromHumanIntent,
|
|
@@ -1430,13 +1444,14 @@ function botRunnerConfigTemplate() {
|
|
|
1430
1444
|
role_profile: "monitor",
|
|
1431
1445
|
},
|
|
1432
1446
|
},
|
|
1433
|
-
routes: [
|
|
1434
|
-
{
|
|
1435
|
-
name: "telegram-monitor",
|
|
1436
|
-
enabled: false,
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1447
|
+
routes: [
|
|
1448
|
+
{
|
|
1449
|
+
name: "telegram-monitor",
|
|
1450
|
+
enabled: false,
|
|
1451
|
+
route_kind: "room",
|
|
1452
|
+
project_id: "<project_uuid>",
|
|
1453
|
+
provider: "telegram",
|
|
1454
|
+
role: "monitor",
|
|
1440
1455
|
role_profile: "monitor",
|
|
1441
1456
|
server_bot_name: "",
|
|
1442
1457
|
server_bot_id: "",
|
|
@@ -1479,13 +1494,14 @@ function serializeBotRunnerWorkspaceRegistry(projectMappings) {
|
|
|
1479
1494
|
};
|
|
1480
1495
|
}
|
|
1481
1496
|
|
|
1482
|
-
function serializeRunnerRoute(route) {
|
|
1483
|
-
const normalized = normalizeRunnerRoute(route);
|
|
1484
|
-
return {
|
|
1485
|
-
name: normalized.name,
|
|
1486
|
-
enabled: normalized.enabled,
|
|
1487
|
-
|
|
1488
|
-
|
|
1497
|
+
function serializeRunnerRoute(route) {
|
|
1498
|
+
const normalized = normalizeRunnerRoute(route);
|
|
1499
|
+
return {
|
|
1500
|
+
name: normalized.name,
|
|
1501
|
+
enabled: normalized.enabled,
|
|
1502
|
+
route_kind: normalized.routeKind,
|
|
1503
|
+
project_id: normalized.projectID,
|
|
1504
|
+
provider: normalized.provider,
|
|
1489
1505
|
role: normalized.role,
|
|
1490
1506
|
...(normalized.roleProfile ? { role_profile: normalized.roleProfile } : {}),
|
|
1491
1507
|
server_bot_name: normalized.botName,
|
|
@@ -1872,19 +1888,32 @@ function rememberProjectWorkspaceMapping({ projectID, workspaceDir, source }) {
|
|
|
1872
1888
|
};
|
|
1873
1889
|
}
|
|
1874
1890
|
|
|
1875
|
-
function parseRunnerStateRouteKey(routeKey) {
|
|
1876
|
-
const text = String(routeKey || "").trim();
|
|
1877
|
-
if (!text) return null;
|
|
1878
|
-
const parts = text.split("::");
|
|
1879
|
-
if (parts.length < 6) return null;
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1891
|
+
function parseRunnerStateRouteKey(routeKey) {
|
|
1892
|
+
const text = String(routeKey || "").trim();
|
|
1893
|
+
if (!text) return null;
|
|
1894
|
+
const parts = text.split("::");
|
|
1895
|
+
if (parts.length < 6) return null;
|
|
1896
|
+
if (parts.length >= 7) {
|
|
1897
|
+
return {
|
|
1898
|
+
raw: text,
|
|
1899
|
+
name: String(parts[0] || "").trim(),
|
|
1900
|
+
projectID: String(parts[1] || "").trim(),
|
|
1901
|
+
provider: normalizeBotProvider(parts[2]),
|
|
1902
|
+
routeKind: normalizeRunnerRouteKind(parts[3], "room"),
|
|
1903
|
+
role: normalizeBotRole(parts[4]),
|
|
1904
|
+
botSelector: String(parts[5] || "").trim(),
|
|
1905
|
+
destinationSelector: parts.slice(6).join("::").trim(),
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
return {
|
|
1909
|
+
raw: text,
|
|
1910
|
+
name: String(parts[0] || "").trim(),
|
|
1911
|
+
projectID: String(parts[1] || "").trim(),
|
|
1912
|
+
provider: normalizeBotProvider(parts[2]),
|
|
1913
|
+
routeKind: "room",
|
|
1914
|
+
role: normalizeBotRole(parts[3]),
|
|
1915
|
+
botSelector: String(parts[4] || "").trim(),
|
|
1916
|
+
destinationSelector: parts.slice(5).join("::").trim(),
|
|
1888
1917
|
};
|
|
1889
1918
|
}
|
|
1890
1919
|
|
|
@@ -1920,19 +1949,22 @@ function findConfiguredRoutesForAnonymousStateKey(parsedKey, runnerConfig) {
|
|
|
1920
1949
|
const enabledRoutes = ensureArray(runnerConfig?.routes)
|
|
1921
1950
|
.map((route) => normalizeRunnerRoute(route))
|
|
1922
1951
|
.filter((route) => route.enabled);
|
|
1923
|
-
return enabledRoutes.filter((route) => {
|
|
1924
|
-
if (route.projectID !== routeKey.projectID) return false;
|
|
1925
|
-
if (route.provider !== routeKey.provider) return false;
|
|
1926
|
-
if (route.
|
|
1927
|
-
if (
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
}
|
|
1952
|
+
return enabledRoutes.filter((route) => {
|
|
1953
|
+
if (route.projectID !== routeKey.projectID) return false;
|
|
1954
|
+
if (route.provider !== routeKey.provider) return false;
|
|
1955
|
+
if ((route.routeKind || "room") !== (routeKey.routeKind || "room")) return false;
|
|
1956
|
+
if (route.role !== routeKey.role) return false;
|
|
1957
|
+
if (!runnerStateSelectorMatchesRoute(routeKey.botSelector, route.botID, route.botName)) {
|
|
1958
|
+
return false;
|
|
1959
|
+
}
|
|
1960
|
+
const routeTargetID = route.routeKind === "dm" ? "direct-messages" : route.destinationID;
|
|
1961
|
+
const routeTargetLabel = route.routeKind === "dm" ? describeRunnerRouteTargetLabel(route) : route.destinationLabel;
|
|
1962
|
+
if (!runnerStateSelectorMatchesRoute(routeKey.destinationSelector, routeTargetID, routeTargetLabel)) {
|
|
1963
|
+
return false;
|
|
1964
|
+
}
|
|
1965
|
+
return true;
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1936
1968
|
|
|
1937
1969
|
function configuredRunnerRouteKeys(runnerConfig) {
|
|
1938
1970
|
return new Set(
|
|
@@ -2979,10 +3011,10 @@ function normalizeBotRunnerRequests(rawRequests, nowMs = Date.now()) {
|
|
|
2979
3011
|
source_bot_username: entry.source_message_bot_username || entry.sourceMessageBotUsername,
|
|
2980
3012
|
}
|
|
2981
3013
|
: {};
|
|
2982
|
-
const normalizedSourceMessageEnvelope = normalizeRunnerTelegramMessageEnvelope({
|
|
2983
|
-
...safeObject(
|
|
2984
|
-
normalizeRunnerTelegramMessageEnvelope(
|
|
2985
|
-
entry.source_message_envelope
|
|
3014
|
+
const normalizedSourceMessageEnvelope = normalizeRunnerTelegramMessageEnvelope({
|
|
3015
|
+
...safeObject(
|
|
3016
|
+
normalizeRunnerTelegramMessageEnvelope(
|
|
3017
|
+
entry.source_message_envelope
|
|
2986
3018
|
|| entry.sourceMessageEnvelope
|
|
2987
3019
|
|| fallbackSourceMessageEnvelope,
|
|
2988
3020
|
),
|
|
@@ -3028,10 +3060,155 @@ function normalizeBotRunnerRequests(rawRequests, nowMs = Date.now()) {
|
|
|
3028
3060
|
source_bot_username: firstNonEmptyString([
|
|
3029
3061
|
safeObject(entry.source_message_envelope || entry.sourceMessageEnvelope).source_bot_username,
|
|
3030
3062
|
safeObject(entry.source_message_envelope || entry.sourceMessageEnvelope).sourceBotUsername,
|
|
3031
|
-
entry.source_message_bot_username,
|
|
3032
|
-
entry.sourceMessageBotUsername,
|
|
3033
|
-
]),
|
|
3034
|
-
});
|
|
3063
|
+
entry.source_message_bot_username,
|
|
3064
|
+
entry.sourceMessageBotUsername,
|
|
3065
|
+
]),
|
|
3066
|
+
});
|
|
3067
|
+
const normalizedRootSourceMessageEnvelope = normalizeRunnerTelegramMessageEnvelope({
|
|
3068
|
+
...safeObject(
|
|
3069
|
+
normalizeRunnerTelegramMessageEnvelope(
|
|
3070
|
+
entry.root_source_message_envelope
|
|
3071
|
+
|| entry.rootSourceMessageEnvelope
|
|
3072
|
+
|| normalizedSourceMessageEnvelope,
|
|
3073
|
+
),
|
|
3074
|
+
),
|
|
3075
|
+
chat_id: firstNonEmptyString([
|
|
3076
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).chat_id,
|
|
3077
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).chatID,
|
|
3078
|
+
normalizedSourceMessageEnvelope.chat_id,
|
|
3079
|
+
entry.chat_id,
|
|
3080
|
+
entry.chatID,
|
|
3081
|
+
]),
|
|
3082
|
+
message_id: intFromRawAllowZero(
|
|
3083
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).message_id
|
|
3084
|
+
|| safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).messageID
|
|
3085
|
+
|| entry.root_source_message_id
|
|
3086
|
+
|| entry.rootSourceMessageID
|
|
3087
|
+
|| entry.source_message_id
|
|
3088
|
+
|| entry.sourceMessageID,
|
|
3089
|
+
0,
|
|
3090
|
+
) || undefined,
|
|
3091
|
+
message_thread_id: intFromRawAllowZero(
|
|
3092
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).message_thread_id
|
|
3093
|
+
|| safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).messageThreadID
|
|
3094
|
+
|| entry.root_source_message_thread_id
|
|
3095
|
+
|| entry.rootSourceMessageThreadID
|
|
3096
|
+
|| entry.source_message_thread_id
|
|
3097
|
+
|| entry.sourceMessageThreadID,
|
|
3098
|
+
0,
|
|
3099
|
+
) || undefined,
|
|
3100
|
+
body: firstNonEmptyString([
|
|
3101
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).body,
|
|
3102
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).text,
|
|
3103
|
+
entry.root_source_message_body,
|
|
3104
|
+
entry.rootSourceMessageBody,
|
|
3105
|
+
entry.source_message_body,
|
|
3106
|
+
entry.sourceMessageBody,
|
|
3107
|
+
normalizedSourceMessageEnvelope.body,
|
|
3108
|
+
]),
|
|
3109
|
+
source_origin: firstNonEmptyString([
|
|
3110
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).source_origin,
|
|
3111
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).sourceOrigin,
|
|
3112
|
+
entry.root_source_message_origin,
|
|
3113
|
+
entry.rootSourceMessageOrigin,
|
|
3114
|
+
entry.source_message_origin,
|
|
3115
|
+
entry.sourceMessageOrigin,
|
|
3116
|
+
normalizedSourceMessageEnvelope.source_origin,
|
|
3117
|
+
]),
|
|
3118
|
+
source_route_key: firstNonEmptyString([
|
|
3119
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).source_route_key,
|
|
3120
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).sourceRouteKey,
|
|
3121
|
+
entry.root_source_message_route_key,
|
|
3122
|
+
entry.rootSourceMessageRouteKey,
|
|
3123
|
+
entry.source_message_route_key,
|
|
3124
|
+
entry.sourceMessageRouteKey,
|
|
3125
|
+
normalizedSourceMessageEnvelope.source_route_key,
|
|
3126
|
+
]),
|
|
3127
|
+
source_bot_username: firstNonEmptyString([
|
|
3128
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).source_bot_username,
|
|
3129
|
+
safeObject(entry.root_source_message_envelope || entry.rootSourceMessageEnvelope).sourceBotUsername,
|
|
3130
|
+
entry.root_source_message_bot_username,
|
|
3131
|
+
entry.rootSourceMessageBotUsername,
|
|
3132
|
+
entry.source_message_bot_username,
|
|
3133
|
+
entry.sourceMessageBotUsername,
|
|
3134
|
+
normalizedSourceMessageEnvelope.source_bot_username,
|
|
3135
|
+
]),
|
|
3136
|
+
});
|
|
3137
|
+
const normalizedCurrentSourceMessageEnvelope = normalizeRunnerTelegramMessageEnvelope({
|
|
3138
|
+
...safeObject(
|
|
3139
|
+
normalizeRunnerTelegramMessageEnvelope(
|
|
3140
|
+
entry.current_source_message_envelope
|
|
3141
|
+
|| entry.currentSourceMessageEnvelope
|
|
3142
|
+
|| normalizedSourceMessageEnvelope,
|
|
3143
|
+
),
|
|
3144
|
+
),
|
|
3145
|
+
chat_id: firstNonEmptyString([
|
|
3146
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).chat_id,
|
|
3147
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).chatID,
|
|
3148
|
+
normalizedSourceMessageEnvelope.chat_id,
|
|
3149
|
+
entry.chat_id,
|
|
3150
|
+
entry.chatID,
|
|
3151
|
+
]),
|
|
3152
|
+
message_id: intFromRawAllowZero(
|
|
3153
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).message_id
|
|
3154
|
+
|| safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).messageID
|
|
3155
|
+
|| entry.current_source_message_id
|
|
3156
|
+
|| entry.currentSourceMessageID
|
|
3157
|
+
|| entry.last_source_message_id
|
|
3158
|
+
|| entry.lastSourceMessageID
|
|
3159
|
+
|| normalizedSourceMessageEnvelope.message_id,
|
|
3160
|
+
0,
|
|
3161
|
+
) || undefined,
|
|
3162
|
+
message_thread_id: intFromRawAllowZero(
|
|
3163
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).message_thread_id
|
|
3164
|
+
|| safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).messageThreadID
|
|
3165
|
+
|| entry.current_source_message_thread_id
|
|
3166
|
+
|| entry.currentSourceMessageThreadID
|
|
3167
|
+
|| entry.last_source_message_thread_id
|
|
3168
|
+
|| entry.lastSourceMessageThreadID
|
|
3169
|
+
|| normalizedSourceMessageEnvelope.message_thread_id,
|
|
3170
|
+
0,
|
|
3171
|
+
) || undefined,
|
|
3172
|
+
body: firstNonEmptyString([
|
|
3173
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).body,
|
|
3174
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).text,
|
|
3175
|
+
entry.current_source_message_body,
|
|
3176
|
+
entry.currentSourceMessageBody,
|
|
3177
|
+
entry.source_message_body,
|
|
3178
|
+
entry.sourceMessageBody,
|
|
3179
|
+
normalizedSourceMessageEnvelope.body,
|
|
3180
|
+
]),
|
|
3181
|
+
source_origin: firstNonEmptyString([
|
|
3182
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).source_origin,
|
|
3183
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).sourceOrigin,
|
|
3184
|
+
entry.current_source_message_origin,
|
|
3185
|
+
entry.currentSourceMessageOrigin,
|
|
3186
|
+
entry.source_message_origin,
|
|
3187
|
+
entry.sourceMessageOrigin,
|
|
3188
|
+
normalizedSourceMessageEnvelope.source_origin,
|
|
3189
|
+
]),
|
|
3190
|
+
source_route_key: firstNonEmptyString([
|
|
3191
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).source_route_key,
|
|
3192
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).sourceRouteKey,
|
|
3193
|
+
entry.current_source_message_route_key,
|
|
3194
|
+
entry.currentSourceMessageRouteKey,
|
|
3195
|
+
entry.source_message_route_key,
|
|
3196
|
+
entry.sourceMessageRouteKey,
|
|
3197
|
+
normalizedSourceMessageEnvelope.source_route_key,
|
|
3198
|
+
]),
|
|
3199
|
+
source_bot_username: firstNonEmptyString([
|
|
3200
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).source_bot_username,
|
|
3201
|
+
safeObject(entry.current_source_message_envelope || entry.currentSourceMessageEnvelope).sourceBotUsername,
|
|
3202
|
+
entry.current_source_message_bot_username,
|
|
3203
|
+
entry.currentSourceMessageBotUsername,
|
|
3204
|
+
entry.source_message_bot_username,
|
|
3205
|
+
entry.sourceMessageBotUsername,
|
|
3206
|
+
normalizedSourceMessageEnvelope.source_bot_username,
|
|
3207
|
+
]),
|
|
3208
|
+
});
|
|
3209
|
+
const normalizedReplyChainContext = normalizeRunnerReplyChainContext(
|
|
3210
|
+
entry.reply_chain_context || entry.replyChainContext,
|
|
3211
|
+
);
|
|
3035
3212
|
normalized[requestKey] = {
|
|
3036
3213
|
request_key: requestKey,
|
|
3037
3214
|
project_id: String(entry.project_id || entry.projectID || "").trim(),
|
|
@@ -3075,16 +3252,108 @@ function normalizeBotRunnerRequests(rawRequests, nowMs = Date.now()) {
|
|
|
3075
3252
|
|| normalizedSourceMessageEnvelope.source_route_key
|
|
3076
3253
|
|| "",
|
|
3077
3254
|
).trim(),
|
|
3078
|
-
source_message_bot_username: normalizeTelegramMentionUsername(
|
|
3079
|
-
entry.source_message_bot_username
|
|
3080
|
-
|| entry.sourceMessageBotUsername
|
|
3081
|
-
|| normalizedSourceMessageEnvelope.source_bot_username,
|
|
3082
|
-
),
|
|
3083
|
-
source_message_envelope: normalizedSourceMessageEnvelope,
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3255
|
+
source_message_bot_username: normalizeTelegramMentionUsername(
|
|
3256
|
+
entry.source_message_bot_username
|
|
3257
|
+
|| entry.sourceMessageBotUsername
|
|
3258
|
+
|| normalizedSourceMessageEnvelope.source_bot_username,
|
|
3259
|
+
),
|
|
3260
|
+
source_message_envelope: normalizedSourceMessageEnvelope,
|
|
3261
|
+
root_source_message_id: intFromRawAllowZero(
|
|
3262
|
+
entry.root_source_message_id
|
|
3263
|
+
|| entry.rootSourceMessageID
|
|
3264
|
+
|| entry.source_message_id
|
|
3265
|
+
|| entry.sourceMessageID
|
|
3266
|
+
|| normalizedRootSourceMessageEnvelope.message_id,
|
|
3267
|
+
0,
|
|
3268
|
+
) || undefined,
|
|
3269
|
+
root_source_message_thread_id: intFromRawAllowZero(
|
|
3270
|
+
entry.root_source_message_thread_id
|
|
3271
|
+
|| entry.rootSourceMessageThreadID
|
|
3272
|
+
|| entry.source_message_thread_id
|
|
3273
|
+
|| entry.sourceMessageThreadID
|
|
3274
|
+
|| normalizedRootSourceMessageEnvelope.message_thread_id,
|
|
3275
|
+
0,
|
|
3276
|
+
) || undefined,
|
|
3277
|
+
root_source_message_body: firstNonEmptyString([
|
|
3278
|
+
entry.root_source_message_body,
|
|
3279
|
+
entry.rootSourceMessageBody,
|
|
3280
|
+
entry.source_message_body,
|
|
3281
|
+
entry.sourceMessageBody,
|
|
3282
|
+
normalizedRootSourceMessageEnvelope.body,
|
|
3283
|
+
]),
|
|
3284
|
+
root_source_message_origin: String(
|
|
3285
|
+
entry.root_source_message_origin
|
|
3286
|
+
|| entry.rootSourceMessageOrigin
|
|
3287
|
+
|| entry.source_message_origin
|
|
3288
|
+
|| entry.sourceMessageOrigin
|
|
3289
|
+
|| normalizedRootSourceMessageEnvelope.source_origin
|
|
3290
|
+
|| "",
|
|
3291
|
+
).trim().toLowerCase(),
|
|
3292
|
+
root_source_message_route_key: String(
|
|
3293
|
+
entry.root_source_message_route_key
|
|
3294
|
+
|| entry.rootSourceMessageRouteKey
|
|
3295
|
+
|| entry.source_message_route_key
|
|
3296
|
+
|| entry.sourceMessageRouteKey
|
|
3297
|
+
|| normalizedRootSourceMessageEnvelope.source_route_key
|
|
3298
|
+
|| "",
|
|
3299
|
+
).trim(),
|
|
3300
|
+
root_source_message_bot_username: normalizeTelegramMentionUsername(
|
|
3301
|
+
entry.root_source_message_bot_username
|
|
3302
|
+
|| entry.rootSourceMessageBotUsername
|
|
3303
|
+
|| entry.source_message_bot_username
|
|
3304
|
+
|| entry.sourceMessageBotUsername
|
|
3305
|
+
|| normalizedRootSourceMessageEnvelope.source_bot_username,
|
|
3306
|
+
),
|
|
3307
|
+
root_source_message_envelope: normalizedRootSourceMessageEnvelope,
|
|
3308
|
+
current_source_message_id: intFromRawAllowZero(
|
|
3309
|
+
entry.current_source_message_id
|
|
3310
|
+
|| entry.currentSourceMessageID
|
|
3311
|
+
|| entry.last_source_message_id
|
|
3312
|
+
|| entry.lastSourceMessageID
|
|
3313
|
+
|| normalizedCurrentSourceMessageEnvelope.message_id,
|
|
3314
|
+
0,
|
|
3315
|
+
) || undefined,
|
|
3316
|
+
current_source_message_thread_id: intFromRawAllowZero(
|
|
3317
|
+
entry.current_source_message_thread_id
|
|
3318
|
+
|| entry.currentSourceMessageThreadID
|
|
3319
|
+
|| entry.last_source_message_thread_id
|
|
3320
|
+
|| entry.lastSourceMessageThreadID
|
|
3321
|
+
|| normalizedCurrentSourceMessageEnvelope.message_thread_id,
|
|
3322
|
+
0,
|
|
3323
|
+
) || undefined,
|
|
3324
|
+
current_source_message_body: firstNonEmptyString([
|
|
3325
|
+
entry.current_source_message_body,
|
|
3326
|
+
entry.currentSourceMessageBody,
|
|
3327
|
+
normalizedCurrentSourceMessageEnvelope.body,
|
|
3328
|
+
]),
|
|
3329
|
+
current_source_message_origin: String(
|
|
3330
|
+
entry.current_source_message_origin
|
|
3331
|
+
|| entry.currentSourceMessageOrigin
|
|
3332
|
+
|| normalizedCurrentSourceMessageEnvelope.source_origin
|
|
3333
|
+
|| "",
|
|
3334
|
+
).trim().toLowerCase(),
|
|
3335
|
+
current_source_message_route_key: String(
|
|
3336
|
+
entry.current_source_message_route_key
|
|
3337
|
+
|| entry.currentSourceMessageRouteKey
|
|
3338
|
+
|| normalizedCurrentSourceMessageEnvelope.source_route_key
|
|
3339
|
+
|| "",
|
|
3340
|
+
).trim(),
|
|
3341
|
+
current_source_message_bot_username: normalizeTelegramMentionUsername(
|
|
3342
|
+
entry.current_source_message_bot_username
|
|
3343
|
+
|| entry.currentSourceMessageBotUsername
|
|
3344
|
+
|| normalizedCurrentSourceMessageEnvelope.source_bot_username,
|
|
3345
|
+
),
|
|
3346
|
+
current_source_message_envelope: normalizedCurrentSourceMessageEnvelope,
|
|
3347
|
+
current_turn_relation: String(
|
|
3348
|
+
entry.current_turn_relation
|
|
3349
|
+
|| entry.currentTurnRelation
|
|
3350
|
+
|| normalizedReplyChainContext.current_turn_relation
|
|
3351
|
+
|| "",
|
|
3352
|
+
).trim(),
|
|
3353
|
+
root_comment_id: String(entry.root_comment_id || entry.rootCommentID || "").trim(),
|
|
3354
|
+
root_comment_kind: String(entry.root_comment_kind || entry.rootCommentKind || "").trim().toLowerCase(),
|
|
3355
|
+
conversation_id: String(entry.conversation_id || entry.conversationId || "").trim(),
|
|
3356
|
+
reply_chain_context: normalizedReplyChainContext,
|
|
3088
3357
|
selected_bot_usernames: ensureArray(entry.selected_bot_usernames || entry.selectedBotUsernames)
|
|
3089
3358
|
.map((value) => normalizeTelegramMentionUsername(value))
|
|
3090
3359
|
.filter(Boolean),
|
|
@@ -3095,12 +3364,25 @@ function normalizeBotRunnerRequests(rawRequests, nowMs = Date.now()) {
|
|
|
3095
3364
|
entry.authoritative_decision_bundle || entry.authoritativeDecisionBundle,
|
|
3096
3365
|
)
|
|
3097
3366
|
: {},
|
|
3367
|
+
last_reply_decision_bundle: Object.keys(
|
|
3368
|
+
safeObject(entry.last_reply_decision_bundle || entry.lastReplyDecisionBundle),
|
|
3369
|
+
).length
|
|
3370
|
+
? normalizeRunnerConversationDecisionBundle(
|
|
3371
|
+
entry.last_reply_decision_bundle || entry.lastReplyDecisionBundle,
|
|
3372
|
+
)
|
|
3373
|
+
: {},
|
|
3098
3374
|
decision_bundle_validation_status: String(
|
|
3099
3375
|
entry.decision_bundle_validation_status || entry.decisionBundleValidationStatus || "",
|
|
3100
3376
|
).trim().toLowerCase(),
|
|
3101
3377
|
decision_bundle_validation_reason: String(
|
|
3102
3378
|
entry.decision_bundle_validation_reason || entry.decisionBundleValidationReason || "",
|
|
3103
3379
|
).trim(),
|
|
3380
|
+
last_reply_decision_bundle_validation_status: String(
|
|
3381
|
+
entry.last_reply_decision_bundle_validation_status || entry.lastReplyDecisionBundleValidationStatus || "",
|
|
3382
|
+
).trim().toLowerCase(),
|
|
3383
|
+
last_reply_decision_bundle_validation_reason: String(
|
|
3384
|
+
entry.last_reply_decision_bundle_validation_reason || entry.lastReplyDecisionBundleValidationReason || "",
|
|
3385
|
+
).trim(),
|
|
3104
3386
|
conversation_allowed_responders: ensureArray(entry.conversation_allowed_responders || entry.conversationAllowedResponders)
|
|
3105
3387
|
.map((value) => normalizeTelegramMentionUsername(value))
|
|
3106
3388
|
.filter(Boolean),
|
|
@@ -3602,7 +3884,7 @@ function findRunnerRequestsForScope(state, normalizedRoute, selectors = {}) {
|
|
|
3602
3884
|
});
|
|
3603
3885
|
}
|
|
3604
3886
|
|
|
3605
|
-
function findScopedConversationSessionState(state, normalizedRoute, conversationIDRaw = "") {
|
|
3887
|
+
function findScopedConversationSessionState(state, normalizedRoute, conversationIDRaw = "") {
|
|
3606
3888
|
const conversationID = String(conversationIDRaw || "").trim();
|
|
3607
3889
|
if (!conversationID) {
|
|
3608
3890
|
return {};
|
|
@@ -3661,11 +3943,315 @@ function findScopedConversationSessionState(state, normalizedRoute, conversation
|
|
|
3661
3943
|
}
|
|
3662
3944
|
return String(left.routeKey || "").localeCompare(String(right.routeKey || ""));
|
|
3663
3945
|
});
|
|
3664
|
-
return safeObject(ranked[0]);
|
|
3665
|
-
}
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3946
|
+
return safeObject(ranked[0]);
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3949
|
+
const DIRECT_MESSAGE_CONTINUATION_LOOKBACK_MS = 45 * 60 * 1000;
|
|
3950
|
+
|
|
3951
|
+
function isPrivateTelegramChatID(chatIDRaw = "") {
|
|
3952
|
+
const chatID = String(chatIDRaw || "").trim();
|
|
3953
|
+
if (!chatID) return false;
|
|
3954
|
+
const numeric = Number.parseInt(chatID, 10);
|
|
3955
|
+
return Number.isFinite(numeric) && numeric > 0;
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
function isRunnerOpenDirectMessageSession(sessionRaw, {
|
|
3959
|
+
chatID = "",
|
|
3960
|
+
botUsername = "",
|
|
3961
|
+
nowMs = Date.now(),
|
|
3962
|
+
} = {}) {
|
|
3963
|
+
const session = safeObject(sessionRaw);
|
|
3964
|
+
if (String(session.mode || "").trim().toLowerCase() !== "direct_single_bot") {
|
|
3965
|
+
return false;
|
|
3966
|
+
}
|
|
3967
|
+
if (String(session.status || "").trim().toLowerCase() !== "open") {
|
|
3968
|
+
return false;
|
|
3969
|
+
}
|
|
3970
|
+
const normalizedChatID = String(chatID || "").trim();
|
|
3971
|
+
if (normalizedChatID && String(session.chat_id || "").trim() !== normalizedChatID) {
|
|
3972
|
+
return false;
|
|
3973
|
+
}
|
|
3974
|
+
const normalizedBotUsername = normalizeTelegramMentionUsername(botUsername);
|
|
3975
|
+
if (
|
|
3976
|
+
normalizedBotUsername
|
|
3977
|
+
&& normalizeTelegramMentionUsername(session.bot_username) !== normalizedBotUsername
|
|
3978
|
+
) {
|
|
3979
|
+
return false;
|
|
3980
|
+
}
|
|
3981
|
+
const expiresAtMs = Date.parse(String(session.expires_at || "").trim());
|
|
3982
|
+
if (Number.isFinite(expiresAtMs) && expiresAtMs > 0 && expiresAtMs <= nowMs) {
|
|
3983
|
+
return false;
|
|
3984
|
+
}
|
|
3985
|
+
return true;
|
|
3986
|
+
}
|
|
3987
|
+
|
|
3988
|
+
function findRunnerDirectMessageSessionCandidate({
|
|
3989
|
+
state,
|
|
3990
|
+
normalizedRoute,
|
|
3991
|
+
routeKey = "",
|
|
3992
|
+
chatID = "",
|
|
3993
|
+
botUsername = "",
|
|
3994
|
+
nowMs = Date.now(),
|
|
3995
|
+
}) {
|
|
3996
|
+
const resolvedRouteKey = String(routeKey || runnerRouteKey(normalizedRoute)).trim();
|
|
3997
|
+
if (!resolvedRouteKey || !isPrivateTelegramChatID(chatID)) {
|
|
3998
|
+
return {};
|
|
3999
|
+
}
|
|
4000
|
+
const routeState = safeObject(safeObject(state?.routes)[resolvedRouteKey]);
|
|
4001
|
+
const ranked = Object.entries(safeObject(routeState.conversation_sessions))
|
|
4002
|
+
.map(([conversationID, sessionRaw]) => ({
|
|
4003
|
+
conversationID: String(conversationID || "").trim(),
|
|
4004
|
+
session: safeObject(sessionRaw),
|
|
4005
|
+
}))
|
|
4006
|
+
.filter((candidate) => candidate.conversationID)
|
|
4007
|
+
.filter((candidate) => isRunnerOpenDirectMessageSession(candidate.session, {
|
|
4008
|
+
chatID,
|
|
4009
|
+
botUsername,
|
|
4010
|
+
nowMs,
|
|
4011
|
+
}))
|
|
4012
|
+
.sort((left, right) => {
|
|
4013
|
+
const leftActivity = firstNonEmptyString([
|
|
4014
|
+
left.session.last_activity_at,
|
|
4015
|
+
left.session.updated_at,
|
|
4016
|
+
left.session.started_at,
|
|
4017
|
+
]);
|
|
4018
|
+
const rightActivity = firstNonEmptyString([
|
|
4019
|
+
right.session.last_activity_at,
|
|
4020
|
+
right.session.updated_at,
|
|
4021
|
+
right.session.started_at,
|
|
4022
|
+
]);
|
|
4023
|
+
if (leftActivity && rightActivity && leftActivity !== rightActivity) {
|
|
4024
|
+
return leftActivity < rightActivity ? 1 : -1;
|
|
4025
|
+
}
|
|
4026
|
+
return String(left.conversationID || "").localeCompare(String(right.conversationID || ""));
|
|
4027
|
+
});
|
|
4028
|
+
return safeObject(ranked[0]);
|
|
4029
|
+
}
|
|
4030
|
+
|
|
4031
|
+
function isRunnerDirectMessageContinuationRequestCandidate(entryRaw, {
|
|
4032
|
+
routeKey = "",
|
|
4033
|
+
chatID = "",
|
|
4034
|
+
botUsername = "",
|
|
4035
|
+
nowMs = Date.now(),
|
|
4036
|
+
} = {}) {
|
|
4037
|
+
const entry = safeObject(entryRaw);
|
|
4038
|
+
const normalizedChatID = String(chatID || "").trim();
|
|
4039
|
+
if (!normalizedChatID || String(entry.chat_id || "").trim() !== normalizedChatID) {
|
|
4040
|
+
return false;
|
|
4041
|
+
}
|
|
4042
|
+
const normalizedRouteKey = String(routeKey || "").trim();
|
|
4043
|
+
if (normalizedRouteKey) {
|
|
4044
|
+
const entryRouteKey = firstNonEmptyString([
|
|
4045
|
+
entry.claimed_by_route,
|
|
4046
|
+
entry.source_message_route_key,
|
|
4047
|
+
safeObject(entry.source_message_envelope).source_route_key,
|
|
4048
|
+
]);
|
|
4049
|
+
if (entryRouteKey && entryRouteKey !== normalizedRouteKey) {
|
|
4050
|
+
return false;
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
const normalizedBotUsername = normalizeTelegramMentionUsername(botUsername);
|
|
4054
|
+
if (normalizedBotUsername) {
|
|
4055
|
+
const entryBotUsername = normalizeTelegramMentionUsername(
|
|
4056
|
+
entry.source_message_bot_username
|
|
4057
|
+
|| safeObject(entry.source_message_envelope).source_bot_username,
|
|
4058
|
+
);
|
|
4059
|
+
if (entryBotUsername && entryBotUsername !== normalizedBotUsername) {
|
|
4060
|
+
return false;
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
4063
|
+
const updatedAt = firstNonEmptyString([
|
|
4064
|
+
entry.updated_at,
|
|
4065
|
+
entry.completed_at,
|
|
4066
|
+
entry.closed_at,
|
|
4067
|
+
entry.claimed_at,
|
|
4068
|
+
]);
|
|
4069
|
+
const updatedAtMs = Date.parse(updatedAt);
|
|
4070
|
+
return !Number.isFinite(updatedAtMs) || nowMs - updatedAtMs <= DIRECT_MESSAGE_CONTINUATION_LOOKBACK_MS;
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
function sortRunnerDirectMessageContinuationCandidates(entries = []) {
|
|
4074
|
+
return ensureArray(entries).slice().sort((leftRaw, rightRaw) => {
|
|
4075
|
+
const left = safeObject(leftRaw);
|
|
4076
|
+
const right = safeObject(rightRaw);
|
|
4077
|
+
const leftStatus = normalizeRunnerRequestStatus(left.status);
|
|
4078
|
+
const rightStatus = normalizeRunnerRequestStatus(right.status);
|
|
4079
|
+
const leftActive = isActiveRunnerRequestStatus(leftStatus);
|
|
4080
|
+
const rightActive = isActiveRunnerRequestStatus(rightStatus);
|
|
4081
|
+
if (leftActive !== rightActive) {
|
|
4082
|
+
return leftActive ? -1 : 1;
|
|
4083
|
+
}
|
|
4084
|
+
const leftTime = firstNonEmptyString([left.updated_at, left.completed_at, left.closed_at, left.claimed_at]);
|
|
4085
|
+
const rightTime = firstNonEmptyString([right.updated_at, right.completed_at, right.closed_at, right.claimed_at]);
|
|
4086
|
+
if (leftTime && rightTime && leftTime !== rightTime) {
|
|
4087
|
+
return leftTime < rightTime ? 1 : -1;
|
|
4088
|
+
}
|
|
4089
|
+
return String(left.request_key || "").localeCompare(String(right.request_key || ""));
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
|
|
4093
|
+
function findRunnerDirectMessageContinuationSourceRequest({
|
|
4094
|
+
state,
|
|
4095
|
+
normalizedRoute,
|
|
4096
|
+
routeKey = "",
|
|
4097
|
+
routeState = {},
|
|
4098
|
+
chatID = "",
|
|
4099
|
+
botUsername = "",
|
|
4100
|
+
nowMs = Date.now(),
|
|
4101
|
+
}) {
|
|
4102
|
+
const normalizedChatID = String(chatID || "").trim();
|
|
4103
|
+
if (!isPrivateTelegramChatID(normalizedChatID)) {
|
|
4104
|
+
return null;
|
|
4105
|
+
}
|
|
4106
|
+
const requestsByKey = normalizeBotRunnerRequests(safeObject(state).requests, nowMs);
|
|
4107
|
+
const resolvedRouteKey = String(routeKey || runnerRouteKey(normalizedRoute)).trim();
|
|
4108
|
+
const currentRouteState = safeObject(routeState);
|
|
4109
|
+
const resolveCandidateByKey = (requestKeyRaw = "") => {
|
|
4110
|
+
const requestKey = String(requestKeyRaw || "").trim();
|
|
4111
|
+
if (!requestKey) return null;
|
|
4112
|
+
const candidate = safeObject(requestsByKey[requestKey]);
|
|
4113
|
+
return candidate.request_key && isRunnerDirectMessageContinuationRequestCandidate(candidate, {
|
|
4114
|
+
routeKey: resolvedRouteKey,
|
|
4115
|
+
chatID: normalizedChatID,
|
|
4116
|
+
botUsername,
|
|
4117
|
+
nowMs,
|
|
4118
|
+
})
|
|
4119
|
+
? candidate
|
|
4120
|
+
: null;
|
|
4121
|
+
};
|
|
4122
|
+
const activeRequestCandidate = resolveCandidateByKey(currentRouteState.active_request_key);
|
|
4123
|
+
if (activeRequestCandidate?.request_key) {
|
|
4124
|
+
return activeRequestCandidate;
|
|
4125
|
+
}
|
|
4126
|
+
const sessionCandidate = findRunnerDirectMessageSessionCandidate({
|
|
4127
|
+
state,
|
|
4128
|
+
normalizedRoute,
|
|
4129
|
+
routeKey: resolvedRouteKey,
|
|
4130
|
+
chatID: normalizedChatID,
|
|
4131
|
+
botUsername,
|
|
4132
|
+
nowMs,
|
|
4133
|
+
});
|
|
4134
|
+
const sessionRequestCandidate = resolveCandidateByKey(safeObject(sessionCandidate.session).request_key);
|
|
4135
|
+
if (sessionRequestCandidate?.request_key) {
|
|
4136
|
+
return sessionRequestCandidate;
|
|
4137
|
+
}
|
|
4138
|
+
const lastRequestCandidate = resolveCandidateByKey(currentRouteState.last_request_key);
|
|
4139
|
+
if (lastRequestCandidate?.request_key) {
|
|
4140
|
+
return lastRequestCandidate;
|
|
4141
|
+
}
|
|
4142
|
+
const scopedCandidates = sortRunnerDirectMessageContinuationCandidates(
|
|
4143
|
+
findRunnerRequestsForScope(state, normalizedRoute, { chatID: normalizedChatID })
|
|
4144
|
+
.filter((entry) => isRunnerDirectMessageContinuationRequestCandidate(entry, {
|
|
4145
|
+
routeKey: resolvedRouteKey,
|
|
4146
|
+
chatID: normalizedChatID,
|
|
4147
|
+
botUsername,
|
|
4148
|
+
nowMs,
|
|
4149
|
+
})),
|
|
4150
|
+
);
|
|
4151
|
+
return safeObject(scopedCandidates[0]).request_key ? safeObject(scopedCandidates[0]) : null;
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
function isRunnerDirectMessageRestartMessage(selectedRecordRaw) {
|
|
4155
|
+
const parsed = safeObject(safeObject(selectedRecordRaw).parsedArchive);
|
|
4156
|
+
if (!isPrivateTelegramChatID(String(parsed.chatID || parsed.chatId || "").trim())) {
|
|
4157
|
+
return false;
|
|
4158
|
+
}
|
|
4159
|
+
const body = String(parsed.body || "").trim();
|
|
4160
|
+
if (!body) {
|
|
4161
|
+
return false;
|
|
4162
|
+
}
|
|
4163
|
+
return /^\/start(?:@[A-Za-z0-9_]+)?(?:\s|$)/i.test(body);
|
|
4164
|
+
}
|
|
4165
|
+
|
|
4166
|
+
function closeRunnerDirectMessageScopedActiveRequests({
|
|
4167
|
+
state,
|
|
4168
|
+
normalizedRoute,
|
|
4169
|
+
routeKey = "",
|
|
4170
|
+
chatID = "",
|
|
4171
|
+
botUsername = "",
|
|
4172
|
+
excludeRequestKey = "",
|
|
4173
|
+
closedReason = "direct_message_restart",
|
|
4174
|
+
nowISO = new Date().toISOString(),
|
|
4175
|
+
}) {
|
|
4176
|
+
const normalizedChatID = String(chatID || "").trim();
|
|
4177
|
+
if (!isPrivateTelegramChatID(normalizedChatID)) {
|
|
4178
|
+
return {
|
|
4179
|
+
requests: normalizeBotRunnerRequests(safeObject(state).requests),
|
|
4180
|
+
closedRequestKeys: [],
|
|
4181
|
+
};
|
|
4182
|
+
}
|
|
4183
|
+
const requests = normalizeBotRunnerRequests(safeObject(state).requests);
|
|
4184
|
+
const projectID = String(normalizedRoute?.projectID || "").trim();
|
|
4185
|
+
const provider = String(normalizedRoute?.provider || "").trim();
|
|
4186
|
+
const normalizedExcludeRequestKey = String(excludeRequestKey || "").trim();
|
|
4187
|
+
const normalizedRouteKey = String(routeKey || "").trim();
|
|
4188
|
+
const normalizedBotUsername = normalizeTelegramMentionUsername(botUsername);
|
|
4189
|
+
const scopedActiveRequests = sortRunnerDirectMessageContinuationCandidates(
|
|
4190
|
+
Object.values(requests).filter((entryRaw) => {
|
|
4191
|
+
const entry = safeObject(entryRaw);
|
|
4192
|
+
const requestKey = String(entry.request_key || "").trim();
|
|
4193
|
+
if (!requestKey || requestKey === normalizedExcludeRequestKey) {
|
|
4194
|
+
return false;
|
|
4195
|
+
}
|
|
4196
|
+
if (!isActiveRunnerRequestStatus(entry.status)) {
|
|
4197
|
+
return false;
|
|
4198
|
+
}
|
|
4199
|
+
if (projectID && String(entry.project_id || "").trim() !== projectID) {
|
|
4200
|
+
return false;
|
|
4201
|
+
}
|
|
4202
|
+
if (provider && String(entry.provider || "").trim() !== provider) {
|
|
4203
|
+
return false;
|
|
4204
|
+
}
|
|
4205
|
+
if (String(entry.chat_id || "").trim() !== normalizedChatID) {
|
|
4206
|
+
return false;
|
|
4207
|
+
}
|
|
4208
|
+
if (normalizedRouteKey) {
|
|
4209
|
+
const entryRouteKey = firstNonEmptyString([
|
|
4210
|
+
entry.claimed_by_route,
|
|
4211
|
+
entry.source_message_route_key,
|
|
4212
|
+
safeObject(entry.source_message_envelope).source_route_key,
|
|
4213
|
+
]);
|
|
4214
|
+
if (entryRouteKey && entryRouteKey !== normalizedRouteKey) {
|
|
4215
|
+
return false;
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
if (normalizedBotUsername) {
|
|
4219
|
+
const entryBotCandidates = uniqueOrderedStrings([
|
|
4220
|
+
entry.source_message_bot_username,
|
|
4221
|
+
safeObject(entry.source_message_envelope).source_bot_username,
|
|
4222
|
+
...ensureArray(entry.selected_bot_usernames),
|
|
4223
|
+
], normalizeTelegramMentionUsername);
|
|
4224
|
+
if (entryBotCandidates.length > 0 && !entryBotCandidates.includes(normalizedBotUsername)) {
|
|
4225
|
+
return false;
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
return true;
|
|
4229
|
+
}),
|
|
4230
|
+
);
|
|
4231
|
+
const closedRequestKeys = [];
|
|
4232
|
+
for (const requestRaw of scopedActiveRequests) {
|
|
4233
|
+
const request = safeObject(requestRaw);
|
|
4234
|
+
const requestKey = String(request.request_key || "").trim();
|
|
4235
|
+
if (!requestKey) {
|
|
4236
|
+
continue;
|
|
4237
|
+
}
|
|
4238
|
+
requests[requestKey] = {
|
|
4239
|
+
...request,
|
|
4240
|
+
status: "closed",
|
|
4241
|
+
closed_reason: closedReason,
|
|
4242
|
+
closed_at: nowISO,
|
|
4243
|
+
updated_at: nowISO,
|
|
4244
|
+
};
|
|
4245
|
+
closedRequestKeys.push(requestKey);
|
|
4246
|
+
}
|
|
4247
|
+
return {
|
|
4248
|
+
requests,
|
|
4249
|
+
closedRequestKeys,
|
|
4250
|
+
};
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
function sessionAllowsConversationResponder(sessionRaw, responderSelectorRaw = "") {
|
|
4254
|
+
const session = safeObject(sessionRaw);
|
|
3669
4255
|
const responderSelector = normalizeTelegramMentionUsername(responderSelectorRaw);
|
|
3670
4256
|
if (!responderSelector) {
|
|
3671
4257
|
return false;
|
|
@@ -3871,14 +4457,24 @@ function runnerRequestAuthoritativeDecisionBundle(entryRaw) {
|
|
|
3871
4457
|
return validation.ok ? safeObject(validation.bundle) : {};
|
|
3872
4458
|
}
|
|
3873
4459
|
|
|
4460
|
+
function runnerDecisionBundleDecisionType(bundleRaw) {
|
|
4461
|
+
return String(
|
|
4462
|
+
safeObject(bundleRaw).decision_type || safeObject(bundleRaw).decisionType || "",
|
|
4463
|
+
).trim().toLowerCase();
|
|
4464
|
+
}
|
|
4465
|
+
|
|
4466
|
+
function runnerDecisionBundleIsRootHumanOpening(bundleRaw) {
|
|
4467
|
+
return runnerDecisionBundleDecisionType(bundleRaw) === "human_opening";
|
|
4468
|
+
}
|
|
4469
|
+
|
|
3874
4470
|
function runnerRequestPreferredExecutionContractType(entryRaw) {
|
|
3875
4471
|
const entry = safeObject(entryRaw);
|
|
3876
4472
|
const decisionBundle = runnerRequestAuthoritativeDecisionBundle(entry);
|
|
3877
|
-
const
|
|
4473
|
+
const authoritativeExecutionContractType = String(
|
|
4474
|
+
decisionBundle.execution_contract_type || "",
|
|
4475
|
+
).trim().toLowerCase();
|
|
3878
4476
|
return String(
|
|
3879
|
-
|
|
3880
|
-
? decisionBundle.execution_contract_type
|
|
3881
|
-
: "")
|
|
4477
|
+
authoritativeExecutionContractType
|
|
3882
4478
|
|| entry.execution_contract_type
|
|
3883
4479
|
|| entry.root_execution_contract_type
|
|
3884
4480
|
|| "",
|
|
@@ -3888,8 +4484,12 @@ function runnerRequestPreferredExecutionContractType(entryRaw) {
|
|
|
3888
4484
|
function runnerRequestPreferredExecutionContractActionable(entryRaw) {
|
|
3889
4485
|
const entry = safeObject(entryRaw);
|
|
3890
4486
|
const decisionBundle = runnerRequestAuthoritativeDecisionBundle(entry);
|
|
3891
|
-
const
|
|
3892
|
-
|
|
4487
|
+
const hasAuthoritativeExecutionContract = Boolean(
|
|
4488
|
+
String(decisionBundle.execution_contract_type || "").trim()
|
|
4489
|
+
|| ensureArray(decisionBundle.execution_contract_targets).length
|
|
4490
|
+
|| ensureArray(decisionBundle.next_expected_responders).length,
|
|
4491
|
+
);
|
|
4492
|
+
if (hasAuthoritativeExecutionContract) {
|
|
3893
4493
|
return decisionBundle.execution_contract_actionable === true;
|
|
3894
4494
|
}
|
|
3895
4495
|
return entry.execution_contract_actionable === true;
|
|
@@ -3898,9 +4498,8 @@ function runnerRequestPreferredExecutionContractActionable(entryRaw) {
|
|
|
3898
4498
|
function runnerRequestPreferredExecutionContractTargets(entryRaw) {
|
|
3899
4499
|
const entry = safeObject(entryRaw);
|
|
3900
4500
|
const decisionBundle = runnerRequestAuthoritativeDecisionBundle(entry);
|
|
3901
|
-
const hasAuthoritativeDecisionBundle = Object.keys(decisionBundle).length > 0;
|
|
3902
4501
|
return uniqueOrderedStrings(
|
|
3903
|
-
|
|
4502
|
+
ensureArray(decisionBundle.execution_contract_targets).length
|
|
3904
4503
|
? decisionBundle.execution_contract_targets
|
|
3905
4504
|
: ensureArray(entry.execution_contract_targets).length
|
|
3906
4505
|
? entry.execution_contract_targets
|
|
@@ -3914,9 +4513,8 @@ function runnerRequestPreferredExecutionContractTargets(entryRaw) {
|
|
|
3914
4513
|
function runnerRequestPreferredNextExpectedResponders(entryRaw) {
|
|
3915
4514
|
const entry = safeObject(entryRaw);
|
|
3916
4515
|
const decisionBundle = runnerRequestAuthoritativeDecisionBundle(entry);
|
|
3917
|
-
const hasAuthoritativeDecisionBundle = Object.keys(decisionBundle).length > 0;
|
|
3918
4516
|
return uniqueOrderedStrings(
|
|
3919
|
-
|
|
4517
|
+
ensureArray(decisionBundle.next_expected_responders).length
|
|
3920
4518
|
? decisionBundle.next_expected_responders
|
|
3921
4519
|
: ensureArray(entry.next_expected_responders).length
|
|
3922
4520
|
? entry.next_expected_responders
|
|
@@ -4738,10 +5336,10 @@ function resolveRunnerReplyChainConversationContext(state, normalizedRoute, sele
|
|
|
4738
5336
|
};
|
|
4739
5337
|
}
|
|
4740
5338
|
|
|
4741
|
-
async function resolveRunnerReplyChainConversationContextWithServerFallback({
|
|
4742
|
-
state,
|
|
4743
|
-
normalizedRoute,
|
|
4744
|
-
selectedRecord,
|
|
5339
|
+
async function resolveRunnerReplyChainConversationContextWithServerFallback({
|
|
5340
|
+
state,
|
|
5341
|
+
normalizedRoute,
|
|
5342
|
+
selectedRecord,
|
|
4745
5343
|
runtime,
|
|
4746
5344
|
archiveThreadID = "",
|
|
4747
5345
|
hydrationAttempted = false,
|
|
@@ -4756,14 +5354,70 @@ async function resolveRunnerReplyChainConversationContextWithServerFallback({
|
|
|
4756
5354
|
};
|
|
4757
5355
|
}
|
|
4758
5356
|
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
4759
|
-
const initialReplyToMessageID = intFromRawAllowZero(
|
|
4760
|
-
parsed.replyToMessageID || safeObject(initialContext).replyToMessageID,
|
|
4761
|
-
0,
|
|
4762
|
-
);
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
5357
|
+
const initialReplyToMessageID = intFromRawAllowZero(
|
|
5358
|
+
parsed.replyToMessageID || safeObject(initialContext).replyToMessageID,
|
|
5359
|
+
0,
|
|
5360
|
+
);
|
|
5361
|
+
const routeKey = runnerRouteKey(normalizedRoute);
|
|
5362
|
+
const routeStateForLookup = safeObject(safeObject(initialState.routes)[routeKey]);
|
|
5363
|
+
const currentBotUsername = normalizeTelegramMentionUsername(
|
|
5364
|
+
normalizedRoute?.botName
|
|
5365
|
+
|| normalizedRoute?.bot_name
|
|
5366
|
+
|| normalizedRoute?.serverBotName
|
|
5367
|
+
|| normalizedRoute?.server_bot_name,
|
|
5368
|
+
);
|
|
5369
|
+
if (initialReplyToMessageID <= 0) {
|
|
5370
|
+
const directMessageContinuationRequest = findRunnerDirectMessageContinuationSourceRequest({
|
|
5371
|
+
state: initialState,
|
|
5372
|
+
normalizedRoute,
|
|
5373
|
+
routeKey,
|
|
5374
|
+
routeState: routeStateForLookup,
|
|
5375
|
+
chatID: String(parsed.chatID || parsed.chatId || "").trim(),
|
|
5376
|
+
botUsername: currentBotUsername,
|
|
5377
|
+
});
|
|
5378
|
+
if (safeObject(directMessageContinuationRequest).request_key) {
|
|
5379
|
+
const referencedRequest = safeObject(directMessageContinuationRequest);
|
|
5380
|
+
const anchorMessageID = intFromRawAllowZero(referencedRequest.source_message_id, 0)
|
|
5381
|
+
|| intFromRawAllowZero(parsed.messageID, 0);
|
|
5382
|
+
return {
|
|
5383
|
+
state: initialState,
|
|
5384
|
+
replyChainContext: {
|
|
5385
|
+
conversationID: String(referencedRequest.conversation_id || "").trim()
|
|
5386
|
+
|| buildSyntheticReplyChainConversationID(
|
|
5387
|
+
normalizedRoute,
|
|
5388
|
+
String(parsed.chatID || parsed.chatId || "").trim(),
|
|
5389
|
+
anchorMessageID,
|
|
5390
|
+
),
|
|
5391
|
+
replyToMessageID: 0,
|
|
5392
|
+
anchorMessageID,
|
|
5393
|
+
reason: "direct_message_continuation_request",
|
|
5394
|
+
referencedRequest,
|
|
5395
|
+
},
|
|
5396
|
+
hydrated: false,
|
|
5397
|
+
};
|
|
5398
|
+
}
|
|
5399
|
+
if (!hydrationAttempted && runtime?.baseURL && runtime?.token) {
|
|
5400
|
+
const hydratedState = await hydrateRunnerRequestLedgerFromServer({
|
|
5401
|
+
normalizedRoute,
|
|
5402
|
+
runtime,
|
|
5403
|
+
});
|
|
5404
|
+
const hydratedResolution = await resolveRunnerReplyChainConversationContextWithServerFallback({
|
|
5405
|
+
state: hydratedState,
|
|
5406
|
+
normalizedRoute,
|
|
5407
|
+
selectedRecord,
|
|
5408
|
+
runtime,
|
|
5409
|
+
archiveThreadID,
|
|
5410
|
+
hydrationAttempted: true,
|
|
5411
|
+
});
|
|
5412
|
+
return {
|
|
5413
|
+
state: hydratedResolution.state,
|
|
5414
|
+
replyChainContext: hydratedResolution.replyChainContext,
|
|
5415
|
+
hydrated: true,
|
|
5416
|
+
};
|
|
5417
|
+
}
|
|
5418
|
+
return {
|
|
5419
|
+
state: initialState,
|
|
5420
|
+
replyChainContext: initialContext,
|
|
4767
5421
|
hydrated: false,
|
|
4768
5422
|
};
|
|
4769
5423
|
}
|
|
@@ -5093,7 +5747,27 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5093
5747
|
requests: backfilled.requests,
|
|
5094
5748
|
};
|
|
5095
5749
|
}
|
|
5096
|
-
const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
5750
|
+
const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
5751
|
+
const normalizedAuthoritativeSourceMessageEnvelope = normalizeRunnerTelegramMessageEnvelope(
|
|
5752
|
+
authoritativeSourceMessageEnvelope,
|
|
5753
|
+
);
|
|
5754
|
+
const currentClaimBotUsername = normalizeTelegramMentionUsername(
|
|
5755
|
+
normalizedAuthoritativeSourceMessageEnvelope.source_bot_username
|
|
5756
|
+
|| normalizedAuthoritativeSourceMessageEnvelope.sender_username
|
|
5757
|
+
|| currentRouteState.last_source_bot_username
|
|
5758
|
+
|| currentRouteState.source_message_bot_username
|
|
5759
|
+
|| currentRouteState.last_speaker_bot_username,
|
|
5760
|
+
);
|
|
5761
|
+
const claimBotUsernameCandidates = uniqueOrderedStrings([
|
|
5762
|
+
currentClaimBotUsername,
|
|
5763
|
+
normalizedAuthoritativeSourceMessageEnvelope.source_bot_username,
|
|
5764
|
+
normalizedAuthoritativeSourceMessageEnvelope.sender_username,
|
|
5765
|
+
...ensureArray(selectedBotUsernames),
|
|
5766
|
+
...ensureArray(normalizedSharedHumanIntent.participantSelectors),
|
|
5767
|
+
currentRouteState.last_source_bot_username,
|
|
5768
|
+
currentRouteState.source_message_bot_username,
|
|
5769
|
+
currentRouteState.last_speaker_bot_username,
|
|
5770
|
+
], normalizeTelegramMentionUsername);
|
|
5097
5771
|
let sharedConversationSource = currentMessageID > 0
|
|
5098
5772
|
? pickRunnerSharedConversationSourceRequest(
|
|
5099
5773
|
findRunnerRequestsForMessageID(stateForClaim, normalizedRoute, {
|
|
@@ -5104,10 +5778,10 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5104
5778
|
provisionalRequestKey,
|
|
5105
5779
|
)
|
|
5106
5780
|
: {};
|
|
5107
|
-
if (
|
|
5108
|
-
!Object.keys(sharedConversationSource).length
|
|
5109
|
-
&& currentMessageID > 0
|
|
5110
|
-
&& runtime?.baseURL
|
|
5781
|
+
if (
|
|
5782
|
+
!Object.keys(sharedConversationSource).length
|
|
5783
|
+
&& currentMessageID > 0
|
|
5784
|
+
&& runtime?.baseURL
|
|
5111
5785
|
&& runtime?.token
|
|
5112
5786
|
) {
|
|
5113
5787
|
sharedConversationSource = safeObject(await findServerRunnerConversationSourceRequestForMessageID({
|
|
@@ -5119,68 +5793,332 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5119
5793
|
excludeRequestKey: provisionalRequestKey,
|
|
5120
5794
|
}));
|
|
5121
5795
|
}
|
|
5796
|
+
if (!Object.keys(sharedConversationSource).length) {
|
|
5797
|
+
sharedConversationSource = safeObject(findRunnerDirectMessageContinuationSourceRequest({
|
|
5798
|
+
state: stateForClaim,
|
|
5799
|
+
normalizedRoute,
|
|
5800
|
+
routeKey,
|
|
5801
|
+
routeState: currentRouteState,
|
|
5802
|
+
chatID: String(parsed.chatID || parsed.chatId || "").trim(),
|
|
5803
|
+
botUsername: currentClaimBotUsername,
|
|
5804
|
+
}));
|
|
5805
|
+
}
|
|
5122
5806
|
const authorityContext = resolveRunnerHumanCommentAuthorityContext({
|
|
5123
5807
|
normalizedRoute,
|
|
5124
5808
|
selectedRecord,
|
|
5125
|
-
replyChainContext,
|
|
5126
|
-
referencedRequest,
|
|
5809
|
+
replyChainContext,
|
|
5810
|
+
referencedRequest,
|
|
5127
5811
|
sharedConversationSource,
|
|
5128
5812
|
selectedBotUsernames,
|
|
5129
|
-
normalizedSharedHumanIntent,
|
|
5130
|
-
resolvedNormalizedIntent,
|
|
5131
|
-
});
|
|
5813
|
+
normalizedSharedHumanIntent,
|
|
5814
|
+
resolvedNormalizedIntent,
|
|
5815
|
+
});
|
|
5816
|
+
const normalizedChatID = String(parsed.chatID || parsed.chatId || "").trim();
|
|
5817
|
+
const isDirectMessageChat = isPrivateTelegramChatID(normalizedChatID);
|
|
5818
|
+
const isDirectMessageRestart = isDirectMessageChat && isRunnerDirectMessageRestartMessage(selectedRecord);
|
|
5819
|
+
const routeStateForClaim = safeObject(safeObject(stateForClaim.routes)[String(routeKey || "").trim()]);
|
|
5820
|
+
const currentRouteStateForClaim = safeObject(
|
|
5821
|
+
safeObject(currentState.routes)[String(routeKey || "").trim()],
|
|
5822
|
+
);
|
|
5823
|
+
const requestsForDirectMessageClaimLookup = normalizeBotRunnerRequests(stateForClaim.requests);
|
|
5824
|
+
const currentRequestsForDirectMessageClaimLookup = normalizeBotRunnerRequests(currentState.requests);
|
|
5825
|
+
const directMessageRouteActiveRequest = safeObject(
|
|
5826
|
+
requestsForDirectMessageClaimLookup[String(routeStateForClaim.active_request_key || "").trim()],
|
|
5827
|
+
);
|
|
5828
|
+
const directMessageRouteLastRequest = safeObject(
|
|
5829
|
+
requestsForDirectMessageClaimLookup[String(routeStateForClaim.last_request_key || "").trim()],
|
|
5830
|
+
);
|
|
5831
|
+
const currentDirectMessageRouteActiveRequest = safeObject(
|
|
5832
|
+
currentRequestsForDirectMessageClaimLookup[String(currentRouteStateForClaim.active_request_key || "").trim()],
|
|
5833
|
+
);
|
|
5834
|
+
const currentDirectMessageRouteLastRequest = safeObject(
|
|
5835
|
+
currentRequestsForDirectMessageClaimLookup[String(currentRouteStateForClaim.last_request_key || "").trim()],
|
|
5836
|
+
);
|
|
5837
|
+
const directMessageRestartRequestKeysToClose = new Set();
|
|
5838
|
+
if (isDirectMessageRestart) {
|
|
5839
|
+
const closeBotUsername = firstNonEmptyString(claimBotUsernameCandidates);
|
|
5840
|
+
const closedActiveDirectMessageRequests = closeRunnerDirectMessageScopedActiveRequests({
|
|
5841
|
+
state: stateForClaim,
|
|
5842
|
+
normalizedRoute,
|
|
5843
|
+
routeKey,
|
|
5844
|
+
chatID: normalizedChatID,
|
|
5845
|
+
botUsername: closeBotUsername,
|
|
5846
|
+
});
|
|
5847
|
+
stateForClaim = {
|
|
5848
|
+
...stateForClaim,
|
|
5849
|
+
requests: closedActiveDirectMessageRequests.requests,
|
|
5850
|
+
};
|
|
5851
|
+
for (const requestKeyRaw of ensureArray(closedActiveDirectMessageRequests.closedRequestKeys)) {
|
|
5852
|
+
const requestKey = String(requestKeyRaw || "").trim();
|
|
5853
|
+
if (requestKey) {
|
|
5854
|
+
directMessageRestartRequestKeysToClose.add(requestKey);
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
const restartRequests = normalizeBotRunnerRequests(stateForClaim.requests);
|
|
5858
|
+
for (const directMessageRequest of [
|
|
5859
|
+
directMessageRouteActiveRequest,
|
|
5860
|
+
directMessageRouteLastRequest,
|
|
5861
|
+
currentDirectMessageRouteActiveRequest,
|
|
5862
|
+
currentDirectMessageRouteLastRequest,
|
|
5863
|
+
]) {
|
|
5864
|
+
const requestKey = String(safeObject(directMessageRequest).request_key || "").trim();
|
|
5865
|
+
if (!requestKey) {
|
|
5866
|
+
continue;
|
|
5867
|
+
}
|
|
5868
|
+
directMessageRestartRequestKeysToClose.add(requestKey);
|
|
5869
|
+
const existingDirectMessageRequest = safeObject(restartRequests[requestKey]);
|
|
5870
|
+
if (
|
|
5871
|
+
!String(existingDirectMessageRequest.request_key || "").trim()
|
|
5872
|
+
|| !isActiveRunnerRequestStatus(existingDirectMessageRequest.status)
|
|
5873
|
+
|| String(existingDirectMessageRequest.chat_id || "").trim() !== normalizedChatID
|
|
5874
|
+
) {
|
|
5875
|
+
continue;
|
|
5876
|
+
}
|
|
5877
|
+
restartRequests[requestKey] = {
|
|
5878
|
+
...existingDirectMessageRequest,
|
|
5879
|
+
status: "closed",
|
|
5880
|
+
closed_reason: "direct_message_restart",
|
|
5881
|
+
closed_at: new Date().toISOString(),
|
|
5882
|
+
updated_at: new Date().toISOString(),
|
|
5883
|
+
};
|
|
5884
|
+
}
|
|
5885
|
+
stateForClaim = {
|
|
5886
|
+
...stateForClaim,
|
|
5887
|
+
requests: restartRequests,
|
|
5888
|
+
};
|
|
5889
|
+
}
|
|
5132
5890
|
const authoritySource = safeObject(authorityContext.authoritySource);
|
|
5133
|
-
const
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5891
|
+
const effectiveAuthoritySource = isDirectMessageRestart ? {} : authoritySource;
|
|
5892
|
+
const authorityReplyChainContext = await buildRunnerAuthorityReplyChainContext({
|
|
5893
|
+
selectedRecord,
|
|
5894
|
+
replyChainContext,
|
|
5895
|
+
authoritySource: effectiveAuthoritySource,
|
|
5896
|
+
runtime,
|
|
5897
|
+
archiveThreadID,
|
|
5898
|
+
});
|
|
5899
|
+
const explicitDecisionBundle = safeObject(decisionBundle);
|
|
5900
|
+
const authorityRootMessageID = intFromRawAllowZero(
|
|
5901
|
+
authorityReplyChainContext.root_message_id || authorityReplyChainContext.rootMessageID,
|
|
5902
|
+
0,
|
|
5903
|
+
);
|
|
5904
|
+
const authoritativeSourceMessageID = intFromRawAllowZero(
|
|
5905
|
+
normalizedAuthoritativeSourceMessageEnvelope.message_id
|
|
5906
|
+
|| normalizedAuthoritativeSourceMessageEnvelope.messageID
|
|
5907
|
+
|| currentMessageID,
|
|
5908
|
+
0,
|
|
5909
|
+
);
|
|
5910
|
+
const explicitDecisionType = String(
|
|
5911
|
+
explicitDecisionBundle.decision_type || explicitDecisionBundle.decisionType || "",
|
|
5912
|
+
).trim().toLowerCase();
|
|
5913
|
+
const preferAuthoritativeSourceReplyAnchor = explicitDecisionType === "human_opening";
|
|
5914
|
+
const inferredReplyAnchorMessageID = preferAuthoritativeSourceReplyAnchor
|
|
5915
|
+
? authoritativeSourceMessageID > 0
|
|
5916
|
+
? authoritativeSourceMessageID
|
|
5917
|
+
: authorityRootMessageID > 0
|
|
5918
|
+
? authorityRootMessageID
|
|
5919
|
+
: 0
|
|
5920
|
+
: authorityRootMessageID > 0
|
|
5921
|
+
? authorityRootMessageID
|
|
5922
|
+
: authoritativeSourceMessageID > 0
|
|
5923
|
+
? authoritativeSourceMessageID
|
|
5924
|
+
: 0;
|
|
5925
|
+
const inferredReplyAnchorSource = preferAuthoritativeSourceReplyAnchor
|
|
5926
|
+
? authoritativeSourceMessageID > 0
|
|
5927
|
+
? "authoritative_source_message_envelope"
|
|
5928
|
+
: authorityRootMessageID > 0
|
|
5929
|
+
? "reply_chain_root_message"
|
|
5930
|
+
: ""
|
|
5931
|
+
: authorityRootMessageID > 0
|
|
5932
|
+
? "reply_chain_root_message"
|
|
5933
|
+
: authoritativeSourceMessageID > 0
|
|
5934
|
+
? "authoritative_source_message_envelope"
|
|
5935
|
+
: "";
|
|
5936
|
+
const inferredReplyAnchorKind = inferredReplyAnchorMessageID > 0 ? "human_root" : "";
|
|
5937
|
+
const shouldOverrideHumanOpeningReplyAnchor = explicitDecisionType === "human_opening"
|
|
5938
|
+
&& inferredReplyAnchorMessageID > 0;
|
|
5939
|
+
const decisionBundleWithReplyAnchor = Object.keys(explicitDecisionBundle).length > 0
|
|
5940
|
+
? {
|
|
5941
|
+
...explicitDecisionBundle,
|
|
5942
|
+
...((shouldOverrideHumanOpeningReplyAnchor || !(
|
|
5943
|
+
intFromRawAllowZero(
|
|
5944
|
+
explicitDecisionBundle.reply_to_message_id || explicitDecisionBundle.replyToMessageID,
|
|
5945
|
+
0,
|
|
5946
|
+
) > 0
|
|
5947
|
+
)) && inferredReplyAnchorMessageID > 0
|
|
5948
|
+
? { reply_to_message_id: inferredReplyAnchorMessageID }
|
|
5949
|
+
: {}),
|
|
5950
|
+
...((shouldOverrideHumanOpeningReplyAnchor || !String(
|
|
5951
|
+
explicitDecisionBundle.reply_anchor_source || explicitDecisionBundle.replyAnchorSource || "",
|
|
5952
|
+
).trim()) && inferredReplyAnchorSource
|
|
5953
|
+
? { reply_anchor_source: inferredReplyAnchorSource }
|
|
5954
|
+
: {}),
|
|
5955
|
+
...((shouldOverrideHumanOpeningReplyAnchor || !String(
|
|
5956
|
+
explicitDecisionBundle.reply_anchor_kind || explicitDecisionBundle.replyAnchorKind || "",
|
|
5957
|
+
).trim()) && inferredReplyAnchorKind
|
|
5958
|
+
? { reply_anchor_kind: inferredReplyAnchorKind }
|
|
5959
|
+
: {}),
|
|
5960
|
+
}
|
|
5961
|
+
: {};
|
|
5962
|
+
const decisionBundleValidation = validateRunnerConversationDecisionBundle(decisionBundleWithReplyAnchor);
|
|
5963
|
+
if (Object.keys(decisionBundleWithReplyAnchor).length > 0 && decisionBundleValidation.ok !== true) {
|
|
5138
5964
|
return {
|
|
5139
5965
|
ok: false,
|
|
5140
5966
|
reason: "invalid_decision_bundle",
|
|
5141
5967
|
detail: String(decisionBundleValidation.reason || "").trim() || "invalid_decision_bundle",
|
|
5142
5968
|
};
|
|
5143
5969
|
}
|
|
5970
|
+
if (
|
|
5971
|
+
Object.keys(decisionBundleWithReplyAnchor).length > 0
|
|
5972
|
+
&& runnerDecisionBundleDecisionType(decisionBundleValidation.bundle) !== "human_opening"
|
|
5973
|
+
) {
|
|
5974
|
+
return {
|
|
5975
|
+
ok: false,
|
|
5976
|
+
reason: "invalid_root_request_decision_bundle",
|
|
5977
|
+
detail: "root human requests require a human_opening authoritative decision bundle",
|
|
5978
|
+
};
|
|
5979
|
+
}
|
|
5144
5980
|
const authoritativeDecisionBundle = decisionBundleValidation.ok === true
|
|
5145
5981
|
? safeObject(decisionBundleValidation.bundle)
|
|
5146
5982
|
: {};
|
|
5147
|
-
const
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5983
|
+
const resolvedConversationID = isDirectMessageRestart
|
|
5984
|
+
? buildSyntheticHumanOpeningConversationID(
|
|
5985
|
+
normalizedRoute,
|
|
5986
|
+
normalizedChatID,
|
|
5987
|
+
currentMessageID,
|
|
5988
|
+
canonicalHumanMessageKey,
|
|
5989
|
+
)
|
|
5990
|
+
: String(authorityContext.conversationID || "").trim();
|
|
5991
|
+
const preferredNormalizedIntent = String(
|
|
5992
|
+
isDirectMessageRestart
|
|
5993
|
+
? normalizedSharedHumanIntent.intentType || resolvedNormalizedIntent || ""
|
|
5994
|
+
: authorityContext.normalizedIntent,
|
|
5995
|
+
).trim().toLowerCase();
|
|
5996
|
+
const requestSelectedBotUsernames = ensureArray(
|
|
5997
|
+
isDirectMessageRestart
|
|
5998
|
+
? ensureArray(selectedBotUsernames).length
|
|
5999
|
+
? selectedBotUsernames
|
|
6000
|
+
: authorityContext.selectedBotUsernames
|
|
6001
|
+
: authorityContext.selectedBotUsernames,
|
|
5156
6002
|
);
|
|
5157
|
-
const authorityReplyChainContext = await buildRunnerAuthorityReplyChainContext({
|
|
5158
|
-
selectedRecord,
|
|
5159
|
-
replyChainContext,
|
|
5160
|
-
authoritySource,
|
|
5161
|
-
runtime,
|
|
5162
|
-
archiveThreadID,
|
|
5163
|
-
});
|
|
5164
|
-
const resolvedConversationID = String(authorityContext.conversationID || "").trim();
|
|
5165
|
-
const preferredNormalizedIntent = String(authorityContext.normalizedIntent || "").trim().toLowerCase();
|
|
5166
|
-
const requestSelectedBotUsernames = ensureArray(authorityContext.selectedBotUsernames);
|
|
5167
6003
|
const effectiveSelectedBotUsernames = uniqueOrderedStrings(
|
|
5168
6004
|
ensureArray(authoritativeDecisionBundle.selected_bot_usernames).length
|
|
5169
6005
|
? authoritativeDecisionBundle.selected_bot_usernames
|
|
5170
6006
|
: requestSelectedBotUsernames,
|
|
5171
6007
|
normalizeTelegramMentionUsername,
|
|
5172
6008
|
);
|
|
5173
|
-
const
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
6009
|
+
const requests = normalizeBotRunnerRequests(stateForClaim.requests);
|
|
6010
|
+
const resolveDirectMessageContinuationRequestByKey = (requestKeyRaw = "") => {
|
|
6011
|
+
const requestKey = String(requestKeyRaw || "").trim();
|
|
6012
|
+
if (!requestKey) {
|
|
6013
|
+
return {};
|
|
6014
|
+
}
|
|
6015
|
+
const candidate = safeObject(requests[requestKey]);
|
|
6016
|
+
if (!String(candidate.request_key || "").trim()) {
|
|
6017
|
+
return {};
|
|
6018
|
+
}
|
|
6019
|
+
const botCandidates = claimBotUsernameCandidates.length > 0 ? claimBotUsernameCandidates : [""];
|
|
6020
|
+
for (const botUsernameCandidate of botCandidates) {
|
|
6021
|
+
if (isRunnerDirectMessageContinuationRequestCandidate(candidate, {
|
|
6022
|
+
routeKey: String(routeKey || "").trim(),
|
|
6023
|
+
chatID: normalizedChatID,
|
|
6024
|
+
botUsername: botUsernameCandidate,
|
|
6025
|
+
})) {
|
|
6026
|
+
return candidate;
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
if (isRunnerDirectMessageContinuationRequestCandidate(candidate, {
|
|
6030
|
+
routeKey: String(routeKey || "").trim(),
|
|
6031
|
+
chatID: normalizedChatID,
|
|
6032
|
+
botUsername: "",
|
|
6033
|
+
})) {
|
|
6034
|
+
return candidate;
|
|
6035
|
+
}
|
|
6036
|
+
return {};
|
|
6037
|
+
};
|
|
6038
|
+
let directMessageContinuationRequest = {};
|
|
6039
|
+
if (isDirectMessageChat && !isDirectMessageRestart) {
|
|
6040
|
+
const prefersDirectMessageRouteActiveRequest = (requestRaw = {}) => {
|
|
6041
|
+
const request = safeObject(requestRaw);
|
|
6042
|
+
return (
|
|
6043
|
+
String(request.request_key || "").trim()
|
|
6044
|
+
&& String(request.project_id || "").trim() === String(normalizedRoute?.projectID || "").trim()
|
|
6045
|
+
&& String(request.provider || "").trim() === String(normalizedRoute?.provider || "").trim()
|
|
6046
|
+
&& String(request.chat_id || "").trim() === normalizedChatID
|
|
6047
|
+
&& isActiveRunnerRequestStatus(request.status)
|
|
6048
|
+
)
|
|
6049
|
+
? request
|
|
6050
|
+
: {};
|
|
6051
|
+
};
|
|
6052
|
+
directMessageContinuationRequest = prefersDirectMessageRouteActiveRequest(directMessageRouteActiveRequest);
|
|
6053
|
+
if (!String(directMessageContinuationRequest.request_key || "").trim()) {
|
|
6054
|
+
directMessageContinuationRequest = prefersDirectMessageRouteActiveRequest(directMessageRouteLastRequest);
|
|
6055
|
+
}
|
|
6056
|
+
const directMessageContinuationRequestKeys = uniqueOrderedStrings([
|
|
6057
|
+
safeObject(effectiveAuthoritySource).request_key,
|
|
6058
|
+
safeObject(authoritySource).request_key,
|
|
6059
|
+
safeObject(referencedRequest).request_key,
|
|
6060
|
+
safeObject(sharedConversationSource).request_key,
|
|
6061
|
+
routeStateForClaim.active_request_key,
|
|
6062
|
+
routeStateForClaim.last_request_key,
|
|
6063
|
+
]);
|
|
6064
|
+
if (!String(directMessageContinuationRequest.request_key || "").trim()) {
|
|
6065
|
+
for (const requestKeyCandidate of directMessageContinuationRequestKeys) {
|
|
6066
|
+
const matchedRequest = resolveDirectMessageContinuationRequestByKey(requestKeyCandidate);
|
|
6067
|
+
if (String(matchedRequest.request_key || "").trim()) {
|
|
6068
|
+
directMessageContinuationRequest = matchedRequest;
|
|
6069
|
+
break;
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
if (!String(directMessageContinuationRequest.request_key || "").trim()) {
|
|
6074
|
+
const lookupBotCandidates = claimBotUsernameCandidates.length > 0 ? claimBotUsernameCandidates : [""];
|
|
6075
|
+
for (const botUsernameCandidate of lookupBotCandidates) {
|
|
6076
|
+
const matchedRequest = safeObject(findRunnerDirectMessageContinuationSourceRequest({
|
|
6077
|
+
state: stateForClaim,
|
|
6078
|
+
normalizedRoute,
|
|
6079
|
+
routeKey,
|
|
6080
|
+
routeState: routeStateForClaim,
|
|
6081
|
+
chatID: normalizedChatID,
|
|
6082
|
+
botUsername: botUsernameCandidate,
|
|
6083
|
+
}));
|
|
6084
|
+
if (String(matchedRequest.request_key || "").trim()) {
|
|
6085
|
+
directMessageContinuationRequest = matchedRequest;
|
|
6086
|
+
break;
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
const shouldReuseDirectMessageRequest = (
|
|
6092
|
+
isDirectMessageChat
|
|
6093
|
+
&& !isDirectMessageRestart
|
|
6094
|
+
&& String(directMessageContinuationRequest.request_key || "").trim()
|
|
6095
|
+
&& isActiveRunnerRequestStatus(directMessageContinuationRequest.status)
|
|
6096
|
+
);
|
|
6097
|
+
const directMessageContinuationConversationID = String(
|
|
6098
|
+
directMessageContinuationRequest.conversation_id
|
|
6099
|
+
|| safeObject(routeStateForClaim.conversation_sessions)[String(routeStateForClaim.last_conversation_id || "").trim()]?.conversation_id
|
|
6100
|
+
|| "",
|
|
6101
|
+
).trim();
|
|
6102
|
+
const effectiveConversationID = shouldReuseDirectMessageRequest
|
|
6103
|
+
? String(
|
|
6104
|
+
resolvedConversationID
|
|
6105
|
+
|| directMessageContinuationConversationID
|
|
6106
|
+
|| safeObject(directMessageContinuationRequest.reply_chain_context).conversation_id
|
|
6107
|
+
|| "",
|
|
6108
|
+
).trim()
|
|
6109
|
+
: resolvedConversationID;
|
|
6110
|
+
const requestKey = shouldReuseDirectMessageRequest
|
|
6111
|
+
? String(directMessageContinuationRequest.request_key || "").trim()
|
|
6112
|
+
: buildRunnerRequestKey({
|
|
6113
|
+
normalizedRoute,
|
|
6114
|
+
selectedRecord,
|
|
6115
|
+
selectedBotUsernames: effectiveSelectedBotUsernames,
|
|
6116
|
+
normalizedIntent: preferredNormalizedIntent,
|
|
6117
|
+
conversationID: effectiveConversationID,
|
|
6118
|
+
});
|
|
6119
|
+
const existing = safeObject(requests[requestKey]);
|
|
6120
|
+
if (isFinalRunnerRequestStatus(existing.status)) {
|
|
6121
|
+
return {
|
|
5184
6122
|
ok: false,
|
|
5185
6123
|
reason: "request_already_finalized",
|
|
5186
6124
|
requestKey,
|
|
@@ -5204,6 +6142,15 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5204
6142
|
}
|
|
5205
6143
|
const nowISO = new Date().toISOString();
|
|
5206
6144
|
const existingSourceMessageEnvelope = normalizeRunnerTelegramMessageEnvelope(existing.source_message_envelope);
|
|
6145
|
+
const existingRootSourceMessageEnvelope = normalizeRunnerTelegramMessageEnvelope(
|
|
6146
|
+
existing.root_source_message_envelope
|
|
6147
|
+
|| existing.rootSourceMessageEnvelope
|
|
6148
|
+
|| existing.source_message_envelope,
|
|
6149
|
+
);
|
|
6150
|
+
const existingCurrentSourceMessageEnvelope = normalizeRunnerTelegramMessageEnvelope(
|
|
6151
|
+
existing.current_source_message_envelope
|
|
6152
|
+
|| existing.currentSourceMessageEnvelope,
|
|
6153
|
+
);
|
|
5207
6154
|
const preserveExistingCanonicalSourceEnvelope = Boolean(
|
|
5208
6155
|
canonicalHumanMessageKey
|
|
5209
6156
|
&& String(existing.canonical_human_message_key || "").trim() === canonicalHumanMessageKey
|
|
@@ -5215,6 +6162,23 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5215
6162
|
: Object.keys(normalizedAuthoritativeSourceMessageEnvelope).length
|
|
5216
6163
|
? normalizedAuthoritativeSourceMessageEnvelope
|
|
5217
6164
|
: existingSourceMessageEnvelope;
|
|
6165
|
+
const effectiveSourceMessageEnvelope = shouldReuseDirectMessageRequest
|
|
6166
|
+
? (Object.keys(existingSourceMessageEnvelope).length
|
|
6167
|
+
? existingSourceMessageEnvelope
|
|
6168
|
+
: sourceMessageEnvelope)
|
|
6169
|
+
: sourceMessageEnvelope;
|
|
6170
|
+
const rootSourceMessageEnvelope = shouldReuseDirectMessageRequest
|
|
6171
|
+
? (Object.keys(existingRootSourceMessageEnvelope).length
|
|
6172
|
+
? existingRootSourceMessageEnvelope
|
|
6173
|
+
: effectiveSourceMessageEnvelope)
|
|
6174
|
+
: sourceMessageEnvelope;
|
|
6175
|
+
const currentSourceMessageEnvelope = preserveExistingCanonicalSourceEnvelope
|
|
6176
|
+
? (Object.keys(existingCurrentSourceMessageEnvelope).length
|
|
6177
|
+
? existingCurrentSourceMessageEnvelope
|
|
6178
|
+
: sourceMessageEnvelope)
|
|
6179
|
+
: Object.keys(normalizedAuthoritativeSourceMessageEnvelope).length
|
|
6180
|
+
? normalizedAuthoritativeSourceMessageEnvelope
|
|
6181
|
+
: sourceMessageEnvelope;
|
|
5218
6182
|
const decisionConversationParticipants = uniqueOrderedStrings(
|
|
5219
6183
|
ensureArray(authoritativeDecisionBundle.participants),
|
|
5220
6184
|
normalizeTelegramMentionUsername,
|
|
@@ -5228,33 +6192,114 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5228
6192
|
normalizeTelegramMentionUsername,
|
|
5229
6193
|
);
|
|
5230
6194
|
const hasAuthoritativeConversationDecision = Object.keys(authoritativeDecisionBundle).length > 0;
|
|
5231
|
-
const
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
6195
|
+
const rootSourceMessageID = shouldReuseDirectMessageRequest
|
|
6196
|
+
? intFromRawAllowZero(existing.source_message_id, 0) || intFromRawAllowZero(parsed.messageID, 0)
|
|
6197
|
+
: intFromRawAllowZero(parsed.messageID, 0);
|
|
6198
|
+
const rootSourceMessageThreadID = shouldReuseDirectMessageRequest
|
|
6199
|
+
? intFromRawAllowZero(existing.source_message_thread_id, 0)
|
|
6200
|
+
: intFromRawAllowZero(parsed.messageThreadID, 0);
|
|
6201
|
+
const rootSourceMessageBody = shouldReuseDirectMessageRequest
|
|
6202
|
+
? String(existing.source_message_body || parsed.body || "").trim()
|
|
6203
|
+
: String(parsed.body || "").trim();
|
|
6204
|
+
const rootCanonicalHumanMessageKey = shouldReuseDirectMessageRequest
|
|
6205
|
+
? firstNonEmptyString([
|
|
6206
|
+
existing.canonical_human_message_key,
|
|
6207
|
+
canonicalHumanMessageKey,
|
|
6208
|
+
])
|
|
6209
|
+
: canonicalHumanMessageKey;
|
|
6210
|
+
const currentSourceMessageID = intFromRawAllowZero(
|
|
6211
|
+
currentSourceMessageEnvelope.message_id || currentMessageID,
|
|
6212
|
+
0,
|
|
6213
|
+
);
|
|
6214
|
+
const currentSourceMessageThreadID = intFromRawAllowZero(
|
|
6215
|
+
currentSourceMessageEnvelope.message_thread_id || parsed.messageThreadID,
|
|
6216
|
+
0,
|
|
6217
|
+
);
|
|
6218
|
+
const currentSourceMessageBody = firstNonEmptyString([
|
|
6219
|
+
currentSourceMessageEnvelope.body,
|
|
6220
|
+
parsed.body,
|
|
6221
|
+
]);
|
|
6222
|
+
const currentTurnRelation = String(
|
|
6223
|
+
authorityReplyChainContext.current_turn_relation
|
|
6224
|
+
|| authorityReplyChainContext.currentTurnRelation
|
|
6225
|
+
|| existing.current_turn_relation
|
|
6226
|
+
|| existing.currentTurnRelation
|
|
6227
|
+
|| "",
|
|
6228
|
+
).trim();
|
|
6229
|
+
let { requests: nextRequests, request } = upsertRunnerRequest(stateForClaim, requestKey, {
|
|
6230
|
+
project_id: String(normalizedRoute?.projectID || "").trim(),
|
|
6231
|
+
provider: String(normalizedRoute?.provider || "").trim(),
|
|
6232
|
+
chat_id: normalizedChatID,
|
|
6233
|
+
source_message_id: rootSourceMessageID || undefined,
|
|
6234
|
+
source_message_thread_id: rootSourceMessageThreadID || undefined,
|
|
6235
|
+
source_message_body: rootSourceMessageBody,
|
|
6236
|
+
canonical_human_message_key: rootCanonicalHumanMessageKey,
|
|
6237
|
+
source_message_origin: shouldReuseDirectMessageRequest
|
|
6238
|
+
? String(existing.source_message_origin || effectiveSourceMessageEnvelope.source_origin || "").trim().toLowerCase()
|
|
6239
|
+
: String(effectiveSourceMessageEnvelope.source_origin || "").trim().toLowerCase(),
|
|
6240
|
+
source_message_route_key: shouldReuseDirectMessageRequest
|
|
6241
|
+
? String(existing.source_message_route_key || effectiveSourceMessageEnvelope.source_route_key || "").trim()
|
|
6242
|
+
: String(effectiveSourceMessageEnvelope.source_route_key || "").trim(),
|
|
6243
|
+
source_message_bot_username: shouldReuseDirectMessageRequest
|
|
6244
|
+
? normalizeTelegramMentionUsername(existing.source_message_bot_username || effectiveSourceMessageEnvelope.source_bot_username)
|
|
6245
|
+
: normalizeTelegramMentionUsername(effectiveSourceMessageEnvelope.source_bot_username),
|
|
6246
|
+
source_message_envelope: effectiveSourceMessageEnvelope,
|
|
6247
|
+
root_source_message_id: rootSourceMessageID || undefined,
|
|
6248
|
+
root_source_message_thread_id: rootSourceMessageThreadID || undefined,
|
|
6249
|
+
root_source_message_body: rootSourceMessageBody,
|
|
6250
|
+
root_source_message_origin: String(
|
|
6251
|
+
(shouldReuseDirectMessageRequest
|
|
6252
|
+
? existing.root_source_message_origin || existing.source_message_origin
|
|
6253
|
+
: rootSourceMessageEnvelope.source_origin)
|
|
6254
|
+
|| "",
|
|
6255
|
+
).trim().toLowerCase(),
|
|
6256
|
+
root_source_message_route_key: String(
|
|
6257
|
+
(shouldReuseDirectMessageRequest
|
|
6258
|
+
? existing.root_source_message_route_key || existing.source_message_route_key
|
|
6259
|
+
: rootSourceMessageEnvelope.source_route_key)
|
|
6260
|
+
|| "",
|
|
6261
|
+
).trim(),
|
|
6262
|
+
root_source_message_bot_username: normalizeTelegramMentionUsername(
|
|
6263
|
+
(shouldReuseDirectMessageRequest
|
|
6264
|
+
? existing.root_source_message_bot_username || existing.source_message_bot_username
|
|
6265
|
+
: rootSourceMessageEnvelope.source_bot_username),
|
|
6266
|
+
),
|
|
6267
|
+
root_source_message_envelope: rootSourceMessageEnvelope,
|
|
6268
|
+
current_source_message_id: currentSourceMessageID || undefined,
|
|
6269
|
+
current_source_message_thread_id: currentSourceMessageThreadID || undefined,
|
|
6270
|
+
current_source_message_body: currentSourceMessageBody,
|
|
6271
|
+
current_source_message_origin: String(currentSourceMessageEnvelope.source_origin || "").trim().toLowerCase(),
|
|
6272
|
+
current_source_message_route_key: String(currentSourceMessageEnvelope.source_route_key || "").trim(),
|
|
6273
|
+
current_source_message_bot_username: normalizeTelegramMentionUsername(
|
|
6274
|
+
currentSourceMessageEnvelope.source_bot_username,
|
|
6275
|
+
),
|
|
6276
|
+
current_source_message_envelope: currentSourceMessageEnvelope,
|
|
6277
|
+
current_turn_relation: currentTurnRelation,
|
|
6278
|
+
root_comment_id: shouldReuseDirectMessageRequest
|
|
6279
|
+
? String(existing.root_comment_id || selectedRecord?.id || "").trim()
|
|
6280
|
+
: String(selectedRecord?.id || "").trim(),
|
|
6281
|
+
root_comment_kind: shouldReuseDirectMessageRequest
|
|
6282
|
+
? String(existing.root_comment_kind || commentKind).trim().toLowerCase()
|
|
6283
|
+
: commentKind,
|
|
6284
|
+
conversation_id: effectiveConversationID,
|
|
5246
6285
|
reply_chain_context: authorityReplyChainContext,
|
|
5247
6286
|
selected_bot_usernames: effectiveSelectedBotUsernames,
|
|
5248
|
-
authoritative_decision_bundle:
|
|
5249
|
-
|
|
5250
|
-
|
|
6287
|
+
authoritative_decision_bundle: shouldReuseDirectMessageRequest
|
|
6288
|
+
? safeObject(existing.authoritative_decision_bundle)
|
|
6289
|
+
: authoritativeDecisionBundle,
|
|
6290
|
+
decision_bundle_validation_status: shouldReuseDirectMessageRequest
|
|
6291
|
+
? String(existing.decision_bundle_validation_status || "").trim()
|
|
6292
|
+
: String(decisionBundleValidation.status || "").trim(),
|
|
6293
|
+
decision_bundle_validation_reason: shouldReuseDirectMessageRequest
|
|
6294
|
+
? String(existing.decision_bundle_validation_reason || "").trim()
|
|
6295
|
+
: String(decisionBundleValidation.reason || "").trim(),
|
|
5251
6296
|
conversation_intent_mode: String(
|
|
5252
6297
|
(hasAuthoritativeConversationDecision
|
|
5253
6298
|
? authoritativeDecisionBundle.conversation_intent_mode
|
|
5254
6299
|
: "")
|
|
5255
6300
|
|| normalizedSharedHumanIntent.intentMode
|
|
5256
6301
|
|| existing.conversation_intent_mode
|
|
5257
|
-
||
|
|
6302
|
+
|| effectiveAuthoritySource.conversation_intent_mode
|
|
5258
6303
|
|| "",
|
|
5259
6304
|
).trim().toLowerCase(),
|
|
5260
6305
|
conversation_lead_bot: normalizeTelegramMentionUsername(
|
|
@@ -5263,7 +6308,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5263
6308
|
: "")
|
|
5264
6309
|
|| normalizedSharedHumanIntent.leadBotSelector
|
|
5265
6310
|
|| existing.conversation_lead_bot
|
|
5266
|
-
||
|
|
6311
|
+
|| effectiveAuthoritySource.conversation_lead_bot,
|
|
5267
6312
|
),
|
|
5268
6313
|
conversation_summary_bot: normalizeTelegramMentionUsername(
|
|
5269
6314
|
(hasAuthoritativeConversationDecision
|
|
@@ -5271,7 +6316,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5271
6316
|
: "")
|
|
5272
6317
|
|| normalizedSharedHumanIntent.summaryBotSelector
|
|
5273
6318
|
|| existing.conversation_summary_bot
|
|
5274
|
-
||
|
|
6319
|
+
|| effectiveAuthoritySource.conversation_summary_bot,
|
|
5275
6320
|
),
|
|
5276
6321
|
conversation_participants: uniqueOrderedStrings(
|
|
5277
6322
|
hasAuthoritativeConversationDecision && decisionConversationParticipants.length
|
|
@@ -5280,10 +6325,10 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5280
6325
|
? normalizedSharedHumanIntent.participantSelectors
|
|
5281
6326
|
: ensureArray(existing.conversation_participants).length
|
|
5282
6327
|
? existing.conversation_participants
|
|
5283
|
-
: ensureArray(
|
|
5284
|
-
?
|
|
5285
|
-
: [],
|
|
5286
|
-
normalizeTelegramMentionUsername,
|
|
6328
|
+
: ensureArray(effectiveAuthoritySource.conversation_participants).length
|
|
6329
|
+
? effectiveAuthoritySource.conversation_participants
|
|
6330
|
+
: [],
|
|
6331
|
+
normalizeTelegramMentionUsername,
|
|
5287
6332
|
),
|
|
5288
6333
|
conversation_initial_responders: uniqueOrderedStrings(
|
|
5289
6334
|
hasAuthoritativeConversationDecision && decisionInitialResponders.length
|
|
@@ -5292,10 +6337,10 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5292
6337
|
? normalizedSharedHumanIntent.initialResponderSelectors
|
|
5293
6338
|
: ensureArray(existing.conversation_initial_responders).length
|
|
5294
6339
|
? existing.conversation_initial_responders
|
|
5295
|
-
: ensureArray(
|
|
5296
|
-
?
|
|
5297
|
-
: [],
|
|
5298
|
-
normalizeTelegramMentionUsername,
|
|
6340
|
+
: ensureArray(effectiveAuthoritySource.conversation_initial_responders).length
|
|
6341
|
+
? effectiveAuthoritySource.conversation_initial_responders
|
|
6342
|
+
: [],
|
|
6343
|
+
normalizeTelegramMentionUsername,
|
|
5299
6344
|
),
|
|
5300
6345
|
conversation_allowed_responders: uniqueOrderedStrings(
|
|
5301
6346
|
hasAuthoritativeConversationDecision && decisionAllowedResponders.length
|
|
@@ -5304,9 +6349,9 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5304
6349
|
? normalizedSharedHumanIntent.allowedResponderSelectors
|
|
5305
6350
|
: ensureArray(existing.conversation_allowed_responders).length
|
|
5306
6351
|
? existing.conversation_allowed_responders
|
|
5307
|
-
: ensureArray(
|
|
5308
|
-
?
|
|
5309
|
-
: [],
|
|
6352
|
+
: ensureArray(effectiveAuthoritySource.conversation_allowed_responders).length
|
|
6353
|
+
? effectiveAuthoritySource.conversation_allowed_responders
|
|
6354
|
+
: [],
|
|
5310
6355
|
normalizeTelegramMentionUsername,
|
|
5311
6356
|
),
|
|
5312
6357
|
conversation_allow_bot_to_bot: (hasAuthoritativeConversationDecision
|
|
@@ -5314,14 +6359,14 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5314
6359
|
: false)
|
|
5315
6360
|
|| normalizedSharedHumanIntent.allowBotToBot === true
|
|
5316
6361
|
|| existing.conversation_allow_bot_to_bot === true
|
|
5317
|
-
||
|
|
6362
|
+
|| effectiveAuthoritySource.conversation_allow_bot_to_bot === true,
|
|
5318
6363
|
conversation_reply_expectation: String(
|
|
5319
6364
|
(hasAuthoritativeConversationDecision
|
|
5320
6365
|
? authoritativeDecisionBundle.conversation_reply_expectation
|
|
5321
6366
|
: "")
|
|
5322
6367
|
|| normalizedSharedHumanIntent.replyExpectation
|
|
5323
6368
|
|| existing.conversation_reply_expectation
|
|
5324
|
-
||
|
|
6369
|
+
|| effectiveAuthoritySource.conversation_reply_expectation
|
|
5325
6370
|
|| "",
|
|
5326
6371
|
).trim().toLowerCase(),
|
|
5327
6372
|
execution_contract_type: String(
|
|
@@ -5329,20 +6374,20 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5329
6374
|
? authoritativeDecisionBundle.execution_contract_type
|
|
5330
6375
|
: "")
|
|
5331
6376
|
|| runnerRequestPreferredExecutionContractType(existing)
|
|
5332
|
-
|| runnerRequestPreferredExecutionContractType(
|
|
6377
|
+
|| runnerRequestPreferredExecutionContractType(effectiveAuthoritySource)
|
|
5333
6378
|
|| "",
|
|
5334
6379
|
).trim().toLowerCase(),
|
|
5335
6380
|
execution_contract_actionable: (hasAuthoritativeConversationDecision
|
|
5336
6381
|
? authoritativeDecisionBundle.execution_contract_actionable === true
|
|
5337
6382
|
: false)
|
|
5338
6383
|
|| runnerRequestPreferredExecutionContractActionable(existing)
|
|
5339
|
-
|| runnerRequestPreferredExecutionContractActionable(
|
|
6384
|
+
|| runnerRequestPreferredExecutionContractActionable(effectiveAuthoritySource),
|
|
5340
6385
|
execution_contract_targets: uniqueOrderedStrings(
|
|
5341
6386
|
hasAuthoritativeConversationDecision && ensureArray(authoritativeDecisionBundle.execution_contract_targets).length
|
|
5342
6387
|
? authoritativeDecisionBundle.execution_contract_targets
|
|
5343
6388
|
: runnerRequestPreferredExecutionContractTargets(existing).length
|
|
5344
6389
|
? runnerRequestPreferredExecutionContractTargets(existing)
|
|
5345
|
-
: runnerRequestPreferredExecutionContractTargets(
|
|
6390
|
+
: runnerRequestPreferredExecutionContractTargets(effectiveAuthoritySource),
|
|
5346
6391
|
normalizeTelegramMentionUsername,
|
|
5347
6392
|
),
|
|
5348
6393
|
next_expected_responders: uniqueOrderedStrings(
|
|
@@ -5350,31 +6395,77 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5350
6395
|
? authoritativeDecisionBundle.next_expected_responders
|
|
5351
6396
|
: runnerRequestPreferredNextExpectedResponders(existing).length
|
|
5352
6397
|
? runnerRequestPreferredNextExpectedResponders(existing)
|
|
5353
|
-
: runnerRequestPreferredNextExpectedResponders(
|
|
6398
|
+
: runnerRequestPreferredNextExpectedResponders(effectiveAuthoritySource),
|
|
5354
6399
|
normalizeTelegramMentionUsername,
|
|
5355
|
-
),
|
|
5356
|
-
normalized_intent: String(preferredNormalizedIntent || existing.normalized_intent || "").trim().toLowerCase(),
|
|
6400
|
+
),
|
|
6401
|
+
normalized_intent: String(preferredNormalizedIntent || existing.normalized_intent || "").trim().toLowerCase(),
|
|
5357
6402
|
status: "claimed",
|
|
5358
|
-
claimed_by_route: String(routeKey || "").trim(),
|
|
5359
|
-
claimed_at: firstNonEmptyString([existing.claimed_at, nowISO]) || nowISO,
|
|
5360
|
-
root_work_item_id: String(existing.root_work_item_id ||
|
|
5361
|
-
root_work_item_title: String(existing.root_work_item_title ||
|
|
5362
|
-
root_work_item_status: normalizeRunnerWorkItemStatus(
|
|
5363
|
-
existing.root_work_item_status ||
|
|
5364
|
-
),
|
|
5365
|
-
root_thread_id: String(existing.root_thread_id ||
|
|
5366
|
-
root_work_item_created_at: firstNonEmptyString([
|
|
5367
|
-
existing.root_work_item_created_at,
|
|
5368
|
-
|
|
5369
|
-
]),
|
|
5370
|
-
root_work_item_last_error: String(
|
|
5371
|
-
existing.root_work_item_last_error ||
|
|
5372
|
-
).trim(),
|
|
6403
|
+
claimed_by_route: String(routeKey || "").trim(),
|
|
6404
|
+
claimed_at: firstNonEmptyString([existing.claimed_at, nowISO]) || nowISO,
|
|
6405
|
+
root_work_item_id: String(existing.root_work_item_id || effectiveAuthoritySource.root_work_item_id || "").trim(),
|
|
6406
|
+
root_work_item_title: String(existing.root_work_item_title || effectiveAuthoritySource.root_work_item_title || "").trim(),
|
|
6407
|
+
root_work_item_status: normalizeRunnerWorkItemStatus(
|
|
6408
|
+
existing.root_work_item_status || effectiveAuthoritySource.root_work_item_status,
|
|
6409
|
+
),
|
|
6410
|
+
root_thread_id: String(existing.root_thread_id || effectiveAuthoritySource.root_thread_id || "").trim(),
|
|
6411
|
+
root_work_item_created_at: firstNonEmptyString([
|
|
6412
|
+
existing.root_work_item_created_at,
|
|
6413
|
+
effectiveAuthoritySource.root_work_item_created_at,
|
|
6414
|
+
]),
|
|
6415
|
+
root_work_item_last_error: String(
|
|
6416
|
+
existing.root_work_item_last_error || effectiveAuthoritySource.root_work_item_last_error || "",
|
|
6417
|
+
).trim(),
|
|
5373
6418
|
last_comment_id: String(selectedRecord?.id || "").trim(),
|
|
5374
6419
|
last_comment_kind: commentKind,
|
|
5375
|
-
last_source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
|
|
5376
|
-
last_source_message_thread_id: intFromRawAllowZero(parsed.messageThreadID, 0) || undefined,
|
|
5377
|
-
});
|
|
6420
|
+
last_source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
|
|
6421
|
+
last_source_message_thread_id: intFromRawAllowZero(parsed.messageThreadID, 0) || undefined,
|
|
6422
|
+
});
|
|
6423
|
+
if (isDirectMessageRestart) {
|
|
6424
|
+
const restartBotUsername = firstNonEmptyString(claimBotUsernameCandidates);
|
|
6425
|
+
const restartClosedRequests = closeRunnerDirectMessageScopedActiveRequests({
|
|
6426
|
+
state: {
|
|
6427
|
+
...stateForClaim,
|
|
6428
|
+
requests: nextRequests,
|
|
6429
|
+
},
|
|
6430
|
+
normalizedRoute,
|
|
6431
|
+
routeKey,
|
|
6432
|
+
chatID: normalizedChatID,
|
|
6433
|
+
botUsername: restartBotUsername,
|
|
6434
|
+
excludeRequestKey: requestKey,
|
|
6435
|
+
});
|
|
6436
|
+
nextRequests = normalizeBotRunnerRequests(restartClosedRequests.requests);
|
|
6437
|
+
request = safeObject(nextRequests[requestKey]);
|
|
6438
|
+
}
|
|
6439
|
+
if (isDirectMessageRestart && directMessageRestartRequestKeysToClose.size > 0) {
|
|
6440
|
+
const restartFinalNowISO = new Date().toISOString();
|
|
6441
|
+
const claimedRequestKey = String(requestKey || "").trim();
|
|
6442
|
+
for (const restartRequestKeyRaw of directMessageRestartRequestKeysToClose) {
|
|
6443
|
+
const restartRequestKey = String(restartRequestKeyRaw || "").trim();
|
|
6444
|
+
if (!restartRequestKey || restartRequestKey === claimedRequestKey) {
|
|
6445
|
+
continue;
|
|
6446
|
+
}
|
|
6447
|
+
const existingRestartRequest = safeObject(
|
|
6448
|
+
nextRequests[restartRequestKey]
|
|
6449
|
+
|| safeObject(stateForClaim.requests)[restartRequestKey]
|
|
6450
|
+
|| safeObject(currentState.requests)[restartRequestKey]
|
|
6451
|
+
|| safeObject(requests)[restartRequestKey],
|
|
6452
|
+
);
|
|
6453
|
+
if (
|
|
6454
|
+
!String(existingRestartRequest.request_key || "").trim()
|
|
6455
|
+
|| String(existingRestartRequest.chat_id || "").trim() !== normalizedChatID
|
|
6456
|
+
) {
|
|
6457
|
+
continue;
|
|
6458
|
+
}
|
|
6459
|
+
nextRequests[restartRequestKey] = {
|
|
6460
|
+
...existingRestartRequest,
|
|
6461
|
+
status: "closed",
|
|
6462
|
+
closed_reason: "direct_message_restart",
|
|
6463
|
+
closed_at: restartFinalNowISO,
|
|
6464
|
+
updated_at: restartFinalNowISO,
|
|
6465
|
+
};
|
|
6466
|
+
}
|
|
6467
|
+
request = safeObject(nextRequests[requestKey]);
|
|
6468
|
+
}
|
|
5378
6469
|
const { consumedComments: nextConsumedComments } = upsertRunnerConsumedComment(stateForClaim, selectedRecord?.id, {
|
|
5379
6470
|
project_id: String(normalizedRoute?.projectID || "").trim(),
|
|
5380
6471
|
provider: String(normalizedRoute?.provider || "").trim(),
|
|
@@ -5388,18 +6479,55 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5388
6479
|
comment_kind: commentKind,
|
|
5389
6480
|
request_status: "claimed",
|
|
5390
6481
|
});
|
|
5391
|
-
saveBotRunnerState({
|
|
5392
|
-
routes: stateForClaim.routes,
|
|
5393
|
-
sharedInboxes: stateForClaim.sharedInboxes || stateForClaim.shared_inboxes,
|
|
5394
|
-
excludedComments: stateForClaim.excludedComments || stateForClaim.excluded_comments,
|
|
5395
|
-
requests: nextRequests,
|
|
5396
|
-
consumedComments: nextConsumedComments,
|
|
5397
|
-
});
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
6482
|
+
saveBotRunnerState({
|
|
6483
|
+
routes: stateForClaim.routes,
|
|
6484
|
+
sharedInboxes: stateForClaim.sharedInboxes || stateForClaim.shared_inboxes,
|
|
6485
|
+
excludedComments: stateForClaim.excludedComments || stateForClaim.excluded_comments,
|
|
6486
|
+
requests: nextRequests,
|
|
6487
|
+
consumedComments: nextConsumedComments,
|
|
6488
|
+
});
|
|
6489
|
+
if (isDirectMessageRestart && directMessageRestartRequestKeysToClose.size > 0) {
|
|
6490
|
+
const persistedState = loadBotRunnerState();
|
|
6491
|
+
const persistedRequests = normalizeBotRunnerRequests(persistedState.requests);
|
|
6492
|
+
let shouldResavePersistedRequests = false;
|
|
6493
|
+
const persistedRestartNowISO = new Date().toISOString();
|
|
6494
|
+
const claimedRequestKey = String(requestKey || "").trim();
|
|
6495
|
+
for (const restartRequestKey of directMessageRestartRequestKeysToClose) {
|
|
6496
|
+
if (!restartRequestKey || restartRequestKey === claimedRequestKey) {
|
|
6497
|
+
continue;
|
|
6498
|
+
}
|
|
6499
|
+
const existingPersistedRequest = safeObject(persistedRequests[restartRequestKey]);
|
|
6500
|
+
if (
|
|
6501
|
+
!String(existingPersistedRequest.request_key || "").trim()
|
|
6502
|
+
|| !isActiveRunnerRequestStatus(existingPersistedRequest.status)
|
|
6503
|
+
|| String(existingPersistedRequest.chat_id || "").trim() !== normalizedChatID
|
|
6504
|
+
) {
|
|
6505
|
+
continue;
|
|
6506
|
+
}
|
|
6507
|
+
persistedRequests[restartRequestKey] = {
|
|
6508
|
+
...existingPersistedRequest,
|
|
6509
|
+
status: "closed",
|
|
6510
|
+
closed_reason: "direct_message_restart",
|
|
6511
|
+
closed_at: persistedRestartNowISO,
|
|
6512
|
+
updated_at: persistedRestartNowISO,
|
|
6513
|
+
};
|
|
6514
|
+
shouldResavePersistedRequests = true;
|
|
6515
|
+
}
|
|
6516
|
+
if (shouldResavePersistedRequests) {
|
|
6517
|
+
saveBotRunnerState({
|
|
6518
|
+
routes: persistedState.routes,
|
|
6519
|
+
sharedInboxes: persistedState.sharedInboxes || persistedState.shared_inboxes,
|
|
6520
|
+
excludedComments: persistedState.excludedComments || persistedState.excluded_comments,
|
|
6521
|
+
requests: persistedRequests,
|
|
6522
|
+
consumedComments: persistedState.consumedComments || persistedState.consumed_comments,
|
|
6523
|
+
});
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
return {
|
|
6527
|
+
ok: true,
|
|
6528
|
+
requestKey,
|
|
6529
|
+
request,
|
|
6530
|
+
};
|
|
5403
6531
|
}
|
|
5404
6532
|
|
|
5405
6533
|
function runnerRequestRequiresActionableContract(requestRaw) {
|
|
@@ -6414,6 +7542,9 @@ function markRunnerRequestLifecycle({
|
|
|
6414
7542
|
const sourceMessageEnvelope = Object.keys(normalizedAuthoritativeSourceMessageEnvelope).length
|
|
6415
7543
|
? normalizedAuthoritativeSourceMessageEnvelope
|
|
6416
7544
|
: normalizeRunnerTelegramMessageEnvelope(existing.source_message_envelope);
|
|
7545
|
+
const commentKind = String(parsed.kind || "").trim().toLowerCase();
|
|
7546
|
+
const isRootHumanComment = ["telegram_message", "telegram_edited_message"].includes(commentKind);
|
|
7547
|
+
const isFollowupComment = !isRootHumanComment;
|
|
6417
7548
|
const normalizedDecisionBundle = normalizeRunnerConversationDecisionBundle(decisionBundle);
|
|
6418
7549
|
const resolvedDecisionBundleValidation = Object.keys(safeObject(decisionBundle)).length > 0
|
|
6419
7550
|
? validateRunnerConversationDecisionBundle(normalizedDecisionBundle)
|
|
@@ -6423,9 +7554,60 @@ function markRunnerRequestLifecycle({
|
|
|
6423
7554
|
reason: String(decisionBundleValidationReason || "").trim(),
|
|
6424
7555
|
bundle: {},
|
|
6425
7556
|
};
|
|
6426
|
-
const
|
|
7557
|
+
const existingAuthoritativeDecisionBundle = runnerRequestAuthoritativeDecisionBundle(existing);
|
|
7558
|
+
const incomingDecisionBundle = resolvedDecisionBundleValidation.ok === true
|
|
6427
7559
|
? safeObject(resolvedDecisionBundleValidation.bundle)
|
|
6428
|
-
:
|
|
7560
|
+
: {};
|
|
7561
|
+
const existingHasImmutableRootAuthority = runnerDecisionBundleIsRootHumanOpening(
|
|
7562
|
+
existingAuthoritativeDecisionBundle,
|
|
7563
|
+
);
|
|
7564
|
+
const incomingDecisionType = runnerDecisionBundleDecisionType(
|
|
7565
|
+
Object.keys(incomingDecisionBundle).length > 0 ? incomingDecisionBundle : normalizedDecisionBundle,
|
|
7566
|
+
);
|
|
7567
|
+
const shouldWriteIncomingAuthoritativeDecisionBundle = incomingDecisionType === "human_opening"
|
|
7568
|
+
&& Object.keys(incomingDecisionBundle).length > 0;
|
|
7569
|
+
const preserveExistingAuthoritativeDecisionBundle = Object.keys(existingAuthoritativeDecisionBundle).length > 0
|
|
7570
|
+
&& (
|
|
7571
|
+
isFollowupComment
|
|
7572
|
+
|| existingHasImmutableRootAuthority
|
|
7573
|
+
|| !shouldWriteIncomingAuthoritativeDecisionBundle
|
|
7574
|
+
);
|
|
7575
|
+
const authoritativeDecisionBundle = preserveExistingAuthoritativeDecisionBundle
|
|
7576
|
+
? existingAuthoritativeDecisionBundle
|
|
7577
|
+
: Object.keys(incomingDecisionBundle).length > 0
|
|
7578
|
+
? incomingDecisionBundle
|
|
7579
|
+
: existingAuthoritativeDecisionBundle;
|
|
7580
|
+
const effectiveDecisionBundleValidationStatus = preserveExistingAuthoritativeDecisionBundle
|
|
7581
|
+
? String(existing.decision_bundle_validation_status || "").trim()
|
|
7582
|
+
: String(
|
|
7583
|
+
resolvedDecisionBundleValidation.status
|
|
7584
|
+
|| decisionBundleValidationStatus
|
|
7585
|
+
|| existing.decision_bundle_validation_status
|
|
7586
|
+
|| "",
|
|
7587
|
+
).trim();
|
|
7588
|
+
const effectiveDecisionBundleValidationReason = preserveExistingAuthoritativeDecisionBundle
|
|
7589
|
+
? String(existing.decision_bundle_validation_reason || "").trim()
|
|
7590
|
+
: String(
|
|
7591
|
+
resolvedDecisionBundleValidation.reason
|
|
7592
|
+
|| decisionBundleValidationReason
|
|
7593
|
+
|| existing.decision_bundle_validation_reason
|
|
7594
|
+
|| "",
|
|
7595
|
+
).trim();
|
|
7596
|
+
const lastReplyDecisionBundle = Object.keys(incomingDecisionBundle).length > 0
|
|
7597
|
+
? incomingDecisionBundle
|
|
7598
|
+
: safeObject(existing.last_reply_decision_bundle);
|
|
7599
|
+
const lastReplyDecisionBundleValidationStatus = String(
|
|
7600
|
+
resolvedDecisionBundleValidation.status
|
|
7601
|
+
|| decisionBundleValidationStatus
|
|
7602
|
+
|| existing.last_reply_decision_bundle_validation_status
|
|
7603
|
+
|| "",
|
|
7604
|
+
).trim();
|
|
7605
|
+
const lastReplyDecisionBundleValidationReason = String(
|
|
7606
|
+
resolvedDecisionBundleValidation.reason
|
|
7607
|
+
|| decisionBundleValidationReason
|
|
7608
|
+
|| existing.last_reply_decision_bundle_validation_reason
|
|
7609
|
+
|| "",
|
|
7610
|
+
).trim();
|
|
6429
7611
|
const effectiveReplyToMessageID = intFromRawAllowZero(replyToMessageID, 0);
|
|
6430
7612
|
const lastReplyMessageEnvelope = buildTelegramBotReplyEnvelope({
|
|
6431
7613
|
sourceEnvelope: sourceMessageEnvelope,
|
|
@@ -6439,7 +7621,7 @@ function markRunnerRequestLifecycle({
|
|
|
6439
7621
|
const normalizedDeliveryStatus = String(deliveryStatus || "").trim().toLowerCase();
|
|
6440
7622
|
const persistSuccessfulReplyEnvelope = ["delivered", "dry_run"].includes(normalizedDeliveryStatus)
|
|
6441
7623
|
&& intFromRawAllowZero(lastReplyMessageEnvelope.message_id, 0) > 0;
|
|
6442
|
-
const attemptedDeliveryEnvelope = buildTelegramBotReplyEnvelope({
|
|
7624
|
+
const attemptedDeliveryEnvelope = buildTelegramBotReplyEnvelope({
|
|
6443
7625
|
sourceEnvelope: sourceMessageEnvelope,
|
|
6444
7626
|
chatID: existing.chat_id,
|
|
6445
7627
|
messageThreadID: lastReplyMessageThreadID,
|
|
@@ -6455,9 +7637,13 @@ function markRunnerRequestLifecycle({
|
|
|
6455
7637
|
|| intFromRawAllowZero(replyToMessageID, 0) > 0
|
|
6456
7638
|
|| intFromRawAllowZero(lastReplyMessageThreadID, 0) > 0
|
|
6457
7639
|
);
|
|
7640
|
+
const continuationDecisionBundle = Object.keys(incomingDecisionBundle).length > 0
|
|
7641
|
+
? incomingDecisionBundle
|
|
7642
|
+
: safeObject(existing.last_reply_decision_bundle);
|
|
6458
7643
|
const rootEffectiveExecutionContractTargets = uniqueOrderedStrings(
|
|
6459
7644
|
[
|
|
6460
7645
|
...ensureArray(authoritativeDecisionBundle.execution_contract_targets),
|
|
7646
|
+
...ensureArray(continuationDecisionBundle.execution_contract_targets),
|
|
6461
7647
|
...ensureArray(executionContractTargets),
|
|
6462
7648
|
...ensureArray(normalizedExecutionContractTargets),
|
|
6463
7649
|
...ensureArray(responseContractValidationTargets),
|
|
@@ -6467,6 +7653,7 @@ function markRunnerRequestLifecycle({
|
|
|
6467
7653
|
const rootEffectiveNextExpectedResponders = uniqueOrderedStrings(
|
|
6468
7654
|
[
|
|
6469
7655
|
...ensureArray(authoritativeDecisionBundle.next_expected_responders),
|
|
7656
|
+
...ensureArray(continuationDecisionBundle.next_expected_responders),
|
|
6470
7657
|
...ensureArray(nextExpectedResponders),
|
|
6471
7658
|
...ensureArray(normalizedExecutionNextResponders),
|
|
6472
7659
|
...ensureArray(responseContractValidationTargets),
|
|
@@ -6477,11 +7664,15 @@ function markRunnerRequestLifecycle({
|
|
|
6477
7664
|
[
|
|
6478
7665
|
...ensureArray(authoritativeDecisionBundle.execution_contract_targets).length
|
|
6479
7666
|
? ensureArray(authoritativeDecisionBundle.execution_contract_targets)
|
|
7667
|
+
: ensureArray(continuationDecisionBundle.execution_contract_targets).length
|
|
7668
|
+
? ensureArray(continuationDecisionBundle.execution_contract_targets)
|
|
6480
7669
|
: ensureArray(executionContractTargets).length
|
|
6481
7670
|
? ensureArray(executionContractTargets)
|
|
6482
7671
|
: ensureArray(existing.execution_contract_targets),
|
|
6483
7672
|
...ensureArray(authoritativeDecisionBundle.next_expected_responders).length
|
|
6484
7673
|
? ensureArray(authoritativeDecisionBundle.next_expected_responders)
|
|
7674
|
+
: ensureArray(continuationDecisionBundle.next_expected_responders).length
|
|
7675
|
+
? ensureArray(continuationDecisionBundle.next_expected_responders)
|
|
6485
7676
|
: ensureArray(nextExpectedResponders).length
|
|
6486
7677
|
? ensureArray(nextExpectedResponders)
|
|
6487
7678
|
: ensureArray(existing.next_expected_responders),
|
|
@@ -6490,13 +7681,20 @@ function markRunnerRequestLifecycle({
|
|
|
6490
7681
|
).filter((selector) => selector && selector !== normalizedCurrentBotSelector);
|
|
6491
7682
|
const nextExecutionContractType = String(
|
|
6492
7683
|
authoritativeDecisionBundle.execution_contract_type
|
|
7684
|
+
|| continuationDecisionBundle.execution_contract_type
|
|
6493
7685
|
|| executionContractType
|
|
6494
7686
|
|| existing.execution_contract_type
|
|
6495
7687
|
|| "",
|
|
6496
7688
|
).trim().toLowerCase();
|
|
7689
|
+
const shouldPromoteRootContinuationState = isRootHumanComment || existingHasImmutableRootAuthority;
|
|
6497
7690
|
const normalizedOutcome = String(outcome || "").trim().toLowerCase();
|
|
6498
7691
|
const normalizedFailureReplyClassification = String(failureReplyClassification || "").trim().toLowerCase();
|
|
6499
7692
|
const normalizedFailureFacts = safeObject(failureFacts);
|
|
7693
|
+
const isTerminalStaleReplyAnchorSkip = normalizedOutcome === "skipped"
|
|
7694
|
+
&& (
|
|
7695
|
+
normalizedDeliveryStatus === "skipped_stale_reply_anchor"
|
|
7696
|
+
|| String(closedReason || "").trim().toLowerCase() === "stale_reply_anchor"
|
|
7697
|
+
);
|
|
6500
7698
|
const shouldPersistReplyAnchor = (
|
|
6501
7699
|
aiReplyGenerated === true
|
|
6502
7700
|
|| intFromRawAllowZero(lastReplyMessageID, 0) > 0
|
|
@@ -6511,6 +7709,7 @@ function markRunnerRequestLifecycle({
|
|
|
6511
7709
|
: continuationSelectors.length > 0;
|
|
6512
7710
|
const shouldRemainRunningAfterSkip = normalizedOutcome === "skipped"
|
|
6513
7711
|
&& parsedKind === "bot_reply"
|
|
7712
|
+
&& !isTerminalStaleReplyAnchorSkip
|
|
6514
7713
|
&& authoritativeDecisionBundle.should_close_after_reply !== true
|
|
6515
7714
|
&& (
|
|
6516
7715
|
nextExecutionContractType === "delegation"
|
|
@@ -6571,25 +7770,17 @@ function markRunnerRequestLifecycle({
|
|
|
6571
7770
|
return normalizeRunnerRequestStatus(existing.status);
|
|
6572
7771
|
})();
|
|
6573
7772
|
const nowISO = new Date().toISOString();
|
|
6574
|
-
const commentKind = String(parsed.kind || "").trim().toLowerCase();
|
|
6575
|
-
const isRootHumanComment = ["telegram_message", "telegram_edited_message"].includes(commentKind);
|
|
6576
|
-
const isFollowupComment = !isRootHumanComment;
|
|
6577
7773
|
const patch = {
|
|
6578
7774
|
authoritative_decision_bundle: Object.keys(authoritativeDecisionBundle).length > 0
|
|
6579
7775
|
? authoritativeDecisionBundle
|
|
6580
7776
|
: safeObject(existing.authoritative_decision_bundle),
|
|
6581
|
-
decision_bundle_validation_status:
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
resolvedDecisionBundleValidation.reason
|
|
6589
|
-
|| decisionBundleValidationReason
|
|
6590
|
-
|| existing.decision_bundle_validation_reason
|
|
6591
|
-
|| "",
|
|
6592
|
-
).trim(),
|
|
7777
|
+
decision_bundle_validation_status: effectiveDecisionBundleValidationStatus,
|
|
7778
|
+
decision_bundle_validation_reason: effectiveDecisionBundleValidationReason,
|
|
7779
|
+
last_reply_decision_bundle: Object.keys(lastReplyDecisionBundle).length > 0
|
|
7780
|
+
? lastReplyDecisionBundle
|
|
7781
|
+
: safeObject(existing.last_reply_decision_bundle),
|
|
7782
|
+
last_reply_decision_bundle_validation_status: lastReplyDecisionBundleValidationStatus,
|
|
7783
|
+
last_reply_decision_bundle_validation_reason: lastReplyDecisionBundleValidationReason,
|
|
6593
7784
|
conversation_id: conversationID,
|
|
6594
7785
|
conversation_participants: uniqueOrderedStrings(
|
|
6595
7786
|
ensureArray(authoritativeDecisionBundle.participants).length
|
|
@@ -6644,7 +7835,7 @@ function markRunnerRequestLifecycle({
|
|
|
6644
7835
|
execution_contract_targets: uniqueOrderedStrings(
|
|
6645
7836
|
ensureArray(authoritativeDecisionBundle.execution_contract_targets).length
|
|
6646
7837
|
? authoritativeDecisionBundle.execution_contract_targets
|
|
6647
|
-
:
|
|
7838
|
+
: shouldPromoteRootContinuationState && rootEffectiveExecutionContractTargets.length
|
|
6648
7839
|
? rootEffectiveExecutionContractTargets
|
|
6649
7840
|
: ensureArray(executionContractTargets).length
|
|
6650
7841
|
? executionContractTargets
|
|
@@ -6654,7 +7845,7 @@ function markRunnerRequestLifecycle({
|
|
|
6654
7845
|
next_expected_responders: uniqueOrderedStrings(
|
|
6655
7846
|
ensureArray(authoritativeDecisionBundle.next_expected_responders).length
|
|
6656
7847
|
? authoritativeDecisionBundle.next_expected_responders
|
|
6657
|
-
:
|
|
7848
|
+
: shouldPromoteRootContinuationState && rootEffectiveNextExpectedResponders.length
|
|
6658
7849
|
? rootEffectiveNextExpectedResponders
|
|
6659
7850
|
: ensureArray(nextExpectedResponders).length
|
|
6660
7851
|
? nextExpectedResponders
|
|
@@ -6690,24 +7881,24 @@ function markRunnerRequestLifecycle({
|
|
|
6690
7881
|
? aiReplyPreview || existing.followup_ai_reply_preview || ""
|
|
6691
7882
|
: existing.followup_ai_reply_preview || "",
|
|
6692
7883
|
).trim(),
|
|
6693
|
-
root_execution_contract_type: String(
|
|
6694
|
-
|
|
6695
|
-
? nextExecutionContractType
|
|
6696
|
-
: existing.root_execution_contract_type || existing.execution_contract_type || "",
|
|
6697
|
-
).trim().toLowerCase(),
|
|
6698
|
-
root_execution_contract_targets: uniqueOrderedStrings(
|
|
6699
|
-
|
|
6700
|
-
? rootEffectiveExecutionContractTargets
|
|
6701
|
-
: ensureArray(existing.root_execution_contract_targets).length
|
|
6702
|
-
? existing.root_execution_contract_targets
|
|
7884
|
+
root_execution_contract_type: String(
|
|
7885
|
+
shouldPromoteRootContinuationState
|
|
7886
|
+
? nextExecutionContractType
|
|
7887
|
+
: existing.root_execution_contract_type || existing.execution_contract_type || "",
|
|
7888
|
+
).trim().toLowerCase(),
|
|
7889
|
+
root_execution_contract_targets: uniqueOrderedStrings(
|
|
7890
|
+
shouldPromoteRootContinuationState && rootEffectiveExecutionContractTargets.length
|
|
7891
|
+
? rootEffectiveExecutionContractTargets
|
|
7892
|
+
: ensureArray(existing.root_execution_contract_targets).length
|
|
7893
|
+
? existing.root_execution_contract_targets
|
|
6703
7894
|
: existing.execution_contract_targets,
|
|
6704
7895
|
normalizeTelegramMentionUsername,
|
|
6705
7896
|
),
|
|
6706
|
-
root_next_expected_responders: uniqueOrderedStrings(
|
|
6707
|
-
|
|
6708
|
-
? rootEffectiveNextExpectedResponders
|
|
6709
|
-
: ensureArray(existing.root_next_expected_responders).length
|
|
6710
|
-
? existing.root_next_expected_responders
|
|
7897
|
+
root_next_expected_responders: uniqueOrderedStrings(
|
|
7898
|
+
shouldPromoteRootContinuationState && rootEffectiveNextExpectedResponders.length
|
|
7899
|
+
? rootEffectiveNextExpectedResponders
|
|
7900
|
+
: ensureArray(existing.root_next_expected_responders).length
|
|
7901
|
+
? existing.root_next_expected_responders
|
|
6711
7902
|
: existing.next_expected_responders,
|
|
6712
7903
|
normalizeTelegramMentionUsername,
|
|
6713
7904
|
),
|
|
@@ -7001,18 +8192,36 @@ function cleanupBotRunnerRequestState({
|
|
|
7001
8192
|
}
|
|
7002
8193
|
const expiresAtMs = Date.parse(String(session.expires_at || "").trim());
|
|
7003
8194
|
const expired = Number.isFinite(expiresAtMs) && expiresAtMs <= nowMs;
|
|
7004
|
-
|
|
7005
|
-
String(entry.project_id || "").trim() === String(candidateRoute.projectID || "").trim()
|
|
7006
|
-
&& String(entry.provider || "").trim() === String(candidateRoute.provider || "").trim()
|
|
7007
|
-
&& String(entry.conversation_id || "").trim() === String(conversationID || "").trim()
|
|
7008
|
-
&& isActiveRunnerRequestStatus(entry.status)
|
|
7009
|
-
));
|
|
7010
|
-
|
|
7011
|
-
.
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
8195
|
+
let activeRequests = Object.values(nextRequests).filter((entry) => (
|
|
8196
|
+
String(entry.project_id || "").trim() === String(candidateRoute.projectID || "").trim()
|
|
8197
|
+
&& String(entry.provider || "").trim() === String(candidateRoute.provider || "").trim()
|
|
8198
|
+
&& String(entry.conversation_id || "").trim() === String(conversationID || "").trim()
|
|
8199
|
+
&& isActiveRunnerRequestStatus(entry.status)
|
|
8200
|
+
));
|
|
8201
|
+
if (String(session.mode || "").trim() === "direct_single_bot") {
|
|
8202
|
+
const directMessageRequestKey = String(session.request_key || "").trim();
|
|
8203
|
+
if (directMessageRequestKey) {
|
|
8204
|
+
const directMessageRequest = safeObject(nextRequests[directMessageRequestKey]);
|
|
8205
|
+
if (
|
|
8206
|
+
Object.keys(directMessageRequest).length > 0
|
|
8207
|
+
&& isActiveRunnerRequestStatus(directMessageRequest.status)
|
|
8208
|
+
&& String(directMessageRequest.project_id || "").trim() === String(candidateRoute.projectID || "").trim()
|
|
8209
|
+
&& String(directMessageRequest.provider || "").trim() === String(candidateRoute.provider || "").trim()
|
|
8210
|
+
&& !activeRequests.some((entry) => String(entry.request_key || "").trim() === directMessageRequestKey)
|
|
8211
|
+
) {
|
|
8212
|
+
activeRequests = [...activeRequests, directMessageRequest];
|
|
8213
|
+
}
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
const pendingContinuationResponders = ensureArray(session.next_expected_responders)
|
|
8217
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
8218
|
+
.filter(Boolean);
|
|
8219
|
+
if (String(session.mode || "").trim() === "direct_single_bot" && !expired) {
|
|
8220
|
+
continue;
|
|
8221
|
+
}
|
|
8222
|
+
if (!expired && (activeRequests.length > 0 || pendingContinuationResponders.length > 0)) {
|
|
8223
|
+
continue;
|
|
8224
|
+
}
|
|
7016
8225
|
const closedReason = expired ? "expired_session" : "orphaned_open_session";
|
|
7017
8226
|
conversationSessions[conversationID] = {
|
|
7018
8227
|
...session,
|
|
@@ -7161,6 +8370,8 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
|
|
|
7161
8370
|
preserveLocalStringWhenServerBlank("conversation_reply_expectation");
|
|
7162
8371
|
preserveLocalStringWhenServerBlank("decision_bundle_validation_status");
|
|
7163
8372
|
preserveLocalStringWhenServerBlank("decision_bundle_validation_reason");
|
|
8373
|
+
preserveLocalStringWhenServerBlank("last_reply_decision_bundle_validation_status");
|
|
8374
|
+
preserveLocalStringWhenServerBlank("last_reply_decision_bundle_validation_reason");
|
|
7164
8375
|
preserveLocalStringWhenServerBlank("execution_contract_type");
|
|
7165
8376
|
preserveLocalStringWhenServerBlank("normalized_execution_contract_type");
|
|
7166
8377
|
preserveLocalStringWhenServerBlank("ai_reply_generated_at");
|
|
@@ -7190,6 +8401,7 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
|
|
|
7190
8401
|
preserveLocalArrayWhenServerEmpty("followup_response_contract_validation_targets");
|
|
7191
8402
|
preserveLocalArrayWhenServerEmpty("followup_assignment_validation_modes");
|
|
7192
8403
|
preserveLocalObjectWhenServerBlank("authoritative_decision_bundle");
|
|
8404
|
+
preserveLocalObjectWhenServerBlank("last_reply_decision_bundle");
|
|
7193
8405
|
preserveLocalObjectWhenServerBlank("reply_chain_context");
|
|
7194
8406
|
clearMergedStringWhenServerBlank("root_execution_contract_type");
|
|
7195
8407
|
clearMergedArrayWhenServerEmpty("root_execution_contract_targets");
|
|
@@ -7760,31 +8972,60 @@ function scanExternalProjectArtifacts({
|
|
|
7760
8972
|
};
|
|
7761
8973
|
}
|
|
7762
8974
|
|
|
7763
|
-
function normalizeRunnerRouteIdentityText(rawValue) {
|
|
7764
|
-
return String(rawValue || "").trim().toLowerCase();
|
|
7765
|
-
}
|
|
7766
|
-
|
|
7767
|
-
function
|
|
7768
|
-
const
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
}
|
|
7777
|
-
|
|
7778
|
-
function
|
|
7779
|
-
const normalized = normalizeRunnerRoute(route);
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
8975
|
+
function normalizeRunnerRouteIdentityText(rawValue) {
|
|
8976
|
+
return String(rawValue || "").trim().toLowerCase();
|
|
8977
|
+
}
|
|
8978
|
+
|
|
8979
|
+
function normalizeRunnerRouteKind(rawValue, fallback = "room") {
|
|
8980
|
+
const value = String(rawValue || "").trim().toLowerCase();
|
|
8981
|
+
if (["dm", "direct-messages", "direct_messages", "private", "private-chat", "private_chat"].includes(value)) {
|
|
8982
|
+
return "dm";
|
|
8983
|
+
}
|
|
8984
|
+
if (["room", "chat-room", "chat_room"].includes(value)) {
|
|
8985
|
+
return "room";
|
|
8986
|
+
}
|
|
8987
|
+
return fallback;
|
|
8988
|
+
}
|
|
8989
|
+
|
|
8990
|
+
function buildRunnerRouteTargetIdentity(route) {
|
|
8991
|
+
const normalized = normalizeRunnerRoute(route);
|
|
8992
|
+
if (normalized.routeKind === "dm") {
|
|
8993
|
+
return "direct-messages";
|
|
8994
|
+
}
|
|
8995
|
+
return normalized.destinationID || normalizeRunnerRouteIdentityText(normalized.destinationLabel) || "-";
|
|
8996
|
+
}
|
|
8997
|
+
|
|
8998
|
+
function describeRunnerRouteTargetLabel(route) {
|
|
8999
|
+
const normalized = normalizeRunnerRoute(route);
|
|
9000
|
+
if (normalized.routeKind === "dm") {
|
|
9001
|
+
return "Direct Messages";
|
|
9002
|
+
}
|
|
9003
|
+
return normalized.destinationLabel || normalized.destinationID || "-";
|
|
9004
|
+
}
|
|
9005
|
+
|
|
9006
|
+
function runnerRouteLogicalSignature(route) {
|
|
9007
|
+
const normalized = normalizeRunnerRoute(route);
|
|
9008
|
+
return [
|
|
9009
|
+
normalized.projectID || "-",
|
|
9010
|
+
normalized.provider || "-",
|
|
9011
|
+
normalized.routeKind || "room",
|
|
9012
|
+
normalized.role || "-",
|
|
9013
|
+
normalized.botID || normalizeRunnerRouteIdentityText(normalized.botName) || "-",
|
|
9014
|
+
buildRunnerRouteTargetIdentity(normalized),
|
|
9015
|
+
].join("::");
|
|
9016
|
+
}
|
|
9017
|
+
|
|
9018
|
+
function describeRunnerRouteLogicalTarget(route) {
|
|
9019
|
+
const normalized = normalizeRunnerRoute(route);
|
|
9020
|
+
return [
|
|
9021
|
+
`project_id=${normalized.projectID || "-"}`,
|
|
9022
|
+
`provider=${normalized.provider || "-"}`,
|
|
9023
|
+
`route_kind=${normalized.routeKind || "room"}`,
|
|
9024
|
+
`role=${normalized.role || "-"}`,
|
|
9025
|
+
`server_bot=${normalized.botName || normalized.botID || "-"}`,
|
|
9026
|
+
`target=${describeRunnerRouteTargetLabel(normalized)}`,
|
|
9027
|
+
].join(", ");
|
|
9028
|
+
}
|
|
7788
9029
|
|
|
7789
9030
|
function findRunnerRouteLogicalConflicts(route, config) {
|
|
7790
9031
|
const normalizedRoute = normalizeRunnerRoute(route);
|
|
@@ -7904,14 +9145,23 @@ async function runTasksWithConcurrencyLimit(items, limit, worker) {
|
|
|
7904
9145
|
return output;
|
|
7905
9146
|
}
|
|
7906
9147
|
|
|
7907
|
-
function runnerRouteSchedulingGroupKey(route) {
|
|
7908
|
-
const normalized = normalizeRunnerRoute(route);
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
9148
|
+
function runnerRouteSchedulingGroupKey(route) {
|
|
9149
|
+
const normalized = normalizeRunnerRoute(route);
|
|
9150
|
+
if (normalized.routeKind === "dm") {
|
|
9151
|
+
return [
|
|
9152
|
+
String(normalized.projectID || "").trim(),
|
|
9153
|
+
String(normalized.provider || "").trim(),
|
|
9154
|
+
String(normalized.routeKind || "room").trim(),
|
|
9155
|
+
String(normalized.botID || "").trim() || normalizeRunnerRouteIdentityText(normalized.botName) || String(normalized.name || "").trim(),
|
|
9156
|
+
].join("::");
|
|
9157
|
+
}
|
|
9158
|
+
return [
|
|
9159
|
+
String(normalized.projectID || "").trim(),
|
|
9160
|
+
String(normalized.provider || "").trim(),
|
|
9161
|
+
String(normalized.routeKind || "room").trim(),
|
|
9162
|
+
String(normalized.destinationID || "").trim() || String(normalized.destinationLabel || "").trim() || String(normalized.name || "").trim(),
|
|
9163
|
+
].join("::");
|
|
9164
|
+
}
|
|
7915
9165
|
|
|
7916
9166
|
function groupRunnerRoutesBySchedulingTarget(routes) {
|
|
7917
9167
|
const groups = [];
|
|
@@ -7945,13 +9195,14 @@ function normalizeBotRole(rawValue) {
|
|
|
7945
9195
|
return "";
|
|
7946
9196
|
}
|
|
7947
9197
|
|
|
7948
|
-
function normalizeRunnerRoute(rawRoute, overrides = {}) {
|
|
7949
|
-
const route = {
|
|
7950
|
-
...safeObject(rawRoute),
|
|
7951
|
-
...safeObject(overrides),
|
|
7952
|
-
};
|
|
7953
|
-
const
|
|
7954
|
-
const
|
|
9198
|
+
function normalizeRunnerRoute(rawRoute, overrides = {}) {
|
|
9199
|
+
const route = {
|
|
9200
|
+
...safeObject(rawRoute),
|
|
9201
|
+
...safeObject(overrides),
|
|
9202
|
+
};
|
|
9203
|
+
const routeKind = normalizeRunnerRouteKind(route.route_kind ?? route.routeKind);
|
|
9204
|
+
const projectID = String(route.project_id || route.projectID || "").trim();
|
|
9205
|
+
const provider = normalizeBotProvider(route.provider);
|
|
7955
9206
|
const role = normalizeBotRole(route.role || route.bot_role || route.botRole);
|
|
7956
9207
|
const roleProfile = normalizeRunnerRoleProfileName(route.role_profile || route.roleProfile || route.execution_profile || route.executionProfile);
|
|
7957
9208
|
const name = String(route.name || "").trim();
|
|
@@ -7969,11 +9220,12 @@ function normalizeRunnerRoute(rawRoute, overrides = {}) {
|
|
|
7969
9220
|
route.dry_run_delivery ?? route.dryRunDelivery ?? process.env.METHEUS_BOT_DELIVERY_DRY_RUN,
|
|
7970
9221
|
false,
|
|
7971
9222
|
);
|
|
7972
|
-
return {
|
|
7973
|
-
name,
|
|
7974
|
-
enabled: boolFromRaw(route.enabled, true),
|
|
7975
|
-
|
|
7976
|
-
|
|
9223
|
+
return {
|
|
9224
|
+
name,
|
|
9225
|
+
enabled: boolFromRaw(route.enabled, true),
|
|
9226
|
+
routeKind,
|
|
9227
|
+
projectID,
|
|
9228
|
+
provider,
|
|
7977
9229
|
role,
|
|
7978
9230
|
roleProfile,
|
|
7979
9231
|
botName,
|
|
@@ -7993,17 +9245,18 @@ function normalizeRunnerRoute(rawRoute, overrides = {}) {
|
|
|
7993
9245
|
};
|
|
7994
9246
|
}
|
|
7995
9247
|
|
|
7996
|
-
function runnerRouteKey(route) {
|
|
7997
|
-
const normalized = normalizeRunnerRoute(route);
|
|
7998
|
-
return [
|
|
7999
|
-
normalized.name || "-",
|
|
8000
|
-
normalized.projectID || "-",
|
|
8001
|
-
normalized.provider || "-",
|
|
8002
|
-
normalized.
|
|
8003
|
-
normalized.
|
|
8004
|
-
normalized.
|
|
8005
|
-
|
|
8006
|
-
|
|
9248
|
+
function runnerRouteKey(route) {
|
|
9249
|
+
const normalized = normalizeRunnerRoute(route);
|
|
9250
|
+
return [
|
|
9251
|
+
normalized.name || "-",
|
|
9252
|
+
normalized.projectID || "-",
|
|
9253
|
+
normalized.provider || "-",
|
|
9254
|
+
normalized.routeKind || "room",
|
|
9255
|
+
normalized.role || "-",
|
|
9256
|
+
normalized.botID || normalized.botName || "-",
|
|
9257
|
+
buildRunnerRouteTargetIdentity(normalized),
|
|
9258
|
+
].join("::");
|
|
9259
|
+
}
|
|
8007
9260
|
|
|
8008
9261
|
function validateRunnerRoute(route, { requireCommand = true, config = null } = {}) {
|
|
8009
9262
|
const errors = [];
|
|
@@ -8015,12 +9268,15 @@ function validateRunnerRoute(route, { requireCommand = true, config = null } = {
|
|
|
8015
9268
|
if (!route.provider) {
|
|
8016
9269
|
errors.push("provider is required");
|
|
8017
9270
|
}
|
|
8018
|
-
if (!route.role) {
|
|
8019
|
-
errors.push("role must be one of: monitor, review, worker, approval");
|
|
8020
|
-
}
|
|
8021
|
-
if (
|
|
8022
|
-
errors.push("
|
|
8023
|
-
}
|
|
9271
|
+
if (!route.role) {
|
|
9272
|
+
errors.push("role must be one of: monitor, review, worker, approval");
|
|
9273
|
+
}
|
|
9274
|
+
if (route.routeKind === "room" && !route.destinationID && !route.destinationLabel) {
|
|
9275
|
+
errors.push("destination_id or destination_label is required for room routes");
|
|
9276
|
+
}
|
|
9277
|
+
if (requireCommand && !route.command) {
|
|
9278
|
+
errors.push("command is required");
|
|
9279
|
+
}
|
|
8024
9280
|
const configuredRoleProfiles = safeObject(normalizedConfig.roleProfiles);
|
|
8025
9281
|
const resolvedRoleProfileName = normalizeRunnerRoleProfileName(route.roleProfile || route.role);
|
|
8026
9282
|
if (resolvedRoleProfileName && !configuredRoleProfiles[resolvedRoleProfileName] && !legacyCommandAvailable) {
|
|
@@ -8081,10 +9337,10 @@ function resolveConfiguredRunnerRouteServerBotName(route, telegramEntries = [])
|
|
|
8081
9337
|
return String(matchedTelegramEntry?.serverBotName || "").trim();
|
|
8082
9338
|
}
|
|
8083
9339
|
|
|
8084
|
-
function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags, options = {}) {
|
|
8085
|
-
const candidate = normalizeRunnerRoute(route);
|
|
8086
|
-
const candidateBotName = resolveConfiguredRunnerRouteServerBotName(candidate, options.telegramEntries);
|
|
8087
|
-
if (hasRunnerFlag(flags, "route-name") && !matchesRunnerRouteText(candidate.name, flags["route-name"])) {
|
|
9340
|
+
function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags, options = {}) {
|
|
9341
|
+
const candidate = normalizeRunnerRoute(route);
|
|
9342
|
+
const candidateBotName = resolveConfiguredRunnerRouteServerBotName(candidate, options.telegramEntries);
|
|
9343
|
+
if (hasRunnerFlag(flags, "route-name") && !matchesRunnerRouteText(candidate.name, flags["route-name"])) {
|
|
8088
9344
|
return false;
|
|
8089
9345
|
}
|
|
8090
9346
|
if (inlineRoute.projectID && candidate.projectID !== inlineRoute.projectID) {
|
|
@@ -8093,12 +9349,15 @@ function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags,
|
|
|
8093
9349
|
if (inlineRoute.provider && candidate.provider !== inlineRoute.provider) {
|
|
8094
9350
|
return false;
|
|
8095
9351
|
}
|
|
8096
|
-
if (inlineRoute.role && candidate.role !== inlineRoute.role) {
|
|
8097
|
-
return false;
|
|
8098
|
-
}
|
|
8099
|
-
if (hasRunnerFlag(flags, "
|
|
8100
|
-
return false;
|
|
8101
|
-
}
|
|
9352
|
+
if (inlineRoute.role && candidate.role !== inlineRoute.role) {
|
|
9353
|
+
return false;
|
|
9354
|
+
}
|
|
9355
|
+
if (hasRunnerFlag(flags, "route-kind") && candidate.routeKind !== inlineRoute.routeKind) {
|
|
9356
|
+
return false;
|
|
9357
|
+
}
|
|
9358
|
+
if (hasRunnerFlag(flags, "role-profile") && candidate.roleProfile !== inlineRoute.roleProfile) {
|
|
9359
|
+
return false;
|
|
9360
|
+
}
|
|
8102
9361
|
if (inlineRoute.botID && candidate.botID !== inlineRoute.botID) {
|
|
8103
9362
|
return false;
|
|
8104
9363
|
}
|
|
@@ -8120,8 +9379,8 @@ function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags,
|
|
|
8120
9379
|
return true;
|
|
8121
9380
|
}
|
|
8122
9381
|
|
|
8123
|
-
function applyRunnerRouteFlagOverrides(route, flags) {
|
|
8124
|
-
const merged = normalizeRunnerRoute(route);
|
|
9382
|
+
function applyRunnerRouteFlagOverrides(route, flags) {
|
|
9383
|
+
const merged = normalizeRunnerRoute(route);
|
|
8125
9384
|
if (hasRunnerFlag(flags, "route-name")) {
|
|
8126
9385
|
merged.name = String(flags["route-name"] || "").trim();
|
|
8127
9386
|
}
|
|
@@ -8134,12 +9393,15 @@ function applyRunnerRouteFlagOverrides(route, flags) {
|
|
|
8134
9393
|
if (hasRunnerFlag(flags, "role")) {
|
|
8135
9394
|
merged.role = normalizeBotRole(flags.role);
|
|
8136
9395
|
}
|
|
8137
|
-
if (hasRunnerFlag(flags, "role-profile")) {
|
|
8138
|
-
merged.roleProfile = normalizeRunnerRoleProfileName(flags["role-profile"]);
|
|
8139
|
-
}
|
|
8140
|
-
if (hasRunnerFlag(flags, "
|
|
8141
|
-
merged.
|
|
8142
|
-
}
|
|
9396
|
+
if (hasRunnerFlag(flags, "role-profile")) {
|
|
9397
|
+
merged.roleProfile = normalizeRunnerRoleProfileName(flags["role-profile"]);
|
|
9398
|
+
}
|
|
9399
|
+
if (hasRunnerFlag(flags, "route-kind")) {
|
|
9400
|
+
merged.routeKind = normalizeRunnerRouteKind(flags["route-kind"], merged.routeKind || "room");
|
|
9401
|
+
}
|
|
9402
|
+
if (hasRunnerFlag(flags, "bot-name")) {
|
|
9403
|
+
merged.botName = String(flags["bot-name"] || "").trim();
|
|
9404
|
+
}
|
|
8143
9405
|
if (hasRunnerFlag(flags, "bot-id")) {
|
|
8144
9406
|
merged.botID = String(flags["bot-id"] || "").trim();
|
|
8145
9407
|
}
|
|
@@ -8225,12 +9487,13 @@ function applyRunnerRouteFlagOverrides(route, flags) {
|
|
|
8225
9487
|
return merged;
|
|
8226
9488
|
}
|
|
8227
9489
|
|
|
8228
|
-
function resolveRunnerRoutes(flags, mode) {
|
|
8229
|
-
const inlineRoute = normalizeRunnerRoute({
|
|
8230
|
-
name: flags["route-name"],
|
|
8231
|
-
enabled: true,
|
|
8232
|
-
|
|
8233
|
-
|
|
9490
|
+
function resolveRunnerRoutes(flags, mode) {
|
|
9491
|
+
const inlineRoute = normalizeRunnerRoute({
|
|
9492
|
+
name: flags["route-name"],
|
|
9493
|
+
enabled: true,
|
|
9494
|
+
...(hasRunnerFlag(flags, "route-kind") ? { route_kind: flags["route-kind"] } : {}),
|
|
9495
|
+
project_id: flags["project-id"],
|
|
9496
|
+
provider: flags.provider,
|
|
8234
9497
|
role: flags.role,
|
|
8235
9498
|
role_profile: flags["role-profile"],
|
|
8236
9499
|
bot_name: flags["bot-name"],
|
|
@@ -8340,13 +9603,29 @@ function resolveRunnerRoutes(flags, mode) {
|
|
|
8340
9603
|
);
|
|
8341
9604
|
}
|
|
8342
9605
|
|
|
8343
|
-
async function listProjectChatDestinations(params) {
|
|
8344
|
-
return listProjectChatDestinationsImpl(params, buildRunnerDataDeps());
|
|
8345
|
-
}
|
|
8346
|
-
|
|
8347
|
-
async function
|
|
8348
|
-
return
|
|
8349
|
-
}
|
|
9606
|
+
async function listProjectChatDestinations(params) {
|
|
9607
|
+
return listProjectChatDestinationsImpl(params, buildRunnerDataDeps());
|
|
9608
|
+
}
|
|
9609
|
+
|
|
9610
|
+
async function getProjectRunnerSettings(params) {
|
|
9611
|
+
return getProjectRunnerSettingsImpl(params, buildRunnerDataDeps());
|
|
9612
|
+
}
|
|
9613
|
+
|
|
9614
|
+
async function listProjectPrivateChats(params) {
|
|
9615
|
+
return listProjectPrivateChatsImpl(params, buildRunnerDataDeps());
|
|
9616
|
+
}
|
|
9617
|
+
|
|
9618
|
+
async function createProjectPrivateChat(params) {
|
|
9619
|
+
return createProjectPrivateChatImpl(params, buildRunnerDataDeps());
|
|
9620
|
+
}
|
|
9621
|
+
|
|
9622
|
+
async function updateProjectPrivateChat(params) {
|
|
9623
|
+
return updateProjectPrivateChatImpl(params, buildRunnerDataDeps());
|
|
9624
|
+
}
|
|
9625
|
+
|
|
9626
|
+
async function listProjectContextItems(params) {
|
|
9627
|
+
return listProjectContextItemsImpl(params, buildRunnerDataDeps());
|
|
9628
|
+
}
|
|
8350
9629
|
|
|
8351
9630
|
async function listProjectRunnerRequests(params) {
|
|
8352
9631
|
return listProjectRunnerRequestsImpl(params, buildRunnerDataDeps());
|
|
@@ -8425,7 +9704,7 @@ function buildTelegramArchiveStructuredPayload(normalized) {
|
|
|
8425
9704
|
occurredAt: normalized?.occurredAt,
|
|
8426
9705
|
messageThreadID: normalized?.messageThreadID,
|
|
8427
9706
|
replyToMessageID: normalized?.replyToMessageID,
|
|
8428
|
-
body: normalized?.text,
|
|
9707
|
+
body: normalized?.body || normalized?.text,
|
|
8429
9708
|
}),
|
|
8430
9709
|
sourceOrigin: String(normalized?.archiveSourceOrigin || normalized?.sourceOrigin || "").trim().toLowerCase(),
|
|
8431
9710
|
sourceRouteKey: String(normalized?.archiveSourceRouteKey || normalized?.sourceRouteKey || "").trim(),
|
|
@@ -8434,7 +9713,11 @@ function buildTelegramArchiveStructuredPayload(normalized) {
|
|
|
8434
9713
|
replyToSender: String(normalized?.replyToFromName || "").trim(),
|
|
8435
9714
|
replyToUsername: String(normalized?.replyToFromUsername || "").trim(),
|
|
8436
9715
|
replyToSenderIsBot: normalized?.replyToFromIsBot === true,
|
|
8437
|
-
body: String(normalized?.text || "").trim(),
|
|
9716
|
+
body: String(normalized?.body || normalized?.text || "").trim(),
|
|
9717
|
+
caption: String(normalized?.caption || "").trim(),
|
|
9718
|
+
attachments: normalizeTelegramMessageAttachments(normalized?.attachments),
|
|
9719
|
+
derivedText: String(normalized?.derivedText || normalized?.derived_text || "").trim(),
|
|
9720
|
+
derivedTextSource: String(normalized?.derivedTextSource || normalized?.derived_text_source || "").trim().toLowerCase(),
|
|
8438
9721
|
};
|
|
8439
9722
|
return payload;
|
|
8440
9723
|
}
|
|
@@ -8578,6 +9861,12 @@ function parseArchivedChatComment(rawBody) {
|
|
|
8578
9861
|
replyToSenderIsBot: payloadHas("replyToSenderIsBot")
|
|
8579
9862
|
? boolFromRaw(structuredPayload.replyToSenderIsBot, false)
|
|
8580
9863
|
: boolFromRaw(metadata.reply_to_sender_is_bot, false),
|
|
9864
|
+
caption: String(safeObject(structuredPayload).caption || metadata.caption || "").trim(),
|
|
9865
|
+
attachments: normalizeTelegramMessageAttachments(
|
|
9866
|
+
safeObject(structuredPayload).attachments,
|
|
9867
|
+
),
|
|
9868
|
+
derivedText: String(safeObject(structuredPayload).derivedText || metadata.derived_text || "").trim(),
|
|
9869
|
+
derivedTextSource: String(safeObject(structuredPayload).derivedTextSource || metadata.derived_text_source || "").trim().toLowerCase(),
|
|
8581
9870
|
botID: firstNonEmptyString([safeObject(structuredPayload).botID, metadata.bot_id]),
|
|
8582
9871
|
botName: firstNonEmptyString([safeObject(structuredPayload).botName, metadata.bot_name]),
|
|
8583
9872
|
botUsername: normalizeTelegramMentionUsername(
|
|
@@ -8824,9 +10113,107 @@ function toISOStringFromUnix(rawValue) {
|
|
|
8824
10113
|
return new Date(numeric * 1000).toISOString();
|
|
8825
10114
|
}
|
|
8826
10115
|
|
|
8827
|
-
function collectTelegramUpdateText(message) {
|
|
8828
|
-
return firstNonEmptyString([message?.text, message?.caption]);
|
|
8829
|
-
}
|
|
10116
|
+
function collectTelegramUpdateText(message) {
|
|
10117
|
+
return firstNonEmptyString([message?.text, message?.caption]);
|
|
10118
|
+
}
|
|
10119
|
+
|
|
10120
|
+
function normalizeTelegramAttachmentKind(rawValue) {
|
|
10121
|
+
const value = String(rawValue || "").trim().toLowerCase();
|
|
10122
|
+
if (!value) return "";
|
|
10123
|
+
if (["image", "picture"].includes(value)) return "photo";
|
|
10124
|
+
if (["voice_note", "voice-message", "voice_message"].includes(value)) return "voice";
|
|
10125
|
+
if (["file", "attachment"].includes(value)) return "document";
|
|
10126
|
+
return value;
|
|
10127
|
+
}
|
|
10128
|
+
|
|
10129
|
+
function normalizeTelegramAttachmentMetadata(kind, rawAttachment, extra = {}) {
|
|
10130
|
+
const attachment = safeObject(rawAttachment);
|
|
10131
|
+
const normalizedKind = normalizeTelegramAttachmentKind(kind);
|
|
10132
|
+
const fileID = String(attachment.file_id || "").trim();
|
|
10133
|
+
if (!normalizedKind || !fileID) {
|
|
10134
|
+
return null;
|
|
10135
|
+
}
|
|
10136
|
+
const normalized = {
|
|
10137
|
+
kind: normalizedKind,
|
|
10138
|
+
file_id: fileID,
|
|
10139
|
+
};
|
|
10140
|
+
const fileUniqueID = String(attachment.file_unique_id || "").trim();
|
|
10141
|
+
if (fileUniqueID) {
|
|
10142
|
+
normalized.file_unique_id = fileUniqueID;
|
|
10143
|
+
}
|
|
10144
|
+
const mimeType = String(attachment.mime_type || "").trim().toLowerCase();
|
|
10145
|
+
if (mimeType) {
|
|
10146
|
+
normalized.mime_type = mimeType;
|
|
10147
|
+
}
|
|
10148
|
+
const caption = String(extra.caption || "").trim();
|
|
10149
|
+
if (caption) {
|
|
10150
|
+
normalized.caption = caption;
|
|
10151
|
+
}
|
|
10152
|
+
const mediaGroupID = String(extra.media_group_id || attachment.media_group_id || "").trim();
|
|
10153
|
+
if (mediaGroupID) {
|
|
10154
|
+
normalized.media_group_id = mediaGroupID;
|
|
10155
|
+
}
|
|
10156
|
+
const fileName = String(attachment.file_name || "").trim();
|
|
10157
|
+
if (fileName) {
|
|
10158
|
+
normalized.file_name = fileName;
|
|
10159
|
+
}
|
|
10160
|
+
for (const [sourceKey, targetKey] of [
|
|
10161
|
+
["duration", "duration"],
|
|
10162
|
+
["file_size", "file_size"],
|
|
10163
|
+
["width", "width"],
|
|
10164
|
+
["height", "height"],
|
|
10165
|
+
]) {
|
|
10166
|
+
const parsed = intFromRawAllowZero(attachment[sourceKey], 0);
|
|
10167
|
+
if (parsed > 0) {
|
|
10168
|
+
normalized[targetKey] = parsed;
|
|
10169
|
+
}
|
|
10170
|
+
}
|
|
10171
|
+
return normalized;
|
|
10172
|
+
}
|
|
10173
|
+
|
|
10174
|
+
function collectTelegramUpdateAttachments(message) {
|
|
10175
|
+
const safeMessage = safeObject(message);
|
|
10176
|
+
const normalizedAttachments = [];
|
|
10177
|
+
const caption = String(safeMessage.caption || "").trim();
|
|
10178
|
+
const mediaGroupID = String(safeMessage.media_group_id || "").trim();
|
|
10179
|
+
const photoCandidates = ensureArray(safeMessage.photo)
|
|
10180
|
+
.map((item) => safeObject(item))
|
|
10181
|
+
.filter((item) => String(item.file_id || "").trim());
|
|
10182
|
+
if (photoCandidates.length > 0) {
|
|
10183
|
+
const selectedPhoto = photoCandidates
|
|
10184
|
+
.slice()
|
|
10185
|
+
.sort((left, right) => {
|
|
10186
|
+
const leftScore = intFromRawAllowZero(left.file_size, 0)
|
|
10187
|
+
|| (intFromRawAllowZero(left.width, 0) * intFromRawAllowZero(left.height, 0));
|
|
10188
|
+
const rightScore = intFromRawAllowZero(right.file_size, 0)
|
|
10189
|
+
|| (intFromRawAllowZero(right.width, 0) * intFromRawAllowZero(right.height, 0));
|
|
10190
|
+
return leftScore - rightScore;
|
|
10191
|
+
})
|
|
10192
|
+
.pop();
|
|
10193
|
+
const normalizedPhoto = normalizeTelegramAttachmentMetadata("photo", selectedPhoto, {
|
|
10194
|
+
caption,
|
|
10195
|
+
media_group_id: mediaGroupID,
|
|
10196
|
+
});
|
|
10197
|
+
if (normalizedPhoto) {
|
|
10198
|
+
normalizedAttachments.push(normalizedPhoto);
|
|
10199
|
+
}
|
|
10200
|
+
}
|
|
10201
|
+
for (const [kind, rawValue] of [
|
|
10202
|
+
["voice", safeMessage.voice],
|
|
10203
|
+
["audio", safeMessage.audio],
|
|
10204
|
+
["document", safeMessage.document],
|
|
10205
|
+
["video", safeMessage.video],
|
|
10206
|
+
]) {
|
|
10207
|
+
const normalized = normalizeTelegramAttachmentMetadata(kind, rawValue, {
|
|
10208
|
+
caption,
|
|
10209
|
+
media_group_id: mediaGroupID,
|
|
10210
|
+
});
|
|
10211
|
+
if (normalized) {
|
|
10212
|
+
normalizedAttachments.push(normalized);
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
return normalizeTelegramMessageAttachments(normalizedAttachments);
|
|
10216
|
+
}
|
|
8830
10217
|
|
|
8831
10218
|
function normalizeTelegramMentionUsername(rawValue) {
|
|
8832
10219
|
return String(rawValue || "").trim().replace(/^@+/, "").toLowerCase();
|
|
@@ -8861,23 +10248,29 @@ function extractTelegramMentionUsernames(text, entities) {
|
|
|
8861
10248
|
return Array.from(set);
|
|
8862
10249
|
}
|
|
8863
10250
|
|
|
8864
|
-
function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
10251
|
+
function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
8865
10252
|
const update = safeObject(rawUpdate);
|
|
8866
10253
|
const message = safeObject(update.message || update.edited_message);
|
|
8867
10254
|
if (!message || Object.keys(message).length === 0) {
|
|
8868
10255
|
return null;
|
|
8869
10256
|
}
|
|
8870
|
-
const chat = safeObject(message.chat);
|
|
8871
|
-
const from = safeObject(message.from);
|
|
8872
|
-
const replyTo = safeObject(message.reply_to_message);
|
|
8873
|
-
const replyToFrom = safeObject(replyTo.from);
|
|
8874
|
-
const
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
10257
|
+
const chat = safeObject(message.chat);
|
|
10258
|
+
const from = safeObject(message.from);
|
|
10259
|
+
const replyTo = safeObject(message.reply_to_message);
|
|
10260
|
+
const replyToFrom = safeObject(replyTo.from);
|
|
10261
|
+
const caption = String(message.caption || "").trim();
|
|
10262
|
+
const attachments = collectTelegramUpdateAttachments(message);
|
|
10263
|
+
const text = collectTelegramUpdateText(message);
|
|
10264
|
+
const normalizedBody = firstNonEmptyString([
|
|
10265
|
+
text,
|
|
10266
|
+
buildTelegramMediaPlaceholder(attachments),
|
|
10267
|
+
]);
|
|
10268
|
+
if (!normalizedBody) {
|
|
10269
|
+
return null;
|
|
10270
|
+
}
|
|
10271
|
+
const mentionUsernames = extractTelegramMentionUsernames(
|
|
10272
|
+
text,
|
|
10273
|
+
message.entities || message.caption_entities,
|
|
8881
10274
|
);
|
|
8882
10275
|
return {
|
|
8883
10276
|
eventName: update.edited_message ? "telegram.message.updated" : "telegram.message.created",
|
|
@@ -8887,12 +10280,17 @@ function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
|
8887
10280
|
chatType: String(chat.type || "").trim(),
|
|
8888
10281
|
chatTitle: firstNonEmptyString([chat.title, chat.username, joinTextParts([chat.first_name, chat.last_name]), chat.id]),
|
|
8889
10282
|
fromID: String(from.id || "").trim(),
|
|
8890
|
-
fromName: firstNonEmptyString([joinTextParts([from.first_name, from.last_name]), from.username, from.id]),
|
|
8891
|
-
fromUsername: String(from.username || "").trim(),
|
|
8892
|
-
fromIsBot: Boolean(from.is_bot),
|
|
8893
|
-
mentionUsernames,
|
|
8894
|
-
text,
|
|
8895
|
-
|
|
10283
|
+
fromName: firstNonEmptyString([joinTextParts([from.first_name, from.last_name]), from.username, from.id]),
|
|
10284
|
+
fromUsername: String(from.username || "").trim(),
|
|
10285
|
+
fromIsBot: Boolean(from.is_bot),
|
|
10286
|
+
mentionUsernames,
|
|
10287
|
+
text: normalizedBody,
|
|
10288
|
+
body: normalizedBody,
|
|
10289
|
+
caption,
|
|
10290
|
+
attachments,
|
|
10291
|
+
derivedText: "",
|
|
10292
|
+
derivedTextSource: "",
|
|
10293
|
+
occurredAt: toISOStringFromUnix(message.edit_date || message.date),
|
|
8896
10294
|
messageThreadID: String(message.message_thread_id || "").trim(),
|
|
8897
10295
|
replyToMessageID: intFromRawAllowZero(replyTo.message_id, 0),
|
|
8898
10296
|
replyToFromName: firstNonEmptyString([joinTextParts([replyToFrom.first_name, replyToFrom.last_name]), replyToFrom.username, replyToFrom.id]),
|
|
@@ -8938,6 +10336,7 @@ function formatTelegramInboundArchiveComment(normalized) {
|
|
|
8938
10336
|
const archiveSourceRouteKey = String(normalized.archiveSourceRouteKey || normalized.sourceRouteKey || "").trim();
|
|
8939
10337
|
const archiveSourceBotUsername = String(normalized.archiveSourceBotUsername || normalized.sourceBotUsername || "").trim();
|
|
8940
10338
|
const archivePayload = buildTelegramArchiveStructuredPayload(normalized);
|
|
10339
|
+
const normalizedAttachments = normalizeTelegramMessageAttachments(normalized.attachments);
|
|
8941
10340
|
const headerLines = [
|
|
8942
10341
|
`[Telegram ${normalized.eventName === "telegram.message.updated" ? "edited" : "message"}]`,
|
|
8943
10342
|
`chat_id: ${normalized.chatID || "<missing>"}`,
|
|
@@ -8960,10 +10359,19 @@ function formatTelegramInboundArchiveComment(normalized) {
|
|
|
8960
10359
|
if (normalized.fromUsername) {
|
|
8961
10360
|
headerLines.push(`telegram_username: @${normalized.fromUsername.replace(/^@+/, "")}`);
|
|
8962
10361
|
}
|
|
8963
|
-
if (normalized.mentionUsernames.length > 0) {
|
|
8964
|
-
headerLines.push(`mention_usernames: ${normalized.mentionUsernames.map((item) => `@${item}`).join(", ")}`);
|
|
8965
|
-
}
|
|
8966
|
-
if (
|
|
10362
|
+
if (normalized.mentionUsernames.length > 0) {
|
|
10363
|
+
headerLines.push(`mention_usernames: ${normalized.mentionUsernames.map((item) => `@${item}`).join(", ")}`);
|
|
10364
|
+
}
|
|
10365
|
+
if (normalizedAttachments.length > 0) {
|
|
10366
|
+
headerLines.push(`attachment_kinds: ${normalizedAttachments.map((item) => item.kind).join(", ")}`);
|
|
10367
|
+
}
|
|
10368
|
+
if (normalized.caption) {
|
|
10369
|
+
headerLines.push(`caption: ${String(normalized.caption).trim()}`);
|
|
10370
|
+
}
|
|
10371
|
+
if (normalized.derivedTextSource) {
|
|
10372
|
+
headerLines.push(`derived_text_source: ${String(normalized.derivedTextSource).trim().toLowerCase()}`);
|
|
10373
|
+
}
|
|
10374
|
+
if (normalized.replyToMessageID > 0) {
|
|
8967
10375
|
headerLines.push(`reply_to_message_id: ${normalized.replyToMessageID}`);
|
|
8968
10376
|
headerLines.push(`reply_to_sender_is_bot: ${normalized.replyToFromIsBot ? "true" : "false"}`);
|
|
8969
10377
|
if (normalized.replyToFromName) {
|
|
@@ -8972,12 +10380,12 @@ function formatTelegramInboundArchiveComment(normalized) {
|
|
|
8972
10380
|
if (normalized.replyToFromUsername) {
|
|
8973
10381
|
headerLines.push(`reply_to_telegram_username: @${normalized.replyToFromUsername.replace(/^@+/, "")}`);
|
|
8974
10382
|
}
|
|
8975
|
-
}
|
|
10383
|
+
}
|
|
8976
10384
|
if (intFromRawAllowZero(normalized.messageThreadID, 0) > 0) {
|
|
8977
10385
|
headerLines.push(`message_thread_id: ${intFromRawAllowZero(normalized.messageThreadID, 0)}`);
|
|
8978
10386
|
}
|
|
8979
10387
|
headerLines.push(`archive_payload_json: ${JSON.stringify(archivePayload)}`);
|
|
8980
|
-
return `${headerLines.join("\n")}\n\n${String(normalized.text || "").trim()}`;
|
|
10388
|
+
return `${headerLines.join("\n")}\n\n${String(normalized.body || normalized.text || "").trim()}`;
|
|
8981
10389
|
}
|
|
8982
10390
|
|
|
8983
10391
|
async function postJSONWithAuthHeaders(urlText, timeoutSeconds, token, payload, extraHeaders = {}) {
|
|
@@ -9936,25 +11344,33 @@ function buildRunnerShowActiveExecutionPayload(activeExecutionStateRaw) {
|
|
|
9936
11344
|
};
|
|
9937
11345
|
}
|
|
9938
11346
|
|
|
9939
|
-
function buildRunnerShowResolvedContext({
|
|
9940
|
-
normalizedRoute,
|
|
9941
|
-
diagnostics,
|
|
9942
|
-
envConfig,
|
|
9943
|
-
resolvedServerBotName,
|
|
11347
|
+
function buildRunnerShowResolvedContext({
|
|
11348
|
+
normalizedRoute,
|
|
11349
|
+
diagnostics,
|
|
11350
|
+
envConfig,
|
|
11351
|
+
resolvedServerBotName,
|
|
9944
11352
|
botNameSource,
|
|
9945
11353
|
}) {
|
|
9946
|
-
return {
|
|
9947
|
-
resolved_server_identity: {
|
|
9948
|
-
server_bot_name: resolvedServerBotName,
|
|
9949
|
-
server_bot_name_source: botNameSource,
|
|
9950
|
-
server_bot_id: normalizedRoute.botID || "-",
|
|
9951
|
-
telegram_entry_file: String(envConfig?.entryFilePath || "").trim() || "-",
|
|
9952
|
-
},
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
11354
|
+
return {
|
|
11355
|
+
resolved_server_identity: {
|
|
11356
|
+
server_bot_name: resolvedServerBotName,
|
|
11357
|
+
server_bot_name_source: botNameSource,
|
|
11358
|
+
server_bot_id: normalizedRoute.botID || "-",
|
|
11359
|
+
telegram_entry_file: String(envConfig?.entryFilePath || "").trim() || "-",
|
|
11360
|
+
},
|
|
11361
|
+
route_target: {
|
|
11362
|
+
route_kind: normalizedRoute.routeKind || "room",
|
|
11363
|
+
target_label: describeRunnerRouteTargetLabel(normalizedRoute),
|
|
11364
|
+
},
|
|
11365
|
+
resolved_destination: {
|
|
11366
|
+
destination_label: normalizedRoute.routeKind === "dm"
|
|
11367
|
+
? "Direct Messages"
|
|
11368
|
+
: (String(normalizedRoute.destinationLabel || "").trim() || "-"),
|
|
11369
|
+
destination_id: normalizedRoute.routeKind === "dm"
|
|
11370
|
+
? "-"
|
|
11371
|
+
: (String(normalizedRoute.destinationID || "").trim() || "-"),
|
|
11372
|
+
destination_source: normalizedRoute.routeKind === "dm" ? "route_kind" : "route_config",
|
|
11373
|
+
},
|
|
9958
11374
|
workspace_mapping: {
|
|
9959
11375
|
workspace_dir: diagnostics.workspaceDir || "-",
|
|
9960
11376
|
workspace_source: diagnostics.workspaceSource || "-",
|
|
@@ -10198,8 +11614,8 @@ async function resolveInformationalQueryReply({
|
|
|
10198
11614
|
return lookupOnlyResponse || null;
|
|
10199
11615
|
}
|
|
10200
11616
|
|
|
10201
|
-
function buildRunnerExecutionDeps() {
|
|
10202
|
-
return {
|
|
11617
|
+
function buildRunnerExecutionDeps() {
|
|
11618
|
+
return {
|
|
10203
11619
|
adjudicateRunnerStartupLoopWithAI,
|
|
10204
11620
|
analyzeHumanConversationIntentWithAI,
|
|
10205
11621
|
auditRoleExecutionPlanWithAI,
|
|
@@ -10228,6 +11644,8 @@ function buildRunnerExecutionDeps() {
|
|
|
10228
11644
|
createProjectContextItem,
|
|
10229
11645
|
replaceProjectCtxpackFiles,
|
|
10230
11646
|
resolveInformationalQueryReply,
|
|
11647
|
+
transcribeRunnerTelegramAudioAttachments,
|
|
11648
|
+
analyzeRunnerTelegramImageAttachments,
|
|
10231
11649
|
prepareRunnerSelectedRecordRecoveryContext: (input) => prepareRunnerSelectedRecordRecoveryContextImpl({
|
|
10232
11650
|
...safeObject(input),
|
|
10233
11651
|
deps: buildRunnerRecoveryDeps(),
|
|
@@ -10235,15 +11653,19 @@ function buildRunnerExecutionDeps() {
|
|
|
10235
11653
|
};
|
|
10236
11654
|
}
|
|
10237
11655
|
|
|
10238
|
-
function buildRunnerRuntimeDeps() {
|
|
10239
|
-
return {
|
|
10240
|
-
loadProviderEnvConfig,
|
|
10241
|
-
loadBotRunnerState,
|
|
10242
|
-
saveBotRunnerState,
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
11656
|
+
function buildRunnerRuntimeDeps() {
|
|
11657
|
+
return {
|
|
11658
|
+
loadProviderEnvConfig,
|
|
11659
|
+
loadBotRunnerState,
|
|
11660
|
+
saveBotRunnerState,
|
|
11661
|
+
getProjectRunnerSettings,
|
|
11662
|
+
listProjectPrivateChats,
|
|
11663
|
+
createProjectPrivateChat,
|
|
11664
|
+
updateProjectPrivateChat,
|
|
11665
|
+
getTelegramBotMe,
|
|
11666
|
+
getTelegramWebhookInfo,
|
|
11667
|
+
deleteTelegramWebhook,
|
|
11668
|
+
saveRunnerRouteState,
|
|
10247
11669
|
getTelegramUpdates,
|
|
10248
11670
|
normalizeLocalTelegramUpdate,
|
|
10249
11671
|
listThreadComments,
|
|
@@ -10586,23 +12008,34 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
10586
12008
|
runnerConfig,
|
|
10587
12009
|
buildRunnerExecutionDeps(),
|
|
10588
12010
|
);
|
|
10589
|
-
const
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
|
|
10605
|
-
|
|
12011
|
+
const destination = normalizedRoute.routeKind === "dm"
|
|
12012
|
+
? {
|
|
12013
|
+
id: "",
|
|
12014
|
+
label: `Direct Messages - ${String(bot?.name || bot?.username || normalizedRoute.botName || normalizedRoute.name || "Bot").trim()}`,
|
|
12015
|
+
chatID: `dm:${normalizeTelegramBotUsername(bot?.username || bot?.name || normalizedRoute.botName) || normalizedRoute.botID || normalizedRoute.name}`,
|
|
12016
|
+
provider: normalizedRoute.provider,
|
|
12017
|
+
routeKind: "dm",
|
|
12018
|
+
isDirectMessage: true,
|
|
12019
|
+
}
|
|
12020
|
+
: await (async () => {
|
|
12021
|
+
const destinations = await listProjectChatDestinations({
|
|
12022
|
+
siteBaseURL: runtime.baseURL,
|
|
12023
|
+
projectID: normalizedRoute.projectID,
|
|
12024
|
+
token: runtime.token,
|
|
12025
|
+
timeoutSeconds: runtime.timeoutSeconds,
|
|
12026
|
+
});
|
|
12027
|
+
return selectProjectChatDestination(
|
|
12028
|
+
destinations,
|
|
12029
|
+
{
|
|
12030
|
+
destinationID: normalizedRoute.destinationID,
|
|
12031
|
+
destinationLabel: normalizedRoute.destinationLabel,
|
|
12032
|
+
},
|
|
12033
|
+
normalizedRoute.provider,
|
|
12034
|
+
);
|
|
12035
|
+
})();
|
|
12036
|
+
const archiveThread = await discoverArchiveThreadForDestination({
|
|
12037
|
+
siteBaseURL: runtime.baseURL,
|
|
12038
|
+
projectID: normalizedRoute.projectID,
|
|
10606
12039
|
provider: normalizedRoute.provider,
|
|
10607
12040
|
destination,
|
|
10608
12041
|
token: runtime.token,
|
|
@@ -10699,9 +12132,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
10699
12132
|
});
|
|
10700
12133
|
const followupRequestsForPending = Object.values(requests).filter((entryRaw) => {
|
|
10701
12134
|
const entry = safeObject(entryRaw);
|
|
10702
|
-
const nextExpectedResponders =
|
|
10703
|
-
.map((value) => normalizeTelegramMentionUsername(value))
|
|
10704
|
-
.filter(Boolean);
|
|
12135
|
+
const nextExpectedResponders = runnerRequestPreferredNextExpectedResponders(entry);
|
|
10705
12136
|
if (!nextExpectedResponders.includes(currentBotSelector)) {
|
|
10706
12137
|
return false;
|
|
10707
12138
|
}
|
|
@@ -11291,8 +12722,8 @@ function buildRunnerRouteListRows() {
|
|
|
11291
12722
|
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
11292
12723
|
const telegramState = readTelegramEnvState();
|
|
11293
12724
|
const telegramEntries = ensureArray(telegramState.entries);
|
|
11294
|
-
return ensureArray(config.routes).map((rawRoute, index) => {
|
|
11295
|
-
const route = normalizeRunnerRoute(rawRoute);
|
|
12725
|
+
return ensureArray(config.routes).map((rawRoute, index) => {
|
|
12726
|
+
const route = normalizeRunnerRoute(rawRoute);
|
|
11296
12727
|
const matchedTelegramEntry = route.provider === "telegram"
|
|
11297
12728
|
? telegramEntries.find((entry) => {
|
|
11298
12729
|
const current = safeObject(entry);
|
|
@@ -11312,24 +12743,25 @@ function buildRunnerRouteListRows() {
|
|
|
11312
12743
|
matchedTelegramEntry?.serverBotName,
|
|
11313
12744
|
"-",
|
|
11314
12745
|
]);
|
|
11315
|
-
return {
|
|
11316
|
-
index: index + 1,
|
|
11317
|
-
name: route.name || runnerRouteKey(route),
|
|
11318
|
-
logicalSignature: runnerRouteLogicalSignature(route),
|
|
11319
|
-
enabled: route.enabled !== false,
|
|
11320
|
-
provider: route.provider || "-",
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
}
|
|
12746
|
+
return {
|
|
12747
|
+
index: index + 1,
|
|
12748
|
+
name: route.name || runnerRouteKey(route),
|
|
12749
|
+
logicalSignature: runnerRouteLogicalSignature(route),
|
|
12750
|
+
enabled: route.enabled !== false,
|
|
12751
|
+
provider: route.provider || "-",
|
|
12752
|
+
routeKind: route.routeKind || "room",
|
|
12753
|
+
projectID: route.projectID || "-",
|
|
12754
|
+
botName: resolvedBotName,
|
|
12755
|
+
botNameSource,
|
|
12756
|
+
botID: route.botID || "-",
|
|
12757
|
+
role: route.role || "-",
|
|
12758
|
+
roleProfile: route.roleProfile || "-",
|
|
12759
|
+
destinationLabel: describeRunnerRouteTargetLabel(route),
|
|
12760
|
+
pollIntervalMs: route.pollIntervalMs || 0,
|
|
12761
|
+
configFilePath: config.filePath,
|
|
12762
|
+
};
|
|
12763
|
+
});
|
|
12764
|
+
}
|
|
11333
12765
|
|
|
11334
12766
|
function slugifyRunnerRouteSegment(rawValue) {
|
|
11335
12767
|
return String(rawValue || "")
|
|
@@ -11339,12 +12771,13 @@ function slugifyRunnerRouteSegment(rawValue) {
|
|
|
11339
12771
|
.replace(/^-+|-+$/g, "");
|
|
11340
12772
|
}
|
|
11341
12773
|
|
|
11342
|
-
function buildRunnerRouteNameSuggestion({ provider = "", role = "", botName = "" }, existingNames = []) {
|
|
11343
|
-
const segments = [
|
|
11344
|
-
slugifyRunnerRouteSegment(provider),
|
|
11345
|
-
slugifyRunnerRouteSegment(role),
|
|
11346
|
-
slugifyRunnerRouteSegment(botName),
|
|
11347
|
-
|
|
12774
|
+
function buildRunnerRouteNameSuggestion({ provider = "", role = "", botName = "", routeKind = "room" }, existingNames = []) {
|
|
12775
|
+
const segments = [
|
|
12776
|
+
slugifyRunnerRouteSegment(provider),
|
|
12777
|
+
slugifyRunnerRouteSegment(role),
|
|
12778
|
+
slugifyRunnerRouteSegment(botName),
|
|
12779
|
+
routeKind === "dm" ? "dm" : "",
|
|
12780
|
+
].filter(Boolean);
|
|
11348
12781
|
const base = segments.join("-") || "runner-route";
|
|
11349
12782
|
const existing = new Set(ensureArray(existingNames).map((name) => String(name || "").trim().toLowerCase()).filter(Boolean));
|
|
11350
12783
|
if (!existing.has(base)) {
|
|
@@ -11395,15 +12828,15 @@ function removeRunnerRouteFromConfig(config, routeName) {
|
|
|
11395
12828
|
};
|
|
11396
12829
|
}
|
|
11397
12830
|
|
|
11398
|
-
function formatRunnerRouteChoiceLabel(route, telegramEntries = []) {
|
|
11399
|
-
const normalizedRoute = normalizeRunnerRoute(route);
|
|
11400
|
-
const resolvedName = resolveConfiguredRunnerRouteServerBotName(normalizedRoute, telegramEntries);
|
|
11401
|
-
const routeName = normalizedRoute.name || runnerRouteKey(normalizedRoute);
|
|
11402
|
-
const role = normalizedRoute.role || "-";
|
|
11403
|
-
const botName = resolvedName || normalizedRoute.botName || normalizedRoute.botID || "-";
|
|
11404
|
-
const
|
|
11405
|
-
return `${routeName}${normalizedRoute.enabled ? "" : " [disabled]"} - ${normalizedRoute.provider || "-"} | ${role} | ${botName} | ${
|
|
11406
|
-
}
|
|
12831
|
+
function formatRunnerRouteChoiceLabel(route, telegramEntries = []) {
|
|
12832
|
+
const normalizedRoute = normalizeRunnerRoute(route);
|
|
12833
|
+
const resolvedName = resolveConfiguredRunnerRouteServerBotName(normalizedRoute, telegramEntries);
|
|
12834
|
+
const routeName = normalizedRoute.name || runnerRouteKey(normalizedRoute);
|
|
12835
|
+
const role = normalizedRoute.role || "-";
|
|
12836
|
+
const botName = resolvedName || normalizedRoute.botName || normalizedRoute.botID || "-";
|
|
12837
|
+
const target = describeRunnerRouteTargetLabel(normalizedRoute);
|
|
12838
|
+
return `${routeName}${normalizedRoute.enabled ? "" : " [disabled]"} - ${normalizedRoute.provider || "-"} | ${normalizedRoute.routeKind || "room"} | ${role} | ${botName} | ${target}`;
|
|
12839
|
+
}
|
|
11407
12840
|
|
|
11408
12841
|
async function selectRunnerManagementRoute(ui, flags, { title = "Select runner route" } = {}) {
|
|
11409
12842
|
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
@@ -11547,15 +12980,16 @@ async function selectRunnerServerBotProfile(ui, { provider, role, flags, current
|
|
|
11547
12980
|
token,
|
|
11548
12981
|
timeoutSeconds,
|
|
11549
12982
|
});
|
|
11550
|
-
const candidates = ensureArray(bots)
|
|
11551
|
-
.filter((bot) => bot.isActive && bot.provider === provider && bot.role === role);
|
|
12983
|
+
const candidates = ensureArray(bots)
|
|
12984
|
+
.filter((bot) => bot.isActive && bot.provider === provider && bot.role === role);
|
|
11552
12985
|
if (!candidates.length) {
|
|
11553
12986
|
throw new Error(`No active ${providerEnvConfig(provider).label} server bot exists for role "${role}".`);
|
|
11554
12987
|
}
|
|
11555
|
-
const resolveChoice = (bot) => ({
|
|
11556
|
-
name: String(bot.name || "").trim(),
|
|
11557
|
-
id: String(bot.id || "").trim(),
|
|
11558
|
-
|
|
12988
|
+
const resolveChoice = (bot) => ({
|
|
12989
|
+
name: String(bot.name || "").trim(),
|
|
12990
|
+
id: String(bot.id || "").trim(),
|
|
12991
|
+
username: normalizeTelegramBotUsername(bot.username),
|
|
12992
|
+
});
|
|
11559
12993
|
if (flaggedBotID) {
|
|
11560
12994
|
const matched = candidates.find((bot) => bot.id === flaggedBotID);
|
|
11561
12995
|
if (!matched) {
|
|
@@ -11597,18 +13031,28 @@ async function selectRunnerServerBotProfile(ui, { provider, role, flags, current
|
|
|
11597
13031
|
return resolveChoice(matched);
|
|
11598
13032
|
}
|
|
11599
13033
|
|
|
11600
|
-
async function selectRunnerDestination(ui, {
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
13034
|
+
async function selectRunnerDestination(ui, {
|
|
13035
|
+
projectID,
|
|
13036
|
+
provider,
|
|
13037
|
+
flags,
|
|
13038
|
+
currentDestinationID = "",
|
|
13039
|
+
currentDestinationLabel = "",
|
|
13040
|
+
preloadedDestinations = null,
|
|
13041
|
+
}) {
|
|
13042
|
+
let destinations = ensureArray(preloadedDestinations);
|
|
13043
|
+
if (!destinations.length) {
|
|
13044
|
+
const timeoutSeconds = intFromRaw(flags["timeout-seconds"], 15) || 15;
|
|
13045
|
+
const { siteBaseURL, token } = await resolveRunnerCommandToken(flags["base-url"], timeoutSeconds);
|
|
13046
|
+
destinations = await listProjectChatDestinations({
|
|
13047
|
+
siteBaseURL,
|
|
13048
|
+
projectID,
|
|
13049
|
+
token,
|
|
13050
|
+
timeoutSeconds,
|
|
13051
|
+
});
|
|
13052
|
+
}
|
|
13053
|
+
const candidates = ensureArray(destinations)
|
|
13054
|
+
.map((item) => normalizeChatDestination(item))
|
|
13055
|
+
.filter((item) => item.isActive && item.provider === provider);
|
|
11612
13056
|
const flaggedID = String(flags["destination-id"] || "").trim();
|
|
11613
13057
|
const flaggedLabel = String(flags["destination-label"] || "").trim();
|
|
11614
13058
|
if (flaggedID) {
|
|
@@ -11652,15 +13096,98 @@ async function selectRunnerDestination(ui, { projectID, provider, flags, current
|
|
|
11652
13096
|
if (!matched) {
|
|
11653
13097
|
throw new Error("selected destination was not found");
|
|
11654
13098
|
}
|
|
11655
|
-
return { id: matched.id, label: matched.label || matched.id };
|
|
11656
|
-
}
|
|
13099
|
+
return { id: matched.id, label: matched.label || matched.id };
|
|
13100
|
+
}
|
|
13101
|
+
|
|
13102
|
+
function isRunnerDirectMessageBotAllowed(projectSettingsRaw, botUsername) {
|
|
13103
|
+
const projectSettings = safeObject(projectSettingsRaw);
|
|
13104
|
+
const normalizedBotUsername = normalizeTelegramBotUsername(botUsername);
|
|
13105
|
+
if (!(projectSettings.directMessagesEnabled === true) || !normalizedBotUsername) {
|
|
13106
|
+
return false;
|
|
13107
|
+
}
|
|
13108
|
+
return ensureArray(projectSettings.directMessageBotUsernames)
|
|
13109
|
+
.map((value) => normalizeTelegramBotUsername(value))
|
|
13110
|
+
.filter(Boolean)
|
|
13111
|
+
.includes(normalizedBotUsername);
|
|
13112
|
+
}
|
|
13113
|
+
|
|
13114
|
+
async function resolveRunnerRouteTargetSelection(ui, {
|
|
13115
|
+
projectID,
|
|
13116
|
+
provider,
|
|
13117
|
+
flags,
|
|
13118
|
+
botSelection,
|
|
13119
|
+
}) {
|
|
13120
|
+
const timeoutSeconds = intFromRaw(flags["timeout-seconds"], 15) || 15;
|
|
13121
|
+
const { siteBaseURL, token } = await resolveRunnerCommandToken(flags["base-url"], timeoutSeconds);
|
|
13122
|
+
const [projectSettings, destinations] = await Promise.all([
|
|
13123
|
+
getProjectRunnerSettings({
|
|
13124
|
+
siteBaseURL,
|
|
13125
|
+
projectID,
|
|
13126
|
+
token,
|
|
13127
|
+
timeoutSeconds,
|
|
13128
|
+
}),
|
|
13129
|
+
listProjectChatDestinations({
|
|
13130
|
+
siteBaseURL,
|
|
13131
|
+
projectID,
|
|
13132
|
+
token,
|
|
13133
|
+
timeoutSeconds,
|
|
13134
|
+
}),
|
|
13135
|
+
]);
|
|
13136
|
+
const activeDestinations = ensureArray(destinations)
|
|
13137
|
+
.map((item) => normalizeChatDestination(item))
|
|
13138
|
+
.filter((item) => item.isActive && item.provider === provider);
|
|
13139
|
+
const requestedRouteKind = hasRunnerFlag(flags, "route-kind")
|
|
13140
|
+
? normalizeRunnerRouteKind(flags["route-kind"])
|
|
13141
|
+
: "";
|
|
13142
|
+
const dmAllowed = isRunnerDirectMessageBotAllowed(projectSettings, botSelection?.username);
|
|
13143
|
+
const routeKind = requestedRouteKind || (!activeDestinations.length && dmAllowed ? "dm" : "room");
|
|
13144
|
+
if (routeKind === "dm") {
|
|
13145
|
+
if (!dmAllowed) {
|
|
13146
|
+
const allowedBots = ensureArray(projectSettings.directMessageBotUsernames)
|
|
13147
|
+
.map((value) => `@${normalizeTelegramBotUsername(value)}`)
|
|
13148
|
+
.filter((value) => value !== "@")
|
|
13149
|
+
.join(", ");
|
|
13150
|
+
throw new Error(
|
|
13151
|
+
projectSettings.directMessagesEnabled === true
|
|
13152
|
+
? `Bot ${String(botSelection?.username || botSelection?.name || botSelection?.id || "").trim() || "-"} is not enabled for project direct messages${allowedBots ? ` (allowed: ${allowedBots})` : ""}.`
|
|
13153
|
+
: `Project ${projectID} does not have 1:1 direct messages enabled.`,
|
|
13154
|
+
);
|
|
13155
|
+
}
|
|
13156
|
+
const envConfig = loadProviderEnvConfig(provider, {
|
|
13157
|
+
serverBotID: botSelection?.id,
|
|
13158
|
+
botName: botSelection?.name,
|
|
13159
|
+
botUsername: botSelection?.username,
|
|
13160
|
+
});
|
|
13161
|
+
if (!envConfig.ok) {
|
|
13162
|
+
throw new Error(`Direct message route requires a local ${providerEnvConfig(provider).label} bot binding for ${String(botSelection?.name || botSelection?.username || botSelection?.id || "").trim() || "-"}. ${String(envConfig.error || "").trim()}`);
|
|
13163
|
+
}
|
|
13164
|
+
return {
|
|
13165
|
+
routeKind,
|
|
13166
|
+
projectSettings,
|
|
13167
|
+
activeDestinations,
|
|
13168
|
+
destination: { id: "", label: "" },
|
|
13169
|
+
};
|
|
13170
|
+
}
|
|
13171
|
+
return {
|
|
13172
|
+
routeKind: "room",
|
|
13173
|
+
projectSettings,
|
|
13174
|
+
activeDestinations,
|
|
13175
|
+
destination: await selectRunnerDestination(ui, {
|
|
13176
|
+
projectID,
|
|
13177
|
+
provider,
|
|
13178
|
+
flags,
|
|
13179
|
+
preloadedDestinations: activeDestinations,
|
|
13180
|
+
}),
|
|
13181
|
+
};
|
|
13182
|
+
}
|
|
11657
13183
|
|
|
11658
|
-
function buildRunnerRoutePayload({
|
|
11659
|
-
currentRoute = null,
|
|
11660
|
-
name = "",
|
|
11661
|
-
enabled = true,
|
|
11662
|
-
|
|
11663
|
-
|
|
13184
|
+
function buildRunnerRoutePayload({
|
|
13185
|
+
currentRoute = null,
|
|
13186
|
+
name = "",
|
|
13187
|
+
enabled = true,
|
|
13188
|
+
routeKind = "",
|
|
13189
|
+
projectID = "",
|
|
13190
|
+
provider = "",
|
|
11664
13191
|
role = "",
|
|
11665
13192
|
roleProfile = "",
|
|
11666
13193
|
serverBotName = "",
|
|
@@ -11675,12 +13202,13 @@ function buildRunnerRoutePayload({
|
|
|
11675
13202
|
const archivePolicy = currentRoute
|
|
11676
13203
|
? safeObject(currentRoute.archivePolicy)
|
|
11677
13204
|
: defaultRunnerArchivePolicyForRole(roleProfile || role);
|
|
11678
|
-
return normalizeRunnerRoute({
|
|
11679
|
-
...(currentRoute ? serializeRunnerRoute(currentRoute) : {}),
|
|
11680
|
-
name,
|
|
11681
|
-
enabled,
|
|
11682
|
-
|
|
11683
|
-
|
|
13205
|
+
return normalizeRunnerRoute({
|
|
13206
|
+
...(currentRoute ? serializeRunnerRoute(currentRoute) : {}),
|
|
13207
|
+
name,
|
|
13208
|
+
enabled,
|
|
13209
|
+
route_kind: normalizeRunnerRouteKind(routeKind || currentRoute?.routeKind || currentRoute?.route_kind),
|
|
13210
|
+
project_id: projectID,
|
|
13211
|
+
provider,
|
|
11684
13212
|
role,
|
|
11685
13213
|
role_profile: roleProfile,
|
|
11686
13214
|
server_bot_name: serverBotName,
|
|
@@ -11693,45 +13221,48 @@ function buildRunnerRoutePayload({
|
|
|
11693
13221
|
});
|
|
11694
13222
|
}
|
|
11695
13223
|
|
|
11696
|
-
async function runRunnerRouteAdd(flags) {
|
|
11697
|
-
const ui = createPrompter();
|
|
11698
|
-
try {
|
|
11699
|
-
if (shouldRenderPromptChrome(flags)) {
|
|
11700
|
-
ui.setFlow("RUNNER ROUTE ADD", "Create one executable runner route from server bot and
|
|
11701
|
-
}
|
|
11702
|
-
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
11703
|
-
const provider = await selectRunnerRouteProvider(ui, flags, "");
|
|
11704
|
-
const projectID = await resolveRunnerRouteProjectID(ui, flags, config);
|
|
11705
|
-
const role = await selectRunnerRole(ui, flags, "");
|
|
11706
|
-
const botSelection = await selectRunnerServerBotProfile(ui, { provider, role, flags });
|
|
11707
|
-
const
|
|
11708
|
-
projectID,
|
|
11709
|
-
provider,
|
|
11710
|
-
flags,
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
13224
|
+
async function runRunnerRouteAdd(flags) {
|
|
13225
|
+
const ui = createPrompter();
|
|
13226
|
+
try {
|
|
13227
|
+
if (shouldRenderPromptChrome(flags)) {
|
|
13228
|
+
ui.setFlow("RUNNER ROUTE ADD", "Create one executable runner route from server bot and target");
|
|
13229
|
+
}
|
|
13230
|
+
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
13231
|
+
const provider = await selectRunnerRouteProvider(ui, flags, "");
|
|
13232
|
+
const projectID = await resolveRunnerRouteProjectID(ui, flags, config);
|
|
13233
|
+
const role = await selectRunnerRole(ui, flags, "");
|
|
13234
|
+
const botSelection = await selectRunnerServerBotProfile(ui, { provider, role, flags });
|
|
13235
|
+
const targetSelection = await resolveRunnerRouteTargetSelection(ui, {
|
|
13236
|
+
projectID,
|
|
13237
|
+
provider,
|
|
13238
|
+
flags,
|
|
13239
|
+
botSelection,
|
|
13240
|
+
});
|
|
13241
|
+
const suggestedName = buildRunnerRouteNameSuggestion({
|
|
13242
|
+
provider,
|
|
13243
|
+
role,
|
|
13244
|
+
botName: botSelection.name,
|
|
13245
|
+
routeKind: targetSelection.routeKind,
|
|
13246
|
+
}, ensureArray(config.routes).map((route) => normalizeRunnerRoute(route).name));
|
|
13247
|
+
const routeName = String(flags.name || "").trim() || suggestedName;
|
|
13248
|
+
const pollIntervalMs = intFromRaw(flags["poll-interval-ms"], 5000) || 5000;
|
|
11719
13249
|
const enabled = Object.prototype.hasOwnProperty.call(flags, "enabled")
|
|
11720
13250
|
? boolFromRaw(flags.enabled, true)
|
|
11721
13251
|
: true;
|
|
11722
|
-
const nextRoute = buildRunnerRoutePayload({
|
|
11723
|
-
name: routeName,
|
|
11724
|
-
enabled,
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
13252
|
+
const nextRoute = buildRunnerRoutePayload({
|
|
13253
|
+
name: routeName,
|
|
13254
|
+
enabled,
|
|
13255
|
+
routeKind: targetSelection.routeKind,
|
|
13256
|
+
projectID,
|
|
13257
|
+
provider,
|
|
13258
|
+
role,
|
|
13259
|
+
roleProfile: role,
|
|
13260
|
+
serverBotName: botSelection.name,
|
|
13261
|
+
serverBotID: botSelection.id,
|
|
13262
|
+
destinationID: targetSelection.destination.id,
|
|
13263
|
+
destinationLabel: targetSelection.destination.label,
|
|
13264
|
+
pollIntervalMs,
|
|
13265
|
+
});
|
|
11735
13266
|
const saved = upsertRunnerRouteConfig(config, nextRoute);
|
|
11736
13267
|
const filePath = saveBotRunnerConfig(saved, config.filePath);
|
|
11737
13268
|
if (!String(flags.name || "").trim()) {
|
|
@@ -11817,24 +13348,28 @@ async function runRunnerRouteEdit(flags) {
|
|
|
11817
13348
|
})
|
|
11818
13349
|
: { name: currentRoute.botName, id: currentRoute.botID };
|
|
11819
13350
|
|
|
11820
|
-
const
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
|
|
13351
|
+
const destination = currentRoute.routeKind === "dm"
|
|
13352
|
+
? { id: "", label: "" }
|
|
13353
|
+
: await (async () => {
|
|
13354
|
+
const destinationAction = await promptChoice(
|
|
13355
|
+
ui,
|
|
13356
|
+
"Project chat destination",
|
|
13357
|
+
[
|
|
13358
|
+
{ value: "keep", label: "Keep current destination", description: currentRoute.destinationLabel || currentRoute.destinationID || "-" },
|
|
13359
|
+
{ value: "change", label: "Change destination", description: "select another active project destination" },
|
|
13360
|
+
],
|
|
13361
|
+
{ defaultIndex: projectID !== currentRoute.projectID ? 1 : 0 },
|
|
13362
|
+
);
|
|
13363
|
+
return destinationAction?.value === "change" || projectID !== currentRoute.projectID
|
|
13364
|
+
? selectRunnerDestination(ui, {
|
|
13365
|
+
projectID,
|
|
13366
|
+
provider: currentRoute.provider,
|
|
13367
|
+
flags: {},
|
|
13368
|
+
currentDestinationID: currentRoute.destinationID,
|
|
13369
|
+
currentDestinationLabel: currentRoute.destinationLabel,
|
|
13370
|
+
})
|
|
13371
|
+
: { id: currentRoute.destinationID, label: currentRoute.destinationLabel };
|
|
13372
|
+
})();
|
|
11838
13373
|
|
|
11839
13374
|
const pollAction = await promptChoice(
|
|
11840
13375
|
ui,
|
|
@@ -11873,12 +13408,13 @@ async function runRunnerRouteEdit(flags) {
|
|
|
11873
13408
|
process.stdout.write("Cancelled.\n");
|
|
11874
13409
|
return;
|
|
11875
13410
|
}
|
|
11876
|
-
const nextRoute = buildRunnerRoutePayload({
|
|
11877
|
-
currentRoute,
|
|
11878
|
-
name: routeName,
|
|
11879
|
-
enabled,
|
|
11880
|
-
|
|
11881
|
-
|
|
13411
|
+
const nextRoute = buildRunnerRoutePayload({
|
|
13412
|
+
currentRoute,
|
|
13413
|
+
name: routeName,
|
|
13414
|
+
enabled,
|
|
13415
|
+
routeKind: currentRoute.routeKind,
|
|
13416
|
+
projectID,
|
|
13417
|
+
provider: currentRoute.provider,
|
|
11882
13418
|
role,
|
|
11883
13419
|
roleProfile: role,
|
|
11884
13420
|
serverBotName: botSelection.name,
|
|
@@ -11942,30 +13478,37 @@ function parseRunnerProjectUpRoles(flags) {
|
|
|
11942
13478
|
return ordered;
|
|
11943
13479
|
}
|
|
11944
13480
|
|
|
11945
|
-
function runnerProjectUpScopeKey({ projectID, provider, destinationID = "", destinationLabel = "", botIdentity = "" }) {
|
|
11946
|
-
return [
|
|
11947
|
-
String(projectID || "").trim() || "-",
|
|
11948
|
-
String(provider || "").trim() || "-",
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
const
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
const
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
13481
|
+
function runnerProjectUpScopeKey({ projectID, provider, routeKind = "room", destinationID = "", destinationLabel = "", botIdentity = "" }) {
|
|
13482
|
+
return [
|
|
13483
|
+
String(projectID || "").trim() || "-",
|
|
13484
|
+
String(provider || "").trim() || "-",
|
|
13485
|
+
normalizeRunnerRouteKind(routeKind),
|
|
13486
|
+
normalizeRunnerRouteKind(routeKind) === "dm"
|
|
13487
|
+
? "direct-messages"
|
|
13488
|
+
: (String(destinationID || "").trim() || normalizeRunnerRouteIdentityText(destinationLabel) || "-"),
|
|
13489
|
+
normalizeRunnerRouteIdentityText(botIdentity) || "-",
|
|
13490
|
+
].join("::");
|
|
13491
|
+
}
|
|
13492
|
+
|
|
13493
|
+
function routeMatchesRunnerProjectUpScope(routeRaw, scopeRaw) {
|
|
13494
|
+
const route = normalizeRunnerRoute(routeRaw);
|
|
13495
|
+
const scope = safeObject(scopeRaw);
|
|
13496
|
+
if (String(route.projectID || "").trim() !== String(scope.projectID || "").trim()) return false;
|
|
13497
|
+
if (String(route.provider || "").trim() !== String(scope.provider || "").trim()) return false;
|
|
13498
|
+
const scopeRouteKind = normalizeRunnerRouteKind(scope.routeKind, "room");
|
|
13499
|
+
if ((route.routeKind || "room") !== scopeRouteKind) return false;
|
|
13500
|
+
if (scopeRouteKind === "room") {
|
|
13501
|
+
const routeDestinationID = String(route.destinationID || "").trim();
|
|
13502
|
+
const scopeDestinationID = String(scope.destinationID || "").trim();
|
|
13503
|
+
const routeDestinationLabel = normalizeRunnerRouteIdentityText(route.destinationLabel);
|
|
13504
|
+
const scopeDestinationLabel = normalizeRunnerRouteIdentityText(scope.destinationLabel);
|
|
13505
|
+
const destinationMatches = scopeDestinationID
|
|
13506
|
+
? (routeDestinationID === scopeDestinationID || (!routeDestinationID && routeDestinationLabel === scopeDestinationLabel))
|
|
13507
|
+
: routeDestinationLabel === scopeDestinationLabel;
|
|
13508
|
+
if (!destinationMatches) return false;
|
|
13509
|
+
}
|
|
13510
|
+
const routeBotName = normalizeRunnerRouteIdentityText(route.botName);
|
|
13511
|
+
const scopeBotIdentity = normalizeRunnerRouteIdentityText(scope.botIdentity);
|
|
11969
13512
|
const routeBotID = String(route.botID || "").trim();
|
|
11970
13513
|
const scopeRoleIDs = new Set(
|
|
11971
13514
|
Object.values(safeObject(scope.serverRoleIDs))
|
|
@@ -11978,17 +13521,24 @@ function routeMatchesRunnerProjectUpScope(routeRaw, scopeRaw) {
|
|
|
11978
13521
|
);
|
|
11979
13522
|
}
|
|
11980
13523
|
|
|
11981
|
-
function resolveRunnerConversationPeers(routeRaw) {
|
|
11982
|
-
const normalizedRoute = normalizeRunnerRoute(routeRaw);
|
|
11983
|
-
|
|
13524
|
+
function resolveRunnerConversationPeers(routeRaw) {
|
|
13525
|
+
const normalizedRoute = normalizeRunnerRoute(routeRaw);
|
|
13526
|
+
if (normalizedRoute.routeKind === "dm") {
|
|
13527
|
+
return [{
|
|
13528
|
+
id: String(normalizedRoute.botID || "").trim(),
|
|
13529
|
+
name: String(normalizedRoute.botName || "").trim(),
|
|
13530
|
+
}].filter((item) => item.id || item.name);
|
|
13531
|
+
}
|
|
13532
|
+
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
11984
13533
|
const seen = new Set();
|
|
11985
13534
|
const peers = [];
|
|
11986
13535
|
ensureArray(config.routes)
|
|
11987
13536
|
.map((rawRoute) => normalizeRunnerRoute(rawRoute))
|
|
11988
|
-
.filter((route) => route.enabled)
|
|
11989
|
-
.filter((route) => String(route.projectID || "").trim() === String(normalizedRoute.projectID || "").trim())
|
|
11990
|
-
.filter((route) => String(route.provider || "").trim() === String(normalizedRoute.provider || "").trim())
|
|
11991
|
-
.filter((route) =>
|
|
13537
|
+
.filter((route) => route.enabled)
|
|
13538
|
+
.filter((route) => String(route.projectID || "").trim() === String(normalizedRoute.projectID || "").trim())
|
|
13539
|
+
.filter((route) => String(route.provider || "").trim() === String(normalizedRoute.provider || "").trim())
|
|
13540
|
+
.filter((route) => (route.routeKind || "room") === (normalizedRoute.routeKind || "room"))
|
|
13541
|
+
.filter((route) => {
|
|
11992
13542
|
const routeDestinationID = String(route.destinationID || "").trim();
|
|
11993
13543
|
const targetDestinationID = String(normalizedRoute.destinationID || "").trim();
|
|
11994
13544
|
const routeDestinationLabel = normalizeRunnerRouteIdentityText(route.destinationLabel);
|
|
@@ -12009,17 +13559,33 @@ function resolveRunnerConversationPeers(routeRaw) {
|
|
|
12009
13559
|
return peers;
|
|
12010
13560
|
}
|
|
12011
13561
|
|
|
12012
|
-
function resolveRunnerConversationManagedBots(routeRaw, availableBots = []) {
|
|
12013
|
-
const normalizedRoute = normalizeRunnerRoute(routeRaw);
|
|
12014
|
-
|
|
13562
|
+
function resolveRunnerConversationManagedBots(routeRaw, availableBots = []) {
|
|
13563
|
+
const normalizedRoute = normalizeRunnerRoute(routeRaw);
|
|
13564
|
+
if (normalizedRoute.routeKind === "dm") {
|
|
13565
|
+
try {
|
|
13566
|
+
const bot = selectRunnerBot(availableBots, normalizedRoute);
|
|
13567
|
+
const username = String(bot?.username || bot?.name || normalizedRoute.botName || "").trim().replace(/^@+/, "").toLowerCase();
|
|
13568
|
+
return username ? [{
|
|
13569
|
+
username,
|
|
13570
|
+
display_name: String(bot?.name || bot?.username || normalizedRoute.botName || username).trim(),
|
|
13571
|
+
route_name: String(normalizedRoute.name || "").trim(),
|
|
13572
|
+
route: normalizedRoute,
|
|
13573
|
+
bot,
|
|
13574
|
+
}] : [];
|
|
13575
|
+
} catch {
|
|
13576
|
+
return [];
|
|
13577
|
+
}
|
|
13578
|
+
}
|
|
13579
|
+
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
12015
13580
|
const seen = new Set();
|
|
12016
13581
|
const managed = [];
|
|
12017
13582
|
ensureArray(config.routes)
|
|
12018
13583
|
.map((rawRoute) => normalizeRunnerRoute(rawRoute))
|
|
12019
|
-
.filter((route) => route.enabled)
|
|
12020
|
-
.filter((route) => String(route.projectID || "").trim() === String(normalizedRoute.projectID || "").trim())
|
|
12021
|
-
.filter((route) => String(route.provider || "").trim() === String(normalizedRoute.provider || "").trim())
|
|
12022
|
-
.filter((route) =>
|
|
13584
|
+
.filter((route) => route.enabled)
|
|
13585
|
+
.filter((route) => String(route.projectID || "").trim() === String(normalizedRoute.projectID || "").trim())
|
|
13586
|
+
.filter((route) => String(route.provider || "").trim() === String(normalizedRoute.provider || "").trim())
|
|
13587
|
+
.filter((route) => (route.routeKind || "room") === (normalizedRoute.routeKind || "room"))
|
|
13588
|
+
.filter((route) => {
|
|
12023
13589
|
const routeDestinationID = String(route.destinationID || "").trim();
|
|
12024
13590
|
const targetDestinationID = String(normalizedRoute.destinationID || "").trim();
|
|
12025
13591
|
const routeDestinationLabel = normalizeRunnerRouteIdentityText(route.destinationLabel);
|
|
@@ -12070,20 +13636,22 @@ function applyRunnerProjectUpRouteSuggestions(routeSuggestions) {
|
|
|
12070
13636
|
desiredRoutes.forEach((suggestionRaw) => {
|
|
12071
13637
|
const suggestion = safeObject(suggestionRaw);
|
|
12072
13638
|
const routePayload = normalizeRunnerRoute(suggestion.routePayload);
|
|
12073
|
-
const scopeKey = runnerProjectUpScopeKey({
|
|
12074
|
-
projectID: routePayload.projectID,
|
|
12075
|
-
provider: routePayload.provider,
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
13639
|
+
const scopeKey = runnerProjectUpScopeKey({
|
|
13640
|
+
projectID: routePayload.projectID,
|
|
13641
|
+
provider: routePayload.provider,
|
|
13642
|
+
routeKind: routePayload.routeKind,
|
|
13643
|
+
destinationID: routePayload.destinationID,
|
|
13644
|
+
destinationLabel: routePayload.destinationLabel,
|
|
13645
|
+
botIdentity: suggestion.botIdentity || suggestion.serverBotName || routePayload.botName,
|
|
12079
13646
|
});
|
|
12080
13647
|
if (!scopeGroups.has(scopeKey)) {
|
|
12081
13648
|
scopeGroups.set(scopeKey, {
|
|
12082
|
-
projectID: routePayload.projectID,
|
|
12083
|
-
provider: routePayload.provider,
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
13649
|
+
projectID: routePayload.projectID,
|
|
13650
|
+
provider: routePayload.provider,
|
|
13651
|
+
routeKind: routePayload.routeKind,
|
|
13652
|
+
destinationID: routePayload.destinationID,
|
|
13653
|
+
destinationLabel: routePayload.destinationLabel,
|
|
13654
|
+
botIdentity: suggestion.botIdentity || suggestion.serverBotName || routePayload.botName,
|
|
12087
13655
|
serverRoleIDs: { ...safeObject(suggestion.serverRoleIDs) },
|
|
12088
13656
|
desiredSignatures: new Set(),
|
|
12089
13657
|
});
|
|
@@ -12136,11 +13704,12 @@ function applyRunnerProjectUpRouteSuggestions(routeSuggestions) {
|
|
|
12136
13704
|
};
|
|
12137
13705
|
}
|
|
12138
13706
|
|
|
12139
|
-
function resolveRunnerProjectUpRoutes({
|
|
12140
|
-
projectID,
|
|
12141
|
-
provider,
|
|
12142
|
-
|
|
12143
|
-
|
|
13707
|
+
function resolveRunnerProjectUpRoutes({
|
|
13708
|
+
projectID,
|
|
13709
|
+
provider,
|
|
13710
|
+
routeKind = "",
|
|
13711
|
+
destinationID,
|
|
13712
|
+
destinationLabel,
|
|
12144
13713
|
botName = "",
|
|
12145
13714
|
botID = "",
|
|
12146
13715
|
roleFilter = [],
|
|
@@ -12148,20 +13717,22 @@ function resolveRunnerProjectUpRoutes({
|
|
|
12148
13717
|
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
12149
13718
|
const telegramEntries = ensureArray(readTelegramEnvState().entries);
|
|
12150
13719
|
const normalizedRoles = ensureArray(roleFilter).map((value) => normalizeBotRole(value)).filter(Boolean);
|
|
12151
|
-
const selectionFlags = {
|
|
12152
|
-
"project-id": projectID,
|
|
12153
|
-
provider,
|
|
12154
|
-
"
|
|
12155
|
-
"destination-
|
|
13720
|
+
const selectionFlags = {
|
|
13721
|
+
"project-id": projectID,
|
|
13722
|
+
provider,
|
|
13723
|
+
...(routeKind ? { "route-kind": routeKind } : {}),
|
|
13724
|
+
"destination-id": destinationID,
|
|
13725
|
+
"destination-label": destinationLabel,
|
|
12156
13726
|
...(botName ? { "bot-name": botName } : {}),
|
|
12157
13727
|
...(botID ? { "bot-id": botID } : {}),
|
|
12158
13728
|
};
|
|
12159
|
-
const selectionRoute = normalizeRunnerRoute({
|
|
12160
|
-
project_id: projectID,
|
|
12161
|
-
provider,
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
13729
|
+
const selectionRoute = normalizeRunnerRoute({
|
|
13730
|
+
project_id: projectID,
|
|
13731
|
+
provider,
|
|
13732
|
+
...(routeKind ? { route_kind: routeKind } : {}),
|
|
13733
|
+
destination_id: destinationID,
|
|
13734
|
+
destination_label: destinationLabel,
|
|
13735
|
+
server_bot_name: botName,
|
|
12165
13736
|
server_bot_id: botID,
|
|
12166
13737
|
});
|
|
12167
13738
|
const matched = ensureArray(config.routes)
|
|
@@ -12173,10 +13744,12 @@ function resolveRunnerProjectUpRoutes({
|
|
|
12173
13744
|
return matched;
|
|
12174
13745
|
}
|
|
12175
13746
|
const seenBotDestinations = new Map();
|
|
12176
|
-
return matched.filter((route) => {
|
|
12177
|
-
const botIdentity = String(route.serverBotID || route.botID || route.botName || route.serverBotName || "").trim().toLowerCase();
|
|
12178
|
-
const
|
|
12179
|
-
|
|
13747
|
+
return matched.filter((route) => {
|
|
13748
|
+
const botIdentity = String(route.serverBotID || route.botID || route.botName || route.serverBotName || "").trim().toLowerCase();
|
|
13749
|
+
const targetIdentity = route.routeKind === "dm"
|
|
13750
|
+
? `${route.routeKind || "dm"}`
|
|
13751
|
+
: String(route.destinationID || route.destinationLabel || "").trim().toLowerCase();
|
|
13752
|
+
const dedupeKey = `${botIdentity}::${targetIdentity}`;
|
|
12180
13753
|
if (!dedupeKey || dedupeKey === "::") return true;
|
|
12181
13754
|
if (seenBotDestinations.has(dedupeKey)) return false;
|
|
12182
13755
|
seenBotDestinations.set(dedupeKey, true);
|
|
@@ -12258,6 +13831,95 @@ async function runRunnerProjectUp(flags) {
|
|
|
12258
13831
|
});
|
|
12259
13832
|
}
|
|
12260
13833
|
|
|
13834
|
+
function resolveRunnerProjectUpDirectMessageBotUsername(botRaw, telegramEntries = []) {
|
|
13835
|
+
const bot = safeObject(botRaw);
|
|
13836
|
+
const directUsername = normalizeTelegramBotUsername(bot.username);
|
|
13837
|
+
if (directUsername) return directUsername;
|
|
13838
|
+
const matchedEntry = ensureArray(telegramEntries).find((entryRaw) => {
|
|
13839
|
+
const entry = safeObject(entryRaw);
|
|
13840
|
+
return (
|
|
13841
|
+
(bot.id && entry.serverBotID && entry.serverBotID === bot.id)
|
|
13842
|
+
|| (bot.name && normalizeRunnerRouteIdentityText(entry.serverBotName) === normalizeRunnerRouteIdentityText(bot.name))
|
|
13843
|
+
);
|
|
13844
|
+
});
|
|
13845
|
+
return normalizeTelegramBotUsername(matchedEntry?.username);
|
|
13846
|
+
}
|
|
13847
|
+
|
|
13848
|
+
function buildRunnerProjectUpDirectMessageRouteSuggestions({
|
|
13849
|
+
projectID,
|
|
13850
|
+
provider,
|
|
13851
|
+
projectSettings,
|
|
13852
|
+
availableBots,
|
|
13853
|
+
telegramEntries = [],
|
|
13854
|
+
botNameFilter = "",
|
|
13855
|
+
botIDFilter = "",
|
|
13856
|
+
roleFilter = [],
|
|
13857
|
+
}) {
|
|
13858
|
+
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
13859
|
+
const allowedBotUsernames = new Set(
|
|
13860
|
+
ensureArray(safeObject(projectSettings).directMessageBotUsernames)
|
|
13861
|
+
.map((value) => normalizeTelegramBotUsername(value))
|
|
13862
|
+
.filter(Boolean),
|
|
13863
|
+
);
|
|
13864
|
+
const desiredRoles = ensureArray(roleFilter).length
|
|
13865
|
+
? ensureArray(roleFilter).map((value) => normalizeBotRole(value)).filter(Boolean)
|
|
13866
|
+
: ["monitor"];
|
|
13867
|
+
const existingRoutes = ensureArray(config.routes).map((route) => normalizeRunnerRoute(route));
|
|
13868
|
+
return ensureArray(availableBots)
|
|
13869
|
+
.filter((bot) => bot && bot.isActive && bot.provider === provider)
|
|
13870
|
+
.map((bot) => ({
|
|
13871
|
+
...bot,
|
|
13872
|
+
username: resolveRunnerProjectUpDirectMessageBotUsername(bot, telegramEntries),
|
|
13873
|
+
hasLocalBinding: Boolean(
|
|
13874
|
+
ensureArray(telegramEntries).find((entryRaw) => {
|
|
13875
|
+
const entry = safeObject(entryRaw);
|
|
13876
|
+
return (
|
|
13877
|
+
(bot.id && entry.serverBotID && entry.serverBotID === bot.id)
|
|
13878
|
+
|| (bot.name && normalizeRunnerRouteIdentityText(entry.serverBotName) === normalizeRunnerRouteIdentityText(bot.name))
|
|
13879
|
+
|| (resolveRunnerProjectUpDirectMessageBotUsername(bot, telegramEntries) && normalizeTelegramBotUsername(entry.username) === resolveRunnerProjectUpDirectMessageBotUsername(bot, telegramEntries))
|
|
13880
|
+
);
|
|
13881
|
+
}),
|
|
13882
|
+
),
|
|
13883
|
+
}))
|
|
13884
|
+
.filter((bot) => bot.username && allowedBotUsernames.has(bot.username))
|
|
13885
|
+
.filter((bot) => bot.hasLocalBinding)
|
|
13886
|
+
.filter((bot) => !botIDFilter || String(bot.id || "").trim() === botIDFilter)
|
|
13887
|
+
.filter((bot) => !botNameFilter || matchesRunnerRouteText(bot.name, botNameFilter) || matchesRunnerRouteText(bot.username, botNameFilter))
|
|
13888
|
+
.filter((bot) => !desiredRoles.length || desiredRoles.includes(normalizeBotRole(bot.role)))
|
|
13889
|
+
.map((bot) => {
|
|
13890
|
+
const routePayload = buildRunnerRoutePayload({
|
|
13891
|
+
name: buildRunnerRouteNameSuggestion({
|
|
13892
|
+
provider,
|
|
13893
|
+
role: normalizeBotRole(bot.role) || "monitor",
|
|
13894
|
+
botName: bot.name,
|
|
13895
|
+
routeKind: "dm",
|
|
13896
|
+
}, existingRoutes.map((route) => route.name)),
|
|
13897
|
+
enabled: true,
|
|
13898
|
+
routeKind: "dm",
|
|
13899
|
+
projectID,
|
|
13900
|
+
provider,
|
|
13901
|
+
role: normalizeBotRole(bot.role) || "monitor",
|
|
13902
|
+
roleProfile: normalizeBotRole(bot.role) || "monitor",
|
|
13903
|
+
serverBotName: String(bot.name || "").trim(),
|
|
13904
|
+
serverBotID: String(bot.id || "").trim(),
|
|
13905
|
+
destinationID: "",
|
|
13906
|
+
destinationLabel: "",
|
|
13907
|
+
});
|
|
13908
|
+
const logicalSignature = runnerRouteLogicalSignature(routePayload);
|
|
13909
|
+
const existing = existingRoutes.find((route) => route.enabled !== false && runnerRouteLogicalSignature(route) === logicalSignature);
|
|
13910
|
+
return {
|
|
13911
|
+
status: existing ? "existing" : "create",
|
|
13912
|
+
routeName: String((existing || routePayload).name || "").trim(),
|
|
13913
|
+
logicalSignature,
|
|
13914
|
+
serverBotName: String(bot.name || "").trim(),
|
|
13915
|
+
role: normalizeBotRole(bot.role) || "monitor",
|
|
13916
|
+
botIdentity: bot.username,
|
|
13917
|
+
serverRoleIDs: bot.id ? { [normalizeBotRole(bot.role) || "monitor"]: String(bot.id || "").trim() } : {},
|
|
13918
|
+
routePayload,
|
|
13919
|
+
};
|
|
13920
|
+
});
|
|
13921
|
+
}
|
|
13922
|
+
|
|
12261
13923
|
function resolveRunnerProjectTUIRuntimePolicy(flags = {}) {
|
|
12262
13924
|
const explicitStartRequested = Object.prototype.hasOwnProperty.call(flags, "start");
|
|
12263
13925
|
return {
|
|
@@ -12788,6 +14450,7 @@ function buildRunnerProjectUpNextSteps({
|
|
|
12788
14450
|
shouldStartRunner,
|
|
12789
14451
|
projectID,
|
|
12790
14452
|
provider,
|
|
14453
|
+
routeKind = "room",
|
|
12791
14454
|
destinationID,
|
|
12792
14455
|
matchingRoutes,
|
|
12793
14456
|
startFlags,
|
|
@@ -12795,14 +14458,18 @@ function buildRunnerProjectUpNextSteps({
|
|
|
12795
14458
|
const nextSteps = [];
|
|
12796
14459
|
const routeNames = ensureArray(matchingRoutes).map((route) => normalizeRunnerRoute(route).name).filter(Boolean);
|
|
12797
14460
|
if (!applyRequested) {
|
|
12798
|
-
nextSteps.push(
|
|
14461
|
+
nextSteps.push(routeKind === "dm"
|
|
14462
|
+
? `${CLI_NAME} runner project up --project-id ${projectID} --provider ${provider} --route-kind dm --apply true --start false`
|
|
14463
|
+
: `${CLI_NAME} runner project up --project-id ${projectID} --provider ${provider} --destination-id ${destinationID} --apply true --start false`);
|
|
12799
14464
|
}
|
|
12800
14465
|
if (!shouldStartRunner) {
|
|
12801
14466
|
if (routeNames.length > 0) {
|
|
12802
14467
|
nextSteps.push(`${CLI_NAME} runner show --route-name ${routeNames[0]}`);
|
|
12803
14468
|
nextSteps.push(buildRunnerStartDetachedCommand(startFlags));
|
|
12804
14469
|
} else if (applyRequested) {
|
|
12805
|
-
nextSteps.push(
|
|
14470
|
+
nextSteps.push(routeKind === "dm"
|
|
14471
|
+
? `${CLI_NAME} runner project up --project-id ${projectID} --provider ${provider} --route-kind dm --apply true --start false`
|
|
14472
|
+
: `${CLI_NAME} runner project up --project-id ${projectID} --provider ${provider} --destination-id ${destinationID} --apply true --start false`);
|
|
12806
14473
|
}
|
|
12807
14474
|
return nextSteps;
|
|
12808
14475
|
}
|
|
@@ -12829,16 +14496,151 @@ async function buildRunnerProjectUpResult(flags = {}) {
|
|
|
12829
14496
|
shouldStartRunner,
|
|
12830
14497
|
foregroundStartRequested,
|
|
12831
14498
|
} = resolveRunnerProjectUpExecutionPolicy(flags);
|
|
12832
|
-
const roleFilter = parseRunnerProjectUpRoles(flags);
|
|
12833
|
-
const botNameFilter = String(flags["bot-name"] || "").trim();
|
|
12834
|
-
const botIDFilter = String(flags["bot-id"] || "").trim();
|
|
12835
|
-
const
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
14499
|
+
const roleFilter = parseRunnerProjectUpRoles(flags);
|
|
14500
|
+
const botNameFilter = String(flags["bot-name"] || "").trim();
|
|
14501
|
+
const botIDFilter = String(flags["bot-id"] || "").trim();
|
|
14502
|
+
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
14503
|
+
const projectID = await resolveRunnerRouteProjectID(ui, flags, config);
|
|
14504
|
+
const timeoutSeconds = intFromRaw(flags["timeout-seconds"], 15) || 15;
|
|
14505
|
+
const { siteBaseURL, token } = await resolveRunnerCommandToken(flags["base-url"], timeoutSeconds);
|
|
14506
|
+
const [projectSettings, destinations] = await Promise.all([
|
|
14507
|
+
getProjectRunnerSettings({
|
|
14508
|
+
siteBaseURL,
|
|
14509
|
+
projectID,
|
|
14510
|
+
token,
|
|
14511
|
+
timeoutSeconds,
|
|
14512
|
+
}),
|
|
14513
|
+
listProjectChatDestinations({
|
|
14514
|
+
siteBaseURL,
|
|
14515
|
+
projectID,
|
|
14516
|
+
token,
|
|
14517
|
+
timeoutSeconds,
|
|
14518
|
+
}),
|
|
14519
|
+
]);
|
|
14520
|
+
const activeDestinations = ensureArray(destinations)
|
|
14521
|
+
.map((item) => normalizeChatDestination(item))
|
|
14522
|
+
.filter((item) => item.isActive && item.provider === provider);
|
|
14523
|
+
const requestedRouteKind = hasRunnerFlag(flags, "route-kind")
|
|
14524
|
+
? normalizeRunnerRouteKind(flags["route-kind"])
|
|
14525
|
+
: "";
|
|
14526
|
+
const useDirectMessageProjectUp = requestedRouteKind === "dm"
|
|
14527
|
+
|| (!activeDestinations.length && safeObject(projectSettings).directMessagesEnabled === true);
|
|
14528
|
+
if (requestedRouteKind === "dm" && safeObject(projectSettings).directMessagesEnabled !== true) {
|
|
14529
|
+
throw new Error(`project ${projectID} does not have 1:1 direct messages enabled`);
|
|
14530
|
+
}
|
|
14531
|
+
if (useDirectMessageProjectUp) {
|
|
14532
|
+
const serverBots = await listUserBotsForRunner({
|
|
14533
|
+
siteBaseURL,
|
|
14534
|
+
token,
|
|
14535
|
+
timeoutSeconds,
|
|
14536
|
+
});
|
|
14537
|
+
const telegramEntries = ensureArray(readTelegramEnvState().entries);
|
|
14538
|
+
const filteredRouteSuggestions = buildRunnerProjectUpDirectMessageRouteSuggestions({
|
|
14539
|
+
projectID,
|
|
14540
|
+
provider,
|
|
14541
|
+
projectSettings,
|
|
14542
|
+
availableBots: serverBots,
|
|
14543
|
+
telegramEntries,
|
|
14544
|
+
botNameFilter,
|
|
14545
|
+
botIDFilter,
|
|
14546
|
+
roleFilter,
|
|
14547
|
+
});
|
|
14548
|
+
const applyResultRaw = applyRequested
|
|
14549
|
+
? applyRunnerProjectUpRouteSuggestions(filteredRouteSuggestions)
|
|
14550
|
+
: null;
|
|
14551
|
+
const applyResult = safeObject(applyResultRaw);
|
|
14552
|
+
const matchingRoutes = resolveRunnerProjectUpRoutes({
|
|
14553
|
+
projectID,
|
|
14554
|
+
provider,
|
|
14555
|
+
routeKind: "dm",
|
|
14556
|
+
destinationID: "",
|
|
14557
|
+
destinationLabel: "",
|
|
14558
|
+
botName: botNameFilter,
|
|
14559
|
+
botID: botIDFilter,
|
|
14560
|
+
roleFilter,
|
|
14561
|
+
});
|
|
14562
|
+
const startFlags = {
|
|
14563
|
+
...flags,
|
|
14564
|
+
provider,
|
|
14565
|
+
"project-id": projectID,
|
|
14566
|
+
"route-kind": "dm",
|
|
14567
|
+
...(botNameFilter ? { "bot-name": botNameFilter } : {}),
|
|
14568
|
+
...(botIDFilter ? { "bot-id": botIDFilter } : {}),
|
|
14569
|
+
};
|
|
14570
|
+
const applyFailureWarningOnly = canStartRunnerDespiteProjectUpApplyFailure({
|
|
14571
|
+
applyRequested,
|
|
14572
|
+
applyResult,
|
|
14573
|
+
matchingRoutes,
|
|
14574
|
+
});
|
|
14575
|
+
const summaryPayload = {
|
|
14576
|
+
ok: !applyRequested || applyResult.ok !== false || applyFailureWarningOnly,
|
|
14577
|
+
provider,
|
|
14578
|
+
route_kind: "dm",
|
|
14579
|
+
project_id: projectID,
|
|
14580
|
+
destination_label: "Direct Messages",
|
|
14581
|
+
destination_id: "-",
|
|
14582
|
+
room_probe_ok: true,
|
|
14583
|
+
room_visible_bot_admins: 0,
|
|
14584
|
+
my_server_bots: ensureArray(serverBots).filter((bot) => bot.provider === provider && bot.isActive).length,
|
|
14585
|
+
my_local_bots: telegramEntries.length,
|
|
14586
|
+
bot_name_filter: botNameFilter || "-",
|
|
14587
|
+
bot_id_filter: botIDFilter || "-",
|
|
14588
|
+
role_filter: roleFilter.join(", ") || "-",
|
|
14589
|
+
route_suggestions_create_total: filteredRouteSuggestions.filter((item) => safeObject(item).status === "create").length,
|
|
14590
|
+
route_suggestions_existing_total: filteredRouteSuggestions.filter((item) => safeObject(item).status === "existing").length,
|
|
14591
|
+
route_suggestions_blocked_total: filteredRouteSuggestions.filter((item) => safeObject(item).status === "blocked").length,
|
|
14592
|
+
route_apply_requested: applyRequested,
|
|
14593
|
+
route_apply_changed: Boolean(applyResult.changed),
|
|
14594
|
+
route_config_file: String(applyResult.filePath || config.filePath || "-").trim() || "-",
|
|
14595
|
+
enabled_routes_for_selection: matchingRoutes.map((route) => normalizeRunnerRoute(route).name).filter(Boolean),
|
|
14596
|
+
start_requested: startRequested,
|
|
14597
|
+
start_detached_requested: startDetachedRequested,
|
|
14598
|
+
next_steps: [],
|
|
14599
|
+
applied_routes: ensureArray(applyResult.appliedRoutes).map((item) => safeObject(item)),
|
|
14600
|
+
disabled_routes: ensureArray(applyResult.disabledRoutes).map((item) => safeObject(item)),
|
|
14601
|
+
};
|
|
14602
|
+
summaryPayload.next_steps.push(...buildRunnerProjectUpNextSteps({
|
|
14603
|
+
applyRequested,
|
|
14604
|
+
shouldStartRunner,
|
|
14605
|
+
projectID: summaryPayload.project_id,
|
|
14606
|
+
provider,
|
|
14607
|
+
routeKind: "dm",
|
|
14608
|
+
destinationID: "",
|
|
14609
|
+
matchingRoutes,
|
|
14610
|
+
startFlags,
|
|
14611
|
+
}));
|
|
14612
|
+
if (!filteredRouteSuggestions.length) {
|
|
14613
|
+
summaryPayload.warning = "No eligible 1:1 DM bot matched the current filters. Enable at least one project DM bot and ensure the local Telegram bot binding exists.";
|
|
14614
|
+
}
|
|
14615
|
+
if (applyRequested && applyResult.ok === false && applyResult.error) {
|
|
14616
|
+
if (applyFailureWarningOnly) {
|
|
14617
|
+
summaryPayload.warning = String(applyResult.error || "").trim();
|
|
14618
|
+
} else {
|
|
14619
|
+
summaryPayload.error = String(applyResult.error || "").trim();
|
|
14620
|
+
}
|
|
14621
|
+
}
|
|
14622
|
+
if (foregroundStartRequested && !summaryPayload.warning && !summaryPayload.error) {
|
|
14623
|
+
summaryPayload.warning = "foreground runner start was explicitly requested; the runner stops when this terminal session ends. Use runner start-detached for persistent polling.";
|
|
14624
|
+
}
|
|
14625
|
+
return {
|
|
14626
|
+
summaryPayload,
|
|
14627
|
+
applyRequested,
|
|
14628
|
+
applyResult,
|
|
14629
|
+
shouldStartRunner,
|
|
14630
|
+
startDetachedRequested,
|
|
14631
|
+
matchingRoutes,
|
|
14632
|
+
startFlags,
|
|
14633
|
+
applyFailureBlocksStart: applyRequested && applyResult.ok === false && !applyFailureWarningOnly,
|
|
14634
|
+
};
|
|
14635
|
+
}
|
|
14636
|
+
const auditPayload = await buildBotRoomAuditPayload(
|
|
14637
|
+
ui,
|
|
14638
|
+
{
|
|
14639
|
+
...flags,
|
|
14640
|
+
"project-id": projectID,
|
|
14641
|
+
provider,
|
|
14642
|
+
apply: false,
|
|
14643
|
+
json: false,
|
|
12842
14644
|
},
|
|
12843
14645
|
buildBotCommandDeps(),
|
|
12844
14646
|
);
|
|
@@ -12870,12 +14672,13 @@ async function buildRunnerProjectUpResult(flags = {}) {
|
|
|
12870
14672
|
})
|
|
12871
14673
|
: null;
|
|
12872
14674
|
const applyResult = safeObject(applyResultRaw);
|
|
12873
|
-
const matchingRoutes = resolveRunnerProjectUpRoutes({
|
|
12874
|
-
projectID: String(auditPayload.projectID || "").trim(),
|
|
12875
|
-
provider,
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
|
|
14675
|
+
const matchingRoutes = resolveRunnerProjectUpRoutes({
|
|
14676
|
+
projectID: String(auditPayload.projectID || "").trim(),
|
|
14677
|
+
provider,
|
|
14678
|
+
routeKind: "room",
|
|
14679
|
+
destinationID: String(destination.destinationID || "").trim(),
|
|
14680
|
+
destinationLabel: String(destination.destinationLabel || "").trim(),
|
|
14681
|
+
botName: botNameFilter,
|
|
12879
14682
|
botID: botIDFilter,
|
|
12880
14683
|
roleFilter,
|
|
12881
14684
|
});
|
|
@@ -12923,6 +14726,7 @@ async function buildRunnerProjectUpResult(flags = {}) {
|
|
|
12923
14726
|
shouldStartRunner,
|
|
12924
14727
|
projectID: summaryPayload.project_id,
|
|
12925
14728
|
provider,
|
|
14729
|
+
routeKind: "room",
|
|
12926
14730
|
destinationID: summaryPayload.destination_id,
|
|
12927
14731
|
matchingRoutes,
|
|
12928
14732
|
startFlags,
|
|
@@ -12958,9 +14762,9 @@ async function runRunnerList(flags) {
|
|
|
12958
14762
|
process.stdout.write(`${JSON.stringify({ ok: true, routes: rows }, null, 2)}\n`);
|
|
12959
14763
|
return;
|
|
12960
14764
|
}
|
|
12961
|
-
process.stdout.write("Runner routes\n");
|
|
12962
|
-
process.stdout.write(" note: routes are the executable unit. Use --route-name in production. --bot-name and --bot-id are convenience selectors that resolve one enabled route only when the match is unique.\n");
|
|
12963
|
-
process.stdout.write(" note: do not create two enabled routes for the same project/provider/
|
|
14765
|
+
process.stdout.write("Runner routes\n");
|
|
14766
|
+
process.stdout.write(" note: routes are the executable unit. Use --route-name in production. --bot-name and --bot-id are convenience selectors that resolve one enabled route only when the match is unique.\n");
|
|
14767
|
+
process.stdout.write(" note: do not create two enabled routes for the same project/provider/route-kind/server-bot/target.\n");
|
|
12964
14768
|
if (!rows.length) {
|
|
12965
14769
|
process.stdout.write(" none configured\n");
|
|
12966
14770
|
return;
|
|
@@ -12971,9 +14775,10 @@ async function runRunnerList(flags) {
|
|
|
12971
14775
|
[
|
|
12972
14776
|
` - ${row.name}${row.enabled ? "" : " [disabled]"}`,
|
|
12973
14777
|
` logical_signature: ${row.logicalSignature}`,
|
|
12974
|
-
` provider: ${row.provider}`,
|
|
12975
|
-
`
|
|
12976
|
-
`
|
|
14778
|
+
` provider: ${row.provider}`,
|
|
14779
|
+
` route_kind: ${row.routeKind}`,
|
|
14780
|
+
` project_id: ${row.projectID}`,
|
|
14781
|
+
` server_bot_name: ${row.botName}`,
|
|
12977
14782
|
` server_bot_name_source: ${row.botNameSource}`,
|
|
12978
14783
|
` server_bot_id: ${row.botID}`,
|
|
12979
14784
|
` role: ${row.role}`,
|
|
@@ -13197,6 +15002,25 @@ function runnerDetachedRouteSetSignature(routes) {
|
|
|
13197
15002
|
|
|
13198
15003
|
function runnerSchedulingTargetKeyFromRouteKey(routeKey) {
|
|
13199
15004
|
const parts = String(routeKey || "").trim().split("::").filter(Boolean);
|
|
15005
|
+
if (parts.length >= 7) {
|
|
15006
|
+
const projectID = String(parts[parts.length - 6] || "").trim();
|
|
15007
|
+
const provider = String(parts[parts.length - 5] || "").trim();
|
|
15008
|
+
const routeKind = normalizeRunnerRouteKind(parts[parts.length - 4], "room");
|
|
15009
|
+
if (routeKind === "dm") {
|
|
15010
|
+
return [
|
|
15011
|
+
projectID,
|
|
15012
|
+
provider,
|
|
15013
|
+
routeKind,
|
|
15014
|
+
String(parts[parts.length - 2] || "").trim(),
|
|
15015
|
+
].join("::");
|
|
15016
|
+
}
|
|
15017
|
+
return [
|
|
15018
|
+
projectID,
|
|
15019
|
+
provider,
|
|
15020
|
+
routeKind,
|
|
15021
|
+
String(parts[parts.length - 1] || "").trim(),
|
|
15022
|
+
].join("::");
|
|
15023
|
+
}
|
|
13200
15024
|
if (parts.length < 6) {
|
|
13201
15025
|
return "";
|
|
13202
15026
|
}
|
|
@@ -13292,8 +15116,8 @@ function classifyDetachedRunnerLaunchReuse(registry, routes) {
|
|
|
13292
15116
|
),
|
|
13293
15117
|
);
|
|
13294
15118
|
const detail = supersets.length > 1
|
|
13295
|
-
? `multiple detached runners already cover the requested route set for the same project/provider/
|
|
13296
|
-
: `another detached runner already owns the same project/provider/
|
|
15119
|
+
? `multiple detached runners already cover the requested route set for the same project/provider/route target (${conflictingLaunches.map((entry) => describeDetachedRunnerLaunch(entry)).join(" | ")})`
|
|
15120
|
+
: `another detached runner already owns the same project/provider/route target (${conflictingLaunches.map((entry) => describeDetachedRunnerLaunch(entry)).join(" | ")}). Stop it first or use runner project tui so one detached runner owns the full route set for that target`;
|
|
13297
15121
|
return {
|
|
13298
15122
|
kind: "conflict",
|
|
13299
15123
|
detail,
|
|
@@ -18927,17 +20751,24 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
18927
20751
|
buildToolAliasMaps,
|
|
18928
20752
|
rewriteAliasedToolCallToCanonical,
|
|
18929
20753
|
normalizeBotRunnerConfigContents,
|
|
18930
|
-
defaultLocalBotBridgeCommand,
|
|
18931
|
-
|
|
18932
|
-
|
|
18933
|
-
|
|
20754
|
+
defaultLocalBotBridgeCommand,
|
|
20755
|
+
cliName: CLI_NAME,
|
|
20756
|
+
resolveRunnerExecutionPlan,
|
|
20757
|
+
resolveRunnerExecutionPlanForRole,
|
|
20758
|
+
resolveRunnerLocalAIExecutionProfile,
|
|
20759
|
+
normalizeRunnerRoute,
|
|
18934
20760
|
buildRunnerRouteNameSuggestion,
|
|
18935
|
-
buildRunnerRoutePayload,
|
|
18936
|
-
upsertRunnerRouteConfig,
|
|
18937
|
-
removeRunnerRouteFromConfig,
|
|
18938
|
-
acquireRunnerRouteLease,
|
|
18939
|
-
|
|
18940
|
-
|
|
20761
|
+
buildRunnerRoutePayload,
|
|
20762
|
+
upsertRunnerRouteConfig,
|
|
20763
|
+
removeRunnerRouteFromConfig,
|
|
20764
|
+
acquireRunnerRouteLease,
|
|
20765
|
+
validateRunnerRoute,
|
|
20766
|
+
runnerRouteSchedulingGroupKey,
|
|
20767
|
+
buildRunnerProjectUpDirectMessageRouteSuggestions,
|
|
20768
|
+
resolveRunnerProjectUpRoutes,
|
|
20769
|
+
buildRunnerProjectUpNextSteps,
|
|
20770
|
+
buildRunnerExecutionDeps,
|
|
20771
|
+
defaultBotRunnerRoleProfiles,
|
|
18941
20772
|
resolveRunnerRoutes,
|
|
18942
20773
|
runnerRouteKey,
|
|
18943
20774
|
runnerRouteLogicalSignature,
|
|
@@ -18993,6 +20824,9 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
18993
20824
|
formatTelegramInboundArchiveComment,
|
|
18994
20825
|
findArchivedBotReplyRecord,
|
|
18995
20826
|
parseArchivedChatComment,
|
|
20827
|
+
normalizeLocalTelegramUpdate,
|
|
20828
|
+
normalizeRunnerTelegramMessageEnvelope,
|
|
20829
|
+
normalizeRunnerRecentLocalInboundReceiptEntry,
|
|
18996
20830
|
intFromRawAllowZero,
|
|
18997
20831
|
validateWorkspaceArtifacts,
|
|
18998
20832
|
});
|
|
@@ -20044,7 +21878,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
20044
21878
|
"detached_runner_blocks_disjoint_same_target_parallel_launches",
|
|
20045
21879
|
decision.kind === "conflict"
|
|
20046
21880
|
&& ensureArray(decision.overlapping_route_names).length === 0
|
|
20047
|
-
&& String(decision.detail || "").includes("same project/provider/
|
|
21881
|
+
&& String(decision.detail || "").includes("same project/provider/route target"),
|
|
20048
21882
|
`kind=${String(decision.kind || "")} overlap=${ensureArray(decision.overlapping_route_names).join(", ")} detail=${String(decision.detail || "")}`,
|
|
20049
21883
|
);
|
|
20050
21884
|
} catch (err) {
|