switchroom 0.19.15 → 0.19.16
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 +1 -1
- package/dist/host-control/main.js +1 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +30 -1
- package/telegram-plugin/dist/gateway/gateway.js +693 -433
- package/telegram-plugin/dist/server.js +30 -1
- package/telegram-plugin/gateway/background-shell-liveness.ts +65 -0
- package/telegram-plugin/gateway/gateway.ts +7 -58
- package/telegram-plugin/gateway/outbound-send-path.ts +25 -23
- package/telegram-plugin/gateway/outbox-listen-markup.ts +67 -0
- package/telegram-plugin/gateway/outbox-sweep.ts +92 -18
- package/telegram-plugin/gateway/rich-message-handler.ts +10 -4
- package/telegram-plugin/gateway/silence-poke-session-event.ts +89 -0
- package/telegram-plugin/session-tail.ts +88 -1
- package/telegram-plugin/silence-poke.ts +118 -1
- package/telegram-plugin/tests/background-shell-liveness.test.ts +72 -0
- package/telegram-plugin/tests/feed-survival.test.ts +7 -1
- package/telegram-plugin/tests/fixtures/bg-shell-liveness-3519.jsonl +3 -0
- package/telegram-plugin/tests/forwarded-rich-message-coalesce.test.ts +290 -0
- package/telegram-plugin/tests/outbox-sweep-listen-button.test.ts +253 -0
- package/telegram-plugin/tests/session-tail.test.ts +91 -1
- package/telegram-plugin/tests/silence-poke.test.ts +280 -0
- package/telegram-plugin/tests/tts-normalize.test.ts +66 -0
- package/telegram-plugin/tests/voice-normalize-text.test.ts +82 -1
- package/telegram-plugin/tts-normalize.ts +12 -0
- package/telegram-plugin/voice-normalize-text.ts +100 -0
- package/telegram-plugin/voice-ondemand.ts +71 -0
|
@@ -8869,12 +8869,12 @@ var require_mod4 = __commonJS((exports) => {
|
|
|
8869
8869
|
// flood-circuit-breaker.ts
|
|
8870
8870
|
import {
|
|
8871
8871
|
existsSync as existsSync2,
|
|
8872
|
-
readFileSync as
|
|
8873
|
-
writeFileSync as
|
|
8874
|
-
mkdirSync as
|
|
8872
|
+
readFileSync as readFileSync3,
|
|
8873
|
+
writeFileSync as writeFileSync4,
|
|
8874
|
+
mkdirSync as mkdirSync5,
|
|
8875
8875
|
chmodSync,
|
|
8876
8876
|
unlinkSync as unlinkSync2,
|
|
8877
|
-
renameSync as
|
|
8877
|
+
renameSync as renameSync5
|
|
8878
8878
|
} from "node:fs";
|
|
8879
8879
|
function floodWaitRemainingMs(state, now) {
|
|
8880
8880
|
if (!state)
|
|
@@ -8884,7 +8884,7 @@ function floodWaitRemainingMs(state, now) {
|
|
|
8884
8884
|
function readFloodStateResult(path) {
|
|
8885
8885
|
let text;
|
|
8886
8886
|
try {
|
|
8887
|
-
text =
|
|
8887
|
+
text = readFileSync3(path, "utf-8");
|
|
8888
8888
|
} catch (err) {
|
|
8889
8889
|
const code = err?.code;
|
|
8890
8890
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -13554,7 +13554,7 @@ __export(exports_tmux, {
|
|
|
13554
13554
|
captureAgentPane: () => captureAgentPane
|
|
13555
13555
|
});
|
|
13556
13556
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
13557
|
-
import { chmodSync as chmodSync2, mkdirSync as
|
|
13557
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync6, readdirSync as readdirSync2, statSync as statSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
13558
13558
|
import { resolve } from "node:path";
|
|
13559
13559
|
function captureAgentPane(opts) {
|
|
13560
13560
|
const { agentName, agentDir, reason } = opts;
|
|
@@ -13566,7 +13566,7 @@ function captureAgentPane(opts) {
|
|
|
13566
13566
|
const reasonSlug = sanitizeReason(reason);
|
|
13567
13567
|
const outPath = resolve(outDir, `${ts}-${reasonSlug}.txt`);
|
|
13568
13568
|
try {
|
|
13569
|
-
|
|
13569
|
+
mkdirSync6(outDir, { recursive: true, mode: 448 });
|
|
13570
13570
|
} catch (err) {
|
|
13571
13571
|
const msg = `mkdir crash-reports failed: ${err.message}`;
|
|
13572
13572
|
console.error(`[tmux-capture] ${agentName}: ${msg}`);
|
|
@@ -13605,7 +13605,7 @@ function captureAgentPane(opts) {
|
|
|
13605
13605
|
` + `
|
|
13606
13606
|
`;
|
|
13607
13607
|
try {
|
|
13608
|
-
|
|
13608
|
+
writeFileSync5(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
13609
13609
|
mode: 384
|
|
13610
13610
|
});
|
|
13611
13611
|
} catch (err) {
|
|
@@ -39416,13 +39416,218 @@ class VoiceOnDemandCache {
|
|
|
39416
39416
|
}
|
|
39417
39417
|
}
|
|
39418
39418
|
|
|
39419
|
+
// voice-ondemand.ts
|
|
39420
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
39421
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync as renameSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
39422
|
+
import { dirname as dirname3 } from "path";
|
|
39423
|
+
var VOICE_ONDEMAND_CALLBACK_PREFIX = "voice:";
|
|
39424
|
+
var VOICE_ONDEMAND_TTL_MS2 = 7 * 24 * 60 * 60 * 1000;
|
|
39425
|
+
var VOICE_ONDEMAND_MAX_ENTRIES2 = 500;
|
|
39426
|
+
|
|
39427
|
+
class VoiceOnDemandCache2 {
|
|
39428
|
+
store = new Map;
|
|
39429
|
+
ttlMs;
|
|
39430
|
+
maxEntries;
|
|
39431
|
+
now;
|
|
39432
|
+
persistPath;
|
|
39433
|
+
constructor(options = {}) {
|
|
39434
|
+
this.ttlMs = options.ttlMs ?? VOICE_ONDEMAND_TTL_MS2;
|
|
39435
|
+
this.maxEntries = options.maxEntries ?? VOICE_ONDEMAND_MAX_ENTRIES2;
|
|
39436
|
+
this.now = options.now ?? Date.now;
|
|
39437
|
+
this.persistPath = options.persistPath;
|
|
39438
|
+
if (this.persistPath !== undefined)
|
|
39439
|
+
this.load();
|
|
39440
|
+
}
|
|
39441
|
+
put(token, payload) {
|
|
39442
|
+
this.store.delete(token);
|
|
39443
|
+
this.store.set(token, {
|
|
39444
|
+
createdAt: this.now(),
|
|
39445
|
+
...payload,
|
|
39446
|
+
expiresAt: this.now() + this.ttlMs
|
|
39447
|
+
});
|
|
39448
|
+
while (this.store.size > this.maxEntries) {
|
|
39449
|
+
const oldest = this.store.keys().next().value;
|
|
39450
|
+
if (oldest === undefined)
|
|
39451
|
+
break;
|
|
39452
|
+
this.store.delete(oldest);
|
|
39453
|
+
}
|
|
39454
|
+
this.flush();
|
|
39455
|
+
}
|
|
39456
|
+
get(token) {
|
|
39457
|
+
const entry = this.store.get(token);
|
|
39458
|
+
if (entry == null)
|
|
39459
|
+
return null;
|
|
39460
|
+
if (entry.expiresAt <= this.now()) {
|
|
39461
|
+
this.store.delete(token);
|
|
39462
|
+
this.flush();
|
|
39463
|
+
return null;
|
|
39464
|
+
}
|
|
39465
|
+
const { text, voice, speed, filePath, telegramFileId } = entry;
|
|
39466
|
+
return {
|
|
39467
|
+
text,
|
|
39468
|
+
speed,
|
|
39469
|
+
...voice !== undefined ? { voice } : {},
|
|
39470
|
+
...filePath !== undefined ? { filePath } : {},
|
|
39471
|
+
...telegramFileId !== undefined ? { telegramFileId } : {}
|
|
39472
|
+
};
|
|
39473
|
+
}
|
|
39474
|
+
setFilePath(token, filePath) {
|
|
39475
|
+
const entry = this.store.get(token);
|
|
39476
|
+
if (entry == null || entry.expiresAt <= this.now())
|
|
39477
|
+
return;
|
|
39478
|
+
entry.filePath = filePath;
|
|
39479
|
+
this.flush();
|
|
39480
|
+
}
|
|
39481
|
+
setTelegramFileId(token, fileId) {
|
|
39482
|
+
if (fileId.length === 0)
|
|
39483
|
+
return;
|
|
39484
|
+
const entry = this.store.get(token);
|
|
39485
|
+
if (entry == null || entry.expiresAt <= this.now())
|
|
39486
|
+
return;
|
|
39487
|
+
entry.telegramFileId = fileId;
|
|
39488
|
+
this.flush();
|
|
39489
|
+
}
|
|
39490
|
+
prune(tokens) {
|
|
39491
|
+
let changed = false;
|
|
39492
|
+
for (const token of tokens) {
|
|
39493
|
+
if (this.store.delete(token))
|
|
39494
|
+
changed = true;
|
|
39495
|
+
}
|
|
39496
|
+
if (changed)
|
|
39497
|
+
this.flush();
|
|
39498
|
+
}
|
|
39499
|
+
get size() {
|
|
39500
|
+
return this.store.size;
|
|
39501
|
+
}
|
|
39502
|
+
load() {
|
|
39503
|
+
if (this.persistPath === undefined)
|
|
39504
|
+
return;
|
|
39505
|
+
let raw;
|
|
39506
|
+
try {
|
|
39507
|
+
raw = readFileSync2(this.persistPath, "utf8");
|
|
39508
|
+
} catch {
|
|
39509
|
+
return;
|
|
39510
|
+
}
|
|
39511
|
+
try {
|
|
39512
|
+
const parsed = JSON.parse(raw);
|
|
39513
|
+
if (parsed == null || parsed.version !== 1 || typeof parsed.entries !== "object")
|
|
39514
|
+
return;
|
|
39515
|
+
const nowMs = this.now();
|
|
39516
|
+
for (const [token, entry] of Object.entries(parsed.entries)) {
|
|
39517
|
+
if (entry == null || typeof entry.expiresAt !== "number" || typeof entry.text !== "string" || typeof entry.speed !== "number") {
|
|
39518
|
+
continue;
|
|
39519
|
+
}
|
|
39520
|
+
if (entry.expiresAt <= nowMs)
|
|
39521
|
+
continue;
|
|
39522
|
+
this.store.set(token, entry);
|
|
39523
|
+
}
|
|
39524
|
+
while (this.store.size > this.maxEntries) {
|
|
39525
|
+
const oldest = this.store.keys().next().value;
|
|
39526
|
+
if (oldest === undefined)
|
|
39527
|
+
break;
|
|
39528
|
+
this.store.delete(oldest);
|
|
39529
|
+
}
|
|
39530
|
+
} catch (err) {
|
|
39531
|
+
process.stderr.write(`voice-ondemand: failed to parse persisted cache ${this.persistPath}: ${String(err)}
|
|
39532
|
+
`);
|
|
39533
|
+
}
|
|
39534
|
+
}
|
|
39535
|
+
flush() {
|
|
39536
|
+
if (this.persistPath === undefined)
|
|
39537
|
+
return;
|
|
39538
|
+
const doc = {
|
|
39539
|
+
version: 1,
|
|
39540
|
+
entries: Object.fromEntries(this.store)
|
|
39541
|
+
};
|
|
39542
|
+
const tmp = `${this.persistPath}.tmp`;
|
|
39543
|
+
try {
|
|
39544
|
+
mkdirSync3(dirname3(this.persistPath), { recursive: true });
|
|
39545
|
+
writeFileSync2(tmp, JSON.stringify(doc), "utf8");
|
|
39546
|
+
renameSync3(tmp, this.persistPath);
|
|
39547
|
+
} catch (err) {
|
|
39548
|
+
process.stderr.write(`voice-ondemand: failed to persist cache ${this.persistPath}: ${String(err)}
|
|
39549
|
+
`);
|
|
39550
|
+
}
|
|
39551
|
+
}
|
|
39552
|
+
}
|
|
39553
|
+
function mintVoiceOnDemandToken() {
|
|
39554
|
+
return randomBytes2(4).toString("hex");
|
|
39555
|
+
}
|
|
39556
|
+
function isVoiceOnDemandCallback(data) {
|
|
39557
|
+
return data.startsWith(VOICE_ONDEMAND_CALLBACK_PREFIX);
|
|
39558
|
+
}
|
|
39559
|
+
function parseVoiceOnDemandToken(data) {
|
|
39560
|
+
if (!isVoiceOnDemandCallback(data))
|
|
39561
|
+
return null;
|
|
39562
|
+
const token = data.slice(VOICE_ONDEMAND_CALLBACK_PREFIX.length);
|
|
39563
|
+
return token.length > 0 ? token : null;
|
|
39564
|
+
}
|
|
39565
|
+
function buildListenKeyboard(token) {
|
|
39566
|
+
return {
|
|
39567
|
+
inline_keyboard: [
|
|
39568
|
+
[{ text: "\uD83D\uDD0A Listen", callback_data: `${VOICE_ONDEMAND_CALLBACK_PREFIX}${token}` }]
|
|
39569
|
+
]
|
|
39570
|
+
};
|
|
39571
|
+
}
|
|
39572
|
+
function mayInjectListenButton(rawKeyboard) {
|
|
39573
|
+
if (rawKeyboard == null)
|
|
39574
|
+
return true;
|
|
39575
|
+
return !rawKeyboard.some((row) => Array.isArray(row) && row.length > 0);
|
|
39576
|
+
}
|
|
39577
|
+
function planListenButton(params) {
|
|
39578
|
+
const plan = params.voiceOutPlan;
|
|
39579
|
+
const useOnDemandButton = plan != null && plan.replyMode === "on-demand" && plan.engine === "kokoro";
|
|
39580
|
+
if (!useOnDemandButton)
|
|
39581
|
+
return null;
|
|
39582
|
+
if (!(plan.ttsChunks.length > 0 && plan.ttsChunks[0].length > 0))
|
|
39583
|
+
return null;
|
|
39584
|
+
if (!mayInjectListenButton(params.rawKeyboard))
|
|
39585
|
+
return null;
|
|
39586
|
+
const token = mintVoiceOnDemandToken();
|
|
39587
|
+
return {
|
|
39588
|
+
replyMarkup: buildListenKeyboard(token),
|
|
39589
|
+
token,
|
|
39590
|
+
payload: {
|
|
39591
|
+
text: plan.ttsChunks[0],
|
|
39592
|
+
...plan.voice != null ? { voice: plan.voice } : {},
|
|
39593
|
+
speed: plan.speed
|
|
39594
|
+
}
|
|
39595
|
+
};
|
|
39596
|
+
}
|
|
39597
|
+
|
|
39598
|
+
// gateway/outbox-listen-markup.ts
|
|
39599
|
+
function makeOutboxListenMarkupResolver(deps) {
|
|
39600
|
+
return (_chatId, _threadId, text) => {
|
|
39601
|
+
const voiceOutPlan = deps.resolveVoiceOutPlan(text);
|
|
39602
|
+
const listenPlan = planListenButton({ voiceOutPlan, rawKeyboard: undefined });
|
|
39603
|
+
if (listenPlan == null)
|
|
39604
|
+
return;
|
|
39605
|
+
const payload = listenPlan.payload;
|
|
39606
|
+
deps.cachePut(listenPlan.token, payload);
|
|
39607
|
+
if (deps.eagerVoiceEnabled()) {
|
|
39608
|
+
deps.enqueuePreSynth({
|
|
39609
|
+
token: listenPlan.token,
|
|
39610
|
+
text: payload.text,
|
|
39611
|
+
...payload.voice != null ? { voice: payload.voice } : {},
|
|
39612
|
+
speed: payload.speed
|
|
39613
|
+
});
|
|
39614
|
+
}
|
|
39615
|
+
return listenPlan.replyMarkup;
|
|
39616
|
+
};
|
|
39617
|
+
}
|
|
39618
|
+
|
|
39419
39619
|
// voice-presynth.ts
|
|
39420
|
-
import { readdirSync, statSync as statSync2, unlinkSync, mkdirSync as
|
|
39620
|
+
import { readdirSync, statSync as statSync2, unlinkSync, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, renameSync as renameSync4 } from "fs";
|
|
39421
39621
|
import { join as join2 } from "path";
|
|
39422
39622
|
var VOICE_FILE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
39423
39623
|
var VOICE_CACHE_MAX_BYTES = 500 * 1024 * 1024;
|
|
39424
39624
|
var PRESYNTH_MAX_PENDING = 50;
|
|
39425
39625
|
var VOICE_SWEEP_INTERVAL_MS = 60 * 60 * 1000;
|
|
39626
|
+
function eagerVoiceEnabled() {
|
|
39627
|
+
const kill = process.env.SWITCHROOM_DISABLE_EAGER_VOICE;
|
|
39628
|
+
return !(kill === "1" || kill === "true");
|
|
39629
|
+
}
|
|
39630
|
+
|
|
39426
39631
|
class PreSynthQueue {
|
|
39427
39632
|
pending = [];
|
|
39428
39633
|
running = false;
|
|
@@ -39474,11 +39679,11 @@ function voiceCacheFilePath(dir, token) {
|
|
|
39474
39679
|
return join2(dir, `${token}.ogg`);
|
|
39475
39680
|
}
|
|
39476
39681
|
function writeVoiceCacheFile(dir, token, audio) {
|
|
39477
|
-
|
|
39682
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
39478
39683
|
const final = voiceCacheFilePath(dir, token);
|
|
39479
39684
|
const tmp = `${final}.tmp`;
|
|
39480
|
-
|
|
39481
|
-
|
|
39685
|
+
writeFileSync3(tmp, audio);
|
|
39686
|
+
renameSync4(tmp, final);
|
|
39482
39687
|
return final;
|
|
39483
39688
|
}
|
|
39484
39689
|
function sweepVoiceCacheDir(options) {
|
|
@@ -39535,6 +39740,49 @@ function sweepVoiceCacheDir(options) {
|
|
|
39535
39740
|
|
|
39536
39741
|
// voice-normalize-text.ts
|
|
39537
39742
|
var CODE_BLOCK_PLACEHOLDER = "code block omitted";
|
|
39743
|
+
var HTML_ENTITIES = {
|
|
39744
|
+
amp: "&",
|
|
39745
|
+
lt: "<",
|
|
39746
|
+
gt: ">",
|
|
39747
|
+
quot: '"',
|
|
39748
|
+
apos: "'",
|
|
39749
|
+
nbsp: " "
|
|
39750
|
+
};
|
|
39751
|
+
var METACHAR_SPOKEN = {
|
|
39752
|
+
"#": " hash ",
|
|
39753
|
+
"*": " asterisk ",
|
|
39754
|
+
_: " underscore ",
|
|
39755
|
+
"~": " tilde ",
|
|
39756
|
+
"`": " backtick ",
|
|
39757
|
+
"|": " bar "
|
|
39758
|
+
};
|
|
39759
|
+
function decodeHtmlEntitiesOnce(input) {
|
|
39760
|
+
const toChar = (cp, raw) => {
|
|
39761
|
+
if (!(cp > 0 && cp <= 1114111))
|
|
39762
|
+
return raw;
|
|
39763
|
+
const ch = String.fromCodePoint(cp);
|
|
39764
|
+
return METACHAR_SPOKEN[ch] ?? ch;
|
|
39765
|
+
};
|
|
39766
|
+
return input.replace(/&#x([0-9a-f]+);/gi, (m, hex) => toChar(parseInt(hex, 16), m)).replace(/&#(\d+);/g, (m, dec) => toChar(Number(dec), m)).replace(/&([a-z][a-z0-9]*);/gi, (m, name) => {
|
|
39767
|
+
const ch = HTML_ENTITIES[name.toLowerCase()];
|
|
39768
|
+
if (ch === undefined)
|
|
39769
|
+
return m;
|
|
39770
|
+
return METACHAR_SPOKEN[ch] ?? ch;
|
|
39771
|
+
});
|
|
39772
|
+
}
|
|
39773
|
+
function decodeHtmlEntities(input) {
|
|
39774
|
+
let s = input;
|
|
39775
|
+
for (let i = 0;i < 10; i++) {
|
|
39776
|
+
const next = decodeHtmlEntitiesOnce(s);
|
|
39777
|
+
if (next === s)
|
|
39778
|
+
break;
|
|
39779
|
+
s = next;
|
|
39780
|
+
}
|
|
39781
|
+
return s;
|
|
39782
|
+
}
|
|
39783
|
+
function stripBackslashEscapes(input) {
|
|
39784
|
+
return input.replace(/\\([\s\S])/g, "$1").replace(/\\/g, "");
|
|
39785
|
+
}
|
|
39538
39786
|
var ONES = [
|
|
39539
39787
|
"zero",
|
|
39540
39788
|
"one",
|
|
@@ -39701,6 +39949,8 @@ function normalizeForSpeech(input) {
|
|
|
39701
39949
|
return "";
|
|
39702
39950
|
let s = input.replace(/\r\n?/g, `
|
|
39703
39951
|
`);
|
|
39952
|
+
s = decodeHtmlEntities(s);
|
|
39953
|
+
s = stripBackslashEscapes(s);
|
|
39704
39954
|
s = s.replace(/[\u{1F000}-\u{1FAFF}\u{1F1E6}-\u{1F1FF}\u{2600}-\u{27BF}\u{2B00}-\u{2BFF}\u{FE00}-\u{FE0F}\u{200D}\u{2B50}\u{3030}\u{303D}\u{3297}\u{3299}\u{24C2}]/gu, "");
|
|
39705
39955
|
s = s.replace(/:([a-z0-9][a-z0-9_+-]*):/gi, " ");
|
|
39706
39956
|
s = s.replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n[ \t]*\2[ \t]*(?=\n|$)/g, `$1${CODE_BLOCK_PLACEHOLDER}.`);
|
|
@@ -39800,6 +40050,65 @@ function normalizeForSpeech(input) {
|
|
|
39800
40050
|
return s.trim();
|
|
39801
40051
|
}
|
|
39802
40052
|
|
|
40053
|
+
// voice-normalize-text.ts
|
|
40054
|
+
var HTML_ENTITIES2 = {
|
|
40055
|
+
amp: "&",
|
|
40056
|
+
lt: "<",
|
|
40057
|
+
gt: ">",
|
|
40058
|
+
quot: '"',
|
|
40059
|
+
apos: "'",
|
|
40060
|
+
nbsp: " "
|
|
40061
|
+
};
|
|
40062
|
+
var METACHAR_SPOKEN2 = {
|
|
40063
|
+
"#": " hash ",
|
|
40064
|
+
"*": " asterisk ",
|
|
40065
|
+
_: " underscore ",
|
|
40066
|
+
"~": " tilde ",
|
|
40067
|
+
"`": " backtick ",
|
|
40068
|
+
"|": " bar "
|
|
40069
|
+
};
|
|
40070
|
+
function decodeHtmlEntitiesOnce2(input) {
|
|
40071
|
+
const toChar = (cp, raw) => {
|
|
40072
|
+
if (!(cp > 0 && cp <= 1114111))
|
|
40073
|
+
return raw;
|
|
40074
|
+
const ch = String.fromCodePoint(cp);
|
|
40075
|
+
return METACHAR_SPOKEN2[ch] ?? ch;
|
|
40076
|
+
};
|
|
40077
|
+
return input.replace(/&#x([0-9a-f]+);/gi, (m, hex) => toChar(parseInt(hex, 16), m)).replace(/&#(\d+);/g, (m, dec) => toChar(Number(dec), m)).replace(/&([a-z][a-z0-9]*);/gi, (m, name) => {
|
|
40078
|
+
const ch = HTML_ENTITIES2[name.toLowerCase()];
|
|
40079
|
+
if (ch === undefined)
|
|
40080
|
+
return m;
|
|
40081
|
+
return METACHAR_SPOKEN2[ch] ?? ch;
|
|
40082
|
+
});
|
|
40083
|
+
}
|
|
40084
|
+
function decodeHtmlEntities2(input) {
|
|
40085
|
+
let s = input;
|
|
40086
|
+
for (let i = 0;i < 10; i++) {
|
|
40087
|
+
const next = decodeHtmlEntitiesOnce2(s);
|
|
40088
|
+
if (next === s)
|
|
40089
|
+
break;
|
|
40090
|
+
s = next;
|
|
40091
|
+
}
|
|
40092
|
+
return s;
|
|
40093
|
+
}
|
|
40094
|
+
function stripBackslashEscapes2(input) {
|
|
40095
|
+
return input.replace(/\\([\s\S])/g, "$1").replace(/\\/g, "");
|
|
40096
|
+
}
|
|
40097
|
+
var ACRONYMS2 = new Set([
|
|
40098
|
+
"CI",
|
|
40099
|
+
"PR",
|
|
40100
|
+
"API",
|
|
40101
|
+
"URL",
|
|
40102
|
+
"GPU",
|
|
40103
|
+
"CPU",
|
|
40104
|
+
"TTS",
|
|
40105
|
+
"STT",
|
|
40106
|
+
"HTTP",
|
|
40107
|
+
"JSON",
|
|
40108
|
+
"SQL",
|
|
40109
|
+
"UI"
|
|
40110
|
+
]);
|
|
40111
|
+
|
|
39803
40112
|
// tts-normalize.ts
|
|
39804
40113
|
var NULL = "\x00";
|
|
39805
40114
|
var INLINE_PH = `${NULL}TN_INLINE`;
|
|
@@ -39980,6 +40289,8 @@ function normalizeForTts(text) {
|
|
|
39980
40289
|
return text;
|
|
39981
40290
|
let s = text.replace(/\r\n?/g, `
|
|
39982
40291
|
`);
|
|
40292
|
+
s = decodeHtmlEntities2(s);
|
|
40293
|
+
s = stripBackslashEscapes2(s);
|
|
39983
40294
|
s = s.replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n[ \t]*\2[ \t]*(?=\n|$)/g, "$1code block omitted.");
|
|
39984
40295
|
s = s.replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*$/g, "$1code block omitted.");
|
|
39985
40296
|
const { parked, parts } = parkInline(s);
|
|
@@ -43629,7 +43940,7 @@ async function handleAnimationMessage(ctx, deps) {
|
|
|
43629
43940
|
}
|
|
43630
43941
|
|
|
43631
43942
|
// gateway/photo-message-handler.ts
|
|
43632
|
-
import { mkdirSync as
|
|
43943
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
43633
43944
|
|
|
43634
43945
|
// attachment-path.ts
|
|
43635
43946
|
import { join as join4, basename as basename3, resolve as resolve2, sep } from "node:path";
|
|
@@ -43690,9 +44001,9 @@ async function handlePhotoMessage(ctx, deps) {
|
|
|
43690
44001
|
fileUniqueId: best.file_unique_id,
|
|
43691
44002
|
now: Date.now()
|
|
43692
44003
|
});
|
|
43693
|
-
|
|
44004
|
+
mkdirSync7(deps.inboxDir, { recursive: true, mode: 448 });
|
|
43694
44005
|
assertInsideInbox(deps.inboxDir, dlPath);
|
|
43695
|
-
|
|
44006
|
+
writeFileSync6(dlPath, buf, { mode: 384 });
|
|
43696
44007
|
return dlPath;
|
|
43697
44008
|
} catch (err) {
|
|
43698
44009
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -44760,165 +45071,6 @@ async function handleAskCallback(ctx, data, deps) {
|
|
|
44760
45071
|
// gateway/voice-ondemand-callback-handler.ts
|
|
44761
45072
|
import { readFileSync as readFileSync8 } from "node:fs";
|
|
44762
45073
|
|
|
44763
|
-
// voice-ondemand.ts
|
|
44764
|
-
import { randomBytes as randomBytes2 } from "crypto";
|
|
44765
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync6, renameSync as renameSync5, mkdirSync as mkdirSync7 } from "fs";
|
|
44766
|
-
import { dirname as dirname3 } from "path";
|
|
44767
|
-
var VOICE_ONDEMAND_CALLBACK_PREFIX = "voice:";
|
|
44768
|
-
var VOICE_ONDEMAND_TTL_MS2 = 7 * 24 * 60 * 60 * 1000;
|
|
44769
|
-
var VOICE_ONDEMAND_MAX_ENTRIES2 = 500;
|
|
44770
|
-
|
|
44771
|
-
class VoiceOnDemandCache2 {
|
|
44772
|
-
store = new Map;
|
|
44773
|
-
ttlMs;
|
|
44774
|
-
maxEntries;
|
|
44775
|
-
now;
|
|
44776
|
-
persistPath;
|
|
44777
|
-
constructor(options = {}) {
|
|
44778
|
-
this.ttlMs = options.ttlMs ?? VOICE_ONDEMAND_TTL_MS2;
|
|
44779
|
-
this.maxEntries = options.maxEntries ?? VOICE_ONDEMAND_MAX_ENTRIES2;
|
|
44780
|
-
this.now = options.now ?? Date.now;
|
|
44781
|
-
this.persistPath = options.persistPath;
|
|
44782
|
-
if (this.persistPath !== undefined)
|
|
44783
|
-
this.load();
|
|
44784
|
-
}
|
|
44785
|
-
put(token, payload) {
|
|
44786
|
-
this.store.delete(token);
|
|
44787
|
-
this.store.set(token, {
|
|
44788
|
-
createdAt: this.now(),
|
|
44789
|
-
...payload,
|
|
44790
|
-
expiresAt: this.now() + this.ttlMs
|
|
44791
|
-
});
|
|
44792
|
-
while (this.store.size > this.maxEntries) {
|
|
44793
|
-
const oldest = this.store.keys().next().value;
|
|
44794
|
-
if (oldest === undefined)
|
|
44795
|
-
break;
|
|
44796
|
-
this.store.delete(oldest);
|
|
44797
|
-
}
|
|
44798
|
-
this.flush();
|
|
44799
|
-
}
|
|
44800
|
-
get(token) {
|
|
44801
|
-
const entry = this.store.get(token);
|
|
44802
|
-
if (entry == null)
|
|
44803
|
-
return null;
|
|
44804
|
-
if (entry.expiresAt <= this.now()) {
|
|
44805
|
-
this.store.delete(token);
|
|
44806
|
-
this.flush();
|
|
44807
|
-
return null;
|
|
44808
|
-
}
|
|
44809
|
-
const { text, voice, speed, filePath, telegramFileId } = entry;
|
|
44810
|
-
return {
|
|
44811
|
-
text,
|
|
44812
|
-
speed,
|
|
44813
|
-
...voice !== undefined ? { voice } : {},
|
|
44814
|
-
...filePath !== undefined ? { filePath } : {},
|
|
44815
|
-
...telegramFileId !== undefined ? { telegramFileId } : {}
|
|
44816
|
-
};
|
|
44817
|
-
}
|
|
44818
|
-
setFilePath(token, filePath) {
|
|
44819
|
-
const entry = this.store.get(token);
|
|
44820
|
-
if (entry == null || entry.expiresAt <= this.now())
|
|
44821
|
-
return;
|
|
44822
|
-
entry.filePath = filePath;
|
|
44823
|
-
this.flush();
|
|
44824
|
-
}
|
|
44825
|
-
setTelegramFileId(token, fileId) {
|
|
44826
|
-
if (fileId.length === 0)
|
|
44827
|
-
return;
|
|
44828
|
-
const entry = this.store.get(token);
|
|
44829
|
-
if (entry == null || entry.expiresAt <= this.now())
|
|
44830
|
-
return;
|
|
44831
|
-
entry.telegramFileId = fileId;
|
|
44832
|
-
this.flush();
|
|
44833
|
-
}
|
|
44834
|
-
prune(tokens) {
|
|
44835
|
-
let changed = false;
|
|
44836
|
-
for (const token of tokens) {
|
|
44837
|
-
if (this.store.delete(token))
|
|
44838
|
-
changed = true;
|
|
44839
|
-
}
|
|
44840
|
-
if (changed)
|
|
44841
|
-
this.flush();
|
|
44842
|
-
}
|
|
44843
|
-
get size() {
|
|
44844
|
-
return this.store.size;
|
|
44845
|
-
}
|
|
44846
|
-
load() {
|
|
44847
|
-
if (this.persistPath === undefined)
|
|
44848
|
-
return;
|
|
44849
|
-
let raw;
|
|
44850
|
-
try {
|
|
44851
|
-
raw = readFileSync4(this.persistPath, "utf8");
|
|
44852
|
-
} catch {
|
|
44853
|
-
return;
|
|
44854
|
-
}
|
|
44855
|
-
try {
|
|
44856
|
-
const parsed = JSON.parse(raw);
|
|
44857
|
-
if (parsed == null || parsed.version !== 1 || typeof parsed.entries !== "object")
|
|
44858
|
-
return;
|
|
44859
|
-
const nowMs = this.now();
|
|
44860
|
-
for (const [token, entry] of Object.entries(parsed.entries)) {
|
|
44861
|
-
if (entry == null || typeof entry.expiresAt !== "number" || typeof entry.text !== "string" || typeof entry.speed !== "number") {
|
|
44862
|
-
continue;
|
|
44863
|
-
}
|
|
44864
|
-
if (entry.expiresAt <= nowMs)
|
|
44865
|
-
continue;
|
|
44866
|
-
this.store.set(token, entry);
|
|
44867
|
-
}
|
|
44868
|
-
while (this.store.size > this.maxEntries) {
|
|
44869
|
-
const oldest = this.store.keys().next().value;
|
|
44870
|
-
if (oldest === undefined)
|
|
44871
|
-
break;
|
|
44872
|
-
this.store.delete(oldest);
|
|
44873
|
-
}
|
|
44874
|
-
} catch (err) {
|
|
44875
|
-
process.stderr.write(`voice-ondemand: failed to parse persisted cache ${this.persistPath}: ${String(err)}
|
|
44876
|
-
`);
|
|
44877
|
-
}
|
|
44878
|
-
}
|
|
44879
|
-
flush() {
|
|
44880
|
-
if (this.persistPath === undefined)
|
|
44881
|
-
return;
|
|
44882
|
-
const doc = {
|
|
44883
|
-
version: 1,
|
|
44884
|
-
entries: Object.fromEntries(this.store)
|
|
44885
|
-
};
|
|
44886
|
-
const tmp = `${this.persistPath}.tmp`;
|
|
44887
|
-
try {
|
|
44888
|
-
mkdirSync7(dirname3(this.persistPath), { recursive: true });
|
|
44889
|
-
writeFileSync6(tmp, JSON.stringify(doc), "utf8");
|
|
44890
|
-
renameSync5(tmp, this.persistPath);
|
|
44891
|
-
} catch (err) {
|
|
44892
|
-
process.stderr.write(`voice-ondemand: failed to persist cache ${this.persistPath}: ${String(err)}
|
|
44893
|
-
`);
|
|
44894
|
-
}
|
|
44895
|
-
}
|
|
44896
|
-
}
|
|
44897
|
-
function mintVoiceOnDemandToken() {
|
|
44898
|
-
return randomBytes2(4).toString("hex");
|
|
44899
|
-
}
|
|
44900
|
-
function isVoiceOnDemandCallback(data) {
|
|
44901
|
-
return data.startsWith(VOICE_ONDEMAND_CALLBACK_PREFIX);
|
|
44902
|
-
}
|
|
44903
|
-
function parseVoiceOnDemandToken(data) {
|
|
44904
|
-
if (!isVoiceOnDemandCallback(data))
|
|
44905
|
-
return null;
|
|
44906
|
-
const token = data.slice(VOICE_ONDEMAND_CALLBACK_PREFIX.length);
|
|
44907
|
-
return token.length > 0 ? token : null;
|
|
44908
|
-
}
|
|
44909
|
-
function buildListenKeyboard(token) {
|
|
44910
|
-
return {
|
|
44911
|
-
inline_keyboard: [
|
|
44912
|
-
[{ text: "\uD83D\uDD0A Listen", callback_data: `${VOICE_ONDEMAND_CALLBACK_PREFIX}${token}` }]
|
|
44913
|
-
]
|
|
44914
|
-
};
|
|
44915
|
-
}
|
|
44916
|
-
function mayInjectListenButton(rawKeyboard) {
|
|
44917
|
-
if (rawKeyboard == null)
|
|
44918
|
-
return true;
|
|
44919
|
-
return !rawKeyboard.some((row) => Array.isArray(row) && row.length > 0);
|
|
44920
|
-
}
|
|
44921
|
-
|
|
44922
45074
|
// voice-send.ts
|
|
44923
45075
|
function extractVoiceFileId(sent) {
|
|
44924
45076
|
const id = sent?.voice?.file_id;
|
|
@@ -45231,6 +45383,8 @@ function normalizeForTts2(text) {
|
|
|
45231
45383
|
return text;
|
|
45232
45384
|
let s = text.replace(/\r\n?/g, `
|
|
45233
45385
|
`);
|
|
45386
|
+
s = decodeHtmlEntities2(s);
|
|
45387
|
+
s = stripBackslashEscapes2(s);
|
|
45234
45388
|
s = s.replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n[ \t]*\2[ \t]*(?=\n|$)/g, "$1code block omitted.");
|
|
45235
45389
|
s = s.replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*$/g, "$1code block omitted.");
|
|
45236
45390
|
const { parked, parts } = parkInline2(s);
|
|
@@ -51566,202 +51720,6 @@ function createSessionModelSource(options = {}) {
|
|
|
51566
51720
|
};
|
|
51567
51721
|
}
|
|
51568
51722
|
|
|
51569
|
-
// tool-labels.ts
|
|
51570
|
-
var MAX_LABEL_CHARS = 60;
|
|
51571
|
-
var MAX_BASH_CHARS = 40;
|
|
51572
|
-
var MAX_DESCRIPTION_CHARS = 160;
|
|
51573
|
-
function basename6(p) {
|
|
51574
|
-
if (!p)
|
|
51575
|
-
return "";
|
|
51576
|
-
const parts = p.split("/").filter(Boolean);
|
|
51577
|
-
return parts.length > 0 ? parts[parts.length - 1] : p;
|
|
51578
|
-
}
|
|
51579
|
-
function shortenGrepPath(p) {
|
|
51580
|
-
if (!p)
|
|
51581
|
-
return "repo";
|
|
51582
|
-
const hadTrailingSlash = /\/+$/.test(p);
|
|
51583
|
-
const trimmed = p.replace(/\/+$/, "");
|
|
51584
|
-
const parts = trimmed.split("/").filter(Boolean);
|
|
51585
|
-
if (parts.length === 0)
|
|
51586
|
-
return "repo";
|
|
51587
|
-
const last = parts[parts.length - 1];
|
|
51588
|
-
if (hadTrailingSlash)
|
|
51589
|
-
return `${last}/`;
|
|
51590
|
-
if (last.startsWith(".") && !last.slice(1).includes("."))
|
|
51591
|
-
return last;
|
|
51592
|
-
if (!last.includes("."))
|
|
51593
|
-
return `${last}/`;
|
|
51594
|
-
return last;
|
|
51595
|
-
}
|
|
51596
|
-
function hostFromUrl(u) {
|
|
51597
|
-
if (!u)
|
|
51598
|
-
return "";
|
|
51599
|
-
try {
|
|
51600
|
-
return new URL(u).host;
|
|
51601
|
-
} catch {
|
|
51602
|
-
return truncate3(u);
|
|
51603
|
-
}
|
|
51604
|
-
}
|
|
51605
|
-
function truncate3(s, n = MAX_LABEL_CHARS) {
|
|
51606
|
-
if (s.length <= n)
|
|
51607
|
-
return s;
|
|
51608
|
-
return s.slice(0, n - 1) + "\u2026";
|
|
51609
|
-
}
|
|
51610
|
-
function stripHtml(s) {
|
|
51611
|
-
return s.replace(/<\/?[a-zA-Z][^>]*>/g, "");
|
|
51612
|
-
}
|
|
51613
|
-
function firstLine(s) {
|
|
51614
|
-
const idx = s.indexOf(`
|
|
51615
|
-
`);
|
|
51616
|
-
return idx === -1 ? s : s.slice(0, idx);
|
|
51617
|
-
}
|
|
51618
|
-
function toolLabel(tool, input, preamble, precomputedLabel) {
|
|
51619
|
-
if (precomputedLabel && precomputedLabel.trim().length > 0) {
|
|
51620
|
-
return truncate3(firstLine(precomputedLabel.trim()), MAX_DESCRIPTION_CHARS);
|
|
51621
|
-
}
|
|
51622
|
-
if (!input || typeof input !== "object")
|
|
51623
|
-
return "";
|
|
51624
|
-
const str = (k) => typeof input[k] === "string" ? input[k] : undefined;
|
|
51625
|
-
const preambleLabel = () => {
|
|
51626
|
-
if (!preamble)
|
|
51627
|
-
return null;
|
|
51628
|
-
if (preamble.includes(`
|
|
51629
|
-
`))
|
|
51630
|
-
return null;
|
|
51631
|
-
const trimmed = preamble.trim();
|
|
51632
|
-
if (!trimmed)
|
|
51633
|
-
return null;
|
|
51634
|
-
if (trimmed.length > MAX_DESCRIPTION_CHARS)
|
|
51635
|
-
return null;
|
|
51636
|
-
return trimmed;
|
|
51637
|
-
};
|
|
51638
|
-
switch (tool) {
|
|
51639
|
-
case "Read":
|
|
51640
|
-
case "Write":
|
|
51641
|
-
case "NotebookEdit":
|
|
51642
|
-
case "Edit": {
|
|
51643
|
-
const pre = preambleLabel();
|
|
51644
|
-
if (pre)
|
|
51645
|
-
return pre;
|
|
51646
|
-
return truncate3(basename6(str("file_path") ?? ""));
|
|
51647
|
-
}
|
|
51648
|
-
case "Bash":
|
|
51649
|
-
case "BashOutput": {
|
|
51650
|
-
const description = str("description");
|
|
51651
|
-
if (description)
|
|
51652
|
-
return truncate3(firstLine(description), MAX_DESCRIPTION_CHARS);
|
|
51653
|
-
const pre = preambleLabel();
|
|
51654
|
-
if (pre)
|
|
51655
|
-
return pre;
|
|
51656
|
-
const cmd = str("command") ?? str("bash_id") ?? "";
|
|
51657
|
-
return truncate3(firstLine(cmd), MAX_BASH_CHARS);
|
|
51658
|
-
}
|
|
51659
|
-
case "KillShell":
|
|
51660
|
-
return truncate3(str("shell_id") ?? "");
|
|
51661
|
-
case "Glob": {
|
|
51662
|
-
const pre = preambleLabel();
|
|
51663
|
-
if (pre)
|
|
51664
|
-
return pre;
|
|
51665
|
-
return truncate3(str("pattern") ?? "");
|
|
51666
|
-
}
|
|
51667
|
-
case "Grep": {
|
|
51668
|
-
const pre = preambleLabel();
|
|
51669
|
-
if (pre)
|
|
51670
|
-
return pre;
|
|
51671
|
-
const pat = str("pattern") ?? "";
|
|
51672
|
-
if (!pat)
|
|
51673
|
-
return "";
|
|
51674
|
-
const path = str("path");
|
|
51675
|
-
const where = shortenGrepPath(path ?? "");
|
|
51676
|
-
return truncate3(`"${pat}" (in ${where})`);
|
|
51677
|
-
}
|
|
51678
|
-
case "WebFetch":
|
|
51679
|
-
return truncate3(hostFromUrl(str("url") ?? ""));
|
|
51680
|
-
case "WebSearch": {
|
|
51681
|
-
const q = str("query") ?? "";
|
|
51682
|
-
return q ? truncate3(`"${q}"`) : "";
|
|
51683
|
-
}
|
|
51684
|
-
case "Task":
|
|
51685
|
-
case "Agent": {
|
|
51686
|
-
const desc = str("description") ?? str("subagent_type") ?? "";
|
|
51687
|
-
return truncate3(desc);
|
|
51688
|
-
}
|
|
51689
|
-
case "TodoWrite":
|
|
51690
|
-
case "TaskCreate":
|
|
51691
|
-
case "TaskUpdate":
|
|
51692
|
-
case "TaskList":
|
|
51693
|
-
case "TaskGet":
|
|
51694
|
-
case "TaskStop":
|
|
51695
|
-
case "TaskOutput":
|
|
51696
|
-
return "";
|
|
51697
|
-
case "Skill":
|
|
51698
|
-
return truncate3(str("skill") ?? "");
|
|
51699
|
-
case "SlashCommand":
|
|
51700
|
-
return truncate3(str("command") ?? "");
|
|
51701
|
-
case "ToolSearch": {
|
|
51702
|
-
const q = str("query") ?? "";
|
|
51703
|
-
if (!q)
|
|
51704
|
-
return "";
|
|
51705
|
-
const selectMatch = q.match(/^\s*select\s*:\s*(.+)$/i);
|
|
51706
|
-
if (selectMatch) {
|
|
51707
|
-
const names = selectMatch[1].split(",").map((n) => n.trim()).filter((n) => n.length > 0).join(", ");
|
|
51708
|
-
return truncate3(`Loading schema: ${names}`);
|
|
51709
|
-
}
|
|
51710
|
-
return truncate3(`Searching tools: ${q}`);
|
|
51711
|
-
}
|
|
51712
|
-
default:
|
|
51713
|
-
if (tool.startsWith("mcp__")) {
|
|
51714
|
-
const description = str("description");
|
|
51715
|
-
if (description)
|
|
51716
|
-
return truncate3(firstLine(stripHtml(description)), MAX_DESCRIPTION_CHARS);
|
|
51717
|
-
const label = mcpBaseLabel(tool);
|
|
51718
|
-
const query = str("query") ?? str("text") ?? str("name");
|
|
51719
|
-
if (label && query) {
|
|
51720
|
-
const budget = Math.max(8, MAX_LABEL_CHARS - label.length - 4);
|
|
51721
|
-
const preview = truncate3(firstLine(stripHtml(query)), budget);
|
|
51722
|
-
return `${label} (${preview})`;
|
|
51723
|
-
}
|
|
51724
|
-
if (label)
|
|
51725
|
-
return truncate3(label);
|
|
51726
|
-
}
|
|
51727
|
-
for (const k of ["description", "file_path", "path", "url", "query", "pattern", "command"]) {
|
|
51728
|
-
const v = str(k);
|
|
51729
|
-
if (v != null && v.length > 0) {
|
|
51730
|
-
if (k === "file_path" || k === "path")
|
|
51731
|
-
return truncate3(basename6(v));
|
|
51732
|
-
if (k === "url")
|
|
51733
|
-
return truncate3(hostFromUrl(v));
|
|
51734
|
-
if (k === "description")
|
|
51735
|
-
return truncate3(firstLine(v), MAX_DESCRIPTION_CHARS);
|
|
51736
|
-
return truncate3(firstLine(v));
|
|
51737
|
-
}
|
|
51738
|
-
}
|
|
51739
|
-
return "";
|
|
51740
|
-
}
|
|
51741
|
-
}
|
|
51742
|
-
function mcpBaseLabel(tool) {
|
|
51743
|
-
if (!tool.startsWith("mcp__"))
|
|
51744
|
-
return "";
|
|
51745
|
-
const parts = tool.slice("mcp__".length).split("__");
|
|
51746
|
-
if (parts.length < 2)
|
|
51747
|
-
return "";
|
|
51748
|
-
const rawServer = parts[0];
|
|
51749
|
-
const action = parts.slice(1).join("__");
|
|
51750
|
-
if (!rawServer || !action)
|
|
51751
|
-
return "";
|
|
51752
|
-
return `${prettifyServer(rawServer)}: ${action}`;
|
|
51753
|
-
}
|
|
51754
|
-
function prettifyServer(name) {
|
|
51755
|
-
const LABELS = {
|
|
51756
|
-
"switchroom-telegram": "Telegram"
|
|
51757
|
-
};
|
|
51758
|
-
if (LABELS[name])
|
|
51759
|
-
return LABELS[name];
|
|
51760
|
-
if (!name)
|
|
51761
|
-
return name;
|
|
51762
|
-
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
51763
|
-
}
|
|
51764
|
-
|
|
51765
51723
|
// typing-wrap.ts
|
|
51766
51724
|
function createTypingWrapper(deps) {
|
|
51767
51725
|
const debounceMs = deps.debounceMs ?? 500;
|
|
@@ -63253,7 +63211,7 @@ function loadInitialFloodWindows(floodStateFilePath, floodWindowsFilePath, now)
|
|
|
63253
63211
|
}
|
|
63254
63212
|
|
|
63255
63213
|
// attachment-path.ts
|
|
63256
|
-
import { join as join17, basename as
|
|
63214
|
+
import { join as join17, basename as basename6, resolve as resolve6, sep as sep2 } from "node:path";
|
|
63257
63215
|
function sanitizeExtension2(ext) {
|
|
63258
63216
|
if (ext == null)
|
|
63259
63217
|
return "bin";
|
|
@@ -63282,7 +63240,7 @@ function assertInsideInbox2(inboxDir, candidatePath) {
|
|
|
63282
63240
|
const inboxReal = resolve6(inboxDir);
|
|
63283
63241
|
const candidateReal = resolve6(candidatePath);
|
|
63284
63242
|
if (candidateReal !== inboxReal && !candidateReal.startsWith(inboxReal + sep2)) {
|
|
63285
|
-
throw new Error(`attachment path escape: ${
|
|
63243
|
+
throw new Error(`attachment path escape: ${basename6(candidatePath)} resolved outside ${inboxDir}`);
|
|
63286
63244
|
}
|
|
63287
63245
|
}
|
|
63288
63246
|
|
|
@@ -68095,6 +68053,8 @@ __export(exports_silence_poke, {
|
|
|
68095
68053
|
noteThinking: () => noteThinking,
|
|
68096
68054
|
noteProduction: () => noteProduction,
|
|
68097
68055
|
noteOutbound: () => noteOutbound2,
|
|
68056
|
+
noteBackgroundShellDead: () => noteBackgroundShellDead,
|
|
68057
|
+
noteBackgroundShellAlive: () => noteBackgroundShellAlive,
|
|
68098
68058
|
longestInFlightTool: () => longestInFlightTool,
|
|
68099
68059
|
formatFrameworkFallbackText: () => formatFrameworkFallbackText,
|
|
68100
68060
|
endTurn: () => endTurn,
|
|
@@ -68102,6 +68062,7 @@ __export(exports_silence_poke, {
|
|
|
68102
68062
|
__setDepsForTests: () => __setDepsForTests,
|
|
68103
68063
|
__resetAllForTests: () => __resetAllForTests2,
|
|
68104
68064
|
__getStateForTests: () => __getStateForTests,
|
|
68065
|
+
__bgMarkerParserConfirmedForTests: () => __bgMarkerParserConfirmedForTests,
|
|
68105
68066
|
DEFAULT_THRESHOLDS: () => DEFAULT_THRESHOLDS,
|
|
68106
68067
|
DEFAULT_POLL_INTERVAL_MS: () => DEFAULT_POLL_INTERVAL_MS
|
|
68107
68068
|
});
|
|
@@ -68172,6 +68133,7 @@ var DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
|
68172
68133
|
var state2 = new Map;
|
|
68173
68134
|
var timer = null;
|
|
68174
68135
|
var activeDeps = null;
|
|
68136
|
+
var bgMarkerParserConfirmed = false;
|
|
68175
68137
|
function silencePokeEnabled() {
|
|
68176
68138
|
const v = process.env.SWITCHROOM_DISABLE_SILENCE_POKE;
|
|
68177
68139
|
return !(v === "1" || v === "true");
|
|
@@ -68185,9 +68147,24 @@ function startTurn(key, now) {
|
|
|
68185
68147
|
lastThinkingAt: null,
|
|
68186
68148
|
fallbackFired: false,
|
|
68187
68149
|
floorFired: false,
|
|
68188
|
-
inFlightTools: new Map
|
|
68150
|
+
inFlightTools: new Map,
|
|
68151
|
+
sawBashThisTurn: false,
|
|
68152
|
+
aliveShells: new Set
|
|
68189
68153
|
});
|
|
68190
68154
|
}
|
|
68155
|
+
function noteBackgroundShellAlive(key, shellId) {
|
|
68156
|
+
bgMarkerParserConfirmed = true;
|
|
68157
|
+
const s = state2.get(key);
|
|
68158
|
+
if (s == null)
|
|
68159
|
+
return;
|
|
68160
|
+
s.aliveShells.add(shellId);
|
|
68161
|
+
}
|
|
68162
|
+
function noteBackgroundShellDead(key, shellId) {
|
|
68163
|
+
const s = state2.get(key);
|
|
68164
|
+
if (s == null)
|
|
68165
|
+
return;
|
|
68166
|
+
s.aliveShells.delete(shellId);
|
|
68167
|
+
}
|
|
68191
68168
|
function noteOutbound2(key, now) {
|
|
68192
68169
|
const s = state2.get(key);
|
|
68193
68170
|
if (s == null)
|
|
@@ -68213,6 +68190,8 @@ function noteToolStart(key, toolUseId, name, label, now) {
|
|
|
68213
68190
|
if (s == null)
|
|
68214
68191
|
return;
|
|
68215
68192
|
s.inFlightTools.set(toolUseId, { name, startedAt: now, label });
|
|
68193
|
+
if (name === "Bash")
|
|
68194
|
+
s.sawBashThisTurn = true;
|
|
68216
68195
|
}
|
|
68217
68196
|
function noteToolEnd(key, toolUseId, _now) {
|
|
68218
68197
|
const s = state2.get(key);
|
|
@@ -68327,6 +68306,10 @@ function tick(now) {
|
|
|
68327
68306
|
if (!forceDisable && activeDeps.isLegitimatelyWorking != null) {
|
|
68328
68307
|
if (activeDeps.isLegitimatelyWorking(key))
|
|
68329
68308
|
continue;
|
|
68309
|
+
if (s.aliveShells.size > 0)
|
|
68310
|
+
continue;
|
|
68311
|
+
if (!bgMarkerParserConfirmed && s.sawBashThisTurn)
|
|
68312
|
+
continue;
|
|
68330
68313
|
} else if (!forceDisable && activeDeps.deferFallbackWhileToolInFlight === true && s.inFlightTools.size > 0) {
|
|
68331
68314
|
continue;
|
|
68332
68315
|
}
|
|
@@ -68409,6 +68392,10 @@ function __getStateForTests(key) {
|
|
|
68409
68392
|
function __resetAllForTests2() {
|
|
68410
68393
|
state2.clear();
|
|
68411
68394
|
stopTimer();
|
|
68395
|
+
bgMarkerParserConfirmed = false;
|
|
68396
|
+
}
|
|
68397
|
+
function __bgMarkerParserConfirmedForTests() {
|
|
68398
|
+
return bgMarkerParserConfirmed;
|
|
68412
68399
|
}
|
|
68413
68400
|
|
|
68414
68401
|
// pending-work-progress.ts
|
|
@@ -68781,7 +68768,7 @@ function resolveAnswerLaneConfig(input) {
|
|
|
68781
68768
|
|
|
68782
68769
|
// session-tail.ts
|
|
68783
68770
|
import { homedir as homedir7 } from "os";
|
|
68784
|
-
import { basename as
|
|
68771
|
+
import { basename as basename7, join as join23 } from "path";
|
|
68785
68772
|
|
|
68786
68773
|
// operator-events.ts
|
|
68787
68774
|
init_format();
|
|
@@ -69218,6 +69205,29 @@ function extractToolResultErrorText(content3) {
|
|
|
69218
69205
|
}
|
|
69219
69206
|
return "";
|
|
69220
69207
|
}
|
|
69208
|
+
function parseBackgroundTaskId(obj) {
|
|
69209
|
+
const tur = obj.toolUseResult;
|
|
69210
|
+
if (typeof tur === "object" && tur != null) {
|
|
69211
|
+
const id = tur.backgroundTaskId;
|
|
69212
|
+
if (typeof id === "string" && id.length > 0)
|
|
69213
|
+
return id;
|
|
69214
|
+
}
|
|
69215
|
+
return null;
|
|
69216
|
+
}
|
|
69217
|
+
function parseBackgroundLaunchString(content3) {
|
|
69218
|
+
const text4 = typeof content3 === "string" ? content3 : extractToolResultErrorText(content3);
|
|
69219
|
+
const m = text4.match(/Command running in background with ID: (\w+)/);
|
|
69220
|
+
return m != null ? m[1] : null;
|
|
69221
|
+
}
|
|
69222
|
+
function parseTaskNotification(content3) {
|
|
69223
|
+
if (!content3.includes("<task-notification>"))
|
|
69224
|
+
return null;
|
|
69225
|
+
const idM = content3.match(/<task-id>([^<]+)<\/task-id>/);
|
|
69226
|
+
const stM = content3.match(/<status>([^<]+)<\/status>/);
|
|
69227
|
+
if (idM == null || stM == null)
|
|
69228
|
+
return null;
|
|
69229
|
+
return { taskId: idM[1].trim(), status: stM[1].trim() };
|
|
69230
|
+
}
|
|
69221
69231
|
function projectAssistantTextBlocks(content3, make) {
|
|
69222
69232
|
const out = new Map;
|
|
69223
69233
|
let lastToolUseIdx = -1;
|
|
@@ -69314,6 +69324,10 @@ function projectTranscriptLine(line) {
|
|
|
69314
69324
|
const op = obj.operation;
|
|
69315
69325
|
if (op === "enqueue") {
|
|
69316
69326
|
const content3 = obj.content ?? "";
|
|
69327
|
+
const notif = parseTaskNotification(content3);
|
|
69328
|
+
if (notif != null) {
|
|
69329
|
+
return [{ kind: "task_notification", taskId: notif.taskId, status: notif.status }];
|
|
69330
|
+
}
|
|
69317
69331
|
const { chatId, messageId, threadId } = parseChannelMeta(content3);
|
|
69318
69332
|
return [{ kind: "enqueue", chatId, messageId, threadId, rawContent: content3 }];
|
|
69319
69333
|
}
|
|
@@ -69371,6 +69385,7 @@ function projectTranscriptLine(line) {
|
|
|
69371
69385
|
const content3 = message?.content;
|
|
69372
69386
|
if (!Array.isArray(content3))
|
|
69373
69387
|
return [];
|
|
69388
|
+
const backgroundTaskId = parseBackgroundTaskId(obj);
|
|
69374
69389
|
const events = [];
|
|
69375
69390
|
for (const c of content3) {
|
|
69376
69391
|
if (c.type === "tool_result") {
|
|
@@ -69380,7 +69395,8 @@ function projectTranscriptLine(line) {
|
|
|
69380
69395
|
toolUseId: c.tool_use_id ?? "",
|
|
69381
69396
|
toolName: null,
|
|
69382
69397
|
isError: isError2,
|
|
69383
|
-
errorText: isError2 ? extractToolResultErrorText(c.content) : undefined
|
|
69398
|
+
errorText: isError2 ? extractToolResultErrorText(c.content) : undefined,
|
|
69399
|
+
backgroundTaskId: backgroundTaskId ?? parseBackgroundLaunchString(c.content) ?? undefined
|
|
69384
69400
|
});
|
|
69385
69401
|
}
|
|
69386
69402
|
}
|
|
@@ -74908,7 +74924,7 @@ function deriveTelegraphTitle2(text4) {
|
|
|
74908
74924
|
var VOICE_FILE_TTL_MS2 = 7 * 24 * 60 * 60 * 1000;
|
|
74909
74925
|
var VOICE_CACHE_MAX_BYTES2 = 500 * 1024 * 1024;
|
|
74910
74926
|
var VOICE_SWEEP_INTERVAL_MS2 = 60 * 60 * 1000;
|
|
74911
|
-
function
|
|
74927
|
+
function eagerVoiceEnabled2() {
|
|
74912
74928
|
const kill = process.env.SWITCHROOM_DISABLE_EAGER_VOICE;
|
|
74913
74929
|
return !(kill === "1" || kill === "true");
|
|
74914
74930
|
}
|
|
@@ -75698,24 +75714,23 @@ ${url}`;
|
|
|
75698
75714
|
replyButtonMeta = extractAgentButtonMeta(redactedKeyboard);
|
|
75699
75715
|
replyMarkup = { inline_keyboard: wrapAgentCallbacks(redactedKeyboard) };
|
|
75700
75716
|
}
|
|
75701
|
-
if (useOnDemandButton
|
|
75702
|
-
|
|
75703
|
-
|
|
75717
|
+
if (useOnDemandButton) {
|
|
75718
|
+
const listenPlan = planListenButton({ voiceOutPlan, rawKeyboard });
|
|
75719
|
+
if (listenPlan == null) {
|
|
75720
|
+
const hasSpeakableText = voiceOutPlan.ttsChunks.length > 0 && voiceOutPlan.ttsChunks[0].length > 0;
|
|
75721
|
+
if (hasSpeakableText && !mayInjectListenButton(rawKeyboard)) {
|
|
75722
|
+
process.stderr.write(`telegram gateway: voice-out on-demand: agent supplied inline_keyboard \u2014 skipping Listen button (single_use collision gate)
|
|
75704
75723
|
`);
|
|
75724
|
+
}
|
|
75705
75725
|
} else {
|
|
75706
|
-
|
|
75707
|
-
|
|
75708
|
-
|
|
75709
|
-
...voiceOutPlan.voice != null ? { voice: voiceOutPlan.voice } : {},
|
|
75710
|
-
speed: voiceOutPlan.speed
|
|
75711
|
-
});
|
|
75712
|
-
replyMarkup = buildListenKeyboard(token);
|
|
75713
|
-
if (eagerVoiceEnabled()) {
|
|
75726
|
+
voiceOnDemandCache.put(listenPlan.token, listenPlan.payload);
|
|
75727
|
+
replyMarkup = listenPlan.replyMarkup;
|
|
75728
|
+
if (eagerVoiceEnabled2()) {
|
|
75714
75729
|
voicePreSynthQueue.enqueue({
|
|
75715
|
-
token,
|
|
75716
|
-
text:
|
|
75717
|
-
...
|
|
75718
|
-
speed:
|
|
75730
|
+
token: listenPlan.token,
|
|
75731
|
+
text: listenPlan.payload.text,
|
|
75732
|
+
...listenPlan.payload.voice != null ? { voice: listenPlan.payload.voice } : {},
|
|
75733
|
+
speed: listenPlan.payload.speed
|
|
75719
75734
|
});
|
|
75720
75735
|
}
|
|
75721
75736
|
}
|
|
@@ -85368,15 +85383,15 @@ function buildMs365CardText(p) {
|
|
|
85368
85383
|
const lines = [];
|
|
85369
85384
|
lines.push(`\uD83D\uDCC4 Microsoft 365 write approval`);
|
|
85370
85385
|
lines.push("");
|
|
85371
|
-
lines.push(`Agent: ${
|
|
85372
|
-
lines.push(`Tool: ${
|
|
85373
|
-
lines.push(`Item: ${
|
|
85386
|
+
lines.push(`Agent: ${truncate3(p.agentName, 64)}`);
|
|
85387
|
+
lines.push(`Tool: ${truncate3(p.toolName.replace(/^mcp__/, ""), 96)}`);
|
|
85388
|
+
lines.push(`Item: ${truncate3(p.itemDisplayName, 256)}`);
|
|
85374
85389
|
if (p.itemId !== "(new)") {
|
|
85375
|
-
lines.push(`ID: ${
|
|
85390
|
+
lines.push(`ID: ${truncate3(p.itemId, 96)}`);
|
|
85376
85391
|
}
|
|
85377
|
-
lines.push(`Account: ${
|
|
85392
|
+
lines.push(`Account: ${truncate3(p.accountEmail, 96)}`);
|
|
85378
85393
|
if (p.eventWhen) {
|
|
85379
|
-
lines.push(`When: ${
|
|
85394
|
+
lines.push(`When: ${truncate3(p.eventWhen, 96)}`);
|
|
85380
85395
|
}
|
|
85381
85396
|
if (typeof p.sizeBytesBefore === "number" || typeof p.sizeBytesAfter === "number") {
|
|
85382
85397
|
const before = p.sizeBytesBefore ?? 0;
|
|
@@ -85386,27 +85401,27 @@ function buildMs365CardText(p) {
|
|
|
85386
85401
|
lines.push(`Size: ${humanBytes(before)} \u2192 ${humanBytes(after)} (${sign}${humanBytes(delta)})`);
|
|
85387
85402
|
}
|
|
85388
85403
|
if (p.deepLink) {
|
|
85389
|
-
lines.push(`Link: ${
|
|
85404
|
+
lines.push(`Link: ${truncate3(p.deepLink, 256)}`);
|
|
85390
85405
|
}
|
|
85391
85406
|
if (p.changes && p.changes.length > 0) {
|
|
85392
85407
|
lines.push("");
|
|
85393
85408
|
lines.push("Changes:");
|
|
85394
85409
|
for (const c of p.changes.slice(0, 8)) {
|
|
85395
|
-
const before = c.before !== undefined ?
|
|
85396
|
-
const after = c.after !== undefined ?
|
|
85410
|
+
const before = c.before !== undefined ? truncate3(c.before, 96) : "(none)";
|
|
85411
|
+
const after = c.after !== undefined ? truncate3(c.after, 96) : "(cleared)";
|
|
85397
85412
|
lines.push(`\u2022 ${c.field}: ${before} \u2192 ${after}`);
|
|
85398
85413
|
}
|
|
85399
85414
|
}
|
|
85400
85415
|
if (p.agentRationale) {
|
|
85401
85416
|
lines.push("");
|
|
85402
|
-
lines.push(`\uD83D\uDCAC ${
|
|
85417
|
+
lines.push(`\uD83D\uDCAC ${truncate3(p.agentRationale, 512)}`);
|
|
85403
85418
|
}
|
|
85404
85419
|
lines.push("");
|
|
85405
85420
|
lines.push(p.changes && p.changes.length > 0 ? "\u26a0\ufe0f Attestation (RFC \u00a78 v1.5): the diff above is derived from live Graph state + the mutation payload. Verify before approving." : "\u26a0\ufe0f Weak attestation (RFC \u00a78 v1): operator should click through to verify the actual change before approving. Structural diff coming v1.5.");
|
|
85406
85421
|
return hardenCardBreaks(lines.join(`
|
|
85407
85422
|
`));
|
|
85408
85423
|
}
|
|
85409
|
-
function
|
|
85424
|
+
function truncate3(s, n) {
|
|
85410
85425
|
const oneLine = s.replace(/[\r\n\t]+/g, " ");
|
|
85411
85426
|
if (oneLine.length <= n)
|
|
85412
85427
|
return oneLine;
|
|
@@ -88718,6 +88733,246 @@ function buildSilencePokeOptions(deps) {
|
|
|
88718
88733
|
};
|
|
88719
88734
|
}
|
|
88720
88735
|
|
|
88736
|
+
// tool-labels.ts
|
|
88737
|
+
var MAX_LABEL_CHARS = 60;
|
|
88738
|
+
var MAX_BASH_CHARS = 40;
|
|
88739
|
+
var MAX_DESCRIPTION_CHARS = 160;
|
|
88740
|
+
function basename8(p) {
|
|
88741
|
+
if (!p)
|
|
88742
|
+
return "";
|
|
88743
|
+
const parts = p.split("/").filter(Boolean);
|
|
88744
|
+
return parts.length > 0 ? parts[parts.length - 1] : p;
|
|
88745
|
+
}
|
|
88746
|
+
function shortenGrepPath(p) {
|
|
88747
|
+
if (!p)
|
|
88748
|
+
return "repo";
|
|
88749
|
+
const hadTrailingSlash = /\/+$/.test(p);
|
|
88750
|
+
const trimmed = p.replace(/\/+$/, "");
|
|
88751
|
+
const parts = trimmed.split("/").filter(Boolean);
|
|
88752
|
+
if (parts.length === 0)
|
|
88753
|
+
return "repo";
|
|
88754
|
+
const last = parts[parts.length - 1];
|
|
88755
|
+
if (hadTrailingSlash)
|
|
88756
|
+
return `${last}/`;
|
|
88757
|
+
if (last.startsWith(".") && !last.slice(1).includes("."))
|
|
88758
|
+
return last;
|
|
88759
|
+
if (!last.includes("."))
|
|
88760
|
+
return `${last}/`;
|
|
88761
|
+
return last;
|
|
88762
|
+
}
|
|
88763
|
+
function hostFromUrl(u) {
|
|
88764
|
+
if (!u)
|
|
88765
|
+
return "";
|
|
88766
|
+
try {
|
|
88767
|
+
return new URL(u).host;
|
|
88768
|
+
} catch {
|
|
88769
|
+
return truncate4(u);
|
|
88770
|
+
}
|
|
88771
|
+
}
|
|
88772
|
+
function truncate4(s, n = MAX_LABEL_CHARS) {
|
|
88773
|
+
if (s.length <= n)
|
|
88774
|
+
return s;
|
|
88775
|
+
return s.slice(0, n - 1) + "\u2026";
|
|
88776
|
+
}
|
|
88777
|
+
function stripHtml(s) {
|
|
88778
|
+
return s.replace(/<\/?[a-zA-Z][^>]*>/g, "");
|
|
88779
|
+
}
|
|
88780
|
+
function firstLine(s) {
|
|
88781
|
+
const idx = s.indexOf(`
|
|
88782
|
+
`);
|
|
88783
|
+
return idx === -1 ? s : s.slice(0, idx);
|
|
88784
|
+
}
|
|
88785
|
+
function toolLabel(tool, input, preamble, precomputedLabel) {
|
|
88786
|
+
if (precomputedLabel && precomputedLabel.trim().length > 0) {
|
|
88787
|
+
return truncate4(firstLine(precomputedLabel.trim()), MAX_DESCRIPTION_CHARS);
|
|
88788
|
+
}
|
|
88789
|
+
if (!input || typeof input !== "object")
|
|
88790
|
+
return "";
|
|
88791
|
+
const str = (k) => typeof input[k] === "string" ? input[k] : undefined;
|
|
88792
|
+
const preambleLabel = () => {
|
|
88793
|
+
if (!preamble)
|
|
88794
|
+
return null;
|
|
88795
|
+
if (preamble.includes(`
|
|
88796
|
+
`))
|
|
88797
|
+
return null;
|
|
88798
|
+
const trimmed = preamble.trim();
|
|
88799
|
+
if (!trimmed)
|
|
88800
|
+
return null;
|
|
88801
|
+
if (trimmed.length > MAX_DESCRIPTION_CHARS)
|
|
88802
|
+
return null;
|
|
88803
|
+
return trimmed;
|
|
88804
|
+
};
|
|
88805
|
+
switch (tool) {
|
|
88806
|
+
case "Read":
|
|
88807
|
+
case "Write":
|
|
88808
|
+
case "NotebookEdit":
|
|
88809
|
+
case "Edit": {
|
|
88810
|
+
const pre = preambleLabel();
|
|
88811
|
+
if (pre)
|
|
88812
|
+
return pre;
|
|
88813
|
+
return truncate4(basename8(str("file_path") ?? ""));
|
|
88814
|
+
}
|
|
88815
|
+
case "Bash":
|
|
88816
|
+
case "BashOutput": {
|
|
88817
|
+
const description = str("description");
|
|
88818
|
+
if (description)
|
|
88819
|
+
return truncate4(firstLine(description), MAX_DESCRIPTION_CHARS);
|
|
88820
|
+
const pre = preambleLabel();
|
|
88821
|
+
if (pre)
|
|
88822
|
+
return pre;
|
|
88823
|
+
const cmd = str("command") ?? str("bash_id") ?? "";
|
|
88824
|
+
return truncate4(firstLine(cmd), MAX_BASH_CHARS);
|
|
88825
|
+
}
|
|
88826
|
+
case "KillShell":
|
|
88827
|
+
return truncate4(str("shell_id") ?? "");
|
|
88828
|
+
case "Glob": {
|
|
88829
|
+
const pre = preambleLabel();
|
|
88830
|
+
if (pre)
|
|
88831
|
+
return pre;
|
|
88832
|
+
return truncate4(str("pattern") ?? "");
|
|
88833
|
+
}
|
|
88834
|
+
case "Grep": {
|
|
88835
|
+
const pre = preambleLabel();
|
|
88836
|
+
if (pre)
|
|
88837
|
+
return pre;
|
|
88838
|
+
const pat = str("pattern") ?? "";
|
|
88839
|
+
if (!pat)
|
|
88840
|
+
return "";
|
|
88841
|
+
const path2 = str("path");
|
|
88842
|
+
const where = shortenGrepPath(path2 ?? "");
|
|
88843
|
+
return truncate4(`"${pat}" (in ${where})`);
|
|
88844
|
+
}
|
|
88845
|
+
case "WebFetch":
|
|
88846
|
+
return truncate4(hostFromUrl(str("url") ?? ""));
|
|
88847
|
+
case "WebSearch": {
|
|
88848
|
+
const q = str("query") ?? "";
|
|
88849
|
+
return q ? truncate4(`"${q}"`) : "";
|
|
88850
|
+
}
|
|
88851
|
+
case "Task":
|
|
88852
|
+
case "Agent": {
|
|
88853
|
+
const desc = str("description") ?? str("subagent_type") ?? "";
|
|
88854
|
+
return truncate4(desc);
|
|
88855
|
+
}
|
|
88856
|
+
case "TodoWrite":
|
|
88857
|
+
case "TaskCreate":
|
|
88858
|
+
case "TaskUpdate":
|
|
88859
|
+
case "TaskList":
|
|
88860
|
+
case "TaskGet":
|
|
88861
|
+
case "TaskStop":
|
|
88862
|
+
case "TaskOutput":
|
|
88863
|
+
return "";
|
|
88864
|
+
case "Skill":
|
|
88865
|
+
return truncate4(str("skill") ?? "");
|
|
88866
|
+
case "SlashCommand":
|
|
88867
|
+
return truncate4(str("command") ?? "");
|
|
88868
|
+
case "ToolSearch": {
|
|
88869
|
+
const q = str("query") ?? "";
|
|
88870
|
+
if (!q)
|
|
88871
|
+
return "";
|
|
88872
|
+
const selectMatch = q.match(/^\s*select\s*:\s*(.+)$/i);
|
|
88873
|
+
if (selectMatch) {
|
|
88874
|
+
const names = selectMatch[1].split(",").map((n) => n.trim()).filter((n) => n.length > 0).join(", ");
|
|
88875
|
+
return truncate4(`Loading schema: ${names}`);
|
|
88876
|
+
}
|
|
88877
|
+
return truncate4(`Searching tools: ${q}`);
|
|
88878
|
+
}
|
|
88879
|
+
default:
|
|
88880
|
+
if (tool.startsWith("mcp__")) {
|
|
88881
|
+
const description = str("description");
|
|
88882
|
+
if (description)
|
|
88883
|
+
return truncate4(firstLine(stripHtml(description)), MAX_DESCRIPTION_CHARS);
|
|
88884
|
+
const label = mcpBaseLabel(tool);
|
|
88885
|
+
const query3 = str("query") ?? str("text") ?? str("name");
|
|
88886
|
+
if (label && query3) {
|
|
88887
|
+
const budget = Math.max(8, MAX_LABEL_CHARS - label.length - 4);
|
|
88888
|
+
const preview = truncate4(firstLine(stripHtml(query3)), budget);
|
|
88889
|
+
return `${label} (${preview})`;
|
|
88890
|
+
}
|
|
88891
|
+
if (label)
|
|
88892
|
+
return truncate4(label);
|
|
88893
|
+
}
|
|
88894
|
+
for (const k of ["description", "file_path", "path", "url", "query", "pattern", "command"]) {
|
|
88895
|
+
const v = str(k);
|
|
88896
|
+
if (v != null && v.length > 0) {
|
|
88897
|
+
if (k === "file_path" || k === "path")
|
|
88898
|
+
return truncate4(basename8(v));
|
|
88899
|
+
if (k === "url")
|
|
88900
|
+
return truncate4(hostFromUrl(v));
|
|
88901
|
+
if (k === "description")
|
|
88902
|
+
return truncate4(firstLine(v), MAX_DESCRIPTION_CHARS);
|
|
88903
|
+
return truncate4(firstLine(v));
|
|
88904
|
+
}
|
|
88905
|
+
}
|
|
88906
|
+
return "";
|
|
88907
|
+
}
|
|
88908
|
+
}
|
|
88909
|
+
function mcpBaseLabel(tool) {
|
|
88910
|
+
if (!tool.startsWith("mcp__"))
|
|
88911
|
+
return "";
|
|
88912
|
+
const parts = tool.slice("mcp__".length).split("__");
|
|
88913
|
+
if (parts.length < 2)
|
|
88914
|
+
return "";
|
|
88915
|
+
const rawServer = parts[0];
|
|
88916
|
+
const action = parts.slice(1).join("__");
|
|
88917
|
+
if (!rawServer || !action)
|
|
88918
|
+
return "";
|
|
88919
|
+
return `${prettifyServer(rawServer)}: ${action}`;
|
|
88920
|
+
}
|
|
88921
|
+
function prettifyServer(name) {
|
|
88922
|
+
const LABELS = {
|
|
88923
|
+
"switchroom-telegram": "Telegram"
|
|
88924
|
+
};
|
|
88925
|
+
if (LABELS[name])
|
|
88926
|
+
return LABELS[name];
|
|
88927
|
+
if (!name)
|
|
88928
|
+
return name;
|
|
88929
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
88930
|
+
}
|
|
88931
|
+
|
|
88932
|
+
// gateway/background-shell-liveness.ts
|
|
88933
|
+
var TERMINAL_STATUSES = new Set(["completed", "failed", "killed"]);
|
|
88934
|
+
function applyBackgroundShellLiveness(registry, key, ev) {
|
|
88935
|
+
if (ev.kind === "tool_result") {
|
|
88936
|
+
if (ev.backgroundTaskId != null && ev.backgroundTaskId.length > 0) {
|
|
88937
|
+
registry.noteBackgroundShellAlive(key, ev.backgroundTaskId);
|
|
88938
|
+
}
|
|
88939
|
+
return;
|
|
88940
|
+
}
|
|
88941
|
+
if (ev.kind === "task_notification") {
|
|
88942
|
+
if (ev.taskId.length > 0 && TERMINAL_STATUSES.has(ev.status)) {
|
|
88943
|
+
registry.noteBackgroundShellDead(key, ev.taskId);
|
|
88944
|
+
}
|
|
88945
|
+
return;
|
|
88946
|
+
}
|
|
88947
|
+
if (ev.kind === "tool_use" && ev.toolName === "KillShell") {
|
|
88948
|
+
const killId = ev.input?.shell_id;
|
|
88949
|
+
if (typeof killId === "string" && killId.length > 0) {
|
|
88950
|
+
registry.noteBackgroundShellDead(key, killId);
|
|
88951
|
+
}
|
|
88952
|
+
}
|
|
88953
|
+
}
|
|
88954
|
+
|
|
88955
|
+
// gateway/silence-poke-session-event.ts
|
|
88956
|
+
function applySilencePokeSessionEvent(silencePoke, pendingProgress, key, ev) {
|
|
88957
|
+
if (ev.kind === "thinking") {
|
|
88958
|
+
silencePoke.noteThinking(key, Date.now());
|
|
88959
|
+
} else if (ev.kind === "tool_use") {
|
|
88960
|
+
if (ev.toolUseId != null && ev.toolUseId.length > 0 && !isTelegramSurfaceTool(ev.toolName)) {
|
|
88961
|
+
const label = toolLabel(ev.toolName, ev.input, undefined, ev.precomputedLabel);
|
|
88962
|
+
silencePoke.noteToolStart(key, ev.toolUseId, ev.toolName, label.length > 0 ? label : null, Date.now());
|
|
88963
|
+
const evInput = ev.input;
|
|
88964
|
+
if (ev.toolName === "Agent" || ev.toolName === "Task" || ev.toolName === "Bash" && evInput?.run_in_background === true) {
|
|
88965
|
+
pendingProgress.noteAsyncDispatch(key);
|
|
88966
|
+
}
|
|
88967
|
+
}
|
|
88968
|
+
} else if (ev.kind === "tool_result") {
|
|
88969
|
+
if (ev.toolUseId != null && ev.toolUseId.length > 0) {
|
|
88970
|
+
silencePoke.noteToolEnd(key, ev.toolUseId, Date.now());
|
|
88971
|
+
}
|
|
88972
|
+
}
|
|
88973
|
+
applyBackgroundShellLiveness(silencePoke, key, ev);
|
|
88974
|
+
}
|
|
88975
|
+
|
|
88721
88976
|
// gateway/inbound-delivery-machine-dispatch.ts
|
|
88722
88977
|
function dispatchEffects(effects, ctx) {
|
|
88723
88978
|
for (const effect of effects) {
|
|
@@ -94446,10 +94701,38 @@ function chatFromTurnKey(turnKey3) {
|
|
|
94446
94701
|
return { chatId, threadId: Number.isFinite(t) ? t : null };
|
|
94447
94702
|
}
|
|
94448
94703
|
var OUTBOX_SWEEP_INTERVAL_MS = 5000;
|
|
94704
|
+
function createOutboxSend(deps) {
|
|
94705
|
+
return async (chatId, threadId, text4) => {
|
|
94706
|
+
const bot = deps.getBot();
|
|
94707
|
+
if (bot == null)
|
|
94708
|
+
throw new Error("outbox-sweep: bot unavailable");
|
|
94709
|
+
if (text4.length === 0)
|
|
94710
|
+
return;
|
|
94711
|
+
const replyMarkup = deps.resolveReplyMarkup?.(chatId, threadId, text4);
|
|
94712
|
+
let lastId;
|
|
94713
|
+
const chunkCount = Math.ceil(text4.length / 4000);
|
|
94714
|
+
for (let i = 0, idx = 0;i < text4.length; i += 4000, idx++) {
|
|
94715
|
+
const chunk2 = text4.slice(i, i + 4000);
|
|
94716
|
+
const isLast = idx === chunkCount - 1;
|
|
94717
|
+
const res = await retryWithThreadFallback(deps.retry, (tid) => {
|
|
94718
|
+
const base = tid != null ? { message_thread_id: tid } : {};
|
|
94719
|
+
const opts = isLast && replyMarkup != null ? { ...base, reply_markup: replyMarkup } : base;
|
|
94720
|
+
return bot.api.sendMessage(chatId, chunk2, opts);
|
|
94721
|
+
}, { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
|
|
94722
|
+
lastId = res?.message_id;
|
|
94723
|
+
}
|
|
94724
|
+
return lastId;
|
|
94725
|
+
};
|
|
94726
|
+
}
|
|
94449
94727
|
function startOutboxSweep(deps) {
|
|
94450
94728
|
if (!deps.isGatewayMain || process.env.SWITCHROOM_TG_OUTBOX_DELIVERY === "0")
|
|
94451
94729
|
return;
|
|
94452
94730
|
const retry = createRetryApiCall({ log: deps.log });
|
|
94731
|
+
const send = createOutboxSend({
|
|
94732
|
+
getBot: deps.getBot,
|
|
94733
|
+
retry,
|
|
94734
|
+
...deps.resolveReplyMarkup != null ? { resolveReplyMarkup: deps.resolveReplyMarkup } : {}
|
|
94735
|
+
});
|
|
94453
94736
|
const tick3 = () => {
|
|
94454
94737
|
const bot = deps.getBot();
|
|
94455
94738
|
if (bot == null)
|
|
@@ -94457,15 +94740,7 @@ function startOutboxSweep(deps) {
|
|
|
94457
94740
|
sweepOutbox({
|
|
94458
94741
|
stateDir: deps.stateDir,
|
|
94459
94742
|
log: deps.log,
|
|
94460
|
-
send
|
|
94461
|
-
let lastId;
|
|
94462
|
-
for (let i = 0;i < text4.length; i += 4000) {
|
|
94463
|
-
const chunk2 = text4.slice(i, i + 4000);
|
|
94464
|
-
const res = await retryWithThreadFallback(retry, (tid) => bot.api.sendMessage(chatId, chunk2, tid != null ? { message_thread_id: tid } : {}), { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
|
|
94465
|
-
lastId = res?.message_id;
|
|
94466
|
-
}
|
|
94467
|
-
return lastId;
|
|
94468
|
-
},
|
|
94743
|
+
send,
|
|
94469
94744
|
textAlreadyDelivered: (chatId, threadId, text4) => deps.dedupCheck(chatId, threadId ?? undefined, text4),
|
|
94470
94745
|
registryChainLookup: (taskId) => {
|
|
94471
94746
|
const db3 = deps.getTurnsDb();
|
|
@@ -94483,10 +94758,10 @@ function startOutboxSweep(deps) {
|
|
|
94483
94758
|
}
|
|
94484
94759
|
|
|
94485
94760
|
// ../src/build-info.ts
|
|
94486
|
-
var VERSION = "0.19.
|
|
94487
|
-
var COMMIT_SHA = "
|
|
94488
|
-
var COMMIT_DATE = "2026-07-
|
|
94489
|
-
var LATEST_PR =
|
|
94761
|
+
var VERSION = "0.19.16";
|
|
94762
|
+
var COMMIT_SHA = "e905f237";
|
|
94763
|
+
var COMMIT_DATE = "2026-07-24T18:26:03+10:00";
|
|
94764
|
+
var LATEST_PR = null;
|
|
94490
94765
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
94491
94766
|
|
|
94492
94767
|
// gateway/boot-version.ts
|
|
@@ -100144,7 +100419,7 @@ function runDeliveryConfirmSweep() {
|
|
|
100144
100419
|
}
|
|
100145
100420
|
var _deliveryConfirmSweep = isGatewayMain ? setInterval(runDeliveryConfirmSweep, DELIVERY_CONFIRM_SWEEP_MS) : undefined;
|
|
100146
100421
|
_deliveryConfirmSweep?.unref?.();
|
|
100147
|
-
startOutboxSweep({ isGatewayMain, stateDir: STATE_DIR, getBot: () => bot, getTurnsDb: () => turnsDb, dedupCheck: (c, t, x) => outboundDedup.check(c, t, x, Date.now()) != null, log: (l) => process.stderr.write(l) });
|
|
100422
|
+
startOutboxSweep({ isGatewayMain, stateDir: STATE_DIR, getBot: () => bot, getTurnsDb: () => turnsDb, dedupCheck: (c, t, x) => outboundDedup.check(c, t, x, Date.now()) != null, resolveReplyMarkup: makeOutboxListenMarkupResolver({ resolveVoiceOutPlan: (t) => resolveVoiceOutPlan(loadAccess().voice_out, t), cachePut: (token, payload) => voiceOnDemandCache.put(token, payload), eagerVoiceEnabled, enqueuePreSynth: (j) => voicePreSynthQueue.enqueue(j) }), log: (l) => process.stderr.write(l) });
|
|
100148
100423
|
if (isGatewayMain)
|
|
100149
100424
|
startTimer2({
|
|
100150
100425
|
editMessage: async (ctx) => {
|
|
@@ -100617,22 +100892,7 @@ if (isGatewayMain)
|
|
|
100617
100892
|
notifyHaltBoundaryWaiters();
|
|
100618
100893
|
if (currentTurn != null) {
|
|
100619
100894
|
const key = statusKey(currentTurn.sessionChatId, currentTurn.sessionThreadId);
|
|
100620
|
-
|
|
100621
|
-
noteThinking(key, Date.now());
|
|
100622
|
-
} else if (ev.kind === "tool_use") {
|
|
100623
|
-
if (ev.toolUseId != null && ev.toolUseId.length > 0 && !isTelegramSurfaceTool2(ev.toolName)) {
|
|
100624
|
-
const label = toolLabel(ev.toolName, ev.input, undefined, ev.precomputedLabel);
|
|
100625
|
-
noteToolStart(key, ev.toolUseId, ev.toolName, label.length > 0 ? label : null, Date.now());
|
|
100626
|
-
const evInput = ev.input;
|
|
100627
|
-
if (ev.toolName === "Agent" || ev.toolName === "Task" || ev.toolName === "Bash" && evInput?.run_in_background === true) {
|
|
100628
|
-
noteAsyncDispatch(key);
|
|
100629
|
-
}
|
|
100630
|
-
}
|
|
100631
|
-
} else if (ev.kind === "tool_result") {
|
|
100632
|
-
if (ev.toolUseId != null && ev.toolUseId.length > 0) {
|
|
100633
|
-
noteToolEnd(key, ev.toolUseId, Date.now());
|
|
100634
|
-
}
|
|
100635
|
-
}
|
|
100895
|
+
applySilencePokeSessionEvent(exports_silence_poke, exports_pending_work_progress, key, ev);
|
|
100636
100896
|
}
|
|
100637
100897
|
},
|
|
100638
100898
|
onPermissionRequest(_client, msg) {
|
|
@@ -107131,7 +107391,7 @@ ${labelWithResume}` : labelWithResume,
|
|
|
107131
107391
|
handleChecklistUpdate(ctx, "checklist_tasks_added", checklistHandlerDeps);
|
|
107132
107392
|
});
|
|
107133
107393
|
bot2.on("message:pinned_message", (ctx) => handlePinnedMessage(ctx, pinnedMessageHandlerDeps));
|
|
107134
|
-
bot2.on("message:rich_message", (ctx) => handleRichMessageMessage(ctx, mediaEnvelopeDeps));
|
|
107394
|
+
bot2.on("message:rich_message", (ctx) => handleRichMessageMessage(ctx, { ...mediaEnvelopeDeps, handleInbound: handleInboundCoalesced }));
|
|
107135
107395
|
installUnhandledMessageCatchAll(bot2, (ctx, text5) => routeInbound(ctx, text5, undefined, undefined, inboundRouterDeps), (line) => process.stderr.write(line));
|
|
107136
107396
|
bot2.on("message_reaction", (ctx) => {
|
|
107137
107397
|
handleMessageReaction(ctx);
|