metheus-governance-mcp-cli 0.2.296 → 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 +2601 -736
- 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 +180 -10
- package/lib/runner-orchestration-decision-bundle.mjs +217 -22
- package/lib/runner-orchestration-entrypoints.mjs +354 -52
- package/lib/runner-orchestration-intent-contracts.mjs +145 -20
- package/lib/runner-orchestration-selected-record-context.mjs +452 -21
- 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 +91 -12
- 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-recovery.mjs +30 -23
- package/lib/runner-runtime.mjs +299 -9
- package/lib/selftest-runner-scenarios.mjs +3982 -357
- 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,21 +4457,42 @@ 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);
|
|
4473
|
+
const authoritativeExecutionContractType = String(
|
|
4474
|
+
decisionBundle.execution_contract_type || "",
|
|
4475
|
+
).trim().toLowerCase();
|
|
3877
4476
|
return String(
|
|
3878
|
-
|
|
4477
|
+
authoritativeExecutionContractType
|
|
3879
4478
|
|| entry.execution_contract_type
|
|
3880
4479
|
|| entry.root_execution_contract_type
|
|
3881
4480
|
|| "",
|
|
3882
4481
|
).trim().toLowerCase();
|
|
3883
4482
|
}
|
|
3884
|
-
|
|
4483
|
+
|
|
3885
4484
|
function runnerRequestPreferredExecutionContractActionable(entryRaw) {
|
|
3886
4485
|
const entry = safeObject(entryRaw);
|
|
3887
4486
|
const decisionBundle = runnerRequestAuthoritativeDecisionBundle(entry);
|
|
3888
|
-
|
|
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) {
|
|
4493
|
+
return decisionBundle.execution_contract_actionable === true;
|
|
4494
|
+
}
|
|
4495
|
+
return entry.execution_contract_actionable === true;
|
|
3889
4496
|
}
|
|
3890
4497
|
|
|
3891
4498
|
function runnerRequestPreferredExecutionContractTargets(entryRaw) {
|
|
@@ -3895,14 +4502,14 @@ function runnerRequestPreferredExecutionContractTargets(entryRaw) {
|
|
|
3895
4502
|
ensureArray(decisionBundle.execution_contract_targets).length
|
|
3896
4503
|
? decisionBundle.execution_contract_targets
|
|
3897
4504
|
: ensureArray(entry.execution_contract_targets).length
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
4505
|
+
? entry.execution_contract_targets
|
|
4506
|
+
: ensureArray(entry.root_execution_contract_targets).length
|
|
4507
|
+
? entry.root_execution_contract_targets
|
|
4508
|
+
: [],
|
|
3902
4509
|
normalizeTelegramMentionUsername,
|
|
3903
4510
|
);
|
|
3904
4511
|
}
|
|
3905
|
-
|
|
4512
|
+
|
|
3906
4513
|
function runnerRequestPreferredNextExpectedResponders(entryRaw) {
|
|
3907
4514
|
const entry = safeObject(entryRaw);
|
|
3908
4515
|
const decisionBundle = runnerRequestAuthoritativeDecisionBundle(entry);
|
|
@@ -3910,32 +4517,38 @@ function runnerRequestPreferredNextExpectedResponders(entryRaw) {
|
|
|
3910
4517
|
ensureArray(decisionBundle.next_expected_responders).length
|
|
3911
4518
|
? decisionBundle.next_expected_responders
|
|
3912
4519
|
: ensureArray(entry.next_expected_responders).length
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
4520
|
+
? entry.next_expected_responders
|
|
4521
|
+
: ensureArray(entry.root_next_expected_responders).length
|
|
4522
|
+
? entry.root_next_expected_responders
|
|
4523
|
+
: [],
|
|
3917
4524
|
normalizeTelegramMentionUsername,
|
|
3918
4525
|
);
|
|
3919
4526
|
}
|
|
3920
|
-
|
|
4527
|
+
|
|
3921
4528
|
function runnerRequestPreferredAuthoritySelectedBotUsernames(entryRaw) {
|
|
3922
4529
|
const entry = safeObject(entryRaw);
|
|
4530
|
+
const decisionBundle = runnerRequestAuthoritativeDecisionBundle(entry);
|
|
4531
|
+
const hasAuthoritativeDecisionBundle = Object.keys(decisionBundle).length > 0;
|
|
3923
4532
|
const normalizedSummaryBot = normalizeTelegramMentionUsername(entry.conversation_summary_bot);
|
|
3924
4533
|
return uniqueOrderedStrings(
|
|
3925
|
-
runnerRequestPreferredNextExpectedResponders(entry).length
|
|
3926
|
-
? runnerRequestPreferredNextExpectedResponders(entry)
|
|
3927
|
-
: runnerRequestPreferredExecutionContractTargets(entry).length
|
|
3928
|
-
? runnerRequestPreferredExecutionContractTargets(entry)
|
|
3929
|
-
: ensureArray(
|
|
3930
|
-
?
|
|
3931
|
-
: ensureArray(
|
|
3932
|
-
?
|
|
3933
|
-
:
|
|
3934
|
-
?
|
|
3935
|
-
:
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
4534
|
+
runnerRequestPreferredNextExpectedResponders(entry).length
|
|
4535
|
+
? runnerRequestPreferredNextExpectedResponders(entry)
|
|
4536
|
+
: runnerRequestPreferredExecutionContractTargets(entry).length
|
|
4537
|
+
? runnerRequestPreferredExecutionContractTargets(entry)
|
|
4538
|
+
: hasAuthoritativeDecisionBundle && ensureArray(decisionBundle.selected_bot_usernames).length
|
|
4539
|
+
? decisionBundle.selected_bot_usernames
|
|
4540
|
+
: hasAuthoritativeDecisionBundle && ensureArray(decisionBundle.initial_responders).length
|
|
4541
|
+
? decisionBundle.initial_responders
|
|
4542
|
+
: ensureArray(entry.selected_bot_usernames).length
|
|
4543
|
+
? entry.selected_bot_usernames
|
|
4544
|
+
: ensureArray(entry.conversation_initial_responders).length
|
|
4545
|
+
? entry.conversation_initial_responders
|
|
4546
|
+
: normalizedSummaryBot
|
|
4547
|
+
? [normalizedSummaryBot]
|
|
4548
|
+
: [],
|
|
4549
|
+
normalizeTelegramMentionUsername,
|
|
4550
|
+
);
|
|
4551
|
+
}
|
|
3939
4552
|
|
|
3940
4553
|
function extractRunnerExplicitSelectedBotUsernamesFromParsed(parsedArchiveRaw) {
|
|
3941
4554
|
const parsedArchive = safeObject(parsedArchiveRaw);
|
|
@@ -3956,6 +4569,8 @@ function runnerRequestAllowsSharedHumanClaimTakeover({
|
|
|
3956
4569
|
canonicalHumanMessageKey = "",
|
|
3957
4570
|
}) {
|
|
3958
4571
|
const entry = safeObject(entryRaw);
|
|
4572
|
+
const decisionBundle = runnerRequestAuthoritativeDecisionBundle(entry);
|
|
4573
|
+
const hasAuthoritativeDecisionBundle = Object.keys(decisionBundle).length > 0;
|
|
3959
4574
|
const normalizedCurrentBotUsername = normalizeTelegramMentionUsername(currentBotUsername);
|
|
3960
4575
|
const normalizedCanonicalHumanMessageKey = String(canonicalHumanMessageKey || "").trim();
|
|
3961
4576
|
if (!normalizedCurrentBotUsername || !normalizedCanonicalHumanMessageKey) {
|
|
@@ -3969,11 +4584,17 @@ function runnerRequestAllowsSharedHumanClaimTakeover({
|
|
|
3969
4584
|
return pendingResponders.includes(normalizedCurrentBotUsername);
|
|
3970
4585
|
}
|
|
3971
4586
|
const directHumanResponders = uniqueOrderedStrings(
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
4587
|
+
hasAuthoritativeDecisionBundle
|
|
4588
|
+
? [
|
|
4589
|
+
...ensureArray(decisionBundle.selected_bot_usernames),
|
|
4590
|
+
...ensureArray(decisionBundle.initial_responders),
|
|
4591
|
+
...ensureArray(decisionBundle.allowed_responders),
|
|
4592
|
+
]
|
|
4593
|
+
: [
|
|
4594
|
+
...ensureArray(entry.selected_bot_usernames),
|
|
4595
|
+
...ensureArray(entry.conversation_initial_responders),
|
|
4596
|
+
...ensureArray(entry.conversation_allowed_responders),
|
|
4597
|
+
],
|
|
3977
4598
|
normalizeTelegramMentionUsername,
|
|
3978
4599
|
);
|
|
3979
4600
|
return directHumanResponders.includes(normalizedCurrentBotUsername);
|
|
@@ -4715,10 +5336,10 @@ function resolveRunnerReplyChainConversationContext(state, normalizedRoute, sele
|
|
|
4715
5336
|
};
|
|
4716
5337
|
}
|
|
4717
5338
|
|
|
4718
|
-
async function resolveRunnerReplyChainConversationContextWithServerFallback({
|
|
4719
|
-
state,
|
|
4720
|
-
normalizedRoute,
|
|
4721
|
-
selectedRecord,
|
|
5339
|
+
async function resolveRunnerReplyChainConversationContextWithServerFallback({
|
|
5340
|
+
state,
|
|
5341
|
+
normalizedRoute,
|
|
5342
|
+
selectedRecord,
|
|
4722
5343
|
runtime,
|
|
4723
5344
|
archiveThreadID = "",
|
|
4724
5345
|
hydrationAttempted = false,
|
|
@@ -4733,14 +5354,70 @@ async function resolveRunnerReplyChainConversationContextWithServerFallback({
|
|
|
4733
5354
|
};
|
|
4734
5355
|
}
|
|
4735
5356
|
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
4736
|
-
const initialReplyToMessageID = intFromRawAllowZero(
|
|
4737
|
-
parsed.replyToMessageID || safeObject(initialContext).replyToMessageID,
|
|
4738
|
-
0,
|
|
4739
|
-
);
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
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,
|
|
4744
5421
|
hydrated: false,
|
|
4745
5422
|
};
|
|
4746
5423
|
}
|
|
@@ -5070,7 +5747,27 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5070
5747
|
requests: backfilled.requests,
|
|
5071
5748
|
};
|
|
5072
5749
|
}
|
|
5073
|
-
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);
|
|
5074
5771
|
let sharedConversationSource = currentMessageID > 0
|
|
5075
5772
|
? pickRunnerSharedConversationSourceRequest(
|
|
5076
5773
|
findRunnerRequestsForMessageID(stateForClaim, normalizedRoute, {
|
|
@@ -5081,10 +5778,10 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5081
5778
|
provisionalRequestKey,
|
|
5082
5779
|
)
|
|
5083
5780
|
: {};
|
|
5084
|
-
if (
|
|
5085
|
-
!Object.keys(sharedConversationSource).length
|
|
5086
|
-
&& currentMessageID > 0
|
|
5087
|
-
&& runtime?.baseURL
|
|
5781
|
+
if (
|
|
5782
|
+
!Object.keys(sharedConversationSource).length
|
|
5783
|
+
&& currentMessageID > 0
|
|
5784
|
+
&& runtime?.baseURL
|
|
5088
5785
|
&& runtime?.token
|
|
5089
5786
|
) {
|
|
5090
5787
|
sharedConversationSource = safeObject(await findServerRunnerConversationSourceRequestForMessageID({
|
|
@@ -5096,68 +5793,332 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5096
5793
|
excludeRequestKey: provisionalRequestKey,
|
|
5097
5794
|
}));
|
|
5098
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
|
+
}
|
|
5099
5806
|
const authorityContext = resolveRunnerHumanCommentAuthorityContext({
|
|
5100
5807
|
normalizedRoute,
|
|
5101
5808
|
selectedRecord,
|
|
5102
|
-
replyChainContext,
|
|
5103
|
-
referencedRequest,
|
|
5809
|
+
replyChainContext,
|
|
5810
|
+
referencedRequest,
|
|
5104
5811
|
sharedConversationSource,
|
|
5105
5812
|
selectedBotUsernames,
|
|
5106
|
-
normalizedSharedHumanIntent,
|
|
5107
|
-
resolvedNormalizedIntent,
|
|
5108
|
-
});
|
|
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
|
+
}
|
|
5109
5890
|
const authoritySource = safeObject(authorityContext.authoritySource);
|
|
5110
|
-
const
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
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) {
|
|
5115
5964
|
return {
|
|
5116
5965
|
ok: false,
|
|
5117
5966
|
reason: "invalid_decision_bundle",
|
|
5118
5967
|
detail: String(decisionBundleValidation.reason || "").trim() || "invalid_decision_bundle",
|
|
5119
5968
|
};
|
|
5120
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
|
+
}
|
|
5121
5980
|
const authoritativeDecisionBundle = decisionBundleValidation.ok === true
|
|
5122
5981
|
? safeObject(decisionBundleValidation.bundle)
|
|
5123
5982
|
: {};
|
|
5124
|
-
const
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
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,
|
|
5133
6002
|
);
|
|
5134
|
-
const authorityReplyChainContext = await buildRunnerAuthorityReplyChainContext({
|
|
5135
|
-
selectedRecord,
|
|
5136
|
-
replyChainContext,
|
|
5137
|
-
authoritySource,
|
|
5138
|
-
runtime,
|
|
5139
|
-
archiveThreadID,
|
|
5140
|
-
});
|
|
5141
|
-
const resolvedConversationID = String(authorityContext.conversationID || "").trim();
|
|
5142
|
-
const preferredNormalizedIntent = String(authorityContext.normalizedIntent || "").trim().toLowerCase();
|
|
5143
|
-
const requestSelectedBotUsernames = ensureArray(authorityContext.selectedBotUsernames);
|
|
5144
6003
|
const effectiveSelectedBotUsernames = uniqueOrderedStrings(
|
|
5145
6004
|
ensureArray(authoritativeDecisionBundle.selected_bot_usernames).length
|
|
5146
6005
|
? authoritativeDecisionBundle.selected_bot_usernames
|
|
5147
6006
|
: requestSelectedBotUsernames,
|
|
5148
6007
|
normalizeTelegramMentionUsername,
|
|
5149
6008
|
);
|
|
5150
|
-
const
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
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 {
|
|
5161
6122
|
ok: false,
|
|
5162
6123
|
reason: "request_already_finalized",
|
|
5163
6124
|
requestKey,
|
|
@@ -5181,6 +6142,15 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5181
6142
|
}
|
|
5182
6143
|
const nowISO = new Date().toISOString();
|
|
5183
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
|
+
);
|
|
5184
6154
|
const preserveExistingCanonicalSourceEnvelope = Boolean(
|
|
5185
6155
|
canonicalHumanMessageKey
|
|
5186
6156
|
&& String(existing.canonical_human_message_key || "").trim() === canonicalHumanMessageKey
|
|
@@ -5192,6 +6162,23 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5192
6162
|
: Object.keys(normalizedAuthoritativeSourceMessageEnvelope).length
|
|
5193
6163
|
? normalizedAuthoritativeSourceMessageEnvelope
|
|
5194
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;
|
|
5195
6182
|
const decisionConversationParticipants = uniqueOrderedStrings(
|
|
5196
6183
|
ensureArray(authoritativeDecisionBundle.participants),
|
|
5197
6184
|
normalizeTelegramMentionUsername,
|
|
@@ -5205,33 +6192,114 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5205
6192
|
normalizeTelegramMentionUsername,
|
|
5206
6193
|
);
|
|
5207
6194
|
const hasAuthoritativeConversationDecision = Object.keys(authoritativeDecisionBundle).length > 0;
|
|
5208
|
-
const
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
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,
|
|
5223
6285
|
reply_chain_context: authorityReplyChainContext,
|
|
5224
6286
|
selected_bot_usernames: effectiveSelectedBotUsernames,
|
|
5225
|
-
authoritative_decision_bundle:
|
|
5226
|
-
|
|
5227
|
-
|
|
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(),
|
|
5228
6296
|
conversation_intent_mode: String(
|
|
5229
6297
|
(hasAuthoritativeConversationDecision
|
|
5230
6298
|
? authoritativeDecisionBundle.conversation_intent_mode
|
|
5231
6299
|
: "")
|
|
5232
6300
|
|| normalizedSharedHumanIntent.intentMode
|
|
5233
6301
|
|| existing.conversation_intent_mode
|
|
5234
|
-
||
|
|
6302
|
+
|| effectiveAuthoritySource.conversation_intent_mode
|
|
5235
6303
|
|| "",
|
|
5236
6304
|
).trim().toLowerCase(),
|
|
5237
6305
|
conversation_lead_bot: normalizeTelegramMentionUsername(
|
|
@@ -5240,7 +6308,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5240
6308
|
: "")
|
|
5241
6309
|
|| normalizedSharedHumanIntent.leadBotSelector
|
|
5242
6310
|
|| existing.conversation_lead_bot
|
|
5243
|
-
||
|
|
6311
|
+
|| effectiveAuthoritySource.conversation_lead_bot,
|
|
5244
6312
|
),
|
|
5245
6313
|
conversation_summary_bot: normalizeTelegramMentionUsername(
|
|
5246
6314
|
(hasAuthoritativeConversationDecision
|
|
@@ -5248,7 +6316,7 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5248
6316
|
: "")
|
|
5249
6317
|
|| normalizedSharedHumanIntent.summaryBotSelector
|
|
5250
6318
|
|| existing.conversation_summary_bot
|
|
5251
|
-
||
|
|
6319
|
+
|| effectiveAuthoritySource.conversation_summary_bot,
|
|
5252
6320
|
),
|
|
5253
6321
|
conversation_participants: uniqueOrderedStrings(
|
|
5254
6322
|
hasAuthoritativeConversationDecision && decisionConversationParticipants.length
|
|
@@ -5257,10 +6325,10 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5257
6325
|
? normalizedSharedHumanIntent.participantSelectors
|
|
5258
6326
|
: ensureArray(existing.conversation_participants).length
|
|
5259
6327
|
? existing.conversation_participants
|
|
5260
|
-
: ensureArray(
|
|
5261
|
-
?
|
|
5262
|
-
: [],
|
|
5263
|
-
normalizeTelegramMentionUsername,
|
|
6328
|
+
: ensureArray(effectiveAuthoritySource.conversation_participants).length
|
|
6329
|
+
? effectiveAuthoritySource.conversation_participants
|
|
6330
|
+
: [],
|
|
6331
|
+
normalizeTelegramMentionUsername,
|
|
5264
6332
|
),
|
|
5265
6333
|
conversation_initial_responders: uniqueOrderedStrings(
|
|
5266
6334
|
hasAuthoritativeConversationDecision && decisionInitialResponders.length
|
|
@@ -5269,10 +6337,10 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5269
6337
|
? normalizedSharedHumanIntent.initialResponderSelectors
|
|
5270
6338
|
: ensureArray(existing.conversation_initial_responders).length
|
|
5271
6339
|
? existing.conversation_initial_responders
|
|
5272
|
-
: ensureArray(
|
|
5273
|
-
?
|
|
5274
|
-
: [],
|
|
5275
|
-
normalizeTelegramMentionUsername,
|
|
6340
|
+
: ensureArray(effectiveAuthoritySource.conversation_initial_responders).length
|
|
6341
|
+
? effectiveAuthoritySource.conversation_initial_responders
|
|
6342
|
+
: [],
|
|
6343
|
+
normalizeTelegramMentionUsername,
|
|
5276
6344
|
),
|
|
5277
6345
|
conversation_allowed_responders: uniqueOrderedStrings(
|
|
5278
6346
|
hasAuthoritativeConversationDecision && decisionAllowedResponders.length
|
|
@@ -5281,9 +6349,9 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5281
6349
|
? normalizedSharedHumanIntent.allowedResponderSelectors
|
|
5282
6350
|
: ensureArray(existing.conversation_allowed_responders).length
|
|
5283
6351
|
? existing.conversation_allowed_responders
|
|
5284
|
-
: ensureArray(
|
|
5285
|
-
?
|
|
5286
|
-
: [],
|
|
6352
|
+
: ensureArray(effectiveAuthoritySource.conversation_allowed_responders).length
|
|
6353
|
+
? effectiveAuthoritySource.conversation_allowed_responders
|
|
6354
|
+
: [],
|
|
5287
6355
|
normalizeTelegramMentionUsername,
|
|
5288
6356
|
),
|
|
5289
6357
|
conversation_allow_bot_to_bot: (hasAuthoritativeConversationDecision
|
|
@@ -5291,14 +6359,14 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5291
6359
|
: false)
|
|
5292
6360
|
|| normalizedSharedHumanIntent.allowBotToBot === true
|
|
5293
6361
|
|| existing.conversation_allow_bot_to_bot === true
|
|
5294
|
-
||
|
|
6362
|
+
|| effectiveAuthoritySource.conversation_allow_bot_to_bot === true,
|
|
5295
6363
|
conversation_reply_expectation: String(
|
|
5296
6364
|
(hasAuthoritativeConversationDecision
|
|
5297
6365
|
? authoritativeDecisionBundle.conversation_reply_expectation
|
|
5298
6366
|
: "")
|
|
5299
6367
|
|| normalizedSharedHumanIntent.replyExpectation
|
|
5300
6368
|
|| existing.conversation_reply_expectation
|
|
5301
|
-
||
|
|
6369
|
+
|| effectiveAuthoritySource.conversation_reply_expectation
|
|
5302
6370
|
|| "",
|
|
5303
6371
|
).trim().toLowerCase(),
|
|
5304
6372
|
execution_contract_type: String(
|
|
@@ -5306,20 +6374,20 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5306
6374
|
? authoritativeDecisionBundle.execution_contract_type
|
|
5307
6375
|
: "")
|
|
5308
6376
|
|| runnerRequestPreferredExecutionContractType(existing)
|
|
5309
|
-
|| runnerRequestPreferredExecutionContractType(
|
|
6377
|
+
|| runnerRequestPreferredExecutionContractType(effectiveAuthoritySource)
|
|
5310
6378
|
|| "",
|
|
5311
6379
|
).trim().toLowerCase(),
|
|
5312
6380
|
execution_contract_actionable: (hasAuthoritativeConversationDecision
|
|
5313
6381
|
? authoritativeDecisionBundle.execution_contract_actionable === true
|
|
5314
6382
|
: false)
|
|
5315
6383
|
|| runnerRequestPreferredExecutionContractActionable(existing)
|
|
5316
|
-
|| runnerRequestPreferredExecutionContractActionable(
|
|
6384
|
+
|| runnerRequestPreferredExecutionContractActionable(effectiveAuthoritySource),
|
|
5317
6385
|
execution_contract_targets: uniqueOrderedStrings(
|
|
5318
6386
|
hasAuthoritativeConversationDecision && ensureArray(authoritativeDecisionBundle.execution_contract_targets).length
|
|
5319
6387
|
? authoritativeDecisionBundle.execution_contract_targets
|
|
5320
6388
|
: runnerRequestPreferredExecutionContractTargets(existing).length
|
|
5321
6389
|
? runnerRequestPreferredExecutionContractTargets(existing)
|
|
5322
|
-
: runnerRequestPreferredExecutionContractTargets(
|
|
6390
|
+
: runnerRequestPreferredExecutionContractTargets(effectiveAuthoritySource),
|
|
5323
6391
|
normalizeTelegramMentionUsername,
|
|
5324
6392
|
),
|
|
5325
6393
|
next_expected_responders: uniqueOrderedStrings(
|
|
@@ -5327,31 +6395,77 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5327
6395
|
? authoritativeDecisionBundle.next_expected_responders
|
|
5328
6396
|
: runnerRequestPreferredNextExpectedResponders(existing).length
|
|
5329
6397
|
? runnerRequestPreferredNextExpectedResponders(existing)
|
|
5330
|
-
: runnerRequestPreferredNextExpectedResponders(
|
|
6398
|
+
: runnerRequestPreferredNextExpectedResponders(effectiveAuthoritySource),
|
|
5331
6399
|
normalizeTelegramMentionUsername,
|
|
5332
|
-
),
|
|
5333
|
-
normalized_intent: String(preferredNormalizedIntent || existing.normalized_intent || "").trim().toLowerCase(),
|
|
6400
|
+
),
|
|
6401
|
+
normalized_intent: String(preferredNormalizedIntent || existing.normalized_intent || "").trim().toLowerCase(),
|
|
5334
6402
|
status: "claimed",
|
|
5335
|
-
claimed_by_route: String(routeKey || "").trim(),
|
|
5336
|
-
claimed_at: firstNonEmptyString([existing.claimed_at, nowISO]) || nowISO,
|
|
5337
|
-
root_work_item_id: String(existing.root_work_item_id ||
|
|
5338
|
-
root_work_item_title: String(existing.root_work_item_title ||
|
|
5339
|
-
root_work_item_status: normalizeRunnerWorkItemStatus(
|
|
5340
|
-
existing.root_work_item_status ||
|
|
5341
|
-
),
|
|
5342
|
-
root_thread_id: String(existing.root_thread_id ||
|
|
5343
|
-
root_work_item_created_at: firstNonEmptyString([
|
|
5344
|
-
existing.root_work_item_created_at,
|
|
5345
|
-
|
|
5346
|
-
]),
|
|
5347
|
-
root_work_item_last_error: String(
|
|
5348
|
-
existing.root_work_item_last_error ||
|
|
5349
|
-
).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(),
|
|
5350
6418
|
last_comment_id: String(selectedRecord?.id || "").trim(),
|
|
5351
6419
|
last_comment_kind: commentKind,
|
|
5352
|
-
last_source_message_id: intFromRawAllowZero(parsed.messageID, 0) || undefined,
|
|
5353
|
-
last_source_message_thread_id: intFromRawAllowZero(parsed.messageThreadID, 0) || undefined,
|
|
5354
|
-
});
|
|
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
|
+
}
|
|
5355
6469
|
const { consumedComments: nextConsumedComments } = upsertRunnerConsumedComment(stateForClaim, selectedRecord?.id, {
|
|
5356
6470
|
project_id: String(normalizedRoute?.projectID || "").trim(),
|
|
5357
6471
|
provider: String(normalizedRoute?.provider || "").trim(),
|
|
@@ -5365,18 +6479,55 @@ async function claimRunnerRequestForHumanComment({
|
|
|
5365
6479
|
comment_kind: commentKind,
|
|
5366
6480
|
request_status: "claimed",
|
|
5367
6481
|
});
|
|
5368
|
-
saveBotRunnerState({
|
|
5369
|
-
routes: stateForClaim.routes,
|
|
5370
|
-
sharedInboxes: stateForClaim.sharedInboxes || stateForClaim.shared_inboxes,
|
|
5371
|
-
excludedComments: stateForClaim.excludedComments || stateForClaim.excluded_comments,
|
|
5372
|
-
requests: nextRequests,
|
|
5373
|
-
consumedComments: nextConsumedComments,
|
|
5374
|
-
});
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
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
|
+
};
|
|
5380
6531
|
}
|
|
5381
6532
|
|
|
5382
6533
|
function runnerRequestRequiresActionableContract(requestRaw) {
|
|
@@ -6391,6 +7542,9 @@ function markRunnerRequestLifecycle({
|
|
|
6391
7542
|
const sourceMessageEnvelope = Object.keys(normalizedAuthoritativeSourceMessageEnvelope).length
|
|
6392
7543
|
? normalizedAuthoritativeSourceMessageEnvelope
|
|
6393
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;
|
|
6394
7548
|
const normalizedDecisionBundle = normalizeRunnerConversationDecisionBundle(decisionBundle);
|
|
6395
7549
|
const resolvedDecisionBundleValidation = Object.keys(safeObject(decisionBundle)).length > 0
|
|
6396
7550
|
? validateRunnerConversationDecisionBundle(normalizedDecisionBundle)
|
|
@@ -6400,9 +7554,60 @@ function markRunnerRequestLifecycle({
|
|
|
6400
7554
|
reason: String(decisionBundleValidationReason || "").trim(),
|
|
6401
7555
|
bundle: {},
|
|
6402
7556
|
};
|
|
6403
|
-
const
|
|
7557
|
+
const existingAuthoritativeDecisionBundle = runnerRequestAuthoritativeDecisionBundle(existing);
|
|
7558
|
+
const incomingDecisionBundle = resolvedDecisionBundleValidation.ok === true
|
|
6404
7559
|
? safeObject(resolvedDecisionBundleValidation.bundle)
|
|
6405
|
-
:
|
|
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();
|
|
6406
7611
|
const effectiveReplyToMessageID = intFromRawAllowZero(replyToMessageID, 0);
|
|
6407
7612
|
const lastReplyMessageEnvelope = buildTelegramBotReplyEnvelope({
|
|
6408
7613
|
sourceEnvelope: sourceMessageEnvelope,
|
|
@@ -6416,7 +7621,7 @@ function markRunnerRequestLifecycle({
|
|
|
6416
7621
|
const normalizedDeliveryStatus = String(deliveryStatus || "").trim().toLowerCase();
|
|
6417
7622
|
const persistSuccessfulReplyEnvelope = ["delivered", "dry_run"].includes(normalizedDeliveryStatus)
|
|
6418
7623
|
&& intFromRawAllowZero(lastReplyMessageEnvelope.message_id, 0) > 0;
|
|
6419
|
-
const attemptedDeliveryEnvelope = buildTelegramBotReplyEnvelope({
|
|
7624
|
+
const attemptedDeliveryEnvelope = buildTelegramBotReplyEnvelope({
|
|
6420
7625
|
sourceEnvelope: sourceMessageEnvelope,
|
|
6421
7626
|
chatID: existing.chat_id,
|
|
6422
7627
|
messageThreadID: lastReplyMessageThreadID,
|
|
@@ -6432,9 +7637,13 @@ function markRunnerRequestLifecycle({
|
|
|
6432
7637
|
|| intFromRawAllowZero(replyToMessageID, 0) > 0
|
|
6433
7638
|
|| intFromRawAllowZero(lastReplyMessageThreadID, 0) > 0
|
|
6434
7639
|
);
|
|
7640
|
+
const continuationDecisionBundle = Object.keys(incomingDecisionBundle).length > 0
|
|
7641
|
+
? incomingDecisionBundle
|
|
7642
|
+
: safeObject(existing.last_reply_decision_bundle);
|
|
6435
7643
|
const rootEffectiveExecutionContractTargets = uniqueOrderedStrings(
|
|
6436
7644
|
[
|
|
6437
7645
|
...ensureArray(authoritativeDecisionBundle.execution_contract_targets),
|
|
7646
|
+
...ensureArray(continuationDecisionBundle.execution_contract_targets),
|
|
6438
7647
|
...ensureArray(executionContractTargets),
|
|
6439
7648
|
...ensureArray(normalizedExecutionContractTargets),
|
|
6440
7649
|
...ensureArray(responseContractValidationTargets),
|
|
@@ -6444,6 +7653,7 @@ function markRunnerRequestLifecycle({
|
|
|
6444
7653
|
const rootEffectiveNextExpectedResponders = uniqueOrderedStrings(
|
|
6445
7654
|
[
|
|
6446
7655
|
...ensureArray(authoritativeDecisionBundle.next_expected_responders),
|
|
7656
|
+
...ensureArray(continuationDecisionBundle.next_expected_responders),
|
|
6447
7657
|
...ensureArray(nextExpectedResponders),
|
|
6448
7658
|
...ensureArray(normalizedExecutionNextResponders),
|
|
6449
7659
|
...ensureArray(responseContractValidationTargets),
|
|
@@ -6454,11 +7664,15 @@ function markRunnerRequestLifecycle({
|
|
|
6454
7664
|
[
|
|
6455
7665
|
...ensureArray(authoritativeDecisionBundle.execution_contract_targets).length
|
|
6456
7666
|
? ensureArray(authoritativeDecisionBundle.execution_contract_targets)
|
|
7667
|
+
: ensureArray(continuationDecisionBundle.execution_contract_targets).length
|
|
7668
|
+
? ensureArray(continuationDecisionBundle.execution_contract_targets)
|
|
6457
7669
|
: ensureArray(executionContractTargets).length
|
|
6458
7670
|
? ensureArray(executionContractTargets)
|
|
6459
7671
|
: ensureArray(existing.execution_contract_targets),
|
|
6460
7672
|
...ensureArray(authoritativeDecisionBundle.next_expected_responders).length
|
|
6461
7673
|
? ensureArray(authoritativeDecisionBundle.next_expected_responders)
|
|
7674
|
+
: ensureArray(continuationDecisionBundle.next_expected_responders).length
|
|
7675
|
+
? ensureArray(continuationDecisionBundle.next_expected_responders)
|
|
6462
7676
|
: ensureArray(nextExpectedResponders).length
|
|
6463
7677
|
? ensureArray(nextExpectedResponders)
|
|
6464
7678
|
: ensureArray(existing.next_expected_responders),
|
|
@@ -6467,13 +7681,20 @@ function markRunnerRequestLifecycle({
|
|
|
6467
7681
|
).filter((selector) => selector && selector !== normalizedCurrentBotSelector);
|
|
6468
7682
|
const nextExecutionContractType = String(
|
|
6469
7683
|
authoritativeDecisionBundle.execution_contract_type
|
|
7684
|
+
|| continuationDecisionBundle.execution_contract_type
|
|
6470
7685
|
|| executionContractType
|
|
6471
7686
|
|| existing.execution_contract_type
|
|
6472
7687
|
|| "",
|
|
6473
7688
|
).trim().toLowerCase();
|
|
7689
|
+
const shouldPromoteRootContinuationState = isRootHumanComment || existingHasImmutableRootAuthority;
|
|
6474
7690
|
const normalizedOutcome = String(outcome || "").trim().toLowerCase();
|
|
6475
7691
|
const normalizedFailureReplyClassification = String(failureReplyClassification || "").trim().toLowerCase();
|
|
6476
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
|
+
);
|
|
6477
7698
|
const shouldPersistReplyAnchor = (
|
|
6478
7699
|
aiReplyGenerated === true
|
|
6479
7700
|
|| intFromRawAllowZero(lastReplyMessageID, 0) > 0
|
|
@@ -6488,6 +7709,7 @@ function markRunnerRequestLifecycle({
|
|
|
6488
7709
|
: continuationSelectors.length > 0;
|
|
6489
7710
|
const shouldRemainRunningAfterSkip = normalizedOutcome === "skipped"
|
|
6490
7711
|
&& parsedKind === "bot_reply"
|
|
7712
|
+
&& !isTerminalStaleReplyAnchorSkip
|
|
6491
7713
|
&& authoritativeDecisionBundle.should_close_after_reply !== true
|
|
6492
7714
|
&& (
|
|
6493
7715
|
nextExecutionContractType === "delegation"
|
|
@@ -6548,25 +7770,17 @@ function markRunnerRequestLifecycle({
|
|
|
6548
7770
|
return normalizeRunnerRequestStatus(existing.status);
|
|
6549
7771
|
})();
|
|
6550
7772
|
const nowISO = new Date().toISOString();
|
|
6551
|
-
const commentKind = String(parsed.kind || "").trim().toLowerCase();
|
|
6552
|
-
const isRootHumanComment = ["telegram_message", "telegram_edited_message"].includes(commentKind);
|
|
6553
|
-
const isFollowupComment = !isRootHumanComment;
|
|
6554
7773
|
const patch = {
|
|
6555
7774
|
authoritative_decision_bundle: Object.keys(authoritativeDecisionBundle).length > 0
|
|
6556
7775
|
? authoritativeDecisionBundle
|
|
6557
7776
|
: safeObject(existing.authoritative_decision_bundle),
|
|
6558
|
-
decision_bundle_validation_status:
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
resolvedDecisionBundleValidation.reason
|
|
6566
|
-
|| decisionBundleValidationReason
|
|
6567
|
-
|| existing.decision_bundle_validation_reason
|
|
6568
|
-
|| "",
|
|
6569
|
-
).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,
|
|
6570
7784
|
conversation_id: conversationID,
|
|
6571
7785
|
conversation_participants: uniqueOrderedStrings(
|
|
6572
7786
|
ensureArray(authoritativeDecisionBundle.participants).length
|
|
@@ -6621,7 +7835,7 @@ function markRunnerRequestLifecycle({
|
|
|
6621
7835
|
execution_contract_targets: uniqueOrderedStrings(
|
|
6622
7836
|
ensureArray(authoritativeDecisionBundle.execution_contract_targets).length
|
|
6623
7837
|
? authoritativeDecisionBundle.execution_contract_targets
|
|
6624
|
-
:
|
|
7838
|
+
: shouldPromoteRootContinuationState && rootEffectiveExecutionContractTargets.length
|
|
6625
7839
|
? rootEffectiveExecutionContractTargets
|
|
6626
7840
|
: ensureArray(executionContractTargets).length
|
|
6627
7841
|
? executionContractTargets
|
|
@@ -6631,7 +7845,7 @@ function markRunnerRequestLifecycle({
|
|
|
6631
7845
|
next_expected_responders: uniqueOrderedStrings(
|
|
6632
7846
|
ensureArray(authoritativeDecisionBundle.next_expected_responders).length
|
|
6633
7847
|
? authoritativeDecisionBundle.next_expected_responders
|
|
6634
|
-
:
|
|
7848
|
+
: shouldPromoteRootContinuationState && rootEffectiveNextExpectedResponders.length
|
|
6635
7849
|
? rootEffectiveNextExpectedResponders
|
|
6636
7850
|
: ensureArray(nextExpectedResponders).length
|
|
6637
7851
|
? nextExpectedResponders
|
|
@@ -6667,24 +7881,24 @@ function markRunnerRequestLifecycle({
|
|
|
6667
7881
|
? aiReplyPreview || existing.followup_ai_reply_preview || ""
|
|
6668
7882
|
: existing.followup_ai_reply_preview || "",
|
|
6669
7883
|
).trim(),
|
|
6670
|
-
root_execution_contract_type: String(
|
|
6671
|
-
|
|
6672
|
-
? nextExecutionContractType
|
|
6673
|
-
: existing.root_execution_contract_type || existing.execution_contract_type || "",
|
|
6674
|
-
).trim().toLowerCase(),
|
|
6675
|
-
root_execution_contract_targets: uniqueOrderedStrings(
|
|
6676
|
-
|
|
6677
|
-
? rootEffectiveExecutionContractTargets
|
|
6678
|
-
: ensureArray(existing.root_execution_contract_targets).length
|
|
6679
|
-
? 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
|
|
6680
7894
|
: existing.execution_contract_targets,
|
|
6681
7895
|
normalizeTelegramMentionUsername,
|
|
6682
7896
|
),
|
|
6683
|
-
root_next_expected_responders: uniqueOrderedStrings(
|
|
6684
|
-
|
|
6685
|
-
? rootEffectiveNextExpectedResponders
|
|
6686
|
-
: ensureArray(existing.root_next_expected_responders).length
|
|
6687
|
-
? 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
|
|
6688
7902
|
: existing.next_expected_responders,
|
|
6689
7903
|
normalizeTelegramMentionUsername,
|
|
6690
7904
|
),
|
|
@@ -6978,18 +8192,36 @@ function cleanupBotRunnerRequestState({
|
|
|
6978
8192
|
}
|
|
6979
8193
|
const expiresAtMs = Date.parse(String(session.expires_at || "").trim());
|
|
6980
8194
|
const expired = Number.isFinite(expiresAtMs) && expiresAtMs <= nowMs;
|
|
6981
|
-
|
|
6982
|
-
String(entry.project_id || "").trim() === String(candidateRoute.projectID || "").trim()
|
|
6983
|
-
&& String(entry.provider || "").trim() === String(candidateRoute.provider || "").trim()
|
|
6984
|
-
&& String(entry.conversation_id || "").trim() === String(conversationID || "").trim()
|
|
6985
|
-
&& isActiveRunnerRequestStatus(entry.status)
|
|
6986
|
-
));
|
|
6987
|
-
|
|
6988
|
-
.
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
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
|
+
}
|
|
6993
8225
|
const closedReason = expired ? "expired_session" : "orphaned_open_session";
|
|
6994
8226
|
conversationSessions[conversationID] = {
|
|
6995
8227
|
...session,
|
|
@@ -7096,18 +8328,30 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
|
|
|
7096
8328
|
merged[fieldName] = localValue;
|
|
7097
8329
|
}
|
|
7098
8330
|
};
|
|
7099
|
-
const preserveLocalArrayWhenServerEmpty = (fieldName) => {
|
|
7100
|
-
const serverValues = ensureArray(serverEntry[fieldName]).filter(Boolean);
|
|
7101
|
-
const localValues = ensureArray(localEntry[fieldName]).filter(Boolean);
|
|
7102
|
-
if (serverValues.length === 0 && localValues.length > 0) {
|
|
7103
|
-
merged[fieldName] = localValues;
|
|
7104
|
-
}
|
|
7105
|
-
};
|
|
7106
|
-
const
|
|
7107
|
-
const serverValue =
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
8331
|
+
const preserveLocalArrayWhenServerEmpty = (fieldName) => {
|
|
8332
|
+
const serverValues = ensureArray(serverEntry[fieldName]).filter(Boolean);
|
|
8333
|
+
const localValues = ensureArray(localEntry[fieldName]).filter(Boolean);
|
|
8334
|
+
if (serverValues.length === 0 && localValues.length > 0) {
|
|
8335
|
+
merged[fieldName] = localValues;
|
|
8336
|
+
}
|
|
8337
|
+
};
|
|
8338
|
+
const clearMergedStringWhenServerBlank = (fieldName) => {
|
|
8339
|
+
const serverValue = String(serverEntry[fieldName] || "").trim();
|
|
8340
|
+
if (!serverValue) {
|
|
8341
|
+
delete merged[fieldName];
|
|
8342
|
+
}
|
|
8343
|
+
};
|
|
8344
|
+
const clearMergedArrayWhenServerEmpty = (fieldName) => {
|
|
8345
|
+
const serverValues = ensureArray(serverEntry[fieldName]).filter(Boolean);
|
|
8346
|
+
if (serverValues.length === 0) {
|
|
8347
|
+
delete merged[fieldName];
|
|
8348
|
+
}
|
|
8349
|
+
};
|
|
8350
|
+
const preserveLocalObjectWhenServerBlank = (fieldName) => {
|
|
8351
|
+
const serverValue = safeObject(serverEntry[fieldName]);
|
|
8352
|
+
const localValue = safeObject(localEntry[fieldName]);
|
|
8353
|
+
if (!Object.keys(serverValue).length && Object.keys(localValue).length) {
|
|
8354
|
+
merged[fieldName] = localValue;
|
|
7111
8355
|
}
|
|
7112
8356
|
};
|
|
7113
8357
|
preserveLocalNumberWhenServerMissing("source_message_id");
|
|
@@ -7126,6 +8370,8 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
|
|
|
7126
8370
|
preserveLocalStringWhenServerBlank("conversation_reply_expectation");
|
|
7127
8371
|
preserveLocalStringWhenServerBlank("decision_bundle_validation_status");
|
|
7128
8372
|
preserveLocalStringWhenServerBlank("decision_bundle_validation_reason");
|
|
8373
|
+
preserveLocalStringWhenServerBlank("last_reply_decision_bundle_validation_status");
|
|
8374
|
+
preserveLocalStringWhenServerBlank("last_reply_decision_bundle_validation_reason");
|
|
7129
8375
|
preserveLocalStringWhenServerBlank("execution_contract_type");
|
|
7130
8376
|
preserveLocalStringWhenServerBlank("normalized_execution_contract_type");
|
|
7131
8377
|
preserveLocalStringWhenServerBlank("ai_reply_generated_at");
|
|
@@ -7136,16 +8382,13 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
|
|
|
7136
8382
|
preserveLocalStringWhenServerBlank("root_thread_id");
|
|
7137
8383
|
preserveLocalStringWhenServerBlank("root_work_item_created_at");
|
|
7138
8384
|
preserveLocalStringWhenServerBlank("root_work_item_last_error");
|
|
7139
|
-
preserveLocalStringWhenServerBlank("
|
|
7140
|
-
preserveLocalStringWhenServerBlank("
|
|
7141
|
-
preserveLocalStringWhenServerBlank("
|
|
7142
|
-
preserveLocalStringWhenServerBlank("
|
|
7143
|
-
preserveLocalStringWhenServerBlank("
|
|
7144
|
-
preserveLocalStringWhenServerBlank("
|
|
7145
|
-
preserveLocalStringWhenServerBlank("
|
|
7146
|
-
preserveLocalStringWhenServerBlank("followup_response_contract_validation_status");
|
|
7147
|
-
preserveLocalStringWhenServerBlank("followup_response_contract_validation_reason");
|
|
7148
|
-
preserveLocalStringWhenServerBlank("followup_assignment_validation_status");
|
|
8385
|
+
preserveLocalStringWhenServerBlank("root_ai_reply_preview");
|
|
8386
|
+
preserveLocalStringWhenServerBlank("root_response_contract_validation_status");
|
|
8387
|
+
preserveLocalStringWhenServerBlank("root_response_contract_validation_reason");
|
|
8388
|
+
preserveLocalStringWhenServerBlank("followup_ai_reply_preview");
|
|
8389
|
+
preserveLocalStringWhenServerBlank("followup_response_contract_validation_status");
|
|
8390
|
+
preserveLocalStringWhenServerBlank("followup_response_contract_validation_reason");
|
|
8391
|
+
preserveLocalStringWhenServerBlank("followup_assignment_validation_status");
|
|
7149
8392
|
preserveLocalStringWhenServerBlank("followup_assignment_validation_reason");
|
|
7150
8393
|
preserveLocalStringWhenServerBlank("followup_delivery_status");
|
|
7151
8394
|
preserveLocalStringWhenServerBlank("followup_archive_status");
|
|
@@ -7154,24 +8397,24 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
|
|
|
7154
8397
|
preserveLocalArrayWhenServerEmpty("conversation_participants");
|
|
7155
8398
|
preserveLocalArrayWhenServerEmpty("conversation_initial_responders");
|
|
7156
8399
|
preserveLocalArrayWhenServerEmpty("conversation_allowed_responders");
|
|
7157
|
-
preserveLocalArrayWhenServerEmpty("
|
|
7158
|
-
preserveLocalArrayWhenServerEmpty("next_expected_responders");
|
|
7159
|
-
preserveLocalArrayWhenServerEmpty("normalized_execution_contract_targets");
|
|
7160
|
-
preserveLocalArrayWhenServerEmpty("normalized_execution_next_responders");
|
|
7161
|
-
preserveLocalArrayWhenServerEmpty("root_execution_contract_targets");
|
|
7162
|
-
preserveLocalArrayWhenServerEmpty("root_next_expected_responders");
|
|
7163
|
-
preserveLocalArrayWhenServerEmpty("root_response_contract_validation_targets");
|
|
7164
|
-
preserveLocalArrayWhenServerEmpty("followup_execution_contract_targets");
|
|
7165
|
-
preserveLocalArrayWhenServerEmpty("followup_next_expected_responders");
|
|
7166
|
-
preserveLocalArrayWhenServerEmpty("followup_normalized_execution_contract_targets");
|
|
7167
|
-
preserveLocalArrayWhenServerEmpty("followup_normalized_execution_next_responders");
|
|
8400
|
+
preserveLocalArrayWhenServerEmpty("root_response_contract_validation_targets");
|
|
7168
8401
|
preserveLocalArrayWhenServerEmpty("followup_response_contract_validation_targets");
|
|
7169
8402
|
preserveLocalArrayWhenServerEmpty("followup_assignment_validation_modes");
|
|
7170
8403
|
preserveLocalObjectWhenServerBlank("authoritative_decision_bundle");
|
|
8404
|
+
preserveLocalObjectWhenServerBlank("last_reply_decision_bundle");
|
|
7171
8405
|
preserveLocalObjectWhenServerBlank("reply_chain_context");
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
8406
|
+
clearMergedStringWhenServerBlank("root_execution_contract_type");
|
|
8407
|
+
clearMergedArrayWhenServerEmpty("root_execution_contract_targets");
|
|
8408
|
+
clearMergedArrayWhenServerEmpty("root_next_expected_responders");
|
|
8409
|
+
clearMergedStringWhenServerBlank("followup_execution_contract_type");
|
|
8410
|
+
clearMergedStringWhenServerBlank("followup_normalized_execution_contract_type");
|
|
8411
|
+
clearMergedArrayWhenServerEmpty("followup_execution_contract_targets");
|
|
8412
|
+
clearMergedArrayWhenServerEmpty("followup_next_expected_responders");
|
|
8413
|
+
clearMergedArrayWhenServerEmpty("followup_normalized_execution_contract_targets");
|
|
8414
|
+
clearMergedArrayWhenServerEmpty("followup_normalized_execution_next_responders");
|
|
8415
|
+
if (serverEntry.conversation_allow_bot_to_bot !== true && localEntry.conversation_allow_bot_to_bot === true) {
|
|
8416
|
+
merged.conversation_allow_bot_to_bot = true;
|
|
8417
|
+
}
|
|
7175
8418
|
if (serverEntry.execution_contract_actionable !== true && localEntry.execution_contract_actionable === true) {
|
|
7176
8419
|
merged.execution_contract_actionable = true;
|
|
7177
8420
|
}
|
|
@@ -7729,31 +8972,60 @@ function scanExternalProjectArtifacts({
|
|
|
7729
8972
|
};
|
|
7730
8973
|
}
|
|
7731
8974
|
|
|
7732
|
-
function normalizeRunnerRouteIdentityText(rawValue) {
|
|
7733
|
-
return String(rawValue || "").trim().toLowerCase();
|
|
7734
|
-
}
|
|
7735
|
-
|
|
7736
|
-
function
|
|
7737
|
-
const
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
}
|
|
7746
|
-
|
|
7747
|
-
function
|
|
7748
|
-
const normalized = normalizeRunnerRoute(route);
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
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
|
+
}
|
|
7757
9029
|
|
|
7758
9030
|
function findRunnerRouteLogicalConflicts(route, config) {
|
|
7759
9031
|
const normalizedRoute = normalizeRunnerRoute(route);
|
|
@@ -7873,14 +9145,23 @@ async function runTasksWithConcurrencyLimit(items, limit, worker) {
|
|
|
7873
9145
|
return output;
|
|
7874
9146
|
}
|
|
7875
9147
|
|
|
7876
|
-
function runnerRouteSchedulingGroupKey(route) {
|
|
7877
|
-
const normalized = normalizeRunnerRoute(route);
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
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
|
+
}
|
|
7884
9165
|
|
|
7885
9166
|
function groupRunnerRoutesBySchedulingTarget(routes) {
|
|
7886
9167
|
const groups = [];
|
|
@@ -7914,13 +9195,14 @@ function normalizeBotRole(rawValue) {
|
|
|
7914
9195
|
return "";
|
|
7915
9196
|
}
|
|
7916
9197
|
|
|
7917
|
-
function normalizeRunnerRoute(rawRoute, overrides = {}) {
|
|
7918
|
-
const route = {
|
|
7919
|
-
...safeObject(rawRoute),
|
|
7920
|
-
...safeObject(overrides),
|
|
7921
|
-
};
|
|
7922
|
-
const
|
|
7923
|
-
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);
|
|
7924
9206
|
const role = normalizeBotRole(route.role || route.bot_role || route.botRole);
|
|
7925
9207
|
const roleProfile = normalizeRunnerRoleProfileName(route.role_profile || route.roleProfile || route.execution_profile || route.executionProfile);
|
|
7926
9208
|
const name = String(route.name || "").trim();
|
|
@@ -7938,11 +9220,12 @@ function normalizeRunnerRoute(rawRoute, overrides = {}) {
|
|
|
7938
9220
|
route.dry_run_delivery ?? route.dryRunDelivery ?? process.env.METHEUS_BOT_DELIVERY_DRY_RUN,
|
|
7939
9221
|
false,
|
|
7940
9222
|
);
|
|
7941
|
-
return {
|
|
7942
|
-
name,
|
|
7943
|
-
enabled: boolFromRaw(route.enabled, true),
|
|
7944
|
-
|
|
7945
|
-
|
|
9223
|
+
return {
|
|
9224
|
+
name,
|
|
9225
|
+
enabled: boolFromRaw(route.enabled, true),
|
|
9226
|
+
routeKind,
|
|
9227
|
+
projectID,
|
|
9228
|
+
provider,
|
|
7946
9229
|
role,
|
|
7947
9230
|
roleProfile,
|
|
7948
9231
|
botName,
|
|
@@ -7962,17 +9245,18 @@ function normalizeRunnerRoute(rawRoute, overrides = {}) {
|
|
|
7962
9245
|
};
|
|
7963
9246
|
}
|
|
7964
9247
|
|
|
7965
|
-
function runnerRouteKey(route) {
|
|
7966
|
-
const normalized = normalizeRunnerRoute(route);
|
|
7967
|
-
return [
|
|
7968
|
-
normalized.name || "-",
|
|
7969
|
-
normalized.projectID || "-",
|
|
7970
|
-
normalized.provider || "-",
|
|
7971
|
-
normalized.
|
|
7972
|
-
normalized.
|
|
7973
|
-
normalized.
|
|
7974
|
-
|
|
7975
|
-
|
|
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
|
+
}
|
|
7976
9260
|
|
|
7977
9261
|
function validateRunnerRoute(route, { requireCommand = true, config = null } = {}) {
|
|
7978
9262
|
const errors = [];
|
|
@@ -7984,12 +9268,15 @@ function validateRunnerRoute(route, { requireCommand = true, config = null } = {
|
|
|
7984
9268
|
if (!route.provider) {
|
|
7985
9269
|
errors.push("provider is required");
|
|
7986
9270
|
}
|
|
7987
|
-
if (!route.role) {
|
|
7988
|
-
errors.push("role must be one of: monitor, review, worker, approval");
|
|
7989
|
-
}
|
|
7990
|
-
if (
|
|
7991
|
-
errors.push("
|
|
7992
|
-
}
|
|
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
|
+
}
|
|
7993
9280
|
const configuredRoleProfiles = safeObject(normalizedConfig.roleProfiles);
|
|
7994
9281
|
const resolvedRoleProfileName = normalizeRunnerRoleProfileName(route.roleProfile || route.role);
|
|
7995
9282
|
if (resolvedRoleProfileName && !configuredRoleProfiles[resolvedRoleProfileName] && !legacyCommandAvailable) {
|
|
@@ -8050,10 +9337,10 @@ function resolveConfiguredRunnerRouteServerBotName(route, telegramEntries = [])
|
|
|
8050
9337
|
return String(matchedTelegramEntry?.serverBotName || "").trim();
|
|
8051
9338
|
}
|
|
8052
9339
|
|
|
8053
|
-
function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags, options = {}) {
|
|
8054
|
-
const candidate = normalizeRunnerRoute(route);
|
|
8055
|
-
const candidateBotName = resolveConfiguredRunnerRouteServerBotName(candidate, options.telegramEntries);
|
|
8056
|
-
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"])) {
|
|
8057
9344
|
return false;
|
|
8058
9345
|
}
|
|
8059
9346
|
if (inlineRoute.projectID && candidate.projectID !== inlineRoute.projectID) {
|
|
@@ -8062,12 +9349,15 @@ function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags,
|
|
|
8062
9349
|
if (inlineRoute.provider && candidate.provider !== inlineRoute.provider) {
|
|
8063
9350
|
return false;
|
|
8064
9351
|
}
|
|
8065
|
-
if (inlineRoute.role && candidate.role !== inlineRoute.role) {
|
|
8066
|
-
return false;
|
|
8067
|
-
}
|
|
8068
|
-
if (hasRunnerFlag(flags, "
|
|
8069
|
-
return false;
|
|
8070
|
-
}
|
|
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
|
+
}
|
|
8071
9361
|
if (inlineRoute.botID && candidate.botID !== inlineRoute.botID) {
|
|
8072
9362
|
return false;
|
|
8073
9363
|
}
|
|
@@ -8089,8 +9379,8 @@ function configuredRunnerRouteMatchesInlineSelection(route, inlineRoute, flags,
|
|
|
8089
9379
|
return true;
|
|
8090
9380
|
}
|
|
8091
9381
|
|
|
8092
|
-
function applyRunnerRouteFlagOverrides(route, flags) {
|
|
8093
|
-
const merged = normalizeRunnerRoute(route);
|
|
9382
|
+
function applyRunnerRouteFlagOverrides(route, flags) {
|
|
9383
|
+
const merged = normalizeRunnerRoute(route);
|
|
8094
9384
|
if (hasRunnerFlag(flags, "route-name")) {
|
|
8095
9385
|
merged.name = String(flags["route-name"] || "").trim();
|
|
8096
9386
|
}
|
|
@@ -8103,12 +9393,15 @@ function applyRunnerRouteFlagOverrides(route, flags) {
|
|
|
8103
9393
|
if (hasRunnerFlag(flags, "role")) {
|
|
8104
9394
|
merged.role = normalizeBotRole(flags.role);
|
|
8105
9395
|
}
|
|
8106
|
-
if (hasRunnerFlag(flags, "role-profile")) {
|
|
8107
|
-
merged.roleProfile = normalizeRunnerRoleProfileName(flags["role-profile"]);
|
|
8108
|
-
}
|
|
8109
|
-
if (hasRunnerFlag(flags, "
|
|
8110
|
-
merged.
|
|
8111
|
-
}
|
|
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
|
+
}
|
|
8112
9405
|
if (hasRunnerFlag(flags, "bot-id")) {
|
|
8113
9406
|
merged.botID = String(flags["bot-id"] || "").trim();
|
|
8114
9407
|
}
|
|
@@ -8194,12 +9487,13 @@ function applyRunnerRouteFlagOverrides(route, flags) {
|
|
|
8194
9487
|
return merged;
|
|
8195
9488
|
}
|
|
8196
9489
|
|
|
8197
|
-
function resolveRunnerRoutes(flags, mode) {
|
|
8198
|
-
const inlineRoute = normalizeRunnerRoute({
|
|
8199
|
-
name: flags["route-name"],
|
|
8200
|
-
enabled: true,
|
|
8201
|
-
|
|
8202
|
-
|
|
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,
|
|
8203
9497
|
role: flags.role,
|
|
8204
9498
|
role_profile: flags["role-profile"],
|
|
8205
9499
|
bot_name: flags["bot-name"],
|
|
@@ -8309,13 +9603,29 @@ function resolveRunnerRoutes(flags, mode) {
|
|
|
8309
9603
|
);
|
|
8310
9604
|
}
|
|
8311
9605
|
|
|
8312
|
-
async function listProjectChatDestinations(params) {
|
|
8313
|
-
return listProjectChatDestinationsImpl(params, buildRunnerDataDeps());
|
|
8314
|
-
}
|
|
8315
|
-
|
|
8316
|
-
async function
|
|
8317
|
-
return
|
|
8318
|
-
}
|
|
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
|
+
}
|
|
8319
9629
|
|
|
8320
9630
|
async function listProjectRunnerRequests(params) {
|
|
8321
9631
|
return listProjectRunnerRequestsImpl(params, buildRunnerDataDeps());
|
|
@@ -8394,7 +9704,7 @@ function buildTelegramArchiveStructuredPayload(normalized) {
|
|
|
8394
9704
|
occurredAt: normalized?.occurredAt,
|
|
8395
9705
|
messageThreadID: normalized?.messageThreadID,
|
|
8396
9706
|
replyToMessageID: normalized?.replyToMessageID,
|
|
8397
|
-
body: normalized?.text,
|
|
9707
|
+
body: normalized?.body || normalized?.text,
|
|
8398
9708
|
}),
|
|
8399
9709
|
sourceOrigin: String(normalized?.archiveSourceOrigin || normalized?.sourceOrigin || "").trim().toLowerCase(),
|
|
8400
9710
|
sourceRouteKey: String(normalized?.archiveSourceRouteKey || normalized?.sourceRouteKey || "").trim(),
|
|
@@ -8403,7 +9713,11 @@ function buildTelegramArchiveStructuredPayload(normalized) {
|
|
|
8403
9713
|
replyToSender: String(normalized?.replyToFromName || "").trim(),
|
|
8404
9714
|
replyToUsername: String(normalized?.replyToFromUsername || "").trim(),
|
|
8405
9715
|
replyToSenderIsBot: normalized?.replyToFromIsBot === true,
|
|
8406
|
-
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(),
|
|
8407
9721
|
};
|
|
8408
9722
|
return payload;
|
|
8409
9723
|
}
|
|
@@ -8547,6 +9861,12 @@ function parseArchivedChatComment(rawBody) {
|
|
|
8547
9861
|
replyToSenderIsBot: payloadHas("replyToSenderIsBot")
|
|
8548
9862
|
? boolFromRaw(structuredPayload.replyToSenderIsBot, false)
|
|
8549
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(),
|
|
8550
9870
|
botID: firstNonEmptyString([safeObject(structuredPayload).botID, metadata.bot_id]),
|
|
8551
9871
|
botName: firstNonEmptyString([safeObject(structuredPayload).botName, metadata.bot_name]),
|
|
8552
9872
|
botUsername: normalizeTelegramMentionUsername(
|
|
@@ -8793,9 +10113,107 @@ function toISOStringFromUnix(rawValue) {
|
|
|
8793
10113
|
return new Date(numeric * 1000).toISOString();
|
|
8794
10114
|
}
|
|
8795
10115
|
|
|
8796
|
-
function collectTelegramUpdateText(message) {
|
|
8797
|
-
return firstNonEmptyString([message?.text, message?.caption]);
|
|
8798
|
-
}
|
|
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
|
+
}
|
|
8799
10217
|
|
|
8800
10218
|
function normalizeTelegramMentionUsername(rawValue) {
|
|
8801
10219
|
return String(rawValue || "").trim().replace(/^@+/, "").toLowerCase();
|
|
@@ -8830,23 +10248,29 @@ function extractTelegramMentionUsernames(text, entities) {
|
|
|
8830
10248
|
return Array.from(set);
|
|
8831
10249
|
}
|
|
8832
10250
|
|
|
8833
|
-
function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
10251
|
+
function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
8834
10252
|
const update = safeObject(rawUpdate);
|
|
8835
10253
|
const message = safeObject(update.message || update.edited_message);
|
|
8836
10254
|
if (!message || Object.keys(message).length === 0) {
|
|
8837
10255
|
return null;
|
|
8838
10256
|
}
|
|
8839
|
-
const chat = safeObject(message.chat);
|
|
8840
|
-
const from = safeObject(message.from);
|
|
8841
|
-
const replyTo = safeObject(message.reply_to_message);
|
|
8842
|
-
const replyToFrom = safeObject(replyTo.from);
|
|
8843
|
-
const
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
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,
|
|
8850
10274
|
);
|
|
8851
10275
|
return {
|
|
8852
10276
|
eventName: update.edited_message ? "telegram.message.updated" : "telegram.message.created",
|
|
@@ -8856,12 +10280,17 @@ function normalizeLocalTelegramUpdate(rawUpdate) {
|
|
|
8856
10280
|
chatType: String(chat.type || "").trim(),
|
|
8857
10281
|
chatTitle: firstNonEmptyString([chat.title, chat.username, joinTextParts([chat.first_name, chat.last_name]), chat.id]),
|
|
8858
10282
|
fromID: String(from.id || "").trim(),
|
|
8859
|
-
fromName: firstNonEmptyString([joinTextParts([from.first_name, from.last_name]), from.username, from.id]),
|
|
8860
|
-
fromUsername: String(from.username || "").trim(),
|
|
8861
|
-
fromIsBot: Boolean(from.is_bot),
|
|
8862
|
-
mentionUsernames,
|
|
8863
|
-
text,
|
|
8864
|
-
|
|
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),
|
|
8865
10294
|
messageThreadID: String(message.message_thread_id || "").trim(),
|
|
8866
10295
|
replyToMessageID: intFromRawAllowZero(replyTo.message_id, 0),
|
|
8867
10296
|
replyToFromName: firstNonEmptyString([joinTextParts([replyToFrom.first_name, replyToFrom.last_name]), replyToFrom.username, replyToFrom.id]),
|
|
@@ -8907,6 +10336,7 @@ function formatTelegramInboundArchiveComment(normalized) {
|
|
|
8907
10336
|
const archiveSourceRouteKey = String(normalized.archiveSourceRouteKey || normalized.sourceRouteKey || "").trim();
|
|
8908
10337
|
const archiveSourceBotUsername = String(normalized.archiveSourceBotUsername || normalized.sourceBotUsername || "").trim();
|
|
8909
10338
|
const archivePayload = buildTelegramArchiveStructuredPayload(normalized);
|
|
10339
|
+
const normalizedAttachments = normalizeTelegramMessageAttachments(normalized.attachments);
|
|
8910
10340
|
const headerLines = [
|
|
8911
10341
|
`[Telegram ${normalized.eventName === "telegram.message.updated" ? "edited" : "message"}]`,
|
|
8912
10342
|
`chat_id: ${normalized.chatID || "<missing>"}`,
|
|
@@ -8929,10 +10359,19 @@ function formatTelegramInboundArchiveComment(normalized) {
|
|
|
8929
10359
|
if (normalized.fromUsername) {
|
|
8930
10360
|
headerLines.push(`telegram_username: @${normalized.fromUsername.replace(/^@+/, "")}`);
|
|
8931
10361
|
}
|
|
8932
|
-
if (normalized.mentionUsernames.length > 0) {
|
|
8933
|
-
headerLines.push(`mention_usernames: ${normalized.mentionUsernames.map((item) => `@${item}`).join(", ")}`);
|
|
8934
|
-
}
|
|
8935
|
-
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) {
|
|
8936
10375
|
headerLines.push(`reply_to_message_id: ${normalized.replyToMessageID}`);
|
|
8937
10376
|
headerLines.push(`reply_to_sender_is_bot: ${normalized.replyToFromIsBot ? "true" : "false"}`);
|
|
8938
10377
|
if (normalized.replyToFromName) {
|
|
@@ -8941,12 +10380,12 @@ function formatTelegramInboundArchiveComment(normalized) {
|
|
|
8941
10380
|
if (normalized.replyToFromUsername) {
|
|
8942
10381
|
headerLines.push(`reply_to_telegram_username: @${normalized.replyToFromUsername.replace(/^@+/, "")}`);
|
|
8943
10382
|
}
|
|
8944
|
-
}
|
|
10383
|
+
}
|
|
8945
10384
|
if (intFromRawAllowZero(normalized.messageThreadID, 0) > 0) {
|
|
8946
10385
|
headerLines.push(`message_thread_id: ${intFromRawAllowZero(normalized.messageThreadID, 0)}`);
|
|
8947
10386
|
}
|
|
8948
10387
|
headerLines.push(`archive_payload_json: ${JSON.stringify(archivePayload)}`);
|
|
8949
|
-
return `${headerLines.join("\n")}\n\n${String(normalized.text || "").trim()}`;
|
|
10388
|
+
return `${headerLines.join("\n")}\n\n${String(normalized.body || normalized.text || "").trim()}`;
|
|
8950
10389
|
}
|
|
8951
10390
|
|
|
8952
10391
|
async function postJSONWithAuthHeaders(urlText, timeoutSeconds, token, payload, extraHeaders = {}) {
|
|
@@ -9905,25 +11344,33 @@ function buildRunnerShowActiveExecutionPayload(activeExecutionStateRaw) {
|
|
|
9905
11344
|
};
|
|
9906
11345
|
}
|
|
9907
11346
|
|
|
9908
|
-
function buildRunnerShowResolvedContext({
|
|
9909
|
-
normalizedRoute,
|
|
9910
|
-
diagnostics,
|
|
9911
|
-
envConfig,
|
|
9912
|
-
resolvedServerBotName,
|
|
11347
|
+
function buildRunnerShowResolvedContext({
|
|
11348
|
+
normalizedRoute,
|
|
11349
|
+
diagnostics,
|
|
11350
|
+
envConfig,
|
|
11351
|
+
resolvedServerBotName,
|
|
9913
11352
|
botNameSource,
|
|
9914
11353
|
}) {
|
|
9915
|
-
return {
|
|
9916
|
-
resolved_server_identity: {
|
|
9917
|
-
server_bot_name: resolvedServerBotName,
|
|
9918
|
-
server_bot_name_source: botNameSource,
|
|
9919
|
-
server_bot_id: normalizedRoute.botID || "-",
|
|
9920
|
-
telegram_entry_file: String(envConfig?.entryFilePath || "").trim() || "-",
|
|
9921
|
-
},
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
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
|
+
},
|
|
9927
11374
|
workspace_mapping: {
|
|
9928
11375
|
workspace_dir: diagnostics.workspaceDir || "-",
|
|
9929
11376
|
workspace_source: diagnostics.workspaceSource || "-",
|
|
@@ -10167,8 +11614,8 @@ async function resolveInformationalQueryReply({
|
|
|
10167
11614
|
return lookupOnlyResponse || null;
|
|
10168
11615
|
}
|
|
10169
11616
|
|
|
10170
|
-
function buildRunnerExecutionDeps() {
|
|
10171
|
-
return {
|
|
11617
|
+
function buildRunnerExecutionDeps() {
|
|
11618
|
+
return {
|
|
10172
11619
|
adjudicateRunnerStartupLoopWithAI,
|
|
10173
11620
|
analyzeHumanConversationIntentWithAI,
|
|
10174
11621
|
auditRoleExecutionPlanWithAI,
|
|
@@ -10197,6 +11644,8 @@ function buildRunnerExecutionDeps() {
|
|
|
10197
11644
|
createProjectContextItem,
|
|
10198
11645
|
replaceProjectCtxpackFiles,
|
|
10199
11646
|
resolveInformationalQueryReply,
|
|
11647
|
+
transcribeRunnerTelegramAudioAttachments,
|
|
11648
|
+
analyzeRunnerTelegramImageAttachments,
|
|
10200
11649
|
prepareRunnerSelectedRecordRecoveryContext: (input) => prepareRunnerSelectedRecordRecoveryContextImpl({
|
|
10201
11650
|
...safeObject(input),
|
|
10202
11651
|
deps: buildRunnerRecoveryDeps(),
|
|
@@ -10204,15 +11653,19 @@ function buildRunnerExecutionDeps() {
|
|
|
10204
11653
|
};
|
|
10205
11654
|
}
|
|
10206
11655
|
|
|
10207
|
-
function buildRunnerRuntimeDeps() {
|
|
10208
|
-
return {
|
|
10209
|
-
loadProviderEnvConfig,
|
|
10210
|
-
loadBotRunnerState,
|
|
10211
|
-
saveBotRunnerState,
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
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,
|
|
10216
11669
|
getTelegramUpdates,
|
|
10217
11670
|
normalizeLocalTelegramUpdate,
|
|
10218
11671
|
listThreadComments,
|
|
@@ -10555,23 +12008,34 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
10555
12008
|
runnerConfig,
|
|
10556
12009
|
buildRunnerExecutionDeps(),
|
|
10557
12010
|
);
|
|
10558
|
-
const
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
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,
|
|
10575
12039
|
provider: normalizedRoute.provider,
|
|
10576
12040
|
destination,
|
|
10577
12041
|
token: runtime.token,
|
|
@@ -10668,9 +12132,7 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
10668
12132
|
});
|
|
10669
12133
|
const followupRequestsForPending = Object.values(requests).filter((entryRaw) => {
|
|
10670
12134
|
const entry = safeObject(entryRaw);
|
|
10671
|
-
const nextExpectedResponders =
|
|
10672
|
-
.map((value) => normalizeTelegramMentionUsername(value))
|
|
10673
|
-
.filter(Boolean);
|
|
12135
|
+
const nextExpectedResponders = runnerRequestPreferredNextExpectedResponders(entry);
|
|
10674
12136
|
if (!nextExpectedResponders.includes(currentBotSelector)) {
|
|
10675
12137
|
return false;
|
|
10676
12138
|
}
|
|
@@ -11260,8 +12722,8 @@ function buildRunnerRouteListRows() {
|
|
|
11260
12722
|
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
11261
12723
|
const telegramState = readTelegramEnvState();
|
|
11262
12724
|
const telegramEntries = ensureArray(telegramState.entries);
|
|
11263
|
-
return ensureArray(config.routes).map((rawRoute, index) => {
|
|
11264
|
-
const route = normalizeRunnerRoute(rawRoute);
|
|
12725
|
+
return ensureArray(config.routes).map((rawRoute, index) => {
|
|
12726
|
+
const route = normalizeRunnerRoute(rawRoute);
|
|
11265
12727
|
const matchedTelegramEntry = route.provider === "telegram"
|
|
11266
12728
|
? telegramEntries.find((entry) => {
|
|
11267
12729
|
const current = safeObject(entry);
|
|
@@ -11281,24 +12743,25 @@ function buildRunnerRouteListRows() {
|
|
|
11281
12743
|
matchedTelegramEntry?.serverBotName,
|
|
11282
12744
|
"-",
|
|
11283
12745
|
]);
|
|
11284
|
-
return {
|
|
11285
|
-
index: index + 1,
|
|
11286
|
-
name: route.name || runnerRouteKey(route),
|
|
11287
|
-
logicalSignature: runnerRouteLogicalSignature(route),
|
|
11288
|
-
enabled: route.enabled !== false,
|
|
11289
|
-
provider: route.provider || "-",
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
}
|
|
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
|
+
}
|
|
11302
12765
|
|
|
11303
12766
|
function slugifyRunnerRouteSegment(rawValue) {
|
|
11304
12767
|
return String(rawValue || "")
|
|
@@ -11308,12 +12771,13 @@ function slugifyRunnerRouteSegment(rawValue) {
|
|
|
11308
12771
|
.replace(/^-+|-+$/g, "");
|
|
11309
12772
|
}
|
|
11310
12773
|
|
|
11311
|
-
function buildRunnerRouteNameSuggestion({ provider = "", role = "", botName = "" }, existingNames = []) {
|
|
11312
|
-
const segments = [
|
|
11313
|
-
slugifyRunnerRouteSegment(provider),
|
|
11314
|
-
slugifyRunnerRouteSegment(role),
|
|
11315
|
-
slugifyRunnerRouteSegment(botName),
|
|
11316
|
-
|
|
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);
|
|
11317
12781
|
const base = segments.join("-") || "runner-route";
|
|
11318
12782
|
const existing = new Set(ensureArray(existingNames).map((name) => String(name || "").trim().toLowerCase()).filter(Boolean));
|
|
11319
12783
|
if (!existing.has(base)) {
|
|
@@ -11364,15 +12828,15 @@ function removeRunnerRouteFromConfig(config, routeName) {
|
|
|
11364
12828
|
};
|
|
11365
12829
|
}
|
|
11366
12830
|
|
|
11367
|
-
function formatRunnerRouteChoiceLabel(route, telegramEntries = []) {
|
|
11368
|
-
const normalizedRoute = normalizeRunnerRoute(route);
|
|
11369
|
-
const resolvedName = resolveConfiguredRunnerRouteServerBotName(normalizedRoute, telegramEntries);
|
|
11370
|
-
const routeName = normalizedRoute.name || runnerRouteKey(normalizedRoute);
|
|
11371
|
-
const role = normalizedRoute.role || "-";
|
|
11372
|
-
const botName = resolvedName || normalizedRoute.botName || normalizedRoute.botID || "-";
|
|
11373
|
-
const
|
|
11374
|
-
return `${routeName}${normalizedRoute.enabled ? "" : " [disabled]"} - ${normalizedRoute.provider || "-"} | ${role} | ${botName} | ${
|
|
11375
|
-
}
|
|
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
|
+
}
|
|
11376
12840
|
|
|
11377
12841
|
async function selectRunnerManagementRoute(ui, flags, { title = "Select runner route" } = {}) {
|
|
11378
12842
|
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
@@ -11516,15 +12980,16 @@ async function selectRunnerServerBotProfile(ui, { provider, role, flags, current
|
|
|
11516
12980
|
token,
|
|
11517
12981
|
timeoutSeconds,
|
|
11518
12982
|
});
|
|
11519
|
-
const candidates = ensureArray(bots)
|
|
11520
|
-
.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);
|
|
11521
12985
|
if (!candidates.length) {
|
|
11522
12986
|
throw new Error(`No active ${providerEnvConfig(provider).label} server bot exists for role "${role}".`);
|
|
11523
12987
|
}
|
|
11524
|
-
const resolveChoice = (bot) => ({
|
|
11525
|
-
name: String(bot.name || "").trim(),
|
|
11526
|
-
id: String(bot.id || "").trim(),
|
|
11527
|
-
|
|
12988
|
+
const resolveChoice = (bot) => ({
|
|
12989
|
+
name: String(bot.name || "").trim(),
|
|
12990
|
+
id: String(bot.id || "").trim(),
|
|
12991
|
+
username: normalizeTelegramBotUsername(bot.username),
|
|
12992
|
+
});
|
|
11528
12993
|
if (flaggedBotID) {
|
|
11529
12994
|
const matched = candidates.find((bot) => bot.id === flaggedBotID);
|
|
11530
12995
|
if (!matched) {
|
|
@@ -11566,18 +13031,28 @@ async function selectRunnerServerBotProfile(ui, { provider, role, flags, current
|
|
|
11566
13031
|
return resolveChoice(matched);
|
|
11567
13032
|
}
|
|
11568
13033
|
|
|
11569
|
-
async function selectRunnerDestination(ui, {
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
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);
|
|
11581
13056
|
const flaggedID = String(flags["destination-id"] || "").trim();
|
|
11582
13057
|
const flaggedLabel = String(flags["destination-label"] || "").trim();
|
|
11583
13058
|
if (flaggedID) {
|
|
@@ -11621,15 +13096,98 @@ async function selectRunnerDestination(ui, { projectID, provider, flags, current
|
|
|
11621
13096
|
if (!matched) {
|
|
11622
13097
|
throw new Error("selected destination was not found");
|
|
11623
13098
|
}
|
|
11624
|
-
return { id: matched.id, label: matched.label || matched.id };
|
|
11625
|
-
}
|
|
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
|
+
}
|
|
11626
13183
|
|
|
11627
|
-
function buildRunnerRoutePayload({
|
|
11628
|
-
currentRoute = null,
|
|
11629
|
-
name = "",
|
|
11630
|
-
enabled = true,
|
|
11631
|
-
|
|
11632
|
-
|
|
13184
|
+
function buildRunnerRoutePayload({
|
|
13185
|
+
currentRoute = null,
|
|
13186
|
+
name = "",
|
|
13187
|
+
enabled = true,
|
|
13188
|
+
routeKind = "",
|
|
13189
|
+
projectID = "",
|
|
13190
|
+
provider = "",
|
|
11633
13191
|
role = "",
|
|
11634
13192
|
roleProfile = "",
|
|
11635
13193
|
serverBotName = "",
|
|
@@ -11644,12 +13202,13 @@ function buildRunnerRoutePayload({
|
|
|
11644
13202
|
const archivePolicy = currentRoute
|
|
11645
13203
|
? safeObject(currentRoute.archivePolicy)
|
|
11646
13204
|
: defaultRunnerArchivePolicyForRole(roleProfile || role);
|
|
11647
|
-
return normalizeRunnerRoute({
|
|
11648
|
-
...(currentRoute ? serializeRunnerRoute(currentRoute) : {}),
|
|
11649
|
-
name,
|
|
11650
|
-
enabled,
|
|
11651
|
-
|
|
11652
|
-
|
|
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,
|
|
11653
13212
|
role,
|
|
11654
13213
|
role_profile: roleProfile,
|
|
11655
13214
|
server_bot_name: serverBotName,
|
|
@@ -11662,45 +13221,48 @@ function buildRunnerRoutePayload({
|
|
|
11662
13221
|
});
|
|
11663
13222
|
}
|
|
11664
13223
|
|
|
11665
|
-
async function runRunnerRouteAdd(flags) {
|
|
11666
|
-
const ui = createPrompter();
|
|
11667
|
-
try {
|
|
11668
|
-
if (shouldRenderPromptChrome(flags)) {
|
|
11669
|
-
ui.setFlow("RUNNER ROUTE ADD", "Create one executable runner route from server bot and
|
|
11670
|
-
}
|
|
11671
|
-
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
11672
|
-
const provider = await selectRunnerRouteProvider(ui, flags, "");
|
|
11673
|
-
const projectID = await resolveRunnerRouteProjectID(ui, flags, config);
|
|
11674
|
-
const role = await selectRunnerRole(ui, flags, "");
|
|
11675
|
-
const botSelection = await selectRunnerServerBotProfile(ui, { provider, role, flags });
|
|
11676
|
-
const
|
|
11677
|
-
projectID,
|
|
11678
|
-
provider,
|
|
11679
|
-
flags,
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
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;
|
|
11688
13249
|
const enabled = Object.prototype.hasOwnProperty.call(flags, "enabled")
|
|
11689
13250
|
? boolFromRaw(flags.enabled, true)
|
|
11690
13251
|
: true;
|
|
11691
|
-
const nextRoute = buildRunnerRoutePayload({
|
|
11692
|
-
name: routeName,
|
|
11693
|
-
enabled,
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
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
|
+
});
|
|
11704
13266
|
const saved = upsertRunnerRouteConfig(config, nextRoute);
|
|
11705
13267
|
const filePath = saveBotRunnerConfig(saved, config.filePath);
|
|
11706
13268
|
if (!String(flags.name || "").trim()) {
|
|
@@ -11786,24 +13348,28 @@ async function runRunnerRouteEdit(flags) {
|
|
|
11786
13348
|
})
|
|
11787
13349
|
: { name: currentRoute.botName, id: currentRoute.botID };
|
|
11788
13350
|
|
|
11789
|
-
const
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
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
|
+
})();
|
|
11807
13373
|
|
|
11808
13374
|
const pollAction = await promptChoice(
|
|
11809
13375
|
ui,
|
|
@@ -11842,12 +13408,13 @@ async function runRunnerRouteEdit(flags) {
|
|
|
11842
13408
|
process.stdout.write("Cancelled.\n");
|
|
11843
13409
|
return;
|
|
11844
13410
|
}
|
|
11845
|
-
const nextRoute = buildRunnerRoutePayload({
|
|
11846
|
-
currentRoute,
|
|
11847
|
-
name: routeName,
|
|
11848
|
-
enabled,
|
|
11849
|
-
|
|
11850
|
-
|
|
13411
|
+
const nextRoute = buildRunnerRoutePayload({
|
|
13412
|
+
currentRoute,
|
|
13413
|
+
name: routeName,
|
|
13414
|
+
enabled,
|
|
13415
|
+
routeKind: currentRoute.routeKind,
|
|
13416
|
+
projectID,
|
|
13417
|
+
provider: currentRoute.provider,
|
|
11851
13418
|
role,
|
|
11852
13419
|
roleProfile: role,
|
|
11853
13420
|
serverBotName: botSelection.name,
|
|
@@ -11911,30 +13478,37 @@ function parseRunnerProjectUpRoles(flags) {
|
|
|
11911
13478
|
return ordered;
|
|
11912
13479
|
}
|
|
11913
13480
|
|
|
11914
|
-
function runnerProjectUpScopeKey({ projectID, provider, destinationID = "", destinationLabel = "", botIdentity = "" }) {
|
|
11915
|
-
return [
|
|
11916
|
-
String(projectID || "").trim() || "-",
|
|
11917
|
-
String(provider || "").trim() || "-",
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
|
|
11924
|
-
|
|
11925
|
-
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
const
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
const
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
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);
|
|
11938
13512
|
const routeBotID = String(route.botID || "").trim();
|
|
11939
13513
|
const scopeRoleIDs = new Set(
|
|
11940
13514
|
Object.values(safeObject(scope.serverRoleIDs))
|
|
@@ -11947,17 +13521,24 @@ function routeMatchesRunnerProjectUpScope(routeRaw, scopeRaw) {
|
|
|
11947
13521
|
);
|
|
11948
13522
|
}
|
|
11949
13523
|
|
|
11950
|
-
function resolveRunnerConversationPeers(routeRaw) {
|
|
11951
|
-
const normalizedRoute = normalizeRunnerRoute(routeRaw);
|
|
11952
|
-
|
|
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 });
|
|
11953
13533
|
const seen = new Set();
|
|
11954
13534
|
const peers = [];
|
|
11955
13535
|
ensureArray(config.routes)
|
|
11956
13536
|
.map((rawRoute) => normalizeRunnerRoute(rawRoute))
|
|
11957
|
-
.filter((route) => route.enabled)
|
|
11958
|
-
.filter((route) => String(route.projectID || "").trim() === String(normalizedRoute.projectID || "").trim())
|
|
11959
|
-
.filter((route) => String(route.provider || "").trim() === String(normalizedRoute.provider || "").trim())
|
|
11960
|
-
.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) => {
|
|
11961
13542
|
const routeDestinationID = String(route.destinationID || "").trim();
|
|
11962
13543
|
const targetDestinationID = String(normalizedRoute.destinationID || "").trim();
|
|
11963
13544
|
const routeDestinationLabel = normalizeRunnerRouteIdentityText(route.destinationLabel);
|
|
@@ -11978,17 +13559,33 @@ function resolveRunnerConversationPeers(routeRaw) {
|
|
|
11978
13559
|
return peers;
|
|
11979
13560
|
}
|
|
11980
13561
|
|
|
11981
|
-
function resolveRunnerConversationManagedBots(routeRaw, availableBots = []) {
|
|
11982
|
-
const normalizedRoute = normalizeRunnerRoute(routeRaw);
|
|
11983
|
-
|
|
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 });
|
|
11984
13580
|
const seen = new Set();
|
|
11985
13581
|
const managed = [];
|
|
11986
13582
|
ensureArray(config.routes)
|
|
11987
13583
|
.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) =>
|
|
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) => {
|
|
11992
13589
|
const routeDestinationID = String(route.destinationID || "").trim();
|
|
11993
13590
|
const targetDestinationID = String(normalizedRoute.destinationID || "").trim();
|
|
11994
13591
|
const routeDestinationLabel = normalizeRunnerRouteIdentityText(route.destinationLabel);
|
|
@@ -12039,20 +13636,22 @@ function applyRunnerProjectUpRouteSuggestions(routeSuggestions) {
|
|
|
12039
13636
|
desiredRoutes.forEach((suggestionRaw) => {
|
|
12040
13637
|
const suggestion = safeObject(suggestionRaw);
|
|
12041
13638
|
const routePayload = normalizeRunnerRoute(suggestion.routePayload);
|
|
12042
|
-
const scopeKey = runnerProjectUpScopeKey({
|
|
12043
|
-
projectID: routePayload.projectID,
|
|
12044
|
-
provider: routePayload.provider,
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
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,
|
|
12048
13646
|
});
|
|
12049
13647
|
if (!scopeGroups.has(scopeKey)) {
|
|
12050
13648
|
scopeGroups.set(scopeKey, {
|
|
12051
|
-
projectID: routePayload.projectID,
|
|
12052
|
-
provider: routePayload.provider,
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
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,
|
|
12056
13655
|
serverRoleIDs: { ...safeObject(suggestion.serverRoleIDs) },
|
|
12057
13656
|
desiredSignatures: new Set(),
|
|
12058
13657
|
});
|
|
@@ -12105,11 +13704,12 @@ function applyRunnerProjectUpRouteSuggestions(routeSuggestions) {
|
|
|
12105
13704
|
};
|
|
12106
13705
|
}
|
|
12107
13706
|
|
|
12108
|
-
function resolveRunnerProjectUpRoutes({
|
|
12109
|
-
projectID,
|
|
12110
|
-
provider,
|
|
12111
|
-
|
|
12112
|
-
|
|
13707
|
+
function resolveRunnerProjectUpRoutes({
|
|
13708
|
+
projectID,
|
|
13709
|
+
provider,
|
|
13710
|
+
routeKind = "",
|
|
13711
|
+
destinationID,
|
|
13712
|
+
destinationLabel,
|
|
12113
13713
|
botName = "",
|
|
12114
13714
|
botID = "",
|
|
12115
13715
|
roleFilter = [],
|
|
@@ -12117,20 +13717,22 @@ function resolveRunnerProjectUpRoutes({
|
|
|
12117
13717
|
const config = loadBotRunnerConfig({ persistIfNeeded: true });
|
|
12118
13718
|
const telegramEntries = ensureArray(readTelegramEnvState().entries);
|
|
12119
13719
|
const normalizedRoles = ensureArray(roleFilter).map((value) => normalizeBotRole(value)).filter(Boolean);
|
|
12120
|
-
const selectionFlags = {
|
|
12121
|
-
"project-id": projectID,
|
|
12122
|
-
provider,
|
|
12123
|
-
"
|
|
12124
|
-
"destination-
|
|
13720
|
+
const selectionFlags = {
|
|
13721
|
+
"project-id": projectID,
|
|
13722
|
+
provider,
|
|
13723
|
+
...(routeKind ? { "route-kind": routeKind } : {}),
|
|
13724
|
+
"destination-id": destinationID,
|
|
13725
|
+
"destination-label": destinationLabel,
|
|
12125
13726
|
...(botName ? { "bot-name": botName } : {}),
|
|
12126
13727
|
...(botID ? { "bot-id": botID } : {}),
|
|
12127
13728
|
};
|
|
12128
|
-
const selectionRoute = normalizeRunnerRoute({
|
|
12129
|
-
project_id: projectID,
|
|
12130
|
-
provider,
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
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,
|
|
12134
13736
|
server_bot_id: botID,
|
|
12135
13737
|
});
|
|
12136
13738
|
const matched = ensureArray(config.routes)
|
|
@@ -12142,10 +13744,12 @@ function resolveRunnerProjectUpRoutes({
|
|
|
12142
13744
|
return matched;
|
|
12143
13745
|
}
|
|
12144
13746
|
const seenBotDestinations = new Map();
|
|
12145
|
-
return matched.filter((route) => {
|
|
12146
|
-
const botIdentity = String(route.serverBotID || route.botID || route.botName || route.serverBotName || "").trim().toLowerCase();
|
|
12147
|
-
const
|
|
12148
|
-
|
|
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}`;
|
|
12149
13753
|
if (!dedupeKey || dedupeKey === "::") return true;
|
|
12150
13754
|
if (seenBotDestinations.has(dedupeKey)) return false;
|
|
12151
13755
|
seenBotDestinations.set(dedupeKey, true);
|
|
@@ -12227,6 +13831,95 @@ async function runRunnerProjectUp(flags) {
|
|
|
12227
13831
|
});
|
|
12228
13832
|
}
|
|
12229
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
|
+
|
|
12230
13923
|
function resolveRunnerProjectTUIRuntimePolicy(flags = {}) {
|
|
12231
13924
|
const explicitStartRequested = Object.prototype.hasOwnProperty.call(flags, "start");
|
|
12232
13925
|
return {
|
|
@@ -12757,6 +14450,7 @@ function buildRunnerProjectUpNextSteps({
|
|
|
12757
14450
|
shouldStartRunner,
|
|
12758
14451
|
projectID,
|
|
12759
14452
|
provider,
|
|
14453
|
+
routeKind = "room",
|
|
12760
14454
|
destinationID,
|
|
12761
14455
|
matchingRoutes,
|
|
12762
14456
|
startFlags,
|
|
@@ -12764,14 +14458,18 @@ function buildRunnerProjectUpNextSteps({
|
|
|
12764
14458
|
const nextSteps = [];
|
|
12765
14459
|
const routeNames = ensureArray(matchingRoutes).map((route) => normalizeRunnerRoute(route).name).filter(Boolean);
|
|
12766
14460
|
if (!applyRequested) {
|
|
12767
|
-
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`);
|
|
12768
14464
|
}
|
|
12769
14465
|
if (!shouldStartRunner) {
|
|
12770
14466
|
if (routeNames.length > 0) {
|
|
12771
14467
|
nextSteps.push(`${CLI_NAME} runner show --route-name ${routeNames[0]}`);
|
|
12772
14468
|
nextSteps.push(buildRunnerStartDetachedCommand(startFlags));
|
|
12773
14469
|
} else if (applyRequested) {
|
|
12774
|
-
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`);
|
|
12775
14473
|
}
|
|
12776
14474
|
return nextSteps;
|
|
12777
14475
|
}
|
|
@@ -12798,16 +14496,151 @@ async function buildRunnerProjectUpResult(flags = {}) {
|
|
|
12798
14496
|
shouldStartRunner,
|
|
12799
14497
|
foregroundStartRequested,
|
|
12800
14498
|
} = resolveRunnerProjectUpExecutionPolicy(flags);
|
|
12801
|
-
const roleFilter = parseRunnerProjectUpRoles(flags);
|
|
12802
|
-
const botNameFilter = String(flags["bot-name"] || "").trim();
|
|
12803
|
-
const botIDFilter = String(flags["bot-id"] || "").trim();
|
|
12804
|
-
const
|
|
12805
|
-
|
|
12806
|
-
|
|
12807
|
-
|
|
12808
|
-
|
|
12809
|
-
|
|
12810
|
-
|
|
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,
|
|
12811
14644
|
},
|
|
12812
14645
|
buildBotCommandDeps(),
|
|
12813
14646
|
);
|
|
@@ -12839,12 +14672,13 @@ async function buildRunnerProjectUpResult(flags = {}) {
|
|
|
12839
14672
|
})
|
|
12840
14673
|
: null;
|
|
12841
14674
|
const applyResult = safeObject(applyResultRaw);
|
|
12842
|
-
const matchingRoutes = resolveRunnerProjectUpRoutes({
|
|
12843
|
-
projectID: String(auditPayload.projectID || "").trim(),
|
|
12844
|
-
provider,
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
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,
|
|
12848
14682
|
botID: botIDFilter,
|
|
12849
14683
|
roleFilter,
|
|
12850
14684
|
});
|
|
@@ -12892,6 +14726,7 @@ async function buildRunnerProjectUpResult(flags = {}) {
|
|
|
12892
14726
|
shouldStartRunner,
|
|
12893
14727
|
projectID: summaryPayload.project_id,
|
|
12894
14728
|
provider,
|
|
14729
|
+
routeKind: "room",
|
|
12895
14730
|
destinationID: summaryPayload.destination_id,
|
|
12896
14731
|
matchingRoutes,
|
|
12897
14732
|
startFlags,
|
|
@@ -12927,9 +14762,9 @@ async function runRunnerList(flags) {
|
|
|
12927
14762
|
process.stdout.write(`${JSON.stringify({ ok: true, routes: rows }, null, 2)}\n`);
|
|
12928
14763
|
return;
|
|
12929
14764
|
}
|
|
12930
|
-
process.stdout.write("Runner routes\n");
|
|
12931
|
-
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");
|
|
12932
|
-
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");
|
|
12933
14768
|
if (!rows.length) {
|
|
12934
14769
|
process.stdout.write(" none configured\n");
|
|
12935
14770
|
return;
|
|
@@ -12940,9 +14775,10 @@ async function runRunnerList(flags) {
|
|
|
12940
14775
|
[
|
|
12941
14776
|
` - ${row.name}${row.enabled ? "" : " [disabled]"}`,
|
|
12942
14777
|
` logical_signature: ${row.logicalSignature}`,
|
|
12943
|
-
` provider: ${row.provider}`,
|
|
12944
|
-
`
|
|
12945
|
-
`
|
|
14778
|
+
` provider: ${row.provider}`,
|
|
14779
|
+
` route_kind: ${row.routeKind}`,
|
|
14780
|
+
` project_id: ${row.projectID}`,
|
|
14781
|
+
` server_bot_name: ${row.botName}`,
|
|
12946
14782
|
` server_bot_name_source: ${row.botNameSource}`,
|
|
12947
14783
|
` server_bot_id: ${row.botID}`,
|
|
12948
14784
|
` role: ${row.role}`,
|
|
@@ -13166,6 +15002,25 @@ function runnerDetachedRouteSetSignature(routes) {
|
|
|
13166
15002
|
|
|
13167
15003
|
function runnerSchedulingTargetKeyFromRouteKey(routeKey) {
|
|
13168
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
|
+
}
|
|
13169
15024
|
if (parts.length < 6) {
|
|
13170
15025
|
return "";
|
|
13171
15026
|
}
|
|
@@ -13261,8 +15116,8 @@ function classifyDetachedRunnerLaunchReuse(registry, routes) {
|
|
|
13261
15116
|
),
|
|
13262
15117
|
);
|
|
13263
15118
|
const detail = supersets.length > 1
|
|
13264
|
-
? `multiple detached runners already cover the requested route set for the same project/provider/
|
|
13265
|
-
: `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`;
|
|
13266
15121
|
return {
|
|
13267
15122
|
kind: "conflict",
|
|
13268
15123
|
detail,
|
|
@@ -18896,17 +20751,24 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
18896
20751
|
buildToolAliasMaps,
|
|
18897
20752
|
rewriteAliasedToolCallToCanonical,
|
|
18898
20753
|
normalizeBotRunnerConfigContents,
|
|
18899
|
-
defaultLocalBotBridgeCommand,
|
|
18900
|
-
|
|
18901
|
-
|
|
18902
|
-
|
|
20754
|
+
defaultLocalBotBridgeCommand,
|
|
20755
|
+
cliName: CLI_NAME,
|
|
20756
|
+
resolveRunnerExecutionPlan,
|
|
20757
|
+
resolveRunnerExecutionPlanForRole,
|
|
20758
|
+
resolveRunnerLocalAIExecutionProfile,
|
|
20759
|
+
normalizeRunnerRoute,
|
|
18903
20760
|
buildRunnerRouteNameSuggestion,
|
|
18904
|
-
buildRunnerRoutePayload,
|
|
18905
|
-
upsertRunnerRouteConfig,
|
|
18906
|
-
removeRunnerRouteFromConfig,
|
|
18907
|
-
acquireRunnerRouteLease,
|
|
18908
|
-
|
|
18909
|
-
|
|
20761
|
+
buildRunnerRoutePayload,
|
|
20762
|
+
upsertRunnerRouteConfig,
|
|
20763
|
+
removeRunnerRouteFromConfig,
|
|
20764
|
+
acquireRunnerRouteLease,
|
|
20765
|
+
validateRunnerRoute,
|
|
20766
|
+
runnerRouteSchedulingGroupKey,
|
|
20767
|
+
buildRunnerProjectUpDirectMessageRouteSuggestions,
|
|
20768
|
+
resolveRunnerProjectUpRoutes,
|
|
20769
|
+
buildRunnerProjectUpNextSteps,
|
|
20770
|
+
buildRunnerExecutionDeps,
|
|
20771
|
+
defaultBotRunnerRoleProfiles,
|
|
18910
20772
|
resolveRunnerRoutes,
|
|
18911
20773
|
runnerRouteKey,
|
|
18912
20774
|
runnerRouteLogicalSignature,
|
|
@@ -18962,6 +20824,9 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
18962
20824
|
formatTelegramInboundArchiveComment,
|
|
18963
20825
|
findArchivedBotReplyRecord,
|
|
18964
20826
|
parseArchivedChatComment,
|
|
20827
|
+
normalizeLocalTelegramUpdate,
|
|
20828
|
+
normalizeRunnerTelegramMessageEnvelope,
|
|
20829
|
+
normalizeRunnerRecentLocalInboundReceiptEntry,
|
|
18965
20830
|
intFromRawAllowZero,
|
|
18966
20831
|
validateWorkspaceArtifacts,
|
|
18967
20832
|
});
|
|
@@ -20013,7 +21878,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
|
|
|
20013
21878
|
"detached_runner_blocks_disjoint_same_target_parallel_launches",
|
|
20014
21879
|
decision.kind === "conflict"
|
|
20015
21880
|
&& ensureArray(decision.overlapping_route_names).length === 0
|
|
20016
|
-
&& String(decision.detail || "").includes("same project/provider/
|
|
21881
|
+
&& String(decision.detail || "").includes("same project/provider/route target"),
|
|
20017
21882
|
`kind=${String(decision.kind || "")} overlap=${ensureArray(decision.overlapping_route_names).join(", ")} detail=${String(decision.detail || "")}`,
|
|
20018
21883
|
);
|
|
20019
21884
|
} catch (err) {
|