metheus-governance-mcp-cli 0.2.247 → 0.2.249
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 +296 -229
- package/lib/runner-orchestration.mjs +8 -8
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -2075,9 +2075,10 @@ function migrateBotRunnerStateRoutes(routes, runnerConfig) {
|
|
|
2075
2075
|
};
|
|
2076
2076
|
}
|
|
2077
2077
|
|
|
2078
|
-
function loadBotRunnerState() {
|
|
2079
|
-
const filePath = botRunnerStateFilePath();
|
|
2080
|
-
|
|
2078
|
+
function loadBotRunnerState() {
|
|
2079
|
+
const filePath = botRunnerStateFilePath();
|
|
2080
|
+
waitForBotRunnerStateLockRelease(filePath);
|
|
2081
|
+
try {
|
|
2081
2082
|
if (!fs.existsSync(filePath)) {
|
|
2082
2083
|
return {
|
|
2083
2084
|
filePath,
|
|
@@ -2127,8 +2128,97 @@ function loadBotRunnerState() {
|
|
|
2127
2128
|
remainingAnonymousKeys: [],
|
|
2128
2129
|
};
|
|
2129
2130
|
}
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
function sleepSyncMs(delayMs) {
|
|
2134
|
+
const ms = Number(delayMs) || 0;
|
|
2135
|
+
if (!(ms > 0)) {
|
|
2136
|
+
return;
|
|
2137
|
+
}
|
|
2138
|
+
const sleepBuffer = new SharedArrayBuffer(4);
|
|
2139
|
+
const sleepArray = new Int32Array(sleepBuffer);
|
|
2140
|
+
Atomics.wait(sleepArray, 0, 0, ms);
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
function botRunnerStateLockFilePath(filePath) {
|
|
2144
|
+
const normalizedPath = String(filePath || "").trim();
|
|
2145
|
+
return normalizedPath ? `${normalizedPath}.lock` : "";
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
function tryReleaseStaleBotRunnerStateLock(lockPath, staleMs = 60000) {
|
|
2149
|
+
const normalizedPath = String(lockPath || "").trim();
|
|
2150
|
+
if (!normalizedPath) {
|
|
2151
|
+
return false;
|
|
2152
|
+
}
|
|
2153
|
+
try {
|
|
2154
|
+
const stats = fs.statSync(normalizedPath);
|
|
2155
|
+
const ageMs = Date.now() - Number(stats.mtimeMs || 0);
|
|
2156
|
+
if (Number.isFinite(ageMs) && ageMs >= staleMs) {
|
|
2157
|
+
fs.rmSync(normalizedPath, { force: true });
|
|
2158
|
+
return true;
|
|
2159
|
+
}
|
|
2160
|
+
} catch {}
|
|
2161
|
+
return false;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
function waitForBotRunnerStateLockRelease(filePath, timeoutMs = 5000, staleMs = 60000) {
|
|
2165
|
+
const lockPath = botRunnerStateLockFilePath(filePath);
|
|
2166
|
+
if (!lockPath) {
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
const deadlineMs = Date.now() + Math.max(0, Number(timeoutMs) || 0);
|
|
2170
|
+
while (fs.existsSync(lockPath)) {
|
|
2171
|
+
tryReleaseStaleBotRunnerStateLock(lockPath, staleMs);
|
|
2172
|
+
if (!fs.existsSync(lockPath)) {
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
if (Date.now() >= deadlineMs) {
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
sleepSyncMs(25);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
function withBotRunnerStateFileLock(filePath, callback, timeoutMs = 5000, staleMs = 60000) {
|
|
2183
|
+
const lockPath = botRunnerStateLockFilePath(filePath);
|
|
2184
|
+
if (!lockPath) {
|
|
2185
|
+
return callback();
|
|
2186
|
+
}
|
|
2187
|
+
const deadlineMs = Date.now() + Math.max(0, Number(timeoutMs) || 0);
|
|
2188
|
+
let lockFD = null;
|
|
2189
|
+
while (lockFD === null) {
|
|
2190
|
+
try {
|
|
2191
|
+
lockFD = fs.openSync(lockPath, "wx");
|
|
2192
|
+
fs.writeFileSync(lockFD, `${process.pid} ${new Date().toISOString()}\n`, "utf8");
|
|
2193
|
+
break;
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
const errorCode = String(error?.code || "").trim().toUpperCase();
|
|
2196
|
+
if (!["EEXIST", "EPERM", "EBUSY"].includes(errorCode)) {
|
|
2197
|
+
throw error;
|
|
2198
|
+
}
|
|
2199
|
+
tryReleaseStaleBotRunnerStateLock(lockPath, staleMs);
|
|
2200
|
+
if (Date.now() >= deadlineMs) {
|
|
2201
|
+
throw error;
|
|
2202
|
+
}
|
|
2203
|
+
sleepSyncMs(25);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
try {
|
|
2207
|
+
return callback();
|
|
2208
|
+
} finally {
|
|
2209
|
+
try {
|
|
2210
|
+
if (lockFD !== null) {
|
|
2211
|
+
fs.closeSync(lockFD);
|
|
2212
|
+
}
|
|
2213
|
+
} catch {}
|
|
2214
|
+
try {
|
|
2215
|
+
if (fs.existsSync(lockPath)) {
|
|
2216
|
+
fs.rmSync(lockPath, { force: true });
|
|
2217
|
+
}
|
|
2218
|
+
} catch {}
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2132
2222
|
function writeTextFileAtomic(filePath, text) {
|
|
2133
2223
|
const normalizedPath = String(filePath || "").trim();
|
|
2134
2224
|
if (!normalizedPath) {
|
|
@@ -2141,14 +2231,12 @@ function writeTextFileAtomic(filePath, text) {
|
|
|
2141
2231
|
`.${path.basename(normalizedPath)}.${process.pid}.${Date.now()}.tmp`,
|
|
2142
2232
|
);
|
|
2143
2233
|
fs.writeFileSync(tempPath, text, "utf8");
|
|
2144
|
-
const renameRetryDelaysMs = [0, 20, 50, 100, 200];
|
|
2145
|
-
|
|
2146
|
-
const sleepArray = new Int32Array(sleepBuffer);
|
|
2147
|
-
let lastRenameError = null;
|
|
2234
|
+
const renameRetryDelaysMs = [0, 20, 50, 100, 200];
|
|
2235
|
+
let lastRenameError = null;
|
|
2148
2236
|
try {
|
|
2149
2237
|
for (const delayMs of renameRetryDelaysMs) {
|
|
2150
2238
|
if (delayMs > 0) {
|
|
2151
|
-
|
|
2239
|
+
sleepSyncMs(delayMs);
|
|
2152
2240
|
}
|
|
2153
2241
|
try {
|
|
2154
2242
|
fs.renameSync(tempPath, normalizedPath);
|
|
@@ -2190,186 +2278,188 @@ function writeTextFileAtomic(filePath, text) {
|
|
|
2190
2278
|
}
|
|
2191
2279
|
}
|
|
2192
2280
|
|
|
2193
|
-
function saveBotRunnerState(nextState) {
|
|
2194
|
-
const filePath = botRunnerStateFilePath();
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
const
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
const
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
const
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
const
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
const
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
const
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
const
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
const
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
const
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2281
|
+
function saveBotRunnerState(nextState) {
|
|
2282
|
+
const filePath = botRunnerStateFilePath();
|
|
2283
|
+
return withBotRunnerStateFileLock(filePath, () => {
|
|
2284
|
+
let current = {};
|
|
2285
|
+
try {
|
|
2286
|
+
current = safeObject(tryJsonParse(fs.readFileSync(filePath, "utf8")));
|
|
2287
|
+
} catch {}
|
|
2288
|
+
const stateEntryTimestampMs = (...values) => {
|
|
2289
|
+
for (const value of values) {
|
|
2290
|
+
const ms = Date.parse(String(value || "").trim());
|
|
2291
|
+
if (Number.isFinite(ms)) {
|
|
2292
|
+
return ms;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
return 0;
|
|
2296
|
+
};
|
|
2297
|
+
const mergeRunnerStateRoutes = (currentRoutesRaw, nextRoutesRaw) => {
|
|
2298
|
+
const currentRoutes = safeObject(currentRoutesRaw);
|
|
2299
|
+
const nextRoutes = safeObject(nextRoutesRaw);
|
|
2300
|
+
const merged = {
|
|
2301
|
+
...currentRoutes,
|
|
2302
|
+
};
|
|
2303
|
+
const mergeConversationSessions = (currentSessionsRaw, nextSessionsRaw) => {
|
|
2304
|
+
const currentSessions = safeObject(currentSessionsRaw);
|
|
2305
|
+
const nextSessions = safeObject(nextSessionsRaw);
|
|
2306
|
+
const mergedSessions = {
|
|
2307
|
+
...currentSessions,
|
|
2308
|
+
};
|
|
2309
|
+
for (const [conversationID, nextSessionRaw] of Object.entries(nextSessions)) {
|
|
2310
|
+
const currentSession = safeObject(currentSessions[conversationID]);
|
|
2311
|
+
const nextSession = safeObject(nextSessionRaw);
|
|
2312
|
+
if (!Object.keys(currentSession).length) {
|
|
2313
|
+
mergedSessions[conversationID] = nextSession;
|
|
2314
|
+
continue;
|
|
2315
|
+
}
|
|
2316
|
+
const currentMs = stateEntryTimestampMs(
|
|
2317
|
+
currentSession.updated_at,
|
|
2318
|
+
currentSession.last_activity_at,
|
|
2319
|
+
currentSession.closed_at,
|
|
2320
|
+
currentSession.started_at,
|
|
2321
|
+
currentSession.expires_at,
|
|
2322
|
+
);
|
|
2323
|
+
const nextMs = stateEntryTimestampMs(
|
|
2324
|
+
nextSession.updated_at,
|
|
2325
|
+
nextSession.last_activity_at,
|
|
2326
|
+
nextSession.closed_at,
|
|
2327
|
+
nextSession.started_at,
|
|
2328
|
+
nextSession.expires_at,
|
|
2329
|
+
);
|
|
2330
|
+
mergedSessions[conversationID] = nextMs >= currentMs
|
|
2331
|
+
? {
|
|
2332
|
+
...currentSession,
|
|
2333
|
+
...nextSession,
|
|
2334
|
+
}
|
|
2335
|
+
: {
|
|
2336
|
+
...nextSession,
|
|
2337
|
+
...currentSession,
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
return mergedSessions;
|
|
2341
|
+
};
|
|
2342
|
+
for (const [routeKey, nextRouteRaw] of Object.entries(nextRoutes)) {
|
|
2343
|
+
const currentRoute = safeObject(currentRoutes[routeKey]);
|
|
2344
|
+
const nextRoute = safeObject(nextRouteRaw);
|
|
2345
|
+
if (!Object.keys(currentRoute).length) {
|
|
2346
|
+
merged[routeKey] = nextRoute;
|
|
2347
|
+
continue;
|
|
2348
|
+
}
|
|
2349
|
+
const preferredRoute = prefersRunnerStateRecord(nextRoute, currentRoute) ? nextRoute : currentRoute;
|
|
2350
|
+
const fallbackRoute = preferredRoute === nextRoute ? currentRoute : nextRoute;
|
|
2351
|
+
merged[routeKey] = cleanupRunnerStateRecord({
|
|
2352
|
+
...mergeRunnerStateRecords(preferredRoute, fallbackRoute),
|
|
2353
|
+
conversation_sessions: mergeConversationSessions(
|
|
2354
|
+
currentRoute.conversation_sessions,
|
|
2355
|
+
nextRoute.conversation_sessions,
|
|
2356
|
+
),
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2359
|
+
return merged;
|
|
2360
|
+
};
|
|
2361
|
+
const mergeRunnerStateRequests = (currentRequestsRaw, nextRequestsRaw) => {
|
|
2362
|
+
const currentRequests = normalizeBotRunnerRequests(currentRequestsRaw);
|
|
2363
|
+
const nextRequests = normalizeBotRunnerRequests(nextRequestsRaw);
|
|
2364
|
+
const merged = {
|
|
2365
|
+
...currentRequests,
|
|
2366
|
+
};
|
|
2367
|
+
for (const [requestKey, nextRequestRaw] of Object.entries(nextRequests)) {
|
|
2368
|
+
const currentRequest = safeObject(currentRequests[requestKey]);
|
|
2369
|
+
const nextRequest = safeObject(nextRequestRaw);
|
|
2370
|
+
const currentMs = stateEntryTimestampMs(
|
|
2371
|
+
currentRequest.updated_at,
|
|
2372
|
+
currentRequest.completed_at,
|
|
2373
|
+
currentRequest.closed_at,
|
|
2374
|
+
currentRequest.claimed_at,
|
|
2375
|
+
);
|
|
2376
|
+
const nextMs = stateEntryTimestampMs(
|
|
2377
|
+
nextRequest.updated_at,
|
|
2378
|
+
nextRequest.completed_at,
|
|
2379
|
+
nextRequest.closed_at,
|
|
2380
|
+
nextRequest.claimed_at,
|
|
2381
|
+
);
|
|
2382
|
+
if (!Object.keys(currentRequest).length) {
|
|
2383
|
+
merged[requestKey] = nextRequest;
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
merged[requestKey] = nextMs >= currentMs
|
|
2387
|
+
? {
|
|
2388
|
+
...currentRequest,
|
|
2389
|
+
...nextRequest,
|
|
2390
|
+
}
|
|
2391
|
+
: currentRequest;
|
|
2392
|
+
}
|
|
2393
|
+
return normalizeBotRunnerRequests(merged);
|
|
2394
|
+
};
|
|
2395
|
+
const mergeRunnerStateExcludedComments = (currentExcludedRaw, nextExcludedRaw) => {
|
|
2396
|
+
const currentExcluded = normalizeBotRunnerExcludedComments(currentExcludedRaw);
|
|
2397
|
+
const nextExcluded = normalizeBotRunnerExcludedComments(nextExcludedRaw);
|
|
2398
|
+
const merged = {
|
|
2399
|
+
...currentExcluded,
|
|
2400
|
+
};
|
|
2401
|
+
for (const [commentID, nextEntryRaw] of Object.entries(nextExcluded)) {
|
|
2402
|
+
const currentEntry = safeObject(currentExcluded[commentID]);
|
|
2403
|
+
const nextEntry = safeObject(nextEntryRaw);
|
|
2404
|
+
const currentMs = stateEntryTimestampMs(currentEntry.excluded_at);
|
|
2405
|
+
const nextMs = stateEntryTimestampMs(nextEntry.excluded_at);
|
|
2406
|
+
if (!Object.keys(currentEntry).length || nextMs >= currentMs) {
|
|
2407
|
+
merged[commentID] = {
|
|
2408
|
+
...currentEntry,
|
|
2409
|
+
...nextEntry,
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
return normalizeBotRunnerExcludedComments(merged);
|
|
2414
|
+
};
|
|
2415
|
+
const mergeRunnerStateConsumedComments = (currentConsumedRaw, nextConsumedRaw) => {
|
|
2416
|
+
const currentConsumed = normalizeBotRunnerConsumedComments(currentConsumedRaw);
|
|
2417
|
+
const nextConsumed = normalizeBotRunnerConsumedComments(nextConsumedRaw);
|
|
2418
|
+
const merged = {
|
|
2419
|
+
...currentConsumed,
|
|
2420
|
+
};
|
|
2421
|
+
for (const [commentID, nextEntryRaw] of Object.entries(nextConsumed)) {
|
|
2422
|
+
const currentEntry = safeObject(currentConsumed[commentID]);
|
|
2423
|
+
const nextEntry = safeObject(nextEntryRaw);
|
|
2424
|
+
const currentMs = stateEntryTimestampMs(currentEntry.consumed_at);
|
|
2425
|
+
const nextMs = stateEntryTimestampMs(nextEntry.consumed_at);
|
|
2426
|
+
if (!Object.keys(currentEntry).length || nextMs >= currentMs) {
|
|
2427
|
+
merged[commentID] = {
|
|
2428
|
+
...currentEntry,
|
|
2429
|
+
...nextEntry,
|
|
2430
|
+
};
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
return normalizeBotRunnerConsumedComments(merged);
|
|
2434
|
+
};
|
|
2435
|
+
const payload = {
|
|
2436
|
+
version: 1,
|
|
2437
|
+
updated_at: new Date().toISOString(),
|
|
2438
|
+
routes: mergeRunnerStateRoutes(
|
|
2439
|
+
current.routes,
|
|
2440
|
+
nextState?.routes ?? current.routes,
|
|
2441
|
+
),
|
|
2442
|
+
shared_inboxes: {
|
|
2443
|
+
...safeObject(current.shared_inboxes ?? current.sharedInboxes),
|
|
2444
|
+
...safeObject(nextState?.sharedInboxes ?? nextState?.shared_inboxes),
|
|
2445
|
+
},
|
|
2446
|
+
excluded_comments: mergeRunnerStateExcludedComments(
|
|
2447
|
+
current.excluded_comments ?? current.excludedComments,
|
|
2448
|
+
nextState?.excludedComments ?? nextState?.excluded_comments ?? current.excluded_comments ?? current.excludedComments,
|
|
2449
|
+
),
|
|
2450
|
+
requests: mergeRunnerStateRequests(
|
|
2451
|
+
current.requests,
|
|
2452
|
+
nextState?.requests ?? current.requests,
|
|
2453
|
+
),
|
|
2454
|
+
consumed_comments: mergeRunnerStateConsumedComments(
|
|
2455
|
+
current.consumed_comments ?? current.consumedComments,
|
|
2456
|
+
nextState?.consumedComments ?? nextState?.consumed_comments ?? current.consumed_comments ?? current.consumedComments,
|
|
2457
|
+
),
|
|
2458
|
+
};
|
|
2459
|
+
writeTextFileAtomic(filePath, `${JSON.stringify(payload, null, 2)}\n`);
|
|
2460
|
+
return filePath;
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2373
2463
|
|
|
2374
2464
|
function normalizeBotRunnerExcludedComments(rawExcluded, nowMs = Date.now()) {
|
|
2375
2465
|
const normalized = {};
|
|
@@ -2728,30 +2818,12 @@ function normalizeBotRunnerRequests(rawRequests, nowMs = Date.now()) {
|
|
|
2728
2818
|
last_reply_message_envelope: normalizeRunnerTelegramMessageEnvelope(
|
|
2729
2819
|
entry.last_reply_message_envelope
|
|
2730
2820
|
|| entry.lastReplyMessageEnvelope
|
|
2731
|
-
||
|
|
2732
|
-
? buildTelegramBotReplyEnvelope({
|
|
2733
|
-
chatID: entry.chat_id || entry.chatID,
|
|
2734
|
-
messageID: entry.last_reply_message_id || entry.lastReplyMessageID,
|
|
2735
|
-
messageThreadID: entry.last_reply_message_thread_id || entry.lastReplyMessageThreadID,
|
|
2736
|
-
replyToMessageID: entry.last_reply_to_message_id || entry.lastReplyToMessageID,
|
|
2737
|
-
senderUsername: entry.conversation_summary_bot || ensureArray(entry.selected_bot_usernames || entry.selectedBotUsernames)[0] || "",
|
|
2738
|
-
body: entry.followup_ai_reply_preview || entry.followupAiReplyPreview || entry.ai_reply_preview || entry.aiReplyPreview,
|
|
2739
|
-
})
|
|
2740
|
-
: {}),
|
|
2821
|
+
|| {},
|
|
2741
2822
|
),
|
|
2742
2823
|
attempted_delivery_envelope: normalizeRunnerTelegramMessageEnvelope(
|
|
2743
2824
|
entry.attempted_delivery_envelope
|
|
2744
2825
|
|| entry.attemptedDeliveryEnvelope
|
|
2745
|
-
||
|
|
2746
|
-
|| intFromRawAllowZero(entry.last_reply_message_thread_id || entry.lastReplyMessageThreadID, 0) > 0)
|
|
2747
|
-
? buildTelegramBotReplyEnvelope({
|
|
2748
|
-
chatID: entry.chat_id || entry.chatID,
|
|
2749
|
-
messageThreadID: entry.last_reply_message_thread_id || entry.lastReplyMessageThreadID,
|
|
2750
|
-
replyToMessageID: entry.last_reply_to_message_id || entry.lastReplyToMessageID,
|
|
2751
|
-
senderUsername: entry.conversation_summary_bot || ensureArray(entry.selected_bot_usernames || entry.selectedBotUsernames)[0] || "",
|
|
2752
|
-
body: entry.followup_ai_reply_preview || entry.followupAiReplyPreview || entry.ai_reply_preview || entry.aiReplyPreview,
|
|
2753
|
-
})
|
|
2754
|
-
: {}),
|
|
2826
|
+
|| {},
|
|
2755
2827
|
),
|
|
2756
2828
|
updated_at: updatedAt || new Date(nowMs).toISOString(),
|
|
2757
2829
|
};
|
|
@@ -3836,20 +3908,10 @@ function buildRunnerReplyChainSnapshotFromRequestReply(requestRaw) {
|
|
|
3836
3908
|
const explicitEnvelope = normalizeRunnerTelegramMessageEnvelope(
|
|
3837
3909
|
request.last_reply_message_envelope || request.lastReplyMessageEnvelope,
|
|
3838
3910
|
);
|
|
3839
|
-
const fallbackEnvelope = intFromRawAllowZero(request.last_reply_message_id, 0) > 0
|
|
3840
|
-
? buildTelegramBotReplyEnvelope({
|
|
3841
|
-
chatID: request.chat_id,
|
|
3842
|
-
messageID: request.last_reply_message_id,
|
|
3843
|
-
messageThreadID: request.last_reply_message_thread_id,
|
|
3844
|
-
replyToMessageID: request.last_reply_to_message_id || request.last_source_message_id || request.source_message_id,
|
|
3845
|
-
senderUsername: request.conversation_summary_bot || ensureArray(request.selected_bot_usernames)[0] || "",
|
|
3846
|
-
body: preview,
|
|
3847
|
-
})
|
|
3848
|
-
: null;
|
|
3849
3911
|
return buildRunnerReplyChainSnapshotFromMessageEnvelope(
|
|
3850
3912
|
explicitEnvelope && intFromRawAllowZero(explicitEnvelope.message_id, 0) > 0
|
|
3851
3913
|
? explicitEnvelope
|
|
3852
|
-
:
|
|
3914
|
+
: null,
|
|
3853
3915
|
{
|
|
3854
3916
|
speaker_type: "bot",
|
|
3855
3917
|
speaker_label: firstNonEmptyString([
|
|
@@ -5217,15 +5279,17 @@ function buildRunnerRequestRecoveryPatchFromRouteState(currentStateRaw, requestR
|
|
|
5217
5279
|
setFollowupStringPatch("followup_archive_status", ["last_followup_archive_status"]);
|
|
5218
5280
|
setFollowupStringPatch("followup_transport_error", ["last_followup_transport_error"]);
|
|
5219
5281
|
setFollowupStringPatch("followup_archive_error", ["last_followup_archive_error"]);
|
|
5282
|
+
const routeFollowupDeliveryStatus = String(routeState.last_followup_delivery_status || "").trim().toLowerCase();
|
|
5283
|
+
const routeFollowupDelivered = ["delivered", "dry_run"].includes(routeFollowupDeliveryStatus);
|
|
5220
5284
|
if (!Object.keys(safeObject(request.source_message_envelope)).length) {
|
|
5221
5285
|
const recoveredSourceEnvelope = normalizeRunnerTelegramMessageEnvelope(routeState.last_followup_source_message_envelope);
|
|
5222
5286
|
if (Object.keys(recoveredSourceEnvelope).length) {
|
|
5223
5287
|
patch.source_message_envelope = recoveredSourceEnvelope;
|
|
5224
5288
|
}
|
|
5225
5289
|
}
|
|
5226
|
-
if (!Object.keys(safeObject(request.last_reply_message_envelope)).length) {
|
|
5290
|
+
if (!Object.keys(safeObject(request.last_reply_message_envelope)).length && routeFollowupDelivered) {
|
|
5227
5291
|
const recoveredReplyEnvelope = normalizeRunnerTelegramMessageEnvelope(routeState.last_followup_last_reply_message_envelope);
|
|
5228
|
-
if (
|
|
5292
|
+
if (intFromRawAllowZero(recoveredReplyEnvelope.message_id, 0) > 0) {
|
|
5229
5293
|
patch.last_reply_message_envelope = recoveredReplyEnvelope;
|
|
5230
5294
|
}
|
|
5231
5295
|
}
|
|
@@ -5592,6 +5656,9 @@ function markRunnerRequestLifecycle({
|
|
|
5592
5656
|
senderUsername: normalizedCurrentBotSelector,
|
|
5593
5657
|
body: aiReplyPreview,
|
|
5594
5658
|
});
|
|
5659
|
+
const normalizedDeliveryStatus = String(deliveryStatus || "").trim().toLowerCase();
|
|
5660
|
+
const persistSuccessfulReplyEnvelope = ["delivered", "dry_run"].includes(normalizedDeliveryStatus)
|
|
5661
|
+
&& intFromRawAllowZero(lastReplyMessageEnvelope.message_id, 0) > 0;
|
|
5595
5662
|
const attemptedDeliveryEnvelope = buildTelegramBotReplyEnvelope({
|
|
5596
5663
|
sourceEnvelope: sourceMessageEnvelope,
|
|
5597
5664
|
chatID: existing.chat_id,
|
|
@@ -5934,11 +6001,11 @@ function markRunnerRequestLifecycle({
|
|
|
5934
6001
|
? transportError || existing.followup_transport_error || ""
|
|
5935
6002
|
: existing.followup_transport_error || "",
|
|
5936
6003
|
).trim(),
|
|
5937
|
-
followup_archive_error: String(
|
|
5938
|
-
isFollowupComment
|
|
5939
|
-
? archiveError || existing.followup_archive_error || ""
|
|
5940
|
-
: existing.followup_archive_error || "",
|
|
5941
|
-
).trim(),
|
|
6004
|
+
followup_archive_error: String(
|
|
6005
|
+
isFollowupComment
|
|
6006
|
+
? archiveError || existing.followup_archive_error || ""
|
|
6007
|
+
: existing.followup_archive_error || "",
|
|
6008
|
+
).trim(),
|
|
5942
6009
|
normalized_intent: nextNormalizedIntent,
|
|
5943
6010
|
status: nextStatus,
|
|
5944
6011
|
started_at: firstNonEmptyString([existing.started_at, nowISO]),
|
|
@@ -5964,7 +6031,7 @@ function markRunnerRequestLifecycle({
|
|
|
5964
6031
|
last_reply_message_id: intFromRawAllowZero(lastReplyMessageID, 0) || existing.last_reply_message_id,
|
|
5965
6032
|
last_reply_message_thread_id: intFromRawAllowZero(lastReplyMessageThreadID, 0) || existing.last_reply_message_thread_id,
|
|
5966
6033
|
last_reply_to_message_id: intFromRawAllowZero(replyToMessageID, 0) || existing.last_reply_to_message_id,
|
|
5967
|
-
last_reply_message_envelope:
|
|
6034
|
+
last_reply_message_envelope: persistSuccessfulReplyEnvelope
|
|
5968
6035
|
? lastReplyMessageEnvelope
|
|
5969
6036
|
: safeObject(existing.last_reply_message_envelope),
|
|
5970
6037
|
attempted_delivery_envelope: shouldRefreshAttemptedDeliveryEnvelope
|
|
@@ -4724,14 +4724,6 @@ export async function processRunnerSelectedRecord({
|
|
|
4724
4724
|
const replyMessageThreadID = intFromRawAllowZero(sourceMessageEnvelope.message_thread_id, 0);
|
|
4725
4725
|
const replyToMessageID = intFromRawAllowZero(sourceMessageEnvelope.message_id, 0);
|
|
4726
4726
|
const replyAnchorSource = replyToMessageID > 0 ? "source_message_envelope" : "";
|
|
4727
|
-
const attemptedDeliveryEnvelope = buildTelegramBotReplyEnvelope({
|
|
4728
|
-
sourceEnvelope: sourceMessageEnvelope,
|
|
4729
|
-
messageThreadID: replyMessageThreadID,
|
|
4730
|
-
replyToMessageID,
|
|
4731
|
-
sender: bot?.username ? `@${String(bot.username || "").trim().replace(/^@+/, "")}` : String(bot?.name || "bot").trim(),
|
|
4732
|
-
senderUsername: normalizeMentionSelector(bot?.username || bot?.name),
|
|
4733
|
-
body: sanitizedReplyText,
|
|
4734
|
-
});
|
|
4735
4727
|
const normalizedPrecomputedHumanIntentContext = safeObject(precomputedHumanIntentContext);
|
|
4736
4728
|
const validateWorkspaceArtifacts = typeof executionDeps.validateWorkspaceArtifacts === "function"
|
|
4737
4729
|
? executionDeps.validateWorkspaceArtifacts
|
|
@@ -5500,6 +5492,14 @@ export async function processRunnerSelectedRecord({
|
|
|
5500
5492
|
triggerDecision: effectiveTriggerDecision,
|
|
5501
5493
|
conversationContext: effectiveConversationContext,
|
|
5502
5494
|
});
|
|
5495
|
+
const attemptedDeliveryEnvelope = buildTelegramBotReplyEnvelope({
|
|
5496
|
+
sourceEnvelope: sourceMessageEnvelope,
|
|
5497
|
+
messageThreadID: replyMessageThreadID,
|
|
5498
|
+
replyToMessageID,
|
|
5499
|
+
sender: bot?.username ? `@${String(bot.username || "").trim().replace(/^@+/, "")}` : String(bot?.name || "bot").trim(),
|
|
5500
|
+
senderUsername: normalizeMentionSelector(bot?.username || bot?.name),
|
|
5501
|
+
body: sanitizedReplyText,
|
|
5502
|
+
});
|
|
5503
5503
|
const normalizedExecutionTargets = ensureArray(executionContract?.assignments)
|
|
5504
5504
|
.map((item) => normalizeMentionSelector(item?.targetBot))
|
|
5505
5505
|
.filter(Boolean);
|