switchroom 0.21.9 → 0.21.11
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 +143 -27
- package/dist/host-control/main.js +1 -1
- package/package.json +3 -2
- package/telegram-plugin/dist/gateway/gateway.js +1152 -296
- package/telegram-plugin/format.ts +74 -1
- package/telegram-plugin/gateway/answer-route-overrides.ts +163 -0
- package/telegram-plugin/gateway/answer-thread-resolve.test.ts +175 -1
- package/telegram-plugin/gateway/answer-thread-resolve.ts +58 -6
- package/telegram-plugin/gateway/escalation-staleness.ts +526 -0
- package/telegram-plugin/gateway/gateway.ts +61 -68
- package/telegram-plugin/gateway/obligation-wiring.ts +91 -3
- package/telegram-plugin/gateway/outbound-send-path.ts +62 -1
- package/telegram-plugin/gateway/reply-route-log.test.ts +134 -0
- package/telegram-plugin/gateway/reply-route-log.ts +118 -0
- package/telegram-plugin/gateway/speech-capture.ts +158 -0
- package/telegram-plugin/gateway/stream-render.ts +1 -1
- package/telegram-plugin/history.ts +21 -0
- package/telegram-plugin/registry/subagents-bugs.test.ts +3 -3
- package/telegram-plugin/render/html-fold.ts +372 -0
- package/telegram-plugin/render/parse.ts +578 -29
- package/telegram-plugin/render/render.ts +14 -13
- package/telegram-plugin/tests/answer-route-side-effect.test.ts +111 -0
- package/telegram-plugin/tests/catch-all-forwarded-history.test.ts +3 -3
- package/telegram-plugin/tests/escalation-staleness.test.ts +1275 -0
- package/telegram-plugin/tests/forwarded-rich-message-coalesce.test.ts +6 -6
- package/telegram-plugin/tests/forwarded-rich-message.test.ts +8 -8
- package/telegram-plugin/tests/history.test.ts +78 -0
- package/telegram-plugin/tests/multitopic-routing-wiring.test.ts +29 -1
- package/telegram-plugin/tests/orphaned-db-sweep.test.ts +17 -1
- package/telegram-plugin/tests/render/html-dialect-content-loss.test.ts +326 -0
- package/telegram-plugin/tests/render/html-dialect.test.ts +283 -0
- package/telegram-plugin/tests/render/parse.test.ts +9 -5
- package/telegram-plugin/tests/send-reply-golden.test.ts +290 -3
- package/telegram-plugin/tests/speech-capture.test.ts +296 -0
- package/telegram-plugin/tests/status-pin.test.ts +2 -2
- package/telegram-plugin/tests/subagent-handback-inbound-builder.test.ts +2 -2
- package/telegram-plugin/tests/subagent-progress-inbound-builder.test.ts +2 -2
- package/telegram-plugin/tests/telegram-format.test.ts +52 -0
- package/telegram-plugin/tests/tts-normalize.test.ts +114 -0
- package/telegram-plugin/tests/turn-supersede-finalizes-prior-card.test.ts +1 -1
- package/telegram-plugin/tests/voice-normalize-text.test.ts +89 -0
- package/telegram-plugin/tests/worker-origin-gap-dispatch.test.ts +1 -1
- package/telegram-plugin/tts-normalize.ts +47 -9
- package/telegram-plugin/uat/scenarios/jtbd-supergroup-reply-channel.test.ts +1 -1
- package/telegram-plugin/voice-normalize-text.ts +48 -9
- package/telegram-plugin/worker-activity-feed.ts +2 -2
|
@@ -6769,8 +6769,22 @@ function escapeMarkdown(text) {
|
|
|
6769
6769
|
function codeSpanSafe(s) {
|
|
6770
6770
|
return s.replace(/`/g, "`\u200b");
|
|
6771
6771
|
}
|
|
6772
|
+
function codeFenceFor(text) {
|
|
6773
|
+
let longestRun = 0;
|
|
6774
|
+
let current = 0;
|
|
6775
|
+
for (let i = 0;i < text.length; i++) {
|
|
6776
|
+
if (text[i] === "`") {
|
|
6777
|
+
current++;
|
|
6778
|
+
if (current > longestRun)
|
|
6779
|
+
longestRun = current;
|
|
6780
|
+
} else {
|
|
6781
|
+
current = 0;
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
return "`".repeat(Math.max(3, longestRun + 1));
|
|
6785
|
+
}
|
|
6772
6786
|
function escapeLinkHref(href) {
|
|
6773
|
-
return href.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
|
|
6787
|
+
return href.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)").replace(/[\x00-\x20\x7f]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`);
|
|
6774
6788
|
}
|
|
6775
6789
|
function repairEscapedWhitespace(text) {
|
|
6776
6790
|
if (!/\\[nrt"\\]/.test(text))
|
|
@@ -30173,7 +30187,7 @@ __export(exports_history, {
|
|
|
30173
30187
|
MIN_PREFIX_MATCH_CHARS: () => MIN_PREFIX_MATCH_CHARS2
|
|
30174
30188
|
});
|
|
30175
30189
|
import { chmodSync as chmodSync9, existsSync as existsSync29 } from "fs";
|
|
30176
|
-
import { join as
|
|
30190
|
+
import { join as join38 } from "path";
|
|
30177
30191
|
function loadDatabaseClass2() {
|
|
30178
30192
|
if (DatabaseClass2 != null)
|
|
30179
30193
|
return DatabaseClass2;
|
|
@@ -30231,7 +30245,7 @@ function initHistory2(stateDir, retentionDays = 30) {
|
|
|
30231
30245
|
return;
|
|
30232
30246
|
const Database = loadDatabaseClass2();
|
|
30233
30247
|
mkdirStateSync(stateDir, { recursive: true, mode: 448 });
|
|
30234
|
-
const path2 =
|
|
30248
|
+
const path2 = join38(stateDir, "history.db");
|
|
30235
30249
|
dbPath2 = path2;
|
|
30236
30250
|
db2 = new Database(path2, { create: true });
|
|
30237
30251
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
@@ -30565,12 +30579,16 @@ function getRecentOutboundCount(chatId, withinSeconds) {
|
|
|
30565
30579
|
const row = prep2("SELECT COUNT(*) as cnt FROM messages WHERE chat_id = ? AND role = ? AND ts >= ?").get(chatId, "assistant", cutoff);
|
|
30566
30580
|
return row?.cnt ?? 0;
|
|
30567
30581
|
}
|
|
30568
|
-
function hasOutboundDeliveredSince2(chatId, sinceMs, threadId, minChars = 200) {
|
|
30582
|
+
function hasOutboundDeliveredSince2(chatId, sinceMs, threadId, minChars = 200, untilMs) {
|
|
30569
30583
|
try {
|
|
30570
30584
|
const cutoffSec = Math.floor(sinceMs / 1000);
|
|
30571
30585
|
const minLen = Math.max(1, Math.floor(minChars));
|
|
30572
30586
|
const params = [chatId, cutoffSec, minLen];
|
|
30573
30587
|
let sql = "SELECT 1 FROM messages WHERE chat_id = ? AND role = 'assistant' AND ts >= ? AND LENGTH(text) >= ?";
|
|
30588
|
+
if (untilMs != null && Number.isFinite(untilMs)) {
|
|
30589
|
+
sql += " AND ts <= ?";
|
|
30590
|
+
params.push(Math.ceil(untilMs / 1000));
|
|
30591
|
+
}
|
|
30574
30592
|
if (threadId !== undefined) {
|
|
30575
30593
|
if (threadId === null) {
|
|
30576
30594
|
sql += " AND thread_id IS NULL";
|
|
@@ -30685,7 +30703,7 @@ var init_turn_flush_suppression = () => {};
|
|
|
30685
30703
|
// ../src/agents/generation-stamp.ts
|
|
30686
30704
|
import { createHash as createHash6 } from "node:crypto";
|
|
30687
30705
|
import { existsSync as existsSync36, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "node:fs";
|
|
30688
|
-
import { join as
|
|
30706
|
+
import { join as join45 } from "node:path";
|
|
30689
30707
|
function sha256Text(s) {
|
|
30690
30708
|
return createHash6("sha256").update(s, "utf-8").digest("hex");
|
|
30691
30709
|
}
|
|
@@ -30695,7 +30713,7 @@ function hashManagedClaudeMd(content3) {
|
|
|
30695
30713
|
return sha256Text(managed);
|
|
30696
30714
|
}
|
|
30697
30715
|
function hashStampedFileOnDisk(agentDir, rel) {
|
|
30698
|
-
const p =
|
|
30716
|
+
const p = join45(agentDir, rel);
|
|
30699
30717
|
if (!existsSync36(p))
|
|
30700
30718
|
return null;
|
|
30701
30719
|
let content3;
|
|
@@ -30707,7 +30725,7 @@ function hashStampedFileOnDisk(agentDir, rel) {
|
|
|
30707
30725
|
return rel === "CLAUDE.md" ? hashManagedClaudeMd(content3) : sha256Text(content3);
|
|
30708
30726
|
}
|
|
30709
30727
|
function readGenerationStamp(agentDir) {
|
|
30710
|
-
const p =
|
|
30728
|
+
const p = join45(agentDir, GENERATION_STAMP_FILE);
|
|
30711
30729
|
if (!existsSync36(p))
|
|
30712
30730
|
return null;
|
|
30713
30731
|
try {
|
|
@@ -33711,7 +33729,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
33711
33729
|
return path2;
|
|
33712
33730
|
}
|
|
33713
33731
|
exports2.normalize = normalize;
|
|
33714
|
-
function
|
|
33732
|
+
function join47(aRoot, aPath) {
|
|
33715
33733
|
if (aRoot === "") {
|
|
33716
33734
|
aRoot = ".";
|
|
33717
33735
|
}
|
|
@@ -33743,7 +33761,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
33743
33761
|
}
|
|
33744
33762
|
return joined;
|
|
33745
33763
|
}
|
|
33746
|
-
exports2.join =
|
|
33764
|
+
exports2.join = join47;
|
|
33747
33765
|
exports2.isAbsolute = function(aPath) {
|
|
33748
33766
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
33749
33767
|
};
|
|
@@ -33916,7 +33934,7 @@ var require_util = __commonJS((exports2) => {
|
|
|
33916
33934
|
parsed.path = parsed.path.substring(0, index2 + 1);
|
|
33917
33935
|
}
|
|
33918
33936
|
}
|
|
33919
|
-
sourceURL =
|
|
33937
|
+
sourceURL = join47(urlGenerate(parsed), sourceURL);
|
|
33920
33938
|
}
|
|
33921
33939
|
return normalize(sourceURL);
|
|
33922
33940
|
}
|
|
@@ -36522,9 +36540,9 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
|
|
|
36522
36540
|
|
|
36523
36541
|
// gateway/quota-cache.ts
|
|
36524
36542
|
import { existsSync as existsSync49, readFileSync as readFileSync49, writeFileSync as writeFileSync41, mkdirSync as mkdirSync42 } from "fs";
|
|
36525
|
-
import { join as
|
|
36543
|
+
import { join as join60, dirname as dirname25 } from "path";
|
|
36526
36544
|
function defaultCachePath() {
|
|
36527
|
-
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ??
|
|
36545
|
+
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join60(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
36528
36546
|
}
|
|
36529
36547
|
function readQuotaCache(opts = {}) {
|
|
36530
36548
|
const path3 = opts.path ?? defaultCachePath();
|
|
@@ -36571,7 +36589,7 @@ var init_quota_cache = __esm(() => {
|
|
|
36571
36589
|
|
|
36572
36590
|
// gateway/boot-probes.ts
|
|
36573
36591
|
import { readFileSync as readFileSync50, readdirSync as readdirSync14, existsSync as existsSync50 } from "fs";
|
|
36574
|
-
import { join as
|
|
36592
|
+
import { join as join61 } from "path";
|
|
36575
36593
|
import { execFile as execFileCb } from "child_process";
|
|
36576
36594
|
import { promisify } from "util";
|
|
36577
36595
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
@@ -36613,8 +36631,8 @@ function mapPlan(billingType, hasExtra) {
|
|
|
36613
36631
|
}
|
|
36614
36632
|
async function probeAccount(agentDir) {
|
|
36615
36633
|
return withTimeout("Account", (async () => {
|
|
36616
|
-
const claudeDir =
|
|
36617
|
-
const claudeJsonPath =
|
|
36634
|
+
const claudeDir = join61(agentDir, ".claude");
|
|
36635
|
+
const claudeJsonPath = join61(claudeDir, ".claude.json");
|
|
36618
36636
|
let cfg = {};
|
|
36619
36637
|
try {
|
|
36620
36638
|
const raw = readFileSync50(claudeJsonPath, "utf8");
|
|
@@ -36635,8 +36653,8 @@ async function probeAccount(agentDir) {
|
|
|
36635
36653
|
let tokenStr = "";
|
|
36636
36654
|
let status = "ok";
|
|
36637
36655
|
for (const candidate of [
|
|
36638
|
-
|
|
36639
|
-
|
|
36656
|
+
join61(claudeDir, ".oauth-token.meta.json"),
|
|
36657
|
+
join61(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
36640
36658
|
]) {
|
|
36641
36659
|
if (existsSync50(candidate)) {
|
|
36642
36660
|
try {
|
|
@@ -37022,9 +37040,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
37022
37040
|
let claudeDirForProbe = null;
|
|
37023
37041
|
for (const candidate of [
|
|
37024
37042
|
claudeConfigDir,
|
|
37025
|
-
|
|
37043
|
+
join61(claudeConfigDir, "accounts", "default")
|
|
37026
37044
|
]) {
|
|
37027
|
-
if (existsSync50(
|
|
37045
|
+
if (existsSync50(join61(candidate, ".oauth-token"))) {
|
|
37028
37046
|
claudeDirForProbe = candidate;
|
|
37029
37047
|
break;
|
|
37030
37048
|
}
|
|
@@ -37259,7 +37277,7 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
37259
37277
|
return withTimeout("Skills", (async () => {
|
|
37260
37278
|
const fs3 = opts.fs ?? realSkillsFs;
|
|
37261
37279
|
const max = opts.maxNamesShown ?? 3;
|
|
37262
|
-
const skillsDir =
|
|
37280
|
+
const skillsDir = join61(agentDir, ".claude", "skills");
|
|
37263
37281
|
if (!fs3.exists(skillsDir)) {
|
|
37264
37282
|
return { status: "ok", label: "Skills", detail: "no skills dir" };
|
|
37265
37283
|
}
|
|
@@ -37274,17 +37292,17 @@ async function probeSkills(agentDir, opts = {}) {
|
|
|
37274
37292
|
}
|
|
37275
37293
|
const dangling = [];
|
|
37276
37294
|
for (const name of entries) {
|
|
37277
|
-
const skillPath =
|
|
37295
|
+
const skillPath = join61(skillsDir, name);
|
|
37278
37296
|
if (!fs3.exists(skillPath)) {
|
|
37279
37297
|
dangling.push(name);
|
|
37280
37298
|
continue;
|
|
37281
37299
|
}
|
|
37282
|
-
const skillMd =
|
|
37300
|
+
const skillMd = join61(skillPath, "SKILL.md");
|
|
37283
37301
|
if (!fs3.exists(skillMd) && !fs3.exists(skillPath + ".md")) {
|
|
37284
37302
|
continue;
|
|
37285
37303
|
}
|
|
37286
37304
|
}
|
|
37287
|
-
const overlayDir = opts.overlaySkillsDir ??
|
|
37305
|
+
const overlayDir = opts.overlaySkillsDir ?? join61(agentDir, "skills.d");
|
|
37288
37306
|
const overlaySlugs = new Set;
|
|
37289
37307
|
if (fs3.exists(overlayDir)) {
|
|
37290
37308
|
let overlayEntries = [];
|
|
@@ -37326,7 +37344,7 @@ function renderBucketedSkills(switchroom, agent) {
|
|
|
37326
37344
|
}
|
|
37327
37345
|
async function probeConnections(agentDir, opts = {}) {
|
|
37328
37346
|
return withTimeout("Connections", (async () => {
|
|
37329
|
-
const path3 =
|
|
37347
|
+
const path3 = join61(agentDir, ".claude", "connection-health.json");
|
|
37330
37348
|
const read = opts.readFileImpl ?? ((p) => readFileSync50(p, "utf8"));
|
|
37331
37349
|
let issues = [];
|
|
37332
37350
|
try {
|
|
@@ -37367,7 +37385,7 @@ async function probeDrift(agentDir, opts = {}) {
|
|
|
37367
37385
|
}
|
|
37368
37386
|
} catch {}
|
|
37369
37387
|
try {
|
|
37370
|
-
const raw = readFileSync50(
|
|
37388
|
+
const raw = readFileSync50(join61(agentDir, ".switchroom-drift.json"), "utf8");
|
|
37371
37389
|
const report = JSON.parse(raw);
|
|
37372
37390
|
if (report?.version === 1 && Array.isArray(report.findings)) {
|
|
37373
37391
|
const gen = report.generatedAt ? Date.parse(report.generatedAt) : NaN;
|
|
@@ -37727,7 +37745,7 @@ __export(exports_boot_card, {
|
|
|
37727
37745
|
readRoutingMode: () => readRoutingMode,
|
|
37728
37746
|
containerBootStartMs: () => containerBootStartMs
|
|
37729
37747
|
});
|
|
37730
|
-
import { join as
|
|
37748
|
+
import { join as join62 } from "path";
|
|
37731
37749
|
import { readFileSync as readFileSync54, statSync as statSync19 } from "fs";
|
|
37732
37750
|
function resolvePersonaName(slug, loadConfig3) {
|
|
37733
37751
|
try {
|
|
@@ -37772,7 +37790,7 @@ function containerBootStartMs(fsImpl = { readFileSync: readFileSync54 }) {
|
|
|
37772
37790
|
}
|
|
37773
37791
|
}
|
|
37774
37792
|
function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync54, statSync: statSync19 }) {
|
|
37775
|
-
const path3 =
|
|
37793
|
+
const path3 = join62(agentDir, ".routing-mode");
|
|
37776
37794
|
try {
|
|
37777
37795
|
const raw = fsImpl.readFileSync(path3, "utf-8");
|
|
37778
37796
|
const line = raw.split(`
|
|
@@ -37879,7 +37897,7 @@ function renderBootCard(opts) {
|
|
|
37879
37897
|
return stackCardLines(flatLines);
|
|
37880
37898
|
}
|
|
37881
37899
|
async function runAllProbes(opts) {
|
|
37882
|
-
const claudeDir =
|
|
37900
|
+
const claudeDir = join62(opts.agentDir, ".claude");
|
|
37883
37901
|
const probes = {};
|
|
37884
37902
|
const slug = opts.agentSlug ?? opts.agentName;
|
|
37885
37903
|
await Promise.allSettled([
|
|
@@ -39870,7 +39888,7 @@ import {
|
|
|
39870
39888
|
closeSync as closeSync18,
|
|
39871
39889
|
existsSync as existsSync65,
|
|
39872
39890
|
unlinkSync as unlinkSync33,
|
|
39873
|
-
appendFileSync as
|
|
39891
|
+
appendFileSync as appendFileSync12
|
|
39874
39892
|
} from "fs";
|
|
39875
39893
|
|
|
39876
39894
|
// ../src/util/atomic.ts
|
|
@@ -39887,7 +39905,7 @@ function fsyncPathSync(path) {
|
|
|
39887
39905
|
|
|
39888
39906
|
// gateway/gateway.ts
|
|
39889
39907
|
import { homedir as homedir22 } from "os";
|
|
39890
|
-
import { join as
|
|
39908
|
+
import { join as join78, sep as sep6, basename as basename19 } from "path";
|
|
39891
39909
|
|
|
39892
39910
|
// plugin-logger.ts
|
|
39893
39911
|
import { appendFileSync, mkdirSync, renameSync as renameSync2, statSync, existsSync } from "fs";
|
|
@@ -41632,7 +41650,7 @@ function normalizeForSpeech(input) {
|
|
|
41632
41650
|
const w = numberToWords(Number(n) * 1000);
|
|
41633
41651
|
return w ? w : m;
|
|
41634
41652
|
});
|
|
41635
|
-
|
|
41653
|
+
const unitReplacer = (m, num, unitRaw) => {
|
|
41636
41654
|
const unit = UNIT_MAP[unitRaw.toLowerCase()];
|
|
41637
41655
|
if (!unit)
|
|
41638
41656
|
return m;
|
|
@@ -41641,7 +41659,9 @@ function normalizeForSpeech(input) {
|
|
|
41641
41659
|
if (!w)
|
|
41642
41660
|
return m;
|
|
41643
41661
|
return `${w} ${n === 1 ? unit.s : unit.p}`;
|
|
41644
|
-
}
|
|
41662
|
+
};
|
|
41663
|
+
s = s.replace(/(?<![\d.,])\b(\d{1,9})(ms|sec|min|kb|mb|gb|tb|hr)(?![a-zA-Z])/gi, unitReplacer);
|
|
41664
|
+
s = s.replace(/(?<![\d.,])\b(\d{1,9})(s|m|h|d)(?![a-zA-Z])/g, unitReplacer);
|
|
41645
41665
|
s = s.replace(/(\S)\s*\+\s*(\S)/g, "$1 plus $2");
|
|
41646
41666
|
s = s.replace(/\s=\s/g, " equals ");
|
|
41647
41667
|
s = s.replace(/(\s)&(\s)/g, "$1and$2");
|
|
@@ -42003,7 +42023,7 @@ table omitted.`);
|
|
|
42003
42023
|
const w = ordinalToWords(num);
|
|
42004
42024
|
return w ?? m;
|
|
42005
42025
|
});
|
|
42006
|
-
|
|
42026
|
+
const unitReplacer = (m, num, unitRaw) => {
|
|
42007
42027
|
const unit = UNIT_MAP2[unitRaw.toLowerCase()];
|
|
42008
42028
|
if (!unit)
|
|
42009
42029
|
return m;
|
|
@@ -42012,7 +42032,9 @@ table omitted.`);
|
|
|
42012
42032
|
if (!w)
|
|
42013
42033
|
return m;
|
|
42014
42034
|
return `${w} ${n === 1 ? unit.s : unit.p}`;
|
|
42015
|
-
}
|
|
42035
|
+
};
|
|
42036
|
+
s = s.replace(/(?<![\d.,])\b(\d{1,9})\s?(ms|sec|min|km|cm|mm|kg|kb|mb|gb|tb|ghz|mhz|hr|mi)(?![a-zA-Z])/gi, unitReplacer);
|
|
42037
|
+
s = s.replace(/(?<![\d.,])\b(\d{1,9})\s?(s|m|h|d|g)(?![a-zA-Z])/g, unitReplacer);
|
|
42016
42038
|
s = s.replace(/(\s)&(\s)/g, "$1and$2");
|
|
42017
42039
|
s = s.replace(/(\w)&(\w)/g, "$1 and $2");
|
|
42018
42040
|
s = s.replace(/(^|\s)@(\w)/g, "$1at $2");
|
|
@@ -48111,7 +48133,7 @@ table omitted.`);
|
|
|
48111
48133
|
const w = ordinalToWords2(num);
|
|
48112
48134
|
return w ?? m;
|
|
48113
48135
|
});
|
|
48114
|
-
|
|
48136
|
+
const unitReplacer = (m, num, unitRaw) => {
|
|
48115
48137
|
const unit = UNIT_MAP3[unitRaw.toLowerCase()];
|
|
48116
48138
|
if (!unit)
|
|
48117
48139
|
return m;
|
|
@@ -48120,7 +48142,9 @@ table omitted.`);
|
|
|
48120
48142
|
if (!w)
|
|
48121
48143
|
return m;
|
|
48122
48144
|
return `${w} ${n === 1 ? unit.s : unit.p}`;
|
|
48123
|
-
}
|
|
48145
|
+
};
|
|
48146
|
+
s = s.replace(/(?<![\d.,])\b(\d{1,9})\s?(ms|sec|min|km|cm|mm|kg|kb|mb|gb|tb|ghz|mhz|hr|mi)(?![a-zA-Z])/gi, unitReplacer);
|
|
48147
|
+
s = s.replace(/(?<![\d.,])\b(\d{1,9})\s?(s|m|h|d|g)(?![a-zA-Z])/g, unitReplacer);
|
|
48124
48148
|
s = s.replace(/(\s)&(\s)/g, "$1and$2");
|
|
48125
48149
|
s = s.replace(/(\w)&(\w)/g, "$1 and $2");
|
|
48126
48150
|
s = s.replace(/(^|\s)@(\w)/g, "$1at $2");
|
|
@@ -65005,7 +65029,178 @@ function gfmFromMarkdown() {
|
|
|
65005
65029
|
gfmTaskListItemFromMarkdown()
|
|
65006
65030
|
];
|
|
65007
65031
|
}
|
|
65032
|
+
// render/html-fold.ts
|
|
65033
|
+
var HTML_FOLD_TAGS = {
|
|
65034
|
+
b: "bold",
|
|
65035
|
+
strong: "bold",
|
|
65036
|
+
i: "italic",
|
|
65037
|
+
em: "italic",
|
|
65038
|
+
s: "strike",
|
|
65039
|
+
del: "strike",
|
|
65040
|
+
strike: "strike",
|
|
65041
|
+
code: "code",
|
|
65042
|
+
a: "link",
|
|
65043
|
+
br: "break",
|
|
65044
|
+
pre: "pre"
|
|
65045
|
+
};
|
|
65046
|
+
var HTML_VOID_TAGS = new Set([
|
|
65047
|
+
"area",
|
|
65048
|
+
"base",
|
|
65049
|
+
"col",
|
|
65050
|
+
"embed",
|
|
65051
|
+
"hr",
|
|
65052
|
+
"img",
|
|
65053
|
+
"input",
|
|
65054
|
+
"link",
|
|
65055
|
+
"meta",
|
|
65056
|
+
"param",
|
|
65057
|
+
"source",
|
|
65058
|
+
"track",
|
|
65059
|
+
"wbr"
|
|
65060
|
+
]);
|
|
65061
|
+
var HTML_BLOCK_LEVEL_TAGS = new Set([
|
|
65062
|
+
"address",
|
|
65063
|
+
"article",
|
|
65064
|
+
"blockquote",
|
|
65065
|
+
"center",
|
|
65066
|
+
"dd",
|
|
65067
|
+
"div",
|
|
65068
|
+
"dl",
|
|
65069
|
+
"dt",
|
|
65070
|
+
"fieldset",
|
|
65071
|
+
"figcaption",
|
|
65072
|
+
"figure",
|
|
65073
|
+
"footer",
|
|
65074
|
+
"form",
|
|
65075
|
+
"h1",
|
|
65076
|
+
"h2",
|
|
65077
|
+
"h3",
|
|
65078
|
+
"h4",
|
|
65079
|
+
"h5",
|
|
65080
|
+
"h6",
|
|
65081
|
+
"header",
|
|
65082
|
+
"hr",
|
|
65083
|
+
"li",
|
|
65084
|
+
"main",
|
|
65085
|
+
"nav",
|
|
65086
|
+
"ol",
|
|
65087
|
+
"p",
|
|
65088
|
+
"section",
|
|
65089
|
+
"table",
|
|
65090
|
+
"tbody",
|
|
65091
|
+
"td",
|
|
65092
|
+
"tfoot",
|
|
65093
|
+
"th",
|
|
65094
|
+
"thead",
|
|
65095
|
+
"tr",
|
|
65096
|
+
"ul"
|
|
65097
|
+
]);
|
|
65098
|
+
function isBlockLevelTag(name) {
|
|
65099
|
+
return HTML_BLOCK_LEVEL_TAGS.has(name);
|
|
65100
|
+
}
|
|
65101
|
+
function isVoidTag(name) {
|
|
65102
|
+
return HTML_VOID_TAGS.has(name);
|
|
65103
|
+
}
|
|
65104
|
+
function escapeHtmlLiteral(text4) {
|
|
65105
|
+
return text4.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
65106
|
+
}
|
|
65107
|
+
var LANGUAGE_CLASS_RE = /(?:^|\s)class\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))/i;
|
|
65108
|
+
function languageOf(tag) {
|
|
65109
|
+
const m = LANGUAGE_CLASS_RE.exec(tag.attrs);
|
|
65110
|
+
if (m == null)
|
|
65111
|
+
return null;
|
|
65112
|
+
const cls = m[1] ?? m[2] ?? m[3] ?? "";
|
|
65113
|
+
const lang = cls.split(/\s+/).map((c) => /^language-(.+)$/.exec(c)?.[1]).find((c) => c != null && c.length > 0);
|
|
65114
|
+
return lang != null && !lang.includes("`") ? lang : null;
|
|
65115
|
+
}
|
|
65116
|
+
var HTML_PASSTHROUGH_TAGS = new Set([
|
|
65117
|
+
"u",
|
|
65118
|
+
"sub",
|
|
65119
|
+
"sup",
|
|
65120
|
+
"details",
|
|
65121
|
+
"summary",
|
|
65122
|
+
"aside",
|
|
65123
|
+
"cite"
|
|
65124
|
+
]);
|
|
65125
|
+
var HTML_TOKEN_RE = /<!--[\s\S]*?-->|<\/?[A-Za-z][A-Za-z0-9-]*(?:\s[^<>]*?)?\/?>/g;
|
|
65126
|
+
var TAG_RE = /^<(\/?)([A-Za-z][A-Za-z0-9-]*)((?:\s[^<>]*?)?)(\/?)>$/;
|
|
65127
|
+
var HREF_RE = /(?:^|\s)href\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))/i;
|
|
65128
|
+
function classifyHtmlTag(raw) {
|
|
65129
|
+
if (raw.startsWith("<!--"))
|
|
65130
|
+
return { kind: "comment", name: "", raw, attrs: "" };
|
|
65131
|
+
const m = TAG_RE.exec(raw);
|
|
65132
|
+
if (m == null)
|
|
65133
|
+
return { kind: "other", name: "", raw, attrs: "" };
|
|
65134
|
+
const [, slash, name, attrs, selfClose] = m;
|
|
65135
|
+
const kind = slash === "/" ? "close" : selfClose === "/" ? "selfclose" : "open";
|
|
65136
|
+
return { kind, name: name.toLowerCase(), raw, attrs: attrs ?? "" };
|
|
65137
|
+
}
|
|
65138
|
+
function hrefOf(tag) {
|
|
65139
|
+
const m = HREF_RE.exec(tag.attrs);
|
|
65140
|
+
if (m == null)
|
|
65141
|
+
return null;
|
|
65142
|
+
const value = m[1] ?? m[2] ?? m[3] ?? "";
|
|
65143
|
+
return value.length > 0 ? value : null;
|
|
65144
|
+
}
|
|
65145
|
+
function isPassthroughTag(tag) {
|
|
65146
|
+
return tag.name.length > 0 && HTML_PASSTHROUGH_TAGS.has(tag.name);
|
|
65147
|
+
}
|
|
65148
|
+
function matchedTagOffsets(tokens) {
|
|
65149
|
+
const matched = new Set;
|
|
65150
|
+
const stack = [];
|
|
65151
|
+
for (const { tag, start } of tokens) {
|
|
65152
|
+
if (tag.kind === "open" && !isVoidTag(tag.name)) {
|
|
65153
|
+
stack.push({ name: tag.name, start });
|
|
65154
|
+
continue;
|
|
65155
|
+
}
|
|
65156
|
+
if (tag.kind !== "close")
|
|
65157
|
+
continue;
|
|
65158
|
+
for (let i = stack.length - 1;i >= 0; i--) {
|
|
65159
|
+
if (stack[i].name !== tag.name)
|
|
65160
|
+
continue;
|
|
65161
|
+
matched.add(stack[i].start);
|
|
65162
|
+
matched.add(start);
|
|
65163
|
+
stack.length = i;
|
|
65164
|
+
break;
|
|
65165
|
+
}
|
|
65166
|
+
}
|
|
65167
|
+
return matched;
|
|
65168
|
+
}
|
|
65169
|
+
function tokenizeHtml(raw, base) {
|
|
65170
|
+
const pieces = [];
|
|
65171
|
+
const re = new RegExp(HTML_TOKEN_RE.source, "g");
|
|
65172
|
+
let last = 0;
|
|
65173
|
+
let m;
|
|
65174
|
+
while ((m = re.exec(raw)) != null) {
|
|
65175
|
+
if (m.index > last) {
|
|
65176
|
+
pieces.push({
|
|
65177
|
+
kind: "text",
|
|
65178
|
+
text: raw.slice(last, m.index),
|
|
65179
|
+
start: base + last,
|
|
65180
|
+
end: base + m.index
|
|
65181
|
+
});
|
|
65182
|
+
}
|
|
65183
|
+
pieces.push({
|
|
65184
|
+
kind: "tag",
|
|
65185
|
+
tag: classifyHtmlTag(m[0]),
|
|
65186
|
+
start: base + m.index,
|
|
65187
|
+
end: base + m.index + m[0].length
|
|
65188
|
+
});
|
|
65189
|
+
last = m.index + m[0].length;
|
|
65190
|
+
}
|
|
65191
|
+
if (last < raw.length) {
|
|
65192
|
+
pieces.push({
|
|
65193
|
+
kind: "text",
|
|
65194
|
+
text: raw.slice(last),
|
|
65195
|
+
start: base + last,
|
|
65196
|
+
end: base + raw.length
|
|
65197
|
+
});
|
|
65198
|
+
}
|
|
65199
|
+
return pieces;
|
|
65200
|
+
}
|
|
65201
|
+
|
|
65008
65202
|
// render/parse.ts
|
|
65203
|
+
init_format();
|
|
65009
65204
|
function pos(node2) {
|
|
65010
65205
|
const p = node2.position;
|
|
65011
65206
|
return {
|
|
@@ -65023,11 +65218,32 @@ function isTgInlineEntityHref(href) {
|
|
|
65023
65218
|
const h = href.toLowerCase();
|
|
65024
65219
|
return TG_INLINE_ENTITY_HREFS.some((base) => h === base || h.startsWith(`${base}?`));
|
|
65025
65220
|
}
|
|
65221
|
+
var FENCE_DELIM_RE = /^ {0,3}(`{3,}|~{3,})/;
|
|
65026
65222
|
function markExpandableQuotes(markdown) {
|
|
65027
65223
|
const expandableLineStarts = new Set;
|
|
65028
65224
|
let out = "";
|
|
65029
65225
|
let offset = 0;
|
|
65226
|
+
let openFence = null;
|
|
65030
65227
|
for (const line of markdown.split(/(?<=\n)/)) {
|
|
65228
|
+
const body = line.replace(/\r?\n$/, "");
|
|
65229
|
+
const fence = FENCE_DELIM_RE.exec(body)?.[1] ?? null;
|
|
65230
|
+
if (openFence !== null) {
|
|
65231
|
+
out += line;
|
|
65232
|
+
if (fence !== null && fence[0] === openFence[0] && fence.length >= openFence.length && body.slice(body.indexOf(fence) + fence.length).trim() === "") {
|
|
65233
|
+
openFence = null;
|
|
65234
|
+
}
|
|
65235
|
+
offset += line.length;
|
|
65236
|
+
continue;
|
|
65237
|
+
}
|
|
65238
|
+
if (fence !== null) {
|
|
65239
|
+
const rest = body.slice(body.indexOf(fence) + fence.length);
|
|
65240
|
+
if (!(fence[0] === "`" && rest.includes("`"))) {
|
|
65241
|
+
openFence = fence;
|
|
65242
|
+
out += line;
|
|
65243
|
+
offset += line.length;
|
|
65244
|
+
continue;
|
|
65245
|
+
}
|
|
65246
|
+
}
|
|
65031
65247
|
if (EXPANDABLE_MARKER_RE.test(line)) {
|
|
65032
65248
|
expandableLineStarts.add(offset);
|
|
65033
65249
|
out += " " + line.slice(2);
|
|
@@ -65048,7 +65264,7 @@ function lineStart(source, offset) {
|
|
|
65048
65264
|
function foldAlign(a) {
|
|
65049
65265
|
return a ?? null;
|
|
65050
65266
|
}
|
|
65051
|
-
function foldInline(node2, source) {
|
|
65267
|
+
function foldInline(node2, source, ctx) {
|
|
65052
65268
|
switch (node2.type) {
|
|
65053
65269
|
case "text":
|
|
65054
65270
|
return { type: "plain", text: node2.value, ...pos(node2) };
|
|
@@ -65057,21 +65273,21 @@ function foldInline(node2, source) {
|
|
|
65057
65273
|
const underline = source.slice(p.start, p.start + 2) === "__";
|
|
65058
65274
|
return {
|
|
65059
65275
|
type: underline ? "underline" : "bold",
|
|
65060
|
-
children: foldInlineChildren(node2, source),
|
|
65276
|
+
children: foldInlineChildren(node2, source, ctx),
|
|
65061
65277
|
...p
|
|
65062
65278
|
};
|
|
65063
65279
|
}
|
|
65064
65280
|
case "emphasis":
|
|
65065
|
-
return { type: "italic", children: foldInlineChildren(node2, source), ...pos(node2) };
|
|
65281
|
+
return { type: "italic", children: foldInlineChildren(node2, source, ctx), ...pos(node2) };
|
|
65066
65282
|
case "delete":
|
|
65067
|
-
return { type: "strike", children: foldInlineChildren(node2, source), ...pos(node2) };
|
|
65283
|
+
return { type: "strike", children: foldInlineChildren(node2, source, ctx), ...pos(node2) };
|
|
65068
65284
|
case "inlineCode":
|
|
65069
65285
|
return { type: "code", text: node2.value, ...pos(node2) };
|
|
65070
65286
|
case "link":
|
|
65071
65287
|
return {
|
|
65072
65288
|
type: "link",
|
|
65073
65289
|
href: node2.url,
|
|
65074
|
-
children: foldInlineChildren(node2, source),
|
|
65290
|
+
children: foldInlineChildren(node2, source, ctx),
|
|
65075
65291
|
...pos(node2)
|
|
65076
65292
|
};
|
|
65077
65293
|
case "image": {
|
|
@@ -65127,28 +65343,250 @@ function expandPlainNode(node2, source) {
|
|
|
65127
65343
|
}
|
|
65128
65344
|
return out;
|
|
65129
65345
|
}
|
|
65130
|
-
function
|
|
65131
|
-
|
|
65346
|
+
function findClosingTag(items, from, name) {
|
|
65347
|
+
let depth = 0;
|
|
65348
|
+
for (let i = from;i < items.length; i++) {
|
|
65349
|
+
const it = items[i];
|
|
65350
|
+
if (it.kind !== "tag" || it.tag.name !== name)
|
|
65351
|
+
continue;
|
|
65352
|
+
if (it.tag.kind === "open")
|
|
65353
|
+
depth++;
|
|
65354
|
+
else if (it.tag.kind === "close") {
|
|
65355
|
+
if (depth === 0)
|
|
65356
|
+
return i;
|
|
65357
|
+
depth--;
|
|
65358
|
+
}
|
|
65359
|
+
}
|
|
65360
|
+
return -1;
|
|
65361
|
+
}
|
|
65362
|
+
function inlineLiteralText(nodes) {
|
|
65363
|
+
let out = "";
|
|
65364
|
+
for (const n of nodes) {
|
|
65365
|
+
if (n.type !== "plain")
|
|
65366
|
+
return null;
|
|
65367
|
+
out += n.text;
|
|
65368
|
+
}
|
|
65369
|
+
return out;
|
|
65370
|
+
}
|
|
65371
|
+
var structuralSeparators = new WeakSet;
|
|
65372
|
+
function mkSeparator(ctx, start, end) {
|
|
65373
|
+
const node2 = {
|
|
65374
|
+
type: "raw",
|
|
65375
|
+
text: ctx.noBreaks === true ? " " : `
|
|
65376
|
+
`,
|
|
65377
|
+
start,
|
|
65378
|
+
end
|
|
65379
|
+
};
|
|
65380
|
+
structuralSeparators.add(node2);
|
|
65381
|
+
return node2;
|
|
65382
|
+
}
|
|
65383
|
+
function normalizeSeparators(nodes) {
|
|
65384
|
+
const out = [];
|
|
65385
|
+
for (const node2 of nodes) {
|
|
65386
|
+
if (!structuralSeparators.has(node2)) {
|
|
65387
|
+
out.push(node2);
|
|
65388
|
+
continue;
|
|
65389
|
+
}
|
|
65390
|
+
if (out.length === 0)
|
|
65391
|
+
continue;
|
|
65392
|
+
if (structuralSeparators.has(out[out.length - 1]))
|
|
65393
|
+
continue;
|
|
65394
|
+
out.push(node2);
|
|
65395
|
+
}
|
|
65396
|
+
while (out.length > 0 && structuralSeparators.has(out[out.length - 1]))
|
|
65397
|
+
out.pop();
|
|
65398
|
+
return out;
|
|
65399
|
+
}
|
|
65400
|
+
function literalTag(tag, start, end) {
|
|
65401
|
+
return { type: "plain", text: escapeHtmlLiteral(tag.raw), start, end };
|
|
65402
|
+
}
|
|
65403
|
+
function foldHtmlItems(items, active, ctx) {
|
|
65404
|
+
const out = [];
|
|
65405
|
+
let i = 0;
|
|
65406
|
+
while (i < items.length) {
|
|
65407
|
+
const it = items[i];
|
|
65408
|
+
if (it.kind === "inline") {
|
|
65409
|
+
out.push(it.node);
|
|
65410
|
+
i++;
|
|
65411
|
+
continue;
|
|
65412
|
+
}
|
|
65413
|
+
const tag = it.tag;
|
|
65414
|
+
if (tag.kind === "comment") {
|
|
65415
|
+
i++;
|
|
65416
|
+
continue;
|
|
65417
|
+
}
|
|
65418
|
+
if (HTML_FOLD_TAGS[tag.name] === "break" && tag.kind !== "close") {
|
|
65419
|
+
out.push(ctx.noBreaks === true ? { type: "raw", text: " ", start: it.start, end: it.end } : { type: "raw", text: `
|
|
65420
|
+
`, start: it.start, end: it.end });
|
|
65421
|
+
i++;
|
|
65422
|
+
continue;
|
|
65423
|
+
}
|
|
65424
|
+
if (tag.kind === "selfclose" || tag.kind === "open" && isVoidTag(tag.name)) {
|
|
65425
|
+
if (isPassthroughTag(tag)) {
|
|
65426
|
+
out.push({ type: "raw", text: tag.raw, start: it.start, end: it.end });
|
|
65427
|
+
} else if (isBlockLevelTag(tag.name)) {
|
|
65428
|
+
out.push(mkSeparator(ctx, it.start, it.end));
|
|
65429
|
+
}
|
|
65430
|
+
i++;
|
|
65431
|
+
continue;
|
|
65432
|
+
}
|
|
65433
|
+
if (isPassthroughTag(tag) && (tag.kind === "open" || tag.kind === "close")) {
|
|
65434
|
+
out.push(ctx.matched.has(it.start) ? { type: "raw", text: tag.raw, start: it.start, end: it.end } : literalTag(tag, it.start, it.end));
|
|
65435
|
+
i++;
|
|
65436
|
+
continue;
|
|
65437
|
+
}
|
|
65438
|
+
if (tag.kind === "open") {
|
|
65439
|
+
const close = findClosingTag(items, i + 1, tag.name);
|
|
65440
|
+
const closeItem = close >= 0 ? items[close] : undefined;
|
|
65441
|
+
if (closeItem !== undefined && closeItem.kind === "tag") {
|
|
65442
|
+
out.push(...foldMatchedTag(tag, closeItem, items.slice(i + 1, close), active, ctx, {
|
|
65443
|
+
start: it.start,
|
|
65444
|
+
end: closeItem.end
|
|
65445
|
+
}));
|
|
65446
|
+
i = close + 1;
|
|
65447
|
+
continue;
|
|
65448
|
+
}
|
|
65449
|
+
}
|
|
65450
|
+
if (ctx.matched.has(it.start) && tag.kind !== "other") {
|
|
65451
|
+
if (isBlockLevelTag(tag.name))
|
|
65452
|
+
out.push(mkSeparator(ctx, it.start, it.end));
|
|
65453
|
+
i++;
|
|
65454
|
+
continue;
|
|
65455
|
+
}
|
|
65456
|
+
out.push(literalTag(tag, it.start, it.end));
|
|
65457
|
+
i++;
|
|
65458
|
+
}
|
|
65459
|
+
return normalizeSeparators(out);
|
|
65460
|
+
}
|
|
65461
|
+
function foldMatchedTag(tag, closeTag, innerItems, active, ctx, span) {
|
|
65462
|
+
const target = HTML_FOLD_TAGS[tag.name];
|
|
65463
|
+
if (target === "pre") {
|
|
65464
|
+
const folded = buildPreBlock(innerItems, ctx, span);
|
|
65465
|
+
if (folded !== null)
|
|
65466
|
+
return folded;
|
|
65467
|
+
}
|
|
65468
|
+
if (target !== undefined && target !== "break" && target !== "pre") {
|
|
65469
|
+
const nested = new Set(active);
|
|
65470
|
+
nested.add(target);
|
|
65471
|
+
const inner2 = foldHtmlItems(innerItems, nested, ctx);
|
|
65472
|
+
if (active.has(target)) {
|
|
65473
|
+
return target === "link" ? [
|
|
65474
|
+
literalTag(tag, span.start, span.start + tag.raw.length),
|
|
65475
|
+
...inner2,
|
|
65476
|
+
literalTag(closeTag.tag, closeTag.start, closeTag.end)
|
|
65477
|
+
] : inner2;
|
|
65478
|
+
}
|
|
65479
|
+
const folded = buildFoldedTag(target, tag, inner2, span.start, span.end);
|
|
65480
|
+
return folded !== null ? [folded] : inner2;
|
|
65481
|
+
}
|
|
65482
|
+
const inner = foldHtmlItems(innerItems, active, ctx);
|
|
65483
|
+
if (!isBlockLevelTag(tag.name))
|
|
65484
|
+
return inner;
|
|
65485
|
+
return [
|
|
65486
|
+
mkSeparator(ctx, span.start, span.start + tag.raw.length),
|
|
65487
|
+
...inner,
|
|
65488
|
+
mkSeparator(ctx, closeTag.start, closeTag.end)
|
|
65489
|
+
];
|
|
65490
|
+
}
|
|
65491
|
+
function buildPreBlock(innerItems, ctx, span) {
|
|
65492
|
+
let body = innerItems;
|
|
65493
|
+
let language = null;
|
|
65494
|
+
const first = body[0];
|
|
65495
|
+
const last = body[body.length - 1];
|
|
65496
|
+
if (body.length >= 2 && first?.kind === "tag" && first.tag.kind === "open" && first.tag.name === "code" && last?.kind === "tag" && last.tag.kind === "close" && last.tag.name === "code") {
|
|
65497
|
+
language = languageOf(first.tag);
|
|
65498
|
+
body = body.slice(1, -1);
|
|
65499
|
+
}
|
|
65500
|
+
let text4 = "";
|
|
65501
|
+
for (const item of body) {
|
|
65502
|
+
if (item.kind !== "inline" || item.node.type !== "plain")
|
|
65503
|
+
return null;
|
|
65504
|
+
text4 += item.node.text;
|
|
65505
|
+
}
|
|
65506
|
+
text4 = text4.replace(/^\r?\n/, "").replace(/[ \t]*\r?\n[ \t]*$/, "");
|
|
65507
|
+
if (ctx.noBreaks === true) {
|
|
65508
|
+
return [{ type: "code", text: text4.replace(/\s*\r?\n\s*/g, " "), ...span }];
|
|
65509
|
+
}
|
|
65510
|
+
const fence = codeFenceFor(text4);
|
|
65511
|
+
return [
|
|
65512
|
+
mkSeparator(ctx, span.start, span.start),
|
|
65513
|
+
{
|
|
65514
|
+
type: "raw",
|
|
65515
|
+
text: `${fence}${language ?? ""}
|
|
65516
|
+
${text4}
|
|
65517
|
+
${fence}`,
|
|
65518
|
+
...span
|
|
65519
|
+
},
|
|
65520
|
+
mkSeparator(ctx, span.end, span.end)
|
|
65521
|
+
];
|
|
65522
|
+
}
|
|
65523
|
+
function buildFoldedTag(target, tag, children, start, end) {
|
|
65524
|
+
switch (target) {
|
|
65525
|
+
case "bold":
|
|
65526
|
+
case "italic":
|
|
65527
|
+
case "strike":
|
|
65528
|
+
if (children.length === 1 && children[0].type === target)
|
|
65529
|
+
return children[0];
|
|
65530
|
+
return { type: target, children, start, end };
|
|
65531
|
+
case "code": {
|
|
65532
|
+
const text4 = inlineLiteralText(children);
|
|
65533
|
+
return text4 === null ? null : { type: "code", text: text4, start, end };
|
|
65534
|
+
}
|
|
65535
|
+
case "link": {
|
|
65536
|
+
const href = hrefOf(tag);
|
|
65537
|
+
return href === null ? null : { type: "link", href, children, start, end };
|
|
65538
|
+
}
|
|
65539
|
+
default:
|
|
65540
|
+
return null;
|
|
65541
|
+
}
|
|
65542
|
+
}
|
|
65543
|
+
function htmlStringToItems(raw, base, source) {
|
|
65544
|
+
return tokenizeHtml(raw, base).flatMap((piece) => piece.kind === "tag" ? [{ kind: "tag", tag: piece.tag, start: piece.start, end: piece.end }] : expandPlainNode(mkPlain(piece.text, piece.start, piece.end), source).map((node2) => ({ kind: "inline", node: node2 })));
|
|
65545
|
+
}
|
|
65546
|
+
function buildInlines(children, source, ctx) {
|
|
65547
|
+
const items = [];
|
|
65548
|
+
let sawHtml = false;
|
|
65549
|
+
for (const child of children) {
|
|
65550
|
+
if (child.type === "html") {
|
|
65551
|
+
sawHtml = true;
|
|
65552
|
+
const p = pos(child);
|
|
65553
|
+
items.push(...htmlStringToItems(slice(source, child), p.start, source));
|
|
65554
|
+
continue;
|
|
65555
|
+
}
|
|
65556
|
+
const folded = foldInline(child, source, ctx);
|
|
65557
|
+
const expanded = folded.type === "plain" ? expandPlainNode(folded, source) : [folded];
|
|
65558
|
+
for (const node2 of expanded)
|
|
65559
|
+
items.push({ kind: "inline", node: node2 });
|
|
65560
|
+
}
|
|
65561
|
+
if (!sawHtml)
|
|
65562
|
+
return items.map((it) => it.node);
|
|
65563
|
+
return foldHtmlItems(items, new Set, ctx);
|
|
65132
65564
|
}
|
|
65133
|
-
function foldInlineChildren(node2, source) {
|
|
65134
|
-
return buildInlines(node2.children, source);
|
|
65565
|
+
function foldInlineChildren(node2, source, ctx) {
|
|
65566
|
+
return buildInlines(node2.children, source, ctx);
|
|
65135
65567
|
}
|
|
65136
|
-
function foldTableRow(row, source) {
|
|
65568
|
+
function foldTableRow(row, source, ctx) {
|
|
65137
65569
|
const cells = row.children.map((cell) => ({
|
|
65138
|
-
children: buildInlines(cell.children, source),
|
|
65570
|
+
children: buildInlines(cell.children, source, { ...ctx, noBreaks: true }),
|
|
65139
65571
|
...pos(cell)
|
|
65140
65572
|
}));
|
|
65141
65573
|
return { cells, ...pos(row) };
|
|
65142
65574
|
}
|
|
65143
|
-
function
|
|
65575
|
+
function isEmptyBlock(block) {
|
|
65576
|
+
return block.type === "paragraph" && block.children.length === 0;
|
|
65577
|
+
}
|
|
65578
|
+
function foldBlocks(nodes, source, expandableLineStarts, ctx) {
|
|
65579
|
+
return nodes.map((n) => foldBlock(n, source, expandableLineStarts, ctx)).filter((b) => !isEmptyBlock(b));
|
|
65580
|
+
}
|
|
65581
|
+
function foldBlock(node2, source, expandableLineStarts, ctx) {
|
|
65144
65582
|
switch (node2.type) {
|
|
65145
65583
|
case "paragraph":
|
|
65146
|
-
return { type: "paragraph", children: foldInlineChildren(node2, source), ...pos(node2) };
|
|
65584
|
+
return { type: "paragraph", children: foldInlineChildren(node2, source, ctx), ...pos(node2) };
|
|
65147
65585
|
case "heading":
|
|
65148
65586
|
return {
|
|
65149
65587
|
type: "heading",
|
|
65150
65588
|
level: node2.depth,
|
|
65151
|
-
children: foldInlineChildren(node2, source),
|
|
65589
|
+
children: foldInlineChildren(node2, source, { ...ctx, noBreaks: true }),
|
|
65152
65590
|
...pos(node2)
|
|
65153
65591
|
};
|
|
65154
65592
|
case "blockquote": {
|
|
@@ -65156,7 +65594,7 @@ function foldBlock(node2, source, expandableLineStarts) {
|
|
|
65156
65594
|
const expandable = expandableLineStarts.has(lineStart(source, p.start));
|
|
65157
65595
|
return {
|
|
65158
65596
|
type: "blockquote",
|
|
65159
|
-
children: node2.children
|
|
65597
|
+
children: foldBlocks(node2.children, source, expandableLineStarts, ctx),
|
|
65160
65598
|
expandable,
|
|
65161
65599
|
...p
|
|
65162
65600
|
};
|
|
@@ -65170,7 +65608,7 @@ function foldBlock(node2, source, expandableLineStarts) {
|
|
|
65170
65608
|
};
|
|
65171
65609
|
case "list": {
|
|
65172
65610
|
const items = node2.children.map((li) => ({
|
|
65173
|
-
children: li.children
|
|
65611
|
+
children: foldBlocks(li.children, source, expandableLineStarts, ctx),
|
|
65174
65612
|
checked: li.checked ?? null,
|
|
65175
65613
|
spread: li.spread ?? false,
|
|
65176
65614
|
...pos(li)
|
|
@@ -65191,8 +65629,8 @@ function foldBlock(node2, source, expandableLineStarts) {
|
|
|
65191
65629
|
const [headerRow2, ...bodyRows] = rows;
|
|
65192
65630
|
return {
|
|
65193
65631
|
type: "table",
|
|
65194
|
-
header: foldTableRow(headerRow2, source),
|
|
65195
|
-
rows: bodyRows.map((r) => foldTableRow(r, source)),
|
|
65632
|
+
header: foldTableRow(headerRow2, source, ctx),
|
|
65633
|
+
rows: bodyRows.map((r) => foldTableRow(r, source, ctx)),
|
|
65196
65634
|
align: (node2.align ?? []).map(foldAlign),
|
|
65197
65635
|
...pos(node2)
|
|
65198
65636
|
};
|
|
@@ -65203,6 +65641,14 @@ function foldBlock(node2, source, expandableLineStarts) {
|
|
|
65203
65641
|
children: [{ type: "raw", text: slice(source, node2), ...pos(node2) }],
|
|
65204
65642
|
...pos(node2)
|
|
65205
65643
|
};
|
|
65644
|
+
case "html": {
|
|
65645
|
+
const p = pos(node2);
|
|
65646
|
+
return {
|
|
65647
|
+
type: "paragraph",
|
|
65648
|
+
children: foldHtmlItems(htmlStringToItems(slice(source, node2), p.start, source), new Set, ctx),
|
|
65649
|
+
...p
|
|
65650
|
+
};
|
|
65651
|
+
}
|
|
65206
65652
|
default:
|
|
65207
65653
|
return {
|
|
65208
65654
|
type: "paragraph",
|
|
@@ -65218,8 +65664,30 @@ function parse2(markdown) {
|
|
|
65218
65664
|
mdastExtensions: [gfmFromMarkdown()]
|
|
65219
65665
|
});
|
|
65220
65666
|
return {
|
|
65221
|
-
blocks: tree.children
|
|
65667
|
+
blocks: foldBlocks(tree.children, markdown, expandableLineStarts, {
|
|
65668
|
+
matched: matchedTagOffsets(collectHtmlTokens(tree.children, markdown))
|
|
65669
|
+
})
|
|
65670
|
+
};
|
|
65671
|
+
}
|
|
65672
|
+
function collectHtmlTokens(nodes, source) {
|
|
65673
|
+
const out = [];
|
|
65674
|
+
const walk = (list2) => {
|
|
65675
|
+
for (const node2 of list2) {
|
|
65676
|
+
if (node2.type === "html") {
|
|
65677
|
+
const p = pos(node2);
|
|
65678
|
+
for (const piece of tokenizeHtml(slice(source, node2), p.start)) {
|
|
65679
|
+
if (piece.kind === "tag")
|
|
65680
|
+
out.push({ tag: piece.tag, start: piece.start });
|
|
65681
|
+
}
|
|
65682
|
+
continue;
|
|
65683
|
+
}
|
|
65684
|
+
const children = node2.children;
|
|
65685
|
+
if (Array.isArray(children))
|
|
65686
|
+
walk(children);
|
|
65687
|
+
}
|
|
65222
65688
|
};
|
|
65689
|
+
walk(nodes);
|
|
65690
|
+
return out;
|
|
65223
65691
|
}
|
|
65224
65692
|
|
|
65225
65693
|
// render/render.ts
|
|
@@ -65273,8 +65741,7 @@ function renderBlockquote(node2) {
|
|
|
65273
65741
|
}
|
|
65274
65742
|
function renderCodeBlock(node2) {
|
|
65275
65743
|
const lang = node2.language ?? "";
|
|
65276
|
-
const
|
|
65277
|
-
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
65744
|
+
const fence = codeFenceFor(node2.text);
|
|
65278
65745
|
return `${fence}${lang}
|
|
65279
65746
|
${node2.text}
|
|
65280
65747
|
${fence}`;
|
|
@@ -65374,8 +65841,7 @@ function tableDegradationReason(node2) {
|
|
|
65374
65841
|
}
|
|
65375
65842
|
function degradeToCodeFence(source, node2) {
|
|
65376
65843
|
const raw = source.slice(node2.start, node2.end);
|
|
65377
|
-
const
|
|
65378
|
-
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
65844
|
+
const fence = codeFenceFor(raw);
|
|
65379
65845
|
return `${fence}
|
|
65380
65846
|
${raw}
|
|
65381
65847
|
${fence}`;
|
|
@@ -76790,12 +77256,16 @@ function lookupMessageRoleAndText(chatId, messageId, opts) {
|
|
|
76790
77256
|
return null;
|
|
76791
77257
|
return { role: row.role, text: row.text ?? "", kind: row.kind ?? null };
|
|
76792
77258
|
}
|
|
76793
|
-
function hasOutboundDeliveredSince(chatId, sinceMs, threadId, minChars = 200) {
|
|
77259
|
+
function hasOutboundDeliveredSince(chatId, sinceMs, threadId, minChars = 200, untilMs) {
|
|
76794
77260
|
try {
|
|
76795
77261
|
const cutoffSec = Math.floor(sinceMs / 1000);
|
|
76796
77262
|
const minLen = Math.max(1, Math.floor(minChars));
|
|
76797
77263
|
const params = [chatId, cutoffSec, minLen];
|
|
76798
77264
|
let sql = "SELECT 1 FROM messages WHERE chat_id = ? AND role = 'assistant' AND ts >= ? AND LENGTH(text) >= ?";
|
|
77265
|
+
if (untilMs != null && Number.isFinite(untilMs)) {
|
|
77266
|
+
sql += " AND ts <= ?";
|
|
77267
|
+
params.push(Math.ceil(untilMs / 1000));
|
|
77268
|
+
}
|
|
76799
77269
|
if (threadId !== undefined) {
|
|
76800
77270
|
if (threadId === null) {
|
|
76801
77271
|
sql += " AND thread_id IS NULL";
|
|
@@ -79880,6 +80350,62 @@ function normalizeTemporal(text4, tz, nowMs2) {
|
|
|
79880
80350
|
return normalizeRelativeDays(normalizeUtcDatetimes(text4, tz, nowMs2), tz, nowMs2);
|
|
79881
80351
|
}
|
|
79882
80352
|
|
|
80353
|
+
// gateway/speech-capture.ts
|
|
80354
|
+
import { appendFileSync as appendFileSync5 } from "node:fs";
|
|
80355
|
+
import { join as join34 } from "node:path";
|
|
80356
|
+
var SPEECH_CAPTURE_FILE_NAME = "speech-capture.jsonl";
|
|
80357
|
+
var SPEECH_CAPTURE_FILE_MODE = 384;
|
|
80358
|
+
function envFlagEnabled() {
|
|
80359
|
+
const v = process.env.SWITCHROOM_SPEECH_CAPTURE;
|
|
80360
|
+
return v === "1" || v === "true";
|
|
80361
|
+
}
|
|
80362
|
+
function isCaptureEnabled(override) {
|
|
80363
|
+
if (override !== undefined)
|
|
80364
|
+
return override;
|
|
80365
|
+
return envFlagEnabled();
|
|
80366
|
+
}
|
|
80367
|
+
var loggedEnabledOnce = false;
|
|
80368
|
+
var lastWriteErrorLoggedAt = null;
|
|
80369
|
+
var WRITE_ERROR_LOG_INTERVAL_MS = 5 * 60000;
|
|
80370
|
+
function logEnabledOnce() {
|
|
80371
|
+
if (loggedEnabledOnce)
|
|
80372
|
+
return;
|
|
80373
|
+
loggedEnabledOnce = true;
|
|
80374
|
+
try {
|
|
80375
|
+
process.stderr.write(`telegram gateway: speech-capture: enabled \u2014 writing $TELEGRAM_STATE_DIR/${SPEECH_CAPTURE_FILE_NAME}
|
|
80376
|
+
`);
|
|
80377
|
+
} catch {}
|
|
80378
|
+
}
|
|
80379
|
+
function logWriteErrorRateLimited(err, nowMs2) {
|
|
80380
|
+
if (lastWriteErrorLoggedAt !== null && nowMs2 - lastWriteErrorLoggedAt < WRITE_ERROR_LOG_INTERVAL_MS)
|
|
80381
|
+
return;
|
|
80382
|
+
lastWriteErrorLoggedAt = nowMs2;
|
|
80383
|
+
try {
|
|
80384
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
80385
|
+
process.stderr.write(`telegram gateway: speech-capture: write failed (further failures rate-limited ` + `${Math.round(WRITE_ERROR_LOG_INTERVAL_MS / 60000)}m) err=${msg}
|
|
80386
|
+
`);
|
|
80387
|
+
} catch {}
|
|
80388
|
+
}
|
|
80389
|
+
function captureSpeechText(text4, opts = {}) {
|
|
80390
|
+
if (!isCaptureEnabled(opts.enabled))
|
|
80391
|
+
return;
|
|
80392
|
+
logEnabledOnce();
|
|
80393
|
+
try {
|
|
80394
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR;
|
|
80395
|
+
if (stateDir == null || stateDir.length === 0)
|
|
80396
|
+
return;
|
|
80397
|
+
const now = opts.now ?? Date.now;
|
|
80398
|
+
const line = `${JSON.stringify({ ts: now(), text: text4 })}
|
|
80399
|
+
`;
|
|
80400
|
+
appendFileSync5(join34(stateDir, SPEECH_CAPTURE_FILE_NAME), line, {
|
|
80401
|
+
encoding: "utf8",
|
|
80402
|
+
mode: SPEECH_CAPTURE_FILE_MODE
|
|
80403
|
+
});
|
|
80404
|
+
} catch (err) {
|
|
80405
|
+
logWriteErrorRateLimited(err, Date.now());
|
|
80406
|
+
}
|
|
80407
|
+
}
|
|
80408
|
+
|
|
79883
80409
|
// gateway/outbound-send-path.ts
|
|
79884
80410
|
var import_grammy11 = __toESM(require_mod2(), 1);
|
|
79885
80411
|
import { statSync as statSync12 } from "fs";
|
|
@@ -80024,9 +80550,9 @@ import {
|
|
|
80024
80550
|
statSync as statSync11,
|
|
80025
80551
|
unlinkSync as unlinkSync14,
|
|
80026
80552
|
writeFileSync as writeFileSync25,
|
|
80027
|
-
appendFileSync as
|
|
80553
|
+
appendFileSync as appendFileSync6
|
|
80028
80554
|
} from "node:fs";
|
|
80029
|
-
import { join as
|
|
80555
|
+
import { join as join35 } from "node:path";
|
|
80030
80556
|
import { homedir as homedir9 } from "node:os";
|
|
80031
80557
|
|
|
80032
80558
|
// hooks/audience-classify.mjs
|
|
@@ -80110,10 +80636,10 @@ function resolveStateDir2(explicit) {
|
|
|
80110
80636
|
if (env != null && env !== "")
|
|
80111
80637
|
return env;
|
|
80112
80638
|
const home2 = process.env.HOME ?? homedir9();
|
|
80113
|
-
return
|
|
80639
|
+
return join35(home2, ".claude", "channels", "telegram");
|
|
80114
80640
|
}
|
|
80115
80641
|
function resolveOutboxDir(stateDir) {
|
|
80116
|
-
return
|
|
80642
|
+
return join35(resolveStateDir2(stateDir), "outbox");
|
|
80117
80643
|
}
|
|
80118
80644
|
var JOURNAL_FILE = "delivered.jsonl";
|
|
80119
80645
|
var OUTBOX_MAX_AGE_MS = 30 * 60000;
|
|
@@ -80123,10 +80649,10 @@ function writeOutboxRecordAtomic(record, stateDir) {
|
|
|
80123
80649
|
const dir = resolveOutboxDir(stateDir);
|
|
80124
80650
|
try {
|
|
80125
80651
|
mkdirSync29(dir, { recursive: true });
|
|
80126
|
-
const finalPath =
|
|
80652
|
+
const finalPath = join35(dir, `${record.turnNonce}.json`);
|
|
80127
80653
|
if (existsSync26(finalPath))
|
|
80128
80654
|
return true;
|
|
80129
|
-
const tmpPath =
|
|
80655
|
+
const tmpPath = join35(dir, `.${record.turnNonce}.${process.pid}.tmp`);
|
|
80130
80656
|
writeFileSync25(tmpPath, JSON.stringify(record), { mode: 384 });
|
|
80131
80657
|
renameSync14(tmpPath, finalPath);
|
|
80132
80658
|
return true;
|
|
@@ -80144,7 +80670,7 @@ function listPendingRecords(stateDir) {
|
|
|
80144
80670
|
}
|
|
80145
80671
|
function readOutboxRecord(fileName, stateDir) {
|
|
80146
80672
|
try {
|
|
80147
|
-
const raw = readFileSync26(
|
|
80673
|
+
const raw = readFileSync26(join35(resolveOutboxDir(stateDir), fileName), "utf8");
|
|
80148
80674
|
return JSON.parse(raw);
|
|
80149
80675
|
} catch {
|
|
80150
80676
|
return null;
|
|
@@ -80152,8 +80678,8 @@ function readOutboxRecord(fileName, stateDir) {
|
|
|
80152
80678
|
}
|
|
80153
80679
|
function claimRecord(nonce, stateDir) {
|
|
80154
80680
|
const dir = resolveOutboxDir(stateDir);
|
|
80155
|
-
const from =
|
|
80156
|
-
const to =
|
|
80681
|
+
const from = join35(dir, `${nonce}.json`);
|
|
80682
|
+
const to = join35(dir, `${nonce}.sending`);
|
|
80157
80683
|
try {
|
|
80158
80684
|
renameSync14(from, to);
|
|
80159
80685
|
return to;
|
|
@@ -80164,12 +80690,12 @@ function claimRecord(nonce, stateDir) {
|
|
|
80164
80690
|
function releaseClaim(nonce, stateDir) {
|
|
80165
80691
|
const dir = resolveOutboxDir(stateDir);
|
|
80166
80692
|
try {
|
|
80167
|
-
renameSync14(
|
|
80693
|
+
renameSync14(join35(dir, `${nonce}.sending`), join35(dir, `${nonce}.json`));
|
|
80168
80694
|
} catch {}
|
|
80169
80695
|
}
|
|
80170
80696
|
function removeClaimed(nonce, stateDir) {
|
|
80171
80697
|
try {
|
|
80172
|
-
unlinkSync14(
|
|
80698
|
+
unlinkSync14(join35(resolveOutboxDir(stateDir), `${nonce}.sending`));
|
|
80173
80699
|
} catch {}
|
|
80174
80700
|
}
|
|
80175
80701
|
function reclaimStaleSending(stateDir, now = Date.now()) {
|
|
@@ -80182,16 +80708,16 @@ function reclaimStaleSending(stateDir, now = Date.now()) {
|
|
|
80182
80708
|
}
|
|
80183
80709
|
for (const f of files) {
|
|
80184
80710
|
try {
|
|
80185
|
-
const st = statSync11(
|
|
80711
|
+
const st = statSync11(join35(dir, f));
|
|
80186
80712
|
if (now - st.mtimeMs > OUTBOX_SENDING_TIMEOUT_MS) {
|
|
80187
|
-
renameSync14(
|
|
80713
|
+
renameSync14(join35(dir, f), join35(dir, f.replace(/\.sending$/, ".json")));
|
|
80188
80714
|
}
|
|
80189
80715
|
} catch {}
|
|
80190
80716
|
}
|
|
80191
80717
|
}
|
|
80192
80718
|
function readDeliveredNonces(stateDir) {
|
|
80193
80719
|
const set = new Set;
|
|
80194
|
-
const path2 =
|
|
80720
|
+
const path2 = join35(resolveOutboxDir(stateDir), JOURNAL_FILE);
|
|
80195
80721
|
if (!existsSync26(path2))
|
|
80196
80722
|
return set;
|
|
80197
80723
|
try {
|
|
@@ -80218,7 +80744,7 @@ function isBackstopDeliveredEntry(e) {
|
|
|
80218
80744
|
function backstopAlreadyDelivered(nonce, stateDir) {
|
|
80219
80745
|
if (nonce == null || nonce === "")
|
|
80220
80746
|
return false;
|
|
80221
|
-
const path2 =
|
|
80747
|
+
const path2 = join35(resolveOutboxDir(stateDir), JOURNAL_FILE);
|
|
80222
80748
|
if (!existsSync26(path2))
|
|
80223
80749
|
return false;
|
|
80224
80750
|
try {
|
|
@@ -80239,10 +80765,10 @@ var JOURNAL_KEEP = 2000;
|
|
|
80239
80765
|
var JOURNAL_ROTATE_AT = 4000;
|
|
80240
80766
|
function appendDelivered(entry, stateDir) {
|
|
80241
80767
|
const dir = resolveOutboxDir(stateDir);
|
|
80242
|
-
const path2 =
|
|
80768
|
+
const path2 = join35(dir, JOURNAL_FILE);
|
|
80243
80769
|
try {
|
|
80244
80770
|
mkdirSync29(dir, { recursive: true });
|
|
80245
|
-
|
|
80771
|
+
appendFileSync6(path2, JSON.stringify(entry) + `
|
|
80246
80772
|
`, { mode: 384 });
|
|
80247
80773
|
compactJournalIfLarge(path2);
|
|
80248
80774
|
} catch {}
|
|
@@ -80265,7 +80791,7 @@ function clearOutboxRecord(nonce, stateDir) {
|
|
|
80265
80791
|
const dir = resolveOutboxDir(stateDir);
|
|
80266
80792
|
for (const suffix of [".json", ".sending"]) {
|
|
80267
80793
|
try {
|
|
80268
|
-
unlinkSync14(
|
|
80794
|
+
unlinkSync14(join35(dir, `${nonce}${suffix}`));
|
|
80269
80795
|
} catch {}
|
|
80270
80796
|
}
|
|
80271
80797
|
}
|
|
@@ -80335,9 +80861,9 @@ import {
|
|
|
80335
80861
|
readFileSync as readFileSync27,
|
|
80336
80862
|
renameSync as renameSync15,
|
|
80337
80863
|
writeFileSync as writeFileSync26,
|
|
80338
|
-
appendFileSync as
|
|
80864
|
+
appendFileSync as appendFileSync7
|
|
80339
80865
|
} from "node:fs";
|
|
80340
|
-
import { join as
|
|
80866
|
+
import { join as join36 } from "node:path";
|
|
80341
80867
|
|
|
80342
80868
|
// hooks/narration-classify.mjs
|
|
80343
80869
|
import { createHash as createHash5 } from "node:crypto";
|
|
@@ -80408,7 +80934,7 @@ var SHOWN_LEDGER_FILE = "shown-ledger.jsonl";
|
|
|
80408
80934
|
var SHOWN_LEDGER_KEEP = 2000;
|
|
80409
80935
|
var SHOWN_LEDGER_ROTATE_AT = 4000;
|
|
80410
80936
|
function shownLedgerPath(stateDir) {
|
|
80411
|
-
return
|
|
80937
|
+
return join36(resolveOutboxDir(stateDir), SHOWN_LEDGER_FILE);
|
|
80412
80938
|
}
|
|
80413
80939
|
function appendShownBlock(turnNonce, text4, stateDir, now = Date.now()) {
|
|
80414
80940
|
if (turnNonce == null || turnNonce === "")
|
|
@@ -80421,7 +80947,7 @@ function appendShownBlock(turnNonce, text4, stateDir, now = Date.now()) {
|
|
|
80421
80947
|
try {
|
|
80422
80948
|
mkdirSync30(dir, { recursive: true });
|
|
80423
80949
|
const entry = { turnNonce, hash: ledgerHashHex(trimmed), ts: now };
|
|
80424
|
-
|
|
80950
|
+
appendFileSync7(path2, JSON.stringify(entry) + `
|
|
80425
80951
|
`, { mode: 384 });
|
|
80426
80952
|
compactIfLarge(path2);
|
|
80427
80953
|
} catch {}
|
|
@@ -80643,6 +81169,33 @@ function journalExternalDelivery(args, stateDir, now = Date.now()) {
|
|
|
80643
81169
|
clearOutboxRecord(nonce, stateDir);
|
|
80644
81170
|
}
|
|
80645
81171
|
|
|
81172
|
+
// gateway/answer-thread-resolve.ts
|
|
81173
|
+
function isCrossChatAnchor(targetChatId, anchorChatId) {
|
|
81174
|
+
if (targetChatId == null || targetChatId === "")
|
|
81175
|
+
return false;
|
|
81176
|
+
if (anchorChatId == null || anchorChatId === "")
|
|
81177
|
+
return false;
|
|
81178
|
+
return String(anchorChatId) !== String(targetChatId);
|
|
81179
|
+
}
|
|
81180
|
+
|
|
81181
|
+
// gateway/reply-route-log.ts
|
|
81182
|
+
function formatReplyRouteLog(i) {
|
|
81183
|
+
const originCrossChat = isCrossChatAnchor(i.chatId, i.originTurn?.sessionChatId);
|
|
81184
|
+
const liveCrossChat = isCrossChatAnchor(i.chatId, i.liveTurn?.sessionChatId);
|
|
81185
|
+
const crossChatDropped = originCrossChat || liveCrossChat;
|
|
81186
|
+
const originAnchor = originCrossChat ? null : i.originTurn;
|
|
81187
|
+
const liveAnchor = liveCrossChat ? null : i.liveTurn;
|
|
81188
|
+
const explicit = i.explicitThreadId;
|
|
81189
|
+
const via = i.frameworkTopicAuthority ? originAnchor != null ? i.originVia === "quoted" ? "quoted" : "origin" : liveAnchor != null ? "live" : explicit != null ? "explicit" : i.recovered != null ? "recovered" : "none" : explicit != null ? "explicit" : originAnchor != null ? i.originVia === "quoted" ? "quoted" : "origin" : liveAnchor?.sessionThreadId != null ? "live" : i.recovered != null ? "recovered" : "none";
|
|
81190
|
+
const explicitOverridden = i.frameworkTopicAuthority && explicit != null && (originAnchor != null || liveAnchor != null) && i.threadId !== explicit;
|
|
81191
|
+
const ownerTurn = originAnchor ?? i.recovered ?? liveAnchor;
|
|
81192
|
+
const isSupergroup = i.chatId.startsWith("-100");
|
|
81193
|
+
const unrouted = isSupergroup && i.threadId == null && ownerTurn == null && !crossChatDropped;
|
|
81194
|
+
const misrouteRisk = isSupergroup && via === "live" && i.hasDifferentThreadedRecentTurn(liveAnchor?.sessionThreadId);
|
|
81195
|
+
return `telegram gateway: reply-route surface=${i.surface} chat=${i.chatId} ` + `resolved_thread=${i.threadId ?? "-"} via=${via} late=${liveAnchor == null} ` + `originTurn=${ownerTurn?.turnId ?? "-"} origin_thread=${ownerTurn?.sessionThreadId ?? "-"}` + (via === "recovered" ? " RECOVERED" : "") + (via === "quoted" ? " QUOTED(framework-origin)" : "") + (unrouted ? " UNROUTED(supergroup\u2192no-topic)" : "") + (misrouteRisk ? " MISROUTE_RISK(no-echo\u2192live-successor)" : "") + (crossChatDropped ? ` CROSS_CHAT_ANCHOR_DROPPED(target=${i.chatId}` + (originCrossChat ? `,origin_chat=${i.originTurn?.sessionChatId},origin_thread=${i.originTurn?.sessionThreadId ?? "-"}` : "") + (liveCrossChat ? `,live_chat=${i.liveTurn?.sessionChatId},live_thread=${i.liveTurn?.sessionThreadId ?? "-"}` : "") + `,routed\u2192${i.threadId ?? "-"})` : "") + (explicitOverridden ? ` EXPLICIT_OVERRIDDEN(model\u2192${explicit},routed\u2192${i.threadId ?? "-"})` : "") + `
|
|
81196
|
+
`;
|
|
81197
|
+
}
|
|
81198
|
+
|
|
80646
81199
|
// gateway/flood-reply-queue.ts
|
|
80647
81200
|
var FLOOD_QUEUED_SOURCE = "flood-deferred-reply";
|
|
80648
81201
|
function isFloodRejection(err) {
|
|
@@ -81229,7 +81782,7 @@ function monotonicNowMs2() {
|
|
|
81229
81782
|
|
|
81230
81783
|
// silent-end.ts
|
|
81231
81784
|
import { existsSync as existsSync28, readFileSync as readFileSync28, writeFileSync as writeFileSync27, unlinkSync as unlinkSync15, mkdirSync as mkdirSync31 } from "node:fs";
|
|
81232
|
-
import { dirname as dirname17, join as
|
|
81785
|
+
import { dirname as dirname17, join as join37 } from "node:path";
|
|
81233
81786
|
import { homedir as homedir10 } from "node:os";
|
|
81234
81787
|
var SILENT_END_MAX_RETRIES2 = 2;
|
|
81235
81788
|
var SILENT_END_STALE_RECORD_MAX_AGE_MS2 = 30 * 60000;
|
|
@@ -81244,10 +81797,10 @@ function resolveStateDir3(deps) {
|
|
|
81244
81797
|
if (env != null && env !== "")
|
|
81245
81798
|
return env;
|
|
81246
81799
|
const home2 = process.env.HOME ?? homedir10();
|
|
81247
|
-
return
|
|
81800
|
+
return join37(home2, ".claude", "channels", "telegram");
|
|
81248
81801
|
}
|
|
81249
81802
|
function resolveStatePath3(deps) {
|
|
81250
|
-
return
|
|
81803
|
+
return join37(resolveStateDir3(deps), "silent-end-pending.json");
|
|
81251
81804
|
}
|
|
81252
81805
|
function emitLog2(deps, line) {
|
|
81253
81806
|
if (deps?.log != null)
|
|
@@ -81775,6 +82328,7 @@ async function sendReply(deps, req) {
|
|
|
81775
82328
|
const protectContent = args.protect_content === true;
|
|
81776
82329
|
const quoteText = args.quote_text;
|
|
81777
82330
|
const access = loadAccess();
|
|
82331
|
+
captureSpeechText(text4);
|
|
81778
82332
|
const voiceOutPlan = resolveVoiceOutPlan(access.voice_out, text4);
|
|
81779
82333
|
const configParseMode = access.parseMode ?? "html";
|
|
81780
82334
|
const format = args.format ?? configParseMode;
|
|
@@ -81851,7 +82405,24 @@ ${url}`;
|
|
|
81851
82405
|
replyRoutedOriginTurn = originTurn ?? null;
|
|
81852
82406
|
threadId = resolveAnswerThreadWithLog(chat_id, Number.isFinite(explicit) ? explicit : undefined, originTurn, originTurn == null ? null : echoedTurn != null ? "echo" : "quoted", turn, "reply");
|
|
81853
82407
|
} else {
|
|
81854
|
-
|
|
82408
|
+
const anchorCrossChat = isCrossChatAnchor(chat_id, turn?.sessionChatId);
|
|
82409
|
+
const anchorThreadId = anchorCrossChat ? undefined : turn?.sessionThreadId;
|
|
82410
|
+
threadId = resolveThreadId(chat_id, args.message_thread_id ?? (anchorThreadId != null ? anchorThreadId : undefined));
|
|
82411
|
+
if (anchorCrossChat) {
|
|
82412
|
+
const explicitRaw = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
82413
|
+
process.stderr.write(formatReplyRouteLog({
|
|
82414
|
+
surface: "reply",
|
|
82415
|
+
chatId: chat_id,
|
|
82416
|
+
threadId,
|
|
82417
|
+
explicitThreadId: Number.isFinite(explicitRaw) ? explicitRaw : undefined,
|
|
82418
|
+
originTurn: null,
|
|
82419
|
+
originVia: null,
|
|
82420
|
+
liveTurn: turn,
|
|
82421
|
+
recovered: null,
|
|
82422
|
+
frameworkTopicAuthority: false,
|
|
82423
|
+
hasDifferentThreadedRecentTurn: () => false
|
|
82424
|
+
}));
|
|
82425
|
+
}
|
|
81855
82426
|
}
|
|
81856
82427
|
let antecedentFromQuoteOptInDefault = false;
|
|
81857
82428
|
if (reply_to == null && quoteOptIn && HISTORY_ENABLED) {
|
|
@@ -83102,20 +83673,20 @@ var INTERRUPTED_VIA = new Set([
|
|
|
83102
83673
|
init_rich_send();
|
|
83103
83674
|
|
|
83104
83675
|
// runtime-metrics.ts
|
|
83105
|
-
import { mkdirSync as mkdirSync32, appendFileSync as
|
|
83106
|
-
import { dirname as dirname18, join as
|
|
83676
|
+
import { mkdirSync as mkdirSync32, appendFileSync as appendFileSync8 } from "node:fs";
|
|
83677
|
+
import { dirname as dirname18, join as join39 } from "node:path";
|
|
83107
83678
|
function resolveJsonlPath2() {
|
|
83108
83679
|
const override = process.env.SWITCHROOM_RUNTIME_METRICS_PATH;
|
|
83109
83680
|
if (override && override.trim() !== "")
|
|
83110
83681
|
return override.trim();
|
|
83111
83682
|
const base = process.env.SWITCHROOM_RUNTIME_STATE_DIR ?? "/state/agent";
|
|
83112
|
-
return
|
|
83683
|
+
return join39(base, "runtime-metrics.jsonl");
|
|
83113
83684
|
}
|
|
83114
83685
|
function appendJsonl2(line) {
|
|
83115
83686
|
const path2 = resolveJsonlPath2();
|
|
83116
83687
|
try {
|
|
83117
83688
|
mkdirSync32(dirname18(path2), { recursive: true });
|
|
83118
|
-
|
|
83689
|
+
appendFileSync8(path2, line + `
|
|
83119
83690
|
`, "utf-8");
|
|
83120
83691
|
} catch (err) {
|
|
83121
83692
|
process.stderr.write(`runtime-metrics: jsonl write failed: ${err.message}
|
|
@@ -83659,18 +84230,18 @@ import {
|
|
|
83659
84230
|
unlinkSync as unlinkSync16,
|
|
83660
84231
|
utimesSync
|
|
83661
84232
|
} from "node:fs";
|
|
83662
|
-
import { join as
|
|
84233
|
+
import { join as join40 } from "node:path";
|
|
83663
84234
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
83664
84235
|
var TURN_ACTIVE_HARD_TTL_MS = 10 * 60000;
|
|
83665
84236
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
83666
84237
|
try {
|
|
83667
84238
|
mkdirStateSync(stateDir, { recursive: true });
|
|
83668
|
-
writeStateFileSync(
|
|
84239
|
+
writeStateFileSync(join40(stateDir, TURN_ACTIVE_MARKER_FILE), JSON.stringify(marker, null, 2) + `
|
|
83669
84240
|
`, { mode: 384 });
|
|
83670
84241
|
} catch {}
|
|
83671
84242
|
}
|
|
83672
84243
|
function touchTurnActiveMarker(stateDir) {
|
|
83673
|
-
const path2 =
|
|
84244
|
+
const path2 = join40(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
83674
84245
|
if (!existsSync31(path2))
|
|
83675
84246
|
return;
|
|
83676
84247
|
const now = new Date;
|
|
@@ -83685,11 +84256,11 @@ function touchTurnActiveMarker(stateDir) {
|
|
|
83685
84256
|
}
|
|
83686
84257
|
function removeTurnActiveMarker(stateDir) {
|
|
83687
84258
|
try {
|
|
83688
|
-
unlinkSync16(
|
|
84259
|
+
unlinkSync16(join40(stateDir, TURN_ACTIVE_MARKER_FILE));
|
|
83689
84260
|
} catch {}
|
|
83690
84261
|
}
|
|
83691
84262
|
function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
83692
|
-
const path2 =
|
|
84263
|
+
const path2 = join40(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
83693
84264
|
try {
|
|
83694
84265
|
const st = statSync13(path2);
|
|
83695
84266
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -86011,10 +86582,10 @@ function resolveAgentDirFromEnv() {
|
|
|
86011
86582
|
|
|
86012
86583
|
// active-reactions.ts
|
|
86013
86584
|
import { readFileSync as readFileSync30, writeFileSync as writeFileSync28, renameSync as renameSync16, existsSync as existsSync32, unlinkSync as unlinkSync17 } from "node:fs";
|
|
86014
|
-
import { join as
|
|
86585
|
+
import { join as join41 } from "node:path";
|
|
86015
86586
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
86016
86587
|
function reactionsPath(agentDir) {
|
|
86017
|
-
return
|
|
86588
|
+
return join41(agentDir, ACTIVE_REACTIONS_FILENAME);
|
|
86018
86589
|
}
|
|
86019
86590
|
function readActiveReactions(agentDir) {
|
|
86020
86591
|
const p = reactionsPath(agentDir);
|
|
@@ -86079,10 +86650,10 @@ function clearActiveReactions(agentDir) {
|
|
|
86079
86650
|
|
|
86080
86651
|
// active-reactions.ts
|
|
86081
86652
|
import { readFileSync as readFileSync31, writeFileSync as writeFileSync29, renameSync as renameSync17, existsSync as existsSync33, unlinkSync as unlinkSync18 } from "node:fs";
|
|
86082
|
-
import { join as
|
|
86653
|
+
import { join as join42 } from "node:path";
|
|
86083
86654
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
86084
86655
|
function reactionsPath2(agentDir) {
|
|
86085
|
-
return
|
|
86656
|
+
return join42(agentDir, ACTIVE_REACTIONS_FILENAME2);
|
|
86086
86657
|
}
|
|
86087
86658
|
function readActiveReactions2(agentDir) {
|
|
86088
86659
|
const p = reactionsPath2(agentDir);
|
|
@@ -86962,12 +87533,12 @@ async function approvalRecord(args, opts) {
|
|
|
86962
87533
|
|
|
86963
87534
|
// quota-check.ts
|
|
86964
87535
|
import { readFileSync as readFileSync32, existsSync as existsSync34 } from "fs";
|
|
86965
|
-
import { join as
|
|
87536
|
+
import { join as join43 } from "path";
|
|
86966
87537
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
86967
87538
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
86968
87539
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
86969
87540
|
function readOauthToken2(claudeConfigDir) {
|
|
86970
|
-
const tokenFile =
|
|
87541
|
+
const tokenFile = join43(claudeConfigDir, ".oauth-token");
|
|
86971
87542
|
if (!existsSync34(tokenFile))
|
|
86972
87543
|
return null;
|
|
86973
87544
|
try {
|
|
@@ -88357,7 +88928,7 @@ async function menuWithBannerStatic(deps, banner) {
|
|
|
88357
88928
|
|
|
88358
88929
|
// gateway/session-model-file.ts
|
|
88359
88930
|
import { readFileSync as readFileSync33, writeFileSync as writeFileSync30, renameSync as renameSync18, rmSync as rmSync7 } from "node:fs";
|
|
88360
|
-
import { join as
|
|
88931
|
+
import { join as join44 } from "node:path";
|
|
88361
88932
|
var SESSION_MODEL_FILE = ".session-model";
|
|
88362
88933
|
var CONFIGURED_DEFAULT_MODEL_FILE = ".configured-default-model";
|
|
88363
88934
|
var SESSION_MODEL_BOOT_ATTEMPTS_FILE = ".session-model-boot-attempts";
|
|
@@ -88379,28 +88950,28 @@ function writeSessionModelFile(agentDir, model, configuredDefaultAtWrite) {
|
|
|
88379
88950
|
throw new Error(`refusing to persist non-canonical session model token: ${JSON.stringify(model)}`);
|
|
88380
88951
|
}
|
|
88381
88952
|
try {
|
|
88382
|
-
rmSync7(
|
|
88953
|
+
rmSync7(join44(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
|
|
88383
88954
|
} catch {}
|
|
88384
|
-
atomicWrite(
|
|
88955
|
+
atomicWrite(join44(agentDir, SESSION_MODEL_FILE), serializeSessionModel({ model, configuredDefaultAtWrite, ts: Date.now() }));
|
|
88385
88956
|
}
|
|
88386
88957
|
function readSessionModelFileRaw(agentDir) {
|
|
88387
88958
|
try {
|
|
88388
|
-
return readFileSync33(
|
|
88959
|
+
return readFileSync33(join44(agentDir, SESSION_MODEL_FILE), "utf8");
|
|
88389
88960
|
} catch {
|
|
88390
88961
|
return null;
|
|
88391
88962
|
}
|
|
88392
88963
|
}
|
|
88393
88964
|
function clearSessionModelFile(agentDir) {
|
|
88394
88965
|
try {
|
|
88395
|
-
rmSync7(
|
|
88966
|
+
rmSync7(join44(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
88396
88967
|
} catch {}
|
|
88397
88968
|
}
|
|
88398
88969
|
function consumeSessionModelCarrierOnHealthyBoot(agentDir) {
|
|
88399
88970
|
try {
|
|
88400
|
-
rmSync7(
|
|
88971
|
+
rmSync7(join44(agentDir, SESSION_MODEL_FILE), { force: true });
|
|
88401
88972
|
} catch {}
|
|
88402
88973
|
try {
|
|
88403
|
-
rmSync7(
|
|
88974
|
+
rmSync7(join44(agentDir, SESSION_MODEL_BOOT_ATTEMPTS_FILE), { force: true });
|
|
88404
88975
|
} catch {}
|
|
88405
88976
|
}
|
|
88406
88977
|
function restoreSessionModelFileRaw(agentDir, raw) {
|
|
@@ -88409,12 +88980,12 @@ function restoreSessionModelFileRaw(agentDir, raw) {
|
|
|
88409
88980
|
return;
|
|
88410
88981
|
}
|
|
88411
88982
|
try {
|
|
88412
|
-
atomicWrite(
|
|
88983
|
+
atomicWrite(join44(agentDir, SESSION_MODEL_FILE), raw);
|
|
88413
88984
|
} catch {}
|
|
88414
88985
|
}
|
|
88415
88986
|
function readConfiguredDefaultModel(agentDir) {
|
|
88416
88987
|
try {
|
|
88417
|
-
const v = readFileSync33(
|
|
88988
|
+
const v = readFileSync33(join44(agentDir, CONFIGURED_DEFAULT_MODEL_FILE), "utf8").trim();
|
|
88418
88989
|
return v.length > 0 ? v : null;
|
|
88419
88990
|
} catch {
|
|
88420
88991
|
return null;
|
|
@@ -88426,12 +88997,12 @@ function writeSessionEffortFile(agentDir, level, configuredDefaultAtWrite) {
|
|
|
88426
88997
|
if (!EFFORT_LEVEL_RE.test(level)) {
|
|
88427
88998
|
throw new Error(`refusing to persist non-allowlisted effort level: ${JSON.stringify(level)}`);
|
|
88428
88999
|
}
|
|
88429
|
-
atomicWrite(
|
|
89000
|
+
atomicWrite(join44(agentDir, SESSION_EFFORT_FILE), `${JSON.stringify({ level, configuredDefaultAtWrite: configuredDefaultAtWrite ?? "", ts: Date.now() })}
|
|
88430
89001
|
`);
|
|
88431
89002
|
}
|
|
88432
89003
|
function clearSessionEffortFile(agentDir) {
|
|
88433
89004
|
try {
|
|
88434
|
-
rmSync7(
|
|
89005
|
+
rmSync7(join44(agentDir, SESSION_EFFORT_FILE), { force: true });
|
|
88435
89006
|
} catch {}
|
|
88436
89007
|
}
|
|
88437
89008
|
var PREMIUM_RECOVERY_FILE = ".premium-recovery";
|
|
@@ -88454,13 +89025,13 @@ function writePremiumRecoveryFile(agentDir, premiumModel, chats) {
|
|
|
88454
89025
|
if (clean.length === 0) {
|
|
88455
89026
|
throw new Error("refusing to persist premium-recovery marker with no chats to notify");
|
|
88456
89027
|
}
|
|
88457
|
-
atomicWrite(
|
|
89028
|
+
atomicWrite(join44(agentDir, PREMIUM_RECOVERY_FILE), `${JSON.stringify({ premiumModel, chats: clean, ts: Date.now() })}
|
|
88458
89029
|
`);
|
|
88459
89030
|
}
|
|
88460
89031
|
function readPremiumRecoveryFile(agentDir) {
|
|
88461
89032
|
let raw;
|
|
88462
89033
|
try {
|
|
88463
|
-
raw = readFileSync33(
|
|
89034
|
+
raw = readFileSync33(join44(agentDir, PREMIUM_RECOVERY_FILE), "utf8");
|
|
88464
89035
|
} catch {
|
|
88465
89036
|
return null;
|
|
88466
89037
|
}
|
|
@@ -88473,7 +89044,7 @@ function readPremiumRecoveryFile(agentDir) {
|
|
|
88473
89044
|
}
|
|
88474
89045
|
function clearPremiumRecoveryFile(agentDir) {
|
|
88475
89046
|
try {
|
|
88476
|
-
rmSync7(
|
|
89047
|
+
rmSync7(join44(agentDir, PREMIUM_RECOVERY_FILE), { force: true });
|
|
88477
89048
|
} catch {}
|
|
88478
89049
|
}
|
|
88479
89050
|
|
|
@@ -88867,7 +89438,7 @@ init_merge();
|
|
|
88867
89438
|
init_timezone();
|
|
88868
89439
|
|
|
88869
89440
|
// ../src/cli/agent-config.ts
|
|
88870
|
-
import { join as
|
|
89441
|
+
import { join as join46 } from "node:path";
|
|
88871
89442
|
import { homedir as homedir11 } from "node:os";
|
|
88872
89443
|
|
|
88873
89444
|
// ../src/cli/helpers.ts
|
|
@@ -88888,12 +89459,12 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
88888
89459
|
init_overlay_loader();
|
|
88889
89460
|
|
|
88890
89461
|
// ../src/cli/agent-config.ts
|
|
88891
|
-
var AUDIT_ROOT =
|
|
89462
|
+
var AUDIT_ROOT = join46(homedir11(), ".switchroom", "audit");
|
|
88892
89463
|
|
|
88893
89464
|
// ../src/agents/profiles.ts
|
|
88894
89465
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
88895
89466
|
import { readFileSync as readFileSync36, writeFileSync as writeFileSync32, existsSync as existsSync37, readdirSync as readdirSync9, statSync as statSync14, copyFileSync, mkdirSync as mkdirSync33, realpathSync as realpathSync4 } from "node:fs";
|
|
88896
|
-
import { resolve as resolve8, join as
|
|
89467
|
+
import { resolve as resolve8, join as join47, sep as pathSep } from "node:path";
|
|
88897
89468
|
function resolveProfilesRootDetailed() {
|
|
88898
89469
|
return resolveShippedAsset(PROFILES_ASSET, {
|
|
88899
89470
|
bundleDir: import.meta.dirname,
|
|
@@ -88912,7 +89483,7 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
|
|
|
88912
89483
|
var SHARED_FRAGMENTS_DIR = resolve8(PROFILES_ROOT, "_shared");
|
|
88913
89484
|
var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service", "execution-discipline", "reply-discipline", "dev-protocol", "local-time"];
|
|
88914
89485
|
for (const name of SHARED_FRAGMENTS) {
|
|
88915
|
-
const fragPath =
|
|
89486
|
+
const fragPath = join47(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
|
|
88916
89487
|
if (existsSync37(fragPath)) {
|
|
88917
89488
|
import_handlebars.default.registerPartial(name, readFileSync36(fragPath, "utf-8"));
|
|
88918
89489
|
}
|
|
@@ -90864,7 +91435,7 @@ function numField(obj, key) {
|
|
|
90864
91435
|
|
|
90865
91436
|
// gateway/context-occupancy.ts
|
|
90866
91437
|
import { mkdirSync as mkdirSync34, writeFileSync as writeFileSync33 } from "node:fs";
|
|
90867
|
-
import { join as
|
|
91438
|
+
import { join as join48 } from "node:path";
|
|
90868
91439
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
90869
91440
|
var TIGHT_FRACTION = 0.8;
|
|
90870
91441
|
function buildContextOccupancy(occupancy, cap, now) {
|
|
@@ -90887,7 +91458,7 @@ function buildContextOccupancy(occupancy, cap, now) {
|
|
|
90887
91458
|
}
|
|
90888
91459
|
function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
90889
91460
|
try {
|
|
90890
|
-
const path2 =
|
|
91461
|
+
const path2 = join48(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
90891
91462
|
(deps?.mkdir ?? ((p, o) => mkdirSync34(p, o)))(stateDir, { recursive: true });
|
|
90892
91463
|
(deps?.writeFile ?? ((p, d) => writeFileSync33(p, d)))(path2, JSON.stringify(snapshot, null, 2) + `
|
|
90893
91464
|
`);
|
|
@@ -91025,17 +91596,17 @@ function idleDurationToMs(raw) {
|
|
|
91025
91596
|
init_state_owner();
|
|
91026
91597
|
import { readFileSync as readFileSync39, mkdirSync as mkdirSync35 } from "node:fs";
|
|
91027
91598
|
import { homedir as homedir13 } from "node:os";
|
|
91028
|
-
import { join as
|
|
91599
|
+
import { join as join49 } from "node:path";
|
|
91029
91600
|
function emptyPrivacyState() {
|
|
91030
91601
|
return { version: 1, intervals: [] };
|
|
91031
91602
|
}
|
|
91032
91603
|
var PRIVATE_ON_REPLY = "\uD83D\uDD12 Private mode ON \u2014 memory writing paused. Nothing said until /public is stored.";
|
|
91033
91604
|
var PUBLIC_REPLY = "\uD83D\uDD13 Public mode \u2014 memory writing resumed. The private stretch was excluded from memory.";
|
|
91034
91605
|
function resolvePrivacyStateDir() {
|
|
91035
|
-
return process.env.TELEGRAM_STATE_DIR ??
|
|
91606
|
+
return process.env.TELEGRAM_STATE_DIR ?? join49(homedir13(), ".claude", "channels", "telegram");
|
|
91036
91607
|
}
|
|
91037
91608
|
function privacyStatePath(stateDir = resolvePrivacyStateDir()) {
|
|
91038
|
-
return
|
|
91609
|
+
return join49(stateDir, "privacy-state.json");
|
|
91039
91610
|
}
|
|
91040
91611
|
function isInterval(v) {
|
|
91041
91612
|
if (v === null || typeof v !== "object")
|
|
@@ -91091,22 +91662,22 @@ function closePrivateInterval(now = new Date, stateDir = resolvePrivacyStateDir(
|
|
|
91091
91662
|
|
|
91092
91663
|
// gateway/privacy-reset.ts
|
|
91093
91664
|
import { existsSync as existsSync40 } from "node:fs";
|
|
91094
|
-
import { join as
|
|
91665
|
+
import { join as join51 } from "node:path";
|
|
91095
91666
|
|
|
91096
91667
|
// gateway/privacy-state.ts
|
|
91097
91668
|
init_state_owner();
|
|
91098
91669
|
import { readFileSync as readFileSync40, mkdirSync as mkdirSync36 } from "node:fs";
|
|
91099
91670
|
import { homedir as homedir14 } from "node:os";
|
|
91100
|
-
import { join as
|
|
91671
|
+
import { join as join50 } from "node:path";
|
|
91101
91672
|
function emptyPrivacyState2() {
|
|
91102
91673
|
return { version: 1, intervals: [] };
|
|
91103
91674
|
}
|
|
91104
91675
|
var SESSION_RESET_ALERT = "\uD83D\uDD13 New session \u2014 memory writing is ON by default. Private mode from the previous session was reset.";
|
|
91105
91676
|
function resolvePrivacyStateDir2() {
|
|
91106
|
-
return process.env.TELEGRAM_STATE_DIR ??
|
|
91677
|
+
return process.env.TELEGRAM_STATE_DIR ?? join50(homedir14(), ".claude", "channels", "telegram");
|
|
91107
91678
|
}
|
|
91108
91679
|
function privacyStatePath2(stateDir = resolvePrivacyStateDir2()) {
|
|
91109
|
-
return
|
|
91680
|
+
return join50(stateDir, "privacy-state.json");
|
|
91110
91681
|
}
|
|
91111
91682
|
function isInterval2(v) {
|
|
91112
91683
|
if (v === null || typeof v !== "object")
|
|
@@ -91170,7 +91741,7 @@ function bootRestoresTranscript(opts) {
|
|
|
91170
91741
|
return opts.resumeMode === "continue" || opts.resumeMode === "auto";
|
|
91171
91742
|
}
|
|
91172
91743
|
function isContinueRestoreBoot(agentDir) {
|
|
91173
|
-
const forceFresh = process.env.SWITCHROOM_FORCE_FRESH === "1" || agentDir != null && existsSync40(
|
|
91744
|
+
const forceFresh = process.env.SWITCHROOM_FORCE_FRESH === "1" || agentDir != null && existsSync40(join51(agentDir, ".force-fresh-session"));
|
|
91174
91745
|
return bootRestoresTranscript({
|
|
91175
91746
|
resumeMode: process.env.SWITCHROOM_RESUME_MODE,
|
|
91176
91747
|
forceFresh
|
|
@@ -92261,7 +92832,7 @@ function reseedSweepLedger(path2, fs2, log = (l) => process.stderr.write(l)) {
|
|
|
92261
92832
|
// ../src/util/state-owner.ts
|
|
92262
92833
|
init_atomic();
|
|
92263
92834
|
import {
|
|
92264
|
-
appendFileSync as
|
|
92835
|
+
appendFileSync as appendFileSync9,
|
|
92265
92836
|
closeSync as closeSync11,
|
|
92266
92837
|
constants as constants5,
|
|
92267
92838
|
existsSync as existsSync42,
|
|
@@ -92273,7 +92844,7 @@ import {
|
|
|
92273
92844
|
statSync as statSync15,
|
|
92274
92845
|
writeFileSync as writeFileSync34
|
|
92275
92846
|
} from "node:fs";
|
|
92276
|
-
import { dirname as dirname21, join as
|
|
92847
|
+
import { dirname as dirname21, join as join52, relative as relative2, sep as sep5 } from "node:path";
|
|
92277
92848
|
var NOFOLLOW2 = constants5.O_NOFOLLOW ?? 0;
|
|
92278
92849
|
var ownerCache2 = new Map;
|
|
92279
92850
|
var loggedFailure2 = false;
|
|
@@ -92347,7 +92918,7 @@ function mkdirStateSync2(path2, options = { recursive: true }, log = defaultLog3
|
|
|
92347
92918
|
for (const part of tail.split(sep5)) {
|
|
92348
92919
|
if (part === "")
|
|
92349
92920
|
continue;
|
|
92350
|
-
current =
|
|
92921
|
+
current = join52(current, part);
|
|
92351
92922
|
adoptStateOwnership2(current, owner, log);
|
|
92352
92923
|
}
|
|
92353
92924
|
}
|
|
@@ -92389,11 +92960,11 @@ function writeStateFileSync2(path2, data, options, log = defaultLog3) {
|
|
|
92389
92960
|
function appendStateFileSync(path2, data, options, log = defaultLog3) {
|
|
92390
92961
|
const owner = resolveStateOwner2(dirname21(path2));
|
|
92391
92962
|
if (!owner) {
|
|
92392
|
-
|
|
92963
|
+
appendFileSync9(path2, data, options);
|
|
92393
92964
|
return;
|
|
92394
92965
|
}
|
|
92395
92966
|
const existed = existsSync42(path2);
|
|
92396
|
-
|
|
92967
|
+
appendFileSync9(path2, data, options);
|
|
92397
92968
|
if (!existed)
|
|
92398
92969
|
adoptStateOwnership2(path2, owner, log);
|
|
92399
92970
|
}
|
|
@@ -92449,7 +93020,7 @@ function reconcileStateDirOwnership(dir, opts = {}) {
|
|
|
92449
93020
|
result.symlinksSkipped++;
|
|
92450
93021
|
continue;
|
|
92451
93022
|
}
|
|
92452
|
-
const child =
|
|
93023
|
+
const child = join52(current, entry.name);
|
|
92453
93024
|
result.scanned++;
|
|
92454
93025
|
if (adoptAndReport(child, owner, log))
|
|
92455
93026
|
result.adopted++;
|
|
@@ -92614,13 +93185,13 @@ function startWebhookIngestServer(opts) {
|
|
|
92614
93185
|
}
|
|
92615
93186
|
|
|
92616
93187
|
// ../src/web/webhook-gateway-record.ts
|
|
92617
|
-
import { appendFileSync as
|
|
92618
|
-
import { join as
|
|
93188
|
+
import { appendFileSync as appendFileSync11, mkdirSync as mkdirSync40 } from "fs";
|
|
93189
|
+
import { join as join56 } from "path";
|
|
92619
93190
|
import { homedir as homedir16 } from "os";
|
|
92620
93191
|
|
|
92621
93192
|
// ../src/web/webhook-handler.ts
|
|
92622
|
-
import { appendFileSync as
|
|
92623
|
-
import { join as
|
|
93193
|
+
import { appendFileSync as appendFileSync10, existsSync as existsSync45, mkdirSync as mkdirSync38, readFileSync as readFileSync41, writeFileSync as writeFileSync35 } from "fs";
|
|
93194
|
+
import { join as join54 } from "path";
|
|
92624
93195
|
|
|
92625
93196
|
// ../src/util/log-rotation.ts
|
|
92626
93197
|
import * as fs2 from "node:fs";
|
|
@@ -92982,8 +93553,8 @@ var agentDedupCache = new Map;
|
|
|
92982
93553
|
function createFileDedupStore(resolveAgentDir) {
|
|
92983
93554
|
return {
|
|
92984
93555
|
check(agent, deliveryId, now) {
|
|
92985
|
-
const telegramDir =
|
|
92986
|
-
const filePath =
|
|
93556
|
+
const telegramDir = join54(resolveAgentDir(agent), "telegram");
|
|
93557
|
+
const filePath = join54(telegramDir, "webhook-dedup.json");
|
|
92987
93558
|
if (!agentDedupCache.has(agent)) {
|
|
92988
93559
|
agentDedupCache.set(agent, loadDedupFile(filePath));
|
|
92989
93560
|
}
|
|
@@ -93005,7 +93576,7 @@ var throttleIssueWindow = new Map;
|
|
|
93005
93576
|
|
|
93006
93577
|
// ../src/web/webhook-dispatch.ts
|
|
93007
93578
|
import { existsSync as existsSync46, mkdirSync as mkdirSync39, readFileSync as readFileSync42, writeFileSync as writeFileSync36 } from "fs";
|
|
93008
|
-
import { join as
|
|
93579
|
+
import { join as join55 } from "path";
|
|
93009
93580
|
import { homedir as homedir15 } from "os";
|
|
93010
93581
|
|
|
93011
93582
|
// ../src/agent-scheduler/ipc-client.ts
|
|
@@ -93325,8 +93896,8 @@ function createFileCooldownStore(resolveAgentDir) {
|
|
|
93325
93896
|
isCoolingDown(agent, key, cooldownMs, now) {
|
|
93326
93897
|
if (cooldownMs <= 0)
|
|
93327
93898
|
return false;
|
|
93328
|
-
const telegramDir =
|
|
93329
|
-
const filePath =
|
|
93899
|
+
const telegramDir = join55(resolveAgentDir(agent), "telegram");
|
|
93900
|
+
const filePath = join55(telegramDir, "webhook-cooldown.json");
|
|
93330
93901
|
if (!cache.has(agent)) {
|
|
93331
93902
|
cache.set(agent, loadCooldownFile(filePath));
|
|
93332
93903
|
}
|
|
@@ -93383,9 +93954,9 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
93383
93954
|
}
|
|
93384
93955
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
93385
93956
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
93386
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
93957
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join55(homedir15(), ".switchroom", "agents", a));
|
|
93387
93958
|
const now = (deps.now ?? Date.now)();
|
|
93388
|
-
const socketPath =
|
|
93959
|
+
const socketPath = join55(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
93389
93960
|
const inbound = {
|
|
93390
93961
|
type: "inbound",
|
|
93391
93962
|
chatId: ctx.chatId,
|
|
@@ -93457,7 +94028,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
93457
94028
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
93458
94029
|
const now = (deps.now ?? Date.now)();
|
|
93459
94030
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
93460
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
94031
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join55(homedir15(), ".switchroom", "agents", a));
|
|
93461
94032
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
93462
94033
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
93463
94034
|
return 0;
|
|
@@ -93535,10 +94106,10 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
93535
94106
|
function recordWebhookEvent(rec, deps = {}) {
|
|
93536
94107
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
93537
94108
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
93538
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) =>
|
|
94109
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join56(homedir16(), ".switchroom", "agents", a));
|
|
93539
94110
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
93540
94111
|
const agent = rec.agent;
|
|
93541
|
-
const telegramDir =
|
|
94112
|
+
const telegramDir = join56(resolveAgentDir(agent), "telegram");
|
|
93542
94113
|
if (rec.source === "github" && rec.delivery_id) {
|
|
93543
94114
|
const originalTs = dedupStore.check(agent, rec.delivery_id, now);
|
|
93544
94115
|
if (originalTs !== undefined) {
|
|
@@ -93547,7 +94118,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
93547
94118
|
return { status: "deduped", ts: originalTs };
|
|
93548
94119
|
}
|
|
93549
94120
|
}
|
|
93550
|
-
const logPath =
|
|
94121
|
+
const logPath = join56(telegramDir, "webhook-events.jsonl");
|
|
93551
94122
|
try {
|
|
93552
94123
|
mkdirSync40(telegramDir, { recursive: true });
|
|
93553
94124
|
rotateWebhookLogIfNeeded(logPath);
|
|
@@ -93558,7 +94129,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
93558
94129
|
rendered_text: rec.rendered_text,
|
|
93559
94130
|
payload: rec.payload
|
|
93560
94131
|
};
|
|
93561
|
-
|
|
94132
|
+
appendFileSync11(logPath, JSON.stringify(record) + `
|
|
93562
94133
|
`, { mode: 384 });
|
|
93563
94134
|
} catch (err) {
|
|
93564
94135
|
log(`webhook-gateway: agent='${agent}' source='${rec.source}' write failed: ${err.message}
|
|
@@ -96589,27 +97160,120 @@ class CurrentTurnMap {
|
|
|
96589
97160
|
}
|
|
96590
97161
|
|
|
96591
97162
|
// gateway/answer-thread-resolve.ts
|
|
97163
|
+
function isCrossChatAnchor2(targetChatId, anchorChatId) {
|
|
97164
|
+
if (targetChatId == null || targetChatId === "")
|
|
97165
|
+
return false;
|
|
97166
|
+
if (anchorChatId == null || anchorChatId === "")
|
|
97167
|
+
return false;
|
|
97168
|
+
return String(anchorChatId) !== String(targetChatId);
|
|
97169
|
+
}
|
|
96592
97170
|
function resolveAnswerThreadId(input) {
|
|
97171
|
+
const originCrossChat = isCrossChatAnchor2(input.targetChatId, input.originChatId);
|
|
97172
|
+
const liveCrossChat = isCrossChatAnchor2(input.targetChatId, input.liveChatId);
|
|
97173
|
+
const originResolved = input.originResolved && !originCrossChat;
|
|
97174
|
+
const originThreadId = originCrossChat ? undefined : input.originThreadId;
|
|
97175
|
+
const liveTurnPresent = input.liveTurnPresent === true && !liveCrossChat;
|
|
97176
|
+
const liveThreadId = liveCrossChat ? undefined : input.liveThreadId;
|
|
96593
97177
|
if (input.frameworkTopicAuthority === false) {
|
|
96594
97178
|
if (input.explicitThreadId != null)
|
|
96595
97179
|
return input.explicitThreadId;
|
|
96596
|
-
if (
|
|
96597
|
-
return
|
|
96598
|
-
if (
|
|
96599
|
-
return
|
|
97180
|
+
if (originResolved)
|
|
97181
|
+
return originThreadId;
|
|
97182
|
+
if (liveThreadId != null)
|
|
97183
|
+
return liveThreadId;
|
|
96600
97184
|
if (input.lastEndedResolvedForChat)
|
|
96601
97185
|
return input.lastEndedThreadIdForChat;
|
|
96602
|
-
return
|
|
97186
|
+
return liveThreadId;
|
|
96603
97187
|
}
|
|
96604
|
-
if (
|
|
96605
|
-
return
|
|
96606
|
-
if (
|
|
96607
|
-
return
|
|
97188
|
+
if (originResolved)
|
|
97189
|
+
return originThreadId;
|
|
97190
|
+
if (liveTurnPresent)
|
|
97191
|
+
return liveThreadId;
|
|
96608
97192
|
if (input.explicitThreadId != null)
|
|
96609
97193
|
return input.explicitThreadId;
|
|
96610
97194
|
if (input.lastEndedResolvedForChat)
|
|
96611
97195
|
return input.lastEndedThreadIdForChat;
|
|
96612
|
-
return
|
|
97196
|
+
return liveThreadId;
|
|
97197
|
+
}
|
|
97198
|
+
|
|
97199
|
+
// gateway/answer-route-overrides.ts
|
|
97200
|
+
function keyFor(chatId, threadId) {
|
|
97201
|
+
return `${chatId}:${threadId ?? "_"}`;
|
|
97202
|
+
}
|
|
97203
|
+
function createAnswerRouteOverrides(maxKeys = 64, maxPerKey = 8) {
|
|
97204
|
+
const byKey = new Map;
|
|
97205
|
+
return {
|
|
97206
|
+
note(args) {
|
|
97207
|
+
const overridden = args.enabled && args.explicitThreadId != null && args.anchored && args.routedThreadId !== args.explicitThreadId;
|
|
97208
|
+
if (!overridden)
|
|
97209
|
+
return false;
|
|
97210
|
+
const key = keyFor(args.chatId, args.explicitThreadId);
|
|
97211
|
+
const list2 = byKey.get(key) ?? [];
|
|
97212
|
+
list2.push({ routedThreadId: args.routedThreadId ?? null, atMs: args.nowMs });
|
|
97213
|
+
while (list2.length > maxPerKey)
|
|
97214
|
+
list2.shift();
|
|
97215
|
+
byKey.set(key, list2);
|
|
97216
|
+
while (byKey.size > maxKeys) {
|
|
97217
|
+
const oldest = byKey.keys().next().value;
|
|
97218
|
+
if (oldest === undefined)
|
|
97219
|
+
break;
|
|
97220
|
+
byKey.delete(oldest);
|
|
97221
|
+
}
|
|
97222
|
+
return true;
|
|
97223
|
+
},
|
|
97224
|
+
routedOverridesSince(chatId, intendedThreadId, notBeforeMs) {
|
|
97225
|
+
const list2 = byKey.get(keyFor(chatId, intendedThreadId));
|
|
97226
|
+
if (list2 == null)
|
|
97227
|
+
return [];
|
|
97228
|
+
const out = [];
|
|
97229
|
+
for (const e of list2) {
|
|
97230
|
+
if (e.atMs < notBeforeMs)
|
|
97231
|
+
continue;
|
|
97232
|
+
if (out.some((seen) => seen.routedThreadId === e.routedThreadId))
|
|
97233
|
+
continue;
|
|
97234
|
+
out.push({ ...e });
|
|
97235
|
+
}
|
|
97236
|
+
return out;
|
|
97237
|
+
},
|
|
97238
|
+
newestOverrideSince(chatId, intendedThreadId, notBeforeMs) {
|
|
97239
|
+
const list2 = byKey.get(keyFor(chatId, intendedThreadId));
|
|
97240
|
+
if (list2 == null)
|
|
97241
|
+
return;
|
|
97242
|
+
let newest;
|
|
97243
|
+
for (const e of list2) {
|
|
97244
|
+
if (e.atMs < notBeforeMs)
|
|
97245
|
+
continue;
|
|
97246
|
+
if (newest == null || e.atMs > newest.atMs)
|
|
97247
|
+
newest = e;
|
|
97248
|
+
}
|
|
97249
|
+
return newest == null ? undefined : { ...newest };
|
|
97250
|
+
},
|
|
97251
|
+
size() {
|
|
97252
|
+
return byKey.size;
|
|
97253
|
+
},
|
|
97254
|
+
clear() {
|
|
97255
|
+
byKey.clear();
|
|
97256
|
+
}
|
|
97257
|
+
};
|
|
97258
|
+
}
|
|
97259
|
+
var answerRouteOverrides = createAnswerRouteOverrides();
|
|
97260
|
+
|
|
97261
|
+
// gateway/reply-route-log.ts
|
|
97262
|
+
function formatReplyRouteLog2(i) {
|
|
97263
|
+
const originCrossChat = isCrossChatAnchor(i.chatId, i.originTurn?.sessionChatId);
|
|
97264
|
+
const liveCrossChat = isCrossChatAnchor(i.chatId, i.liveTurn?.sessionChatId);
|
|
97265
|
+
const crossChatDropped = originCrossChat || liveCrossChat;
|
|
97266
|
+
const originAnchor = originCrossChat ? null : i.originTurn;
|
|
97267
|
+
const liveAnchor = liveCrossChat ? null : i.liveTurn;
|
|
97268
|
+
const explicit = i.explicitThreadId;
|
|
97269
|
+
const via = i.frameworkTopicAuthority ? originAnchor != null ? i.originVia === "quoted" ? "quoted" : "origin" : liveAnchor != null ? "live" : explicit != null ? "explicit" : i.recovered != null ? "recovered" : "none" : explicit != null ? "explicit" : originAnchor != null ? i.originVia === "quoted" ? "quoted" : "origin" : liveAnchor?.sessionThreadId != null ? "live" : i.recovered != null ? "recovered" : "none";
|
|
97270
|
+
const explicitOverridden = i.frameworkTopicAuthority && explicit != null && (originAnchor != null || liveAnchor != null) && i.threadId !== explicit;
|
|
97271
|
+
const ownerTurn = originAnchor ?? i.recovered ?? liveAnchor;
|
|
97272
|
+
const isSupergroup = i.chatId.startsWith("-100");
|
|
97273
|
+
const unrouted = isSupergroup && i.threadId == null && ownerTurn == null && !crossChatDropped;
|
|
97274
|
+
const misrouteRisk = isSupergroup && via === "live" && i.hasDifferentThreadedRecentTurn(liveAnchor?.sessionThreadId);
|
|
97275
|
+
return `telegram gateway: reply-route surface=${i.surface} chat=${i.chatId} ` + `resolved_thread=${i.threadId ?? "-"} via=${via} late=${liveAnchor == null} ` + `originTurn=${ownerTurn?.turnId ?? "-"} origin_thread=${ownerTurn?.sessionThreadId ?? "-"}` + (via === "recovered" ? " RECOVERED" : "") + (via === "quoted" ? " QUOTED(framework-origin)" : "") + (unrouted ? " UNROUTED(supergroup\u2192no-topic)" : "") + (misrouteRisk ? " MISROUTE_RISK(no-echo\u2192live-successor)" : "") + (crossChatDropped ? ` CROSS_CHAT_ANCHOR_DROPPED(target=${i.chatId}` + (originCrossChat ? `,origin_chat=${i.originTurn?.sessionChatId},origin_thread=${i.originTurn?.sessionThreadId ?? "-"}` : "") + (liveCrossChat ? `,live_chat=${i.liveTurn?.sessionChatId},live_thread=${i.liveTurn?.sessionThreadId ?? "-"}` : "") + `,routed\u2192${i.threadId ?? "-"})` : "") + (explicitOverridden ? ` EXPLICIT_OVERRIDDEN(model\u2192${explicit},routed\u2192${i.threadId ?? "-"})` : "") + `
|
|
97276
|
+
`;
|
|
96613
97277
|
}
|
|
96614
97278
|
|
|
96615
97279
|
// gateway/latest-turn-lookup.ts
|
|
@@ -98026,6 +98690,153 @@ function shouldDeferEscalationForBridge(deps) {
|
|
|
98026
98690
|
return !deps.bridgeAlive;
|
|
98027
98691
|
}
|
|
98028
98692
|
|
|
98693
|
+
// gateway/escalation-staleness.ts
|
|
98694
|
+
var OBLIGATION_ESCALATE_SETTLE_MS_DEFAULT = 8500;
|
|
98695
|
+
var OBLIGATION_ESCALATE_SETTLE_MS_MAX = 60000;
|
|
98696
|
+
var REROUTE_MATCH_GRACE_MS = 1e4;
|
|
98697
|
+
var OBLIGATION_REROUTE_MATCH_MS_MAX = OBLIGATION_ESCALATE_SETTLE_MS_MAX + REROUTE_MATCH_GRACE_MS;
|
|
98698
|
+
function resolveRerouteMatchWindowMs(settleMs, env = process.env) {
|
|
98699
|
+
const derived = Math.max(settleMs, 0) + REROUTE_MATCH_GRACE_MS;
|
|
98700
|
+
const raw = env.SWITCHROOM_OBLIGATION_REROUTE_MATCH_MS;
|
|
98701
|
+
if (raw == null || raw.trim() === "")
|
|
98702
|
+
return derived;
|
|
98703
|
+
const n = Number(raw);
|
|
98704
|
+
if (!(Number.isFinite(n) && n >= 0))
|
|
98705
|
+
return derived;
|
|
98706
|
+
return Math.min(n, OBLIGATION_REROUTE_MATCH_MS_MAX);
|
|
98707
|
+
}
|
|
98708
|
+
function resolveEscalateSettleMs(env = process.env) {
|
|
98709
|
+
const raw = env.SWITCHROOM_OBLIGATION_ESCALATE_SETTLE_MS;
|
|
98710
|
+
if (raw == null || raw.trim() === "")
|
|
98711
|
+
return OBLIGATION_ESCALATE_SETTLE_MS_DEFAULT;
|
|
98712
|
+
const n = Number(raw);
|
|
98713
|
+
if (!(Number.isFinite(n) && n >= 0))
|
|
98714
|
+
return OBLIGATION_ESCALATE_SETTLE_MS_DEFAULT;
|
|
98715
|
+
return Math.min(n, OBLIGATION_ESCALATE_SETTLE_MS_MAX);
|
|
98716
|
+
}
|
|
98717
|
+
function answeredSinceOpen(o, deps) {
|
|
98718
|
+
const NOT_ANSWERED = { answered: false, via: null };
|
|
98719
|
+
if (!deps.historyEnabled)
|
|
98720
|
+
return NOT_ANSWERED;
|
|
98721
|
+
if (deps.hasOutboundDeliveredSince(o.chatId, o.openedAt, o.threadId)) {
|
|
98722
|
+
return { answered: true, via: "thread" };
|
|
98723
|
+
}
|
|
98724
|
+
if (!(deps.rerouteMatchWindowMs > 0))
|
|
98725
|
+
return NOT_ANSWERED;
|
|
98726
|
+
const notBefore = Math.max(o.openedAt, deps.anchorMs - deps.rerouteMatchWindowMs);
|
|
98727
|
+
for (const ovr of deps.routeOverrides.routedOverridesSince(o.chatId, o.threadId, notBefore)) {
|
|
98728
|
+
const since = Math.max(ovr.atMs, o.openedAt);
|
|
98729
|
+
const until = ovr.atMs + deps.rerouteMatchWindowMs;
|
|
98730
|
+
if (deps.hasOutboundDeliveredBetween(o.chatId, since, until, ovr.routedThreadId)) {
|
|
98731
|
+
return { answered: true, via: "reroute", routedThreadId: ovr.routedThreadId };
|
|
98732
|
+
}
|
|
98733
|
+
}
|
|
98734
|
+
const newest = deps.routeOverrides.newestOverrideSince(o.chatId, o.threadId, o.openedAt);
|
|
98735
|
+
if (newest != null && newest.atMs < notBefore) {
|
|
98736
|
+
return { answered: false, via: null, staleOverrideAgeMs: Math.max(0, deps.anchorMs - newest.atMs) };
|
|
98737
|
+
}
|
|
98738
|
+
return NOT_ANSWERED;
|
|
98739
|
+
}
|
|
98740
|
+
function createEscalationSettleGate(settleMs, maxKeys = 256) {
|
|
98741
|
+
const entries = new Map;
|
|
98742
|
+
return {
|
|
98743
|
+
shouldDefer(id, now, openedAt) {
|
|
98744
|
+
if (!(settleMs > 0))
|
|
98745
|
+
return false;
|
|
98746
|
+
const prev = entries.get(id);
|
|
98747
|
+
if (prev == null || prev.openedAt !== openedAt) {
|
|
98748
|
+
entries.set(id, { firstAt: now, openedAt });
|
|
98749
|
+
while (entries.size > maxKeys) {
|
|
98750
|
+
const oldest = entries.keys().next().value;
|
|
98751
|
+
if (oldest === undefined)
|
|
98752
|
+
break;
|
|
98753
|
+
entries.delete(oldest);
|
|
98754
|
+
}
|
|
98755
|
+
return true;
|
|
98756
|
+
}
|
|
98757
|
+
if (now < prev.firstAt) {
|
|
98758
|
+
entries.set(id, { firstAt: now, openedAt });
|
|
98759
|
+
return true;
|
|
98760
|
+
}
|
|
98761
|
+
return now - prev.firstAt < settleMs;
|
|
98762
|
+
},
|
|
98763
|
+
firstAt(id, openedAt) {
|
|
98764
|
+
const prev = entries.get(id);
|
|
98765
|
+
if (prev == null || prev.openedAt !== openedAt)
|
|
98766
|
+
return;
|
|
98767
|
+
return prev.firstAt;
|
|
98768
|
+
},
|
|
98769
|
+
clear(id) {
|
|
98770
|
+
entries.delete(id);
|
|
98771
|
+
},
|
|
98772
|
+
size() {
|
|
98773
|
+
return entries.size;
|
|
98774
|
+
}
|
|
98775
|
+
};
|
|
98776
|
+
}
|
|
98777
|
+
|
|
98778
|
+
// gateway/answer-route-overrides.ts
|
|
98779
|
+
function keyFor2(chatId, threadId) {
|
|
98780
|
+
return `${chatId}:${threadId ?? "_"}`;
|
|
98781
|
+
}
|
|
98782
|
+
function createAnswerRouteOverrides2(maxKeys = 64, maxPerKey = 8) {
|
|
98783
|
+
const byKey = new Map;
|
|
98784
|
+
return {
|
|
98785
|
+
note(args) {
|
|
98786
|
+
const overridden = args.enabled && args.explicitThreadId != null && args.anchored && args.routedThreadId !== args.explicitThreadId;
|
|
98787
|
+
if (!overridden)
|
|
98788
|
+
return false;
|
|
98789
|
+
const key = keyFor2(args.chatId, args.explicitThreadId);
|
|
98790
|
+
const list2 = byKey.get(key) ?? [];
|
|
98791
|
+
list2.push({ routedThreadId: args.routedThreadId ?? null, atMs: args.nowMs });
|
|
98792
|
+
while (list2.length > maxPerKey)
|
|
98793
|
+
list2.shift();
|
|
98794
|
+
byKey.set(key, list2);
|
|
98795
|
+
while (byKey.size > maxKeys) {
|
|
98796
|
+
const oldest = byKey.keys().next().value;
|
|
98797
|
+
if (oldest === undefined)
|
|
98798
|
+
break;
|
|
98799
|
+
byKey.delete(oldest);
|
|
98800
|
+
}
|
|
98801
|
+
return true;
|
|
98802
|
+
},
|
|
98803
|
+
routedOverridesSince(chatId, intendedThreadId, notBeforeMs) {
|
|
98804
|
+
const list2 = byKey.get(keyFor2(chatId, intendedThreadId));
|
|
98805
|
+
if (list2 == null)
|
|
98806
|
+
return [];
|
|
98807
|
+
const out = [];
|
|
98808
|
+
for (const e of list2) {
|
|
98809
|
+
if (e.atMs < notBeforeMs)
|
|
98810
|
+
continue;
|
|
98811
|
+
if (out.some((seen) => seen.routedThreadId === e.routedThreadId))
|
|
98812
|
+
continue;
|
|
98813
|
+
out.push({ ...e });
|
|
98814
|
+
}
|
|
98815
|
+
return out;
|
|
98816
|
+
},
|
|
98817
|
+
newestOverrideSince(chatId, intendedThreadId, notBeforeMs) {
|
|
98818
|
+
const list2 = byKey.get(keyFor2(chatId, intendedThreadId));
|
|
98819
|
+
if (list2 == null)
|
|
98820
|
+
return;
|
|
98821
|
+
let newest;
|
|
98822
|
+
for (const e of list2) {
|
|
98823
|
+
if (e.atMs < notBeforeMs)
|
|
98824
|
+
continue;
|
|
98825
|
+
if (newest == null || e.atMs > newest.atMs)
|
|
98826
|
+
newest = e;
|
|
98827
|
+
}
|
|
98828
|
+
return newest == null ? undefined : { ...newest };
|
|
98829
|
+
},
|
|
98830
|
+
size() {
|
|
98831
|
+
return byKey.size;
|
|
98832
|
+
},
|
|
98833
|
+
clear() {
|
|
98834
|
+
byKey.clear();
|
|
98835
|
+
}
|
|
98836
|
+
};
|
|
98837
|
+
}
|
|
98838
|
+
var answerRouteOverrides2 = createAnswerRouteOverrides2();
|
|
98839
|
+
|
|
98029
98840
|
// gateway/stream-render.ts
|
|
98030
98841
|
init_history();
|
|
98031
98842
|
init_rich_send();
|
|
@@ -98064,14 +98875,19 @@ function createObligationWiring(deps) {
|
|
|
98064
98875
|
bridgeAlive,
|
|
98065
98876
|
sendEscalationNudge
|
|
98066
98877
|
} = deps;
|
|
98878
|
+
const escalateSettleMs = resolveEscalateSettleMs();
|
|
98879
|
+
const escalateSettleGate = createEscalationSettleGate(escalateSettleMs);
|
|
98880
|
+
const rerouteMatchWindowMs = resolveRerouteMatchWindowMs(escalateSettleMs);
|
|
98067
98881
|
function closeObligationOnSubstantiveReply(args, liveTurn, routedOriginTurn) {
|
|
98068
98882
|
if (!OBLIGATION_LEDGER_ENABLED)
|
|
98069
98883
|
return;
|
|
98070
98884
|
const echoed = findTurnByOriginId(args.origin_turn_id);
|
|
98071
98885
|
const routedOriginId = routedOriginTurn != null && echoed == null ? routedOriginTurn.turnId : null;
|
|
98072
98886
|
const target = obligationLedger.resolveCloseTarget(echoed?.turnId, liveTurn?.turnId, routedOriginId);
|
|
98073
|
-
if (target != null)
|
|
98887
|
+
if (target != null) {
|
|
98074
98888
|
obligationLedger.close(target);
|
|
98889
|
+
escalateSettleGate.clear(target);
|
|
98890
|
+
}
|
|
98075
98891
|
}
|
|
98076
98892
|
function openObligationFromInbound(inboundMsg, gate) {
|
|
98077
98893
|
if (!OBLIGATION_LEDGER_ENABLED)
|
|
@@ -98103,6 +98919,7 @@ function createObligationWiring(deps) {
|
|
|
98103
98919
|
if (turn == null)
|
|
98104
98920
|
return;
|
|
98105
98921
|
if (obligationLedger.close(turn.turnId)) {
|
|
98922
|
+
escalateSettleGate.clear(turn.turnId);
|
|
98106
98923
|
process.stderr.write(`telegram gateway: obligation cancelled by interrupt origin=${turn.turnId}
|
|
98107
98924
|
`);
|
|
98108
98925
|
}
|
|
@@ -98143,6 +98960,7 @@ function createObligationWiring(deps) {
|
|
|
98143
98960
|
})) {
|
|
98144
98961
|
process.stderr.write(`telegram gateway: obligation closed silently \u2014 reply delivered since last represent (no re-fire) origin=${o.originTurnId}
|
|
98145
98962
|
`);
|
|
98963
|
+
escalateSettleGate.clear(o.originTurnId);
|
|
98146
98964
|
obligationLedger.close(o.originTurnId);
|
|
98147
98965
|
return;
|
|
98148
98966
|
}
|
|
@@ -98164,12 +98982,31 @@ function createObligationWiring(deps) {
|
|
|
98164
98982
|
`);
|
|
98165
98983
|
return;
|
|
98166
98984
|
}
|
|
98167
|
-
|
|
98168
|
-
|
|
98985
|
+
const answered = answeredSinceOpen(o, {
|
|
98986
|
+
historyEnabled: HISTORY_ENABLED,
|
|
98987
|
+
hasOutboundDeliveredSince: hasOutboundDeliveredSince3,
|
|
98988
|
+
hasOutboundDeliveredBetween: (chatId, sinceMs, untilMs, threadId) => hasOutboundDeliveredSince3(chatId, sinceMs, threadId, undefined, untilMs),
|
|
98989
|
+
routeOverrides: answerRouteOverrides2,
|
|
98990
|
+
anchorMs: escalateSettleGate.firstAt(o.originTurnId, o.openedAt) ?? now,
|
|
98991
|
+
rerouteMatchWindowMs
|
|
98992
|
+
});
|
|
98993
|
+
if (answered.answered) {
|
|
98994
|
+
const scope = answered.via === "reroute" ? `via=reroute routed_thread=${answered.routedThreadId ?? "-"}` : "via=thread";
|
|
98995
|
+
process.stderr.write(`telegram gateway: obligation closed silently \u2014 outbound delivered since open ${scope} origin=${o.originTurnId}
|
|
98169
98996
|
`);
|
|
98997
|
+
escalateSettleGate.clear(o.originTurnId);
|
|
98170
98998
|
obligationLedger.close(o.originTurnId);
|
|
98171
98999
|
return;
|
|
98172
99000
|
}
|
|
99001
|
+
if (answered.staleOverrideAgeMs != null) {
|
|
99002
|
+
process.stderr.write(`telegram gateway: obligation reroute record rejected age=${answered.staleOverrideAgeMs}ms ` + `window=${rerouteMatchWindowMs}ms origin=${o.originTurnId}
|
|
99003
|
+
`);
|
|
99004
|
+
}
|
|
99005
|
+
if (escalateSettleGate.shouldDefer(o.originTurnId, now, o.openedAt)) {
|
|
99006
|
+
process.stderr.write(`telegram gateway: obligation escalation deferred \u2014 settling (re-checking for an in-flight answer) origin=${o.originTurnId}
|
|
99007
|
+
`);
|
|
99008
|
+
return;
|
|
99009
|
+
}
|
|
98173
99010
|
driveEscalation({
|
|
98174
99011
|
escId: o.originTurnId,
|
|
98175
99012
|
inFlight: obligationEscalateInFlight,
|
|
@@ -98178,6 +99015,7 @@ function createObligationWiring(deps) {
|
|
|
98178
99015
|
maxAttempts: OBLIGATION_ESCALATE_MAX,
|
|
98179
99016
|
deadlineMs: OBLIGATION_ESCALATE_SEND_DEADLINE_MS
|
|
98180
99017
|
});
|
|
99018
|
+
escalateSettleGate.clear(o.originTurnId);
|
|
98181
99019
|
}
|
|
98182
99020
|
return {
|
|
98183
99021
|
closeObligationOnSubstantiveReply,
|
|
@@ -98213,14 +99051,14 @@ function purgeStaleTurnsForChat(chatId, keys, purger, isStale = () => true) {
|
|
|
98213
99051
|
// gateway/compaction-marker.ts
|
|
98214
99052
|
import { statSync as statSync17, unlinkSync as unlinkSync22 } from "node:fs";
|
|
98215
99053
|
import { homedir as homedir17 } from "node:os";
|
|
98216
|
-
import { join as
|
|
99054
|
+
import { join as join57 } from "node:path";
|
|
98217
99055
|
var COMPACTION_MARKER_FILE = "compaction-in-flight.json";
|
|
98218
99056
|
function resolveTelegramStateDir() {
|
|
98219
|
-
return process.env.TELEGRAM_STATE_DIR ??
|
|
99057
|
+
return process.env.TELEGRAM_STATE_DIR ?? join57(homedir17(), ".claude", "channels", "telegram");
|
|
98220
99058
|
}
|
|
98221
99059
|
function readCompactionMarkerAgeMs(stateDir, now) {
|
|
98222
99060
|
try {
|
|
98223
|
-
const st = statSync17(
|
|
99061
|
+
const st = statSync17(join57(stateDir, COMPACTION_MARKER_FILE));
|
|
98224
99062
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
98225
99063
|
} catch {
|
|
98226
99064
|
return null;
|
|
@@ -98228,7 +99066,7 @@ function readCompactionMarkerAgeMs(stateDir, now) {
|
|
|
98228
99066
|
}
|
|
98229
99067
|
function removeCompactionMarker(stateDir) {
|
|
98230
99068
|
try {
|
|
98231
|
-
unlinkSync22(
|
|
99069
|
+
unlinkSync22(join57(stateDir, COMPACTION_MARKER_FILE));
|
|
98232
99070
|
} catch {}
|
|
98233
99071
|
}
|
|
98234
99072
|
|
|
@@ -100293,7 +101131,7 @@ import {
|
|
|
100293
101131
|
readdirSync as readdirSync12,
|
|
100294
101132
|
readFileSync as readFileSync47
|
|
100295
101133
|
} from "fs";
|
|
100296
|
-
import { join as
|
|
101134
|
+
import { join as join58 } from "path";
|
|
100297
101135
|
|
|
100298
101136
|
// session-tail.ts
|
|
100299
101137
|
function sanitizeCwdToProjectName2(cwd) {
|
|
@@ -101616,8 +102454,8 @@ function startSubagentWatcher(config) {
|
|
|
101616
102454
|
if (stopped)
|
|
101617
102455
|
return;
|
|
101618
102456
|
pruneVanishedDirWatchers();
|
|
101619
|
-
const claudeHome =
|
|
101620
|
-
const projectsRoot =
|
|
102457
|
+
const claudeHome = join58(agentDir, ".claude");
|
|
102458
|
+
const projectsRoot = join58(claudeHome, "projects");
|
|
101621
102459
|
if (!fs3.existsSync(projectsRoot))
|
|
101622
102460
|
return;
|
|
101623
102461
|
let projectDirs;
|
|
@@ -101651,7 +102489,7 @@ function startSubagentWatcher(config) {
|
|
|
101651
102489
|
continue;
|
|
101652
102490
|
}
|
|
101653
102491
|
warnedForeignSlugs.delete(pDir);
|
|
101654
|
-
const projectPath =
|
|
102492
|
+
const projectPath = join58(projectsRoot, pDir);
|
|
101655
102493
|
let sessionDirs;
|
|
101656
102494
|
try {
|
|
101657
102495
|
sessionDirs = fs3.readdirSync(projectPath);
|
|
@@ -101661,7 +102499,7 @@ function startSubagentWatcher(config) {
|
|
|
101661
102499
|
for (const sDir of sessionDirs) {
|
|
101662
102500
|
if (sDir.endsWith(".jsonl"))
|
|
101663
102501
|
continue;
|
|
101664
|
-
const subagentsPath =
|
|
102502
|
+
const subagentsPath = join58(projectPath, sDir, "subagents");
|
|
101665
102503
|
if (!fs3.existsSync(subagentsPath))
|
|
101666
102504
|
continue;
|
|
101667
102505
|
const watchAndScan = (dirPath) => {
|
|
@@ -101670,7 +102508,7 @@ function startSubagentWatcher(config) {
|
|
|
101670
102508
|
const w = fs3.watch(dirPath, (_event, filename) => {
|
|
101671
102509
|
if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
|
|
101672
102510
|
return;
|
|
101673
|
-
const filePath =
|
|
102511
|
+
const filePath = join58(dirPath, filename.toString());
|
|
101674
102512
|
if (!knownFiles.has(filePath)) {
|
|
101675
102513
|
scanSubagentsDir(dirPath);
|
|
101676
102514
|
}
|
|
@@ -101684,7 +102522,7 @@ function startSubagentWatcher(config) {
|
|
|
101684
102522
|
scanSubagentsDir(dirPath);
|
|
101685
102523
|
};
|
|
101686
102524
|
watchAndScan(subagentsPath);
|
|
101687
|
-
const workflowsPath =
|
|
102525
|
+
const workflowsPath = join58(subagentsPath, "workflows");
|
|
101688
102526
|
if (fs3.existsSync(workflowsPath)) {
|
|
101689
102527
|
let wfDirs;
|
|
101690
102528
|
try {
|
|
@@ -101694,7 +102532,7 @@ function startSubagentWatcher(config) {
|
|
|
101694
102532
|
}
|
|
101695
102533
|
for (const wfDir of wfDirs) {
|
|
101696
102534
|
try {
|
|
101697
|
-
const wfPath =
|
|
102535
|
+
const wfPath = join58(workflowsPath, wfDir);
|
|
101698
102536
|
if (!fs3.statSync(wfPath).isDirectory())
|
|
101699
102537
|
continue;
|
|
101700
102538
|
watchAndScan(wfPath);
|
|
@@ -101714,7 +102552,7 @@ function startSubagentWatcher(config) {
|
|
|
101714
102552
|
for (const e of entries) {
|
|
101715
102553
|
if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
|
|
101716
102554
|
continue;
|
|
101717
|
-
const filePath =
|
|
102555
|
+
const filePath = join58(subagentsPath, e);
|
|
101718
102556
|
if (knownFiles.has(filePath))
|
|
101719
102557
|
continue;
|
|
101720
102558
|
const agentId = e.slice("agent-".length, -".jsonl".length);
|
|
@@ -101850,13 +102688,13 @@ import {
|
|
|
101850
102688
|
existsSync as existsSync48,
|
|
101851
102689
|
renameSync as renameSync24
|
|
101852
102690
|
} from "node:fs";
|
|
101853
|
-
import { join as
|
|
102691
|
+
import { join as join59, resolve as resolve10 } from "node:path";
|
|
101854
102692
|
import { homedir as homedir18 } from "node:os";
|
|
101855
102693
|
function registryDir2() {
|
|
101856
|
-
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
102694
|
+
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join59(homedir18(), ".switchroom", "worktrees"));
|
|
101857
102695
|
}
|
|
101858
102696
|
function recordPath(id) {
|
|
101859
|
-
return
|
|
102697
|
+
return join59(registryDir2(), `${id}.json`);
|
|
101860
102698
|
}
|
|
101861
102699
|
function ensureDir4() {
|
|
101862
102700
|
mkdirSync41(registryDir2(), { recursive: true });
|
|
@@ -102040,19 +102878,19 @@ function determineBridgeReconnectReason(opts) {
|
|
|
102040
102878
|
|
|
102041
102879
|
// gateway/update-announce.ts
|
|
102042
102880
|
import { existsSync as existsSync53, mkdirSync as mkdirSync45, openSync as openSync14, closeSync as closeSync14, readFileSync as readFileSync55 } from "node:fs";
|
|
102043
|
-
import { join as
|
|
102881
|
+
import { join as join64 } from "node:path";
|
|
102044
102882
|
import { homedir as homedir20 } from "node:os";
|
|
102045
102883
|
|
|
102046
102884
|
// ../src/host-control/audit-reader.ts
|
|
102047
102885
|
import { homedir as homedir19 } from "node:os";
|
|
102048
|
-
import { join as
|
|
102886
|
+
import { join as join63 } from "node:path";
|
|
102049
102887
|
|
|
102050
102888
|
// ../src/host-control/audit-rotation-config.ts
|
|
102051
102889
|
var DEFAULT_HOSTD_AUDIT_MAX_BYTES = 32 * 1024 * 1024;
|
|
102052
102890
|
|
|
102053
102891
|
// ../src/host-control/audit-reader.ts
|
|
102054
102892
|
function defaultAuditLogPath(home2 = homedir19()) {
|
|
102055
|
-
return
|
|
102893
|
+
return join63(home2, ".switchroom", "host-control-audit.log");
|
|
102056
102894
|
}
|
|
102057
102895
|
var AUDIT_READ_WINDOW_BYTES = 4 * 1024 * 1024;
|
|
102058
102896
|
function parseAuditLine(line) {
|
|
@@ -102251,15 +103089,15 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
102251
103089
|
`);
|
|
102252
103090
|
}
|
|
102253
103091
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
102254
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ??
|
|
102255
|
-
const dir =
|
|
103092
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join64(homedir20(), ".switchroom");
|
|
103093
|
+
const dir = join64(stateDir, "update-announced");
|
|
102256
103094
|
try {
|
|
102257
103095
|
mkdirSync45(dir, { recursive: true });
|
|
102258
103096
|
} catch {
|
|
102259
103097
|
return false;
|
|
102260
103098
|
}
|
|
102261
103099
|
const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
|
|
102262
|
-
const path3 =
|
|
103100
|
+
const path3 = join64(dir, safeId);
|
|
102263
103101
|
try {
|
|
102264
103102
|
const fd = openSync14(path3, "wx");
|
|
102265
103103
|
closeSync14(fd);
|
|
@@ -102481,7 +103319,7 @@ function createIssuesCardHandle(opts) {
|
|
|
102481
103319
|
|
|
102482
103320
|
// issues-watcher.ts
|
|
102483
103321
|
import { existsSync as existsSync55, statSync as statSync21 } from "node:fs";
|
|
102484
|
-
import { join as
|
|
103322
|
+
import { join as join66 } from "node:path";
|
|
102485
103323
|
|
|
102486
103324
|
// ../src/issues/store.ts
|
|
102487
103325
|
import {
|
|
@@ -102497,7 +103335,7 @@ import {
|
|
|
102497
103335
|
writeFileSync as writeFileSync46,
|
|
102498
103336
|
writeSync as writeSync8
|
|
102499
103337
|
} from "node:fs";
|
|
102500
|
-
import { join as
|
|
103338
|
+
import { join as join65 } from "node:path";
|
|
102501
103339
|
import { randomBytes as randomBytes10 } from "node:crypto";
|
|
102502
103340
|
import { execSync } from "node:child_process";
|
|
102503
103341
|
|
|
@@ -102516,7 +103354,7 @@ init_redact();
|
|
|
102516
103354
|
var ISSUES_FILE = "issues.jsonl";
|
|
102517
103355
|
var ISSUES_LOCK = "issues.lock";
|
|
102518
103356
|
function readAll(stateDir) {
|
|
102519
|
-
const path3 =
|
|
103357
|
+
const path3 = join65(stateDir, ISSUES_FILE);
|
|
102520
103358
|
if (!existsSync54(path3))
|
|
102521
103359
|
return [];
|
|
102522
103360
|
let raw;
|
|
@@ -102553,7 +103391,7 @@ function list2(stateDir, opts = {}) {
|
|
|
102553
103391
|
});
|
|
102554
103392
|
}
|
|
102555
103393
|
function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
102556
|
-
if (!existsSync54(
|
|
103394
|
+
if (!existsSync54(join65(stateDir, ISSUES_FILE)))
|
|
102557
103395
|
return 0;
|
|
102558
103396
|
return withLock(stateDir, () => {
|
|
102559
103397
|
const all2 = readAll(stateDir);
|
|
@@ -102571,7 +103409,7 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
102571
103409
|
});
|
|
102572
103410
|
}
|
|
102573
103411
|
function writeAll(stateDir, events) {
|
|
102574
|
-
const path3 =
|
|
103412
|
+
const path3 = join65(stateDir, ISSUES_FILE);
|
|
102575
103413
|
sweepOrphanTmpFiles(stateDir);
|
|
102576
103414
|
const tmp = `${path3}.tmp-${process.pid}-${randomBytes10(4).toString("hex")}`;
|
|
102577
103415
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -102593,7 +103431,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
102593
103431
|
for (const entry of entries) {
|
|
102594
103432
|
if (!entry.startsWith(TMP_PREFIX))
|
|
102595
103433
|
continue;
|
|
102596
|
-
const tmpPath2 =
|
|
103434
|
+
const tmpPath2 = join65(stateDir, entry);
|
|
102597
103435
|
try {
|
|
102598
103436
|
const stat = statSync20(tmpPath2);
|
|
102599
103437
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -102605,7 +103443,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
102605
103443
|
var LOCK_RETRY_MS = 25;
|
|
102606
103444
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
102607
103445
|
function withLock(stateDir, fn) {
|
|
102608
|
-
const lockPath =
|
|
103446
|
+
const lockPath = join65(stateDir, ISSUES_LOCK);
|
|
102609
103447
|
const startedAt = Date.now();
|
|
102610
103448
|
let fd = null;
|
|
102611
103449
|
while (fd === null) {
|
|
@@ -102690,7 +103528,7 @@ function isIssueEvent(v) {
|
|
|
102690
103528
|
// issues-watcher.ts
|
|
102691
103529
|
var DEFAULT_POLL_INTERVAL_MS2 = 2000;
|
|
102692
103530
|
function startIssuesWatcher(opts) {
|
|
102693
|
-
const path3 =
|
|
103531
|
+
const path3 = join66(opts.stateDir, ISSUES_FILE);
|
|
102694
103532
|
const log = opts.log ?? (() => {});
|
|
102695
103533
|
const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
102696
103534
|
const setIntervalFn = opts.setInterval ?? setInterval;
|
|
@@ -103450,7 +104288,7 @@ function readBashCommand(inputPreview) {
|
|
|
103450
104288
|
|
|
103451
104289
|
// gateway/scoped-grant-store.ts
|
|
103452
104290
|
init_state_owner();
|
|
103453
|
-
import { join as
|
|
104291
|
+
import { join as join67 } from "node:path";
|
|
103454
104292
|
|
|
103455
104293
|
// scoped-approval.ts
|
|
103456
104294
|
var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
|
|
@@ -103492,7 +104330,7 @@ function scopedGrantPersistEnabled(env = process.env) {
|
|
|
103492
104330
|
return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
|
|
103493
104331
|
}
|
|
103494
104332
|
function createScopedGrantStore(stateDir, env = process.env, log = (l) => process.stderr.write(l)) {
|
|
103495
|
-
const filePath =
|
|
104333
|
+
const filePath = join67(stateDir, "scoped-grants.json");
|
|
103496
104334
|
const enabled7 = scopedGrantPersistEnabled(env);
|
|
103497
104335
|
let unreadable = false;
|
|
103498
104336
|
function read() {
|
|
@@ -103871,7 +104709,7 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
|
|
|
103871
104709
|
// credits-watch.ts
|
|
103872
104710
|
init_card_format();
|
|
103873
104711
|
import { readFileSync as readFileSync58, writeFileSync as writeFileSync47, existsSync as existsSync56, mkdirSync as mkdirSync47 } from "fs";
|
|
103874
|
-
import { join as
|
|
104712
|
+
import { join as join68 } from "path";
|
|
103875
104713
|
var STATE_FILE = "credits-watch.json";
|
|
103876
104714
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
103877
104715
|
var KNOWN_CREDIT_REASONS = [
|
|
@@ -103893,7 +104731,7 @@ function emptyCreditState() {
|
|
|
103893
104731
|
return { lastNotifiedReason: null, lastNotifiedAt: 0 };
|
|
103894
104732
|
}
|
|
103895
104733
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
103896
|
-
const path3 =
|
|
104734
|
+
const path3 = join68(claudeConfigDir, ".claude.json");
|
|
103897
104735
|
if (!existsSync56(path3))
|
|
103898
104736
|
return null;
|
|
103899
104737
|
let raw;
|
|
@@ -103976,7 +104814,7 @@ function humanizeReason(reason) {
|
|
|
103976
104814
|
}
|
|
103977
104815
|
}
|
|
103978
104816
|
function loadCreditState(stateDir) {
|
|
103979
|
-
const path3 =
|
|
104817
|
+
const path3 = join68(stateDir, STATE_FILE);
|
|
103980
104818
|
if (!existsSync56(path3))
|
|
103981
104819
|
return emptyCreditState();
|
|
103982
104820
|
try {
|
|
@@ -103993,7 +104831,7 @@ function loadCreditState(stateDir) {
|
|
|
103993
104831
|
}
|
|
103994
104832
|
function saveCreditState(stateDir, state7) {
|
|
103995
104833
|
mkdirSync47(stateDir, { recursive: true });
|
|
103996
|
-
const path3 =
|
|
104834
|
+
const path3 = join68(stateDir, STATE_FILE);
|
|
103997
104835
|
writeFileSync47(path3, JSON.stringify(state7, null, 2) + `
|
|
103998
104836
|
`, { mode: 384 });
|
|
103999
104837
|
}
|
|
@@ -104002,7 +104840,7 @@ function saveCreditState(stateDir, state7) {
|
|
|
104002
104840
|
init_auth_snapshot_format();
|
|
104003
104841
|
init_card_format();
|
|
104004
104842
|
import { readFileSync as readFileSync59, writeFileSync as writeFileSync48, existsSync as existsSync57, mkdirSync as mkdirSync48 } from "fs";
|
|
104005
|
-
import { join as
|
|
104843
|
+
import { join as join69 } from "path";
|
|
104006
104844
|
var STATE_FILE2 = "quota-watch.json";
|
|
104007
104845
|
function emptyQuotaWatchState() {
|
|
104008
104846
|
return {};
|
|
@@ -104240,7 +105078,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
104240
105078
|
`);
|
|
104241
105079
|
}
|
|
104242
105080
|
function loadQuotaWatchState(stateDir) {
|
|
104243
|
-
const path3 =
|
|
105081
|
+
const path3 = join69(stateDir, STATE_FILE2);
|
|
104244
105082
|
if (!existsSync57(path3))
|
|
104245
105083
|
return emptyQuotaWatchState();
|
|
104246
105084
|
try {
|
|
@@ -104262,7 +105100,7 @@ function loadQuotaWatchState(stateDir) {
|
|
|
104262
105100
|
}
|
|
104263
105101
|
function saveQuotaWatchState(stateDir, state7) {
|
|
104264
105102
|
mkdirSync48(stateDir, { recursive: true });
|
|
104265
|
-
const path3 =
|
|
105103
|
+
const path3 = join69(stateDir, STATE_FILE2);
|
|
104266
105104
|
writeFileSync48(path3, JSON.stringify(state7, null, 2) + `
|
|
104267
105105
|
`, { mode: 384 });
|
|
104268
105106
|
}
|
|
@@ -104319,17 +105157,17 @@ import {
|
|
|
104319
105157
|
unlinkSync as unlinkSync28,
|
|
104320
105158
|
utimesSync as utimesSync2
|
|
104321
105159
|
} from "node:fs";
|
|
104322
|
-
import { join as
|
|
105160
|
+
import { join as join70 } from "node:path";
|
|
104323
105161
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
104324
105162
|
var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
|
|
104325
105163
|
var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
|
|
104326
105164
|
function removeTurnActiveMarker2(stateDir) {
|
|
104327
105165
|
try {
|
|
104328
|
-
unlinkSync28(
|
|
105166
|
+
unlinkSync28(join70(stateDir, TURN_ACTIVE_MARKER_FILE2));
|
|
104329
105167
|
} catch {}
|
|
104330
105168
|
}
|
|
104331
105169
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
104332
|
-
const path3 =
|
|
105170
|
+
const path3 = join70(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
104333
105171
|
if (!existsSync58(path3))
|
|
104334
105172
|
return false;
|
|
104335
105173
|
const now = opts.now ?? Date.now();
|
|
@@ -104360,7 +105198,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
104360
105198
|
}
|
|
104361
105199
|
}
|
|
104362
105200
|
function readTurnActiveMarkerAgeMs2(stateDir, now) {
|
|
104363
|
-
const path3 =
|
|
105201
|
+
const path3 = join70(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
104364
105202
|
try {
|
|
104365
105203
|
const st = statSync22(path3);
|
|
104366
105204
|
return (now ?? Date.now()) - st.mtimeMs;
|
|
@@ -104375,11 +105213,11 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
|
|
|
104375
105213
|
// gateway/gateway-heartbeat.ts
|
|
104376
105214
|
init_state_owner();
|
|
104377
105215
|
import { utimesSync as utimesSync3 } from "node:fs";
|
|
104378
|
-
import { join as
|
|
105216
|
+
import { join as join71 } from "node:path";
|
|
104379
105217
|
var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
|
|
104380
105218
|
var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
|
|
104381
105219
|
function touchGatewayHeartbeat(stateDir) {
|
|
104382
|
-
const path3 =
|
|
105220
|
+
const path3 = join71(stateDir, GATEWAY_HEARTBEAT_FILE);
|
|
104383
105221
|
const now = new Date;
|
|
104384
105222
|
try {
|
|
104385
105223
|
utimesSync3(path3, now, now);
|
|
@@ -104410,7 +105248,7 @@ import {
|
|
|
104410
105248
|
unlinkSync as unlinkSync29,
|
|
104411
105249
|
writeFileSync as writeFileSync49
|
|
104412
105250
|
} from "node:fs";
|
|
104413
|
-
import { join as
|
|
105251
|
+
import { join as join72 } from "node:path";
|
|
104414
105252
|
var BOOT_BEACON_FILE = "gateway-beacon.json";
|
|
104415
105253
|
var HOST_BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id";
|
|
104416
105254
|
var CGROUP_MEMORY_EVENTS_PATH = "/sys/fs/cgroup/memory.events";
|
|
@@ -104509,7 +105347,7 @@ function sampleBeaconHost(paths = {}) {
|
|
|
104509
105347
|
return sample3;
|
|
104510
105348
|
}
|
|
104511
105349
|
function writeBootBeaconFile(stateDir, beacon) {
|
|
104512
|
-
const path3 =
|
|
105350
|
+
const path3 = join72(stateDir, BOOT_BEACON_FILE);
|
|
104513
105351
|
const tmp = `${path3}.tmp-${process.pid}`;
|
|
104514
105352
|
let fd;
|
|
104515
105353
|
try {
|
|
@@ -104901,10 +105739,10 @@ function startOutboxSweep(deps) {
|
|
|
104901
105739
|
}
|
|
104902
105740
|
|
|
104903
105741
|
// ../src/build-info.ts
|
|
104904
|
-
var VERSION2 = "0.21.
|
|
104905
|
-
var COMMIT_SHA = "
|
|
104906
|
-
var COMMIT_DATE = "2026-08-
|
|
104907
|
-
var LATEST_PR =
|
|
105742
|
+
var VERSION2 = "0.21.11";
|
|
105743
|
+
var COMMIT_SHA = "7a80af9d";
|
|
105744
|
+
var COMMIT_DATE = "2026-08-14T09:19:49Z";
|
|
105745
|
+
var LATEST_PR = 4713;
|
|
104908
105746
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
104909
105747
|
|
|
104910
105748
|
// gateway/boot-version.ts
|
|
@@ -104982,11 +105820,11 @@ init_peercred();
|
|
|
104982
105820
|
import * as net5 from "node:net";
|
|
104983
105821
|
import * as fs3 from "node:fs";
|
|
104984
105822
|
import { homedir as homedir21 } from "node:os";
|
|
104985
|
-
import { join as
|
|
105823
|
+
import { join as join73 } from "node:path";
|
|
104986
105824
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
104987
105825
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
104988
|
-
var LEGACY_SOCKET_PATH2 =
|
|
104989
|
-
var OPERATOR_SOCKET_PATH2 =
|
|
105826
|
+
var LEGACY_SOCKET_PATH2 = join73(homedir21(), ".switchroom", "vault-broker.sock");
|
|
105827
|
+
var OPERATOR_SOCKET_PATH2 = join73(homedir21(), ".switchroom", "broker-operator", "sock");
|
|
104990
105828
|
function defaultBrokerSocketPath2() {
|
|
104991
105829
|
if (fs3.existsSync(OPERATOR_SOCKET_PATH2))
|
|
104992
105830
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -105870,7 +106708,7 @@ function resolveVaultApprovalPosture(broker) {
|
|
|
105870
106708
|
// registry/turns-schema.ts
|
|
105871
106709
|
init_state_owner();
|
|
105872
106710
|
import { chmodSync as chmodSync12 } from "fs";
|
|
105873
|
-
import { join as
|
|
106711
|
+
import { join as join74 } from "path";
|
|
105874
106712
|
var DatabaseClass3 = null;
|
|
105875
106713
|
function loadDatabaseClass3() {
|
|
105876
106714
|
if (DatabaseClass3 != null)
|
|
@@ -105942,9 +106780,9 @@ function applySchema(db3) {
|
|
|
105942
106780
|
}
|
|
105943
106781
|
function openTurnsDb(agentDir) {
|
|
105944
106782
|
const Database = loadDatabaseClass3();
|
|
105945
|
-
const dir =
|
|
106783
|
+
const dir = join74(agentDir, "telegram");
|
|
105946
106784
|
mkdirStateSync(dir, { recursive: true, mode: 448 });
|
|
105947
|
-
const path3 =
|
|
106785
|
+
const path3 = join74(dir, "registry.db");
|
|
105948
106786
|
const db3 = new Database(path3, { create: true });
|
|
105949
106787
|
applySchema(db3);
|
|
105950
106788
|
try {
|
|
@@ -106287,7 +107125,7 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
106287
107125
|
|
|
106288
107126
|
// gateway/boot-briefing-wiring.ts
|
|
106289
107127
|
import { existsSync as existsSync60, readFileSync as readFileSync63, writeFileSync as writeFileSync50 } from "node:fs";
|
|
106290
|
-
import { join as
|
|
107128
|
+
import { join as join75 } from "node:path";
|
|
106291
107129
|
init_history();
|
|
106292
107130
|
|
|
106293
107131
|
// gateway/resume-inbound-builder.ts
|
|
@@ -106594,8 +107432,8 @@ function readDailyMemory(agentDir, env, nowMs3, readFile = (p) => readFileSync63
|
|
|
106594
107432
|
const date = agentLocalDate(nowMs3, env.SWITCHROOM_TIMEZONE || env.TZ || undefined);
|
|
106595
107433
|
if (!date)
|
|
106596
107434
|
return null;
|
|
106597
|
-
const workspaceDir = env.WORKSPACE_DIR && env.WORKSPACE_DIR.trim() ? env.WORKSPACE_DIR :
|
|
106598
|
-
const file =
|
|
107435
|
+
const workspaceDir = env.WORKSPACE_DIR && env.WORKSPACE_DIR.trim() ? env.WORKSPACE_DIR : join75(agentDir, "workspace");
|
|
107436
|
+
const file = join75(workspaceDir, "memory", `${date}.md`);
|
|
106599
107437
|
try {
|
|
106600
107438
|
const content3 = readFile(file);
|
|
106601
107439
|
if (!content3 || !content3.trim())
|
|
@@ -106624,7 +107462,7 @@ async function maybeQueueBootBriefing(opts) {
|
|
|
106624
107462
|
try {
|
|
106625
107463
|
const agentDir = opts.stateDir.endsWith("/telegram") ? opts.stateDir.slice(0, -"/telegram".length) : opts.stateDir;
|
|
106626
107464
|
const bootId = opts.env.SWITCHROOM_GATEWAY_BOOT_ID;
|
|
106627
|
-
const genMarkerPath =
|
|
107465
|
+
const genMarkerPath = join75(agentDir, ".boot-briefing-generation");
|
|
106628
107466
|
if (bootId) {
|
|
106629
107467
|
let prevGen = null;
|
|
106630
107468
|
try {
|
|
@@ -106646,7 +107484,7 @@ async function maybeQueueBootBriefing(opts) {
|
|
|
106646
107484
|
`);
|
|
106647
107485
|
} catch {}
|
|
106648
107486
|
};
|
|
106649
|
-
const forceFresh = opts.env.SWITCHROOM_FORCE_FRESH === "1" || existsSync60(
|
|
107487
|
+
const forceFresh = opts.env.SWITCHROOM_FORCE_FRESH === "1" || existsSync60(join75(agentDir, ".force-fresh-session"));
|
|
106650
107488
|
const decision = decideBootBriefing({
|
|
106651
107489
|
briefingMode: opts.env.SWITCHROOM_SESSION_BRIEFING,
|
|
106652
107490
|
resumeMode: opts.env.SWITCHROOM_RESUME_MODE,
|
|
@@ -106680,7 +107518,7 @@ async function maybeQueueBootBriefing(opts) {
|
|
|
106680
107518
|
return null;
|
|
106681
107519
|
}
|
|
106682
107520
|
const restartReason = readRestartBreadcrumb({
|
|
106683
|
-
restartReasonPath:
|
|
107521
|
+
restartReasonPath: join75(agentDir, ".restart-reason"),
|
|
106684
107522
|
env: opts.env,
|
|
106685
107523
|
readFile: (p) => readFileSync63(p, "utf8")
|
|
106686
107524
|
});
|
|
@@ -106717,9 +107555,9 @@ async function maybeQueueBootBriefing(opts) {
|
|
|
106717
107555
|
|
|
106718
107556
|
// gateway/pending-turn-env.ts
|
|
106719
107557
|
import { existsSync as existsSync61, renameSync as renameSync29, rmSync as rmSync8, writeFileSync as writeFileSync51 } from "node:fs";
|
|
106720
|
-
import { join as
|
|
107558
|
+
import { join as join76 } from "node:path";
|
|
106721
107559
|
function writePendingTurnEnv(agentDir, pending, log = (l) => process.stderr.write(l)) {
|
|
106722
|
-
const pendingEnvPath =
|
|
107560
|
+
const pendingEnvPath = join76(agentDir, ".pending-turn.env");
|
|
106723
107561
|
try {
|
|
106724
107562
|
if (pending != null) {
|
|
106725
107563
|
const lines = [
|
|
@@ -106752,7 +107590,7 @@ function writePendingTurnEnv(agentDir, pending, log = (l) => process.stderr.writ
|
|
|
106752
107590
|
|
|
106753
107591
|
// gateway/agent-process-liveness.ts
|
|
106754
107592
|
import { existsSync as existsSync62, readFileSync as readFileSync64, writeFileSync as writeFileSync52, renameSync as renameSync30, unlinkSync as unlinkSync30, readdirSync as readdirSync16 } from "node:fs";
|
|
106755
|
-
import { join as
|
|
107593
|
+
import { join as join77, dirname as dirname28, basename as basename18 } from "node:path";
|
|
106756
107594
|
var AGENT_PROCESS_RECORD_FILE = "agent-process.json";
|
|
106757
107595
|
var BOOT_RESUME_DONE_FILE = ".boot-resume-done";
|
|
106758
107596
|
function parseProcStat(raw) {
|
|
@@ -106775,7 +107613,7 @@ function readProcIdentity(pid, procRoot = "/proc") {
|
|
|
106775
107613
|
return null;
|
|
106776
107614
|
let identity;
|
|
106777
107615
|
try {
|
|
106778
|
-
identity = parseProcStat(readFileSync64(
|
|
107616
|
+
identity = parseProcStat(readFileSync64(join77(procRoot, String(pid), "stat"), "utf8"));
|
|
106779
107617
|
} catch {
|
|
106780
107618
|
return null;
|
|
106781
107619
|
}
|
|
@@ -106807,7 +107645,7 @@ function readAgentProcessRecord(path3) {
|
|
|
106807
107645
|
return rec;
|
|
106808
107646
|
}
|
|
106809
107647
|
function bootResumeDonePath(stateDir) {
|
|
106810
|
-
return
|
|
107648
|
+
return join77(stateDir, BOOT_RESUME_DONE_FILE);
|
|
106811
107649
|
}
|
|
106812
107650
|
function containerBootIdentity(procRoot = "/proc") {
|
|
106813
107651
|
return readProcIdentity(1, procRoot)?.starttime ?? null;
|
|
@@ -106840,7 +107678,7 @@ function sweepStrayTmps(path3) {
|
|
|
106840
107678
|
if (!name.startsWith(prefix))
|
|
106841
107679
|
continue;
|
|
106842
107680
|
try {
|
|
106843
|
-
unlinkSync30(
|
|
107681
|
+
unlinkSync30(join77(dirname28(path3), name));
|
|
106844
107682
|
} catch {}
|
|
106845
107683
|
}
|
|
106846
107684
|
} catch {}
|
|
@@ -106892,7 +107730,7 @@ function detectGatewayOnlyRespawn(opts) {
|
|
|
106892
107730
|
return { gatewayOnly: false, reason: "guard-disabled", pid: null };
|
|
106893
107731
|
const procRoot = opts.procRoot ?? "/proc";
|
|
106894
107732
|
const sentinel = readBootResumeSentinel(opts.sentinelPath ?? bootResumeDonePath(opts.stateDir), { procRoot });
|
|
106895
|
-
const recordPath2 = opts.recordPath ??
|
|
107733
|
+
const recordPath2 = opts.recordPath ?? join77(opts.stateDir, AGENT_PROCESS_RECORD_FILE);
|
|
106896
107734
|
const record2 = readAgentProcessRecord(recordPath2);
|
|
106897
107735
|
const live = record2 == null ? null : readProcIdentity(record2.pid, procRoot);
|
|
106898
107736
|
return decideGatewayOnlyRespawn({
|
|
@@ -107450,7 +108288,7 @@ if (isGatewayMain) {
|
|
|
107450
108288
|
shutdownAnalytics();
|
|
107451
108289
|
});
|
|
107452
108290
|
}
|
|
107453
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
108291
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join78(homedir22(), ".claude", "channels", "telegram");
|
|
107454
108292
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
107455
108293
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
107456
108294
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -107550,11 +108388,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
107550
108388
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
107551
108389
|
timer3.unref?.();
|
|
107552
108390
|
}
|
|
107553
|
-
var ACCESS_FILE =
|
|
107554
|
-
var APPROVED_DIR =
|
|
107555
|
-
var ENV_FILE =
|
|
107556
|
-
var INBOX_DIR =
|
|
107557
|
-
var PEOPLE_FILE =
|
|
108391
|
+
var ACCESS_FILE = join78(STATE_DIR, "access.json");
|
|
108392
|
+
var APPROVED_DIR = join78(STATE_DIR, "approved");
|
|
108393
|
+
var ENV_FILE = join78(STATE_DIR, ".env");
|
|
108394
|
+
var INBOX_DIR = join78(STATE_DIR, "inbox");
|
|
108395
|
+
var PEOPLE_FILE = join78(STATE_DIR, "people.json");
|
|
107558
108396
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
107559
108397
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
107560
108398
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -107681,7 +108519,7 @@ function assertSendable(f) {
|
|
|
107681
108519
|
} catch {
|
|
107682
108520
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
107683
108521
|
}
|
|
107684
|
-
const inbox =
|
|
108522
|
+
const inbox = join78(stateReal, "inbox");
|
|
107685
108523
|
if (real.startsWith(stateReal + sep6) && !real.startsWith(inbox + sep6)) {
|
|
107686
108524
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
107687
108525
|
}
|
|
@@ -107710,7 +108548,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
107710
108548
|
if (isGatewayMain && HISTORY_ENABLED) {
|
|
107711
108549
|
try {
|
|
107712
108550
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
107713
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
108551
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join78(STATE_DIR, "history.db")}
|
|
107714
108552
|
`);
|
|
107715
108553
|
} catch (err) {
|
|
107716
108554
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -107733,7 +108571,7 @@ bootResumeInit:
|
|
|
107733
108571
|
let markerTurnKey = null;
|
|
107734
108572
|
let markerAgeMs = null;
|
|
107735
108573
|
try {
|
|
107736
|
-
const markerPath =
|
|
108574
|
+
const markerPath = join78(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
107737
108575
|
if (existsSync65(markerPath)) {
|
|
107738
108576
|
const st = statSync25(markerPath);
|
|
107739
108577
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
@@ -107758,10 +108596,10 @@ bootResumeInit:
|
|
|
107758
108596
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
107759
108597
|
`);
|
|
107760
108598
|
} else {
|
|
107761
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
108599
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join78(agentDir, "telegram", "registry.db")}
|
|
107762
108600
|
`);
|
|
107763
108601
|
}
|
|
107764
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
108602
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join78(STATE_DIR, "bridge-dead-escalation.json"));
|
|
107765
108603
|
if (bridgeDeadMarker != null) {
|
|
107766
108604
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
107767
108605
|
process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
|
|
@@ -107774,7 +108612,7 @@ bootResumeInit:
|
|
|
107774
108612
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
107775
108613
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
107776
108614
|
if (pending2 != null && selfAgent) {
|
|
107777
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
108615
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join78(STATE_DIR, "clean-shutdown.json");
|
|
107778
108616
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
107779
108617
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
107780
108618
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -107981,7 +108819,7 @@ function checkApprovals() {
|
|
|
107981
108819
|
return;
|
|
107982
108820
|
}
|
|
107983
108821
|
for (const senderId of files) {
|
|
107984
|
-
const file =
|
|
108822
|
+
const file = join78(APPROVED_DIR, senderId);
|
|
107985
108823
|
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync9(file, { force: true }), (err) => {
|
|
107986
108824
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
107987
108825
|
`);
|
|
@@ -108159,7 +108997,7 @@ function noteAgentOutputAt(key, ts) {
|
|
|
108159
108997
|
lastAgentOutputAt.delete(oldest);
|
|
108160
108998
|
}
|
|
108161
108999
|
}
|
|
108162
|
-
var OBLIGATION_STORE_PATH =
|
|
109000
|
+
var OBLIGATION_STORE_PATH = join78(STATE_DIR, "obligations.json");
|
|
108163
109001
|
var obligationStoreFs = {
|
|
108164
109002
|
readFileSync: (p) => readFileSync67(p, "utf8"),
|
|
108165
109003
|
writeFileSync: (p, d) => writeStateFileSync2(p, d),
|
|
@@ -108433,7 +109271,9 @@ function resolveReplyOwnerTurn(liveTurn, chatId, args) {
|
|
|
108433
109271
|
return resolveReplyOwnerTurnWith({ findTurnByOriginId, findTurnByQuotedMessageId, findLatestTurnForChat, now: Date.now }, liveTurn, chatId, args);
|
|
108434
109272
|
}
|
|
108435
109273
|
function resolveAnswerThreadWithLog(chatId, explicitThreadId, originTurn, originVia, liveTurn, surface) {
|
|
108436
|
-
const
|
|
109274
|
+
const originAnchor = isCrossChatAnchor2(chatId, originTurn?.sessionChatId) ? null : originTurn;
|
|
109275
|
+
const liveAnchor = isCrossChatAnchor2(chatId, liveTurn?.sessionChatId) ? null : liveTurn;
|
|
109276
|
+
const recovered = LATE_REPLY_TOPIC_RECOVERY_ENABLED && explicitThreadId == null && originAnchor == null && liveAnchor == null ? findLatestTurnForChat(chatId, { endedOnly: false }) : null;
|
|
108437
109277
|
const threadId = resolveAnswerThreadId({
|
|
108438
109278
|
explicitThreadId,
|
|
108439
109279
|
originResolved: originTurn != null,
|
|
@@ -108442,16 +109282,31 @@ function resolveAnswerThreadWithLog(chatId, explicitThreadId, originTurn, origin
|
|
|
108442
109282
|
liveTurnPresent: liveTurn != null,
|
|
108443
109283
|
lastEndedResolvedForChat: recovered != null,
|
|
108444
109284
|
lastEndedThreadIdForChat: recovered?.sessionThreadId,
|
|
108445
|
-
frameworkTopicAuthority: REPLY_TOPIC_AUTHORITY_ENABLED
|
|
109285
|
+
frameworkTopicAuthority: REPLY_TOPIC_AUTHORITY_ENABLED,
|
|
109286
|
+
targetChatId: chatId,
|
|
109287
|
+
originChatId: originTurn?.sessionChatId,
|
|
109288
|
+
liveChatId: liveTurn?.sessionChatId
|
|
108446
109289
|
});
|
|
108447
|
-
|
|
108448
|
-
|
|
108449
|
-
|
|
108450
|
-
|
|
108451
|
-
|
|
108452
|
-
|
|
108453
|
-
|
|
108454
|
-
|
|
109290
|
+
answerRouteOverrides.note({
|
|
109291
|
+
chatId,
|
|
109292
|
+
enabled: REPLY_TOPIC_AUTHORITY_ENABLED,
|
|
109293
|
+
explicitThreadId,
|
|
109294
|
+
anchored: originAnchor != null || liveAnchor != null,
|
|
109295
|
+
routedThreadId: threadId,
|
|
109296
|
+
nowMs: Date.now()
|
|
109297
|
+
});
|
|
109298
|
+
process.stderr.write(formatReplyRouteLog2({
|
|
109299
|
+
surface,
|
|
109300
|
+
chatId,
|
|
109301
|
+
threadId,
|
|
109302
|
+
explicitThreadId,
|
|
109303
|
+
originTurn,
|
|
109304
|
+
originVia,
|
|
109305
|
+
liveTurn,
|
|
109306
|
+
recovered,
|
|
109307
|
+
frameworkTopicAuthority: REPLY_TOPIC_AUTHORITY_ENABLED,
|
|
109308
|
+
hasDifferentThreadedRecentTurn: (liveThreadId) => hasDifferentThreadedRecentTurn(chatId, liveThreadId)
|
|
109309
|
+
}));
|
|
108455
109310
|
return threadId;
|
|
108456
109311
|
}
|
|
108457
109312
|
function hasDifferentThreadedRecentTurn(chatId, liveThreadId) {
|
|
@@ -108928,7 +109783,7 @@ function emitTurnRecord(turn, endedAt) {
|
|
|
108928
109783
|
},
|
|
108929
109784
|
rename: (from, to) => renameSync32(from, to)
|
|
108930
109785
|
});
|
|
108931
|
-
|
|
109786
|
+
appendFileSync12(turnsPath, rec);
|
|
108932
109787
|
} catch {}
|
|
108933
109788
|
}
|
|
108934
109789
|
function maybeProactiveCompact() {
|
|
@@ -110497,7 +111352,7 @@ var PIN_STATUS_WHILE_WORKING = (() => {
|
|
|
110497
111352
|
})();
|
|
110498
111353
|
var statusPinClaims = new Map;
|
|
110499
111354
|
var statusPinRightsCache = new PinRightsCache2;
|
|
110500
|
-
var STATUS_PIN_STORE_PATH =
|
|
111355
|
+
var STATUS_PIN_STORE_PATH = join78(STATE_DIR, "status-pins.json");
|
|
110501
111356
|
var statusPinStoreFs = {
|
|
110502
111357
|
readFileSync: (p) => readFileSync67(p, "utf8"),
|
|
110503
111358
|
writeFileSync: (p, d) => writeStateFileSync2(p, d),
|
|
@@ -110505,7 +111360,7 @@ var statusPinStoreFs = {
|
|
|
110505
111360
|
existsSync: (p) => existsSync65(p)
|
|
110506
111361
|
};
|
|
110507
111362
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
110508
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
111363
|
+
var ACTIVITY_CARD_STORE_PATH = join78(STATE_DIR, "activity-cards-pending.json");
|
|
110509
111364
|
var activityCardStoreFs = {
|
|
110510
111365
|
readFileSync: (p) => readFileSync67(p, "utf8"),
|
|
110511
111366
|
writeFileSync: (p, d) => writeStateFileSync2(p, d),
|
|
@@ -110513,7 +111368,7 @@ var activityCardStoreFs = {
|
|
|
110513
111368
|
existsSync: (p) => existsSync65(p)
|
|
110514
111369
|
};
|
|
110515
111370
|
var activityCardPersistEnabled = !STATIC;
|
|
110516
|
-
var QUEUED_CARD_STORE_PATH =
|
|
111371
|
+
var QUEUED_CARD_STORE_PATH = join78(STATE_DIR, "queued-cards-pending.json");
|
|
110517
111372
|
var queuedCardStoreFs = {
|
|
110518
111373
|
readFileSync: (p) => readFileSync67(p, "utf8"),
|
|
110519
111374
|
writeFileSync: (p, d) => writeStateFileSync2(p, d),
|
|
@@ -110801,7 +111656,7 @@ async function unpinAllStatusPins() {
|
|
|
110801
111656
|
}
|
|
110802
111657
|
}
|
|
110803
111658
|
var stalePinSweepEligible = false;
|
|
110804
|
-
var STALE_PIN_SWEEP_STORE_PATH =
|
|
111659
|
+
var STALE_PIN_SWEEP_STORE_PATH = join78(STATE_DIR, "stale-pin-sweep.json");
|
|
110805
111660
|
var sweepStoreFs = {
|
|
110806
111661
|
readFileSync: (p) => readFileSync67(p, "utf-8"),
|
|
110807
111662
|
writeFileSync: (p, data) => atomicWriteStateFileSync2(p, data, 384),
|
|
@@ -110842,12 +111697,12 @@ var getPinnedProgressCardMessageId = null;
|
|
|
110842
111697
|
var completeProgressCardTurn = null;
|
|
110843
111698
|
var subagentWatcher = null;
|
|
110844
111699
|
var workerActivityFeed = null;
|
|
110845
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
111700
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join78(STATE_DIR, "gateway.sock");
|
|
110846
111701
|
if (isGatewayMain)
|
|
110847
111702
|
mkdirStateSync2(STATE_DIR, { recursive: true, mode: 448 });
|
|
110848
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
110849
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
110850
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
111703
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join78(STATE_DIR, "gateway.pid.json");
|
|
111704
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join78(STATE_DIR, "gateway-session.json");
|
|
111705
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join78(STATE_DIR, "clean-shutdown.json");
|
|
110851
111706
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
110852
111707
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
110853
111708
|
var activeBootCard = null;
|
|
@@ -110877,7 +111732,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
110877
111732
|
bot: botApi,
|
|
110878
111733
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
110879
111734
|
`),
|
|
110880
|
-
persistPath:
|
|
111735
|
+
persistPath: join78(stateDir, "issues-card.json")
|
|
110881
111736
|
});
|
|
110882
111737
|
activeIssuesWatcher = startIssuesWatcher({
|
|
110883
111738
|
stateDir,
|
|
@@ -111082,7 +111937,7 @@ if (isGatewayMain)
|
|
|
111082
111937
|
var inboundSpool;
|
|
111083
111938
|
if (isGatewayMain)
|
|
111084
111939
|
inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
111085
|
-
path:
|
|
111940
|
+
path: join78(STATE_DIR, "inbound-spool.jsonl"),
|
|
111086
111941
|
fs: {
|
|
111087
111942
|
appendFileSync: (p, d) => appendStateFileSync(p, d),
|
|
111088
111943
|
readFileSync: (p) => readFileSync67(p, "utf8"),
|
|
@@ -111153,7 +112008,7 @@ async function maybeRedeliverUndeliveredAnswer() {
|
|
|
111153
112008
|
let transcriptText;
|
|
111154
112009
|
try {
|
|
111155
112010
|
const projectsDir = getProjectsDirForCwd();
|
|
111156
|
-
const path3 =
|
|
112011
|
+
const path3 = join78(projectsDir, `${sessionId}.jsonl`);
|
|
111157
112012
|
if (!existsSync65(path3)) {
|
|
111158
112013
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path3}); skipping
|
|
111159
112014
|
`);
|
|
@@ -111328,8 +112183,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
111328
112183
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
111329
112184
|
isShuttingDown: () => shuttingDown,
|
|
111330
112185
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
111331
|
-
crashLogPath:
|
|
111332
|
-
markerPath:
|
|
112186
|
+
crashLogPath: join78(STATE_DIR, "bridge-crash.log"),
|
|
112187
|
+
markerPath: join78(STATE_DIR, "bridge-dead-escalation.json"),
|
|
111333
112188
|
log: (line) => process.stderr.write(`${line}
|
|
111334
112189
|
`),
|
|
111335
112190
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -111436,8 +112291,8 @@ if (isGatewayMain)
|
|
|
111436
112291
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
111437
112292
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
111438
112293
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
111439
|
-
configSnapshotPath:
|
|
111440
|
-
bootCardStatePath:
|
|
112294
|
+
configSnapshotPath: join78(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
112295
|
+
bootCardStatePath: join78(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
111441
112296
|
floodStatePath: FLOOD_STATE_PATH,
|
|
111442
112297
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
111443
112298
|
}, ackMsgId).then((handle) => {
|
|
@@ -112071,7 +112926,7 @@ if (isGatewayMain)
|
|
|
112071
112926
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
112072
112927
|
if (Number.isInteger(receiverUid))
|
|
112073
112928
|
allowedUids.push(receiverUid);
|
|
112074
|
-
const socketPath =
|
|
112929
|
+
const socketPath = join78(STATE_DIR, "webhook.sock");
|
|
112075
112930
|
const webhookInject = (agentName3, inbound) => {
|
|
112076
112931
|
const msg = inbound;
|
|
112077
112932
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -112323,9 +113178,9 @@ function redactOutboundText(text5, site) {
|
|
|
112323
113178
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
112324
113179
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
112325
113180
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
112326
|
-
persistPath:
|
|
113181
|
+
persistPath: join78(STATE_DIR, "voice-ondemand.json")
|
|
112327
113182
|
});
|
|
112328
|
-
var VOICE_CACHE_DIR =
|
|
113183
|
+
var VOICE_CACHE_DIR = join78(STATE_DIR, "voice-cache");
|
|
112329
113184
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
112330
113185
|
runJob: async (job) => {
|
|
112331
113186
|
const sidecarToken = await materializeSidecarToken2();
|
|
@@ -112733,7 +113588,7 @@ async function executeSendGif(rawArgs) {
|
|
|
112733
113588
|
};
|
|
112734
113589
|
}
|
|
112735
113590
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
112736
|
-
const accountPath =
|
|
113591
|
+
const accountPath = join78(STATE_DIR, "telegraph-account.json");
|
|
112737
113592
|
let account = null;
|
|
112738
113593
|
try {
|
|
112739
113594
|
if (existsSync65(accountPath)) {
|
|
@@ -114283,7 +115138,7 @@ function restartMarkerPath() {
|
|
|
114283
115138
|
const agentDir = resolveAgentDirFromEnv();
|
|
114284
115139
|
if (!agentDir)
|
|
114285
115140
|
return null;
|
|
114286
|
-
return
|
|
115141
|
+
return join78(agentDir, "restart-pending.json");
|
|
114287
115142
|
}
|
|
114288
115143
|
function writeRestartMarker(marker) {
|
|
114289
115144
|
const p = restartMarkerPath();
|
|
@@ -114475,7 +115330,7 @@ function _resetDockerReachableCache() {
|
|
|
114475
115330
|
}
|
|
114476
115331
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
114477
115332
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
114478
|
-
const logPath =
|
|
115333
|
+
const logPath = join78(STATE_DIR, "detached-spawn.log");
|
|
114479
115334
|
let outFd = null;
|
|
114480
115335
|
try {
|
|
114481
115336
|
mkdirStateSync2(STATE_DIR, { recursive: true });
|
|
@@ -114768,7 +115623,7 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
114768
115623
|
}
|
|
114769
115624
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
114770
115625
|
try {
|
|
114771
|
-
const auditPath =
|
|
115626
|
+
const auditPath = join78(homedir22(), ".switchroom", "vault-audit.log");
|
|
114772
115627
|
if (!existsSync65(auditPath))
|
|
114773
115628
|
return [];
|
|
114774
115629
|
const raw = readFileSync67(auditPath, "utf8");
|
|
@@ -114822,7 +115677,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
114822
115677
|
try {
|
|
114823
115678
|
const agentDir = resolveAgentDirFromEnv();
|
|
114824
115679
|
if (agentDir) {
|
|
114825
|
-
const raw = readFileSync67(
|
|
115680
|
+
const raw = readFileSync67(join78(agentDir, ".claude", ".claude.json"), "utf8");
|
|
114826
115681
|
claudeJson = JSON.parse(raw);
|
|
114827
115682
|
}
|
|
114828
115683
|
} catch {}
|
|
@@ -114951,7 +115806,7 @@ function buildModelDeps(restartCtx) {
|
|
|
114951
115806
|
try {
|
|
114952
115807
|
const agentDir = resolveAgentDirFromEnv();
|
|
114953
115808
|
if (agentDir) {
|
|
114954
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
115809
|
+
const local = await fetchQuota2({ claudeConfigDir: join78(agentDir, ".claude") });
|
|
114955
115810
|
if (local.ok)
|
|
114956
115811
|
return formatQuotaLine2(local.data);
|
|
114957
115812
|
}
|
|
@@ -115198,7 +116053,7 @@ function effortMenuReplyMarkup(reply) {
|
|
|
115198
116053
|
function flushAgentHandoff(agentDir) {
|
|
115199
116054
|
let removed = 0;
|
|
115200
116055
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
115201
|
-
const p =
|
|
116056
|
+
const p = join78(agentDir, fname);
|
|
115202
116057
|
try {
|
|
115203
116058
|
if (existsSync65(p)) {
|
|
115204
116059
|
unlinkSync33(p);
|
|
@@ -115256,7 +116111,7 @@ async function handleNewCommand(ctx) {
|
|
|
115256
116111
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
115257
116112
|
if (agentDir != null) {
|
|
115258
116113
|
try {
|
|
115259
|
-
writeFileSync55(
|
|
116114
|
+
writeFileSync55(join78(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
115260
116115
|
`, "utf8");
|
|
115261
116116
|
} catch (err) {
|
|
115262
116117
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -115373,7 +116228,7 @@ var lockoutOps = {
|
|
|
115373
116228
|
writeFileSync: (p, data, opts) => writeStateFileSync2(p, data, opts),
|
|
115374
116229
|
existsSync: (p) => existsSync65(p),
|
|
115375
116230
|
mkdirSync: (p, opts) => mkdirStateSync2(p, opts),
|
|
115376
|
-
joinPath: (...parts) =>
|
|
116231
|
+
joinPath: (...parts) => join78(...parts)
|
|
115377
116232
|
};
|
|
115378
116233
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
115379
116234
|
function isAuthBrokerSocketReachable() {
|
|
@@ -115634,7 +116489,7 @@ async function runCreditWatch() {
|
|
|
115634
116489
|
if (!agentDir)
|
|
115635
116490
|
return;
|
|
115636
116491
|
const agentName3 = getMyAgentName();
|
|
115637
|
-
const claudeConfigDir =
|
|
116492
|
+
const claudeConfigDir = join78(agentDir, ".claude");
|
|
115638
116493
|
const stateDir = STATE_DIR;
|
|
115639
116494
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
115640
116495
|
const prev = loadCreditState(stateDir);
|
|
@@ -117404,7 +118259,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
117404
118259
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
117405
118260
|
return;
|
|
117406
118261
|
}
|
|
117407
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
118262
|
+
const result = await fetchQuota2({ claudeConfigDir: join78(agentDir, ".claude") });
|
|
117408
118263
|
if (!result.ok) {
|
|
117409
118264
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
117410
118265
|
return;
|
|
@@ -118670,7 +119525,7 @@ async function startGateway() {
|
|
|
118670
119525
|
return;
|
|
118671
119526
|
}
|
|
118672
119527
|
})();
|
|
118673
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
119528
|
+
const resolvedAgentDirForBootCard = agentDir ?? join78(homedir22(), ".switchroom", "agents", agentSlug);
|
|
118674
119529
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
118675
119530
|
agentName: agentDisplayName,
|
|
118676
119531
|
agentSlug,
|
|
@@ -118684,8 +119539,8 @@ async function startGateway() {
|
|
|
118684
119539
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
118685
119540
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
118686
119541
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
118687
|
-
configSnapshotPath:
|
|
118688
|
-
bootCardStatePath:
|
|
119542
|
+
configSnapshotPath: join78(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
119543
|
+
bootCardStatePath: join78(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
118689
119544
|
floodStatePath: FLOOD_STATE_PATH,
|
|
118690
119545
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
118691
119546
|
}, ackMsgId);
|
|
@@ -118718,7 +119573,7 @@ async function startGateway() {
|
|
|
118718
119573
|
if (smAgentDir) {
|
|
118719
119574
|
const resolutionTimeoutMs = resolveSessionModelResolutionTimeoutMs(process.env.SWITCHROOM_SESSION_MODEL_RESOLUTION_TIMEOUT_MS);
|
|
118720
119575
|
const resolved = await waitForSessionModelResolution({
|
|
118721
|
-
barrierExists: () => existsSync65(
|
|
119576
|
+
barrierExists: () => existsSync65(join78(smAgentDir, ".session-model-resolved")),
|
|
118722
119577
|
timeoutMs: resolutionTimeoutMs
|
|
118723
119578
|
});
|
|
118724
119579
|
if (!resolved) {
|
|
@@ -118726,7 +119581,7 @@ async function startGateway() {
|
|
|
118726
119581
|
process.stderr.write(`telegram gateway: gw /model relaunch UNRESOLVED agent=${getMyAgentName()} target=${target} (barrier timeout after ${resolutionTimeoutMs}ms)
|
|
118727
119582
|
`);
|
|
118728
119583
|
} else {
|
|
118729
|
-
const activePath =
|
|
119584
|
+
const activePath = join78(smAgentDir, ".active-session-model");
|
|
118730
119585
|
if (existsSync65(activePath)) {
|
|
118731
119586
|
try {
|
|
118732
119587
|
const launched = readFileSync67(activePath, "utf8").trim();
|
|
@@ -118762,12 +119617,12 @@ async function startGateway() {
|
|
|
118762
119617
|
deliverModelSwitchBootNotice({
|
|
118763
119618
|
...modelBootCardDeps,
|
|
118764
119619
|
confirmation,
|
|
118765
|
-
hasSessionModelAlert: existsSync65(
|
|
119620
|
+
hasSessionModelAlert: existsSync65(join78(smAgentDir, ".session-model-alert"))
|
|
118766
119621
|
});
|
|
118767
119622
|
}
|
|
118768
119623
|
} catch {}
|
|
118769
119624
|
}
|
|
118770
|
-
const activeEffortPath =
|
|
119625
|
+
const activeEffortPath = join78(smAgentDir, ".active-session-effort");
|
|
118771
119626
|
if (existsSync65(activeEffortPath)) {
|
|
118772
119627
|
try {
|
|
118773
119628
|
const launchedEffort = readFileSync67(activeEffortPath, "utf8").trim();
|
|
@@ -118775,7 +119630,7 @@ async function startGateway() {
|
|
|
118775
119630
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
118776
119631
|
} catch {}
|
|
118777
119632
|
}
|
|
118778
|
-
const alertPath =
|
|
119633
|
+
const alertPath = join78(smAgentDir, ".session-model-alert");
|
|
118779
119634
|
if (existsSync65(alertPath)) {
|
|
118780
119635
|
let alertText = null;
|
|
118781
119636
|
try {
|
|
@@ -119317,6 +120172,7 @@ async function startGateway() {
|
|
|
119317
120172
|
if (isGatewayMain)
|
|
119318
120173
|
startGateway();
|
|
119319
120174
|
export {
|
|
120175
|
+
resolveAnswerThreadWithLog,
|
|
119320
120176
|
initVaultApprovalPosture,
|
|
119321
120177
|
handleInboundCoalesced,
|
|
119322
120178
|
handleInbound,
|