switchroom 0.18.18 → 0.18.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/switchroom.js +1 -1
- package/dist/host-control/main.js +1 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +175 -220
- package/telegram-plugin/dist/server.js +6 -0
- package/telegram-plugin/format.ts +137 -213
- package/telegram-plugin/gateway/gateway.ts +40 -17
- package/telegram-plugin/gateway/outbound-send-path.ts +9 -7
- package/telegram-plugin/llm-error-present.ts +68 -30
- package/telegram-plugin/stream-reply-handler.ts +5 -14
- package/telegram-plugin/tests/format-consistency.test.ts +68 -53
- package/telegram-plugin/tests/formatting-parse-regression.test.ts +5 -6
- package/telegram-plugin/tests/formatting-torture-set.ts +1 -1
- package/telegram-plugin/tests/gateway-outbound-redact.test.ts +26 -0
- package/telegram-plugin/tests/llm-error-present.test.ts +110 -9
- package/telegram-plugin/tests/outbound-send-path.test.ts +4 -3
- package/telegram-plugin/tests/paragraph-normalizer.test.ts +42 -100
- package/telegram-plugin/tests/stream-reply-handler.test.ts +9 -12
- package/telegram-plugin/tests/telegram-format.test.ts +86 -31
- package/telegram-plugin/tests/turn-flush-safety.test.ts +17 -21
- package/telegram-plugin/turn-flush-safety.ts +4 -3
|
@@ -6824,99 +6824,28 @@ function hardenCardBreaks(text) {
|
|
|
6824
6824
|
}
|
|
6825
6825
|
return restore(pieces.join(""));
|
|
6826
6826
|
}
|
|
6827
|
-
function addParagraphSpacers(text) {
|
|
6828
|
-
if (!text.includes(`
|
|
6829
|
-
|
|
6830
|
-
`))
|
|
6831
|
-
return text;
|
|
6832
|
-
const nonce = Math.random().toString(36).slice(2);
|
|
6833
|
-
const { masked, restore, placeholder } = maskCodeRegions(text, nonce);
|
|
6834
|
-
if (!masked.includes(`
|
|
6835
|
-
|
|
6836
|
-
`))
|
|
6837
|
-
return restore(masked);
|
|
6838
|
-
const spacerLine = PARAGRAPH_SPACER;
|
|
6839
|
-
const isBlankLine = (line) => /^[ \t\r\f\v]*$/.test(line);
|
|
6840
|
-
const asciiTrim = (line) => line.replace(/^[ \t\r\f\v]+|[ \t\r\f\v]+$/g, "");
|
|
6841
|
-
const blockKind = (line) => {
|
|
6842
|
-
if (asciiTrim(line) === spacerLine)
|
|
6843
|
-
return "spacer";
|
|
6844
|
-
if (isFenceOpenLine(line, placeholder))
|
|
6845
|
-
return "fence";
|
|
6846
|
-
if (isListItemLine(line))
|
|
6847
|
-
return "list";
|
|
6848
|
-
if (isTableRowLine(line) || isTableDelimiterLine(line))
|
|
6849
|
-
return "table";
|
|
6850
|
-
if (isBlockquoteLine(line))
|
|
6851
|
-
return "quote";
|
|
6852
|
-
if (isHeadingLine(line))
|
|
6853
|
-
return "heading";
|
|
6854
|
-
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trimStart()))
|
|
6855
|
-
return "divider";
|
|
6856
|
-
return "prose";
|
|
6857
|
-
};
|
|
6858
|
-
const SAME_KIND_TIGHT = new Set([
|
|
6859
|
-
"list",
|
|
6860
|
-
"table",
|
|
6861
|
-
"quote",
|
|
6862
|
-
"fence",
|
|
6863
|
-
"divider"
|
|
6864
|
-
]);
|
|
6865
|
-
const shouldSpaceGap = (above, below) => {
|
|
6866
|
-
const a = blockKind(above);
|
|
6867
|
-
const b = blockKind(below);
|
|
6868
|
-
if (a === "spacer" || b === "spacer")
|
|
6869
|
-
return false;
|
|
6870
|
-
if (a === b && SAME_KIND_TIGHT.has(a))
|
|
6871
|
-
return false;
|
|
6872
|
-
return true;
|
|
6873
|
-
};
|
|
6874
|
-
const lines = masked.split(`
|
|
6875
|
-
`);
|
|
6876
|
-
const out = [];
|
|
6877
|
-
for (let i = 0;i < lines.length; i++) {
|
|
6878
|
-
const line = lines[i];
|
|
6879
|
-
const isBlank = isBlankLine(line);
|
|
6880
|
-
if (isBlank) {
|
|
6881
|
-
const prevEmitted = out.length > 0 ? out[out.length - 1] : null;
|
|
6882
|
-
const prevIsBlank = prevEmitted != null && isBlankLine(prevEmitted);
|
|
6883
|
-
if (!prevIsBlank) {
|
|
6884
|
-
const above = lastNonBlank(out, isBlankLine);
|
|
6885
|
-
const below = nextNonBlank(lines, i + 1, isBlankLine);
|
|
6886
|
-
const alreadySpaced = above != null && asciiTrim(above) === spacerLine || below != null && asciiTrim(below) === spacerLine;
|
|
6887
|
-
if (!alreadySpaced && above != null && below != null && shouldSpaceGap(above, below)) {
|
|
6888
|
-
out.push("");
|
|
6889
|
-
out.push(spacerLine);
|
|
6890
|
-
out.push("");
|
|
6891
|
-
continue;
|
|
6892
|
-
}
|
|
6893
|
-
}
|
|
6894
|
-
}
|
|
6895
|
-
out.push(line);
|
|
6896
|
-
}
|
|
6897
|
-
return restore(out.join(`
|
|
6898
|
-
`));
|
|
6899
|
-
}
|
|
6900
|
-
function lastNonBlank(arr, isBlank) {
|
|
6901
|
-
for (let i = arr.length - 1;i >= 0; i--) {
|
|
6902
|
-
if (!isBlank(arr[i]))
|
|
6903
|
-
return arr[i];
|
|
6904
|
-
}
|
|
6905
|
-
return null;
|
|
6906
|
-
}
|
|
6907
|
-
function nextNonBlank(lines, from, isBlank) {
|
|
6908
|
-
for (let i = from;i < lines.length; i++) {
|
|
6909
|
-
if (!isBlank(lines[i]))
|
|
6910
|
-
return lines[i];
|
|
6911
|
-
}
|
|
6912
|
-
return null;
|
|
6913
|
-
}
|
|
6914
6827
|
function normalizePunctuation(text) {
|
|
6915
6828
|
if (!/[\u2014\u2013\u2022\u00b7]/.test(text))
|
|
6916
6829
|
return text;
|
|
6917
6830
|
const nonce = Math.random().toString(36).slice(2);
|
|
6918
6831
|
const { masked, restore } = maskCodeRegions(text, nonce);
|
|
6919
|
-
|
|
6832
|
+
const linkMasks = [];
|
|
6833
|
+
const LINK_MASK_PH = `\x00RML${nonce}_`;
|
|
6834
|
+
const maskedLinks = masked.replace(/(\]\()([^)\n]*)(\))/g, (_m, open, href, close) => {
|
|
6835
|
+
const idx = linkMasks.length;
|
|
6836
|
+
linkMasks.push(href);
|
|
6837
|
+
return `${open}${LINK_MASK_PH}${idx}\x00${close}`;
|
|
6838
|
+
});
|
|
6839
|
+
const maskedAutolinks = maskedLinks.replace(/(<)([a-zA-Z][a-zA-Z0-9+.-]*:[^>\s]*)(>)/g, (_m, lt, uri, gt) => {
|
|
6840
|
+
const idx = linkMasks.length;
|
|
6841
|
+
linkMasks.push(uri);
|
|
6842
|
+
return `${lt}${LINK_MASK_PH}${idx}\x00${gt}`;
|
|
6843
|
+
});
|
|
6844
|
+
const escNonce = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6845
|
+
const linkRestoreRe = new RegExp(`\x00RML${escNonce}_(\\d+)\x00`, "g");
|
|
6846
|
+
const restoreLinks = (s) => s.replace(linkRestoreRe, (_m, idx) => linkMasks[Number(idx)] ?? _m);
|
|
6847
|
+
let out = maskedAutolinks.replace(/(\S)[ \t][\u2014\u2013][ \t](?=(\S))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2014(?=(\w))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2013(?=\w)/g, "$1-");
|
|
6848
|
+
out = restoreLinks(out);
|
|
6920
6849
|
out = out.split(`
|
|
6921
6850
|
`).map((line) => line.replace(/^([ \t]*)[\u2022\u00b7][ \t]+/, "$1- ")).join(`
|
|
6922
6851
|
`);
|
|
@@ -7109,6 +7038,7 @@ function splitMarkdownChunks(text, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
|
7109
7038
|
}
|
|
7110
7039
|
cut = backOffOpenFence(rest, cut);
|
|
7111
7040
|
cut = backOffTableRow(rest, cut);
|
|
7041
|
+
cut = backOffOpenInline(rest, cut);
|
|
7112
7042
|
if (cut <= 0) {
|
|
7113
7043
|
const sliced = hardSliceToCap(rest, maxLen);
|
|
7114
7044
|
chunks.push(stripBoundarySpacers(sliced[0], "trailing"));
|
|
@@ -7121,11 +7051,10 @@ function splitMarkdownChunks(text, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
|
7121
7051
|
return chunks.map((c) => stripBoundarySpacers(c, "trailing"));
|
|
7122
7052
|
}
|
|
7123
7053
|
function stripBoundarySpacers(chunk, side) {
|
|
7124
|
-
const sp = PARAGRAPH_SPACER;
|
|
7125
7054
|
if (side === "leading") {
|
|
7126
|
-
return chunk.replace(
|
|
7055
|
+
return chunk.replace(/^(?:[ \t]*\n)+/, "");
|
|
7127
7056
|
}
|
|
7128
|
-
return chunk.replace(
|
|
7057
|
+
return chunk.replace(/(?:\n[ \t]*)+$/, "");
|
|
7129
7058
|
}
|
|
7130
7059
|
function backOffOpenFence(text, cut) {
|
|
7131
7060
|
if (cut <= 0 || cut >= text.length)
|
|
@@ -7154,7 +7083,38 @@ function backOffTableRow(text, cut) {
|
|
|
7154
7083
|
}
|
|
7155
7084
|
return cut;
|
|
7156
7085
|
}
|
|
7157
|
-
|
|
7086
|
+
function backOffOpenInline(text, cut) {
|
|
7087
|
+
if (cut <= 0 || cut >= text.length)
|
|
7088
|
+
return cut;
|
|
7089
|
+
let earliest = cut;
|
|
7090
|
+
for (const re of INLINE_SPAN_PATTERNS) {
|
|
7091
|
+
re.lastIndex = 0;
|
|
7092
|
+
let m;
|
|
7093
|
+
while ((m = re.exec(text)) !== null) {
|
|
7094
|
+
const start = m.index;
|
|
7095
|
+
const end = start + m[0].length;
|
|
7096
|
+
if (start < cut && cut < end && start < earliest)
|
|
7097
|
+
earliest = start;
|
|
7098
|
+
if (start >= cut)
|
|
7099
|
+
break;
|
|
7100
|
+
if (re.lastIndex === start)
|
|
7101
|
+
re.lastIndex = start + 1;
|
|
7102
|
+
}
|
|
7103
|
+
}
|
|
7104
|
+
return earliest;
|
|
7105
|
+
}
|
|
7106
|
+
var RICH_MESSAGE_MAX_CHARS = 32768, INLINE_SPAN_PATTERNS;
|
|
7107
|
+
var init_format = __esm(() => {
|
|
7108
|
+
INLINE_SPAN_PATTERNS = [
|
|
7109
|
+
/`[^`\n]+`/g,
|
|
7110
|
+
/\*\*\*[^*\n]+\*\*\*/g,
|
|
7111
|
+
/___[^_\n]+___/g,
|
|
7112
|
+
/\*\*[^*\n]+\*\*/g,
|
|
7113
|
+
/__[^_\n]+__/g,
|
|
7114
|
+
/(?<![\w*])_[^_\n]+_(?![\w*])/g,
|
|
7115
|
+
/\[[^\]\n]*\]\([^)\n]*\)/g
|
|
7116
|
+
];
|
|
7117
|
+
});
|
|
7158
7118
|
|
|
7159
7119
|
// text-voice-scrub.ts
|
|
7160
7120
|
function enabled() {
|
|
@@ -7339,6 +7299,7 @@ function cleanWorkerResultParagraph(s) {
|
|
|
7339
7299
|
return kept.join(" ").replace(/\s+/g, " ").trim();
|
|
7340
7300
|
}
|
|
7341
7301
|
var init_card_format = __esm(() => {
|
|
7302
|
+
init_format();
|
|
7342
7303
|
init_text_voice_scrub();
|
|
7343
7304
|
});
|
|
7344
7305
|
|
|
@@ -7480,6 +7441,7 @@ function ttlMsFromToken(token) {
|
|
|
7480
7441
|
}
|
|
7481
7442
|
var import_grammy3;
|
|
7482
7443
|
var init_approval_card = __esm(() => {
|
|
7444
|
+
init_format();
|
|
7483
7445
|
import_grammy3 = __toESM(require_mod2(), 1);
|
|
7484
7446
|
});
|
|
7485
7447
|
|
|
@@ -36885,6 +36847,7 @@ function parseConfigApprovalCallback(data) {
|
|
|
36885
36847
|
var pending, TELEGRAM_SENDMESSAGE_LIMIT2 = 32768, RENDERED_BODY_CAP2 = 32000, REASON_MAX_CHARS = 500, REASON_ELLIPSIS = "\u2026", DIFF_SENTINEL = `
|
|
36886
36848
|
[\u2026 diff continues, see attached file]`;
|
|
36887
36849
|
var init_config_approval_handler = __esm(() => {
|
|
36850
|
+
init_format();
|
|
36888
36851
|
pending = new Map;
|
|
36889
36852
|
});
|
|
36890
36853
|
|
|
@@ -37009,6 +36972,7 @@ ${detail}`)));
|
|
|
37009
36972
|
});
|
|
37010
36973
|
}
|
|
37011
36974
|
var init_approvals_commands = __esm(() => {
|
|
36975
|
+
init_format();
|
|
37012
36976
|
init_rich_send();
|
|
37013
36977
|
init_client3();
|
|
37014
36978
|
});
|
|
@@ -39778,6 +39742,7 @@ class DeferredDoneReactions {
|
|
|
39778
39742
|
init_card_format();
|
|
39779
39743
|
|
|
39780
39744
|
// status-no-truncate.ts
|
|
39745
|
+
init_format();
|
|
39781
39746
|
var STATUS_ROLLING_LINES = 5;
|
|
39782
39747
|
var STATUS_LINE_MAX = 200;
|
|
39783
39748
|
var STATUS_CARD_CHAR_BUDGET = RICH_MESSAGE_MAX_CHARS;
|
|
@@ -41247,6 +41212,7 @@ var import_grammy2 = __toESM(require_mod2(), 1);
|
|
|
41247
41212
|
var import_runner = __toESM(require_mod4(), 1);
|
|
41248
41213
|
import { AsyncLocalStorage } from "async_hooks";
|
|
41249
41214
|
init_flood_circuit_breaker();
|
|
41215
|
+
init_format();
|
|
41250
41216
|
|
|
41251
41217
|
// shared/gw-trace-gate.ts
|
|
41252
41218
|
function computeGwTraceVerbose(flag) {
|
|
@@ -41281,6 +41247,7 @@ function escapeHtmlForTg(text) {
|
|
|
41281
41247
|
|
|
41282
41248
|
// gateway/vault-request-access-card.ts
|
|
41283
41249
|
init_approval_card();
|
|
41250
|
+
init_format();
|
|
41284
41251
|
function renderVaultRequestAccessCard(req) {
|
|
41285
41252
|
const lines = [];
|
|
41286
41253
|
const scopeLabel = req.scope === "write" ? "write" : "read";
|
|
@@ -54817,6 +54784,7 @@ function parse2(markdown) {
|
|
|
54817
54784
|
}
|
|
54818
54785
|
|
|
54819
54786
|
// render/render.ts
|
|
54787
|
+
init_format();
|
|
54820
54788
|
function renderInline(node2, ctx = {}) {
|
|
54821
54789
|
switch (node2.type) {
|
|
54822
54790
|
case "plain":
|
|
@@ -55022,6 +54990,7 @@ function renderSafe(doc, source, maxLen = RICH_MESSAGE_MAX_CHARS) {
|
|
|
55022
54990
|
}
|
|
55023
54991
|
|
|
55024
54992
|
// render/rich-render.ts
|
|
54993
|
+
init_format();
|
|
55025
54994
|
var PLAIN_TEXT_MAX_CHARS = 4096;
|
|
55026
54995
|
function parseRichRenderEnabled(raw) {
|
|
55027
54996
|
if (raw == null)
|
|
@@ -55307,6 +55276,10 @@ function handlePtyPartialPure(text4, state, deps) {
|
|
|
55307
55276
|
stream.update(text4).catch(() => {});
|
|
55308
55277
|
return created ? "update-new" : "update-existing";
|
|
55309
55278
|
}
|
|
55279
|
+
|
|
55280
|
+
// stream-reply-handler.ts
|
|
55281
|
+
init_format();
|
|
55282
|
+
|
|
55310
55283
|
// chat-lock.ts
|
|
55311
55284
|
function createChatLock() {
|
|
55312
55285
|
const chains = new Map;
|
|
@@ -56401,6 +56374,7 @@ var import_runner2 = __toESM(require_mod4(), 1);
|
|
|
56401
56374
|
import { createHash as createHash2 } from "crypto";
|
|
56402
56375
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
56403
56376
|
init_flood_circuit_breaker();
|
|
56377
|
+
init_format();
|
|
56404
56378
|
var tgPostTagStore2 = new AsyncLocalStorage2;
|
|
56405
56379
|
function _getTgPostTags() {
|
|
56406
56380
|
return tgPostTagStore2.getStore();
|
|
@@ -61497,6 +61471,7 @@ function decideOverPing(input) {
|
|
|
61497
61471
|
}
|
|
61498
61472
|
|
|
61499
61473
|
// silent-reply-anchor.ts
|
|
61474
|
+
init_format();
|
|
61500
61475
|
var TELEGRAM_MSG_CAP = RICH_MESSAGE_MAX_CHARS;
|
|
61501
61476
|
function enabled3() {
|
|
61502
61477
|
const v = process.env.SWITCHROOM_DISABLE_SILENT_REPLY_AUTOEDIT;
|
|
@@ -62634,6 +62609,7 @@ function safeResolvePersonName(directory, opts, rawFallback) {
|
|
|
62634
62609
|
}
|
|
62635
62610
|
|
|
62636
62611
|
// gateway/auth-command.ts
|
|
62612
|
+
init_format();
|
|
62637
62613
|
init_auth_snapshot_format();
|
|
62638
62614
|
init_demo_mask();
|
|
62639
62615
|
var AUTH_RM_CONFIRM_TTL_MS = 60000;
|
|
@@ -64783,6 +64759,10 @@ function autoClassifyMidTurnInbound(i) {
|
|
|
64783
64759
|
return { decision: "queue", reason: "cross_topic", sameTopic: false };
|
|
64784
64760
|
return recent && i.msSinceLastAgentOutput <= i.topicSteerWindowMs ? { decision: "steer", reason: "same_topic_recent", sameTopic: true } : { decision: "queue", reason: "same_topic_stale", sameTopic: true };
|
|
64785
64761
|
}
|
|
64762
|
+
|
|
64763
|
+
// operator-events.ts
|
|
64764
|
+
init_format();
|
|
64765
|
+
|
|
64786
64766
|
// raw-error-scrub.ts
|
|
64787
64767
|
function stripRawErrorBytes(raw) {
|
|
64788
64768
|
if (typeof raw !== "string" || raw.length === 0)
|
|
@@ -65352,7 +65332,9 @@ function renderThrottleEscalationNotice(opts) {
|
|
|
65352
65332
|
return `${head}
|
|
65353
65333
|
${tail}`;
|
|
65354
65334
|
}
|
|
65335
|
+
|
|
65355
65336
|
// operator-events.ts
|
|
65337
|
+
init_format();
|
|
65356
65338
|
function classifyClaudeError(raw) {
|
|
65357
65339
|
try {
|
|
65358
65340
|
return classifyInner(raw);
|
|
@@ -65539,6 +65521,26 @@ function formatRelativeTail(deltaMs) {
|
|
|
65539
65521
|
const remH = hours % 24;
|
|
65540
65522
|
return remH > 0 ? `~in ${days}d ${remH}h` : `~in ${days}d`;
|
|
65541
65523
|
}
|
|
65524
|
+
function formatResetClock(resetAt, tz) {
|
|
65525
|
+
if (resetAt == null)
|
|
65526
|
+
return "";
|
|
65527
|
+
const ms = resetAt.getTime();
|
|
65528
|
+
if (!Number.isFinite(ms))
|
|
65529
|
+
return "";
|
|
65530
|
+
return `${fmtLocalClock(ms, tz)} ${tzAbbrev(ms, tz)}`;
|
|
65531
|
+
}
|
|
65532
|
+
function buildRecommendation(parsed, tz) {
|
|
65533
|
+
switch (parsed.kind) {
|
|
65534
|
+
case "auth":
|
|
65535
|
+
return "\u2192 Re-authenticate this account to continue.";
|
|
65536
|
+
case "quota_wall": {
|
|
65537
|
+
const reset2 = formatResetClock(parsed.resetAt, tz);
|
|
65538
|
+
return reset2 ? `\u2192 Switch to another account, or wait for the quota to reset at ${reset2}.` : "\u2192 Switch to another account, or wait for the quota to reset.";
|
|
65539
|
+
}
|
|
65540
|
+
default:
|
|
65541
|
+
return;
|
|
65542
|
+
}
|
|
65543
|
+
}
|
|
65542
65544
|
function renderLlmError(parsed, agent, tz, now = new Date) {
|
|
65543
65545
|
const safeAgent = escapeAgent(agent);
|
|
65544
65546
|
const emoji = kindEmoji(parsed.kind);
|
|
@@ -65548,32 +65550,18 @@ function renderLlmError(parsed, agent, tz, now = new Date) {
|
|
|
65548
65550
|
lines.push(`_${resetLine}_`);
|
|
65549
65551
|
if (parsed.model)
|
|
65550
65552
|
lines.push(`_model: ${escapeAgent(parsed.model)}_`);
|
|
65551
|
-
const
|
|
65552
|
-
|
|
65553
|
-
|
|
65554
|
-
|
|
65555
|
-
|
|
65556
|
-
|
|
65557
|
-
|
|
65558
|
-
|
|
65559
|
-
|
|
65560
|
-
|
|
65561
|
-
|
|
65562
|
-
|
|
65563
|
-
]
|
|
65564
|
-
}
|
|
65565
|
-
};
|
|
65566
|
-
case "quota_wall":
|
|
65567
|
-
return {
|
|
65568
|
-
text: text4,
|
|
65569
|
-
keyboard: {
|
|
65570
|
-
inline_keyboard: [
|
|
65571
|
-
[{ text: "\u23f3 Wait", callback_data: `op:dismiss:${encodeURIComponent(agent)}` }]
|
|
65572
|
-
]
|
|
65573
|
-
}
|
|
65574
|
-
};
|
|
65575
|
-
default:
|
|
65576
|
-
return { text: text4 };
|
|
65553
|
+
const recommendation2 = buildRecommendation(parsed, tz);
|
|
65554
|
+
if (recommendation2)
|
|
65555
|
+
lines.push(recommendation2);
|
|
65556
|
+
return { text: lines.join(`
|
|
65557
|
+
`) };
|
|
65558
|
+
}
|
|
65559
|
+
function renderLlmErrorSafe(parsed, agent, tz, now = new Date) {
|
|
65560
|
+
try {
|
|
65561
|
+
return renderLlmError(parsed, agent, tz, now);
|
|
65562
|
+
} catch {
|
|
65563
|
+
const safeAgent = escapeAgent(agent);
|
|
65564
|
+
return { text: `${kindEmoji(parsed.kind)} ${parsed.coreText} (**${safeAgent}**)` };
|
|
65577
65565
|
}
|
|
65578
65566
|
}
|
|
65579
65567
|
function kindEmoji(kind) {
|
|
@@ -66535,100 +66523,28 @@ function hardenCardBreaks2(text4) {
|
|
|
66535
66523
|
}
|
|
66536
66524
|
return restore2(pieces.join(""));
|
|
66537
66525
|
}
|
|
66538
|
-
var PARAGRAPH_SPACER2 = "\u00a0";
|
|
66539
|
-
function addParagraphSpacers2(text4) {
|
|
66540
|
-
if (!text4.includes(`
|
|
66541
|
-
|
|
66542
|
-
`))
|
|
66543
|
-
return text4;
|
|
66544
|
-
const nonce = Math.random().toString(36).slice(2);
|
|
66545
|
-
const { masked, restore: restore2, placeholder } = maskCodeRegions2(text4, nonce);
|
|
66546
|
-
if (!masked.includes(`
|
|
66547
|
-
|
|
66548
|
-
`))
|
|
66549
|
-
return restore2(masked);
|
|
66550
|
-
const spacerLine = PARAGRAPH_SPACER2;
|
|
66551
|
-
const isBlankLine = (line) => /^[ \t\r\f\v]*$/.test(line);
|
|
66552
|
-
const asciiTrim = (line) => line.replace(/^[ \t\r\f\v]+|[ \t\r\f\v]+$/g, "");
|
|
66553
|
-
const blockKind = (line) => {
|
|
66554
|
-
if (asciiTrim(line) === spacerLine)
|
|
66555
|
-
return "spacer";
|
|
66556
|
-
if (isFenceOpenLine2(line, placeholder))
|
|
66557
|
-
return "fence";
|
|
66558
|
-
if (isListItemLine2(line))
|
|
66559
|
-
return "list";
|
|
66560
|
-
if (isTableRowLine2(line) || isTableDelimiterLine2(line))
|
|
66561
|
-
return "table";
|
|
66562
|
-
if (isBlockquoteLine2(line))
|
|
66563
|
-
return "quote";
|
|
66564
|
-
if (isHeadingLine2(line))
|
|
66565
|
-
return "heading";
|
|
66566
|
-
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trimStart()))
|
|
66567
|
-
return "divider";
|
|
66568
|
-
return "prose";
|
|
66569
|
-
};
|
|
66570
|
-
const SAME_KIND_TIGHT = new Set([
|
|
66571
|
-
"list",
|
|
66572
|
-
"table",
|
|
66573
|
-
"quote",
|
|
66574
|
-
"fence",
|
|
66575
|
-
"divider"
|
|
66576
|
-
]);
|
|
66577
|
-
const shouldSpaceGap = (above, below) => {
|
|
66578
|
-
const a = blockKind(above);
|
|
66579
|
-
const b = blockKind(below);
|
|
66580
|
-
if (a === "spacer" || b === "spacer")
|
|
66581
|
-
return false;
|
|
66582
|
-
if (a === b && SAME_KIND_TIGHT.has(a))
|
|
66583
|
-
return false;
|
|
66584
|
-
return true;
|
|
66585
|
-
};
|
|
66586
|
-
const lines = masked.split(`
|
|
66587
|
-
`);
|
|
66588
|
-
const out = [];
|
|
66589
|
-
for (let i = 0;i < lines.length; i++) {
|
|
66590
|
-
const line = lines[i];
|
|
66591
|
-
const isBlank = isBlankLine(line);
|
|
66592
|
-
if (isBlank) {
|
|
66593
|
-
const prevEmitted = out.length > 0 ? out[out.length - 1] : null;
|
|
66594
|
-
const prevIsBlank = prevEmitted != null && isBlankLine(prevEmitted);
|
|
66595
|
-
if (!prevIsBlank) {
|
|
66596
|
-
const above = lastNonBlank2(out, isBlankLine);
|
|
66597
|
-
const below = nextNonBlank2(lines, i + 1, isBlankLine);
|
|
66598
|
-
const alreadySpaced = above != null && asciiTrim(above) === spacerLine || below != null && asciiTrim(below) === spacerLine;
|
|
66599
|
-
if (!alreadySpaced && above != null && below != null && shouldSpaceGap(above, below)) {
|
|
66600
|
-
out.push("");
|
|
66601
|
-
out.push(spacerLine);
|
|
66602
|
-
out.push("");
|
|
66603
|
-
continue;
|
|
66604
|
-
}
|
|
66605
|
-
}
|
|
66606
|
-
}
|
|
66607
|
-
out.push(line);
|
|
66608
|
-
}
|
|
66609
|
-
return restore2(out.join(`
|
|
66610
|
-
`));
|
|
66611
|
-
}
|
|
66612
|
-
function lastNonBlank2(arr, isBlank) {
|
|
66613
|
-
for (let i = arr.length - 1;i >= 0; i--) {
|
|
66614
|
-
if (!isBlank(arr[i]))
|
|
66615
|
-
return arr[i];
|
|
66616
|
-
}
|
|
66617
|
-
return null;
|
|
66618
|
-
}
|
|
66619
|
-
function nextNonBlank2(lines, from, isBlank) {
|
|
66620
|
-
for (let i = from;i < lines.length; i++) {
|
|
66621
|
-
if (!isBlank(lines[i]))
|
|
66622
|
-
return lines[i];
|
|
66623
|
-
}
|
|
66624
|
-
return null;
|
|
66625
|
-
}
|
|
66626
66526
|
function normalizePunctuation2(text4) {
|
|
66627
66527
|
if (!/[\u2014\u2013\u2022\u00b7]/.test(text4))
|
|
66628
66528
|
return text4;
|
|
66629
66529
|
const nonce = Math.random().toString(36).slice(2);
|
|
66630
66530
|
const { masked, restore: restore2 } = maskCodeRegions2(text4, nonce);
|
|
66631
|
-
|
|
66531
|
+
const linkMasks = [];
|
|
66532
|
+
const LINK_MASK_PH = `\x00RML${nonce}_`;
|
|
66533
|
+
const maskedLinks = masked.replace(/(\]\()([^)\n]*)(\))/g, (_m, open, href, close) => {
|
|
66534
|
+
const idx = linkMasks.length;
|
|
66535
|
+
linkMasks.push(href);
|
|
66536
|
+
return `${open}${LINK_MASK_PH}${idx}\x00${close}`;
|
|
66537
|
+
});
|
|
66538
|
+
const maskedAutolinks = maskedLinks.replace(/(<)([a-zA-Z][a-zA-Z0-9+.-]*:[^>\s]*)(>)/g, (_m, lt, uri, gt) => {
|
|
66539
|
+
const idx = linkMasks.length;
|
|
66540
|
+
linkMasks.push(uri);
|
|
66541
|
+
return `${lt}${LINK_MASK_PH}${idx}\x00${gt}`;
|
|
66542
|
+
});
|
|
66543
|
+
const escNonce = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
66544
|
+
const linkRestoreRe = new RegExp(`\x00RML${escNonce}_(\\d+)\x00`, "g");
|
|
66545
|
+
const restoreLinks = (s) => s.replace(linkRestoreRe, (_m, idx) => linkMasks[Number(idx)] ?? _m);
|
|
66546
|
+
let out = maskedAutolinks.replace(/(\S)[ \t][\u2014\u2013][ \t](?=(\S))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2014(?=(\w))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2013(?=\w)/g, "$1-");
|
|
66547
|
+
out = restoreLinks(out);
|
|
66632
66548
|
out = out.split(`
|
|
66633
66549
|
`).map((line) => line.replace(/^([ \t]*)[\u2022\u00b7][ \t]+/, "$1- ")).join(`
|
|
66634
66550
|
`);
|
|
@@ -66821,6 +66737,7 @@ function splitMarkdownChunks2(text4, maxLen = RICH_MESSAGE_MAX_CHARS2) {
|
|
|
66821
66737
|
}
|
|
66822
66738
|
cut = backOffOpenFence2(rest, cut);
|
|
66823
66739
|
cut = backOffTableRow2(rest, cut);
|
|
66740
|
+
cut = backOffOpenInline2(rest, cut);
|
|
66824
66741
|
if (cut <= 0) {
|
|
66825
66742
|
const sliced = hardSliceToCap2(rest, maxLen);
|
|
66826
66743
|
chunks.push(stripBoundarySpacers2(sliced[0], "trailing"));
|
|
@@ -66833,11 +66750,10 @@ function splitMarkdownChunks2(text4, maxLen = RICH_MESSAGE_MAX_CHARS2) {
|
|
|
66833
66750
|
return chunks.map((c) => stripBoundarySpacers2(c, "trailing"));
|
|
66834
66751
|
}
|
|
66835
66752
|
function stripBoundarySpacers2(chunk2, side) {
|
|
66836
|
-
const sp = PARAGRAPH_SPACER2;
|
|
66837
66753
|
if (side === "leading") {
|
|
66838
|
-
return chunk2.replace(
|
|
66754
|
+
return chunk2.replace(/^(?:[ \t]*\n)+/, "");
|
|
66839
66755
|
}
|
|
66840
|
-
return chunk2.replace(
|
|
66756
|
+
return chunk2.replace(/(?:\n[ \t]*)+$/, "");
|
|
66841
66757
|
}
|
|
66842
66758
|
function backOffOpenFence2(text4, cut) {
|
|
66843
66759
|
if (cut <= 0 || cut >= text4.length)
|
|
@@ -66866,6 +66782,35 @@ function backOffTableRow2(text4, cut) {
|
|
|
66866
66782
|
}
|
|
66867
66783
|
return cut;
|
|
66868
66784
|
}
|
|
66785
|
+
var INLINE_SPAN_PATTERNS2 = [
|
|
66786
|
+
/`[^`\n]+`/g,
|
|
66787
|
+
/\*\*\*[^*\n]+\*\*\*/g,
|
|
66788
|
+
/___[^_\n]+___/g,
|
|
66789
|
+
/\*\*[^*\n]+\*\*/g,
|
|
66790
|
+
/__[^_\n]+__/g,
|
|
66791
|
+
/(?<![\w*])_[^_\n]+_(?![\w*])/g,
|
|
66792
|
+
/\[[^\]\n]*\]\([^)\n]*\)/g
|
|
66793
|
+
];
|
|
66794
|
+
function backOffOpenInline2(text4, cut) {
|
|
66795
|
+
if (cut <= 0 || cut >= text4.length)
|
|
66796
|
+
return cut;
|
|
66797
|
+
let earliest = cut;
|
|
66798
|
+
for (const re of INLINE_SPAN_PATTERNS2) {
|
|
66799
|
+
re.lastIndex = 0;
|
|
66800
|
+
let m;
|
|
66801
|
+
while ((m = re.exec(text4)) !== null) {
|
|
66802
|
+
const start = m.index;
|
|
66803
|
+
const end = start + m[0].length;
|
|
66804
|
+
if (start < cut && cut < end && start < earliest)
|
|
66805
|
+
earliest = start;
|
|
66806
|
+
if (start >= cut)
|
|
66807
|
+
break;
|
|
66808
|
+
if (re.lastIndex === start)
|
|
66809
|
+
re.lastIndex = start + 1;
|
|
66810
|
+
}
|
|
66811
|
+
}
|
|
66812
|
+
return earliest;
|
|
66813
|
+
}
|
|
66869
66814
|
|
|
66870
66815
|
// rich-send.ts
|
|
66871
66816
|
var import_grammy8 = __toESM(require_mod2(), 1);
|
|
@@ -66984,6 +66929,7 @@ function scrubVoice2(text4) {
|
|
|
66984
66929
|
}
|
|
66985
66930
|
|
|
66986
66931
|
// gateway/outbound-send-path.ts
|
|
66932
|
+
init_format();
|
|
66987
66933
|
init_text_voice_scrub();
|
|
66988
66934
|
function normalizeOutboundBody(rawText, site, redact2) {
|
|
66989
66935
|
let text4 = normalizeParagraphBreaks(repairEscapedWhitespace(rawText));
|
|
@@ -66997,8 +66943,8 @@ function normalizeOutboundBody(rawText, site, redact2) {
|
|
|
66997
66943
|
}
|
|
66998
66944
|
return { text: text4, voiceReplaced };
|
|
66999
66945
|
}
|
|
67000
|
-
function computeEffectiveText(text4,
|
|
67001
|
-
return
|
|
66946
|
+
function computeEffectiveText(text4, _literalText) {
|
|
66947
|
+
return text4;
|
|
67002
66948
|
}
|
|
67003
66949
|
function computeReplyChunks(args) {
|
|
67004
66950
|
const { effectiveText, literalText, limit, chunkMode } = args;
|
|
@@ -69339,7 +69285,9 @@ function makeTmuxRunner2(tmuxBin) {
|
|
|
69339
69285
|
}
|
|
69340
69286
|
};
|
|
69341
69287
|
}
|
|
69288
|
+
|
|
69342
69289
|
// stream-reply-handler.ts
|
|
69290
|
+
init_format();
|
|
69343
69291
|
function buildAccentHeader(accent) {
|
|
69344
69292
|
switch (accent) {
|
|
69345
69293
|
case "in-progress":
|
|
@@ -73169,6 +73117,7 @@ function recordWebhookEvent(rec, deps = {}) {
|
|
|
73169
73117
|
}
|
|
73170
73118
|
|
|
73171
73119
|
// gateway/ipc-server.ts
|
|
73120
|
+
init_format();
|
|
73172
73121
|
import { renameSync as renameSync11, unlinkSync as unlinkSync14, chmodSync as chmodSync9 } from "fs";
|
|
73173
73122
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
73174
73123
|
var VALID_OPERATOR_KINDS = new Set([
|
|
@@ -73874,6 +73823,7 @@ function validateInput(input) {
|
|
|
73874
73823
|
}
|
|
73875
73824
|
|
|
73876
73825
|
// gateway/drive-write-approval.ts
|
|
73826
|
+
init_format();
|
|
73877
73827
|
var DEFAULT_TTL_MS = 5 * 60 * 1000;
|
|
73878
73828
|
var MAX_TTL_MS = 30 * 60 * 1000;
|
|
73879
73829
|
var MIN_TTL_MS = 30 * 1000;
|
|
@@ -74004,6 +73954,7 @@ function clampTtl(requested, fallback, min, max) {
|
|
|
74004
73954
|
}
|
|
74005
73955
|
|
|
74006
73956
|
// gateway/ms365-write-approval.ts
|
|
73957
|
+
init_format();
|
|
74007
73958
|
function validateMs365Preview(input) {
|
|
74008
73959
|
if (!input || typeof input !== "object")
|
|
74009
73960
|
return null;
|
|
@@ -74182,6 +74133,7 @@ async function handleRequestMs365Approval(client3, msg, deps) {
|
|
|
74182
74133
|
}
|
|
74183
74134
|
|
|
74184
74135
|
// gateway/diff-preview-card.ts
|
|
74136
|
+
init_format();
|
|
74185
74137
|
var import_grammy10 = __toESM(require_mod2(), 1);
|
|
74186
74138
|
var REQUEST_ID_RE = /^[0-9a-f]{32}$/;
|
|
74187
74139
|
var PENDING_FILE_ID_SENTINEL = "pending-create";
|
|
@@ -76340,6 +76292,7 @@ function maybeFireWarmup(ctx) {
|
|
|
76340
76292
|
|
|
76341
76293
|
// gateway/mental-model-propose-card.ts
|
|
76342
76294
|
init_approval_card();
|
|
76295
|
+
init_format();
|
|
76343
76296
|
function renderMentalModelProposeCard(req) {
|
|
76344
76297
|
const lines = [];
|
|
76345
76298
|
lines.push(`\uD83E\uDDE0 **${escapeHtmlForTg(req.agent)}** proposes a mental model`);
|
|
@@ -82081,10 +82034,10 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
82081
82034
|
}
|
|
82082
82035
|
|
|
82083
82036
|
// ../src/build-info.ts
|
|
82084
|
-
var VERSION = "0.18.
|
|
82085
|
-
var COMMIT_SHA = "
|
|
82086
|
-
var COMMIT_DATE = "2026-07-
|
|
82087
|
-
var LATEST_PR =
|
|
82037
|
+
var VERSION = "0.18.19";
|
|
82038
|
+
var COMMIT_SHA = "34c72776";
|
|
82039
|
+
var COMMIT_DATE = "2026-07-13T14:06:25+10:00";
|
|
82040
|
+
var LATEST_PR = 3212;
|
|
82088
82041
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
82089
82042
|
|
|
82090
82043
|
// gateway/boot-version.ts
|
|
@@ -82341,6 +82294,7 @@ async function listGrantsViaBroker2(agent, opts) {
|
|
|
82341
82294
|
}
|
|
82342
82295
|
|
|
82343
82296
|
// gateway/linear-activity.ts
|
|
82297
|
+
init_format();
|
|
82344
82298
|
init_client();
|
|
82345
82299
|
|
|
82346
82300
|
// ../src/linear/oauth-refresh.ts
|
|
@@ -86768,6 +86722,7 @@ var inboundCoalescer = createInboundCoalescer({
|
|
|
86768
86722
|
});
|
|
86769
86723
|
function emitGatewayOperatorEvent(event) {
|
|
86770
86724
|
const { agent, kind } = event;
|
|
86725
|
+
event = { ...event, detail: redactOutboundText(event.detail, "operator_event") };
|
|
86771
86726
|
let throttleEscalation = null;
|
|
86772
86727
|
let escalationFired = false;
|
|
86773
86728
|
let rateLimitedCooldownConsulted = false;
|
|
@@ -86872,9 +86827,9 @@ function emitGatewayOperatorEvent(event) {
|
|
|
86872
86827
|
return;
|
|
86873
86828
|
}
|
|
86874
86829
|
const tz = process.env.SWITCHROOM_TIMEZONE ?? process.env.TZ ?? "UTC";
|
|
86875
|
-
const r =
|
|
86830
|
+
const r = renderLlmErrorSafe(parsed, agent, tz, new Date(now));
|
|
86876
86831
|
renderedText = r.text;
|
|
86877
|
-
renderedKeyboard =
|
|
86832
|
+
renderedKeyboard = undefined;
|
|
86878
86833
|
} else {
|
|
86879
86834
|
try {
|
|
86880
86835
|
const r = renderOperatorEvent(event);
|
|
@@ -90533,7 +90488,7 @@ async function executeEditMessage(args) {
|
|
|
90533
90488
|
editRawText = normalizeParagraphBreaks2(editRawText);
|
|
90534
90489
|
editRawText = redactOutboundText(editRawText, "edit_message");
|
|
90535
90490
|
if (!editLiteralText)
|
|
90536
|
-
editRawText =
|
|
90491
|
+
editRawText = stripExcessBold2(normalizePunctuation2(editRawText));
|
|
90537
90492
|
{
|
|
90538
90493
|
const scrub = scrubVoice2(editRawText);
|
|
90539
90494
|
if (scrub.replaced > 0) {
|
|
@@ -91708,7 +91663,7 @@ function handleSessionEvent(ev) {
|
|
|
91708
91663
|
link_preview_options: { is_disabled: true }
|
|
91709
91664
|
};
|
|
91710
91665
|
const limit = RICH_MESSAGE_MAX_CHARS2;
|
|
91711
|
-
const renderedText =
|
|
91666
|
+
const renderedText = capturedText;
|
|
91712
91667
|
const htmlChunks = splitMarkdownChunks2(renderedText, limit);
|
|
91713
91668
|
const sentIds = [];
|
|
91714
91669
|
try {
|
|
@@ -16994,6 +16994,10 @@ var init_plugin_logger = __esm(() => {
|
|
|
16994
16994
|
DEFAULT_LOG_PATH2 = join2(homedir2(), ".switchroom", "logs", "telegram-plugin.log");
|
|
16995
16995
|
ROTATE_AT_BYTES2 = 50 * 1024 * 1024;
|
|
16996
16996
|
});
|
|
16997
|
+
|
|
16998
|
+
// format.ts
|
|
16999
|
+
var init_format = () => {};
|
|
17000
|
+
|
|
16997
17001
|
// raw-error-scrub.ts
|
|
16998
17002
|
function extractRequestId(raw) {
|
|
16999
17003
|
if (typeof raw !== "string" || raw.length === 0)
|
|
@@ -17079,6 +17083,7 @@ function getNestedObj(obj, key) {
|
|
|
17079
17083
|
}
|
|
17080
17084
|
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS, cooldownMap;
|
|
17081
17085
|
var init_operator_events = __esm(() => {
|
|
17086
|
+
init_format();
|
|
17082
17087
|
DEFAULT_OPERATOR_EVENT_COOLDOWN_MS = 5 * 60000;
|
|
17083
17088
|
cooldownMap = new Map;
|
|
17084
17089
|
});
|
|
@@ -17098,6 +17103,7 @@ var init_text_voice_scrub = __esm(() => {
|
|
|
17098
17103
|
|
|
17099
17104
|
// card-format.ts
|
|
17100
17105
|
var init_card_format = __esm(() => {
|
|
17106
|
+
init_format();
|
|
17101
17107
|
init_text_voice_scrub();
|
|
17102
17108
|
});
|
|
17103
17109
|
|