metheus-governance-mcp-cli 0.2.230 → 0.2.232
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 +2284 -1320
- package/lib/local-ai-adapters.mjs +2 -82
- package/lib/runner-orchestration.mjs +311 -293
- package/lib/runner-trigger.mjs +3 -5
- package/lib/selftest-runner-scenarios.mjs +191 -46
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
adjudicateRunnerStartupLoopWithAI,
|
|
16
16
|
analyzeHumanConversationIntentWithAI,
|
|
17
17
|
auditRoleExecutionPlanWithAI,
|
|
18
|
-
auditDirectHumanReplyWithAI,
|
|
19
18
|
explainExecutionFailureWithAI,
|
|
20
19
|
normalizeExecutionArtifacts,
|
|
21
20
|
planRoleExecutionWithAI,
|
|
@@ -1915,6 +1914,15 @@ function mergeRunnerStateRecords(preferred, fallback) {
|
|
|
1915
1914
|
}
|
|
1916
1915
|
return allowUndefined ? undefined : 0;
|
|
1917
1916
|
};
|
|
1917
|
+
const pickArrayField = (key, normalizer = (value) => String(value || "").trim()) => {
|
|
1918
|
+
if (hasOwn(primary, key)) {
|
|
1919
|
+
const value = uniqueOrderedStrings(ensureArray(primary[key]), normalizer).filter(Boolean);
|
|
1920
|
+
if (value.length) {
|
|
1921
|
+
return value;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
return uniqueOrderedStrings(ensureArray(secondary[key]), normalizer).filter(Boolean);
|
|
1925
|
+
};
|
|
1918
1926
|
return {
|
|
1919
1927
|
last_processed_comment_id: pickStringField("last_processed_comment_id"),
|
|
1920
1928
|
last_processed_created_at: pickStringField("last_processed_created_at"),
|
|
@@ -1949,6 +1957,31 @@ function mergeRunnerStateRecords(preferred, fallback) {
|
|
|
1949
1957
|
last_root_work_item_id: pickStringField("last_root_work_item_id"),
|
|
1950
1958
|
last_root_work_item_title: pickStringField("last_root_work_item_title"),
|
|
1951
1959
|
last_root_work_item_status: pickStringField("last_root_work_item_status"),
|
|
1960
|
+
last_contract_validation_status: pickStringField("last_contract_validation_status"),
|
|
1961
|
+
last_contract_validation_reason: pickStringField("last_contract_validation_reason"),
|
|
1962
|
+
last_normalized_execution_contract_type: pickStringField("last_normalized_execution_contract_type"),
|
|
1963
|
+
last_assignment_validation_status: pickStringField("last_assignment_validation_status"),
|
|
1964
|
+
last_assignment_validation_reason: pickStringField("last_assignment_validation_reason"),
|
|
1965
|
+
last_followup_ai_reply_preview: pickStringField("last_followup_ai_reply_preview"),
|
|
1966
|
+
last_followup_execution_contract_type: pickStringField("last_followup_execution_contract_type"),
|
|
1967
|
+
last_followup_normalized_execution_contract_type: pickStringField("last_followup_normalized_execution_contract_type"),
|
|
1968
|
+
last_followup_response_contract_validation_status: pickStringField("last_followup_response_contract_validation_status"),
|
|
1969
|
+
last_followup_response_contract_validation_reason: pickStringField("last_followup_response_contract_validation_reason"),
|
|
1970
|
+
last_followup_assignment_validation_status: pickStringField("last_followup_assignment_validation_status"),
|
|
1971
|
+
last_followup_assignment_validation_reason: pickStringField("last_followup_assignment_validation_reason"),
|
|
1972
|
+
last_followup_delivery_status: pickStringField("last_followup_delivery_status"),
|
|
1973
|
+
last_followup_archive_status: pickStringField("last_followup_archive_status"),
|
|
1974
|
+
last_followup_transport_error: pickStringField("last_followup_transport_error"),
|
|
1975
|
+
last_followup_archive_error: pickStringField("last_followup_archive_error"),
|
|
1976
|
+
last_contract_validation_targets: pickArrayField("last_contract_validation_targets", normalizeTelegramMentionUsername),
|
|
1977
|
+
last_normalized_execution_contract_targets: pickArrayField("last_normalized_execution_contract_targets", normalizeTelegramMentionUsername),
|
|
1978
|
+
last_normalized_execution_next_responders: pickArrayField("last_normalized_execution_next_responders", normalizeTelegramMentionUsername),
|
|
1979
|
+
last_followup_execution_contract_targets: pickArrayField("last_followup_execution_contract_targets", normalizeTelegramMentionUsername),
|
|
1980
|
+
last_followup_next_expected_responders: pickArrayField("last_followup_next_expected_responders", normalizeTelegramMentionUsername),
|
|
1981
|
+
last_followup_normalized_execution_contract_targets: pickArrayField("last_followup_normalized_execution_contract_targets", normalizeTelegramMentionUsername),
|
|
1982
|
+
last_followup_normalized_execution_next_responders: pickArrayField("last_followup_normalized_execution_next_responders", normalizeTelegramMentionUsername),
|
|
1983
|
+
last_followup_response_contract_validation_targets: pickArrayField("last_followup_response_contract_validation_targets", normalizeTelegramMentionUsername),
|
|
1984
|
+
last_followup_assignment_validation_modes: pickArrayField("last_followup_assignment_validation_modes", (value) => String(value || "").trim().toLowerCase()),
|
|
1952
1985
|
conversation_sessions: {
|
|
1953
1986
|
...safeObject(secondary.conversation_sessions),
|
|
1954
1987
|
...safeObject(primary.conversation_sessions),
|
|
@@ -2430,26 +2463,75 @@ function normalizeBotRunnerRequests(rawRequests, nowMs = Date.now()) {
|
|
|
2430
2463
|
next_expected_responders: ensureArray(entry.next_expected_responders || entry.nextExpectedResponders)
|
|
2431
2464
|
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2432
2465
|
.filter(Boolean),
|
|
2466
|
+
normalized_execution_contract_type: String(entry.normalized_execution_contract_type || entry.normalizedExecutionContractType || "").trim().toLowerCase(),
|
|
2467
|
+
normalized_execution_contract_targets: ensureArray(entry.normalized_execution_contract_targets || entry.normalizedExecutionContractTargets)
|
|
2468
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2469
|
+
.filter(Boolean),
|
|
2470
|
+
normalized_execution_next_responders: ensureArray(entry.normalized_execution_next_responders || entry.normalizedExecutionNextResponders)
|
|
2471
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2472
|
+
.filter(Boolean),
|
|
2433
2473
|
ai_reply_generated: boolFromRaw(
|
|
2434
2474
|
entry.ai_reply_generated ?? entry.aiReplyGenerated,
|
|
2435
2475
|
false,
|
|
2436
2476
|
),
|
|
2437
2477
|
ai_reply_generated_at: firstNonEmptyString([entry.ai_reply_generated_at, entry.aiReplyGeneratedAt]),
|
|
2438
2478
|
ai_reply_preview: String(entry.ai_reply_preview || entry.aiReplyPreview || "").trim(),
|
|
2479
|
+
followup_ai_reply_preview: String(entry.followup_ai_reply_preview || entry.followupAiReplyPreview || "").trim(),
|
|
2480
|
+
root_execution_contract_type: String(entry.root_execution_contract_type || entry.rootExecutionContractType || "").trim().toLowerCase(),
|
|
2481
|
+
root_execution_contract_targets: ensureArray(entry.root_execution_contract_targets || entry.rootExecutionContractTargets)
|
|
2482
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2483
|
+
.filter(Boolean),
|
|
2484
|
+
root_next_expected_responders: ensureArray(entry.root_next_expected_responders || entry.rootNextExpectedResponders)
|
|
2485
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2486
|
+
.filter(Boolean),
|
|
2487
|
+
root_ai_reply_preview: String(entry.root_ai_reply_preview || entry.rootAiReplyPreview || "").trim(),
|
|
2488
|
+
root_response_contract_validation_status: String(entry.root_response_contract_validation_status || entry.rootResponseContractValidationStatus || "").trim().toLowerCase(),
|
|
2489
|
+
root_response_contract_validation_reason: String(entry.root_response_contract_validation_reason || entry.rootResponseContractValidationReason || "").trim(),
|
|
2490
|
+
root_response_contract_validation_targets: ensureArray(entry.root_response_contract_validation_targets || entry.rootResponseContractValidationTargets)
|
|
2491
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2492
|
+
.filter(Boolean),
|
|
2439
2493
|
response_contract_validation_status: String(entry.response_contract_validation_status || entry.responseContractValidationStatus || "").trim().toLowerCase(),
|
|
2440
2494
|
response_contract_validation_reason: String(entry.response_contract_validation_reason || entry.responseContractValidationReason || "").trim(),
|
|
2441
2495
|
response_contract_validation_targets: ensureArray(entry.response_contract_validation_targets || entry.responseContractValidationTargets)
|
|
2442
2496
|
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2443
2497
|
.filter(Boolean),
|
|
2498
|
+
followup_execution_contract_type: String(entry.followup_execution_contract_type || entry.followupExecutionContractType || "").trim().toLowerCase(),
|
|
2499
|
+
followup_execution_contract_targets: ensureArray(entry.followup_execution_contract_targets || entry.followupExecutionContractTargets)
|
|
2500
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2501
|
+
.filter(Boolean),
|
|
2502
|
+
followup_next_expected_responders: ensureArray(entry.followup_next_expected_responders || entry.followupNextExpectedResponders)
|
|
2503
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2504
|
+
.filter(Boolean),
|
|
2505
|
+
followup_normalized_execution_contract_type: String(entry.followup_normalized_execution_contract_type || entry.followupNormalizedExecutionContractType || "").trim().toLowerCase(),
|
|
2506
|
+
followup_normalized_execution_contract_targets: ensureArray(entry.followup_normalized_execution_contract_targets || entry.followupNormalizedExecutionContractTargets)
|
|
2507
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2508
|
+
.filter(Boolean),
|
|
2509
|
+
followup_normalized_execution_next_responders: ensureArray(entry.followup_normalized_execution_next_responders || entry.followupNormalizedExecutionNextResponders)
|
|
2510
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2511
|
+
.filter(Boolean),
|
|
2512
|
+
followup_response_contract_validation_status: String(entry.followup_response_contract_validation_status || entry.followupResponseContractValidationStatus || "").trim().toLowerCase(),
|
|
2513
|
+
followup_response_contract_validation_reason: String(entry.followup_response_contract_validation_reason || entry.followupResponseContractValidationReason || "").trim(),
|
|
2514
|
+
followup_response_contract_validation_targets: ensureArray(entry.followup_response_contract_validation_targets || entry.followupResponseContractValidationTargets)
|
|
2515
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
2516
|
+
.filter(Boolean),
|
|
2444
2517
|
assignment_validation_status: String(entry.assignment_validation_status || entry.assignmentValidationStatus || "").trim().toLowerCase(),
|
|
2445
2518
|
assignment_validation_reason: String(entry.assignment_validation_reason || entry.assignmentValidationReason || "").trim(),
|
|
2446
2519
|
assignment_validation_modes: ensureArray(entry.assignment_validation_modes || entry.assignmentValidationModes)
|
|
2447
2520
|
.map((value) => String(value || "").trim().toLowerCase())
|
|
2448
2521
|
.filter(Boolean),
|
|
2522
|
+
followup_assignment_validation_status: String(entry.followup_assignment_validation_status || entry.followupAssignmentValidationStatus || "").trim().toLowerCase(),
|
|
2523
|
+
followup_assignment_validation_reason: String(entry.followup_assignment_validation_reason || entry.followupAssignmentValidationReason || "").trim(),
|
|
2524
|
+
followup_assignment_validation_modes: ensureArray(entry.followup_assignment_validation_modes || entry.followupAssignmentValidationModes)
|
|
2525
|
+
.map((value) => String(value || "").trim().toLowerCase())
|
|
2526
|
+
.filter(Boolean),
|
|
2449
2527
|
delivery_status: String(entry.delivery_status || entry.deliveryStatus || "").trim().toLowerCase(),
|
|
2450
2528
|
archive_status: String(entry.archive_status || entry.archiveStatus || "").trim().toLowerCase(),
|
|
2451
2529
|
transport_error: String(entry.transport_error || entry.transportError || "").trim(),
|
|
2452
2530
|
archive_error: String(entry.archive_error || entry.archiveError || "").trim(),
|
|
2531
|
+
followup_delivery_status: String(entry.followup_delivery_status || entry.followupDeliveryStatus || "").trim().toLowerCase(),
|
|
2532
|
+
followup_archive_status: String(entry.followup_archive_status || entry.followupArchiveStatus || "").trim().toLowerCase(),
|
|
2533
|
+
followup_transport_error: String(entry.followup_transport_error || entry.followupTransportError || "").trim(),
|
|
2534
|
+
followup_archive_error: String(entry.followup_archive_error || entry.followupArchiveError || "").trim(),
|
|
2453
2535
|
normalized_intent: String(entry.normalized_intent || entry.normalizedIntent || "").trim().toLowerCase(),
|
|
2454
2536
|
status,
|
|
2455
2537
|
claimed_by_route: String(entry.claimed_by_route || entry.claimedByRoute || "").trim(),
|
|
@@ -3031,11 +3113,341 @@ function runnerRequestHasConversationContractData(entryRaw) {
|
|
|
3031
3113
|
|| ensureArray(entry.conversation_allowed_responders).length
|
|
3032
3114
|
|| entry.conversation_allow_bot_to_bot === true
|
|
3033
3115
|
|| String(entry.conversation_reply_expectation || "").trim()
|
|
3034
|
-
||
|
|
3035
|
-
|| entry
|
|
3036
|
-
||
|
|
3037
|
-
||
|
|
3116
|
+
|| runnerRequestPreferredExecutionContractType(entry)
|
|
3117
|
+
|| runnerRequestPreferredExecutionContractActionable(entry) === true
|
|
3118
|
+
|| runnerRequestPreferredExecutionContractTargets(entry).length
|
|
3119
|
+
|| runnerRequestPreferredNextExpectedResponders(entry).length
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
function runnerRequestPreferredExecutionContractType(entryRaw) {
|
|
3124
|
+
const entry = safeObject(entryRaw);
|
|
3125
|
+
return String(
|
|
3126
|
+
entry.execution_contract_type
|
|
3127
|
+
|| entry.followup_execution_contract_type
|
|
3128
|
+
|| entry.root_execution_contract_type
|
|
3129
|
+
|| "",
|
|
3130
|
+
).trim().toLowerCase();
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
function runnerRequestPreferredExecutionContractActionable(entryRaw) {
|
|
3134
|
+
const entry = safeObject(entryRaw);
|
|
3135
|
+
return entry.execution_contract_actionable === true;
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
function runnerRequestPreferredExecutionContractTargets(entryRaw) {
|
|
3139
|
+
const entry = safeObject(entryRaw);
|
|
3140
|
+
return uniqueOrderedStrings(
|
|
3141
|
+
ensureArray(entry.execution_contract_targets).length
|
|
3142
|
+
? entry.execution_contract_targets
|
|
3143
|
+
: ensureArray(entry.followup_execution_contract_targets).length
|
|
3144
|
+
? entry.followup_execution_contract_targets
|
|
3145
|
+
: ensureArray(entry.root_execution_contract_targets).length
|
|
3146
|
+
? entry.root_execution_contract_targets
|
|
3147
|
+
: [],
|
|
3148
|
+
normalizeTelegramMentionUsername,
|
|
3149
|
+
);
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
function runnerRequestPreferredNextExpectedResponders(entryRaw) {
|
|
3153
|
+
const entry = safeObject(entryRaw);
|
|
3154
|
+
return uniqueOrderedStrings(
|
|
3155
|
+
ensureArray(entry.next_expected_responders).length
|
|
3156
|
+
? entry.next_expected_responders
|
|
3157
|
+
: ensureArray(entry.followup_next_expected_responders).length
|
|
3158
|
+
? entry.followup_next_expected_responders
|
|
3159
|
+
: ensureArray(entry.root_next_expected_responders).length
|
|
3160
|
+
? entry.root_next_expected_responders
|
|
3161
|
+
: [],
|
|
3162
|
+
normalizeTelegramMentionUsername,
|
|
3163
|
+
);
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3166
|
+
function runnerRequestPreferredAIReplyPreview(entryRaw) {
|
|
3167
|
+
const entry = safeObject(entryRaw);
|
|
3168
|
+
return String(
|
|
3169
|
+
entry.ai_reply_preview
|
|
3170
|
+
|| entry.followup_ai_reply_preview
|
|
3171
|
+
|| entry.root_ai_reply_preview
|
|
3172
|
+
|| "",
|
|
3173
|
+
).trim();
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3176
|
+
function runnerRequestPreferredResponseContractValidationStatus(entryRaw) {
|
|
3177
|
+
const entry = safeObject(entryRaw);
|
|
3178
|
+
return String(
|
|
3179
|
+
entry.response_contract_validation_status
|
|
3180
|
+
|| entry.followup_response_contract_validation_status
|
|
3181
|
+
|| entry.root_response_contract_validation_status
|
|
3182
|
+
|| "",
|
|
3183
|
+
).trim().toLowerCase();
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3186
|
+
function runnerRequestPreferredResponseContractValidationReason(entryRaw) {
|
|
3187
|
+
const entry = safeObject(entryRaw);
|
|
3188
|
+
return String(
|
|
3189
|
+
entry.response_contract_validation_reason
|
|
3190
|
+
|| entry.followup_response_contract_validation_reason
|
|
3191
|
+
|| entry.root_response_contract_validation_reason
|
|
3192
|
+
|| "",
|
|
3193
|
+
).trim();
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
function runnerRequestPreferredResponseContractValidationTargets(entryRaw) {
|
|
3197
|
+
const entry = safeObject(entryRaw);
|
|
3198
|
+
return uniqueOrderedStrings(
|
|
3199
|
+
ensureArray(entry.response_contract_validation_targets).length
|
|
3200
|
+
? entry.response_contract_validation_targets
|
|
3201
|
+
: ensureArray(entry.followup_response_contract_validation_targets).length
|
|
3202
|
+
? entry.followup_response_contract_validation_targets
|
|
3203
|
+
: ensureArray(entry.root_response_contract_validation_targets).length
|
|
3204
|
+
? entry.root_response_contract_validation_targets
|
|
3205
|
+
: [],
|
|
3206
|
+
normalizeTelegramMentionUsername,
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
function runnerRequestPreferredAssignmentValidationStatus(entryRaw) {
|
|
3211
|
+
const entry = safeObject(entryRaw);
|
|
3212
|
+
return String(
|
|
3213
|
+
entry.assignment_validation_status
|
|
3214
|
+
|| entry.followup_assignment_validation_status
|
|
3215
|
+
|| "",
|
|
3216
|
+
).trim().toLowerCase();
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
function runnerRequestPreferredAssignmentValidationReason(entryRaw) {
|
|
3220
|
+
const entry = safeObject(entryRaw);
|
|
3221
|
+
return String(
|
|
3222
|
+
entry.assignment_validation_reason
|
|
3223
|
+
|| entry.followup_assignment_validation_reason
|
|
3224
|
+
|| "",
|
|
3225
|
+
).trim();
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
function runnerRequestPreferredAssignmentValidationModes(entryRaw) {
|
|
3229
|
+
const entry = safeObject(entryRaw);
|
|
3230
|
+
return uniqueOrderedStrings(
|
|
3231
|
+
ensureArray(entry.assignment_validation_modes).length
|
|
3232
|
+
? entry.assignment_validation_modes
|
|
3233
|
+
: ensureArray(entry.followup_assignment_validation_modes).length
|
|
3234
|
+
? entry.followup_assignment_validation_modes
|
|
3235
|
+
: [],
|
|
3236
|
+
(value) => String(value || "").trim().toLowerCase(),
|
|
3237
|
+
);
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
function runnerRequestPreferredDeliveryStatus(entryRaw) {
|
|
3241
|
+
const entry = safeObject(entryRaw);
|
|
3242
|
+
return String(
|
|
3243
|
+
entry.delivery_status
|
|
3244
|
+
|| entry.followup_delivery_status
|
|
3245
|
+
|| "",
|
|
3246
|
+
).trim().toLowerCase();
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
function runnerRequestPreferredArchiveStatus(entryRaw) {
|
|
3250
|
+
const entry = safeObject(entryRaw);
|
|
3251
|
+
return String(
|
|
3252
|
+
entry.archive_status
|
|
3253
|
+
|| entry.followup_archive_status
|
|
3254
|
+
|| "",
|
|
3255
|
+
).trim().toLowerCase();
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
function runnerRequestPreferredTransportError(entryRaw) {
|
|
3259
|
+
const entry = safeObject(entryRaw);
|
|
3260
|
+
return String(
|
|
3261
|
+
entry.transport_error
|
|
3262
|
+
|| entry.followup_transport_error
|
|
3263
|
+
|| "",
|
|
3264
|
+
).trim();
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3267
|
+
function runnerRequestPreferredArchiveError(entryRaw) {
|
|
3268
|
+
const entry = safeObject(entryRaw);
|
|
3269
|
+
return String(
|
|
3270
|
+
entry.archive_error
|
|
3271
|
+
|| entry.followup_archive_error
|
|
3272
|
+
|| "",
|
|
3273
|
+
).trim();
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
function buildRunnerValidationAndDeliverySummary({
|
|
3277
|
+
aiReplyPreview = "",
|
|
3278
|
+
executionContractType = "",
|
|
3279
|
+
executionContractTargets = [],
|
|
3280
|
+
nextExpectedResponders = [],
|
|
3281
|
+
responseContractValidationStatus = "",
|
|
3282
|
+
responseContractValidationReason = "",
|
|
3283
|
+
responseContractValidationTargets = [],
|
|
3284
|
+
assignmentValidationStatus = "",
|
|
3285
|
+
assignmentValidationReason = "",
|
|
3286
|
+
assignmentValidationModes = [],
|
|
3287
|
+
deliveryStatus = "",
|
|
3288
|
+
archiveStatus = "",
|
|
3289
|
+
transportError = "",
|
|
3290
|
+
archiveError = "",
|
|
3291
|
+
} = {}) {
|
|
3292
|
+
return {
|
|
3293
|
+
ai_reply_preview: String(aiReplyPreview || "").trim(),
|
|
3294
|
+
execution_contract_type: String(executionContractType || "").trim().toLowerCase(),
|
|
3295
|
+
execution_contract_targets: uniqueOrderedStrings(
|
|
3296
|
+
ensureArray(executionContractTargets),
|
|
3297
|
+
normalizeTelegramMentionUsername,
|
|
3298
|
+
),
|
|
3299
|
+
next_expected_responders: uniqueOrderedStrings(
|
|
3300
|
+
ensureArray(nextExpectedResponders),
|
|
3301
|
+
normalizeTelegramMentionUsername,
|
|
3302
|
+
),
|
|
3303
|
+
response_contract_validation_status: String(responseContractValidationStatus || "").trim().toLowerCase(),
|
|
3304
|
+
response_contract_validation_reason: String(responseContractValidationReason || "").trim(),
|
|
3305
|
+
response_contract_validation_targets: uniqueOrderedStrings(
|
|
3306
|
+
ensureArray(responseContractValidationTargets),
|
|
3307
|
+
normalizeTelegramMentionUsername,
|
|
3308
|
+
),
|
|
3309
|
+
assignment_validation_status: String(assignmentValidationStatus || "").trim().toLowerCase(),
|
|
3310
|
+
assignment_validation_reason: String(assignmentValidationReason || "").trim(),
|
|
3311
|
+
assignment_validation_modes: uniqueOrderedStrings(
|
|
3312
|
+
ensureArray(assignmentValidationModes),
|
|
3313
|
+
(value) => String(value || "").trim().toLowerCase(),
|
|
3314
|
+
),
|
|
3315
|
+
delivery_status: String(deliveryStatus || "").trim().toLowerCase(),
|
|
3316
|
+
archive_status: String(archiveStatus || "").trim().toLowerCase(),
|
|
3317
|
+
transport_error: String(transportError || "").trim(),
|
|
3318
|
+
archive_error: String(archiveError || "").trim(),
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
function buildRunnerRequestRootWorkItemSummary(entryRaw) {
|
|
3323
|
+
const entry = safeObject(entryRaw);
|
|
3324
|
+
const rootWorkItemID = String(entry.root_work_item_id || "").trim();
|
|
3325
|
+
if (!rootWorkItemID) {
|
|
3326
|
+
return null;
|
|
3327
|
+
}
|
|
3328
|
+
return {
|
|
3329
|
+
id: rootWorkItemID,
|
|
3330
|
+
title: String(entry.root_work_item_title || "").trim(),
|
|
3331
|
+
status: normalizeRunnerWorkItemStatus(entry.root_work_item_status),
|
|
3332
|
+
thread_id: String(entry.root_thread_id || "").trim(),
|
|
3333
|
+
};
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
function buildRunnerRouteLastResultSummary(routeStateRaw) {
|
|
3337
|
+
const routeState = safeObject(routeStateRaw);
|
|
3338
|
+
const executionContractType = String(
|
|
3339
|
+
routeState.last_followup_execution_contract_type
|
|
3340
|
+
|| routeState.last_normalized_execution_contract_type
|
|
3341
|
+
|| "",
|
|
3342
|
+
).trim().toLowerCase();
|
|
3343
|
+
const executionContractTargets = uniqueOrderedStrings(
|
|
3344
|
+
ensureArray(routeState.last_followup_execution_contract_targets).length
|
|
3345
|
+
? routeState.last_followup_execution_contract_targets
|
|
3346
|
+
: ensureArray(routeState.last_normalized_execution_contract_targets),
|
|
3347
|
+
normalizeTelegramMentionUsername,
|
|
3348
|
+
);
|
|
3349
|
+
const nextExpectedResponders = uniqueOrderedStrings(
|
|
3350
|
+
ensureArray(routeState.last_followup_next_expected_responders).length
|
|
3351
|
+
? routeState.last_followup_next_expected_responders
|
|
3352
|
+
: ensureArray(routeState.last_normalized_execution_next_responders),
|
|
3353
|
+
normalizeTelegramMentionUsername,
|
|
3354
|
+
);
|
|
3355
|
+
const responseContractValidationStatus = String(
|
|
3356
|
+
routeState.last_followup_response_contract_validation_status
|
|
3357
|
+
|| routeState.last_contract_validation_status
|
|
3358
|
+
|| "",
|
|
3359
|
+
).trim().toLowerCase();
|
|
3360
|
+
const responseContractValidationReason = String(
|
|
3361
|
+
routeState.last_followup_response_contract_validation_reason
|
|
3362
|
+
|| routeState.last_contract_validation_reason
|
|
3363
|
+
|| "",
|
|
3364
|
+
).trim();
|
|
3365
|
+
const responseContractValidationTargets = uniqueOrderedStrings(
|
|
3366
|
+
ensureArray(routeState.last_followup_response_contract_validation_targets).length
|
|
3367
|
+
? routeState.last_followup_response_contract_validation_targets
|
|
3368
|
+
: ensureArray(routeState.last_contract_validation_targets),
|
|
3369
|
+
normalizeTelegramMentionUsername,
|
|
3370
|
+
);
|
|
3371
|
+
const assignmentValidationStatus = String(
|
|
3372
|
+
routeState.last_followup_assignment_validation_status
|
|
3373
|
+
|| routeState.last_assignment_validation_status
|
|
3374
|
+
|| "",
|
|
3375
|
+
).trim().toLowerCase();
|
|
3376
|
+
const assignmentValidationReason = String(
|
|
3377
|
+
routeState.last_followup_assignment_validation_reason
|
|
3378
|
+
|| routeState.last_assignment_validation_reason
|
|
3379
|
+
|| "",
|
|
3380
|
+
).trim();
|
|
3381
|
+
const assignmentValidationModes = uniqueOrderedStrings(
|
|
3382
|
+
ensureArray(routeState.last_followup_assignment_validation_modes),
|
|
3383
|
+
(value) => String(value || "").trim().toLowerCase(),
|
|
3038
3384
|
);
|
|
3385
|
+
return {
|
|
3386
|
+
action: String(routeState.last_action || "").trim(),
|
|
3387
|
+
reason: String(routeState.last_reason || "").trim(),
|
|
3388
|
+
intent_type: String(routeState.last_intent_type || "").trim(),
|
|
3389
|
+
...buildRunnerValidationAndDeliverySummary({
|
|
3390
|
+
aiReplyPreview: routeState.last_followup_ai_reply_preview,
|
|
3391
|
+
executionContractType,
|
|
3392
|
+
executionContractTargets,
|
|
3393
|
+
nextExpectedResponders,
|
|
3394
|
+
responseContractValidationStatus,
|
|
3395
|
+
responseContractValidationReason,
|
|
3396
|
+
responseContractValidationTargets,
|
|
3397
|
+
assignmentValidationStatus,
|
|
3398
|
+
assignmentValidationReason,
|
|
3399
|
+
assignmentValidationModes,
|
|
3400
|
+
deliveryStatus: routeState.last_followup_delivery_status,
|
|
3401
|
+
archiveStatus: routeState.last_followup_archive_status,
|
|
3402
|
+
transportError: routeState.last_followup_transport_error,
|
|
3403
|
+
archiveError: routeState.last_followup_archive_error,
|
|
3404
|
+
}),
|
|
3405
|
+
workspace_dir: String(routeState.last_workspace_dir || "").trim(),
|
|
3406
|
+
artifact_validation: String(routeState.last_artifact_validation || "").trim(),
|
|
3407
|
+
artifact_paths: ensureArray(routeState.last_artifact_paths).map((item) => String(item || "").trim()).filter(Boolean),
|
|
3408
|
+
artifact_errors: ensureArray(routeState.last_artifact_errors).map((item) => String(item || "").trim()).filter(Boolean),
|
|
3409
|
+
boundary_violations: ensureArray(routeState.last_boundary_violations).map((item) => safeObject(item)),
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
function buildRunnerStatusLookupWorkItemSummary({
|
|
3414
|
+
relatedRequest,
|
|
3415
|
+
routeState,
|
|
3416
|
+
currentConversationID,
|
|
3417
|
+
}) {
|
|
3418
|
+
const requestRootWorkItem = buildRunnerRequestRootWorkItemSummary(relatedRequest);
|
|
3419
|
+
const safeRouteState = safeObject(routeState);
|
|
3420
|
+
const routeConversationID = String(safeRouteState.last_conversation_id || "").trim();
|
|
3421
|
+
const activeRootWorkItemID = String(safeRouteState.active_root_work_item_id || "").trim();
|
|
3422
|
+
const activeRootWorkItemTitle = String(safeRouteState.active_root_work_item_title || "").trim();
|
|
3423
|
+
const activeRootWorkItemStatus = normalizeRunnerWorkItemStatus(safeRouteState.active_root_work_item_status);
|
|
3424
|
+
const routeRootWorkItemID = String(safeRouteState.last_root_work_item_id || "").trim();
|
|
3425
|
+
const routeRootWorkItemTitle = String(safeRouteState.last_root_work_item_title || "").trim();
|
|
3426
|
+
const routeRootWorkItemStatus = normalizeRunnerWorkItemStatus(safeRouteState.last_root_work_item_status);
|
|
3427
|
+
const routeWorkItemIDs = ensureArray(safeRouteState.last_work_item_ids).map((item) => String(item || "").trim()).filter(Boolean);
|
|
3428
|
+
const routeWorkItemTitles = ensureArray(safeRouteState.last_work_item_titles).map((item) => String(item || "").trim()).filter(Boolean);
|
|
3429
|
+
return {
|
|
3430
|
+
root_work_item: requestRootWorkItem
|
|
3431
|
+
|| (activeRootWorkItemID
|
|
3432
|
+
? {
|
|
3433
|
+
id: activeRootWorkItemID,
|
|
3434
|
+
title: activeRootWorkItemTitle,
|
|
3435
|
+
status: activeRootWorkItemStatus,
|
|
3436
|
+
}
|
|
3437
|
+
: currentConversationID && routeConversationID === currentConversationID && routeRootWorkItemID
|
|
3438
|
+
? {
|
|
3439
|
+
id: routeRootWorkItemID,
|
|
3440
|
+
title: routeRootWorkItemTitle,
|
|
3441
|
+
status: routeRootWorkItemStatus,
|
|
3442
|
+
}
|
|
3443
|
+
: null),
|
|
3444
|
+
route_work_items: currentConversationID && routeConversationID === currentConversationID && (routeWorkItemIDs.length > 0 || routeWorkItemTitles.length > 0)
|
|
3445
|
+
? {
|
|
3446
|
+
ids: routeWorkItemIDs,
|
|
3447
|
+
titles: routeWorkItemTitles,
|
|
3448
|
+
}
|
|
3449
|
+
: null,
|
|
3450
|
+
};
|
|
3039
3451
|
}
|
|
3040
3452
|
|
|
3041
3453
|
function pickRunnerSharedConversationSourceRequest(entries = [], excludeRequestKey = "") {
|
|
@@ -3628,28 +4040,26 @@ async function claimRunnerRequestForHumanComment({
|
|
|
3628
4040
|
|| referencedRequest.conversation_reply_expectation
|
|
3629
4041
|
|| "",
|
|
3630
4042
|
).trim().toLowerCase(),
|
|
3631
|
-
execution_contract_type:
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
execution_contract_actionable: existing
|
|
3635
|
-
|| sharedConversationSource
|
|
3636
|
-
|| referencedRequest
|
|
4043
|
+
execution_contract_type: runnerRequestPreferredExecutionContractType(existing)
|
|
4044
|
+
|| runnerRequestPreferredExecutionContractType(sharedConversationSource)
|
|
4045
|
+
|| runnerRequestPreferredExecutionContractType(referencedRequest),
|
|
4046
|
+
execution_contract_actionable: runnerRequestPreferredExecutionContractActionable(existing)
|
|
4047
|
+
|| runnerRequestPreferredExecutionContractActionable(sharedConversationSource)
|
|
4048
|
+
|| runnerRequestPreferredExecutionContractActionable(referencedRequest),
|
|
3637
4049
|
execution_contract_targets: uniqueOrderedStrings(
|
|
3638
|
-
|
|
3639
|
-
? existing
|
|
3640
|
-
:
|
|
3641
|
-
? sharedConversationSource
|
|
3642
|
-
: referencedRequest
|
|
4050
|
+
runnerRequestPreferredExecutionContractTargets(existing).length
|
|
4051
|
+
? runnerRequestPreferredExecutionContractTargets(existing)
|
|
4052
|
+
: runnerRequestPreferredExecutionContractTargets(sharedConversationSource).length
|
|
4053
|
+
? runnerRequestPreferredExecutionContractTargets(sharedConversationSource)
|
|
4054
|
+
: runnerRequestPreferredExecutionContractTargets(referencedRequest),
|
|
3643
4055
|
normalizeTelegramMentionUsername,
|
|
3644
4056
|
),
|
|
3645
4057
|
next_expected_responders: uniqueOrderedStrings(
|
|
3646
|
-
|
|
3647
|
-
? existing
|
|
3648
|
-
:
|
|
3649
|
-
? sharedConversationSource
|
|
3650
|
-
:
|
|
3651
|
-
? referencedRequest.next_expected_responders
|
|
3652
|
-
: [],
|
|
4058
|
+
runnerRequestPreferredNextExpectedResponders(existing).length
|
|
4059
|
+
? runnerRequestPreferredNextExpectedResponders(existing)
|
|
4060
|
+
: runnerRequestPreferredNextExpectedResponders(sharedConversationSource).length
|
|
4061
|
+
? runnerRequestPreferredNextExpectedResponders(sharedConversationSource)
|
|
4062
|
+
: runnerRequestPreferredNextExpectedResponders(referencedRequest),
|
|
3653
4063
|
normalizeTelegramMentionUsername,
|
|
3654
4064
|
),
|
|
3655
4065
|
normalized_intent: String(preferredNormalizedIntent || existing.normalized_intent || "").trim().toLowerCase(),
|
|
@@ -3698,92 +4108,15 @@ async function claimRunnerRequestForHumanComment({
|
|
|
3698
4108
|
};
|
|
3699
4109
|
}
|
|
3700
4110
|
|
|
3701
|
-
function isActionableRunnerRequestIntent(rawIntent) {
|
|
3702
|
-
const normalizedIntent = String(rawIntent || "").trim().toLowerCase();
|
|
3703
|
-
return Boolean(normalizedIntent) && !isInformationalRunnerRequestIntent(normalizedIntent);
|
|
3704
|
-
}
|
|
3705
|
-
|
|
3706
|
-
function runnerRequestHasConversationContract(requestRaw) {
|
|
3707
|
-
const request = safeObject(requestRaw);
|
|
3708
|
-
return Boolean(
|
|
3709
|
-
String(request.conversation_id || "").trim()
|
|
3710
|
-
|| String(request.conversation_intent_mode || "").trim()
|
|
3711
|
-
|| String(request.conversation_reply_expectation || "").trim()
|
|
3712
|
-
|| ensureArray(request.conversation_participants).length
|
|
3713
|
-
|| ensureArray(request.conversation_initial_responders).length
|
|
3714
|
-
|| ensureArray(request.conversation_allowed_responders).length
|
|
3715
|
-
);
|
|
3716
|
-
}
|
|
3717
|
-
|
|
3718
|
-
function runnerRequestHasCompleteConversationContract(requestRaw) {
|
|
3719
|
-
const request = safeObject(requestRaw);
|
|
3720
|
-
const intentMode = String(request.conversation_intent_mode || "").trim().toLowerCase();
|
|
3721
|
-
const conversationID = String(request.conversation_id || "").trim();
|
|
3722
|
-
const replyExpectation = String(request.conversation_reply_expectation || "").trim().toLowerCase();
|
|
3723
|
-
const participants = ensureArray(request.conversation_participants);
|
|
3724
|
-
const initialResponders = ensureArray(request.conversation_initial_responders);
|
|
3725
|
-
const allowedResponders = ensureArray(request.conversation_allowed_responders);
|
|
3726
|
-
if (!runnerRequestHasConversationContract(request)) {
|
|
3727
|
-
return false;
|
|
3728
|
-
}
|
|
3729
|
-
if (!conversationID || !intentMode || !replyExpectation) {
|
|
3730
|
-
return false;
|
|
3731
|
-
}
|
|
3732
|
-
if (intentMode === "single_bot") {
|
|
3733
|
-
return allowedResponders.length > 0 || participants.length > 0;
|
|
3734
|
-
}
|
|
3735
|
-
return participants.length > 0 && initialResponders.length > 0 && allowedResponders.length > 0;
|
|
3736
|
-
}
|
|
3737
|
-
|
|
3738
|
-
function runnerRequestHasExecutionContract(requestRaw) {
|
|
3739
|
-
const request = safeObject(requestRaw);
|
|
3740
|
-
return Boolean(
|
|
3741
|
-
String(request.execution_contract_type || "").trim()
|
|
3742
|
-
|| request.execution_contract_actionable === true
|
|
3743
|
-
|| ensureArray(request.execution_contract_targets).length
|
|
3744
|
-
|| ensureArray(request.next_expected_responders).length
|
|
3745
|
-
);
|
|
3746
|
-
}
|
|
3747
|
-
|
|
3748
|
-
function runnerRequestHasCompleteExecutionContract(requestRaw) {
|
|
3749
|
-
const request = safeObject(requestRaw);
|
|
3750
|
-
const executionContractType = String(request.execution_contract_type || "").trim().toLowerCase();
|
|
3751
|
-
const executionTargets = ensureArray(request.execution_contract_targets);
|
|
3752
|
-
const nextExpectedResponders = ensureArray(request.next_expected_responders);
|
|
3753
|
-
if (!runnerRequestHasExecutionContract(request)) {
|
|
3754
|
-
return false;
|
|
3755
|
-
}
|
|
3756
|
-
if (!executionContractType) {
|
|
3757
|
-
return false;
|
|
3758
|
-
}
|
|
3759
|
-
if (executionContractType === "delegation") {
|
|
3760
|
-
return executionTargets.length > 0 || nextExpectedResponders.length > 0;
|
|
3761
|
-
}
|
|
3762
|
-
if (executionContractType === "summary_request") {
|
|
3763
|
-
return nextExpectedResponders.length > 0 || executionTargets.length > 0;
|
|
3764
|
-
}
|
|
3765
|
-
return true;
|
|
3766
|
-
}
|
|
3767
|
-
|
|
3768
|
-
function runnerRequestHasContractSignals(requestRaw) {
|
|
3769
|
-
const request = safeObject(requestRaw);
|
|
3770
|
-
return runnerRequestHasConversationContract(request) || runnerRequestHasExecutionContract(request);
|
|
3771
|
-
}
|
|
3772
|
-
|
|
3773
4111
|
function runnerRequestRequiresActionableContract(requestRaw) {
|
|
3774
4112
|
const request = safeObject(requestRaw);
|
|
3775
|
-
const
|
|
3776
|
-
|
|
3777
|
-
const intentMode = String(request.conversation_intent_mode || "").trim().toLowerCase();
|
|
3778
|
-
if (request.execution_contract_actionable === true) {
|
|
4113
|
+
const executionContractType = runnerRequestPreferredExecutionContractType(request);
|
|
4114
|
+
if (runnerRequestPreferredExecutionContractActionable(request) === true) {
|
|
3779
4115
|
return true;
|
|
3780
4116
|
}
|
|
3781
4117
|
if (["delegation", "direct_result", "summary_request", "final_summary"].includes(executionContractType)) {
|
|
3782
4118
|
return true;
|
|
3783
4119
|
}
|
|
3784
|
-
if (intentMode === "single_bot" && replyExpectation === "actionable") {
|
|
3785
|
-
return true;
|
|
3786
|
-
}
|
|
3787
4120
|
return false;
|
|
3788
4121
|
}
|
|
3789
4122
|
|
|
@@ -3816,13 +4149,9 @@ function truncateRunnerWorkItemTitleText(rawText, maxLength = 96) {
|
|
|
3816
4149
|
|
|
3817
4150
|
function buildRunnerRootWorkItemTitle({ selectedRecord, request }) {
|
|
3818
4151
|
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
3819
|
-
|
|
4152
|
+
void request;
|
|
3820
4153
|
const requestBody = truncateRunnerWorkItemTitleText(parsed.body || "", 84);
|
|
3821
|
-
const prefix =
|
|
3822
|
-
? "Ctxpack request"
|
|
3823
|
-
: intent === "workitem_mutation"
|
|
3824
|
-
? "Work item request"
|
|
3825
|
-
: "Runner request";
|
|
4154
|
+
const prefix = "Runner request";
|
|
3826
4155
|
if (requestBody) {
|
|
3827
4156
|
return `${prefix}: ${requestBody}`;
|
|
3828
4157
|
}
|
|
@@ -4270,6 +4599,52 @@ function buildRunnerRootWorkItemTransitionPath(currentStatusRaw, targetStatusRaw
|
|
|
4270
4599
|
return [];
|
|
4271
4600
|
}
|
|
4272
4601
|
|
|
4602
|
+
function buildRunnerRouteFollowupSnapshotPatch(selectedRecordRaw, resultRaw = {}) {
|
|
4603
|
+
const parsed = safeObject(safeObject(selectedRecordRaw).parsedArchive);
|
|
4604
|
+
const commentKind = String(parsed.kind || "").trim().toLowerCase();
|
|
4605
|
+
if (["telegram_message", "telegram_edited_message"].includes(commentKind)) {
|
|
4606
|
+
return {};
|
|
4607
|
+
}
|
|
4608
|
+
const result = safeObject(resultRaw);
|
|
4609
|
+
return cleanupRunnerStateRecord({
|
|
4610
|
+
last_followup_ai_reply_preview: String(result.ai_reply_preview || "").trim(),
|
|
4611
|
+
last_followup_execution_contract_type: String(result.execution_contract_type || "").trim().toLowerCase(),
|
|
4612
|
+
last_followup_execution_contract_targets: uniqueOrderedStrings(
|
|
4613
|
+
ensureArray(result.execution_contract_targets),
|
|
4614
|
+
normalizeTelegramMentionUsername,
|
|
4615
|
+
),
|
|
4616
|
+
last_followup_next_expected_responders: uniqueOrderedStrings(
|
|
4617
|
+
ensureArray(result.next_expected_responders),
|
|
4618
|
+
normalizeTelegramMentionUsername,
|
|
4619
|
+
),
|
|
4620
|
+
last_followup_normalized_execution_contract_type: String(result.normalized_execution_contract_type || "").trim().toLowerCase(),
|
|
4621
|
+
last_followup_normalized_execution_contract_targets: uniqueOrderedStrings(
|
|
4622
|
+
ensureArray(result.normalized_execution_contract_targets),
|
|
4623
|
+
normalizeTelegramMentionUsername,
|
|
4624
|
+
),
|
|
4625
|
+
last_followup_normalized_execution_next_responders: uniqueOrderedStrings(
|
|
4626
|
+
ensureArray(result.normalized_execution_next_responders),
|
|
4627
|
+
normalizeTelegramMentionUsername,
|
|
4628
|
+
),
|
|
4629
|
+
last_followup_response_contract_validation_status: String(result.response_contract_validation_status || "").trim().toLowerCase(),
|
|
4630
|
+
last_followup_response_contract_validation_reason: String(result.response_contract_validation_reason || "").trim(),
|
|
4631
|
+
last_followup_response_contract_validation_targets: uniqueOrderedStrings(
|
|
4632
|
+
ensureArray(result.response_contract_validation_targets),
|
|
4633
|
+
normalizeTelegramMentionUsername,
|
|
4634
|
+
),
|
|
4635
|
+
last_followup_assignment_validation_status: String(result.assignment_validation_status || "").trim().toLowerCase(),
|
|
4636
|
+
last_followup_assignment_validation_reason: String(result.assignment_validation_reason || "").trim(),
|
|
4637
|
+
last_followup_assignment_validation_modes: uniqueOrderedStrings(
|
|
4638
|
+
ensureArray(result.assignment_validation_modes),
|
|
4639
|
+
(value) => String(value || "").trim().toLowerCase(),
|
|
4640
|
+
),
|
|
4641
|
+
last_followup_delivery_status: String(result.delivery_status || "").trim().toLowerCase(),
|
|
4642
|
+
last_followup_archive_status: String(result.archive_status || "").trim().toLowerCase(),
|
|
4643
|
+
last_followup_transport_error: String(result.transport_error || "").trim(),
|
|
4644
|
+
last_followup_archive_error: String(result.archive_error || "").trim(),
|
|
4645
|
+
});
|
|
4646
|
+
}
|
|
4647
|
+
|
|
4273
4648
|
function buildRunnerRequestRecoveryPatchFromRouteState(currentStateRaw, requestRaw, routeKeyHint = "") {
|
|
4274
4649
|
const currentState = safeObject(currentStateRaw);
|
|
4275
4650
|
const request = safeObject(requestRaw);
|
|
@@ -4314,6 +4689,44 @@ function buildRunnerRequestRecoveryPatchFromRouteState(currentStateRaw, requestR
|
|
|
4314
4689
|
|| routeState.last_root_work_item_status,
|
|
4315
4690
|
);
|
|
4316
4691
|
}
|
|
4692
|
+
const setFollowupStringPatch = (requestField, routeFields = []) => {
|
|
4693
|
+
if (String(request[requestField] || "").trim()) {
|
|
4694
|
+
return;
|
|
4695
|
+
}
|
|
4696
|
+
const recovered = firstNonEmptyString(routeFields.map((field) => routeState[field]));
|
|
4697
|
+
if (recovered) {
|
|
4698
|
+
patch[requestField] = recovered;
|
|
4699
|
+
}
|
|
4700
|
+
};
|
|
4701
|
+
const setFollowupArrayPatch = (requestField, routeFields = [], normalizer = normalizeTelegramMentionUsername) => {
|
|
4702
|
+
if (ensureArray(request[requestField]).length) {
|
|
4703
|
+
return;
|
|
4704
|
+
}
|
|
4705
|
+
for (const field of ensureArray(routeFields)) {
|
|
4706
|
+
const recovered = uniqueOrderedStrings(ensureArray(routeState[field]), normalizer).filter(Boolean);
|
|
4707
|
+
if (recovered.length) {
|
|
4708
|
+
patch[requestField] = recovered;
|
|
4709
|
+
return;
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
};
|
|
4713
|
+
setFollowupStringPatch("followup_ai_reply_preview", ["last_followup_ai_reply_preview"]);
|
|
4714
|
+
setFollowupStringPatch("followup_execution_contract_type", ["last_followup_execution_contract_type"]);
|
|
4715
|
+
setFollowupArrayPatch("followup_execution_contract_targets", ["last_followup_execution_contract_targets"]);
|
|
4716
|
+
setFollowupArrayPatch("followup_next_expected_responders", ["last_followup_next_expected_responders"]);
|
|
4717
|
+
setFollowupStringPatch("followup_normalized_execution_contract_type", ["last_followup_normalized_execution_contract_type", "last_normalized_execution_contract_type"]);
|
|
4718
|
+
setFollowupArrayPatch("followup_normalized_execution_contract_targets", ["last_followup_normalized_execution_contract_targets", "last_normalized_execution_contract_targets"]);
|
|
4719
|
+
setFollowupArrayPatch("followup_normalized_execution_next_responders", ["last_followup_normalized_execution_next_responders", "last_normalized_execution_next_responders"]);
|
|
4720
|
+
setFollowupStringPatch("followup_response_contract_validation_status", ["last_followup_response_contract_validation_status", "last_contract_validation_status"]);
|
|
4721
|
+
setFollowupStringPatch("followup_response_contract_validation_reason", ["last_followup_response_contract_validation_reason", "last_contract_validation_reason"]);
|
|
4722
|
+
setFollowupArrayPatch("followup_response_contract_validation_targets", ["last_followup_response_contract_validation_targets", "last_contract_validation_targets"]);
|
|
4723
|
+
setFollowupStringPatch("followup_assignment_validation_status", ["last_followup_assignment_validation_status", "last_assignment_validation_status"]);
|
|
4724
|
+
setFollowupStringPatch("followup_assignment_validation_reason", ["last_followup_assignment_validation_reason", "last_assignment_validation_reason"]);
|
|
4725
|
+
setFollowupArrayPatch("followup_assignment_validation_modes", ["last_followup_assignment_validation_modes"], (value) => String(value || "").trim().toLowerCase());
|
|
4726
|
+
setFollowupStringPatch("followup_delivery_status", ["last_followup_delivery_status"]);
|
|
4727
|
+
setFollowupStringPatch("followup_archive_status", ["last_followup_archive_status"]);
|
|
4728
|
+
setFollowupStringPatch("followup_transport_error", ["last_followup_transport_error"]);
|
|
4729
|
+
setFollowupStringPatch("followup_archive_error", ["last_followup_archive_error"]);
|
|
4317
4730
|
const requestStatus = normalizeRunnerRequestStatus(request.status);
|
|
4318
4731
|
if (!isFinalRunnerRequestStatus(requestStatus)) {
|
|
4319
4732
|
const routeAction = String(routeState.last_action || "").trim().toLowerCase();
|
|
@@ -4597,6 +5010,9 @@ function markRunnerRequestLifecycle({
|
|
|
4597
5010
|
aiReplyGenerated = false,
|
|
4598
5011
|
aiReplyGeneratedAt = "",
|
|
4599
5012
|
aiReplyPreview = "",
|
|
5013
|
+
normalizedExecutionContractType = "",
|
|
5014
|
+
normalizedExecutionContractTargets = [],
|
|
5015
|
+
normalizedExecutionNextResponders = [],
|
|
4600
5016
|
responseContractValidationStatus = "",
|
|
4601
5017
|
responseContractValidationReason = "",
|
|
4602
5018
|
responseContractValidationTargets = [],
|
|
@@ -4668,6 +5084,9 @@ function markRunnerRequestLifecycle({
|
|
|
4668
5084
|
return normalizeRunnerRequestStatus(existing.status);
|
|
4669
5085
|
})();
|
|
4670
5086
|
const nowISO = new Date().toISOString();
|
|
5087
|
+
const commentKind = String(parsed.kind || "").trim().toLowerCase();
|
|
5088
|
+
const isRootHumanComment = ["telegram_message", "telegram_edited_message"].includes(commentKind);
|
|
5089
|
+
const isFollowupComment = !isRootHumanComment;
|
|
4671
5090
|
const patch = {
|
|
4672
5091
|
conversation_id: conversationID,
|
|
4673
5092
|
conversation_participants: uniqueOrderedStrings(
|
|
@@ -4713,39 +5132,217 @@ function markRunnerRequestLifecycle({
|
|
|
4713
5132
|
ensureArray(nextExpectedResponders).length ? nextExpectedResponders : existing.next_expected_responders,
|
|
4714
5133
|
normalizeTelegramMentionUsername,
|
|
4715
5134
|
),
|
|
5135
|
+
normalized_execution_contract_type: String(
|
|
5136
|
+
normalizedExecutionContractType || existing.normalized_execution_contract_type || "",
|
|
5137
|
+
).trim().toLowerCase(),
|
|
5138
|
+
normalized_execution_contract_targets: uniqueOrderedStrings(
|
|
5139
|
+
ensureArray(normalizedExecutionContractTargets).length
|
|
5140
|
+
? normalizedExecutionContractTargets
|
|
5141
|
+
: existing.normalized_execution_contract_targets,
|
|
5142
|
+
normalizeTelegramMentionUsername,
|
|
5143
|
+
),
|
|
5144
|
+
normalized_execution_next_responders: uniqueOrderedStrings(
|
|
5145
|
+
ensureArray(normalizedExecutionNextResponders).length
|
|
5146
|
+
? normalizedExecutionNextResponders
|
|
5147
|
+
: existing.normalized_execution_next_responders,
|
|
5148
|
+
normalizeTelegramMentionUsername,
|
|
5149
|
+
),
|
|
4716
5150
|
ai_reply_generated: aiReplyGenerated === true || existing.ai_reply_generated === true,
|
|
4717
5151
|
ai_reply_generated_at: aiReplyGenerated === true
|
|
4718
5152
|
? firstNonEmptyString([aiReplyGeneratedAt, existing.ai_reply_generated_at, nowISO])
|
|
4719
5153
|
: String(existing.ai_reply_generated_at || "").trim(),
|
|
4720
|
-
ai_reply_preview: String(
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
5154
|
+
ai_reply_preview: String(
|
|
5155
|
+
isRootHumanComment
|
|
5156
|
+
? aiReplyPreview || existing.ai_reply_preview || ""
|
|
5157
|
+
: existing.ai_reply_preview || "",
|
|
5158
|
+
).trim(),
|
|
5159
|
+
followup_ai_reply_preview: String(
|
|
5160
|
+
isFollowupComment
|
|
5161
|
+
? aiReplyPreview || existing.followup_ai_reply_preview || ""
|
|
5162
|
+
: existing.followup_ai_reply_preview || "",
|
|
5163
|
+
).trim(),
|
|
5164
|
+
root_execution_contract_type: String(
|
|
5165
|
+
isRootHumanComment
|
|
5166
|
+
? nextExecutionContractType
|
|
5167
|
+
: existing.root_execution_contract_type || existing.execution_contract_type || "",
|
|
5168
|
+
).trim().toLowerCase(),
|
|
5169
|
+
root_execution_contract_targets: uniqueOrderedStrings(
|
|
5170
|
+
isRootHumanComment && ensureArray(executionContractTargets).length
|
|
5171
|
+
? executionContractTargets
|
|
5172
|
+
: ensureArray(existing.root_execution_contract_targets).length
|
|
5173
|
+
? existing.root_execution_contract_targets
|
|
5174
|
+
: existing.execution_contract_targets,
|
|
5175
|
+
normalizeTelegramMentionUsername,
|
|
5176
|
+
),
|
|
5177
|
+
root_next_expected_responders: uniqueOrderedStrings(
|
|
5178
|
+
isRootHumanComment && ensureArray(nextExpectedResponders).length
|
|
5179
|
+
? nextExpectedResponders
|
|
5180
|
+
: ensureArray(existing.root_next_expected_responders).length
|
|
5181
|
+
? existing.root_next_expected_responders
|
|
5182
|
+
: existing.next_expected_responders,
|
|
5183
|
+
normalizeTelegramMentionUsername,
|
|
5184
|
+
),
|
|
5185
|
+
root_ai_reply_preview: String(
|
|
5186
|
+
isRootHumanComment
|
|
5187
|
+
? aiReplyPreview || existing.root_ai_reply_preview || existing.ai_reply_preview || ""
|
|
5188
|
+
: existing.root_ai_reply_preview || existing.ai_reply_preview || "",
|
|
5189
|
+
).trim(),
|
|
5190
|
+
root_response_contract_validation_status: String(
|
|
5191
|
+
isRootHumanComment
|
|
5192
|
+
? responseContractValidationStatus || existing.root_response_contract_validation_status || existing.response_contract_validation_status || ""
|
|
5193
|
+
: existing.root_response_contract_validation_status || existing.response_contract_validation_status || "",
|
|
5194
|
+
).trim().toLowerCase(),
|
|
5195
|
+
root_response_contract_validation_reason: String(
|
|
5196
|
+
isRootHumanComment
|
|
5197
|
+
? responseContractValidationReason || existing.root_response_contract_validation_reason || existing.response_contract_validation_reason || ""
|
|
5198
|
+
: existing.root_response_contract_validation_reason || existing.response_contract_validation_reason || "",
|
|
5199
|
+
).trim(),
|
|
5200
|
+
root_response_contract_validation_targets: uniqueOrderedStrings(
|
|
5201
|
+
isRootHumanComment && ensureArray(responseContractValidationTargets).length
|
|
5202
|
+
? responseContractValidationTargets
|
|
5203
|
+
: ensureArray(existing.root_response_contract_validation_targets).length
|
|
5204
|
+
? existing.root_response_contract_validation_targets
|
|
5205
|
+
: existing.response_contract_validation_targets,
|
|
5206
|
+
normalizeTelegramMentionUsername,
|
|
5207
|
+
),
|
|
5208
|
+
response_contract_validation_status: String(
|
|
5209
|
+
isRootHumanComment
|
|
5210
|
+
? responseContractValidationStatus || existing.response_contract_validation_status || ""
|
|
5211
|
+
: existing.response_contract_validation_status || "",
|
|
5212
|
+
).trim().toLowerCase(),
|
|
4724
5213
|
response_contract_validation_reason: String(
|
|
4725
|
-
|
|
5214
|
+
isRootHumanComment
|
|
5215
|
+
? responseContractValidationReason || existing.response_contract_validation_reason || ""
|
|
5216
|
+
: existing.response_contract_validation_reason || "",
|
|
4726
5217
|
).trim(),
|
|
4727
5218
|
response_contract_validation_targets: uniqueOrderedStrings(
|
|
4728
|
-
ensureArray(responseContractValidationTargets).length
|
|
5219
|
+
isRootHumanComment && ensureArray(responseContractValidationTargets).length
|
|
4729
5220
|
? responseContractValidationTargets
|
|
4730
5221
|
: existing.response_contract_validation_targets,
|
|
4731
5222
|
normalizeTelegramMentionUsername,
|
|
4732
5223
|
),
|
|
5224
|
+
followup_execution_contract_type: String(
|
|
5225
|
+
isFollowupComment
|
|
5226
|
+
? nextExecutionContractType || existing.followup_execution_contract_type || ""
|
|
5227
|
+
: existing.followup_execution_contract_type || "",
|
|
5228
|
+
).trim().toLowerCase(),
|
|
5229
|
+
followup_execution_contract_targets: uniqueOrderedStrings(
|
|
5230
|
+
isFollowupComment && ensureArray(executionContractTargets).length
|
|
5231
|
+
? executionContractTargets
|
|
5232
|
+
: existing.followup_execution_contract_targets,
|
|
5233
|
+
normalizeTelegramMentionUsername,
|
|
5234
|
+
),
|
|
5235
|
+
followup_next_expected_responders: uniqueOrderedStrings(
|
|
5236
|
+
isFollowupComment && ensureArray(nextExpectedResponders).length
|
|
5237
|
+
? nextExpectedResponders
|
|
5238
|
+
: existing.followup_next_expected_responders,
|
|
5239
|
+
normalizeTelegramMentionUsername,
|
|
5240
|
+
),
|
|
5241
|
+
followup_normalized_execution_contract_type: String(
|
|
5242
|
+
isFollowupComment
|
|
5243
|
+
? normalizedExecutionContractType || existing.followup_normalized_execution_contract_type || ""
|
|
5244
|
+
: existing.followup_normalized_execution_contract_type || "",
|
|
5245
|
+
).trim().toLowerCase(),
|
|
5246
|
+
followup_normalized_execution_contract_targets: uniqueOrderedStrings(
|
|
5247
|
+
isFollowupComment && ensureArray(normalizedExecutionContractTargets).length
|
|
5248
|
+
? normalizedExecutionContractTargets
|
|
5249
|
+
: existing.followup_normalized_execution_contract_targets,
|
|
5250
|
+
normalizeTelegramMentionUsername,
|
|
5251
|
+
),
|
|
5252
|
+
followup_normalized_execution_next_responders: uniqueOrderedStrings(
|
|
5253
|
+
isFollowupComment && ensureArray(normalizedExecutionNextResponders).length
|
|
5254
|
+
? normalizedExecutionNextResponders
|
|
5255
|
+
: existing.followup_normalized_execution_next_responders,
|
|
5256
|
+
normalizeTelegramMentionUsername,
|
|
5257
|
+
),
|
|
5258
|
+
followup_response_contract_validation_status: String(
|
|
5259
|
+
isFollowupComment
|
|
5260
|
+
? responseContractValidationStatus || existing.followup_response_contract_validation_status || ""
|
|
5261
|
+
: existing.followup_response_contract_validation_status || "",
|
|
5262
|
+
).trim().toLowerCase(),
|
|
5263
|
+
followup_response_contract_validation_reason: String(
|
|
5264
|
+
isFollowupComment
|
|
5265
|
+
? responseContractValidationReason || existing.followup_response_contract_validation_reason || ""
|
|
5266
|
+
: existing.followup_response_contract_validation_reason || "",
|
|
5267
|
+
).trim(),
|
|
5268
|
+
followup_response_contract_validation_targets: uniqueOrderedStrings(
|
|
5269
|
+
isFollowupComment && ensureArray(responseContractValidationTargets).length
|
|
5270
|
+
? responseContractValidationTargets
|
|
5271
|
+
: existing.followup_response_contract_validation_targets,
|
|
5272
|
+
normalizeTelegramMentionUsername,
|
|
5273
|
+
),
|
|
4733
5274
|
assignment_validation_status: String(
|
|
4734
|
-
|
|
5275
|
+
isRootHumanComment
|
|
5276
|
+
? assignmentValidationStatus || existing.assignment_validation_status || ""
|
|
5277
|
+
: existing.assignment_validation_status || "",
|
|
4735
5278
|
).trim().toLowerCase(),
|
|
4736
5279
|
assignment_validation_reason: String(
|
|
4737
|
-
|
|
5280
|
+
isRootHumanComment
|
|
5281
|
+
? assignmentValidationReason || existing.assignment_validation_reason || ""
|
|
5282
|
+
: existing.assignment_validation_reason || "",
|
|
4738
5283
|
).trim(),
|
|
4739
5284
|
assignment_validation_modes: uniqueOrderedStrings(
|
|
4740
|
-
ensureArray(assignmentValidationModes).length
|
|
5285
|
+
isRootHumanComment && ensureArray(assignmentValidationModes).length
|
|
4741
5286
|
? assignmentValidationModes
|
|
4742
5287
|
: existing.assignment_validation_modes,
|
|
4743
5288
|
(value) => String(value || "").trim().toLowerCase(),
|
|
4744
5289
|
),
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
5290
|
+
followup_assignment_validation_status: String(
|
|
5291
|
+
isFollowupComment
|
|
5292
|
+
? assignmentValidationStatus || existing.followup_assignment_validation_status || ""
|
|
5293
|
+
: existing.followup_assignment_validation_status || "",
|
|
5294
|
+
).trim().toLowerCase(),
|
|
5295
|
+
followup_assignment_validation_reason: String(
|
|
5296
|
+
isFollowupComment
|
|
5297
|
+
? assignmentValidationReason || existing.followup_assignment_validation_reason || ""
|
|
5298
|
+
: existing.followup_assignment_validation_reason || "",
|
|
5299
|
+
).trim(),
|
|
5300
|
+
followup_assignment_validation_modes: uniqueOrderedStrings(
|
|
5301
|
+
isFollowupComment && ensureArray(assignmentValidationModes).length
|
|
5302
|
+
? assignmentValidationModes
|
|
5303
|
+
: existing.followup_assignment_validation_modes,
|
|
5304
|
+
(value) => String(value || "").trim().toLowerCase(),
|
|
5305
|
+
),
|
|
5306
|
+
delivery_status: String(
|
|
5307
|
+
isRootHumanComment
|
|
5308
|
+
? deliveryStatus || existing.delivery_status || ""
|
|
5309
|
+
: existing.delivery_status || "",
|
|
5310
|
+
).trim().toLowerCase(),
|
|
5311
|
+
archive_status: String(
|
|
5312
|
+
isRootHumanComment
|
|
5313
|
+
? archiveStatus || existing.archive_status || ""
|
|
5314
|
+
: existing.archive_status || "",
|
|
5315
|
+
).trim().toLowerCase(),
|
|
5316
|
+
transport_error: String(
|
|
5317
|
+
isRootHumanComment
|
|
5318
|
+
? transportError || existing.transport_error || ""
|
|
5319
|
+
: existing.transport_error || "",
|
|
5320
|
+
).trim(),
|
|
5321
|
+
archive_error: String(
|
|
5322
|
+
isRootHumanComment
|
|
5323
|
+
? archiveError || existing.archive_error || ""
|
|
5324
|
+
: existing.archive_error || "",
|
|
5325
|
+
).trim(),
|
|
5326
|
+
followup_delivery_status: String(
|
|
5327
|
+
isFollowupComment
|
|
5328
|
+
? deliveryStatus || existing.followup_delivery_status || ""
|
|
5329
|
+
: existing.followup_delivery_status || "",
|
|
5330
|
+
).trim().toLowerCase(),
|
|
5331
|
+
followup_archive_status: String(
|
|
5332
|
+
isFollowupComment
|
|
5333
|
+
? archiveStatus || existing.followup_archive_status || ""
|
|
5334
|
+
: existing.followup_archive_status || "",
|
|
5335
|
+
).trim().toLowerCase(),
|
|
5336
|
+
followup_transport_error: String(
|
|
5337
|
+
isFollowupComment
|
|
5338
|
+
? transportError || existing.followup_transport_error || ""
|
|
5339
|
+
: existing.followup_transport_error || "",
|
|
5340
|
+
).trim(),
|
|
5341
|
+
followup_archive_error: String(
|
|
5342
|
+
isFollowupComment
|
|
5343
|
+
? archiveError || existing.followup_archive_error || ""
|
|
5344
|
+
: existing.followup_archive_error || "",
|
|
5345
|
+
).trim(),
|
|
4749
5346
|
normalized_intent: nextNormalizedIntent,
|
|
4750
5347
|
status: nextStatus,
|
|
4751
5348
|
started_at: firstNonEmptyString([existing.started_at, nowISO]),
|
|
@@ -4962,23 +5559,55 @@ function mergeRunnerRequestForServerHydration(localEntryRaw, serverEntryRaw) {
|
|
|
4962
5559
|
preserveLocalStringWhenServerBlank("conversation_summary_bot");
|
|
4963
5560
|
preserveLocalStringWhenServerBlank("conversation_reply_expectation");
|
|
4964
5561
|
preserveLocalStringWhenServerBlank("execution_contract_type");
|
|
5562
|
+
preserveLocalStringWhenServerBlank("normalized_execution_contract_type");
|
|
5563
|
+
preserveLocalStringWhenServerBlank("ai_reply_generated_at");
|
|
5564
|
+
preserveLocalStringWhenServerBlank("ai_reply_preview");
|
|
4965
5565
|
preserveLocalStringWhenServerBlank("root_work_item_id");
|
|
4966
5566
|
preserveLocalStringWhenServerBlank("root_work_item_title");
|
|
4967
5567
|
preserveLocalStringWhenServerBlank("root_work_item_status");
|
|
4968
5568
|
preserveLocalStringWhenServerBlank("root_thread_id");
|
|
4969
5569
|
preserveLocalStringWhenServerBlank("root_work_item_created_at");
|
|
4970
5570
|
preserveLocalStringWhenServerBlank("root_work_item_last_error");
|
|
5571
|
+
preserveLocalStringWhenServerBlank("root_execution_contract_type");
|
|
5572
|
+
preserveLocalStringWhenServerBlank("root_ai_reply_preview");
|
|
5573
|
+
preserveLocalStringWhenServerBlank("root_response_contract_validation_status");
|
|
5574
|
+
preserveLocalStringWhenServerBlank("root_response_contract_validation_reason");
|
|
5575
|
+
preserveLocalStringWhenServerBlank("followup_ai_reply_preview");
|
|
5576
|
+
preserveLocalStringWhenServerBlank("followup_execution_contract_type");
|
|
5577
|
+
preserveLocalStringWhenServerBlank("followup_normalized_execution_contract_type");
|
|
5578
|
+
preserveLocalStringWhenServerBlank("followup_response_contract_validation_status");
|
|
5579
|
+
preserveLocalStringWhenServerBlank("followup_response_contract_validation_reason");
|
|
5580
|
+
preserveLocalStringWhenServerBlank("followup_assignment_validation_status");
|
|
5581
|
+
preserveLocalStringWhenServerBlank("followup_assignment_validation_reason");
|
|
5582
|
+
preserveLocalStringWhenServerBlank("followup_delivery_status");
|
|
5583
|
+
preserveLocalStringWhenServerBlank("followup_archive_status");
|
|
5584
|
+
preserveLocalStringWhenServerBlank("followup_transport_error");
|
|
5585
|
+
preserveLocalStringWhenServerBlank("followup_archive_error");
|
|
4971
5586
|
preserveLocalArrayWhenServerEmpty("conversation_participants");
|
|
4972
5587
|
preserveLocalArrayWhenServerEmpty("conversation_initial_responders");
|
|
4973
5588
|
preserveLocalArrayWhenServerEmpty("conversation_allowed_responders");
|
|
4974
5589
|
preserveLocalArrayWhenServerEmpty("execution_contract_targets");
|
|
4975
5590
|
preserveLocalArrayWhenServerEmpty("next_expected_responders");
|
|
5591
|
+
preserveLocalArrayWhenServerEmpty("normalized_execution_contract_targets");
|
|
5592
|
+
preserveLocalArrayWhenServerEmpty("normalized_execution_next_responders");
|
|
5593
|
+
preserveLocalArrayWhenServerEmpty("root_execution_contract_targets");
|
|
5594
|
+
preserveLocalArrayWhenServerEmpty("root_next_expected_responders");
|
|
5595
|
+
preserveLocalArrayWhenServerEmpty("root_response_contract_validation_targets");
|
|
5596
|
+
preserveLocalArrayWhenServerEmpty("followup_execution_contract_targets");
|
|
5597
|
+
preserveLocalArrayWhenServerEmpty("followup_next_expected_responders");
|
|
5598
|
+
preserveLocalArrayWhenServerEmpty("followup_normalized_execution_contract_targets");
|
|
5599
|
+
preserveLocalArrayWhenServerEmpty("followup_normalized_execution_next_responders");
|
|
5600
|
+
preserveLocalArrayWhenServerEmpty("followup_response_contract_validation_targets");
|
|
5601
|
+
preserveLocalArrayWhenServerEmpty("followup_assignment_validation_modes");
|
|
4976
5602
|
if (serverEntry.conversation_allow_bot_to_bot !== true && localEntry.conversation_allow_bot_to_bot === true) {
|
|
4977
5603
|
merged.conversation_allow_bot_to_bot = true;
|
|
4978
5604
|
}
|
|
4979
5605
|
if (serverEntry.execution_contract_actionable !== true && localEntry.execution_contract_actionable === true) {
|
|
4980
5606
|
merged.execution_contract_actionable = true;
|
|
4981
5607
|
}
|
|
5608
|
+
if (serverEntry.ai_reply_generated !== true && localEntry.ai_reply_generated === true) {
|
|
5609
|
+
merged.ai_reply_generated = true;
|
|
5610
|
+
}
|
|
4982
5611
|
const localStatus = normalizeRunnerRequestStatus(localEntry.status);
|
|
4983
5612
|
const serverStatus = normalizeRunnerRequestStatus(serverEntry.status);
|
|
4984
5613
|
if (isFinalRunnerRequestStatus(localStatus) && !isFinalRunnerRequestStatus(serverStatus)) {
|
|
@@ -7366,17 +7995,26 @@ function summarizeRunnerRequestForStatusLookup(entryRaw) {
|
|
|
7366
7995
|
updated_at: String(entry.updated_at || "").trim(),
|
|
7367
7996
|
source_message_id: intFromRawAllowZero(entry.source_message_id, 0) || undefined,
|
|
7368
7997
|
last_source_message_id: intFromRawAllowZero(entry.last_source_message_id, 0) || undefined,
|
|
7998
|
+
...buildRunnerValidationAndDeliverySummary({
|
|
7999
|
+
aiReplyPreview: runnerRequestPreferredAIReplyPreview(entry),
|
|
8000
|
+
executionContractType: runnerRequestPreferredExecutionContractType(entry),
|
|
8001
|
+
executionContractTargets: runnerRequestPreferredExecutionContractTargets(entry),
|
|
8002
|
+
nextExpectedResponders: runnerRequestPreferredNextExpectedResponders(entry),
|
|
8003
|
+
responseContractValidationStatus: runnerRequestPreferredResponseContractValidationStatus(entry),
|
|
8004
|
+
responseContractValidationReason: runnerRequestPreferredResponseContractValidationReason(entry),
|
|
8005
|
+
responseContractValidationTargets: runnerRequestPreferredResponseContractValidationTargets(entry),
|
|
8006
|
+
assignmentValidationStatus: runnerRequestPreferredAssignmentValidationStatus(entry),
|
|
8007
|
+
assignmentValidationReason: runnerRequestPreferredAssignmentValidationReason(entry),
|
|
8008
|
+
assignmentValidationModes: runnerRequestPreferredAssignmentValidationModes(entry),
|
|
8009
|
+
deliveryStatus: runnerRequestPreferredDeliveryStatus(entry),
|
|
8010
|
+
archiveStatus: runnerRequestPreferredArchiveStatus(entry),
|
|
8011
|
+
transportError: runnerRequestPreferredTransportError(entry),
|
|
8012
|
+
archiveError: runnerRequestPreferredArchiveError(entry),
|
|
8013
|
+
}),
|
|
7369
8014
|
selected_bot_usernames: ensureArray(entry.selected_bot_usernames)
|
|
7370
8015
|
.map((value) => normalizeTelegramMentionUsername(value))
|
|
7371
8016
|
.filter(Boolean),
|
|
7372
|
-
root_work_item:
|
|
7373
|
-
? {
|
|
7374
|
-
id: String(entry.root_work_item_id || "").trim(),
|
|
7375
|
-
title: String(entry.root_work_item_title || "").trim(),
|
|
7376
|
-
status: normalizeRunnerWorkItemStatus(entry.root_work_item_status),
|
|
7377
|
-
thread_id: String(entry.root_thread_id || "").trim(),
|
|
7378
|
-
}
|
|
7379
|
-
: null,
|
|
8017
|
+
root_work_item: buildRunnerRequestRootWorkItemSummary(entry),
|
|
7380
8018
|
};
|
|
7381
8019
|
}
|
|
7382
8020
|
|
|
@@ -7435,6 +8073,150 @@ function pickPreferredStatusLookupRequest(entries = []) {
|
|
|
7435
8073
|
return candidates[0];
|
|
7436
8074
|
}
|
|
7437
8075
|
|
|
8076
|
+
function resolveRunnerStatusLookupRequests({
|
|
8077
|
+
runnerState,
|
|
8078
|
+
route,
|
|
8079
|
+
routeKey,
|
|
8080
|
+
selfBotUsername,
|
|
8081
|
+
currentMessageID,
|
|
8082
|
+
currentConversationID,
|
|
8083
|
+
currentChatID,
|
|
8084
|
+
activeRequestKey,
|
|
8085
|
+
replyChainContext,
|
|
8086
|
+
}) {
|
|
8087
|
+
const requestMatchesCurrentRoute = (entry) => requestEligibleForStatusLookup(
|
|
8088
|
+
entry,
|
|
8089
|
+
routeKey,
|
|
8090
|
+
selfBotUsername,
|
|
8091
|
+
currentMessageID,
|
|
8092
|
+
);
|
|
8093
|
+
const referencedRequestCandidate = safeObject(replyChainContext?.referencedRequest);
|
|
8094
|
+
const selectors = currentConversationID
|
|
8095
|
+
? { conversationID: currentConversationID, chatID: currentChatID }
|
|
8096
|
+
: { chatID: currentChatID };
|
|
8097
|
+
let scopedRequests = findRunnerRequestsForScope(runnerState, route, selectors);
|
|
8098
|
+
if (!scopedRequests.length && currentConversationID) {
|
|
8099
|
+
scopedRequests = findRunnerRequestsForScope(runnerState, route, { chatID: currentChatID });
|
|
8100
|
+
}
|
|
8101
|
+
if (!scopedRequests.length && activeRequestKey) {
|
|
8102
|
+
scopedRequests = findRunnerRequestsForScope(runnerState, route, { requestKey: activeRequestKey });
|
|
8103
|
+
}
|
|
8104
|
+
const statusLookupCandidates = scopedRequests.filter(requestMatchesCurrentRoute);
|
|
8105
|
+
if (
|
|
8106
|
+
Object.keys(referencedRequestCandidate).length > 0
|
|
8107
|
+
&& requestMatchesCurrentRoute(referencedRequestCandidate)
|
|
8108
|
+
&& !statusLookupCandidates.some(
|
|
8109
|
+
(entry) => String(safeObject(entry).request_key || "").trim() === String(referencedRequestCandidate.request_key || "").trim(),
|
|
8110
|
+
)
|
|
8111
|
+
) {
|
|
8112
|
+
statusLookupCandidates.push(referencedRequestCandidate);
|
|
8113
|
+
}
|
|
8114
|
+
return {
|
|
8115
|
+
related_active_request: statusLookupCandidates
|
|
8116
|
+
.filter((entry) => isActiveRunnerRequestStatus(entry.status))[0] || null,
|
|
8117
|
+
related_request: pickPreferredStatusLookupRequest(statusLookupCandidates),
|
|
8118
|
+
};
|
|
8119
|
+
}
|
|
8120
|
+
|
|
8121
|
+
function buildRunnerShowLastRunPayload(lastRunSummaryRaw) {
|
|
8122
|
+
const lastRunSummary = safeObject(lastRunSummaryRaw);
|
|
8123
|
+
return {
|
|
8124
|
+
action: lastRunSummary.action || "-",
|
|
8125
|
+
reason: lastRunSummary.reason || "-",
|
|
8126
|
+
intent_type: lastRunSummary.intent_type || "-",
|
|
8127
|
+
ai_reply_preview: lastRunSummary.ai_reply_preview || "-",
|
|
8128
|
+
execution_contract_type: lastRunSummary.execution_contract_type || "-",
|
|
8129
|
+
execution_contract_targets: ensureArray(lastRunSummary.execution_contract_targets),
|
|
8130
|
+
next_expected_responders: ensureArray(lastRunSummary.next_expected_responders),
|
|
8131
|
+
response_contract_validation_status: lastRunSummary.response_contract_validation_status || "-",
|
|
8132
|
+
response_contract_validation_reason: lastRunSummary.response_contract_validation_reason || "-",
|
|
8133
|
+
response_contract_validation_targets: ensureArray(lastRunSummary.response_contract_validation_targets),
|
|
8134
|
+
assignment_validation_status: lastRunSummary.assignment_validation_status || "-",
|
|
8135
|
+
assignment_validation_reason: lastRunSummary.assignment_validation_reason || "-",
|
|
8136
|
+
assignment_validation_modes: ensureArray(lastRunSummary.assignment_validation_modes),
|
|
8137
|
+
delivery_status: lastRunSummary.delivery_status || "-",
|
|
8138
|
+
archive_status: lastRunSummary.archive_status || "-",
|
|
8139
|
+
transport_error: lastRunSummary.transport_error || "-",
|
|
8140
|
+
archive_error: lastRunSummary.archive_error || "-",
|
|
8141
|
+
workspace_dir: lastRunSummary.workspace_dir || "-",
|
|
8142
|
+
artifact_validation: lastRunSummary.artifact_validation || "-",
|
|
8143
|
+
artifact_paths: ensureArray(lastRunSummary.artifact_paths),
|
|
8144
|
+
artifact_errors: ensureArray(lastRunSummary.artifact_errors),
|
|
8145
|
+
boundary_violations: ensureArray(lastRunSummary.boundary_violations),
|
|
8146
|
+
};
|
|
8147
|
+
}
|
|
8148
|
+
|
|
8149
|
+
function buildRunnerShowActiveExecutionPayload(activeExecutionStateRaw) {
|
|
8150
|
+
const activeExecutionState = safeObject(activeExecutionStateRaw);
|
|
8151
|
+
return {
|
|
8152
|
+
active: activeExecutionState.active === true,
|
|
8153
|
+
stale: activeExecutionState.stale === true,
|
|
8154
|
+
stuck: activeExecutionState.stuck === true,
|
|
8155
|
+
comment_id: String(activeExecutionState.commentID || "").trim(),
|
|
8156
|
+
source_message_id: intFromRawAllowZero(activeExecutionState.sourceMessageID, 0),
|
|
8157
|
+
started_at: String(activeExecutionState.startedAt || "").trim(),
|
|
8158
|
+
age_seconds: intFromRawAllowZero(activeExecutionState.ageSeconds, 0),
|
|
8159
|
+
warning: String(activeExecutionState.warning || "").trim(),
|
|
8160
|
+
};
|
|
8161
|
+
}
|
|
8162
|
+
|
|
8163
|
+
function buildRunnerShowResolvedContext({
|
|
8164
|
+
normalizedRoute,
|
|
8165
|
+
diagnostics,
|
|
8166
|
+
envConfig,
|
|
8167
|
+
resolvedServerBotName,
|
|
8168
|
+
botNameSource,
|
|
8169
|
+
}) {
|
|
8170
|
+
return {
|
|
8171
|
+
resolved_server_identity: {
|
|
8172
|
+
server_bot_name: resolvedServerBotName,
|
|
8173
|
+
server_bot_name_source: botNameSource,
|
|
8174
|
+
server_bot_id: normalizedRoute.botID || "-",
|
|
8175
|
+
telegram_entry_file: String(envConfig?.entryFilePath || "").trim() || "-",
|
|
8176
|
+
},
|
|
8177
|
+
resolved_destination: {
|
|
8178
|
+
destination_label: String(normalizedRoute.destinationLabel || "").trim() || "-",
|
|
8179
|
+
destination_id: String(normalizedRoute.destinationID || "").trim() || "-",
|
|
8180
|
+
destination_source: "route_config",
|
|
8181
|
+
},
|
|
8182
|
+
workspace_mapping: {
|
|
8183
|
+
workspace_dir: diagnostics.workspaceDir || "-",
|
|
8184
|
+
workspace_source: diagnostics.workspaceSource || "-",
|
|
8185
|
+
},
|
|
8186
|
+
execution_profile: {
|
|
8187
|
+
route_role: normalizedRoute.role || "-",
|
|
8188
|
+
role_profile_name: diagnostics.roleProfileName || "-",
|
|
8189
|
+
client: String(diagnostics.roleProfile?.client || "").trim() || "-",
|
|
8190
|
+
model: String(diagnostics.roleProfile?.model || "").trim() || "-",
|
|
8191
|
+
permission_mode: String(diagnostics.roleProfile?.permissionMode || "").trim() || "-",
|
|
8192
|
+
reasoning_effort: String(diagnostics.roleProfile?.reasoningEffort || "").trim() || "-",
|
|
8193
|
+
},
|
|
8194
|
+
};
|
|
8195
|
+
}
|
|
8196
|
+
|
|
8197
|
+
function buildRunnerStatusReplyChainResolution(replyChainContextRaw) {
|
|
8198
|
+
const replyChainContext = safeObject(replyChainContextRaw);
|
|
8199
|
+
return {
|
|
8200
|
+
reason: String(replyChainContext.reason || "").trim(),
|
|
8201
|
+
reply_to_message_id: intFromRawAllowZero(replyChainContext.replyToMessageID, 0) || undefined,
|
|
8202
|
+
anchor_message_id: intFromRawAllowZero(replyChainContext.anchorMessageID, 0) || undefined,
|
|
8203
|
+
};
|
|
8204
|
+
}
|
|
8205
|
+
|
|
8206
|
+
function buildRunnerStatusActiveExecutionSummary(activeExecutionRaw, selfBusyFiltered = false) {
|
|
8207
|
+
const activeExecution = safeObject(activeExecutionRaw);
|
|
8208
|
+
if (!(activeExecution.active && !selfBusyFiltered)) {
|
|
8209
|
+
return null;
|
|
8210
|
+
}
|
|
8211
|
+
return {
|
|
8212
|
+
started_at: String(activeExecution.startedAt || "").trim(),
|
|
8213
|
+
age_seconds: intFromRawAllowZero(activeExecution.ageSeconds, 0),
|
|
8214
|
+
stale: activeExecution.stale === true,
|
|
8215
|
+
stuck: activeExecution.stuck === true,
|
|
8216
|
+
warning: String(activeExecution.warning || "").trim(),
|
|
8217
|
+
};
|
|
8218
|
+
}
|
|
8219
|
+
|
|
7438
8220
|
function buildRunnerStatusQueryLookup({ route, routeState, selectedRecord, runnerStateOverride = null }) {
|
|
7439
8221
|
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
7440
8222
|
const currentMessageID = intFromRawAllowZero(parsed.messageID, 0);
|
|
@@ -7464,109 +8246,156 @@ function buildRunnerStatusQueryLookup({ route, routeState, selectedRecord, runne
|
|
|
7464
8246
|
const replyChainContext = resolveRunnerReplyChainConversationContext(runnerState, route, selectedRecord);
|
|
7465
8247
|
const currentConversationID = String(parsed.conversationID || replyChainContext.conversationID || "").trim();
|
|
7466
8248
|
const activeRequestKey = String(safeObject(routeState).active_request_key || "").trim();
|
|
7467
|
-
const
|
|
7468
|
-
|
|
8249
|
+
const { related_active_request: relatedActiveRequest, related_request: relatedRequest } = resolveRunnerStatusLookupRequests({
|
|
8250
|
+
runnerState,
|
|
8251
|
+
route,
|
|
7469
8252
|
routeKey,
|
|
7470
8253
|
selfBotUsername,
|
|
7471
8254
|
currentMessageID,
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
}
|
|
7483
|
-
if (!scopedRequests.length && activeRequestKey) {
|
|
7484
|
-
scopedRequests = findRunnerRequestsForScope(runnerState, route, { requestKey: activeRequestKey });
|
|
7485
|
-
}
|
|
7486
|
-
const eligibleScopedRequests = scopedRequests.filter(requestMatchesCurrentRoute);
|
|
7487
|
-
const statusLookupCandidates = [...eligibleScopedRequests];
|
|
7488
|
-
if (
|
|
7489
|
-
Object.keys(referencedRequestCandidate).length > 0
|
|
7490
|
-
&& requestMatchesCurrentRoute(referencedRequestCandidate)
|
|
7491
|
-
&& !statusLookupCandidates.some(
|
|
7492
|
-
(entry) => String(safeObject(entry).request_key || "").trim() === String(referencedRequestCandidate.request_key || "").trim(),
|
|
7493
|
-
)
|
|
7494
|
-
) {
|
|
7495
|
-
statusLookupCandidates.push(referencedRequestCandidate);
|
|
7496
|
-
}
|
|
7497
|
-
relatedActiveRequest = statusLookupCandidates
|
|
7498
|
-
.filter((entry) => isActiveRunnerRequestStatus(entry.status))[0] || null;
|
|
7499
|
-
relatedRequest = pickPreferredStatusLookupRequest(statusLookupCandidates);
|
|
7500
|
-
const lastAction = String(safeObject(routeState).last_action || "").trim();
|
|
7501
|
-
const lastReason = String(safeObject(routeState).last_reason || "").trim();
|
|
7502
|
-
const lastIntentType = String(safeObject(routeState).last_intent_type || "").trim();
|
|
7503
|
-
const routeConversationID = String(safeObject(routeState).last_conversation_id || "").trim();
|
|
7504
|
-
const activeRootWorkItemID = String(safeObject(routeState).active_root_work_item_id || "").trim();
|
|
7505
|
-
const activeRootWorkItemTitle = String(safeObject(routeState).active_root_work_item_title || "").trim();
|
|
7506
|
-
const activeRootWorkItemStatus = normalizeRunnerWorkItemStatus(safeObject(routeState).active_root_work_item_status);
|
|
7507
|
-
const routeRootWorkItemID = String(safeObject(routeState).last_root_work_item_id || "").trim();
|
|
7508
|
-
const routeRootWorkItemTitle = String(safeObject(routeState).last_root_work_item_title || "").trim();
|
|
7509
|
-
const routeRootWorkItemStatus = normalizeRunnerWorkItemStatus(safeObject(routeState).last_root_work_item_status);
|
|
7510
|
-
const routeWorkItemIDs = ensureArray(safeObject(routeState).last_work_item_ids).map((item) => String(item || "").trim()).filter(Boolean);
|
|
7511
|
-
const routeWorkItemTitles = ensureArray(safeObject(routeState).last_work_item_titles).map((item) => String(item || "").trim()).filter(Boolean);
|
|
7512
|
-
const requestRootWorkItem = String(safeObject(relatedRequest).root_work_item_id || "").trim()
|
|
7513
|
-
? {
|
|
7514
|
-
id: String(safeObject(relatedRequest).root_work_item_id || "").trim(),
|
|
7515
|
-
title: String(safeObject(relatedRequest).root_work_item_title || "").trim(),
|
|
7516
|
-
status: normalizeRunnerWorkItemStatus(safeObject(relatedRequest).root_work_item_status),
|
|
7517
|
-
thread_id: String(safeObject(relatedRequest).root_thread_id || "").trim(),
|
|
7518
|
-
}
|
|
7519
|
-
: null;
|
|
8255
|
+
currentConversationID,
|
|
8256
|
+
currentChatID,
|
|
8257
|
+
activeRequestKey,
|
|
8258
|
+
replyChainContext,
|
|
8259
|
+
});
|
|
8260
|
+
const lastRouteResult = buildRunnerRouteLastResultSummary(routeState);
|
|
8261
|
+
const workItemSummary = buildRunnerStatusLookupWorkItemSummary({
|
|
8262
|
+
relatedRequest,
|
|
8263
|
+
routeState,
|
|
8264
|
+
currentConversationID,
|
|
8265
|
+
});
|
|
7520
8266
|
return {
|
|
7521
8267
|
kind: "runner_status",
|
|
7522
8268
|
status: (!selfBusyFiltered && activeExecution.active) || relatedActiveRequest
|
|
7523
8269
|
? "running"
|
|
7524
8270
|
: String(safeObject(relatedRequest).status || "").trim() || "idle",
|
|
7525
8271
|
resolved_conversation_id: currentConversationID,
|
|
7526
|
-
reply_chain_resolution:
|
|
7527
|
-
reason: String(replyChainContext.reason || "").trim(),
|
|
7528
|
-
reply_to_message_id: intFromRawAllowZero(replyChainContext.replyToMessageID, 0) || undefined,
|
|
7529
|
-
anchor_message_id: intFromRawAllowZero(replyChainContext.anchorMessageID, 0) || undefined,
|
|
7530
|
-
},
|
|
8272
|
+
reply_chain_resolution: buildRunnerStatusReplyChainResolution(replyChainContext),
|
|
7531
8273
|
self_busy_filtered: selfBusyFiltered,
|
|
7532
|
-
active_execution: activeExecution
|
|
7533
|
-
? {
|
|
7534
|
-
started_at: String(activeExecution.startedAt || "").trim(),
|
|
7535
|
-
age_seconds: intFromRawAllowZero(activeExecution.ageSeconds, 0),
|
|
7536
|
-
stale: activeExecution.stale === true,
|
|
7537
|
-
stuck: activeExecution.stuck === true,
|
|
7538
|
-
warning: String(activeExecution.warning || "").trim(),
|
|
7539
|
-
}
|
|
7540
|
-
: null,
|
|
8274
|
+
active_execution: buildRunnerStatusActiveExecutionSummary(activeExecution, selfBusyFiltered),
|
|
7541
8275
|
related_active_request: relatedActiveRequest ? summarizeRunnerRequestForStatusLookup(relatedActiveRequest) : null,
|
|
7542
8276
|
related_request: relatedRequest ? summarizeRunnerRequestForStatusLookup(relatedRequest) : null,
|
|
7543
|
-
root_work_item:
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
8277
|
+
root_work_item: workItemSummary.root_work_item,
|
|
8278
|
+
route_work_items: workItemSummary.route_work_items,
|
|
8279
|
+
last_route_result: lastRouteResult,
|
|
8280
|
+
};
|
|
8281
|
+
}
|
|
8282
|
+
|
|
8283
|
+
function buildProjectWorkspaceLookupResponse({ route, executionPlan, executionOverride }) {
|
|
8284
|
+
const workspace = resolveProjectWorkspaceBindingSummary(
|
|
8285
|
+
route?.projectID,
|
|
8286
|
+
executionPlan?.workspaceDir || route?.workspaceDir || "",
|
|
8287
|
+
);
|
|
8288
|
+
return buildLookupOnlyInformationalReply("project.workspace", {
|
|
8289
|
+
...workspace,
|
|
8290
|
+
}, executionOverride);
|
|
8291
|
+
}
|
|
8292
|
+
|
|
8293
|
+
function buildSmallTalkLookupResponse({ route, messageText, executionOverride }) {
|
|
8294
|
+
return buildLookupOnlyInformationalReply("small_talk", {
|
|
8295
|
+
intent_type: "small_talk",
|
|
8296
|
+
user_message: messageText,
|
|
8297
|
+
bot_name: firstNonEmptyString([route?.botName, route?.serverBotName, route?.server_bot_name, route?.name]),
|
|
8298
|
+
bot_role: String(route?.role || route?.roleProfile || "").trim(),
|
|
8299
|
+
}, executionOverride);
|
|
8300
|
+
}
|
|
8301
|
+
|
|
8302
|
+
function buildProjectBotRolesLookupResponse({ route, executionOverride }) {
|
|
8303
|
+
const payload = buildProjectBotRolesPayload({
|
|
8304
|
+
projectID: route?.projectID,
|
|
8305
|
+
provider: route?.provider,
|
|
8306
|
+
destinationID: route?.destinationID,
|
|
8307
|
+
destinationLabel: route?.destinationLabel,
|
|
8308
|
+
});
|
|
8309
|
+
return buildLookupOnlyInformationalReply("project.bot_roles", {
|
|
8310
|
+
...payload,
|
|
8311
|
+
}, executionOverride);
|
|
8312
|
+
}
|
|
8313
|
+
|
|
8314
|
+
async function buildRunnerStatusLookupOnlyResponse({
|
|
8315
|
+
route,
|
|
8316
|
+
routeState,
|
|
8317
|
+
selectedRecord,
|
|
8318
|
+
runtime,
|
|
8319
|
+
executionOverride,
|
|
8320
|
+
}) {
|
|
8321
|
+
let hydratedRunnerState = null;
|
|
8322
|
+
try {
|
|
8323
|
+
hydratedRunnerState = await hydrateRunnerRequestLedgerFromServer({
|
|
8324
|
+
normalizedRoute: route,
|
|
8325
|
+
runtime,
|
|
8326
|
+
});
|
|
8327
|
+
} catch {}
|
|
8328
|
+
const lookup = buildRunnerStatusQueryLookup({
|
|
8329
|
+
route,
|
|
8330
|
+
routeState,
|
|
8331
|
+
selectedRecord,
|
|
8332
|
+
runnerStateOverride: hydratedRunnerState,
|
|
8333
|
+
});
|
|
8334
|
+
return buildLookupOnlyInformationalReply("runner.status", lookup, executionOverride);
|
|
8335
|
+
}
|
|
8336
|
+
|
|
8337
|
+
async function buildArtifactLocationLookupResponse({
|
|
8338
|
+
route,
|
|
8339
|
+
runtime,
|
|
8340
|
+
messageText,
|
|
8341
|
+
executionOverride,
|
|
8342
|
+
}) {
|
|
8343
|
+
const payload = await locateProjectFilesForQuery({
|
|
8344
|
+
siteBaseURL: runtime.baseURL,
|
|
8345
|
+
projectID: route?.projectID,
|
|
8346
|
+
token: runtime.token,
|
|
8347
|
+
timeoutSeconds: runtime.timeoutSeconds,
|
|
8348
|
+
query: messageText,
|
|
8349
|
+
});
|
|
8350
|
+
return buildLookupOnlyInformationalReply("project.file.locate", {
|
|
8351
|
+
...payload,
|
|
8352
|
+
}, executionOverride);
|
|
8353
|
+
}
|
|
8354
|
+
|
|
8355
|
+
async function resolveLookupOnlyInformationalReply({
|
|
8356
|
+
normalizedIntentType,
|
|
8357
|
+
route,
|
|
8358
|
+
routeState,
|
|
8359
|
+
selectedRecord,
|
|
8360
|
+
runtime,
|
|
8361
|
+
executionPlan,
|
|
8362
|
+
messageText,
|
|
8363
|
+
executionOverride,
|
|
8364
|
+
}) {
|
|
8365
|
+
const lookupHandlers = {
|
|
8366
|
+
small_talk: () => buildSmallTalkLookupResponse({ route, messageText, executionOverride }),
|
|
8367
|
+
workspace_query: () => buildProjectWorkspaceLookupResponse({ route, executionPlan, executionOverride }),
|
|
8368
|
+
bot_role_query: () => buildProjectBotRolesLookupResponse({ route, executionOverride }),
|
|
8369
|
+
status_query: () => buildRunnerStatusLookupOnlyResponse({
|
|
8370
|
+
route,
|
|
8371
|
+
routeState,
|
|
8372
|
+
selectedRecord,
|
|
8373
|
+
runtime,
|
|
8374
|
+
executionOverride,
|
|
8375
|
+
}),
|
|
8376
|
+
artifact_location_query: () => buildArtifactLocationLookupResponse({
|
|
8377
|
+
route,
|
|
8378
|
+
runtime,
|
|
8379
|
+
messageText,
|
|
8380
|
+
executionOverride,
|
|
8381
|
+
}),
|
|
8382
|
+
};
|
|
8383
|
+
const handler = lookupHandlers[normalizedIntentType];
|
|
8384
|
+
return typeof handler === "function" ? handler() : null;
|
|
8385
|
+
}
|
|
8386
|
+
|
|
8387
|
+
function buildLookupOnlyInformationalReply(source, lookup, executionOverride = null) {
|
|
8388
|
+
const response = {
|
|
8389
|
+
handled: true,
|
|
8390
|
+
source: String(source || "").trim(),
|
|
8391
|
+
response_mode: "lookup_only",
|
|
8392
|
+
reply: "",
|
|
8393
|
+
lookup: safeObject(lookup),
|
|
7569
8394
|
};
|
|
8395
|
+
if (executionOverride && typeof executionOverride === "object") {
|
|
8396
|
+
response.execution_override = executionOverride;
|
|
8397
|
+
}
|
|
8398
|
+
return response;
|
|
7570
8399
|
}
|
|
7571
8400
|
|
|
7572
8401
|
async function resolveInformationalQueryReply({
|
|
@@ -7580,20 +8409,19 @@ async function resolveInformationalQueryReply({
|
|
|
7580
8409
|
const normalizedIntentType = String(intentType || "").trim();
|
|
7581
8410
|
const messageText = String(safeObject(selectedRecord?.parsedArchive).body || "").trim();
|
|
7582
8411
|
const executionOverride = buildInformationalMiniExecutionOverride({ route, executionPlan });
|
|
8412
|
+
const lookupOnlyResponse = await resolveLookupOnlyInformationalReply({
|
|
8413
|
+
normalizedIntentType,
|
|
8414
|
+
route,
|
|
8415
|
+
routeState,
|
|
8416
|
+
selectedRecord,
|
|
8417
|
+
runtime,
|
|
8418
|
+
executionPlan,
|
|
8419
|
+
messageText,
|
|
8420
|
+
executionOverride,
|
|
8421
|
+
});
|
|
8422
|
+
return lookupOnlyResponse || null;
|
|
7583
8423
|
if (normalizedIntentType === "small_talk") {
|
|
7584
|
-
return {
|
|
7585
|
-
handled: true,
|
|
7586
|
-
response_mode: "lookup_only",
|
|
7587
|
-
reply: "",
|
|
7588
|
-
source: "small_talk",
|
|
7589
|
-
lookup: {
|
|
7590
|
-
intent_type: "small_talk",
|
|
7591
|
-
user_message: messageText,
|
|
7592
|
-
bot_name: firstNonEmptyString([route?.botName, route?.serverBotName, route?.server_bot_name, route?.name]),
|
|
7593
|
-
bot_role: String(route?.role || route?.roleProfile || "").trim(),
|
|
7594
|
-
},
|
|
7595
|
-
execution_override: executionOverride,
|
|
7596
|
-
};
|
|
8424
|
+
return buildSmallTalkLookupResponse({ route, messageText, executionOverride });
|
|
7597
8425
|
}
|
|
7598
8426
|
/* Dead legacy direct small_talk branch kept commented for reference.
|
|
7599
8427
|
if (false && normalizedIntentType === "small_talk") {
|
|
@@ -7605,41 +8433,31 @@ async function resolveInformationalQueryReply({
|
|
|
7605
8433
|
}
|
|
7606
8434
|
*/
|
|
7607
8435
|
if (normalizedIntentType === "workspace_query") {
|
|
8436
|
+
return buildProjectWorkspaceLookupResponse({
|
|
8437
|
+
route,
|
|
8438
|
+
executionPlan,
|
|
8439
|
+
executionOverride,
|
|
8440
|
+
});
|
|
7608
8441
|
const workspace = resolveProjectWorkspaceBindingSummary(route?.projectID, executionPlan?.workspaceDir || route?.workspaceDir || "");
|
|
7609
8442
|
const workspaceDir = String(workspace.workspace_dir || "").trim();
|
|
7610
8443
|
const reply = workspaceDir
|
|
7611
8444
|
? `현재 이 프로젝트에 바인딩된 로컬 작업 폴더는 "${workspaceDir}" 입니다.`
|
|
7612
8445
|
: "현재 이 프로젝트는 project-workspaces.json에 로컬 작업 폴더가 바인딩되어 있지 않습니다.";
|
|
7613
|
-
return {
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
reply: "",
|
|
7617
|
-
source: "project.workspace",
|
|
7618
|
-
lookup: {
|
|
7619
|
-
...workspace,
|
|
7620
|
-
},
|
|
7621
|
-
execution_override: executionOverride,
|
|
7622
|
-
};
|
|
8446
|
+
return buildLookupOnlyInformationalReply("project.workspace", {
|
|
8447
|
+
...workspace,
|
|
8448
|
+
}, executionOverride);
|
|
7623
8449
|
}
|
|
7624
8450
|
if (normalizedIntentType === "bot_role_query") {
|
|
7625
|
-
|
|
7626
|
-
projectID: route?.projectID,
|
|
7627
|
-
provider: route?.provider,
|
|
7628
|
-
destinationID: route?.destinationID,
|
|
7629
|
-
destinationLabel: route?.destinationLabel,
|
|
7630
|
-
});
|
|
7631
|
-
return {
|
|
7632
|
-
handled: true,
|
|
7633
|
-
response_mode: "lookup_only",
|
|
7634
|
-
reply: "",
|
|
7635
|
-
source: "project.bot_roles",
|
|
7636
|
-
lookup: {
|
|
7637
|
-
...payload,
|
|
7638
|
-
},
|
|
7639
|
-
execution_override: executionOverride,
|
|
7640
|
-
};
|
|
8451
|
+
return buildProjectBotRolesLookupResponse({ route, executionOverride });
|
|
7641
8452
|
}
|
|
7642
8453
|
if (normalizedIntentType === "status_query") {
|
|
8454
|
+
return buildRunnerStatusLookupOnlyResponse({
|
|
8455
|
+
route,
|
|
8456
|
+
routeState,
|
|
8457
|
+
selectedRecord,
|
|
8458
|
+
runtime,
|
|
8459
|
+
executionOverride,
|
|
8460
|
+
});
|
|
7643
8461
|
let hydratedRunnerState = null;
|
|
7644
8462
|
try {
|
|
7645
8463
|
hydratedRunnerState = await hydrateRunnerRequestLedgerFromServer({
|
|
@@ -7647,18 +8465,13 @@ async function resolveInformationalQueryReply({
|
|
|
7647
8465
|
runtime,
|
|
7648
8466
|
});
|
|
7649
8467
|
} catch {}
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
routeState,
|
|
7658
|
-
selectedRecord,
|
|
7659
|
-
runnerStateOverride: hydratedRunnerState,
|
|
7660
|
-
}),
|
|
7661
|
-
};
|
|
8468
|
+
const lookup = buildRunnerStatusQueryLookup({
|
|
8469
|
+
route,
|
|
8470
|
+
routeState,
|
|
8471
|
+
selectedRecord,
|
|
8472
|
+
runnerStateOverride: hydratedRunnerState,
|
|
8473
|
+
});
|
|
8474
|
+
return buildLookupOnlyInformationalReply("runner.status", lookup, executionOverride);
|
|
7662
8475
|
const activeExecution = resolveRunnerActiveExecutionState(routeState);
|
|
7663
8476
|
if (activeExecution.active) {
|
|
7664
8477
|
const startedAt = String(activeExecution.startedAt || "").trim();
|
|
@@ -7690,23 +8503,12 @@ async function resolveInformationalQueryReply({
|
|
|
7690
8503
|
};
|
|
7691
8504
|
}
|
|
7692
8505
|
if (normalizedIntentType === "artifact_location_query") {
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
query: messageText,
|
|
8506
|
+
return buildArtifactLocationLookupResponse({
|
|
8507
|
+
route,
|
|
8508
|
+
runtime,
|
|
8509
|
+
messageText,
|
|
8510
|
+
executionOverride,
|
|
7699
8511
|
});
|
|
7700
|
-
return {
|
|
7701
|
-
handled: true,
|
|
7702
|
-
response_mode: "lookup_only",
|
|
7703
|
-
reply: "",
|
|
7704
|
-
source: "project.file.locate",
|
|
7705
|
-
lookup: {
|
|
7706
|
-
...payload,
|
|
7707
|
-
},
|
|
7708
|
-
execution_override: executionOverride,
|
|
7709
|
-
};
|
|
7710
8512
|
}
|
|
7711
8513
|
return null;
|
|
7712
8514
|
}
|
|
@@ -7717,7 +8519,6 @@ function buildRunnerExecutionDeps() {
|
|
|
7717
8519
|
adjudicateRunnerStartupLoopWithAI,
|
|
7718
8520
|
analyzeHumanConversationIntentWithAI,
|
|
7719
8521
|
auditRoleExecutionPlanWithAI,
|
|
7720
|
-
auditDirectHumanReplyWithAI,
|
|
7721
8522
|
planRoleExecutionWithAI,
|
|
7722
8523
|
repairRoleExecutionPlanWithAI,
|
|
7723
8524
|
normalizeRunnerRoleProfileName,
|
|
@@ -8317,6 +9118,74 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8317
9118
|
}
|
|
8318
9119
|
return null;
|
|
8319
9120
|
};
|
|
9121
|
+
const buildSelectedRecordSkippedRecord = (selectedRecord, reason, extra = {}) => ({
|
|
9122
|
+
id: selectedRecord.id,
|
|
9123
|
+
reason: String(reason || "").trim(),
|
|
9124
|
+
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
9125
|
+
...safeObject(extra),
|
|
9126
|
+
});
|
|
9127
|
+
const saveSelectedRecordSkipState = (selectedRecord, {
|
|
9128
|
+
action,
|
|
9129
|
+
reason,
|
|
9130
|
+
trigger = "",
|
|
9131
|
+
statePatch = {},
|
|
9132
|
+
}) => {
|
|
9133
|
+
saveRunnerRouteState(
|
|
9134
|
+
routeKey,
|
|
9135
|
+
buildRunnerRouteStateFromComment(selectedRecord, {
|
|
9136
|
+
last_action: String(action || "").trim(),
|
|
9137
|
+
last_reason: String(reason || "").trim(),
|
|
9138
|
+
last_trigger: String(trigger || "").trim(),
|
|
9139
|
+
...safeObject(statePatch),
|
|
9140
|
+
}),
|
|
9141
|
+
);
|
|
9142
|
+
};
|
|
9143
|
+
const pushSelectedRecordSkip = (skippedRecords, selectedRecord, {
|
|
9144
|
+
action,
|
|
9145
|
+
reason,
|
|
9146
|
+
trigger = "",
|
|
9147
|
+
statePatch = {},
|
|
9148
|
+
recordPatch = {},
|
|
9149
|
+
}) => {
|
|
9150
|
+
saveSelectedRecordSkipState(selectedRecord, {
|
|
9151
|
+
action,
|
|
9152
|
+
reason,
|
|
9153
|
+
trigger,
|
|
9154
|
+
statePatch,
|
|
9155
|
+
});
|
|
9156
|
+
skippedRecords.push(
|
|
9157
|
+
buildSelectedRecordSkippedRecord(selectedRecord, reason, recordPatch),
|
|
9158
|
+
);
|
|
9159
|
+
};
|
|
9160
|
+
const finalizeSkippedPendingRecords = async (skippedRecords) => {
|
|
9161
|
+
if (!ensureArray(skippedRecords).length) {
|
|
9162
|
+
return null;
|
|
9163
|
+
}
|
|
9164
|
+
await archiveRunnerDiagnosticComments({
|
|
9165
|
+
comments,
|
|
9166
|
+
skippedRecords,
|
|
9167
|
+
normalizedRoute,
|
|
9168
|
+
bot,
|
|
9169
|
+
archiveThread,
|
|
9170
|
+
runtime,
|
|
9171
|
+
});
|
|
9172
|
+
const distinctReasons = Array.from(new Set(skippedRecords.map((item) => item.reason).filter(Boolean)));
|
|
9173
|
+
const lastSkipped = skippedRecords[skippedRecords.length - 1];
|
|
9174
|
+
return {
|
|
9175
|
+
route_key: routeKey,
|
|
9176
|
+
route_name: normalizedRoute.name,
|
|
9177
|
+
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
9178
|
+
outcome: "skipped",
|
|
9179
|
+
detail: distinctReasons.join("; ") || "all pending messages were skipped",
|
|
9180
|
+
archive_source: String(archiveThread.source || "").trim() || "-",
|
|
9181
|
+
archive_work_item_id: String(archiveThread.workItemID || "").trim() || "",
|
|
9182
|
+
thread_id: archiveThread.threadID,
|
|
9183
|
+
comment_id: lastSkipped.id,
|
|
9184
|
+
skipped_count: skippedRecords.length,
|
|
9185
|
+
execution_mode: executionPlan.mode,
|
|
9186
|
+
role_profile: executionPlan.roleProfileName,
|
|
9187
|
+
};
|
|
9188
|
+
};
|
|
8320
9189
|
const prepareRunnerRequestClaim = async (selectedRecord, selectedResponderSelectors = [], sharedHumanIntent = null) => {
|
|
8321
9190
|
const parsed = safeObject(selectedRecord?.parsedArchive);
|
|
8322
9191
|
const kind = String(parsed.kind || "").trim().toLowerCase();
|
|
@@ -8353,316 +9222,122 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8353
9222
|
archiveThreadID: archiveThread.threadID,
|
|
8354
9223
|
});
|
|
8355
9224
|
};
|
|
8356
|
-
|
|
8357
|
-
const
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
executionPlan,
|
|
8419
|
-
deps: routingExecutionDeps,
|
|
8420
|
-
});
|
|
8421
|
-
if (safeObject(sharedHumanIntentContext).contractNeedsResolution === true) {
|
|
8422
|
-
const reason = "human intent contract is incomplete and requires regeneration";
|
|
8423
|
-
saveRunnerRouteState(
|
|
8424
|
-
routeKey,
|
|
8425
|
-
buildRunnerRouteStateFromComment(selectedRecord, {
|
|
8426
|
-
last_action: "needs_contract",
|
|
8427
|
-
last_reason: reason,
|
|
8428
|
-
last_trigger: "human_intent_contract",
|
|
8429
|
-
}),
|
|
8430
|
-
);
|
|
8431
|
-
skippedRecords.push({
|
|
8432
|
-
id: selectedRecord.id,
|
|
8433
|
-
reason,
|
|
8434
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
8435
|
-
});
|
|
8436
|
-
continue;
|
|
8437
|
-
}
|
|
8438
|
-
const precomputedAdjudication = buildRunnerResponderAdjudicationFromHumanIntent({
|
|
8439
|
-
selectedRecord,
|
|
8440
|
-
normalizedRoute,
|
|
8441
|
-
bot,
|
|
8442
|
-
deps: routingExecutionDeps,
|
|
8443
|
-
precomputedHumanIntent: safeObject(sharedHumanIntentContext).humanIntent || null,
|
|
8444
|
-
});
|
|
8445
|
-
const adjudication = precomputedAdjudication || await resolveRunnerResponderAdjudication({
|
|
8446
|
-
selectedRecord,
|
|
8447
|
-
pendingOrdered: pending.ordered,
|
|
8448
|
-
normalizedRoute,
|
|
8449
|
-
bot,
|
|
8450
|
-
executionPlan,
|
|
8451
|
-
deps: routingExecutionDeps,
|
|
8452
|
-
precomputedHumanIntent: safeObject(sharedHumanIntentContext).humanIntent || null,
|
|
8453
|
-
});
|
|
8454
|
-
const currentBotSelected = ensureArray(adjudication.selected_bot_usernames)
|
|
8455
|
-
.map((value) => String(value || "").trim().replace(/^@+/, "").toLowerCase())
|
|
8456
|
-
.includes(currentBotSelector);
|
|
8457
|
-
if (!currentBotSelected) {
|
|
8458
|
-
saveRunnerRouteState(
|
|
9225
|
+
const buildContinuationResponderAdjudication = (selectedRecord, requestRaw) => {
|
|
9226
|
+
const request = safeObject(requestRaw);
|
|
9227
|
+
const referencedBotUsernames = ensureArray(safeObject(selectedRecord?.parsedArchive).mentionUsernames)
|
|
9228
|
+
.map((value) => normalizeTelegramMentionUsername(value))
|
|
9229
|
+
.filter(Boolean);
|
|
9230
|
+
const selectedBotUsernames = uniqueOrderedStrings(
|
|
9231
|
+
ensureArray(request.next_expected_responders).length
|
|
9232
|
+
? request.next_expected_responders
|
|
9233
|
+
: ensureArray(request.conversation_allowed_responders).length
|
|
9234
|
+
? request.conversation_allowed_responders
|
|
9235
|
+
: ensureArray(request.selected_bot_usernames).length
|
|
9236
|
+
? request.selected_bot_usernames
|
|
9237
|
+
: request.conversation_initial_responders,
|
|
9238
|
+
normalizeTelegramMentionUsername,
|
|
9239
|
+
);
|
|
9240
|
+
return {
|
|
9241
|
+
decision: selectedBotUsernames.length > 1
|
|
9242
|
+
? "multiple_responders"
|
|
9243
|
+
: selectedBotUsernames.length === 1
|
|
9244
|
+
? "single_responder"
|
|
9245
|
+
: "no_responder",
|
|
9246
|
+
selected_bot_usernames: selectedBotUsernames,
|
|
9247
|
+
referenced_bot_usernames: referencedBotUsernames,
|
|
9248
|
+
confidence: "high",
|
|
9249
|
+
reason_code: selectedBotUsernames.length > 0
|
|
9250
|
+
? "continuation_request_contract"
|
|
9251
|
+
: "continuation_request_contract_no_responder",
|
|
9252
|
+
clarification: "",
|
|
9253
|
+
};
|
|
9254
|
+
};
|
|
9255
|
+
const finalizePreparedRunnerRequest = async (selectedRecord, requestClaim) => {
|
|
9256
|
+
const inheritedRootReference = await inheritRunnerReferenceRootWorkItemForRequest({
|
|
9257
|
+
normalizedRoute,
|
|
9258
|
+
selectedRecord,
|
|
9259
|
+
runtime,
|
|
9260
|
+
requestKey: requestClaim.requestKey,
|
|
9261
|
+
});
|
|
9262
|
+
const rootWorkItemClaim = await ensureRunnerRootWorkItemForRequest({
|
|
9263
|
+
normalizedRoute,
|
|
9264
|
+
routeKey,
|
|
9265
|
+
selectedRecord,
|
|
9266
|
+
runtime,
|
|
9267
|
+
requestKey: requestClaim.requestKey,
|
|
9268
|
+
});
|
|
9269
|
+
const claimedRequest = safeObject(
|
|
9270
|
+
loadRunnerRequestByKey(requestClaim.requestKey)
|
|
9271
|
+
|| rootWorkItemClaim.request
|
|
9272
|
+
|| inheritedRootReference.request
|
|
9273
|
+
|| requestClaim.request,
|
|
9274
|
+
);
|
|
9275
|
+
const missingRequiredRootWorkItem = actionableRunnerRequestMissingRootWorkItem(claimedRequest);
|
|
9276
|
+
if (!rootWorkItemClaim.ok || missingRequiredRootWorkItem) {
|
|
9277
|
+
const rootWorkItemFailure = String(
|
|
9278
|
+
rootWorkItemClaim.error
|
|
9279
|
+
|| rootWorkItemClaim.reason
|
|
9280
|
+
|| (missingRequiredRootWorkItem ? "root_work_item_missing" : "root_work_item_create_failed"),
|
|
9281
|
+
).trim() || "root_work_item_create_failed";
|
|
9282
|
+
if (String(requestClaim.requestKey || "").trim()) {
|
|
9283
|
+
markRunnerRequestLifecycle({
|
|
9284
|
+
normalizedRoute,
|
|
9285
|
+
requestKey: requestClaim.requestKey,
|
|
9286
|
+
selectedRecord,
|
|
8459
9287
|
routeKey,
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
last_reason: String(adjudication.reason_code || "").trim() || "not_selected_by_adjudicator",
|
|
8463
|
-
last_trigger: "responder_adjudication",
|
|
8464
|
-
}),
|
|
8465
|
-
);
|
|
8466
|
-
skippedRecords.push({
|
|
8467
|
-
id: selectedRecord.id,
|
|
8468
|
-
reason: String(adjudication.reason_code || "").trim() || "not_selected_by_adjudicator",
|
|
8469
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
9288
|
+
outcome: "closed",
|
|
9289
|
+
closedReason: rootWorkItemFailure,
|
|
8470
9290
|
});
|
|
8471
|
-
continue;
|
|
8472
|
-
}
|
|
8473
|
-
const requestClaim = await prepareRunnerRequestClaim(
|
|
8474
|
-
selectedRecord,
|
|
8475
|
-
adjudication.selected_bot_usernames,
|
|
8476
|
-
safeObject(sharedHumanIntentContext).humanIntent || null,
|
|
8477
|
-
);
|
|
8478
|
-
if (!requestClaim.ok) {
|
|
8479
9291
|
await syncRunnerRequestLedgerForProjectToServer({
|
|
8480
9292
|
normalizedRoute,
|
|
8481
9293
|
runtime,
|
|
8482
9294
|
});
|
|
8483
|
-
saveRunnerRouteState(
|
|
8484
|
-
routeKey,
|
|
8485
|
-
buildRunnerRouteStateFromComment(selectedRecord, {
|
|
8486
|
-
last_action: "request_skipped",
|
|
8487
|
-
last_reason: String(requestClaim.reason || "request_unavailable").trim() || "request_unavailable",
|
|
8488
|
-
last_trigger: "request_ledger",
|
|
8489
|
-
}),
|
|
8490
|
-
);
|
|
8491
|
-
skippedRecords.push({
|
|
8492
|
-
id: selectedRecord.id,
|
|
8493
|
-
reason: String(requestClaim.reason || "request_unavailable").trim() || "request_unavailable",
|
|
8494
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
8495
|
-
diagnosticType: "skip",
|
|
8496
|
-
contextExcluded: String(requestClaim.reason || "").trim() === "bot_reply_without_active_request",
|
|
8497
|
-
action: "skip_invalid_request_state",
|
|
8498
|
-
closedReason: String(requestClaim.reason || "").trim(),
|
|
8499
|
-
});
|
|
8500
|
-
continue;
|
|
8501
|
-
}
|
|
8502
|
-
const inheritedRootReference = await inheritRunnerReferenceRootWorkItemForRequest({
|
|
8503
|
-
normalizedRoute,
|
|
8504
|
-
selectedRecord,
|
|
8505
|
-
runtime,
|
|
8506
|
-
requestKey: requestClaim.requestKey,
|
|
8507
|
-
});
|
|
8508
|
-
const rootWorkItemClaim = await ensureRunnerRootWorkItemForRequest({
|
|
8509
|
-
normalizedRoute,
|
|
8510
|
-
routeKey,
|
|
8511
|
-
selectedRecord,
|
|
8512
|
-
runtime,
|
|
8513
|
-
requestKey: requestClaim.requestKey,
|
|
8514
|
-
});
|
|
8515
|
-
const claimedRequest = safeObject(
|
|
8516
|
-
loadRunnerRequestByKey(requestClaim.requestKey)
|
|
8517
|
-
|| rootWorkItemClaim.request
|
|
8518
|
-
|| inheritedRootReference.request
|
|
8519
|
-
|| requestClaim.request,
|
|
8520
|
-
);
|
|
8521
|
-
const missingRequiredRootWorkItem = actionableRunnerRequestMissingRootWorkItem(claimedRequest);
|
|
8522
|
-
if (!rootWorkItemClaim.ok || missingRequiredRootWorkItem) {
|
|
8523
|
-
const rootWorkItemFailure = String(
|
|
8524
|
-
rootWorkItemClaim.error
|
|
8525
|
-
|| rootWorkItemClaim.reason
|
|
8526
|
-
|| (missingRequiredRootWorkItem ? "root_work_item_missing" : "root_work_item_create_failed"),
|
|
8527
|
-
).trim() || "root_work_item_create_failed";
|
|
8528
|
-
if (String(requestClaim.requestKey || "").trim()) {
|
|
8529
|
-
markRunnerRequestLifecycle({
|
|
8530
|
-
normalizedRoute,
|
|
8531
|
-
requestKey: requestClaim.requestKey,
|
|
8532
|
-
selectedRecord,
|
|
8533
|
-
routeKey,
|
|
8534
|
-
outcome: "closed",
|
|
8535
|
-
closedReason: rootWorkItemFailure,
|
|
8536
|
-
});
|
|
8537
|
-
await syncRunnerRequestLedgerForProjectToServer({
|
|
8538
|
-
normalizedRoute,
|
|
8539
|
-
runtime,
|
|
8540
|
-
});
|
|
8541
|
-
}
|
|
8542
|
-
saveRunnerRouteState(
|
|
8543
|
-
routeKey,
|
|
8544
|
-
buildRunnerRouteStateFromComment(selectedRecord, {
|
|
8545
|
-
last_action: "request_skipped",
|
|
8546
|
-
last_reason: rootWorkItemFailure,
|
|
8547
|
-
last_trigger: "work_item_root",
|
|
8548
|
-
last_request_key: String(requestClaim.requestKey || "").trim(),
|
|
8549
|
-
}),
|
|
8550
|
-
);
|
|
8551
|
-
skippedRecords.push({
|
|
8552
|
-
id: selectedRecord.id,
|
|
8553
|
-
reason: rootWorkItemFailure,
|
|
8554
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
8555
|
-
diagnosticType: "skip",
|
|
8556
|
-
action: "skip_missing_root_work_item",
|
|
8557
|
-
closedReason: rootWorkItemFailure,
|
|
8558
|
-
});
|
|
8559
|
-
continue;
|
|
8560
9295
|
}
|
|
8561
|
-
saveRunnerRouteState(routeKey, {
|
|
8562
|
-
...buildRunnerActiveExecutionPatch(selectedRecord, requestClaim.requestKey, {
|
|
8563
|
-
id: String(claimedRequest.root_work_item_id || "").trim(),
|
|
8564
|
-
title: String(claimedRequest.root_work_item_title || "").trim(),
|
|
8565
|
-
status: String(claimedRequest.root_work_item_status || "").trim(),
|
|
8566
|
-
}),
|
|
8567
|
-
last_request_key: String(requestClaim.requestKey || "").trim(),
|
|
8568
|
-
last_root_work_item_id: String(claimedRequest.root_work_item_id || "").trim(),
|
|
8569
|
-
last_root_work_item_title: String(claimedRequest.root_work_item_title || "").trim(),
|
|
8570
|
-
last_root_work_item_status: String(claimedRequest.root_work_item_status || "").trim(),
|
|
8571
|
-
});
|
|
8572
|
-
await syncRunnerRequestLedgerForProjectToServer({
|
|
8573
|
-
normalizedRoute,
|
|
8574
|
-
runtime,
|
|
8575
|
-
});
|
|
8576
9296
|
return {
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
routeState: safeObject(loadBotRunnerState().routes[routeKey]),
|
|
8592
|
-
selectedRecord,
|
|
8593
|
-
pendingOrdered: pending.ordered,
|
|
8594
|
-
bot,
|
|
8595
|
-
destination,
|
|
8596
|
-
archiveThread,
|
|
8597
|
-
executionPlan,
|
|
8598
|
-
runtime,
|
|
8599
|
-
managedConversationBots,
|
|
8600
|
-
requestKey: String(requestClaim.requestKey || "").trim(),
|
|
8601
|
-
triggerDecision,
|
|
8602
|
-
responderAdjudication: adjudication,
|
|
8603
|
-
humanIntentContext: sharedHumanIntentContext,
|
|
9297
|
+
ok: false,
|
|
9298
|
+
skip: {
|
|
9299
|
+
kind: "skip",
|
|
9300
|
+
skipAction: "request_skipped",
|
|
9301
|
+
skipReason: rootWorkItemFailure,
|
|
9302
|
+
skipTrigger: "work_item_root",
|
|
9303
|
+
skipStatePatch: {
|
|
9304
|
+
last_request_key: String(requestClaim.requestKey || "").trim(),
|
|
9305
|
+
},
|
|
9306
|
+
skipRecordPatch: {
|
|
9307
|
+
diagnosticType: "skip",
|
|
9308
|
+
action: "skip_missing_root_work_item",
|
|
9309
|
+
closedReason: rootWorkItemFailure,
|
|
9310
|
+
},
|
|
8604
9311
|
},
|
|
8605
9312
|
};
|
|
8606
9313
|
}
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
archiveThread,
|
|
8614
|
-
runtime,
|
|
8615
|
-
});
|
|
8616
|
-
const distinctReasons = Array.from(new Set(skippedRecords.map((item) => item.reason).filter(Boolean)));
|
|
8617
|
-
const lastSkipped = skippedRecords[skippedRecords.length - 1];
|
|
8618
|
-
return {
|
|
8619
|
-
route_key: routeKey,
|
|
8620
|
-
route_name: normalizedRoute.name,
|
|
8621
|
-
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
8622
|
-
outcome: "skipped",
|
|
8623
|
-
detail: distinctReasons.join("; ") || "all pending messages were skipped",
|
|
8624
|
-
archive_source: String(archiveThread.source || "").trim() || "-",
|
|
8625
|
-
archive_work_item_id: String(archiveThread.workItemID || "").trim() || "",
|
|
8626
|
-
thread_id: archiveThread.threadID,
|
|
8627
|
-
comment_id: lastSkipped.id,
|
|
8628
|
-
skipped_count: skippedRecords.length,
|
|
8629
|
-
execution_mode: executionPlan.mode,
|
|
8630
|
-
role_profile: executionPlan.roleProfileName,
|
|
8631
|
-
};
|
|
8632
|
-
}
|
|
8633
|
-
}
|
|
8634
|
-
const skippedRecords = [];
|
|
8635
|
-
for (const selectedRecord of pending.pending) {
|
|
9314
|
+
return {
|
|
9315
|
+
ok: true,
|
|
9316
|
+
claimedRequest,
|
|
9317
|
+
};
|
|
9318
|
+
};
|
|
9319
|
+
const prepareRunnerSelectedRecordForExecution = async (selectedRecord) => {
|
|
8636
9320
|
const duplicateArchivedSkip = maybeBuildDuplicateArchivedSkip(selectedRecord, refreshedState);
|
|
8637
9321
|
if (duplicateArchivedSkip) {
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
);
|
|
8646
|
-
skippedRecords.push(duplicateArchivedSkip);
|
|
8647
|
-
continue;
|
|
9322
|
+
return {
|
|
9323
|
+
kind: "skip",
|
|
9324
|
+
skipAction: "duplicate_skipped",
|
|
9325
|
+
skipReason: String(duplicateArchivedSkip.reason || "duplicate_archived_source_message").trim(),
|
|
9326
|
+
skipTrigger: "archive_dedupe",
|
|
9327
|
+
skipRecordPatch: duplicateArchivedSkip,
|
|
9328
|
+
};
|
|
8648
9329
|
}
|
|
8649
9330
|
const triggerDecision = evaluateTelegramRunnerTrigger(selectedRecord, normalizedRoute, bot);
|
|
8650
9331
|
if (triggerDecision.shouldRespond !== true) {
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
id: selectedRecord.id,
|
|
8661
|
-
reason: String(triggerDecision.reason || "trigger policy skipped message").trim() || "trigger policy skipped message",
|
|
8662
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
8663
|
-
suppressDiagnostic: true,
|
|
8664
|
-
});
|
|
8665
|
-
continue;
|
|
9332
|
+
return {
|
|
9333
|
+
kind: "skip",
|
|
9334
|
+
skipAction: "trigger_skipped",
|
|
9335
|
+
skipReason: String(triggerDecision.reason || "trigger policy skipped message").trim() || "trigger policy skipped message",
|
|
9336
|
+
skipTrigger: String(triggerDecision.trigger || "").trim() || "trigger_policy",
|
|
9337
|
+
skipRecordPatch: {
|
|
9338
|
+
suppressDiagnostic: true,
|
|
9339
|
+
},
|
|
9340
|
+
};
|
|
8666
9341
|
}
|
|
8667
9342
|
const startupLoopSkipped = await maybeHandleRunnerStartupLoopCandidate({
|
|
8668
9343
|
routeKey,
|
|
@@ -8680,14 +9355,66 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8680
9355
|
: false,
|
|
8681
9356
|
});
|
|
8682
9357
|
if (startupLoopSkipped) {
|
|
8683
|
-
|
|
8684
|
-
|
|
9358
|
+
return {
|
|
9359
|
+
kind: "startup_loop_skipped",
|
|
9360
|
+
skippedRecord: startupLoopSkipped.skippedRecord,
|
|
9361
|
+
};
|
|
8685
9362
|
}
|
|
8686
9363
|
const routingExecutionDeps = {
|
|
8687
9364
|
...buildRunnerExecutionDeps(),
|
|
8688
9365
|
managedConversationBots,
|
|
8689
9366
|
resolveConversationPeerBots: resolveRunnerConversationPeers,
|
|
8690
9367
|
};
|
|
9368
|
+
const selectedRecordKind = String(safeObject(selectedRecord?.parsedArchive).kind || "").trim().toLowerCase();
|
|
9369
|
+
if (selectedRecordKind === "bot_reply") {
|
|
9370
|
+
const requestClaim = await prepareRunnerRequestClaim(selectedRecord);
|
|
9371
|
+
if (!requestClaim.ok) {
|
|
9372
|
+
await syncRunnerRequestLedgerForProjectToServer({
|
|
9373
|
+
normalizedRoute,
|
|
9374
|
+
runtime,
|
|
9375
|
+
});
|
|
9376
|
+
return {
|
|
9377
|
+
kind: "skip",
|
|
9378
|
+
skipAction: "request_skipped",
|
|
9379
|
+
skipReason: String(requestClaim.reason || "request_unavailable").trim() || "request_unavailable",
|
|
9380
|
+
skipTrigger: "request_ledger",
|
|
9381
|
+
skipRecordPatch: {
|
|
9382
|
+
diagnosticType: "skip",
|
|
9383
|
+
contextExcluded: String(requestClaim.reason || "").trim() === "bot_reply_without_active_request",
|
|
9384
|
+
action: "skip_invalid_request_state",
|
|
9385
|
+
closedReason: String(requestClaim.reason || "").trim(),
|
|
9386
|
+
},
|
|
9387
|
+
};
|
|
9388
|
+
}
|
|
9389
|
+
const continuationAdjudication = buildContinuationResponderAdjudication(
|
|
9390
|
+
selectedRecord,
|
|
9391
|
+
requestClaim.request,
|
|
9392
|
+
);
|
|
9393
|
+
const currentBotSelected = ensureArray(continuationAdjudication.selected_bot_usernames)
|
|
9394
|
+
.map((value) => String(value || "").trim().replace(/^@+/, "").toLowerCase())
|
|
9395
|
+
.includes(currentBotSelector);
|
|
9396
|
+
if (!currentBotSelected) {
|
|
9397
|
+
return {
|
|
9398
|
+
kind: "skip",
|
|
9399
|
+
skipAction: "adjudication_skipped",
|
|
9400
|
+
skipReason: String(continuationAdjudication.reason_code || "").trim() || "not_selected_by_request_contract",
|
|
9401
|
+
skipTrigger: "request_contract",
|
|
9402
|
+
};
|
|
9403
|
+
}
|
|
9404
|
+
const finalizedRequest = await finalizePreparedRunnerRequest(selectedRecord, requestClaim);
|
|
9405
|
+
if (!finalizedRequest.ok) {
|
|
9406
|
+
return finalizedRequest.skip;
|
|
9407
|
+
}
|
|
9408
|
+
return {
|
|
9409
|
+
kind: "ready",
|
|
9410
|
+
triggerDecision,
|
|
9411
|
+
routingExecutionDeps,
|
|
9412
|
+
sharedHumanIntentContext: null,
|
|
9413
|
+
adjudication: continuationAdjudication,
|
|
9414
|
+
requestClaim,
|
|
9415
|
+
claimedRequest: finalizedRequest.claimedRequest,
|
|
9416
|
+
};
|
|
9417
|
+
}
|
|
8691
9418
|
const sharedHumanIntentContext = await resolveHumanIntentContext({
|
|
8692
9419
|
selectedRecord,
|
|
8693
9420
|
normalizedRoute,
|
|
@@ -8696,30 +9423,31 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8696
9423
|
deps: routingExecutionDeps,
|
|
8697
9424
|
});
|
|
8698
9425
|
if (safeObject(sharedHumanIntentContext).contractNeedsResolution === true) {
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
last_trigger: "human_intent_contract",
|
|
8706
|
-
}),
|
|
8707
|
-
);
|
|
8708
|
-
skippedRecords.push({
|
|
8709
|
-
id: selectedRecord.id,
|
|
8710
|
-
reason,
|
|
8711
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
8712
|
-
});
|
|
8713
|
-
continue;
|
|
9426
|
+
return {
|
|
9427
|
+
kind: "skip",
|
|
9428
|
+
skipAction: "needs_contract",
|
|
9429
|
+
skipReason: "human intent contract is incomplete and requires regeneration",
|
|
9430
|
+
skipTrigger: "human_intent_contract",
|
|
9431
|
+
};
|
|
8714
9432
|
}
|
|
8715
|
-
const
|
|
9433
|
+
const precomputedAdjudication = buildRunnerResponderAdjudicationFromHumanIntent({
|
|
8716
9434
|
selectedRecord,
|
|
8717
9435
|
normalizedRoute,
|
|
8718
9436
|
bot,
|
|
8719
9437
|
deps: routingExecutionDeps,
|
|
8720
9438
|
precomputedHumanIntent: safeObject(sharedHumanIntentContext).humanIntent || null,
|
|
8721
9439
|
});
|
|
8722
|
-
const
|
|
9440
|
+
const shouldRequireContractDrivenResponderSelection = selectedRecordKind !== "bot_reply"
|
|
9441
|
+
&& safeObject(selectedRecord?.parsedArchive).senderIsBot !== true;
|
|
9442
|
+
if (!precomputedAdjudication && shouldRequireContractDrivenResponderSelection) {
|
|
9443
|
+
return {
|
|
9444
|
+
kind: "skip",
|
|
9445
|
+
skipAction: "needs_contract",
|
|
9446
|
+
skipReason: "human intent contract did not select any responder",
|
|
9447
|
+
skipTrigger: "human_intent_contract",
|
|
9448
|
+
};
|
|
9449
|
+
}
|
|
9450
|
+
const adjudication = precomputedAdjudication || await resolveRunnerResponderAdjudication({
|
|
8723
9451
|
selectedRecord,
|
|
8724
9452
|
pendingOrdered: pending.ordered,
|
|
8725
9453
|
normalizedRoute,
|
|
@@ -8728,29 +9456,20 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8728
9456
|
deps: routingExecutionDeps,
|
|
8729
9457
|
precomputedHumanIntent: safeObject(sharedHumanIntentContext).humanIntent || null,
|
|
8730
9458
|
});
|
|
8731
|
-
const
|
|
9459
|
+
const currentBotSelected = ensureArray(adjudication.selected_bot_usernames)
|
|
8732
9460
|
.map((value) => String(value || "").trim().replace(/^@+/, "").toLowerCase())
|
|
8733
9461
|
.includes(currentBotSelector);
|
|
8734
|
-
if (!
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
}),
|
|
8742
|
-
);
|
|
8743
|
-
skippedRecords.push({
|
|
8744
|
-
id: selectedRecord.id,
|
|
8745
|
-
reason: String(inlineAdjudication.reason_code || "").trim() || "not_selected_by_adjudicator",
|
|
8746
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
8747
|
-
});
|
|
8748
|
-
continue;
|
|
9462
|
+
if (!currentBotSelected) {
|
|
9463
|
+
return {
|
|
9464
|
+
kind: "skip",
|
|
9465
|
+
skipAction: "adjudication_skipped",
|
|
9466
|
+
skipReason: String(adjudication.reason_code || "").trim() || "not_selected_by_adjudicator",
|
|
9467
|
+
skipTrigger: "responder_adjudication",
|
|
9468
|
+
};
|
|
8749
9469
|
}
|
|
8750
|
-
const currentRouteState = safeObject(loadBotRunnerState().routes[routeKey]);
|
|
8751
9470
|
const requestClaim = await prepareRunnerRequestClaim(
|
|
8752
9471
|
selectedRecord,
|
|
8753
|
-
|
|
9472
|
+
adjudication.selected_bot_usernames,
|
|
8754
9473
|
safeObject(sharedHumanIntentContext).humanIntent || null,
|
|
8755
9474
|
);
|
|
8756
9475
|
if (!requestClaim.ok) {
|
|
@@ -8758,84 +9477,136 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8758
9477
|
normalizedRoute,
|
|
8759
9478
|
runtime,
|
|
8760
9479
|
});
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
9480
|
+
return {
|
|
9481
|
+
kind: "skip",
|
|
9482
|
+
skipAction: "request_skipped",
|
|
9483
|
+
skipReason: String(requestClaim.reason || "request_unavailable").trim() || "request_unavailable",
|
|
9484
|
+
skipTrigger: "request_ledger",
|
|
9485
|
+
skipRecordPatch: {
|
|
9486
|
+
diagnosticType: "skip",
|
|
9487
|
+
contextExcluded: String(requestClaim.reason || "").trim() === "bot_reply_without_active_request",
|
|
9488
|
+
action: "skip_invalid_request_state",
|
|
9489
|
+
closedReason: String(requestClaim.reason || "").trim(),
|
|
9490
|
+
},
|
|
9491
|
+
};
|
|
9492
|
+
}
|
|
9493
|
+
const finalizedRequest = await finalizePreparedRunnerRequest(selectedRecord, requestClaim);
|
|
9494
|
+
if (!finalizedRequest.ok) {
|
|
9495
|
+
return finalizedRequest.skip;
|
|
9496
|
+
}
|
|
9497
|
+
return {
|
|
9498
|
+
kind: "ready",
|
|
9499
|
+
triggerDecision,
|
|
9500
|
+
routingExecutionDeps,
|
|
9501
|
+
sharedHumanIntentContext,
|
|
9502
|
+
adjudication,
|
|
9503
|
+
requestClaim,
|
|
9504
|
+
claimedRequest: finalizedRequest.claimedRequest,
|
|
9505
|
+
};
|
|
9506
|
+
};
|
|
9507
|
+
if (deferExecution) {
|
|
9508
|
+
const skippedRecords = [];
|
|
9509
|
+
for (const selectedRecord of pending.pending) {
|
|
9510
|
+
const preparation = await prepareRunnerSelectedRecordForExecution(selectedRecord);
|
|
9511
|
+
if (preparation.kind === "skip") {
|
|
9512
|
+
pushSelectedRecordSkip(skippedRecords, selectedRecord, {
|
|
9513
|
+
action: preparation.skipAction,
|
|
9514
|
+
reason: preparation.skipReason,
|
|
9515
|
+
trigger: preparation.skipTrigger,
|
|
9516
|
+
statePatch: preparation.skipStatePatch,
|
|
9517
|
+
recordPatch: preparation.skipRecordPatch,
|
|
9518
|
+
});
|
|
9519
|
+
continue;
|
|
9520
|
+
}
|
|
9521
|
+
if (preparation.kind === "startup_loop_skipped") {
|
|
9522
|
+
skippedRecords.push(preparation.skippedRecord);
|
|
9523
|
+
continue;
|
|
9524
|
+
}
|
|
9525
|
+
const {
|
|
9526
|
+
triggerDecision,
|
|
9527
|
+
sharedHumanIntentContext,
|
|
9528
|
+
adjudication,
|
|
9529
|
+
requestClaim,
|
|
9530
|
+
claimedRequest,
|
|
9531
|
+
} = preparation;
|
|
9532
|
+
saveRunnerRouteState(routeKey, {
|
|
9533
|
+
...buildRunnerActiveExecutionPatch(selectedRecord, requestClaim.requestKey, {
|
|
9534
|
+
id: String(claimedRequest.root_work_item_id || "").trim(),
|
|
9535
|
+
title: String(claimedRequest.root_work_item_title || "").trim(),
|
|
9536
|
+
status: String(claimedRequest.root_work_item_status || "").trim(),
|
|
8767
9537
|
}),
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
8773
|
-
diagnosticType: "skip",
|
|
8774
|
-
contextExcluded: String(requestClaim.reason || "").trim() === "bot_reply_without_active_request",
|
|
8775
|
-
action: "skip_invalid_request_state",
|
|
8776
|
-
closedReason: String(requestClaim.reason || "").trim(),
|
|
9538
|
+
last_request_key: String(requestClaim.requestKey || "").trim(),
|
|
9539
|
+
last_root_work_item_id: String(claimedRequest.root_work_item_id || "").trim(),
|
|
9540
|
+
last_root_work_item_title: String(claimedRequest.root_work_item_title || "").trim(),
|
|
9541
|
+
last_root_work_item_status: String(claimedRequest.root_work_item_status || "").trim(),
|
|
8777
9542
|
});
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|| rootWorkItemClaim.request
|
|
8796
|
-
|| inheritedRootReference.request
|
|
8797
|
-
|| requestClaim.request,
|
|
8798
|
-
);
|
|
8799
|
-
const missingRequiredRootWorkItem = actionableRunnerRequestMissingRootWorkItem(claimedRequest);
|
|
8800
|
-
if (!rootWorkItemClaim.ok || missingRequiredRootWorkItem) {
|
|
8801
|
-
const rootWorkItemFailure = String(
|
|
8802
|
-
rootWorkItemClaim.error
|
|
8803
|
-
|| rootWorkItemClaim.reason
|
|
8804
|
-
|| (missingRequiredRootWorkItem ? "root_work_item_missing" : "root_work_item_create_failed"),
|
|
8805
|
-
).trim() || "root_work_item_create_failed";
|
|
8806
|
-
if (String(requestClaim.requestKey || "").trim()) {
|
|
8807
|
-
markRunnerRequestLifecycle({
|
|
8808
|
-
normalizedRoute,
|
|
8809
|
-
requestKey: requestClaim.requestKey,
|
|
8810
|
-
selectedRecord,
|
|
9543
|
+
await syncRunnerRequestLedgerForProjectToServer({
|
|
9544
|
+
normalizedRoute,
|
|
9545
|
+
runtime,
|
|
9546
|
+
});
|
|
9547
|
+
return {
|
|
9548
|
+
route_key: routeKey,
|
|
9549
|
+
route_name: normalizedRoute.name,
|
|
9550
|
+
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
9551
|
+
outcome: "accepted",
|
|
9552
|
+
detail: `accepted comment ${selectedRecord.id} for background execution`,
|
|
9553
|
+
archive_source: String(archiveThread.source || "").trim() || "-",
|
|
9554
|
+
archive_work_item_id: String(archiveThread.workItemID || "").trim() || "",
|
|
9555
|
+
thread_id: archiveThread.threadID,
|
|
9556
|
+
comment_id: selectedRecord.id,
|
|
9557
|
+
execution_mode: executionPlan.mode,
|
|
9558
|
+
role_profile: executionPlan.roleProfileName,
|
|
9559
|
+
deferred_execution: {
|
|
8811
9560
|
routeKey,
|
|
8812
|
-
outcome: "closed",
|
|
8813
|
-
closedReason: rootWorkItemFailure,
|
|
8814
|
-
});
|
|
8815
|
-
await syncRunnerRequestLedgerForProjectToServer({
|
|
8816
9561
|
normalizedRoute,
|
|
9562
|
+
routeState: safeObject(loadBotRunnerState().routes[routeKey]),
|
|
9563
|
+
selectedRecord,
|
|
9564
|
+
pendingOrdered: pending.ordered,
|
|
9565
|
+
bot,
|
|
9566
|
+
destination,
|
|
9567
|
+
archiveThread,
|
|
9568
|
+
executionPlan,
|
|
8817
9569
|
runtime,
|
|
8818
|
-
|
|
9570
|
+
managedConversationBots,
|
|
9571
|
+
requestKey: String(requestClaim.requestKey || "").trim(),
|
|
9572
|
+
triggerDecision,
|
|
9573
|
+
responderAdjudication: adjudication,
|
|
9574
|
+
humanIntentContext: sharedHumanIntentContext,
|
|
9575
|
+
},
|
|
9576
|
+
};
|
|
9577
|
+
}
|
|
9578
|
+
{
|
|
9579
|
+
const skippedOutcome = await finalizeSkippedPendingRecords(skippedRecords);
|
|
9580
|
+
if (skippedOutcome) {
|
|
9581
|
+
return skippedOutcome;
|
|
8819
9582
|
}
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
messageID: intFromRawAllowZero(selectedRecord?.parsedArchive?.messageID, 0),
|
|
8833
|
-
diagnosticType: "skip",
|
|
8834
|
-
action: "skip_missing_root_work_item",
|
|
8835
|
-
closedReason: rootWorkItemFailure,
|
|
9583
|
+
}
|
|
9584
|
+
}
|
|
9585
|
+
const skippedRecords = [];
|
|
9586
|
+
for (const selectedRecord of pending.pending) {
|
|
9587
|
+
const preparation = await prepareRunnerSelectedRecordForExecution(selectedRecord);
|
|
9588
|
+
if (preparation.kind === "skip") {
|
|
9589
|
+
pushSelectedRecordSkip(skippedRecords, selectedRecord, {
|
|
9590
|
+
action: preparation.skipAction,
|
|
9591
|
+
reason: preparation.skipReason,
|
|
9592
|
+
trigger: preparation.skipTrigger,
|
|
9593
|
+
statePatch: preparation.skipStatePatch,
|
|
9594
|
+
recordPatch: preparation.skipRecordPatch,
|
|
8836
9595
|
});
|
|
8837
9596
|
continue;
|
|
8838
9597
|
}
|
|
9598
|
+
if (preparation.kind === "startup_loop_skipped") {
|
|
9599
|
+
skippedRecords.push(preparation.skippedRecord);
|
|
9600
|
+
continue;
|
|
9601
|
+
}
|
|
9602
|
+
const currentRouteState = safeObject(loadBotRunnerState().routes[routeKey]);
|
|
9603
|
+
const {
|
|
9604
|
+
triggerDecision,
|
|
9605
|
+
sharedHumanIntentContext,
|
|
9606
|
+
adjudication: inlineAdjudication,
|
|
9607
|
+
requestClaim,
|
|
9608
|
+
claimedRequest,
|
|
9609
|
+
} = preparation;
|
|
8839
9610
|
saveRunnerRouteState(routeKey, {
|
|
8840
9611
|
...buildRunnerActiveExecutionPatch(selectedRecord, requestClaim.requestKey, {
|
|
8841
9612
|
id: String(claimedRequest.root_work_item_id || "").trim(),
|
|
@@ -8967,6 +9738,9 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
8967
9738
|
executionContractActionable: processed.result?.execution_contract_actionable === true,
|
|
8968
9739
|
executionContractTargets: ensureArray(processed.result?.execution_contract_targets),
|
|
8969
9740
|
nextExpectedResponders: ensureArray(processed.result?.next_expected_responders),
|
|
9741
|
+
normalizedExecutionContractType: String(processed.result?.normalized_execution_contract_type || "").trim(),
|
|
9742
|
+
normalizedExecutionContractTargets: ensureArray(processed.result?.normalized_execution_contract_targets),
|
|
9743
|
+
normalizedExecutionNextResponders: ensureArray(processed.result?.normalized_execution_next_responders),
|
|
8970
9744
|
currentBotSelector,
|
|
8971
9745
|
conversationIntentMode: String(processed.result?.conversation_intent_mode || "").trim(),
|
|
8972
9746
|
normalizedIntent: resolvedIntentType,
|
|
@@ -9019,31 +9793,11 @@ async function processRunnerRouteOnce(route, runtime, mode, options = {}) {
|
|
|
9019
9793
|
};
|
|
9020
9794
|
}
|
|
9021
9795
|
|
|
9022
|
-
|
|
9023
|
-
await
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
bot,
|
|
9028
|
-
archiveThread,
|
|
9029
|
-
runtime,
|
|
9030
|
-
});
|
|
9031
|
-
const lastSkipped = skippedRecords[skippedRecords.length - 1];
|
|
9032
|
-
const distinctReasons = Array.from(new Set(skippedRecords.map((item) => item.reason).filter(Boolean)));
|
|
9033
|
-
return {
|
|
9034
|
-
route_key: routeKey,
|
|
9035
|
-
route_name: normalizedRoute.name,
|
|
9036
|
-
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
9037
|
-
outcome: "skipped",
|
|
9038
|
-
detail: distinctReasons.join("; ") || "all pending messages were skipped",
|
|
9039
|
-
archive_source: String(archiveThread.source || "").trim() || "-",
|
|
9040
|
-
archive_work_item_id: String(archiveThread.workItemID || "").trim() || "",
|
|
9041
|
-
thread_id: archiveThread.threadID,
|
|
9042
|
-
comment_id: lastSkipped.id,
|
|
9043
|
-
skipped_count: skippedRecords.length,
|
|
9044
|
-
execution_mode: executionPlan.mode,
|
|
9045
|
-
role_profile: executionPlan.roleProfileName,
|
|
9046
|
-
};
|
|
9796
|
+
{
|
|
9797
|
+
const skippedOutcome = await finalizeSkippedPendingRecords(skippedRecords);
|
|
9798
|
+
if (skippedOutcome) {
|
|
9799
|
+
return skippedOutcome;
|
|
9800
|
+
}
|
|
9047
9801
|
}
|
|
9048
9802
|
|
|
9049
9803
|
return {
|
|
@@ -10266,70 +11020,40 @@ function buildRunnerShowPayload(route, flags = {}) {
|
|
|
10266
11020
|
const resolvedServerBotName = firstNonEmptyString([
|
|
10267
11021
|
normalizedRoute.botName,
|
|
10268
11022
|
matchedTelegramEntry?.serverBotName,
|
|
10269
|
-
"-",
|
|
10270
|
-
]);
|
|
10271
|
-
const envConfig = normalizedRoute.provider
|
|
10272
|
-
? loadProviderEnvConfig(normalizedRoute.provider, {
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
: null;
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
workspace_source: diagnostics.workspaceSource || "-",
|
|
10300
|
-
},
|
|
10301
|
-
execution_profile: {
|
|
10302
|
-
route_role: normalizedRoute.role || "-",
|
|
10303
|
-
role_profile_name: diagnostics.roleProfileName || "-",
|
|
10304
|
-
client: String(diagnostics.roleProfile?.client || "").trim() || "-",
|
|
10305
|
-
model: String(diagnostics.roleProfile?.model || "").trim() || "-",
|
|
10306
|
-
permission_mode: String(diagnostics.roleProfile?.permissionMode || "").trim() || "-",
|
|
10307
|
-
reasoning_effort: String(diagnostics.roleProfile?.reasoningEffort || "").trim() || "-",
|
|
10308
|
-
},
|
|
10309
|
-
last_run: {
|
|
10310
|
-
action: String(routeState.last_action || "").trim() || "-",
|
|
10311
|
-
reason: String(routeState.last_reason || "").trim() || "-",
|
|
10312
|
-
intent_type: String(routeState.last_intent_type || "").trim() || "-",
|
|
10313
|
-
workspace_dir: String(routeState.last_workspace_dir || "").trim() || "-",
|
|
10314
|
-
artifact_validation: String(routeState.last_artifact_validation || "").trim() || "-",
|
|
10315
|
-
artifact_paths: ensureArray(routeState.last_artifact_paths).map((item) => String(item || "").trim()).filter(Boolean),
|
|
10316
|
-
artifact_errors: ensureArray(routeState.last_artifact_errors).map((item) => String(item || "").trim()).filter(Boolean),
|
|
10317
|
-
boundary_violations: ensureArray(routeState.last_boundary_violations).map((item) => safeObject(item)),
|
|
10318
|
-
},
|
|
10319
|
-
active_execution: {
|
|
10320
|
-
active: activeExecutionState.active === true,
|
|
10321
|
-
stale: activeExecutionState.stale === true,
|
|
10322
|
-
stuck: activeExecutionState.stuck === true,
|
|
10323
|
-
comment_id: String(activeExecutionState.commentID || "").trim(),
|
|
10324
|
-
source_message_id: intFromRawAllowZero(activeExecutionState.sourceMessageID, 0),
|
|
10325
|
-
started_at: String(activeExecutionState.startedAt || "").trim(),
|
|
10326
|
-
age_seconds: intFromRawAllowZero(activeExecutionState.ageSeconds, 0),
|
|
10327
|
-
warning: String(activeExecutionState.warning || "").trim(),
|
|
10328
|
-
},
|
|
11023
|
+
"-",
|
|
11024
|
+
]);
|
|
11025
|
+
const envConfig = normalizedRoute.provider
|
|
11026
|
+
? loadProviderEnvConfig(normalizedRoute.provider, {
|
|
11027
|
+
botID: normalizedRoute.botID,
|
|
11028
|
+
botName: resolvedServerBotName !== "-" ? resolvedServerBotName : "",
|
|
11029
|
+
route: normalizedRoute,
|
|
11030
|
+
})
|
|
11031
|
+
: null;
|
|
11032
|
+
const lastRunSummary = buildRunnerRouteLastResultSummary(routeState);
|
|
11033
|
+
const lastRunPayload = buildRunnerShowLastRunPayload(lastRunSummary);
|
|
11034
|
+
const activeExecutionPayload = buildRunnerShowActiveExecutionPayload(activeExecutionState);
|
|
11035
|
+
const resolvedContext = buildRunnerShowResolvedContext({
|
|
11036
|
+
normalizedRoute,
|
|
11037
|
+
diagnostics,
|
|
11038
|
+
envConfig,
|
|
11039
|
+
resolvedServerBotName,
|
|
11040
|
+
botNameSource,
|
|
11041
|
+
});
|
|
11042
|
+
return {
|
|
11043
|
+
ok: diagnostics.errors.length === 0,
|
|
11044
|
+
route_name: normalizedRoute.name || runnerRouteKey(normalizedRoute),
|
|
11045
|
+
route_key: runnerRouteKey(normalizedRoute),
|
|
11046
|
+
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
11047
|
+
route_config_file: runnerConfig.filePath,
|
|
11048
|
+
workspace_registry_file: String(runnerConfig.workspaceRegistryFilePath || botRunnerWorkspaceRegistryFilePath()).trim() || "-",
|
|
11049
|
+
route_config: serializeRunnerRoute(normalizedRoute),
|
|
11050
|
+
...resolvedContext,
|
|
11051
|
+
last_run: lastRunPayload,
|
|
11052
|
+
active_execution: activeExecutionPayload,
|
|
10329
11053
|
route_selection_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.",
|
|
10330
11054
|
warnings: [
|
|
10331
11055
|
...ensureArray(diagnostics.warnings),
|
|
10332
|
-
...(
|
|
11056
|
+
...(activeExecutionPayload.stuck && activeExecutionPayload.warning ? [activeExecutionPayload.warning] : []),
|
|
10333
11057
|
],
|
|
10334
11058
|
errors: diagnostics.errors,
|
|
10335
11059
|
};
|
|
@@ -10381,6 +11105,20 @@ async function runRunnerShow(flags) {
|
|
|
10381
11105
|
` action: ${payload.last_run.action}`,
|
|
10382
11106
|
` reason: ${payload.last_run.reason}`,
|
|
10383
11107
|
` intent_type: ${payload.last_run.intent_type}`,
|
|
11108
|
+
` ai_reply_preview: ${payload.last_run.ai_reply_preview}`,
|
|
11109
|
+
` execution_contract_type: ${payload.last_run.execution_contract_type}`,
|
|
11110
|
+
` execution_contract_targets: ${payload.last_run.execution_contract_targets.length ? payload.last_run.execution_contract_targets.join(", ") : "-"}`,
|
|
11111
|
+
` next_expected_responders: ${payload.last_run.next_expected_responders.length ? payload.last_run.next_expected_responders.join(", ") : "-"}`,
|
|
11112
|
+
` response_contract_validation_status: ${payload.last_run.response_contract_validation_status}`,
|
|
11113
|
+
` response_contract_validation_reason: ${payload.last_run.response_contract_validation_reason}`,
|
|
11114
|
+
` response_contract_validation_targets: ${payload.last_run.response_contract_validation_targets.length ? payload.last_run.response_contract_validation_targets.join(", ") : "-"}`,
|
|
11115
|
+
` assignment_validation_status: ${payload.last_run.assignment_validation_status}`,
|
|
11116
|
+
` assignment_validation_reason: ${payload.last_run.assignment_validation_reason}`,
|
|
11117
|
+
` assignment_validation_modes: ${payload.last_run.assignment_validation_modes.length ? payload.last_run.assignment_validation_modes.join(", ") : "-"}`,
|
|
11118
|
+
` delivery_status: ${payload.last_run.delivery_status}`,
|
|
11119
|
+
` archive_status: ${payload.last_run.archive_status}`,
|
|
11120
|
+
` transport_error: ${payload.last_run.transport_error}`,
|
|
11121
|
+
` archive_error: ${payload.last_run.archive_error}`,
|
|
10384
11122
|
` workspace_dir: ${payload.last_run.workspace_dir}`,
|
|
10385
11123
|
` artifact_validation: ${payload.last_run.artifact_validation}`,
|
|
10386
11124
|
` artifact_paths: ${payload.last_run.artifact_paths.length ? payload.last_run.artifact_paths.join(", ") : "-"}`,
|
|
@@ -10926,6 +11664,161 @@ function summarizeRunnerTUIPhases(routes = []) {
|
|
|
10926
11664
|
return { counts, warningCount };
|
|
10927
11665
|
}
|
|
10928
11666
|
|
|
11667
|
+
function buildRunnerTUISummaryLine(phaseSummary) {
|
|
11668
|
+
const summary = safeObject(phaseSummary);
|
|
11669
|
+
const counts = safeObject(summary.counts);
|
|
11670
|
+
return `Summary: waiting=${intFromRawAllowZero(counts.waiting, 0)} polling=${intFromRawAllowZero(counts.polling, 0)} running=${intFromRawAllowZero(counts.running, 0)} busy=${intFromRawAllowZero(counts.busy, 0)} replied=${intFromRawAllowZero(counts.replied, 0)} error=${intFromRawAllowZero(counts.error, 0)} warnings=${intFromRawAllowZero(summary.warningCount, 0)}`;
|
|
11671
|
+
}
|
|
11672
|
+
|
|
11673
|
+
function buildRunnerTUIRouteTableHeader() {
|
|
11674
|
+
return [
|
|
11675
|
+
bootstrapPadRight("Route", 24),
|
|
11676
|
+
bootstrapPadRight("Phase", 18),
|
|
11677
|
+
bootstrapPadRight("Age", 7),
|
|
11678
|
+
bootstrapPadRight("AI", 20),
|
|
11679
|
+
bootstrapPadRight("Intent", 18),
|
|
11680
|
+
bootstrapPadRight("Msg", 8),
|
|
11681
|
+
bootstrapPadRight("Next", 8),
|
|
11682
|
+
bootstrapPadRight("Warn", 6),
|
|
11683
|
+
].join(" ");
|
|
11684
|
+
}
|
|
11685
|
+
|
|
11686
|
+
function buildRunnerTUIRouteRow(route, now, useColor) {
|
|
11687
|
+
const currentRoute = safeObject(route);
|
|
11688
|
+
const phase = normalizeRunnerTUIPhase(currentRoute.phase);
|
|
11689
|
+
const aiLabel = truncateRunnerTUIText(
|
|
11690
|
+
currentRoute.client
|
|
11691
|
+
? `${String(currentRoute.client || "").trim()}/${String(currentRoute.model || "").trim() || "-"}`
|
|
11692
|
+
: "-",
|
|
11693
|
+
20,
|
|
11694
|
+
);
|
|
11695
|
+
const ageLabel = formatRunnerTUIAge(now, currentRoute.phase_started_at || currentRoute.updated_at || now);
|
|
11696
|
+
const nextSeconds = Number.isFinite(Number(currentRoute.next_run_at)) && Number(currentRoute.next_run_at) > now
|
|
11697
|
+
? `${Math.max(0, Math.ceil((Number(currentRoute.next_run_at) - now) / 1000))}s`
|
|
11698
|
+
: phase === "ai_running" || phase === "delivering" || phase === "context_suggesting"
|
|
11699
|
+
? "run"
|
|
11700
|
+
: "-";
|
|
11701
|
+
const warningLabel = String(currentRoute.warning || currentRoute.last_error || "").trim() ? "yes" : "-";
|
|
11702
|
+
return [
|
|
11703
|
+
bootstrapPadRight(truncateRunnerTUIText(currentRoute.route_name, 24), 24),
|
|
11704
|
+
colorizeRunnerTUIPhase(phase, 18, useColor),
|
|
11705
|
+
bootstrapPadRight(ageLabel, 7),
|
|
11706
|
+
bootstrapPadRight(aiLabel, 20),
|
|
11707
|
+
bootstrapPadRight(truncateRunnerTUIText(currentRoute.intent_type || "-", 18), 18),
|
|
11708
|
+
bootstrapPadRight(String(currentRoute.source_message_id || "-"), 8),
|
|
11709
|
+
bootstrapPadRight(nextSeconds, 8),
|
|
11710
|
+
bootstrapPadRight(warningLabel, 6),
|
|
11711
|
+
].join(" ");
|
|
11712
|
+
}
|
|
11713
|
+
|
|
11714
|
+
function buildRunnerTUIRouteDetailLine(route) {
|
|
11715
|
+
const currentRoute = safeObject(route);
|
|
11716
|
+
const detailParts = [
|
|
11717
|
+
String(currentRoute.detail || "").trim(),
|
|
11718
|
+
String(currentRoute.planner_client || "").trim() || String(currentRoute.planner_model || "").trim()
|
|
11719
|
+
? `planner=${String(currentRoute.planner_client || "").trim() || "-"}/${String(currentRoute.planner_model || "").trim() || "-"}`
|
|
11720
|
+
: "",
|
|
11721
|
+
String(currentRoute.context_suggestion_status || "").trim()
|
|
11722
|
+
? `context=${String(currentRoute.context_suggestion_status || "").trim()}`
|
|
11723
|
+
: "",
|
|
11724
|
+
String(currentRoute.warning || "").trim()
|
|
11725
|
+
? `warning=${String(currentRoute.warning || "").trim()}`
|
|
11726
|
+
: "",
|
|
11727
|
+
String(currentRoute.last_error || "").trim()
|
|
11728
|
+
? `error=${String(currentRoute.last_error || "").trim()}`
|
|
11729
|
+
: "",
|
|
11730
|
+
].filter(Boolean);
|
|
11731
|
+
return ` ${truncateRunnerTUIText(detailParts.join(" | ") || "-", 116)}`;
|
|
11732
|
+
}
|
|
11733
|
+
|
|
11734
|
+
function buildRunnerTUIRecentEventLine(event) {
|
|
11735
|
+
const currentEvent = safeObject(event);
|
|
11736
|
+
return ` [${String(currentEvent.time || "").trim() || "--:--:--"}] ${truncateRunnerTUIText(currentEvent.route_name, 24)} ${bootstrapPadRight(String(currentEvent.outcome || "-").trim(), 12)} ${truncateRunnerTUIText(currentEvent.detail || "-", 74)}`;
|
|
11737
|
+
}
|
|
11738
|
+
|
|
11739
|
+
function buildRunnerTUIRecentEvent(event, fallbackRouteName = "runner", fallbackOutcome = "-", fallbackDetail = "") {
|
|
11740
|
+
const currentEvent = safeObject(event);
|
|
11741
|
+
return {
|
|
11742
|
+
time: new Date().toLocaleTimeString("en-US", { hour12: false }),
|
|
11743
|
+
route_name: String(currentEvent.route_name || fallbackRouteName || "runner").trim(),
|
|
11744
|
+
outcome: String(currentEvent.outcome || fallbackOutcome || "-").trim(),
|
|
11745
|
+
detail: String(currentEvent.detail || fallbackDetail || "").trim(),
|
|
11746
|
+
};
|
|
11747
|
+
}
|
|
11748
|
+
|
|
11749
|
+
function buildRunnerTUIInitialRouteState(route) {
|
|
11750
|
+
const normalizedRoute = normalizeRunnerRoute(route);
|
|
11751
|
+
const routeKey = runnerRouteKey(normalizedRoute);
|
|
11752
|
+
const now = Date.now();
|
|
11753
|
+
return {
|
|
11754
|
+
route_key: routeKey,
|
|
11755
|
+
route_name: normalizedRoute.name,
|
|
11756
|
+
phase: "waiting",
|
|
11757
|
+
detail: "awaiting next poll",
|
|
11758
|
+
client: "",
|
|
11759
|
+
model: "",
|
|
11760
|
+
planner_client: "",
|
|
11761
|
+
planner_model: "",
|
|
11762
|
+
intent_type: "",
|
|
11763
|
+
source_message_id: "",
|
|
11764
|
+
next_run_at: 0,
|
|
11765
|
+
context_suggestion_status: "",
|
|
11766
|
+
warning: "",
|
|
11767
|
+
last_error: "",
|
|
11768
|
+
phase_started_at: now,
|
|
11769
|
+
updated_at: now,
|
|
11770
|
+
};
|
|
11771
|
+
}
|
|
11772
|
+
|
|
11773
|
+
function appendRunnerTUIRecentEvent(recentEvents, event, fallbackRouteName = "runner", fallbackOutcome = "-", fallbackDetail = "") {
|
|
11774
|
+
const nextEvents = ensureArray(recentEvents);
|
|
11775
|
+
nextEvents.unshift(buildRunnerTUIRecentEvent(event, fallbackRouteName, fallbackOutcome, fallbackDetail));
|
|
11776
|
+
if (nextEvents.length > 8) {
|
|
11777
|
+
nextEvents.length = 8;
|
|
11778
|
+
}
|
|
11779
|
+
}
|
|
11780
|
+
|
|
11781
|
+
function buildRunnerTUIRouteStatePatchFromResult(result, routeState = null, currentRouteState = null) {
|
|
11782
|
+
const current = safeObject(currentRouteState);
|
|
11783
|
+
const currentRouteSnapshot = safeObject(routeState);
|
|
11784
|
+
return {
|
|
11785
|
+
phase: mapRunnerTUIPhaseFromOutcome(result?.outcome),
|
|
11786
|
+
detail: String(result?.detail || "").trim(),
|
|
11787
|
+
intent_type: String(currentRouteSnapshot.last_intent_type || current.intent_type || "").trim(),
|
|
11788
|
+
source_message_id: intFromRawAllowZero(currentRouteSnapshot.last_source_message_id, intFromRawAllowZero(current.source_message_id, 0)) || current.source_message_id,
|
|
11789
|
+
context_suggestion_status: String(result?.context_suggestion_status || currentRouteSnapshot.last_context_suggestion_status || current.context_suggestion_status || "").trim(),
|
|
11790
|
+
warning: String(currentRouteSnapshot.active_execution_warning || result?.warning || "").trim(),
|
|
11791
|
+
last_error: String(currentRouteSnapshot.last_error || "").trim(),
|
|
11792
|
+
};
|
|
11793
|
+
}
|
|
11794
|
+
|
|
11795
|
+
function buildRunnerTUIRouteStatePatchFromExecutionStage({ executionPlan, selectedRecord, phase, detail, warning = "", contextSuggestionStatus = "", intentType = "" }) {
|
|
11796
|
+
const currentExecutionPlan = safeObject(executionPlan);
|
|
11797
|
+
const normalizedPhase = normalizeRunnerTUIPhase(phase);
|
|
11798
|
+
const clearLastError = ["ai_running", "delivering", "context_suggesting", "replied"].includes(normalizedPhase);
|
|
11799
|
+
const plannerFallbackClient = String(currentExecutionPlan.roleProfile?.client || "").trim();
|
|
11800
|
+
const plannerFallbackModel = String(currentExecutionPlan.roleProfile?.model || "").trim();
|
|
11801
|
+
const plannerClient = resolveRolePlannerClient({ env: process.env }) || plannerFallbackClient;
|
|
11802
|
+
const plannerModel = resolveRolePlannerModelDisplayName({
|
|
11803
|
+
env: process.env,
|
|
11804
|
+
fallbackClient: plannerFallbackClient,
|
|
11805
|
+
fallbackModel: plannerFallbackModel,
|
|
11806
|
+
});
|
|
11807
|
+
return {
|
|
11808
|
+
phase: normalizedPhase,
|
|
11809
|
+
detail: String(detail || "").trim(),
|
|
11810
|
+
client: plannerFallbackClient,
|
|
11811
|
+
model: plannerFallbackModel,
|
|
11812
|
+
planner_client: String(plannerClient || "").trim(),
|
|
11813
|
+
planner_model: String(plannerModel || "").trim(),
|
|
11814
|
+
source_message_id: intFromRawAllowZero(safeObject(selectedRecord).parsedArchive?.messageID, 0),
|
|
11815
|
+
intent_type: String(intentType || currentExecutionPlan.intentType || "").trim(),
|
|
11816
|
+
warning: String(warning || "").trim(),
|
|
11817
|
+
context_suggestion_status: String(contextSuggestionStatus || "").trim(),
|
|
11818
|
+
last_error: clearLastError ? "" : undefined,
|
|
11819
|
+
};
|
|
11820
|
+
}
|
|
11821
|
+
|
|
10929
11822
|
function buildRunnerTUIFrame({ routes = [], recentEvents = [], concurrency = 1, now = Date.now(), stopping = false, useColor = false, sourceLabel = "runner start", logFilePath = "" }) {
|
|
10930
11823
|
const lines = [];
|
|
10931
11824
|
const activeCount = ensureArray(routes).filter((item) => ["polling", "ai_running", "delivering", "context_suggesting", "busy"].includes(normalizeRunnerTUIPhase(item.phase))).length;
|
|
@@ -10935,65 +11828,14 @@ function buildRunnerTUIFrame({ routes = [], recentEvents = [], concurrency = 1,
|
|
|
10935
11828
|
if (String(logFilePath || "").trim()) {
|
|
10936
11829
|
lines.push(`Log: ${truncateRunnerTUIText(String(logFilePath || "").trim(), 112)}`);
|
|
10937
11830
|
}
|
|
10938
|
-
lines.push(
|
|
11831
|
+
lines.push(buildRunnerTUISummaryLine(phaseSummary));
|
|
10939
11832
|
lines.push("Ctrl+C to stop. Foreground runner state is shown below.");
|
|
10940
11833
|
lines.push("");
|
|
10941
|
-
lines.push(
|
|
10942
|
-
[
|
|
10943
|
-
bootstrapPadRight("Route", 24),
|
|
10944
|
-
bootstrapPadRight("Phase", 18),
|
|
10945
|
-
bootstrapPadRight("Age", 7),
|
|
10946
|
-
bootstrapPadRight("AI", 20),
|
|
10947
|
-
bootstrapPadRight("Intent", 18),
|
|
10948
|
-
bootstrapPadRight("Msg", 8),
|
|
10949
|
-
bootstrapPadRight("Next", 8),
|
|
10950
|
-
bootstrapPadRight("Warn", 6),
|
|
10951
|
-
].join(" "),
|
|
10952
|
-
);
|
|
11834
|
+
lines.push(buildRunnerTUIRouteTableHeader());
|
|
10953
11835
|
lines.push("-".repeat(120));
|
|
10954
11836
|
for (const route of ensureArray(routes)) {
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
route.client
|
|
10958
|
-
? `${String(route.client || "").trim()}/${String(route.model || "").trim() || "-"}`
|
|
10959
|
-
: "-",
|
|
10960
|
-
20,
|
|
10961
|
-
);
|
|
10962
|
-
const ageLabel = formatRunnerTUIAge(now, route.phase_started_at || route.updated_at || now);
|
|
10963
|
-
const nextSeconds = Number.isFinite(Number(route.next_run_at)) && Number(route.next_run_at) > now
|
|
10964
|
-
? `${Math.max(0, Math.ceil((Number(route.next_run_at) - now) / 1000))}s`
|
|
10965
|
-
: phase === "ai_running" || phase === "delivering" || phase === "context_suggesting"
|
|
10966
|
-
? "run"
|
|
10967
|
-
: "-";
|
|
10968
|
-
const warningLabel = String(route.warning || route.last_error || "").trim() ? "yes" : "-";
|
|
10969
|
-
lines.push(
|
|
10970
|
-
[
|
|
10971
|
-
bootstrapPadRight(truncateRunnerTUIText(route.route_name, 24), 24),
|
|
10972
|
-
colorizeRunnerTUIPhase(phase, 18, useColor),
|
|
10973
|
-
bootstrapPadRight(ageLabel, 7),
|
|
10974
|
-
bootstrapPadRight(aiLabel, 20),
|
|
10975
|
-
bootstrapPadRight(truncateRunnerTUIText(route.intent_type || "-", 18), 18),
|
|
10976
|
-
bootstrapPadRight(String(route.source_message_id || "-"), 8),
|
|
10977
|
-
bootstrapPadRight(nextSeconds, 8),
|
|
10978
|
-
bootstrapPadRight(warningLabel, 6),
|
|
10979
|
-
].join(" "),
|
|
10980
|
-
);
|
|
10981
|
-
const detailParts = [
|
|
10982
|
-
String(route.detail || "").trim(),
|
|
10983
|
-
String(route.planner_client || "").trim() || String(route.planner_model || "").trim()
|
|
10984
|
-
? `planner=${String(route.planner_client || "").trim() || "-"}/${String(route.planner_model || "").trim() || "-"}`
|
|
10985
|
-
: "",
|
|
10986
|
-
String(route.context_suggestion_status || "").trim()
|
|
10987
|
-
? `context=${String(route.context_suggestion_status || "").trim()}`
|
|
10988
|
-
: "",
|
|
10989
|
-
String(route.warning || "").trim()
|
|
10990
|
-
? `warning=${String(route.warning || "").trim()}`
|
|
10991
|
-
: "",
|
|
10992
|
-
String(route.last_error || "").trim()
|
|
10993
|
-
? `error=${String(route.last_error || "").trim()}`
|
|
10994
|
-
: "",
|
|
10995
|
-
].filter(Boolean);
|
|
10996
|
-
lines.push(` ${truncateRunnerTUIText(detailParts.join(" | ") || "-", 116)}`);
|
|
11837
|
+
lines.push(buildRunnerTUIRouteRow(route, now, useColor));
|
|
11838
|
+
lines.push(buildRunnerTUIRouteDetailLine(route));
|
|
10997
11839
|
}
|
|
10998
11840
|
lines.push("");
|
|
10999
11841
|
lines.push(useColor ? bootstrapColorText("Recent Events", "35") : "Recent Events");
|
|
@@ -11001,9 +11843,7 @@ function buildRunnerTUIFrame({ routes = [], recentEvents = [], concurrency = 1,
|
|
|
11001
11843
|
lines.push(" (none yet)");
|
|
11002
11844
|
} else {
|
|
11003
11845
|
for (const event of ensureArray(recentEvents).slice(0, 8)) {
|
|
11004
|
-
lines.push(
|
|
11005
|
-
` [${String(event.time || "").trim() || "--:--:--"}] ${truncateRunnerTUIText(event.route_name, 24)} ${bootstrapPadRight(String(event.outcome || "-").trim(), 12)} ${truncateRunnerTUIText(event.detail || "-", 74)}`,
|
|
11006
|
-
);
|
|
11846
|
+
lines.push(buildRunnerTUIRecentEventLine(event));
|
|
11007
11847
|
}
|
|
11008
11848
|
}
|
|
11009
11849
|
return lines.join("\n");
|
|
@@ -11015,37 +11855,15 @@ function createRunnerStartTUI({ routes, flags, jsonMode, concurrency, sourceLabe
|
|
|
11015
11855
|
return null;
|
|
11016
11856
|
}
|
|
11017
11857
|
const useColor = bootstrapSupportsANSIColors();
|
|
11018
|
-
const routeStates = new Map(
|
|
11858
|
+
const routeStates = new Map(
|
|
11859
|
+
ensureArray(routes).map((route) => {
|
|
11860
|
+
const routeState = buildRunnerTUIInitialRouteState(route);
|
|
11861
|
+
return [routeState.route_key, routeState];
|
|
11862
|
+
}),
|
|
11863
|
+
);
|
|
11019
11864
|
const recentEvents = [];
|
|
11020
|
-
for (const route of ensureArray(routes)) {
|
|
11021
|
-
const normalizedRoute = normalizeRunnerRoute(route);
|
|
11022
|
-
const routeKey = runnerRouteKey(normalizedRoute);
|
|
11023
|
-
routeStates.set(routeKey, {
|
|
11024
|
-
route_key: routeKey,
|
|
11025
|
-
route_name: normalizedRoute.name,
|
|
11026
|
-
phase: "waiting",
|
|
11027
|
-
detail: "awaiting next poll",
|
|
11028
|
-
client: "",
|
|
11029
|
-
model: "",
|
|
11030
|
-
planner_client: "",
|
|
11031
|
-
planner_model: "",
|
|
11032
|
-
intent_type: "",
|
|
11033
|
-
source_message_id: "",
|
|
11034
|
-
next_run_at: 0,
|
|
11035
|
-
context_suggestion_status: "",
|
|
11036
|
-
warning: "",
|
|
11037
|
-
last_error: "",
|
|
11038
|
-
phase_started_at: Date.now(),
|
|
11039
|
-
updated_at: Date.now(),
|
|
11040
|
-
});
|
|
11041
|
-
}
|
|
11042
11865
|
if (bootstrapEvent && typeof bootstrapEvent === "object") {
|
|
11043
|
-
recentEvents.unshift(
|
|
11044
|
-
time: new Date().toLocaleTimeString("en-US", { hour12: false }),
|
|
11045
|
-
route_name: String(bootstrapEvent.route_name || sourceLabel || "runner").trim(),
|
|
11046
|
-
outcome: String(bootstrapEvent.outcome || "prepared").trim(),
|
|
11047
|
-
detail: String(bootstrapEvent.detail || "").trim(),
|
|
11048
|
-
});
|
|
11866
|
+
recentEvents.unshift(buildRunnerTUIRecentEvent(bootstrapEvent, sourceLabel, "prepared"));
|
|
11049
11867
|
}
|
|
11050
11868
|
let intervalHandle = null;
|
|
11051
11869
|
let disposed = false;
|
|
@@ -11084,57 +11902,27 @@ function createRunnerStartTUI({ routes, flags, jsonMode, concurrency, sourceLabe
|
|
|
11084
11902
|
const routeKey = String(result?.route_key || "").trim();
|
|
11085
11903
|
if (routeKey && routeStates.has(routeKey)) {
|
|
11086
11904
|
const current = safeObject(routeStates.get(routeKey));
|
|
11087
|
-
setRouteState(routeKey,
|
|
11088
|
-
phase: mapRunnerTUIPhaseFromOutcome(result?.outcome),
|
|
11089
|
-
detail: String(result?.detail || "").trim(),
|
|
11090
|
-
intent_type: String(routeState?.last_intent_type || current.intent_type || "").trim(),
|
|
11091
|
-
source_message_id: intFromRawAllowZero(routeState?.last_source_message_id, intFromRawAllowZero(current.source_message_id, 0)) || current.source_message_id,
|
|
11092
|
-
context_suggestion_status: String(result?.context_suggestion_status || routeState?.last_context_suggestion_status || current.context_suggestion_status || "").trim(),
|
|
11093
|
-
warning: String(safeObject(routeState).active_execution_warning || result?.warning || "").trim(),
|
|
11094
|
-
last_error: String(routeState?.last_error || "").trim(),
|
|
11095
|
-
});
|
|
11905
|
+
setRouteState(routeKey, buildRunnerTUIRouteStatePatchFromResult(result, routeState, current));
|
|
11096
11906
|
}
|
|
11097
11907
|
if (!result || ["idle", "busy"].includes(String(result.outcome || "").trim().toLowerCase())) {
|
|
11098
11908
|
return;
|
|
11099
11909
|
}
|
|
11100
|
-
recentEvents.
|
|
11101
|
-
time: new Date().toLocaleTimeString("en-US", { hour12: false }),
|
|
11102
|
-
route_name: String(result.route_name || result.route_key || "").trim(),
|
|
11103
|
-
outcome: String(result.outcome || "").trim(),
|
|
11104
|
-
detail: String(result.detail || "").trim(),
|
|
11105
|
-
});
|
|
11106
|
-
if (recentEvents.length > 8) {
|
|
11107
|
-
recentEvents.length = 8;
|
|
11108
|
-
}
|
|
11910
|
+
appendRunnerTUIRecentEvent(recentEvents, result, String(result.route_name || result.route_key || "").trim());
|
|
11109
11911
|
render(false);
|
|
11110
11912
|
};
|
|
11111
11913
|
|
|
11112
11914
|
const reportExecutionStage = ({ routeKey, executionPlan, selectedRecord, phase, detail, warning = "", contextSuggestionStatus = "", intentType = "" }) => {
|
|
11113
11915
|
const normalizedRouteKey = String(routeKey || "").trim();
|
|
11114
11916
|
if (!normalizedRouteKey || !routeStates.has(normalizedRouteKey)) return;
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
});
|
|
11125
|
-
setRouteState(normalizedRouteKey, {
|
|
11126
|
-
phase: normalizedPhase,
|
|
11127
|
-
detail: String(detail || "").trim(),
|
|
11128
|
-
client: plannerFallbackClient,
|
|
11129
|
-
model: plannerFallbackModel,
|
|
11130
|
-
planner_client: String(plannerClient || "").trim(),
|
|
11131
|
-
planner_model: String(plannerModel || "").trim(),
|
|
11132
|
-
source_message_id: intFromRawAllowZero(safeObject(selectedRecord).parsedArchive?.messageID, 0),
|
|
11133
|
-
intent_type: String(intentType || safeObject(executionPlan).intentType || "").trim(),
|
|
11134
|
-
warning: String(warning || "").trim(),
|
|
11135
|
-
context_suggestion_status: String(contextSuggestionStatus || "").trim(),
|
|
11136
|
-
last_error: clearLastError ? "" : undefined,
|
|
11137
|
-
});
|
|
11917
|
+
setRouteState(normalizedRouteKey, buildRunnerTUIRouteStatePatchFromExecutionStage({
|
|
11918
|
+
executionPlan,
|
|
11919
|
+
selectedRecord,
|
|
11920
|
+
phase,
|
|
11921
|
+
detail,
|
|
11922
|
+
warning,
|
|
11923
|
+
contextSuggestionStatus,
|
|
11924
|
+
intentType,
|
|
11925
|
+
}));
|
|
11138
11926
|
};
|
|
11139
11927
|
|
|
11140
11928
|
const updateNextRunAt = (routeKey, nextRunAt) => {
|
|
@@ -11161,6 +11949,550 @@ function createRunnerStartTUI({ routes, flags, jsonMode, concurrency, sourceLabe
|
|
|
11161
11949
|
};
|
|
11162
11950
|
}
|
|
11163
11951
|
|
|
11952
|
+
function beginRunnerStartRoutePoll({ routeKey, normalizedRoute, tui, runnerLogger }) {
|
|
11953
|
+
tui?.setRouteState(routeKey, {
|
|
11954
|
+
phase: "polling",
|
|
11955
|
+
detail: "checking inbound messages and archive thread",
|
|
11956
|
+
});
|
|
11957
|
+
runnerLogger?.append("route_poll", {
|
|
11958
|
+
route_key: routeKey,
|
|
11959
|
+
route_name: normalizedRoute.name,
|
|
11960
|
+
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
11961
|
+
});
|
|
11962
|
+
}
|
|
11963
|
+
|
|
11964
|
+
function buildRunnerStartRouteResultLogPayload(result, routeKey, routeName) {
|
|
11965
|
+
return {
|
|
11966
|
+
route_key: String(result?.route_key || routeKey).trim(),
|
|
11967
|
+
route_name: String(result?.route_name || routeName || "").trim(),
|
|
11968
|
+
outcome: String(result?.outcome || "").trim(),
|
|
11969
|
+
detail: String(result?.detail || "").trim(),
|
|
11970
|
+
comment_id: String(result?.comment_id || "").trim(),
|
|
11971
|
+
};
|
|
11972
|
+
}
|
|
11973
|
+
|
|
11974
|
+
function publishRunnerStartRouteResult({ result, routeState = null, routeKey, routeName, runnerLogger, tui, jsonMode }) {
|
|
11975
|
+
runnerLogger?.append("route_result", buildRunnerStartRouteResultLogPayload(result, routeKey, routeName));
|
|
11976
|
+
if (tui) {
|
|
11977
|
+
tui.recordResult(result, routeState);
|
|
11978
|
+
return;
|
|
11979
|
+
}
|
|
11980
|
+
printRunnerResult("start", result, jsonMode);
|
|
11981
|
+
}
|
|
11982
|
+
|
|
11983
|
+
function buildRunnerStartCycleErrorResult({ normalizedRoute, routeKey, errorText, fatalArchiveBootstrapError = false }) {
|
|
11984
|
+
return {
|
|
11985
|
+
route_key: routeKey,
|
|
11986
|
+
route_name: normalizedRoute.name,
|
|
11987
|
+
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
11988
|
+
outcome: fatalArchiveBootstrapError ? "blocked" : "error",
|
|
11989
|
+
detail: errorText,
|
|
11990
|
+
};
|
|
11991
|
+
}
|
|
11992
|
+
|
|
11993
|
+
function finalizeRunnerStartRouteCycle({ routeKey, normalizedRoute, cycleOutcome, schedules, tui }) {
|
|
11994
|
+
let routeState = safeObject(loadBotRunnerState().routes[routeKey]);
|
|
11995
|
+
let activeExecutionState = resolveRunnerActiveExecutionState(routeState);
|
|
11996
|
+
const successfulCycleOutcome = [
|
|
11997
|
+
"accepted",
|
|
11998
|
+
"busy",
|
|
11999
|
+
"idle",
|
|
12000
|
+
"primed",
|
|
12001
|
+
"skipped",
|
|
12002
|
+
"replied",
|
|
12003
|
+
"dry_run",
|
|
12004
|
+
"delivery_failed_after_generation",
|
|
12005
|
+
].includes(cycleOutcome);
|
|
12006
|
+
const shouldClearLastError = successfulCycleOutcome
|
|
12007
|
+
&& String(routeState.last_error || "").trim();
|
|
12008
|
+
const shouldClearStaleActiveExecution = !activeExecutionState.active
|
|
12009
|
+
&& successfulCycleOutcome
|
|
12010
|
+
&& String(routeState.active_comment_id || "").trim();
|
|
12011
|
+
if (shouldClearLastError || shouldClearStaleActiveExecution) {
|
|
12012
|
+
saveRunnerRouteState(routeKey, {
|
|
12013
|
+
...(shouldClearStaleActiveExecution ? emptyRunnerActiveExecutionPatch() : {}),
|
|
12014
|
+
...(shouldClearLastError ? { last_error: "" } : {}),
|
|
12015
|
+
});
|
|
12016
|
+
routeState = safeObject(loadBotRunnerState().routes[routeKey]);
|
|
12017
|
+
activeExecutionState = resolveRunnerActiveExecutionState(routeState);
|
|
12018
|
+
}
|
|
12019
|
+
tui?.setRouteState(routeKey, {
|
|
12020
|
+
intent_type: String(routeState.last_intent_type || "").trim(),
|
|
12021
|
+
source_message_id: intFromRawAllowZero(routeState.last_source_message_id, 0),
|
|
12022
|
+
warning: String(activeExecutionState.warning || "").trim(),
|
|
12023
|
+
last_error: String(routeState.last_error || "").trim(),
|
|
12024
|
+
});
|
|
12025
|
+
const lastErrorText = String(routeState.last_error || "").trim();
|
|
12026
|
+
const isFatalArchiveBootstrapError = lastErrorText.includes("Archive thread is missing")
|
|
12027
|
+
&& lastErrorText.includes("write access is denied");
|
|
12028
|
+
const nextRunAt = Date.now() + (isFatalArchiveBootstrapError
|
|
12029
|
+
? 60000
|
|
12030
|
+
: Math.max(1000, normalizedRoute.pollIntervalMs));
|
|
12031
|
+
schedules.set(routeKey, nextRunAt);
|
|
12032
|
+
tui?.updateNextRunAt(routeKey, nextRunAt);
|
|
12033
|
+
}
|
|
12034
|
+
|
|
12035
|
+
async function syncRunnerDeferredExecutionRunningState(deferredExecution) {
|
|
12036
|
+
if (!String(deferredExecution?.requestKey || "").trim()) {
|
|
12037
|
+
return;
|
|
12038
|
+
}
|
|
12039
|
+
markRunnerRequestLifecycle({
|
|
12040
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12041
|
+
requestKey: deferredExecution.requestKey,
|
|
12042
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12043
|
+
routeKey: deferredExecution.routeKey,
|
|
12044
|
+
outcome: "running",
|
|
12045
|
+
});
|
|
12046
|
+
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
12047
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12048
|
+
runtime: deferredExecution.runtime,
|
|
12049
|
+
requestKey: deferredExecution.requestKey,
|
|
12050
|
+
});
|
|
12051
|
+
const syncedRequest = safeObject(rootWorkItemSync.request);
|
|
12052
|
+
if (String(syncedRequest.root_work_item_id || "").trim()) {
|
|
12053
|
+
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
12054
|
+
active_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
12055
|
+
active_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
12056
|
+
active_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
12057
|
+
last_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
12058
|
+
last_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
12059
|
+
last_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
12060
|
+
});
|
|
12061
|
+
}
|
|
12062
|
+
await syncRunnerRequestLedgerForProjectToServer({
|
|
12063
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12064
|
+
runtime: deferredExecution.runtime,
|
|
12065
|
+
});
|
|
12066
|
+
}
|
|
12067
|
+
|
|
12068
|
+
async function finalizeRunnerDeferredExecutionSkipped(deferredExecution, processed) {
|
|
12069
|
+
if (String(deferredExecution?.requestKey || "").trim()) {
|
|
12070
|
+
markRunnerRequestLifecycle({
|
|
12071
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12072
|
+
requestKey: deferredExecution.requestKey,
|
|
12073
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12074
|
+
routeKey: deferredExecution.routeKey,
|
|
12075
|
+
outcome: "skipped",
|
|
12076
|
+
closedReason: String(processed?.skippedRecord?.reason || "skipped").trim() || "skipped",
|
|
12077
|
+
});
|
|
12078
|
+
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
12079
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12080
|
+
runtime: deferredExecution.runtime,
|
|
12081
|
+
requestKey: deferredExecution.requestKey,
|
|
12082
|
+
});
|
|
12083
|
+
const syncedRequest = safeObject(rootWorkItemSync.request);
|
|
12084
|
+
if (String(syncedRequest.root_work_item_id || "").trim()) {
|
|
12085
|
+
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
12086
|
+
last_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
12087
|
+
last_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
12088
|
+
last_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
12089
|
+
});
|
|
12090
|
+
}
|
|
12091
|
+
await syncRunnerRequestLedgerForProjectToServer({
|
|
12092
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12093
|
+
runtime: deferredExecution.runtime,
|
|
12094
|
+
});
|
|
12095
|
+
}
|
|
12096
|
+
return {
|
|
12097
|
+
route_key: deferredExecution.routeKey,
|
|
12098
|
+
route_name: deferredExecution.normalizedRoute.name,
|
|
12099
|
+
logical_signature: runnerRouteLogicalSignature(deferredExecution.normalizedRoute),
|
|
12100
|
+
outcome: "skipped",
|
|
12101
|
+
detail: String(processed?.skippedRecord?.reason || "trigger policy skipped message").trim(),
|
|
12102
|
+
archive_source: String(deferredExecution.archiveThread.source || "").trim() || "-",
|
|
12103
|
+
archive_work_item_id: String(deferredExecution.archiveThread.workItemID || "").trim() || "",
|
|
12104
|
+
thread_id: deferredExecution.archiveThread.threadID,
|
|
12105
|
+
comment_id: deferredExecution.selectedRecord.id,
|
|
12106
|
+
execution_mode: deferredExecution.executionPlan.mode,
|
|
12107
|
+
role_profile: deferredExecution.executionPlan.roleProfileName,
|
|
12108
|
+
};
|
|
12109
|
+
}
|
|
12110
|
+
|
|
12111
|
+
async function finalizeRunnerDeferredExecutionProcessed(deferredExecution, processed) {
|
|
12112
|
+
if (String(deferredExecution?.requestKey || "").trim()) {
|
|
12113
|
+
const resolvedIntentType = String(
|
|
12114
|
+
safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]).last_intent_type || "",
|
|
12115
|
+
).trim();
|
|
12116
|
+
markRunnerRequestLifecycle({
|
|
12117
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12118
|
+
requestKey: deferredExecution.requestKey,
|
|
12119
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12120
|
+
routeKey: deferredExecution.routeKey,
|
|
12121
|
+
outcome: processed.kind === "delivery_failed"
|
|
12122
|
+
? "delivery_failed_after_generation"
|
|
12123
|
+
: String(processed.result?.outcome || "replied").trim().toLowerCase(),
|
|
12124
|
+
conversationIDRaw: String(processed.result?.conversation_id || "").trim(),
|
|
12125
|
+
conversationParticipants: ensureArray(processed.result?.conversation_participants),
|
|
12126
|
+
conversationInitialResponders: ensureArray(processed.result?.conversation_initial_responders),
|
|
12127
|
+
allowedResponders: ensureArray(processed.result?.conversation_allowed_responders),
|
|
12128
|
+
conversationLeadBot: String(processed.result?.conversation_lead_bot || "").trim(),
|
|
12129
|
+
conversationSummaryBot: String(processed.result?.conversation_summary_bot || "").trim(),
|
|
12130
|
+
conversationAllowBotToBot: processed.result?.conversation_allow_bot_to_bot === true,
|
|
12131
|
+
conversationReplyExpectation: String(processed.result?.conversation_reply_expectation || "").trim(),
|
|
12132
|
+
executionContractType: String(processed.result?.execution_contract_type || "").trim(),
|
|
12133
|
+
executionContractActionable: processed.result?.execution_contract_actionable === true,
|
|
12134
|
+
executionContractTargets: ensureArray(processed.result?.execution_contract_targets),
|
|
12135
|
+
nextExpectedResponders: ensureArray(processed.result?.next_expected_responders),
|
|
12136
|
+
normalizedExecutionContractType: String(processed.result?.normalized_execution_contract_type || "").trim(),
|
|
12137
|
+
normalizedExecutionContractTargets: ensureArray(processed.result?.normalized_execution_contract_targets),
|
|
12138
|
+
normalizedExecutionNextResponders: ensureArray(processed.result?.normalized_execution_next_responders),
|
|
12139
|
+
currentBotSelector: normalizeTelegramMentionUsername(
|
|
12140
|
+
deferredExecution.bot?.username || deferredExecution.bot?.name,
|
|
12141
|
+
),
|
|
12142
|
+
conversationIntentMode: String(processed.result?.conversation_intent_mode || "").trim(),
|
|
12143
|
+
normalizedIntent: resolvedIntentType,
|
|
12144
|
+
aiReplyGenerated: processed.result?.ai_reply_generated === true,
|
|
12145
|
+
aiReplyGeneratedAt: String(processed.result?.ai_reply_generated_at || "").trim(),
|
|
12146
|
+
aiReplyPreview: String(processed.result?.ai_reply_preview || "").trim(),
|
|
12147
|
+
responseContractValidationStatus: String(processed.result?.response_contract_validation_status || "").trim(),
|
|
12148
|
+
responseContractValidationReason: String(processed.result?.response_contract_validation_reason || "").trim(),
|
|
12149
|
+
responseContractValidationTargets: ensureArray(processed.result?.response_contract_validation_targets),
|
|
12150
|
+
assignmentValidationStatus: String(processed.result?.assignment_validation_status || "").trim(),
|
|
12151
|
+
assignmentValidationReason: String(processed.result?.assignment_validation_reason || "").trim(),
|
|
12152
|
+
assignmentValidationModes: ensureArray(processed.result?.assignment_validation_modes),
|
|
12153
|
+
deliveryStatus: String(processed.result?.delivery_status || "").trim(),
|
|
12154
|
+
archiveStatus: String(processed.result?.archive_status || "").trim(),
|
|
12155
|
+
transportError: String(processed.result?.transport_error || "").trim(),
|
|
12156
|
+
archiveError: String(processed.result?.archive_error || "").trim(),
|
|
12157
|
+
});
|
|
12158
|
+
if (processed.kind !== "delivery_failed") {
|
|
12159
|
+
await ensureRunnerRootWorkItemForRequest({
|
|
12160
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12161
|
+
routeKey: deferredExecution.routeKey,
|
|
12162
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12163
|
+
runtime: deferredExecution.runtime,
|
|
12164
|
+
requestKey: deferredExecution.requestKey,
|
|
12165
|
+
});
|
|
12166
|
+
}
|
|
12167
|
+
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
12168
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12169
|
+
runtime: deferredExecution.runtime,
|
|
12170
|
+
requestKey: deferredExecution.requestKey,
|
|
12171
|
+
});
|
|
12172
|
+
const syncedRequest = safeObject(rootWorkItemSync.request);
|
|
12173
|
+
const followupRoutePatch = buildRunnerRouteFollowupSnapshotPatch(
|
|
12174
|
+
deferredExecution.selectedRecord,
|
|
12175
|
+
processed.result,
|
|
12176
|
+
);
|
|
12177
|
+
if (String(syncedRequest.root_work_item_id || "").trim()) {
|
|
12178
|
+
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
12179
|
+
last_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
12180
|
+
last_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
12181
|
+
last_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
12182
|
+
...followupRoutePatch,
|
|
12183
|
+
});
|
|
12184
|
+
} else if (Object.keys(followupRoutePatch).length) {
|
|
12185
|
+
saveRunnerRouteState(deferredExecution.routeKey, followupRoutePatch);
|
|
12186
|
+
}
|
|
12187
|
+
await syncRunnerRequestLedgerForProjectToServer({
|
|
12188
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12189
|
+
runtime: deferredExecution.runtime,
|
|
12190
|
+
});
|
|
12191
|
+
}
|
|
12192
|
+
return {
|
|
12193
|
+
logical_signature: runnerRouteLogicalSignature(deferredExecution.normalizedRoute),
|
|
12194
|
+
archive_source: String(deferredExecution.archiveThread.source || "").trim() || "-",
|
|
12195
|
+
archive_work_item_id: String(deferredExecution.archiveThread.workItemID || "").trim() || "",
|
|
12196
|
+
...processed.result,
|
|
12197
|
+
};
|
|
12198
|
+
}
|
|
12199
|
+
|
|
12200
|
+
async function finalizeRunnerDeferredExecutionError(deferredExecution, errorText) {
|
|
12201
|
+
const currentRouteState = safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]);
|
|
12202
|
+
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
12203
|
+
...currentRouteState,
|
|
12204
|
+
...emptyRunnerActiveExecutionPatch(),
|
|
12205
|
+
last_error: errorText,
|
|
12206
|
+
});
|
|
12207
|
+
if (String(deferredExecution?.requestKey || "").trim()) {
|
|
12208
|
+
const currentRequest = safeObject(loadRunnerRequestByKey(deferredExecution.requestKey));
|
|
12209
|
+
const resolvedIntentType = String(
|
|
12210
|
+
safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]).last_intent_type
|
|
12211
|
+
|| currentRequest.normalized_intent
|
|
12212
|
+
|| "",
|
|
12213
|
+
).trim();
|
|
12214
|
+
markRunnerRequestLifecycle({
|
|
12215
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12216
|
+
requestKey: deferredExecution.requestKey,
|
|
12217
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12218
|
+
routeKey: deferredExecution.routeKey,
|
|
12219
|
+
outcome: "error",
|
|
12220
|
+
closedReason: errorText || "execution_error",
|
|
12221
|
+
normalizedIntent: resolvedIntentType,
|
|
12222
|
+
});
|
|
12223
|
+
await ensureRunnerRootWorkItemForRequest({
|
|
12224
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12225
|
+
routeKey: deferredExecution.routeKey,
|
|
12226
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12227
|
+
runtime: deferredExecution.runtime,
|
|
12228
|
+
requestKey: deferredExecution.requestKey,
|
|
12229
|
+
});
|
|
12230
|
+
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
12231
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12232
|
+
runtime: deferredExecution.runtime,
|
|
12233
|
+
requestKey: deferredExecution.requestKey,
|
|
12234
|
+
});
|
|
12235
|
+
const syncedRequest = safeObject(rootWorkItemSync.request);
|
|
12236
|
+
if (String(syncedRequest.root_work_item_id || "").trim()) {
|
|
12237
|
+
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
12238
|
+
last_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
12239
|
+
last_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
12240
|
+
last_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
12241
|
+
});
|
|
12242
|
+
}
|
|
12243
|
+
await syncRunnerRequestLedgerForProjectToServer({
|
|
12244
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12245
|
+
runtime: deferredExecution.runtime,
|
|
12246
|
+
});
|
|
12247
|
+
}
|
|
12248
|
+
return {
|
|
12249
|
+
route_key: deferredExecution.routeKey,
|
|
12250
|
+
route_name: deferredExecution.normalizedRoute.name,
|
|
12251
|
+
logical_signature: runnerRouteLogicalSignature(deferredExecution.normalizedRoute),
|
|
12252
|
+
outcome: "error",
|
|
12253
|
+
detail: errorText,
|
|
12254
|
+
archive_source: String(deferredExecution.archiveThread.source || "").trim() || "-",
|
|
12255
|
+
archive_work_item_id: String(deferredExecution.archiveThread.workItemID || "").trim() || "",
|
|
12256
|
+
thread_id: deferredExecution.archiveThread.threadID,
|
|
12257
|
+
comment_id: deferredExecution.selectedRecord.id,
|
|
12258
|
+
};
|
|
12259
|
+
}
|
|
12260
|
+
|
|
12261
|
+
function reportRunnerDeferredExecutionAccepted({ deferredExecution, runnerLogger, tui }) {
|
|
12262
|
+
tui?.reportExecutionStage({
|
|
12263
|
+
routeKey: deferredExecution.routeKey,
|
|
12264
|
+
executionPlan: deferredExecution.executionPlan,
|
|
12265
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12266
|
+
phase: "ai_running",
|
|
12267
|
+
detail: `running local AI client for comment ${String(deferredExecution.selectedRecord?.id || "").trim() || "-"}`,
|
|
12268
|
+
});
|
|
12269
|
+
runnerLogger?.append("execution_accepted", {
|
|
12270
|
+
route_key: deferredExecution.routeKey,
|
|
12271
|
+
route_name: deferredExecution.normalizedRoute?.name,
|
|
12272
|
+
comment_id: String(deferredExecution.selectedRecord?.id || "").trim(),
|
|
12273
|
+
source_message_id: intFromRawAllowZero(deferredExecution.selectedRecord?.parsedArchive?.messageID, 0),
|
|
12274
|
+
execution_mode: String(deferredExecution.executionPlan?.mode || "").trim(),
|
|
12275
|
+
role_profile: String(deferredExecution.executionPlan?.roleProfileName || "").trim(),
|
|
12276
|
+
});
|
|
12277
|
+
}
|
|
12278
|
+
|
|
12279
|
+
function createRunnerDeferredExecutionStageReporter({ deferredExecution, runnerLogger, tui }) {
|
|
12280
|
+
return (stage) => {
|
|
12281
|
+
runnerLogger?.append("execution_stage", {
|
|
12282
|
+
route_key: deferredExecution.routeKey,
|
|
12283
|
+
route_name: deferredExecution.normalizedRoute?.name,
|
|
12284
|
+
comment_id: String(deferredExecution.selectedRecord?.id || "").trim(),
|
|
12285
|
+
source_message_id: intFromRawAllowZero(deferredExecution.selectedRecord?.parsedArchive?.messageID, 0),
|
|
12286
|
+
phase: String(safeObject(stage).phase || "").trim(),
|
|
12287
|
+
detail: String(safeObject(stage).detail || "").trim(),
|
|
12288
|
+
intent_type: String(safeObject(stage).intentType || "").trim(),
|
|
12289
|
+
context_suggestion_status: String(safeObject(stage).contextSuggestionStatus || "").trim(),
|
|
12290
|
+
});
|
|
12291
|
+
tui?.reportExecutionStage({
|
|
12292
|
+
routeKey: deferredExecution.routeKey,
|
|
12293
|
+
executionPlan: deferredExecution.executionPlan,
|
|
12294
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12295
|
+
...safeObject(stage),
|
|
12296
|
+
});
|
|
12297
|
+
};
|
|
12298
|
+
}
|
|
12299
|
+
|
|
12300
|
+
function publishRunnerDeferredExecutionFinalResult({
|
|
12301
|
+
deferredExecution,
|
|
12302
|
+
finalResult,
|
|
12303
|
+
runnerLogger,
|
|
12304
|
+
tui,
|
|
12305
|
+
jsonMode,
|
|
12306
|
+
}) {
|
|
12307
|
+
const latestRouteState = safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]);
|
|
12308
|
+
runnerLogger?.append("execution_result", {
|
|
12309
|
+
route_key: String(finalResult?.route_key || deferredExecution.routeKey).trim(),
|
|
12310
|
+
route_name: String(finalResult?.route_name || deferredExecution.normalizedRoute?.name || "").trim(),
|
|
12311
|
+
outcome: String(finalResult?.outcome || "").trim(),
|
|
12312
|
+
detail: String(finalResult?.detail || "").trim(),
|
|
12313
|
+
comment_id: String(finalResult?.comment_id || deferredExecution.selectedRecord?.id || "").trim(),
|
|
12314
|
+
source_message_id: intFromRawAllowZero(
|
|
12315
|
+
latestRouteState?.last_source_message_id,
|
|
12316
|
+
intFromRawAllowZero(deferredExecution.selectedRecord?.parsedArchive?.messageID, 0),
|
|
12317
|
+
),
|
|
12318
|
+
intent_type: String(latestRouteState?.last_intent_type || "").trim(),
|
|
12319
|
+
context_suggestion_status: String(
|
|
12320
|
+
finalResult?.context_suggestion_status || latestRouteState?.last_context_suggestion_status || "",
|
|
12321
|
+
).trim(),
|
|
12322
|
+
});
|
|
12323
|
+
if (tui) {
|
|
12324
|
+
tui.recordResult(finalResult, latestRouteState);
|
|
12325
|
+
return;
|
|
12326
|
+
}
|
|
12327
|
+
printRunnerResult("start", finalResult, jsonMode);
|
|
12328
|
+
}
|
|
12329
|
+
|
|
12330
|
+
function handleRunnerStartRouteCycleError({
|
|
12331
|
+
normalizedRoute,
|
|
12332
|
+
routeKey,
|
|
12333
|
+
errorText,
|
|
12334
|
+
runnerLogger,
|
|
12335
|
+
tui,
|
|
12336
|
+
jsonMode,
|
|
12337
|
+
}) {
|
|
12338
|
+
const fatalArchiveBootstrapError = errorText.includes("Archive thread is missing")
|
|
12339
|
+
&& errorText.includes("write access is denied");
|
|
12340
|
+
saveRunnerRouteState(routeKey, {
|
|
12341
|
+
...safeObject(loadBotRunnerState().routes[routeKey]),
|
|
12342
|
+
last_error: errorText,
|
|
12343
|
+
});
|
|
12344
|
+
const result = buildRunnerStartCycleErrorResult({
|
|
12345
|
+
normalizedRoute,
|
|
12346
|
+
routeKey,
|
|
12347
|
+
errorText,
|
|
12348
|
+
fatalArchiveBootstrapError,
|
|
12349
|
+
});
|
|
12350
|
+
publishRunnerStartRouteResult({
|
|
12351
|
+
result,
|
|
12352
|
+
routeKey,
|
|
12353
|
+
routeName: normalizedRoute.name,
|
|
12354
|
+
runnerLogger,
|
|
12355
|
+
tui,
|
|
12356
|
+
jsonMode,
|
|
12357
|
+
});
|
|
12358
|
+
return fatalArchiveBootstrapError ? "blocked" : "error";
|
|
12359
|
+
}
|
|
12360
|
+
|
|
12361
|
+
function publishRunnerStartImmediateResult({
|
|
12362
|
+
result,
|
|
12363
|
+
routeKey,
|
|
12364
|
+
normalizedRoute,
|
|
12365
|
+
runnerLogger,
|
|
12366
|
+
tui,
|
|
12367
|
+
jsonMode,
|
|
12368
|
+
}) {
|
|
12369
|
+
publishRunnerStartRouteResult({
|
|
12370
|
+
result,
|
|
12371
|
+
routeKey,
|
|
12372
|
+
routeName: normalizedRoute.name,
|
|
12373
|
+
runnerLogger,
|
|
12374
|
+
tui,
|
|
12375
|
+
jsonMode,
|
|
12376
|
+
});
|
|
12377
|
+
}
|
|
12378
|
+
|
|
12379
|
+
function resolveRunnerStartDueRoutes(routes, schedules, now = Date.now()) {
|
|
12380
|
+
const dueRoutes = [];
|
|
12381
|
+
let nextSleepMs = 5000;
|
|
12382
|
+
for (const route of routes) {
|
|
12383
|
+
const normalizedRoute = normalizeRunnerRoute(route);
|
|
12384
|
+
const routeKey = runnerRouteKey(normalizedRoute);
|
|
12385
|
+
const nextAt = Number(schedules.get(routeKey) || 0);
|
|
12386
|
+
if (nextAt > now) {
|
|
12387
|
+
nextSleepMs = Math.min(nextSleepMs, Math.max(250, nextAt - now));
|
|
12388
|
+
continue;
|
|
12389
|
+
}
|
|
12390
|
+
dueRoutes.push(normalizedRoute);
|
|
12391
|
+
}
|
|
12392
|
+
return {
|
|
12393
|
+
dueRoutes,
|
|
12394
|
+
nextSleepMs,
|
|
12395
|
+
};
|
|
12396
|
+
}
|
|
12397
|
+
|
|
12398
|
+
function refreshRunnerStartNextSleepMs(routes, schedules, currentNextSleepMs, now = Date.now()) {
|
|
12399
|
+
let nextSleepMs = currentNextSleepMs;
|
|
12400
|
+
for (const route of routes) {
|
|
12401
|
+
const normalizedRoute = normalizeRunnerRoute(route);
|
|
12402
|
+
const routeKey = runnerRouteKey(normalizedRoute);
|
|
12403
|
+
const nextAt = Number(schedules.get(routeKey) || 0);
|
|
12404
|
+
if (nextAt > now) {
|
|
12405
|
+
nextSleepMs = Math.min(nextSleepMs, Math.max(250, nextAt - now));
|
|
12406
|
+
}
|
|
12407
|
+
}
|
|
12408
|
+
return nextSleepMs;
|
|
12409
|
+
}
|
|
12410
|
+
|
|
12411
|
+
function finalizeRunnerDeferredExecutionLoopState({
|
|
12412
|
+
deferredExecution,
|
|
12413
|
+
executionHeartbeat,
|
|
12414
|
+
inFlightExecutions,
|
|
12415
|
+
schedules,
|
|
12416
|
+
tui,
|
|
12417
|
+
}) {
|
|
12418
|
+
executionHeartbeat.stop();
|
|
12419
|
+
inFlightExecutions.delete(deferredExecution.routeKey);
|
|
12420
|
+
const nextRunAt = Date.now() + 250;
|
|
12421
|
+
schedules.set(deferredExecution.routeKey, nextRunAt);
|
|
12422
|
+
tui?.updateNextRunAt(deferredExecution.routeKey, nextRunAt);
|
|
12423
|
+
}
|
|
12424
|
+
|
|
12425
|
+
function createRunnerDeferredExecutionPromise({
|
|
12426
|
+
deferredExecution,
|
|
12427
|
+
executionHeartbeat,
|
|
12428
|
+
inFlightExecutions,
|
|
12429
|
+
schedules,
|
|
12430
|
+
tui,
|
|
12431
|
+
runnerLogger,
|
|
12432
|
+
jsonMode,
|
|
12433
|
+
reportRunnerStage,
|
|
12434
|
+
}) {
|
|
12435
|
+
return (async () => {
|
|
12436
|
+
try {
|
|
12437
|
+
const processed = await processRunnerSelectedRecord({
|
|
12438
|
+
routeKey: deferredExecution.routeKey,
|
|
12439
|
+
normalizedRoute: deferredExecution.normalizedRoute,
|
|
12440
|
+
routeState: deferredExecution.routeState,
|
|
12441
|
+
selectedRecord: deferredExecution.selectedRecord,
|
|
12442
|
+
pendingOrdered: deferredExecution.pendingOrdered,
|
|
12443
|
+
bot: deferredExecution.bot,
|
|
12444
|
+
destination: deferredExecution.destination,
|
|
12445
|
+
archiveThread: deferredExecution.archiveThread,
|
|
12446
|
+
executionPlan: deferredExecution.executionPlan,
|
|
12447
|
+
runtime: deferredExecution.runtime,
|
|
12448
|
+
triggerDecision: deferredExecution.triggerDecision,
|
|
12449
|
+
responderAdjudication: deferredExecution.responderAdjudication,
|
|
12450
|
+
persistedHumanIntentRequest: loadRunnerRequestByKey(deferredExecution.requestKey),
|
|
12451
|
+
precomputedHumanIntentContext: safeObject(deferredExecution.humanIntentContext),
|
|
12452
|
+
deps: {
|
|
12453
|
+
saveRunnerRouteState,
|
|
12454
|
+
startRunnerTypingHeartbeat,
|
|
12455
|
+
runRunnerAIExecution,
|
|
12456
|
+
explainExecutionFailureWithAI,
|
|
12457
|
+
performLocalBotDelivery,
|
|
12458
|
+
serializeRunnerTriggerPolicy,
|
|
12459
|
+
serializeRunnerArchivePolicy,
|
|
12460
|
+
buildRunnerExecutionDeps,
|
|
12461
|
+
buildRunnerDeliveryDeps,
|
|
12462
|
+
buildRunnerRuntimeDeps,
|
|
12463
|
+
managedConversationBots: deferredExecution.managedConversationBots,
|
|
12464
|
+
resolveConversationPeerBots: resolveRunnerConversationPeers,
|
|
12465
|
+
reportRunnerStage,
|
|
12466
|
+
},
|
|
12467
|
+
});
|
|
12468
|
+
if (processed.kind === "skipped") {
|
|
12469
|
+
return await finalizeRunnerDeferredExecutionSkipped(deferredExecution, processed);
|
|
12470
|
+
}
|
|
12471
|
+
return await finalizeRunnerDeferredExecutionProcessed(deferredExecution, processed);
|
|
12472
|
+
} catch (err) {
|
|
12473
|
+
const errorText = String(err?.message || err).trim();
|
|
12474
|
+
return await finalizeRunnerDeferredExecutionError(deferredExecution, errorText);
|
|
12475
|
+
} finally {
|
|
12476
|
+
finalizeRunnerDeferredExecutionLoopState({
|
|
12477
|
+
deferredExecution,
|
|
12478
|
+
executionHeartbeat,
|
|
12479
|
+
inFlightExecutions,
|
|
12480
|
+
schedules,
|
|
12481
|
+
tui,
|
|
12482
|
+
});
|
|
12483
|
+
}
|
|
12484
|
+
})().then((finalResult) => {
|
|
12485
|
+
publishRunnerDeferredExecutionFinalResult({
|
|
12486
|
+
deferredExecution,
|
|
12487
|
+
finalResult,
|
|
12488
|
+
runnerLogger,
|
|
12489
|
+
tui,
|
|
12490
|
+
jsonMode,
|
|
12491
|
+
});
|
|
12492
|
+
return finalResult;
|
|
12493
|
+
});
|
|
12494
|
+
}
|
|
12495
|
+
|
|
11164
12496
|
async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
11165
12497
|
const jsonMode = boolFromRaw(flags.json, false);
|
|
11166
12498
|
const sourceLabel = String(safeObject(options).sourceLabel || "runner start").trim() || "runner start";
|
|
@@ -11190,460 +12522,92 @@ async function runRunnerStartResolvedRoutes(routes, flags, options = {}) {
|
|
|
11190
12522
|
}
|
|
11191
12523
|
tui?.start();
|
|
11192
12524
|
while (!stopRequested) {
|
|
11193
|
-
const
|
|
11194
|
-
|
|
11195
|
-
let nextSleepMs = 5000;
|
|
11196
|
-
for (const route of routes) {
|
|
11197
|
-
const normalizedRoute = normalizeRunnerRoute(route);
|
|
11198
|
-
const routeKey = runnerRouteKey(normalizedRoute);
|
|
11199
|
-
const nextAt = Number(schedules.get(routeKey) || 0);
|
|
11200
|
-
if (nextAt > now) {
|
|
11201
|
-
nextSleepMs = Math.min(nextSleepMs, Math.max(250, nextAt - now));
|
|
11202
|
-
continue;
|
|
11203
|
-
}
|
|
11204
|
-
dueRoutes.push(normalizedRoute);
|
|
11205
|
-
}
|
|
12525
|
+
const { dueRoutes, nextSleepMs: initialNextSleepMs } = resolveRunnerStartDueRoutes(routes, schedules, Date.now());
|
|
12526
|
+
let nextSleepMs = initialNextSleepMs;
|
|
11206
12527
|
if (dueRoutes.length > 0) {
|
|
11207
12528
|
const runtime = await resolveRunnerContext(flags);
|
|
11208
12529
|
const dueRouteGroups = groupRunnerRoutesBySchedulingTarget(dueRoutes);
|
|
11209
12530
|
await runTasksWithConcurrencyLimit(dueRouteGroups, concurrency, async (routeGroup) => {
|
|
11210
12531
|
for (const normalizedRoute of ensureArray(routeGroup)) {
|
|
11211
12532
|
const routeKey = runnerRouteKey(normalizedRoute);
|
|
11212
|
-
|
|
11213
|
-
phase: "polling",
|
|
11214
|
-
detail: "checking inbound messages and archive thread",
|
|
11215
|
-
});
|
|
11216
|
-
runnerLogger?.append("route_poll", {
|
|
11217
|
-
route_key: routeKey,
|
|
11218
|
-
route_name: normalizedRoute.name,
|
|
11219
|
-
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
11220
|
-
});
|
|
12533
|
+
beginRunnerStartRoutePoll({ routeKey, normalizedRoute, tui, runnerLogger });
|
|
11221
12534
|
let cycleOutcome = "polling";
|
|
11222
12535
|
try {
|
|
11223
12536
|
const result = await processRunnerRouteOnce(normalizedRoute, runtime, "start", { deferExecution: true });
|
|
11224
12537
|
cycleOutcome = String(result?.outcome || "").trim().toLowerCase() || "idle";
|
|
11225
12538
|
const deferredExecution = safeObject(result.deferred_execution);
|
|
11226
12539
|
if (deferredExecution.routeKey) {
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
phase: "ai_running",
|
|
11232
|
-
detail: `running local AI client for comment ${String(deferredExecution.selectedRecord?.id || "").trim() || "-"}`,
|
|
11233
|
-
});
|
|
11234
|
-
runnerLogger?.append("execution_accepted", {
|
|
11235
|
-
route_key: deferredExecution.routeKey,
|
|
11236
|
-
route_name: deferredExecution.normalizedRoute?.name,
|
|
11237
|
-
comment_id: String(deferredExecution.selectedRecord?.id || "").trim(),
|
|
11238
|
-
source_message_id: intFromRawAllowZero(deferredExecution.selectedRecord?.parsedArchive?.messageID, 0),
|
|
11239
|
-
execution_mode: String(deferredExecution.executionPlan?.mode || "").trim(),
|
|
11240
|
-
role_profile: String(deferredExecution.executionPlan?.roleProfileName || "").trim(),
|
|
12540
|
+
reportRunnerDeferredExecutionAccepted({
|
|
12541
|
+
deferredExecution,
|
|
12542
|
+
runnerLogger,
|
|
12543
|
+
tui,
|
|
11241
12544
|
});
|
|
11242
12545
|
const executionHeartbeat = startRunnerExecutionHeartbeat(
|
|
11243
12546
|
deferredExecution.routeKey,
|
|
11244
12547
|
deferredExecution.selectedRecord,
|
|
11245
12548
|
);
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
routeKey: deferredExecution.routeKey,
|
|
11252
|
-
outcome: "running",
|
|
11253
|
-
});
|
|
11254
|
-
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
11255
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11256
|
-
runtime: deferredExecution.runtime,
|
|
11257
|
-
requestKey: deferredExecution.requestKey,
|
|
11258
|
-
});
|
|
11259
|
-
const syncedRequest = safeObject(rootWorkItemSync.request);
|
|
11260
|
-
if (String(syncedRequest.root_work_item_id || "").trim()) {
|
|
11261
|
-
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
11262
|
-
active_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
11263
|
-
active_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
11264
|
-
active_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
11265
|
-
last_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
11266
|
-
last_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
11267
|
-
last_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
11268
|
-
});
|
|
11269
|
-
}
|
|
11270
|
-
await syncRunnerRequestLedgerForProjectToServer({
|
|
11271
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11272
|
-
runtime: deferredExecution.runtime,
|
|
11273
|
-
});
|
|
11274
|
-
}
|
|
11275
|
-
const executionPromise = (async () => {
|
|
11276
|
-
try {
|
|
11277
|
-
const processed = await processRunnerSelectedRecord({
|
|
11278
|
-
routeKey: deferredExecution.routeKey,
|
|
11279
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11280
|
-
routeState: deferredExecution.routeState,
|
|
11281
|
-
selectedRecord: deferredExecution.selectedRecord,
|
|
11282
|
-
pendingOrdered: deferredExecution.pendingOrdered,
|
|
11283
|
-
bot: deferredExecution.bot,
|
|
11284
|
-
destination: deferredExecution.destination,
|
|
11285
|
-
archiveThread: deferredExecution.archiveThread,
|
|
11286
|
-
executionPlan: deferredExecution.executionPlan,
|
|
11287
|
-
runtime: deferredExecution.runtime,
|
|
11288
|
-
triggerDecision: deferredExecution.triggerDecision,
|
|
11289
|
-
responderAdjudication: deferredExecution.responderAdjudication,
|
|
11290
|
-
persistedHumanIntentRequest: loadRunnerRequestByKey(deferredExecution.requestKey),
|
|
11291
|
-
precomputedHumanIntentContext: safeObject(deferredExecution.humanIntentContext),
|
|
11292
|
-
deps: {
|
|
11293
|
-
saveRunnerRouteState,
|
|
11294
|
-
startRunnerTypingHeartbeat,
|
|
11295
|
-
runRunnerAIExecution,
|
|
11296
|
-
explainExecutionFailureWithAI,
|
|
11297
|
-
performLocalBotDelivery,
|
|
11298
|
-
serializeRunnerTriggerPolicy,
|
|
11299
|
-
serializeRunnerArchivePolicy,
|
|
11300
|
-
buildRunnerExecutionDeps,
|
|
11301
|
-
buildRunnerDeliveryDeps,
|
|
11302
|
-
buildRunnerRuntimeDeps,
|
|
11303
|
-
managedConversationBots: deferredExecution.managedConversationBots,
|
|
11304
|
-
resolveConversationPeerBots: resolveRunnerConversationPeers,
|
|
11305
|
-
reportRunnerStage: (stage) => {
|
|
11306
|
-
runnerLogger?.append("execution_stage", {
|
|
11307
|
-
route_key: deferredExecution.routeKey,
|
|
11308
|
-
route_name: deferredExecution.normalizedRoute?.name,
|
|
11309
|
-
comment_id: String(deferredExecution.selectedRecord?.id || "").trim(),
|
|
11310
|
-
source_message_id: intFromRawAllowZero(deferredExecution.selectedRecord?.parsedArchive?.messageID, 0),
|
|
11311
|
-
phase: String(safeObject(stage).phase || "").trim(),
|
|
11312
|
-
detail: String(safeObject(stage).detail || "").trim(),
|
|
11313
|
-
intent_type: String(safeObject(stage).intentType || "").trim(),
|
|
11314
|
-
context_suggestion_status: String(safeObject(stage).contextSuggestionStatus || "").trim(),
|
|
11315
|
-
});
|
|
11316
|
-
tui?.reportExecutionStage({
|
|
11317
|
-
routeKey: deferredExecution.routeKey,
|
|
11318
|
-
executionPlan: deferredExecution.executionPlan,
|
|
11319
|
-
selectedRecord: deferredExecution.selectedRecord,
|
|
11320
|
-
...safeObject(stage),
|
|
11321
|
-
});
|
|
11322
|
-
},
|
|
11323
|
-
},
|
|
11324
|
-
});
|
|
11325
|
-
if (processed.kind === "skipped") {
|
|
11326
|
-
if (String(deferredExecution.requestKey || "").trim()) {
|
|
11327
|
-
markRunnerRequestLifecycle({
|
|
11328
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11329
|
-
requestKey: deferredExecution.requestKey,
|
|
11330
|
-
selectedRecord: deferredExecution.selectedRecord,
|
|
11331
|
-
routeKey: deferredExecution.routeKey,
|
|
11332
|
-
outcome: "skipped",
|
|
11333
|
-
closedReason: String(processed.skippedRecord?.reason || "skipped").trim() || "skipped",
|
|
11334
|
-
});
|
|
11335
|
-
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
11336
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11337
|
-
runtime: deferredExecution.runtime,
|
|
11338
|
-
requestKey: deferredExecution.requestKey,
|
|
11339
|
-
});
|
|
11340
|
-
const syncedRequest = safeObject(rootWorkItemSync.request);
|
|
11341
|
-
if (String(syncedRequest.root_work_item_id || "").trim()) {
|
|
11342
|
-
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
11343
|
-
last_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
11344
|
-
last_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
11345
|
-
last_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
11346
|
-
});
|
|
11347
|
-
}
|
|
11348
|
-
await syncRunnerRequestLedgerForProjectToServer({
|
|
11349
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11350
|
-
runtime: deferredExecution.runtime,
|
|
11351
|
-
});
|
|
11352
|
-
}
|
|
11353
|
-
return {
|
|
11354
|
-
route_key: deferredExecution.routeKey,
|
|
11355
|
-
route_name: deferredExecution.normalizedRoute.name,
|
|
11356
|
-
logical_signature: runnerRouteLogicalSignature(deferredExecution.normalizedRoute),
|
|
11357
|
-
outcome: "skipped",
|
|
11358
|
-
detail: String(processed.skippedRecord?.reason || "trigger policy skipped message").trim(),
|
|
11359
|
-
archive_source: String(deferredExecution.archiveThread.source || "").trim() || "-",
|
|
11360
|
-
archive_work_item_id: String(deferredExecution.archiveThread.workItemID || "").trim() || "",
|
|
11361
|
-
thread_id: deferredExecution.archiveThread.threadID,
|
|
11362
|
-
comment_id: deferredExecution.selectedRecord.id,
|
|
11363
|
-
execution_mode: deferredExecution.executionPlan.mode,
|
|
11364
|
-
role_profile: deferredExecution.executionPlan.roleProfileName,
|
|
11365
|
-
};
|
|
11366
|
-
}
|
|
11367
|
-
if (String(deferredExecution.requestKey || "").trim()) {
|
|
11368
|
-
const resolvedIntentType = String(
|
|
11369
|
-
safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]).last_intent_type || "",
|
|
11370
|
-
).trim();
|
|
11371
|
-
markRunnerRequestLifecycle({
|
|
11372
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11373
|
-
requestKey: deferredExecution.requestKey,
|
|
11374
|
-
selectedRecord: deferredExecution.selectedRecord,
|
|
11375
|
-
routeKey: deferredExecution.routeKey,
|
|
11376
|
-
outcome: processed.kind === "delivery_failed"
|
|
11377
|
-
? "delivery_failed_after_generation"
|
|
11378
|
-
: String(processed.result?.outcome || "replied").trim().toLowerCase(),
|
|
11379
|
-
conversationIDRaw: String(processed.result?.conversation_id || "").trim(),
|
|
11380
|
-
conversationParticipants: ensureArray(processed.result?.conversation_participants),
|
|
11381
|
-
conversationInitialResponders: ensureArray(processed.result?.conversation_initial_responders),
|
|
11382
|
-
allowedResponders: ensureArray(processed.result?.conversation_allowed_responders),
|
|
11383
|
-
conversationLeadBot: String(processed.result?.conversation_lead_bot || "").trim(),
|
|
11384
|
-
conversationSummaryBot: String(processed.result?.conversation_summary_bot || "").trim(),
|
|
11385
|
-
conversationAllowBotToBot: processed.result?.conversation_allow_bot_to_bot === true,
|
|
11386
|
-
conversationReplyExpectation: String(processed.result?.conversation_reply_expectation || "").trim(),
|
|
11387
|
-
executionContractType: String(processed.result?.execution_contract_type || "").trim(),
|
|
11388
|
-
executionContractActionable: processed.result?.execution_contract_actionable === true,
|
|
11389
|
-
executionContractTargets: ensureArray(processed.result?.execution_contract_targets),
|
|
11390
|
-
nextExpectedResponders: ensureArray(processed.result?.next_expected_responders),
|
|
11391
|
-
currentBotSelector: normalizeTelegramMentionUsername(
|
|
11392
|
-
deferredExecution.bot?.username || deferredExecution.bot?.name,
|
|
11393
|
-
),
|
|
11394
|
-
conversationIntentMode: String(processed.result?.conversation_intent_mode || "").trim(),
|
|
11395
|
-
normalizedIntent: resolvedIntentType,
|
|
11396
|
-
aiReplyGenerated: processed.result?.ai_reply_generated === true,
|
|
11397
|
-
aiReplyGeneratedAt: String(processed.result?.ai_reply_generated_at || "").trim(),
|
|
11398
|
-
aiReplyPreview: String(processed.result?.ai_reply_preview || "").trim(),
|
|
11399
|
-
responseContractValidationStatus: String(processed.result?.response_contract_validation_status || "").trim(),
|
|
11400
|
-
responseContractValidationReason: String(processed.result?.response_contract_validation_reason || "").trim(),
|
|
11401
|
-
responseContractValidationTargets: ensureArray(processed.result?.response_contract_validation_targets),
|
|
11402
|
-
assignmentValidationStatus: String(processed.result?.assignment_validation_status || "").trim(),
|
|
11403
|
-
assignmentValidationReason: String(processed.result?.assignment_validation_reason || "").trim(),
|
|
11404
|
-
assignmentValidationModes: ensureArray(processed.result?.assignment_validation_modes),
|
|
11405
|
-
deliveryStatus: String(processed.result?.delivery_status || "").trim(),
|
|
11406
|
-
archiveStatus: String(processed.result?.archive_status || "").trim(),
|
|
11407
|
-
transportError: String(processed.result?.transport_error || "").trim(),
|
|
11408
|
-
archiveError: String(processed.result?.archive_error || "").trim(),
|
|
11409
|
-
});
|
|
11410
|
-
if (processed.kind !== "delivery_failed") {
|
|
11411
|
-
await ensureRunnerRootWorkItemForRequest({
|
|
11412
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11413
|
-
routeKey: deferredExecution.routeKey,
|
|
11414
|
-
selectedRecord: deferredExecution.selectedRecord,
|
|
11415
|
-
runtime: deferredExecution.runtime,
|
|
11416
|
-
requestKey: deferredExecution.requestKey,
|
|
11417
|
-
});
|
|
11418
|
-
}
|
|
11419
|
-
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
11420
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11421
|
-
runtime: deferredExecution.runtime,
|
|
11422
|
-
requestKey: deferredExecution.requestKey,
|
|
11423
|
-
});
|
|
11424
|
-
const syncedRequest = safeObject(rootWorkItemSync.request);
|
|
11425
|
-
if (String(syncedRequest.root_work_item_id || "").trim()) {
|
|
11426
|
-
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
11427
|
-
last_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
11428
|
-
last_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
11429
|
-
last_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
11430
|
-
});
|
|
11431
|
-
}
|
|
11432
|
-
await syncRunnerRequestLedgerForProjectToServer({
|
|
11433
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11434
|
-
runtime: deferredExecution.runtime,
|
|
11435
|
-
});
|
|
11436
|
-
}
|
|
11437
|
-
return {
|
|
11438
|
-
logical_signature: runnerRouteLogicalSignature(deferredExecution.normalizedRoute),
|
|
11439
|
-
archive_source: String(deferredExecution.archiveThread.source || "").trim() || "-",
|
|
11440
|
-
archive_work_item_id: String(deferredExecution.archiveThread.workItemID || "").trim() || "",
|
|
11441
|
-
...processed.result,
|
|
11442
|
-
};
|
|
11443
|
-
} catch (err) {
|
|
11444
|
-
const currentRouteState = safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]);
|
|
11445
|
-
const errorText = String(err?.message || err).trim();
|
|
11446
|
-
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
11447
|
-
...currentRouteState,
|
|
11448
|
-
...emptyRunnerActiveExecutionPatch(),
|
|
11449
|
-
last_error: errorText,
|
|
11450
|
-
});
|
|
11451
|
-
if (String(deferredExecution.requestKey || "").trim()) {
|
|
11452
|
-
const currentRequest = safeObject(loadRunnerRequestByKey(deferredExecution.requestKey));
|
|
11453
|
-
const resolvedIntentType = String(
|
|
11454
|
-
safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]).last_intent_type
|
|
11455
|
-
|| currentRequest.normalized_intent
|
|
11456
|
-
|| "",
|
|
11457
|
-
).trim();
|
|
11458
|
-
markRunnerRequestLifecycle({
|
|
11459
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11460
|
-
requestKey: deferredExecution.requestKey,
|
|
11461
|
-
selectedRecord: deferredExecution.selectedRecord,
|
|
11462
|
-
routeKey: deferredExecution.routeKey,
|
|
11463
|
-
outcome: "error",
|
|
11464
|
-
closedReason: errorText || "execution_error",
|
|
11465
|
-
normalizedIntent: resolvedIntentType,
|
|
11466
|
-
});
|
|
11467
|
-
await ensureRunnerRootWorkItemForRequest({
|
|
11468
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11469
|
-
routeKey: deferredExecution.routeKey,
|
|
11470
|
-
selectedRecord: deferredExecution.selectedRecord,
|
|
11471
|
-
runtime: deferredExecution.runtime,
|
|
11472
|
-
requestKey: deferredExecution.requestKey,
|
|
11473
|
-
});
|
|
11474
|
-
const rootWorkItemSync = await syncRunnerRequestRootWorkItemForOutcome({
|
|
11475
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11476
|
-
runtime: deferredExecution.runtime,
|
|
11477
|
-
requestKey: deferredExecution.requestKey,
|
|
11478
|
-
});
|
|
11479
|
-
const syncedRequest = safeObject(rootWorkItemSync.request);
|
|
11480
|
-
if (String(syncedRequest.root_work_item_id || "").trim()) {
|
|
11481
|
-
saveRunnerRouteState(deferredExecution.routeKey, {
|
|
11482
|
-
last_root_work_item_id: String(syncedRequest.root_work_item_id || "").trim(),
|
|
11483
|
-
last_root_work_item_title: String(syncedRequest.root_work_item_title || "").trim(),
|
|
11484
|
-
last_root_work_item_status: String(syncedRequest.root_work_item_status || "").trim(),
|
|
11485
|
-
});
|
|
11486
|
-
}
|
|
11487
|
-
await syncRunnerRequestLedgerForProjectToServer({
|
|
11488
|
-
normalizedRoute: deferredExecution.normalizedRoute,
|
|
11489
|
-
runtime: deferredExecution.runtime,
|
|
11490
|
-
});
|
|
11491
|
-
}
|
|
11492
|
-
return {
|
|
11493
|
-
route_key: deferredExecution.routeKey,
|
|
11494
|
-
route_name: deferredExecution.normalizedRoute.name,
|
|
11495
|
-
logical_signature: runnerRouteLogicalSignature(deferredExecution.normalizedRoute),
|
|
11496
|
-
outcome: "error",
|
|
11497
|
-
detail: errorText,
|
|
11498
|
-
archive_source: String(deferredExecution.archiveThread.source || "").trim() || "-",
|
|
11499
|
-
archive_work_item_id: String(deferredExecution.archiveThread.workItemID || "").trim() || "",
|
|
11500
|
-
thread_id: deferredExecution.archiveThread.threadID,
|
|
11501
|
-
comment_id: deferredExecution.selectedRecord.id,
|
|
11502
|
-
};
|
|
11503
|
-
} finally {
|
|
11504
|
-
executionHeartbeat.stop();
|
|
11505
|
-
inFlightExecutions.delete(deferredExecution.routeKey);
|
|
11506
|
-
schedules.set(deferredExecution.routeKey, Date.now() + 250);
|
|
11507
|
-
tui?.updateNextRunAt(deferredExecution.routeKey, Date.now() + 250);
|
|
11508
|
-
}
|
|
11509
|
-
})();
|
|
11510
|
-
inFlightExecutions.set(routeKey, executionPromise);
|
|
11511
|
-
executionPromise.then((finalResult) => {
|
|
11512
|
-
const latestRouteState = safeObject(loadBotRunnerState().routes[deferredExecution.routeKey]);
|
|
11513
|
-
runnerLogger?.append("execution_result", {
|
|
11514
|
-
route_key: String(finalResult?.route_key || deferredExecution.routeKey).trim(),
|
|
11515
|
-
route_name: String(finalResult?.route_name || deferredExecution.normalizedRoute?.name || "").trim(),
|
|
11516
|
-
outcome: String(finalResult?.outcome || "").trim(),
|
|
11517
|
-
detail: String(finalResult?.detail || "").trim(),
|
|
11518
|
-
comment_id: String(finalResult?.comment_id || deferredExecution.selectedRecord?.id || "").trim(),
|
|
11519
|
-
source_message_id: intFromRawAllowZero(latestRouteState?.last_source_message_id, intFromRawAllowZero(deferredExecution.selectedRecord?.parsedArchive?.messageID, 0)),
|
|
11520
|
-
intent_type: String(latestRouteState?.last_intent_type || "").trim(),
|
|
11521
|
-
context_suggestion_status: String(finalResult?.context_suggestion_status || latestRouteState?.last_context_suggestion_status || "").trim(),
|
|
11522
|
-
});
|
|
11523
|
-
if (tui) {
|
|
11524
|
-
tui.recordResult(finalResult, latestRouteState);
|
|
11525
|
-
} else {
|
|
11526
|
-
printRunnerResult("start", finalResult, jsonMode);
|
|
11527
|
-
}
|
|
12549
|
+
await syncRunnerDeferredExecutionRunningState(deferredExecution);
|
|
12550
|
+
const reportRunnerStage = createRunnerDeferredExecutionStageReporter({
|
|
12551
|
+
deferredExecution,
|
|
12552
|
+
runnerLogger,
|
|
12553
|
+
tui,
|
|
11528
12554
|
});
|
|
12555
|
+
const executionPromise = createRunnerDeferredExecutionPromise({
|
|
12556
|
+
deferredExecution,
|
|
12557
|
+
executionHeartbeat,
|
|
12558
|
+
inFlightExecutions,
|
|
12559
|
+
schedules,
|
|
12560
|
+
tui,
|
|
12561
|
+
runnerLogger,
|
|
12562
|
+
jsonMode,
|
|
12563
|
+
reportRunnerStage,
|
|
12564
|
+
});
|
|
12565
|
+
inFlightExecutions.set(routeKey, executionPromise);
|
|
11529
12566
|
const acceptedResult = {
|
|
11530
12567
|
...result,
|
|
11531
12568
|
deferred_execution: undefined,
|
|
11532
12569
|
};
|
|
11533
12570
|
cycleOutcome = String(acceptedResult?.outcome || "accepted").trim().toLowerCase() || "accepted";
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
12571
|
+
publishRunnerStartImmediateResult({
|
|
12572
|
+
result: acceptedResult,
|
|
12573
|
+
routeKey,
|
|
12574
|
+
normalizedRoute,
|
|
12575
|
+
runnerLogger,
|
|
12576
|
+
tui,
|
|
12577
|
+
jsonMode,
|
|
11539
12578
|
});
|
|
11540
|
-
if (tui) {
|
|
11541
|
-
tui.recordResult(acceptedResult);
|
|
11542
|
-
} else {
|
|
11543
|
-
printRunnerResult("start", acceptedResult, jsonMode);
|
|
11544
|
-
}
|
|
11545
|
-
} else if (result.outcome !== "busy") {
|
|
11546
|
-
runnerLogger?.append("route_result", {
|
|
11547
|
-
route_key: String(result?.route_key || routeKey).trim(),
|
|
11548
|
-
route_name: String(result?.route_name || normalizedRoute.name || "").trim(),
|
|
11549
|
-
outcome: String(result?.outcome || "").trim(),
|
|
11550
|
-
detail: String(result?.detail || "").trim(),
|
|
11551
|
-
comment_id: String(result?.comment_id || "").trim(),
|
|
11552
|
-
});
|
|
11553
|
-
if (tui) {
|
|
11554
|
-
tui.recordResult(result);
|
|
11555
|
-
} else {
|
|
11556
|
-
printRunnerResult("start", result, jsonMode);
|
|
11557
|
-
}
|
|
11558
12579
|
} else {
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
12580
|
+
publishRunnerStartImmediateResult({
|
|
12581
|
+
result,
|
|
12582
|
+
routeKey,
|
|
12583
|
+
normalizedRoute,
|
|
12584
|
+
runnerLogger,
|
|
12585
|
+
tui,
|
|
12586
|
+
jsonMode,
|
|
11565
12587
|
});
|
|
11566
|
-
tui?.recordResult(result);
|
|
11567
12588
|
}
|
|
11568
12589
|
} catch (err) {
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
});
|
|
11577
|
-
const result = {
|
|
11578
|
-
route_key: routeKey,
|
|
11579
|
-
route_name: normalizedRoute.name,
|
|
11580
|
-
logical_signature: runnerRouteLogicalSignature(normalizedRoute),
|
|
11581
|
-
outcome: fatalArchiveBootstrapError ? "blocked" : "error",
|
|
11582
|
-
detail: errorText,
|
|
11583
|
-
};
|
|
11584
|
-
runnerLogger?.append("route_result", {
|
|
11585
|
-
route_key: routeKey,
|
|
11586
|
-
route_name: normalizedRoute.name,
|
|
11587
|
-
outcome: String(result.outcome || "error").trim(),
|
|
11588
|
-
detail: errorText,
|
|
12590
|
+
cycleOutcome = handleRunnerStartRouteCycleError({
|
|
12591
|
+
normalizedRoute,
|
|
12592
|
+
routeKey,
|
|
12593
|
+
errorText: String(err?.message || err),
|
|
12594
|
+
runnerLogger,
|
|
12595
|
+
tui,
|
|
12596
|
+
jsonMode,
|
|
11589
12597
|
});
|
|
11590
|
-
if (tui) {
|
|
11591
|
-
tui.recordResult(result);
|
|
11592
|
-
} else {
|
|
11593
|
-
printRunnerResult("start", result, jsonMode);
|
|
11594
|
-
}
|
|
11595
12598
|
} finally {
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
"primed",
|
|
11603
|
-
"skipped",
|
|
11604
|
-
"replied",
|
|
11605
|
-
"dry_run",
|
|
11606
|
-
"delivery_failed_after_generation",
|
|
11607
|
-
].includes(cycleOutcome);
|
|
11608
|
-
const shouldClearLastError = successfulCycleOutcome
|
|
11609
|
-
&& String(routeState.last_error || "").trim();
|
|
11610
|
-
const shouldClearStaleActiveExecution = !activeExecutionState.active
|
|
11611
|
-
&& successfulCycleOutcome
|
|
11612
|
-
&& String(routeState.active_comment_id || "").trim();
|
|
11613
|
-
if (shouldClearLastError || shouldClearStaleActiveExecution) {
|
|
11614
|
-
saveRunnerRouteState(routeKey, {
|
|
11615
|
-
...(shouldClearStaleActiveExecution ? emptyRunnerActiveExecutionPatch() : {}),
|
|
11616
|
-
...(shouldClearLastError ? { last_error: "" } : {}),
|
|
11617
|
-
});
|
|
11618
|
-
routeState = safeObject(loadBotRunnerState().routes[routeKey]);
|
|
11619
|
-
activeExecutionState = resolveRunnerActiveExecutionState(routeState);
|
|
11620
|
-
}
|
|
11621
|
-
tui?.setRouteState(routeKey, {
|
|
11622
|
-
intent_type: String(routeState.last_intent_type || "").trim(),
|
|
11623
|
-
source_message_id: intFromRawAllowZero(routeState.last_source_message_id, 0),
|
|
11624
|
-
warning: String(activeExecutionState.warning || "").trim(),
|
|
11625
|
-
last_error: String(routeState.last_error || "").trim(),
|
|
12599
|
+
finalizeRunnerStartRouteCycle({
|
|
12600
|
+
routeKey,
|
|
12601
|
+
normalizedRoute,
|
|
12602
|
+
cycleOutcome,
|
|
12603
|
+
schedules,
|
|
12604
|
+
tui,
|
|
11626
12605
|
});
|
|
11627
|
-
const lastErrorText = String(routeState.last_error || "").trim();
|
|
11628
|
-
const isFatalArchiveBootstrapError = lastErrorText.includes("Archive thread is missing")
|
|
11629
|
-
&& lastErrorText.includes("write access is denied");
|
|
11630
|
-
const nextRunAt = Date.now() + (isFatalArchiveBootstrapError
|
|
11631
|
-
? 60000
|
|
11632
|
-
: Math.max(1000, normalizedRoute.pollIntervalMs));
|
|
11633
|
-
schedules.set(routeKey, nextRunAt);
|
|
11634
|
-
tui?.updateNextRunAt(routeKey, nextRunAt);
|
|
11635
12606
|
}
|
|
11636
12607
|
}
|
|
11637
12608
|
});
|
|
11638
12609
|
}
|
|
11639
|
-
|
|
11640
|
-
const normalizedRoute = normalizeRunnerRoute(route);
|
|
11641
|
-
const routeKey = runnerRouteKey(normalizedRoute);
|
|
11642
|
-
const nextAt = Number(schedules.get(routeKey) || 0);
|
|
11643
|
-
if (nextAt > Date.now()) {
|
|
11644
|
-
nextSleepMs = Math.min(nextSleepMs, Math.max(250, nextAt - Date.now()));
|
|
11645
|
-
}
|
|
11646
|
-
}
|
|
12610
|
+
nextSleepMs = refreshRunnerStartNextSleepMs(routes, schedules, nextSleepMs, Date.now());
|
|
11647
12611
|
if (!stopRequested) {
|
|
11648
12612
|
await sleep(Math.max(250, nextSleepMs));
|
|
11649
12613
|
}
|