switchroom 0.13.13 → 0.13.15
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/dist/cli/switchroom.js +2 -2
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +293 -92
- package/telegram-plugin/gateway/gateway.ts +223 -17
- package/telegram-plugin/pending-work-progress.ts +377 -0
- package/telegram-plugin/runtime-metrics.ts +20 -0
- package/telegram-plugin/tests/pending-work-progress.test.ts +354 -0
- package/telegram-plugin/uat/scenarios/cross-turn-pending-progress-dm.test.ts +239 -0
- package/telegram-plugin/uat/scenarios/visible-answer-stream-dm.test.ts +219 -0
|
@@ -16565,7 +16565,7 @@ class AuthBrokerClient {
|
|
|
16565
16565
|
const id = req.id;
|
|
16566
16566
|
const frame = encodeRequest(req);
|
|
16567
16567
|
return new Promise((resolve2, reject) => {
|
|
16568
|
-
const
|
|
16568
|
+
const timer3 = setTimeout(() => {
|
|
16569
16569
|
this.pending.delete(id);
|
|
16570
16570
|
reject(new AuthBrokerUnreachableError(`request ${req.op} timed out after ${this.timeoutMs}ms`, this.socketPath));
|
|
16571
16571
|
}, this.timeoutMs);
|
|
@@ -16578,7 +16578,7 @@ class AuthBrokerClient {
|
|
|
16578
16578
|
}
|
|
16579
16579
|
},
|
|
16580
16580
|
reject,
|
|
16581
|
-
timer:
|
|
16581
|
+
timer: timer3
|
|
16582
16582
|
});
|
|
16583
16583
|
sock.write(frame, (err) => {
|
|
16584
16584
|
if (err) {
|
|
@@ -25066,12 +25066,12 @@ async function rpc(req, opts) {
|
|
|
25066
25066
|
resolve5(val);
|
|
25067
25067
|
};
|
|
25068
25068
|
const client3 = new net3.Socket;
|
|
25069
|
-
const
|
|
25069
|
+
const timer3 = setTimeout(() => {
|
|
25070
25070
|
client3.destroy();
|
|
25071
25071
|
settle({ kind: "unreachable", msg: `broker did not respond within ${timeoutMs}ms` });
|
|
25072
25072
|
}, timeoutMs);
|
|
25073
25073
|
client3.on("error", (err) => {
|
|
25074
|
-
clearTimeout(
|
|
25074
|
+
clearTimeout(timer3);
|
|
25075
25075
|
const code = err.code ?? "ERR";
|
|
25076
25076
|
let msg;
|
|
25077
25077
|
if (code === "ENOENT")
|
|
@@ -25091,7 +25091,7 @@ async function rpc(req, opts) {
|
|
|
25091
25091
|
`);
|
|
25092
25092
|
if (newlineIdx !== -1) {
|
|
25093
25093
|
const line = buffer.slice(0, newlineIdx).trimEnd();
|
|
25094
|
-
clearTimeout(
|
|
25094
|
+
clearTimeout(timer3);
|
|
25095
25095
|
client3.destroy();
|
|
25096
25096
|
try {
|
|
25097
25097
|
const resp = decodeResponse2(line);
|
|
@@ -25108,7 +25108,7 @@ async function rpc(req, opts) {
|
|
|
25108
25108
|
try {
|
|
25109
25109
|
client3.write(encodeRequest2(req));
|
|
25110
25110
|
} catch (err) {
|
|
25111
|
-
clearTimeout(
|
|
25111
|
+
clearTimeout(timer3);
|
|
25112
25112
|
client3.destroy();
|
|
25113
25113
|
settle({
|
|
25114
25114
|
kind: "unreachable",
|
|
@@ -27352,14 +27352,14 @@ import { join as join21 } from "path";
|
|
|
27352
27352
|
import { execFile as execFileCb } from "child_process";
|
|
27353
27353
|
import { promisify as promisify3 } from "util";
|
|
27354
27354
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
27355
|
-
let
|
|
27355
|
+
let timer3;
|
|
27356
27356
|
const timeout = new Promise((resolve6) => {
|
|
27357
|
-
|
|
27357
|
+
timer3 = setTimeout(() => resolve6({ status: "fail", label, detail: "timed out" }), timeoutMs);
|
|
27358
27358
|
});
|
|
27359
27359
|
try {
|
|
27360
27360
|
return await Promise.race([p, timeout]);
|
|
27361
27361
|
} finally {
|
|
27362
|
-
clearTimeout(
|
|
27362
|
+
clearTimeout(timer3);
|
|
27363
27363
|
}
|
|
27364
27364
|
}
|
|
27365
27365
|
function formatMs(ms) {
|
|
@@ -28163,7 +28163,7 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
28163
28163
|
const snoozeBoots = opts.snoozeBoots ?? DEFAULT_SNOOZE_BOOTS;
|
|
28164
28164
|
const snoozeMs = opts.snoozeMs ?? DEFAULT_SNOOZE_MS;
|
|
28165
28165
|
const now = opts.now ?? Date.now;
|
|
28166
|
-
const
|
|
28166
|
+
const nowMs2 = now();
|
|
28167
28167
|
const out = {};
|
|
28168
28168
|
for (const [key, r] of Object.entries(probes)) {
|
|
28169
28169
|
if (!r)
|
|
@@ -28182,12 +28182,12 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
28182
28182
|
continue;
|
|
28183
28183
|
}
|
|
28184
28184
|
let consecutiveBoots = 1;
|
|
28185
|
-
let firstSeenMs =
|
|
28185
|
+
let firstSeenMs = nowMs2;
|
|
28186
28186
|
if (prev != null && prev.fingerprint === fp) {
|
|
28187
28187
|
consecutiveBoots = prev.consecutiveBoots + 1;
|
|
28188
28188
|
firstSeenMs = prev.firstSeenMs;
|
|
28189
28189
|
}
|
|
28190
|
-
const ageMs =
|
|
28190
|
+
const ageMs = nowMs2 - firstSeenMs;
|
|
28191
28191
|
const snoozed = consecutiveBoots > snoozeBoots || ageMs >= snoozeMs;
|
|
28192
28192
|
out[key] = {
|
|
28193
28193
|
fingerprint: fp,
|
|
@@ -28198,7 +28198,7 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
28198
28198
|
fingerprint: fp,
|
|
28199
28199
|
consecutiveBoots,
|
|
28200
28200
|
firstSeenMs,
|
|
28201
|
-
lastSeenMs:
|
|
28201
|
+
lastSeenMs: nowMs2
|
|
28202
28202
|
}
|
|
28203
28203
|
};
|
|
28204
28204
|
}
|
|
@@ -37296,6 +37296,144 @@ function startTimer(deps) {
|
|
|
37296
37296
|
timer.unref();
|
|
37297
37297
|
}
|
|
37298
37298
|
|
|
37299
|
+
// pending-work-progress.ts
|
|
37300
|
+
var EDIT_INTERVAL_MS = 60000;
|
|
37301
|
+
var POLL_INTERVAL_MS = 5000;
|
|
37302
|
+
var MAX_LIFETIME_MS = 30 * 60000;
|
|
37303
|
+
var TELEGRAM_MSG_CAP = 4000;
|
|
37304
|
+
var SUFFIX_RE = /\n\n\u2014 still working \(\d+m\)$/;
|
|
37305
|
+
var stateByKey = new Map;
|
|
37306
|
+
var timer2 = null;
|
|
37307
|
+
var activeDeps2 = null;
|
|
37308
|
+
function enabled2() {
|
|
37309
|
+
const v = process.env.SWITCHROOM_DISABLE_PENDING_PROGRESS;
|
|
37310
|
+
return !(v === "1" || v === "true");
|
|
37311
|
+
}
|
|
37312
|
+
function nowMs() {
|
|
37313
|
+
return activeDeps2?.nowMs ? activeDeps2.nowMs() : Date.now();
|
|
37314
|
+
}
|
|
37315
|
+
function ensure(key) {
|
|
37316
|
+
let s = stateByKey.get(key);
|
|
37317
|
+
if (!s) {
|
|
37318
|
+
s = {
|
|
37319
|
+
pending: false,
|
|
37320
|
+
anchorMessageId: null,
|
|
37321
|
+
anchorOriginalText: "",
|
|
37322
|
+
activatedAt: null,
|
|
37323
|
+
lastEditAt: null
|
|
37324
|
+
};
|
|
37325
|
+
stateByKey.set(key, s);
|
|
37326
|
+
}
|
|
37327
|
+
return s;
|
|
37328
|
+
}
|
|
37329
|
+
function noteAsyncDispatch(key) {
|
|
37330
|
+
if (!enabled2())
|
|
37331
|
+
return;
|
|
37332
|
+
ensure(key).pending = true;
|
|
37333
|
+
}
|
|
37334
|
+
function noteOutbound3(key, opts) {
|
|
37335
|
+
if (!enabled2())
|
|
37336
|
+
return;
|
|
37337
|
+
const s = ensure(key);
|
|
37338
|
+
s.anchorMessageId = opts.messageId;
|
|
37339
|
+
s.anchorOriginalText = opts.text.replace(SUFFIX_RE, "");
|
|
37340
|
+
}
|
|
37341
|
+
function noteTurnEnd(key) {
|
|
37342
|
+
if (!enabled2())
|
|
37343
|
+
return;
|
|
37344
|
+
const s = stateByKey.get(key);
|
|
37345
|
+
if (s == null)
|
|
37346
|
+
return;
|
|
37347
|
+
if (s.pending && s.anchorMessageId != null) {
|
|
37348
|
+
s.activatedAt = nowMs();
|
|
37349
|
+
s.lastEditAt = s.activatedAt;
|
|
37350
|
+
activeDeps2?.emitMetric?.({
|
|
37351
|
+
kind: "pending_progress_started",
|
|
37352
|
+
chatKey: key
|
|
37353
|
+
});
|
|
37354
|
+
} else {
|
|
37355
|
+
stateByKey.delete(key);
|
|
37356
|
+
}
|
|
37357
|
+
}
|
|
37358
|
+
function clearPending(key, reason) {
|
|
37359
|
+
if (!stateByKey.has(key))
|
|
37360
|
+
return;
|
|
37361
|
+
const s = stateByKey.get(key);
|
|
37362
|
+
const elapsed = s.activatedAt != null ? nowMs() - s.activatedAt : 0;
|
|
37363
|
+
stateByKey.delete(key);
|
|
37364
|
+
activeDeps2?.emitMetric?.({
|
|
37365
|
+
kind: "pending_progress_cleared",
|
|
37366
|
+
chatKey: key,
|
|
37367
|
+
elapsedMs: elapsed,
|
|
37368
|
+
reason
|
|
37369
|
+
});
|
|
37370
|
+
}
|
|
37371
|
+
function startTimer2(deps) {
|
|
37372
|
+
if (!enabled2())
|
|
37373
|
+
return;
|
|
37374
|
+
if (timer2 != null)
|
|
37375
|
+
return;
|
|
37376
|
+
activeDeps2 = deps;
|
|
37377
|
+
const interval = deps.pollIntervalMs ?? POLL_INTERVAL_MS;
|
|
37378
|
+
timer2 = setInterval(() => tick2(nowMs()), interval);
|
|
37379
|
+
if (typeof timer2.unref === "function")
|
|
37380
|
+
timer2.unref();
|
|
37381
|
+
}
|
|
37382
|
+
function parseKey2(key) {
|
|
37383
|
+
const idx = key.indexOf(":");
|
|
37384
|
+
if (idx < 0)
|
|
37385
|
+
return { chatId: key, threadId: null };
|
|
37386
|
+
const chatId = key.slice(0, idx);
|
|
37387
|
+
const tail = key.slice(idx + 1);
|
|
37388
|
+
if (tail === "" || tail === "undefined")
|
|
37389
|
+
return { chatId, threadId: null };
|
|
37390
|
+
const n = Number(tail);
|
|
37391
|
+
return { chatId, threadId: Number.isFinite(n) ? n : null };
|
|
37392
|
+
}
|
|
37393
|
+
function tick2(now) {
|
|
37394
|
+
if (activeDeps2 == null)
|
|
37395
|
+
return;
|
|
37396
|
+
for (const [key, s] of stateByKey.entries()) {
|
|
37397
|
+
if (s.activatedAt == null || s.anchorMessageId == null)
|
|
37398
|
+
continue;
|
|
37399
|
+
const elapsed = now - s.activatedAt;
|
|
37400
|
+
if (elapsed >= MAX_LIFETIME_MS) {
|
|
37401
|
+
clearPending(key, "timeout");
|
|
37402
|
+
continue;
|
|
37403
|
+
}
|
|
37404
|
+
const sinceEdit = s.lastEditAt == null ? 0 : now - s.lastEditAt;
|
|
37405
|
+
if (sinceEdit < EDIT_INTERVAL_MS)
|
|
37406
|
+
continue;
|
|
37407
|
+
const minutes = Math.max(1, Math.round(elapsed / 60000));
|
|
37408
|
+
const suffix = `
|
|
37409
|
+
|
|
37410
|
+
\u2014 still working (${minutes}m)`;
|
|
37411
|
+
const newText = s.anchorOriginalText + suffix;
|
|
37412
|
+
if (newText.length > TELEGRAM_MSG_CAP) {
|
|
37413
|
+
s.lastEditAt = now;
|
|
37414
|
+
continue;
|
|
37415
|
+
}
|
|
37416
|
+
const { chatId, threadId } = parseKey2(key);
|
|
37417
|
+
s.lastEditAt = now;
|
|
37418
|
+
const editCtx = {
|
|
37419
|
+
chatId,
|
|
37420
|
+
threadId,
|
|
37421
|
+
messageId: s.anchorMessageId,
|
|
37422
|
+
newText
|
|
37423
|
+
};
|
|
37424
|
+
Promise.resolve().then(() => activeDeps2.editMessage(editCtx)).then(() => {
|
|
37425
|
+
activeDeps2.emitMetric?.({
|
|
37426
|
+
kind: "pending_progress_edited",
|
|
37427
|
+
chatKey: key,
|
|
37428
|
+
elapsedMs: elapsed
|
|
37429
|
+
});
|
|
37430
|
+
}).catch((err) => {
|
|
37431
|
+
process.stderr.write(`pending-work-progress: edit failed key=${key} ` + `msg=${editCtx.messageId}: ${err.message}
|
|
37432
|
+
`);
|
|
37433
|
+
});
|
|
37434
|
+
}
|
|
37435
|
+
}
|
|
37436
|
+
|
|
37299
37437
|
// silent-end.ts
|
|
37300
37438
|
import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync, mkdirSync as mkdirSync5 } from "node:fs";
|
|
37301
37439
|
import { dirname as dirname6, join as join6 } from "node:path";
|
|
@@ -38956,7 +39094,7 @@ class AuthBrokerClient2 {
|
|
|
38956
39094
|
const id = req.id;
|
|
38957
39095
|
const frame = encodeRequest(req);
|
|
38958
39096
|
return new Promise((resolve2, reject) => {
|
|
38959
|
-
const
|
|
39097
|
+
const timer3 = setTimeout(() => {
|
|
38960
39098
|
this.pending.delete(id);
|
|
38961
39099
|
reject(new AuthBrokerUnreachableError2(`request ${req.op} timed out after ${this.timeoutMs}ms`, this.socketPath));
|
|
38962
39100
|
}, this.timeoutMs);
|
|
@@ -38969,7 +39107,7 @@ class AuthBrokerClient2 {
|
|
|
38969
39107
|
}
|
|
38970
39108
|
},
|
|
38971
39109
|
reject,
|
|
38972
|
-
timer:
|
|
39110
|
+
timer: timer3
|
|
38973
39111
|
});
|
|
38974
39112
|
sock.write(frame, (err) => {
|
|
38975
39113
|
if (err) {
|
|
@@ -39122,7 +39260,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
39122
39260
|
cleanup();
|
|
39123
39261
|
reject(new Error(`claude setup-token did not print an OAuth URL within ${urlTimeoutMs}ms`));
|
|
39124
39262
|
}, urlTimeoutMs);
|
|
39125
|
-
const
|
|
39263
|
+
const tick3 = setInterval(() => {
|
|
39126
39264
|
const url = parseSetupTokenUrl(buffer);
|
|
39127
39265
|
if (url) {
|
|
39128
39266
|
cleanup();
|
|
@@ -39136,7 +39274,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
39136
39274
|
child.once("exit", onExit);
|
|
39137
39275
|
function cleanup() {
|
|
39138
39276
|
clearTimeout(deadline);
|
|
39139
|
-
clearInterval(
|
|
39277
|
+
clearInterval(tick3);
|
|
39140
39278
|
child.removeListener("exit", onExit);
|
|
39141
39279
|
}
|
|
39142
39280
|
}).catch((err) => {
|
|
@@ -39794,7 +39932,7 @@ function startRestartWatchdog(config) {
|
|
|
39794
39932
|
}
|
|
39795
39933
|
const unit = `switchroom-${agentName3}`;
|
|
39796
39934
|
let snapshot = null;
|
|
39797
|
-
function
|
|
39935
|
+
function tick3() {
|
|
39798
39936
|
let raw;
|
|
39799
39937
|
try {
|
|
39800
39938
|
raw = execShow(unit);
|
|
@@ -39824,14 +39962,14 @@ function startRestartWatchdog(config) {
|
|
|
39824
39962
|
log?.(`restart-watchdog: tick agent=${agentName3} ${decision.detail}`);
|
|
39825
39963
|
}
|
|
39826
39964
|
}
|
|
39827
|
-
|
|
39828
|
-
const
|
|
39829
|
-
if (typeof
|
|
39830
|
-
|
|
39965
|
+
tick3();
|
|
39966
|
+
const timer3 = setInterval(tick3, pollIntervalMs);
|
|
39967
|
+
if (typeof timer3.unref === "function")
|
|
39968
|
+
timer3.unref();
|
|
39831
39969
|
log?.(`restart-watchdog: started agent=${agentName3} unit=${unit} interval=${pollIntervalMs}ms`);
|
|
39832
39970
|
return {
|
|
39833
39971
|
stop() {
|
|
39834
|
-
clearInterval(
|
|
39972
|
+
clearInterval(timer3);
|
|
39835
39973
|
}
|
|
39836
39974
|
};
|
|
39837
39975
|
}
|
|
@@ -41186,11 +41324,11 @@ class BrokerCredentialsExpiredError extends Error {
|
|
|
41186
41324
|
account;
|
|
41187
41325
|
expiresAt;
|
|
41188
41326
|
nowMs;
|
|
41189
|
-
constructor(account, expiresAt,
|
|
41190
|
-
super(`Google credentials for account '${account}' expired at ${new Date(expiresAt).toISOString()} (now ${new Date(
|
|
41327
|
+
constructor(account, expiresAt, nowMs2) {
|
|
41328
|
+
super(`Google credentials for account '${account}' expired at ${new Date(expiresAt).toISOString()} (now ${new Date(nowMs2).toISOString()}). Re-run \`switchroom auth google account add ${account}\` to mint fresh tokens.`);
|
|
41191
41329
|
this.account = account;
|
|
41192
41330
|
this.expiresAt = expiresAt;
|
|
41193
|
-
this.nowMs =
|
|
41331
|
+
this.nowMs = nowMs2;
|
|
41194
41332
|
this.name = "BrokerCredentialsExpiredError";
|
|
41195
41333
|
}
|
|
41196
41334
|
}
|
|
@@ -41898,7 +42036,7 @@ class InjectError extends Error {
|
|
|
41898
42036
|
this.code = code;
|
|
41899
42037
|
}
|
|
41900
42038
|
}
|
|
41901
|
-
var
|
|
42039
|
+
var POLL_INTERVAL_MS2 = 150;
|
|
41902
42040
|
var OUTPUT_BYTE_CAP = 3000;
|
|
41903
42041
|
function validateInjectCommand(command) {
|
|
41904
42042
|
if (typeof command !== "string" || command.trim().length === 0) {
|
|
@@ -42067,12 +42205,12 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
42067
42205
|
let last = before;
|
|
42068
42206
|
let stableSince = null;
|
|
42069
42207
|
while (Date.now() - start < timeoutMs) {
|
|
42070
|
-
await sleep(
|
|
42208
|
+
await sleep(POLL_INTERVAL_MS2);
|
|
42071
42209
|
const cur = runner.capture(socket, session) ?? "";
|
|
42072
42210
|
if (cur === last && cur !== before) {
|
|
42073
42211
|
if (stableSince === null) {
|
|
42074
42212
|
stableSince = Date.now();
|
|
42075
|
-
} else if (Date.now() - stableSince >=
|
|
42213
|
+
} else if (Date.now() - stableSince >= POLL_INTERVAL_MS2) {
|
|
42076
42214
|
last = cur;
|
|
42077
42215
|
break;
|
|
42078
42216
|
}
|
|
@@ -43021,7 +43159,7 @@ async function hostdRequest(opts, req) {
|
|
|
43021
43159
|
const socket = connect(opts.socketPath);
|
|
43022
43160
|
let buf = "";
|
|
43023
43161
|
let settled = false;
|
|
43024
|
-
const
|
|
43162
|
+
const timer3 = setTimeout(() => {
|
|
43025
43163
|
if (settled)
|
|
43026
43164
|
return;
|
|
43027
43165
|
settled = true;
|
|
@@ -43035,7 +43173,7 @@ async function hostdRequest(opts, req) {
|
|
|
43035
43173
|
if (settled)
|
|
43036
43174
|
return;
|
|
43037
43175
|
settled = true;
|
|
43038
|
-
clearTimeout(
|
|
43176
|
+
clearTimeout(timer3);
|
|
43039
43177
|
socket.destroy();
|
|
43040
43178
|
reject(err);
|
|
43041
43179
|
}
|
|
@@ -43046,7 +43184,7 @@ async function hostdRequest(opts, req) {
|
|
|
43046
43184
|
if (settled)
|
|
43047
43185
|
return;
|
|
43048
43186
|
settled = true;
|
|
43049
|
-
clearTimeout(
|
|
43187
|
+
clearTimeout(timer3);
|
|
43050
43188
|
socket.destroy();
|
|
43051
43189
|
reject(new Error("hostd: response exceeded frame budget"));
|
|
43052
43190
|
return;
|
|
@@ -43061,14 +43199,14 @@ async function hostdRequest(opts, req) {
|
|
|
43061
43199
|
if (settled)
|
|
43062
43200
|
return;
|
|
43063
43201
|
settled = true;
|
|
43064
|
-
clearTimeout(
|
|
43202
|
+
clearTimeout(timer3);
|
|
43065
43203
|
socket.end();
|
|
43066
43204
|
resolve6(resp);
|
|
43067
43205
|
} catch (err) {
|
|
43068
43206
|
if (settled)
|
|
43069
43207
|
return;
|
|
43070
43208
|
settled = true;
|
|
43071
|
-
clearTimeout(
|
|
43209
|
+
clearTimeout(timer3);
|
|
43072
43210
|
socket.destroy();
|
|
43073
43211
|
reject(new Error(`hostd: bad response frame: ${err.message}`, { cause: err }));
|
|
43074
43212
|
}
|
|
@@ -43077,14 +43215,14 @@ async function hostdRequest(opts, req) {
|
|
|
43077
43215
|
if (settled)
|
|
43078
43216
|
return;
|
|
43079
43217
|
settled = true;
|
|
43080
|
-
clearTimeout(
|
|
43218
|
+
clearTimeout(timer3);
|
|
43081
43219
|
reject(err);
|
|
43082
43220
|
});
|
|
43083
43221
|
socket.on("close", () => {
|
|
43084
43222
|
if (settled)
|
|
43085
43223
|
return;
|
|
43086
43224
|
settled = true;
|
|
43087
|
-
clearTimeout(
|
|
43225
|
+
clearTimeout(timer3);
|
|
43088
43226
|
reject(new Error("hostd: connection closed before response"));
|
|
43089
43227
|
});
|
|
43090
43228
|
});
|
|
@@ -44448,9 +44586,9 @@ function transition(state3, event) {
|
|
|
44448
44586
|
|
|
44449
44587
|
// gateway/inbound-delivery-machine-shadow.ts
|
|
44450
44588
|
var state3 = initialState();
|
|
44451
|
-
var
|
|
44589
|
+
var enabled3 = process.env.SWITCHROOM_DELIVERY_MACHINE_SHADOW !== "0";
|
|
44452
44590
|
function shadowEmit(event) {
|
|
44453
|
-
if (!
|
|
44591
|
+
if (!enabled3)
|
|
44454
44592
|
return [];
|
|
44455
44593
|
try {
|
|
44456
44594
|
const result = transition(state3, event);
|
|
@@ -44508,12 +44646,12 @@ function redeliverBufferedInbound2(buffer, agent, send, spool) {
|
|
|
44508
44646
|
}
|
|
44509
44647
|
|
|
44510
44648
|
// gateway/inbound-delivery-machine-dispatch.ts
|
|
44511
|
-
var
|
|
44649
|
+
var enabled4 = process.env.SWITCHROOM_DELIVERY_MACHINE_CUTOVER !== "0";
|
|
44512
44650
|
function isDispatchEnabled() {
|
|
44513
|
-
return
|
|
44651
|
+
return enabled4;
|
|
44514
44652
|
}
|
|
44515
44653
|
function dispatchEffects(effects, ctx) {
|
|
44516
|
-
if (!
|
|
44654
|
+
if (!enabled4)
|
|
44517
44655
|
return;
|
|
44518
44656
|
for (const effect of effects) {
|
|
44519
44657
|
dispatchOne(effect, ctx);
|
|
@@ -44822,10 +44960,10 @@ function createPollHealthCheck(options) {
|
|
|
44822
44960
|
setIntervalFn = setInterval,
|
|
44823
44961
|
clearIntervalFn = clearInterval
|
|
44824
44962
|
} = options;
|
|
44825
|
-
let
|
|
44963
|
+
let timer3 = null;
|
|
44826
44964
|
let failures = 0;
|
|
44827
44965
|
let active = false;
|
|
44828
|
-
async function
|
|
44966
|
+
async function tick3() {
|
|
44829
44967
|
try {
|
|
44830
44968
|
await ping();
|
|
44831
44969
|
if (failures > 0) {
|
|
@@ -44849,9 +44987,9 @@ function createPollHealthCheck(options) {
|
|
|
44849
44987
|
}
|
|
44850
44988
|
function doStop() {
|
|
44851
44989
|
active = false;
|
|
44852
|
-
if (
|
|
44853
|
-
clearIntervalFn(
|
|
44854
|
-
|
|
44990
|
+
if (timer3 !== null) {
|
|
44991
|
+
clearIntervalFn(timer3);
|
|
44992
|
+
timer3 = null;
|
|
44855
44993
|
}
|
|
44856
44994
|
}
|
|
44857
44995
|
return {
|
|
@@ -44860,11 +44998,11 @@ function createPollHealthCheck(options) {
|
|
|
44860
44998
|
return;
|
|
44861
44999
|
active = true;
|
|
44862
45000
|
failures = 0;
|
|
44863
|
-
|
|
44864
|
-
|
|
45001
|
+
timer3 = setIntervalFn(() => {
|
|
45002
|
+
tick3();
|
|
44865
45003
|
}, intervalMs);
|
|
44866
|
-
if (typeof
|
|
44867
|
-
|
|
45004
|
+
if (typeof timer3?.unref === "function") {
|
|
45005
|
+
timer3.unref();
|
|
44868
45006
|
}
|
|
44869
45007
|
log(`telegram gateway: poll.health_check started interval=${intervalMs}ms threshold=${failureThreshold}`);
|
|
44870
45008
|
},
|
|
@@ -47603,7 +47741,7 @@ function startIssuesWatcher(opts) {
|
|
|
47603
47741
|
log(`issues-watcher: refresh failed: ${err.message}`);
|
|
47604
47742
|
}
|
|
47605
47743
|
}
|
|
47606
|
-
async function
|
|
47744
|
+
async function tick3() {
|
|
47607
47745
|
if (stopped)
|
|
47608
47746
|
return;
|
|
47609
47747
|
const signature = signatureProvider(path);
|
|
@@ -47612,25 +47750,25 @@ function startIssuesWatcher(opts) {
|
|
|
47612
47750
|
lastSignature = signature;
|
|
47613
47751
|
await readAndRefresh();
|
|
47614
47752
|
}
|
|
47615
|
-
|
|
47753
|
+
tick3().catch((err) => {
|
|
47616
47754
|
log(`issues-watcher: initial tick failed: ${err.message}`);
|
|
47617
47755
|
});
|
|
47618
|
-
const
|
|
47619
|
-
|
|
47756
|
+
const timer3 = setIntervalFn(() => {
|
|
47757
|
+
tick3().catch((err) => {
|
|
47620
47758
|
log(`issues-watcher: tick failed: ${err.message}`);
|
|
47621
47759
|
});
|
|
47622
47760
|
}, intervalMs);
|
|
47623
|
-
if (typeof
|
|
47624
|
-
|
|
47761
|
+
if (typeof timer3.unref === "function") {
|
|
47762
|
+
timer3.unref();
|
|
47625
47763
|
}
|
|
47626
47764
|
return {
|
|
47627
47765
|
stop() {
|
|
47628
47766
|
if (stopped)
|
|
47629
47767
|
return;
|
|
47630
47768
|
stopped = true;
|
|
47631
|
-
clearIntervalFn(
|
|
47769
|
+
clearIntervalFn(timer3);
|
|
47632
47770
|
},
|
|
47633
|
-
tick:
|
|
47771
|
+
tick: tick3
|
|
47634
47772
|
};
|
|
47635
47773
|
}
|
|
47636
47774
|
function defaultSignatureProvider(path) {
|
|
@@ -48016,11 +48154,11 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
48016
48154
|
}
|
|
48017
48155
|
|
|
48018
48156
|
// ../src/build-info.ts
|
|
48019
|
-
var VERSION = "0.13.
|
|
48020
|
-
var COMMIT_SHA = "
|
|
48021
|
-
var COMMIT_DATE = "2026-05-
|
|
48022
|
-
var LATEST_PR =
|
|
48023
|
-
var COMMITS_AHEAD_OF_TAG =
|
|
48157
|
+
var VERSION = "0.13.15";
|
|
48158
|
+
var COMMIT_SHA = "bc0b5540";
|
|
48159
|
+
var COMMIT_DATE = "2026-05-23T02:55:43Z";
|
|
48160
|
+
var LATEST_PR = 1673;
|
|
48161
|
+
var COMMITS_AHEAD_OF_TAG = 0;
|
|
48024
48162
|
|
|
48025
48163
|
// gateway/boot-version.ts
|
|
48026
48164
|
function formatRelativeAgo(iso) {
|
|
@@ -48119,12 +48257,12 @@ async function rpc2(req, opts) {
|
|
|
48119
48257
|
resolve7(val);
|
|
48120
48258
|
};
|
|
48121
48259
|
const client3 = new net4.Socket;
|
|
48122
|
-
const
|
|
48260
|
+
const timer3 = setTimeout(() => {
|
|
48123
48261
|
client3.destroy();
|
|
48124
48262
|
settle({ kind: "unreachable", msg: `broker did not respond within ${timeoutMs}ms` });
|
|
48125
48263
|
}, timeoutMs);
|
|
48126
48264
|
client3.on("error", (err) => {
|
|
48127
|
-
clearTimeout(
|
|
48265
|
+
clearTimeout(timer3);
|
|
48128
48266
|
const code = err.code ?? "ERR";
|
|
48129
48267
|
let msg;
|
|
48130
48268
|
if (code === "ENOENT")
|
|
@@ -48144,7 +48282,7 @@ async function rpc2(req, opts) {
|
|
|
48144
48282
|
`);
|
|
48145
48283
|
if (newlineIdx !== -1) {
|
|
48146
48284
|
const line = buffer.slice(0, newlineIdx).trimEnd();
|
|
48147
|
-
clearTimeout(
|
|
48285
|
+
clearTimeout(timer3);
|
|
48148
48286
|
client3.destroy();
|
|
48149
48287
|
try {
|
|
48150
48288
|
const resp = decodeResponse2(line);
|
|
@@ -48161,7 +48299,7 @@ async function rpc2(req, opts) {
|
|
|
48161
48299
|
try {
|
|
48162
48300
|
client3.write(encodeRequest2(req));
|
|
48163
48301
|
} catch (err) {
|
|
48164
|
-
clearTimeout(
|
|
48302
|
+
clearTimeout(timer3);
|
|
48165
48303
|
client3.destroy();
|
|
48166
48304
|
settle({
|
|
48167
48305
|
kind: "unreachable",
|
|
@@ -48208,13 +48346,13 @@ async function unlockViaBroker(passphrase, opts) {
|
|
|
48208
48346
|
settled = true;
|
|
48209
48347
|
resolve7(val);
|
|
48210
48348
|
};
|
|
48211
|
-
const
|
|
48349
|
+
const timer3 = setTimeout(() => {
|
|
48212
48350
|
client3.destroy();
|
|
48213
48351
|
settle({ ok: false, msg: "Timeout waiting for broker" });
|
|
48214
48352
|
}, timeoutMs);
|
|
48215
48353
|
const client3 = net4.createConnection({ path: unlockSocketPath });
|
|
48216
48354
|
client3.on("error", (err) => {
|
|
48217
|
-
clearTimeout(
|
|
48355
|
+
clearTimeout(timer3);
|
|
48218
48356
|
settle({ ok: false, msg: `Broker unreachable: ${err.message}` });
|
|
48219
48357
|
});
|
|
48220
48358
|
let buffer = "";
|
|
@@ -48224,7 +48362,7 @@ async function unlockViaBroker(passphrase, opts) {
|
|
|
48224
48362
|
`);
|
|
48225
48363
|
if (newlineIdx !== -1) {
|
|
48226
48364
|
const line = buffer.slice(0, newlineIdx).trimEnd();
|
|
48227
|
-
clearTimeout(
|
|
48365
|
+
clearTimeout(timer3);
|
|
48228
48366
|
client3.destroy();
|
|
48229
48367
|
if (line === "OK") {
|
|
48230
48368
|
settle({ ok: true });
|
|
@@ -49078,17 +49216,17 @@ async function postCompactCard(occ, cap) {
|
|
|
49078
49216
|
const messageId = sent?.message_id;
|
|
49079
49217
|
if (typeof messageId !== "number")
|
|
49080
49218
|
return;
|
|
49081
|
-
const
|
|
49219
|
+
const timer3 = setTimeout(() => {
|
|
49082
49220
|
resolveCompactCard("timeout", null);
|
|
49083
49221
|
}, COMPACT_CARD_TIMEOUT_MS);
|
|
49084
|
-
|
|
49222
|
+
timer3.unref?.();
|
|
49085
49223
|
outstandingCompactCard = {
|
|
49086
49224
|
chatId,
|
|
49087
49225
|
threadId,
|
|
49088
49226
|
messageId,
|
|
49089
49227
|
occAtStart: occ,
|
|
49090
49228
|
capAtStart: cap,
|
|
49091
|
-
timer:
|
|
49229
|
+
timer: timer3
|
|
49092
49230
|
};
|
|
49093
49231
|
} catch (err) {
|
|
49094
49232
|
process.stderr.write(`telegram gateway: proactive-compact start card failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -49719,6 +49857,7 @@ var STREAM_THROTTLE_MS_OVERRIDE = (() => {
|
|
|
49719
49857
|
return Number.isFinite(n) && n >= 0 ? n : undefined;
|
|
49720
49858
|
})();
|
|
49721
49859
|
var TURN_FLUSH_SAFETY_ENABLED = isTurnFlushSafetyEnabled();
|
|
49860
|
+
var ANSWER_STREAM_VISIBLE_ENABLED = process.env.SWITCHROOM_VISIBLE_ANSWER_STREAM === "1" || process.env.SWITCHROOM_VISIBLE_ANSWER_STREAM === "true";
|
|
49722
49861
|
var progressDriver = null;
|
|
49723
49862
|
var unpinProgressCardForChat = null;
|
|
49724
49863
|
var getPinnedProgressCardMessageId = null;
|
|
@@ -49871,6 +50010,7 @@ startTimer({
|
|
|
49871
50010
|
lastPtyPreviewByChat.delete(fbKey);
|
|
49872
50011
|
preambleSuppressor.dropNow();
|
|
49873
50012
|
endTurn(fbKey);
|
|
50013
|
+
noteTurnEnd(fbKey);
|
|
49874
50014
|
purgeReactionTracking(fbKey);
|
|
49875
50015
|
const fbExtraPurge = purgeStaleTurnsForChat(fbChatId, activeTurnStartedAt.keys(), purgeReactionTracking);
|
|
49876
50016
|
if (turnMatchesFallback && currentTurn === wedgedTurn)
|
|
@@ -49884,6 +50024,16 @@ startTimer({
|
|
|
49884
50024
|
`);
|
|
49885
50025
|
}
|
|
49886
50026
|
});
|
|
50027
|
+
startTimer2({
|
|
50028
|
+
editMessage: async (ctx) => {
|
|
50029
|
+
await swallowingApiCall(() => lockedBot.api.editMessageText(ctx.chatId, ctx.messageId, ctx.newText), {
|
|
50030
|
+
chat_id: ctx.chatId,
|
|
50031
|
+
verb: "pending-progress-edit",
|
|
50032
|
+
...ctx.threadId != null ? { threadId: ctx.threadId } : {}
|
|
50033
|
+
});
|
|
50034
|
+
},
|
|
50035
|
+
emitMetric: (event) => emitRuntimeMetric(event)
|
|
50036
|
+
});
|
|
49887
50037
|
var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
49888
50038
|
path: join32(STATE_DIR, "inbound-spool.jsonl"),
|
|
49889
50039
|
fs: {
|
|
@@ -49973,18 +50123,18 @@ var ipcServer = createIpcServer({
|
|
|
49973
50123
|
process.stderr.write(`telegram gateway: bridge-reconnect: skipping boot card (${dedupeDecision.reason})
|
|
49974
50124
|
`);
|
|
49975
50125
|
} else {
|
|
49976
|
-
const
|
|
50126
|
+
const nowMs2 = Date.now();
|
|
49977
50127
|
const marker = readRestartMarker();
|
|
49978
50128
|
const cleanMarker = readCleanShutdownMarker(GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH);
|
|
49979
50129
|
const storedSession = readSessionMarker(GATEWAY_SESSION_MARKER_PATH);
|
|
49980
|
-
const markerAgeMs = marker ?
|
|
50130
|
+
const markerAgeMs = marker ? nowMs2 - marker.ts : undefined;
|
|
49981
50131
|
if (marker) {
|
|
49982
50132
|
const ageSec = Math.max(1, Math.round((markerAgeMs ?? 0) / 1000));
|
|
49983
50133
|
process.stderr.write(`telegram gateway: bridge-reconnect: restart-marker present, chat_id=${marker.chat_id} age=${ageSec}s agent=${client3.agentName}
|
|
49984
50134
|
`);
|
|
49985
50135
|
clearRestartMarker();
|
|
49986
50136
|
}
|
|
49987
|
-
const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now:
|
|
50137
|
+
const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now: nowMs2 });
|
|
49988
50138
|
const target = resolveBootChatId(marker, markerAgeMs);
|
|
49989
50139
|
if (target) {
|
|
49990
50140
|
const { chatId, threadId, ackMsgId } = target;
|
|
@@ -50113,6 +50263,10 @@ ${reminder}
|
|
|
50113
50263
|
if (ev.toolUseId != null && ev.toolUseId.length > 0 && !isTelegramSurfaceTool(ev.toolName)) {
|
|
50114
50264
|
const label = toolLabel(ev.toolName, ev.input, undefined, ev.precomputedLabel);
|
|
50115
50265
|
noteToolStart(key, ev.toolUseId, ev.toolName, label.length > 0 ? label : null, Date.now());
|
|
50266
|
+
const evInput = ev.input;
|
|
50267
|
+
if (ev.toolName === "Agent" || ev.toolName === "Task" || ev.toolName === "Bash" && evInput?.run_in_background === true) {
|
|
50268
|
+
noteAsyncDispatch(key);
|
|
50269
|
+
}
|
|
50116
50270
|
}
|
|
50117
50271
|
} else if (ev.kind === "tool_result") {
|
|
50118
50272
|
if (ev.toolUseId != null && ev.toolUseId.length > 0) {
|
|
@@ -50649,6 +50803,15 @@ ${url}`;
|
|
|
50649
50803
|
rememberAgentButtonMeta(chat_id, keyboardMsgId, replyButtonMeta);
|
|
50650
50804
|
}
|
|
50651
50805
|
}
|
|
50806
|
+
if (sentIds.length === chunks.length && chunks.length > 0) {
|
|
50807
|
+
const anchorMsgId = sentIds[chunks.length - 1];
|
|
50808
|
+
if (typeof anchorMsgId === "number") {
|
|
50809
|
+
noteOutbound3(statusKey(chat_id, threadId), {
|
|
50810
|
+
messageId: anchorMsgId,
|
|
50811
|
+
text: chunks[chunks.length - 1]
|
|
50812
|
+
});
|
|
50813
|
+
}
|
|
50814
|
+
}
|
|
50652
50815
|
const allPhotos = files.length >= 2 && files.length <= 10 && files.every((f) => PHOTO_EXTS.has(extname(f).toLowerCase()));
|
|
50653
50816
|
const replyParams = reply_to != null && replyMode !== "off" ? { reply_parameters: { message_id: reply_to } } : {};
|
|
50654
50817
|
if (allPhotos) {
|
|
@@ -50835,6 +50998,10 @@ async function executeStreamReply(args) {
|
|
|
50835
50998
|
const sChatId = args.chat_id;
|
|
50836
50999
|
const sThreadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
50837
51000
|
outboundDedup.record(sChatId, sThreadId, args.text, Date.now());
|
|
51001
|
+
noteOutbound3(statusKey(sChatId, sThreadId), {
|
|
51002
|
+
messageId: result.messageId,
|
|
51003
|
+
text: args.text
|
|
51004
|
+
});
|
|
50838
51005
|
}
|
|
50839
51006
|
if (turn != null && isFinalAnswerReply({
|
|
50840
51007
|
text: args.text ?? "",
|
|
@@ -50996,7 +51163,7 @@ async function executeAskUser(rawArgs) {
|
|
|
50996
51163
|
return { content: [{ type: "text", text: JSON.stringify({ kind: "cancelled", reason: `send-failed: ${msg}` }) }] };
|
|
50997
51164
|
}
|
|
50998
51165
|
return new Promise((resolveTool) => {
|
|
50999
|
-
const
|
|
51166
|
+
const timer3 = setTimeout(() => {
|
|
51000
51167
|
const entry = pendingAskUser.get(askId);
|
|
51001
51168
|
pendingAskUser.delete(askId);
|
|
51002
51169
|
if (!entry)
|
|
@@ -51016,7 +51183,7 @@ async function executeAskUser(rawArgs) {
|
|
|
51016
51183
|
threadId,
|
|
51017
51184
|
messageId,
|
|
51018
51185
|
startedAt: Date.now(),
|
|
51019
|
-
timer:
|
|
51186
|
+
timer: timer3,
|
|
51020
51187
|
resolve: (outcome) => {
|
|
51021
51188
|
resolveTool({ content: [{ type: "text", text: JSON.stringify(outcome) }] });
|
|
51022
51189
|
}
|
|
@@ -51580,6 +51747,10 @@ function handleSessionEvent(ev) {
|
|
|
51580
51747
|
switch (ev.kind) {
|
|
51581
51748
|
case "enqueue": {
|
|
51582
51749
|
typingWrapper.drainAll();
|
|
51750
|
+
if (ev.chatId) {
|
|
51751
|
+
const enqThreadId = ev.threadId != null ? Number(ev.threadId) : undefined;
|
|
51752
|
+
clearPending(statusKey(ev.chatId, enqThreadId), "handback");
|
|
51753
|
+
}
|
|
51583
51754
|
if (ev.chatId) {
|
|
51584
51755
|
const prior = currentTurn;
|
|
51585
51756
|
if (prior?.answerStream != null) {
|
|
@@ -51684,7 +51855,7 @@ function handleSessionEvent(ev) {
|
|
|
51684
51855
|
chatId: turn.sessionChatId,
|
|
51685
51856
|
isPrivateChat: turn.isDm,
|
|
51686
51857
|
threadId: turn.sessionThreadId,
|
|
51687
|
-
sendMessageDraft: sendMessageDraftFn,
|
|
51858
|
+
...ANSWER_STREAM_VISIBLE_ENABLED ? { minInitialChars: 1 } : { sendMessageDraft: sendMessageDraftFn },
|
|
51688
51859
|
sendMessage: async (chatId, text, params) => {
|
|
51689
51860
|
const tid = params?.message_thread_id;
|
|
51690
51861
|
const msg = await robustApiCall(() => bot.api.sendMessage(chatId, text, {
|
|
@@ -51769,6 +51940,7 @@ function handleSessionEvent(ev) {
|
|
|
51769
51940
|
ctrl.setError();
|
|
51770
51941
|
purgeReactionTracking(ceKey);
|
|
51771
51942
|
endTurn(ceKey);
|
|
51943
|
+
noteTurnEnd(ceKey);
|
|
51772
51944
|
if (turn.answerStream != null) {
|
|
51773
51945
|
turn.answerStream.stop();
|
|
51774
51946
|
turn.answerStream = null;
|
|
@@ -51805,20 +51977,45 @@ function handleSessionEvent(ev) {
|
|
|
51805
51977
|
turn.orphanedReplyTimeoutId = null;
|
|
51806
51978
|
}
|
|
51807
51979
|
preambleSuppressor.flushNow();
|
|
51980
|
+
let streamFinalizedAsAnswer = false;
|
|
51808
51981
|
if (turn?.answerStream != null) {
|
|
51809
51982
|
const stream = turn.answerStream;
|
|
51810
|
-
|
|
51811
|
-
|
|
51812
|
-
|
|
51983
|
+
const streamedMsgId = stream.messageId();
|
|
51984
|
+
const streamedFinalText = turn.capturedText.join("").trim();
|
|
51985
|
+
if (ANSWER_STREAM_VISIBLE_ENABLED && !turn.replyCalled && streamedMsgId != null && streamedFinalText.length > 0) {
|
|
51986
|
+
turn.answerStream = null;
|
|
51987
|
+
stream.stop();
|
|
51988
|
+
streamFinalizedAsAnswer = true;
|
|
51989
|
+
turn.finalAnswerDelivered = true;
|
|
51990
|
+
try {
|
|
51991
|
+
outboundDedup.record(turn.sessionChatId, turn.sessionThreadId, streamedFinalText, Date.now());
|
|
51992
|
+
} catch {}
|
|
51993
|
+
if (HISTORY_ENABLED) {
|
|
51994
|
+
try {
|
|
51995
|
+
recordOutbound({
|
|
51996
|
+
chat_id: turn.sessionChatId,
|
|
51997
|
+
thread_id: turn.sessionThreadId ?? null,
|
|
51998
|
+
message_ids: [streamedMsgId],
|
|
51999
|
+
texts: [streamedFinalText]
|
|
52000
|
+
});
|
|
52001
|
+
} catch {}
|
|
52002
|
+
}
|
|
52003
|
+
process.stderr.write(`telegram gateway: answer-stream finalized as answer chat=${turn.sessionChatId} msg=${streamedMsgId} chars=${streamedFinalText.length}
|
|
51813
52004
|
`);
|
|
51814
|
-
}
|
|
52005
|
+
} else {
|
|
52006
|
+
turn.answerStream = null;
|
|
52007
|
+
stream.retract().catch((err) => {
|
|
52008
|
+
process.stderr.write(`telegram gateway: answer-stream retract failed: ${err instanceof Error ? err.message : String(err)}
|
|
52009
|
+
`);
|
|
52010
|
+
});
|
|
52011
|
+
}
|
|
51815
52012
|
}
|
|
51816
52013
|
if (turn == null)
|
|
51817
52014
|
return;
|
|
51818
52015
|
const chatId = turn.sessionChatId;
|
|
51819
52016
|
const threadId = turn.sessionThreadId;
|
|
51820
52017
|
const ctrl = activeStatusReactions.get(statusKey(chatId, threadId));
|
|
51821
|
-
const flushDecision = decideTurnFlush({
|
|
52018
|
+
const flushDecision = streamFinalizedAsAnswer ? { kind: "skip", reason: "reply-called" } : decideTurnFlush({
|
|
51822
52019
|
chatId: turn.sessionChatId,
|
|
51823
52020
|
replyCalled: turn.replyCalled,
|
|
51824
52021
|
capturedText: turn.capturedText,
|
|
@@ -51881,6 +52078,7 @@ function handleSessionEvent(ev) {
|
|
|
51881
52078
|
} catch {}
|
|
51882
52079
|
clear(tKey);
|
|
51883
52080
|
endTurn(tKey);
|
|
52081
|
+
noteTurnEnd(tKey);
|
|
51884
52082
|
}
|
|
51885
52083
|
lastPtyPreviewByChat.delete(statusKey(chatId, threadId));
|
|
51886
52084
|
pendingPtyPartial = null;
|
|
@@ -51907,6 +52105,7 @@ function handleSessionEvent(ev) {
|
|
|
51907
52105
|
const tKey = statusKey(chatId, threadId);
|
|
51908
52106
|
clear(tKey);
|
|
51909
52107
|
endTurn(tKey);
|
|
52108
|
+
noteTurnEnd(tKey);
|
|
51910
52109
|
}
|
|
51911
52110
|
(async () => {
|
|
51912
52111
|
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
@@ -52050,6 +52249,7 @@ function handleSessionEvent(ev) {
|
|
|
52050
52249
|
}
|
|
52051
52250
|
clear(tKey);
|
|
52052
52251
|
endTurn(tKey);
|
|
52252
|
+
noteTurnEnd(tKey);
|
|
52053
52253
|
}
|
|
52054
52254
|
lastPtyPreviewByChat.delete(statusKey(chatId, threadId));
|
|
52055
52255
|
pendingPtyPartial = null;
|
|
@@ -52749,6 +52949,7 @@ ${preBlock(write.output)}`;
|
|
|
52749
52949
|
logStreamingEvent({ kind: "inbound_ack", chatId: chat_id, messageId: msgId, ackDelayMs: Date.now() - inboundReceivedAt });
|
|
52750
52950
|
reset(statusKey(chat_id, messageThreadId), Date.now());
|
|
52751
52951
|
startTurn(statusKey(chat_id, messageThreadId), Date.now());
|
|
52952
|
+
clearPending(statusKey(chat_id, messageThreadId), "inbound");
|
|
52752
52953
|
startTurnTypingLoop(chat_id);
|
|
52753
52954
|
emitRuntimeMetric({
|
|
52754
52955
|
kind: "turn_started",
|
|
@@ -57142,21 +57343,21 @@ var didOneTimeSetup = false;
|
|
|
57142
57343
|
}
|
|
57143
57344
|
} catch {}
|
|
57144
57345
|
try {
|
|
57145
|
-
const
|
|
57346
|
+
const nowMs2 = Date.now();
|
|
57146
57347
|
const marker = readRestartMarker();
|
|
57147
57348
|
const cleanMarker = readCleanShutdownMarker(GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH);
|
|
57148
57349
|
const currentSession = { pid: process.pid, startedAtMs: GATEWAY_STARTED_AT_MS };
|
|
57149
57350
|
const storedSession = readSessionMarker(GATEWAY_SESSION_MARKER_PATH);
|
|
57150
57351
|
const isRealRestart = shouldFireRestartBanner({ stored: storedSession, current: currentSession });
|
|
57151
57352
|
if (cleanMarker) {
|
|
57152
|
-
const ageSec = Math.max(0, Math.round((
|
|
57353
|
+
const ageSec = Math.max(0, Math.round((nowMs2 - cleanMarker.ts) / 1000));
|
|
57153
57354
|
const reasonTag = cleanMarker.reason ? ` reason=${JSON.stringify(cleanMarker.reason)}` : "";
|
|
57154
|
-
const cleanFresh = shouldSuppressRecoveryBanner(cleanMarker,
|
|
57355
|
+
const cleanFresh = shouldSuppressRecoveryBanner(cleanMarker, nowMs2, DEFAULT_MAX_AGE_MS);
|
|
57155
57356
|
const isOperatorMarker = typeof cleanMarker.reason === "string" && cleanMarker.reason.startsWith("operator:");
|
|
57156
57357
|
if (cleanFresh) {
|
|
57157
57358
|
process.stderr.write(`telegram gateway: boot.clean_shutdown_detected age=${ageSec}s signal=${cleanMarker.signal}${reasonTag}
|
|
57158
57359
|
`);
|
|
57159
|
-
} else if (isOperatorMarker &&
|
|
57360
|
+
} else if (isOperatorMarker && nowMs2 - cleanMarker.ts < 300000) {
|
|
57160
57361
|
process.stderr.write(`telegram gateway: boot.clean_shutdown_marker_fresh_extended age=${ageSec}s signal=${cleanMarker.signal}${reasonTag} window=5min
|
|
57161
57362
|
`);
|
|
57162
57363
|
} else {
|
|
@@ -57165,7 +57366,7 @@ var didOneTimeSetup = false;
|
|
|
57165
57366
|
}
|
|
57166
57367
|
}
|
|
57167
57368
|
if (marker) {
|
|
57168
|
-
const ageMs =
|
|
57369
|
+
const ageMs = nowMs2 - marker.ts;
|
|
57169
57370
|
const ageSec = Math.max(1, Math.round(ageMs / 1000));
|
|
57170
57371
|
process.stderr.write(`telegram gateway: boot: restart-marker present, chat_id=${marker.chat_id} age=${ageSec}s within5min=${ageMs < 300000}
|
|
57171
57372
|
`);
|
|
@@ -57175,11 +57376,11 @@ var didOneTimeSetup = false;
|
|
|
57175
57376
|
process.stderr.write(`telegram gateway: boot: suppressed boot card \u2014 session marker matches current process (pid=${process.pid} startedAt=${GATEWAY_STARTED_AT_MS})
|
|
57176
57377
|
`);
|
|
57177
57378
|
} else {
|
|
57178
|
-
const markerAgeMs = marker ?
|
|
57179
|
-
const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now:
|
|
57379
|
+
const markerAgeMs = marker ? nowMs2 - marker.ts : undefined;
|
|
57380
|
+
const reason = determineRestartReason({ marker, cleanMarker, sessionMarker: storedSession, now: nowMs2 });
|
|
57180
57381
|
const target = resolveBootChatId(marker, markerAgeMs);
|
|
57181
57382
|
if (reason === "crash") {
|
|
57182
|
-
const cleanMarkerStale = cleanMarker ? !shouldSuppressRecoveryBanner(cleanMarker,
|
|
57383
|
+
const cleanMarkerStale = cleanMarker ? !shouldSuppressRecoveryBanner(cleanMarker, nowMs2, DEFAULT_MAX_AGE_MS) : false;
|
|
57183
57384
|
const supervisor = process.env.SWITCHROOM_RUNTIME === "docker" ? "docker compose" : "systemd";
|
|
57184
57385
|
const detailParts = [`gateway crashed and was auto-restarted by ${supervisor}`];
|
|
57185
57386
|
if (cleanMarker?.signal)
|