blun-king-cli 9.1.615 → 9.1.617
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/CHANGELOG.md +113 -104
- package/LIESMICH.txt +56 -56
- package/README.md +89 -89
- package/bin/core-bootstrap.js +24 -8
- package/bin/launcher-runtime.js +124 -48
- package/bin/managed-plugin-selection.cjs +15 -8
- package/bin/plugin-bootstrap.js +40 -17
- package/bin/private-paths.js +10 -2
- package/bin/profile-handoff.cjs +152 -0
- package/bin/profile-launch.cjs +84 -6
- package/bin/update-lease.js +3 -1
- package/blun.mjs +3773 -1284
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge.mjs +53 -13
- package/telegram-plugin/dist/mcp-server.mjs +54 -10
- package/telegram-plugin/dist/noise.mjs +9 -1
- package/worker-host.mjs +2449 -749
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ import { homedir } from "node:os";
|
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
import { Readable, Writable } from "node:stream";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { StringDecoder } from "node:string_decoder";
|
|
9
10
|
//#region \0rolldown/runtime.js
|
|
10
11
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
11
12
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
@@ -41794,10 +41795,10 @@ var require_internal = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
41794
41795
|
}
|
|
41795
41796
|
InternalCodec.prototype.encoder = InternalEncoder;
|
|
41796
41797
|
InternalCodec.prototype.decoder = InternalDecoder;
|
|
41797
|
-
var StringDecoder = __require("string_decoder").StringDecoder;
|
|
41798
|
-
if (!StringDecoder.prototype.end) StringDecoder.prototype.end = function() {};
|
|
41798
|
+
var StringDecoder$1 = __require("string_decoder").StringDecoder;
|
|
41799
|
+
if (!StringDecoder$1.prototype.end) StringDecoder$1.prototype.end = function() {};
|
|
41799
41800
|
function InternalDecoder(options, codec) {
|
|
41800
|
-
this.decoder = new StringDecoder(codec.enc);
|
|
41801
|
+
this.decoder = new StringDecoder$1(codec.enc);
|
|
41801
41802
|
}
|
|
41802
41803
|
InternalDecoder.prototype.write = function(buf) {
|
|
41803
41804
|
if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf);
|
|
@@ -60026,7 +60027,7 @@ function toInboundCtx(ctx, text) {
|
|
|
60026
60027
|
chatId: ctx.chat?.id,
|
|
60027
60028
|
chatType: chatType === "private" || chatType === "group" || chatType === "supergroup" || chatType === "channel" ? chatType : void 0,
|
|
60028
60029
|
messageId: ctx.message?.message_id,
|
|
60029
|
-
messageThreadId: ctx.message?.message_thread_id,
|
|
60030
|
+
messageThreadId: ctx.message?.is_topic_message === true || ctx.chat?.is_forum === true ? ctx.message?.message_thread_id : void 0,
|
|
60030
60031
|
text,
|
|
60031
60032
|
entities: (ctx.message?.entities ?? ctx.message?.caption_entities ?? []).map((entity) => ({
|
|
60032
60033
|
type: entity.type,
|
|
@@ -60197,7 +60198,7 @@ var BoundedChannelContextBuffer = class {
|
|
|
60197
60198
|
function buildChannelTag(content, meta, addressed = true, inferAddressing = false) {
|
|
60198
60199
|
const pairs = [["source", "telegram"]];
|
|
60199
60200
|
for (const [key, value] of Object.entries(meta)) if (value !== void 0) pairs.push([key, String(value)]);
|
|
60200
|
-
return `<channel ${pairs.map(([key, value]) => `${key}="${escapeAttr(value)}"`).join(" ")}>\n${content}\n</channel>\n${addressed && meta.chat_id.startsWith("-") ? `[GRUPPENANTWORT: Eine bloße Information oder eine Nachricht, die ausdrücklich keine Bestätigung verlangt, macht keine Antwort erforderlich. Beende den Turn dann ohne Ausgabe und ohne Bestätigung deiner Stille.
|
|
60201
|
+
return `<channel ${pairs.map(([key, value]) => `${key}="${escapeAttr(value)}"`).join(" ")}>\n${content}\n</channel>\n${addressed && meta.chat_id.startsWith("-") ? `[GRUPPENANTWORT: Eine bloße Information oder eine Nachricht, die ausdrücklich keine Bestätigung verlangt, macht keine Antwort erforderlich. Beende den Turn dann ohne Ausgabe und ohne Bestätigung deiner Stille. Antwortet jemand auf deine eigene Statusfrage ohne neuen Auftrag, schweige. Fragt dich jemand nach dem Stand, antworte kurz und ehrlich. Falls ein Abschlusstext nötig ist, gib nur [[BLUN_TELEGRAM_SILENT]] aus. Einen konkreten Auftrag an dich nimm kurz an und beginne die Arbeit; melde danach das Ergebnis oder einen echten Blocker. Eine Frage beantworte, wenn du etwas beitragen kannst. Sende eine nötige Antwort mit dem telegram reply-Tool an chat_id=${meta.chat_id}, ohne dieselbe Annahme zu wiederholen.]` : addressed ? `[REPLY-PFLICHT: Der Absender liest NUR Telegram. Sende deine Antwort mit dem telegram reply-Tool an chat_id=${meta.chat_id}. Sichtbarer Antworttext kann auch automatisch weitergeleitet werden; er ist kein privater Denkbereich.]` : inferAddressing ? `[SEMANTISCHE ADRESSIERUNG: Diese menschliche Gruppennachricht nennt dich nicht explizit. Entscheide anhand von Inhalt und Gesprächskontext, ob sie an dich gerichtet ist oder Arbeit von dir verlangt. Falls ja, antworte kurz mit dem telegram reply-Tool an chat_id=${meta.chat_id}. Falls nein, beende den Turn ohne Ausgabe; sende keine Bestätigung deiner Stille.]` : "[NUR MITLESEN — diese Nachricht ist NICHT an dich gerichtet (kein @King, keine Antwort an dich, sie spricht jemand anderen oder die Gruppe allgemein an). ANTWORTE NICHT: kein reply-Tool, keine Bestätigung, kein \"verstanden\", kein \"halte mich raus\" — beende den Turn einfach ohne Ausgabe. NUR wenn du ein echtes, konkretes Ergebnis oder einen echten Blocker beitragen kannst, antwortest du kurz. Im Zweifel: schweigen.]"}`;
|
|
60201
60202
|
}
|
|
60202
60203
|
/**
|
|
60203
60204
|
* Sent once as the head of the first prompt of every bridged session —
|
|
@@ -67697,9 +67698,12 @@ var RequestError = class RequestError extends Error {
|
|
|
67697
67698
|
function isGroupChat(chatId) {
|
|
67698
67699
|
return chatId.startsWith("-");
|
|
67699
67700
|
}
|
|
67700
|
-
|
|
67701
|
-
|
|
67702
|
-
|
|
67701
|
+
function stripTrailingSilentTurnMarker(text) {
|
|
67702
|
+
const trimmed = text.trimEnd();
|
|
67703
|
+
if (!trimmed.endsWith("[[BLUN_TELEGRAM_SILENT]]")) return text;
|
|
67704
|
+
const prefix = trimmed.slice(0, -24);
|
|
67705
|
+
if (prefix.length > 0 && !/\s$/u.test(prefix)) return text;
|
|
67706
|
+
return prefix.trimEnd();
|
|
67703
67707
|
}
|
|
67704
67708
|
function normalize(s) {
|
|
67705
67709
|
return s.toLowerCase().replace(/ä/g, "a").replace(/ö/g, "o").replace(/ü/g, "u").replace(/ß/g, "ss").replace(/[‐-―]/g, " ").replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ").trim();
|
|
@@ -67709,6 +67713,7 @@ const EMPTY_CLAUSES = [
|
|
|
67709
67713
|
/^(bin da|bin hier|hier bin ich|ich bin da|was brauchst du|was gibt es|was liegt an|wie kann ich helfen|bereit|ich warte|warte auf|melde mich|freut mich|here|standing by|ready|awaiting|what do you need)$/,
|
|
67710
67714
|
/^(keine (gruppen ?)?antwort|gar keine antwort|keine reaktion|kein wort|no ?action|no (response|reply|answer|comment))( (notig|erforderlich|needed|required))?$/,
|
|
67711
67715
|
/^(ich )?(halte mich raus|(bin|bleibe) (jetzt )?still|schweige|habe nichts zu melden)$/,
|
|
67716
|
+
/^ich best(?:a|ae|ä)tige nichts und beende still$/u,
|
|
67712
67717
|
/^(stille|still|silence|nichts (zu tun|zu melden|fur mich|fuer mich)|nicht meine lane)$/,
|
|
67713
67718
|
/^(kein handlungsbedarf|es gibt keinen handlungsbedarf|hier besteht kein handlungsbedarf)( (fur|fuer) mich)?$/,
|
|
67714
67719
|
/^(i (am |will be )?)?(staying (silent|out|quiet)|keeping (quiet|out)|holding back)$/,
|
|
@@ -67737,6 +67742,40 @@ function isGroupNoiseReply(text) {
|
|
|
67737
67742
|
return text.split(/[.!?;,\r\n]+|\s+[-\u2013\u2014]\s+/u).map(normalize).filter(Boolean).every((clause) => EMPTY_CLAUSES.some((pattern) => pattern.test(clause)) || NOTIFICATION_CONTEXT.test(clause));
|
|
67738
67743
|
}
|
|
67739
67744
|
//#endregion
|
|
67745
|
+
//#region src/outbox-reader.ts
|
|
67746
|
+
/** Read a byte suffix from one outbox descriptor, never the preceding history. */
|
|
67747
|
+
function readOutboxSuffix(start) {
|
|
67748
|
+
if (typeof start === "number" && (!Number.isSafeInteger(start) || start < 0)) return void 0;
|
|
67749
|
+
let fd;
|
|
67750
|
+
try {
|
|
67751
|
+
fd = openSync(outboxLog(), "r");
|
|
67752
|
+
const stat = fstatSync(fd);
|
|
67753
|
+
if (!stat.isFile()) return void 0;
|
|
67754
|
+
const end = stat.size;
|
|
67755
|
+
const offset = typeof start === "number" ? start : start(end);
|
|
67756
|
+
if (!Number.isSafeInteger(offset) || offset < 0) return void 0;
|
|
67757
|
+
if (offset >= end) return "";
|
|
67758
|
+
const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, end - offset));
|
|
67759
|
+
const decoder = new StringDecoder("utf8");
|
|
67760
|
+
const chunks = [];
|
|
67761
|
+
let position = offset;
|
|
67762
|
+
while (position < end) {
|
|
67763
|
+
const count = readSync(fd, buffer, 0, Math.min(buffer.length, end - position), position);
|
|
67764
|
+
if (count === 0) break;
|
|
67765
|
+
position += count;
|
|
67766
|
+
chunks.push(decoder.write(buffer.subarray(0, count)));
|
|
67767
|
+
}
|
|
67768
|
+
chunks.push(decoder.end());
|
|
67769
|
+
return chunks.join("");
|
|
67770
|
+
} catch {
|
|
67771
|
+
return;
|
|
67772
|
+
} finally {
|
|
67773
|
+
if (fd !== void 0) try {
|
|
67774
|
+
closeSync(fd);
|
|
67775
|
+
} catch {}
|
|
67776
|
+
}
|
|
67777
|
+
}
|
|
67778
|
+
//#endregion
|
|
67740
67779
|
//#region src/acp-session.ts
|
|
67741
67780
|
/**
|
|
67742
67781
|
* The King side of the bridge: spawns `<node> <main.mjs> acp`, drives it as
|
|
@@ -67830,8 +67869,8 @@ function outboxMarker() {
|
|
|
67830
67869
|
/** True when the reply tool appended an outbox entry for this chat since `marker`. */
|
|
67831
67870
|
function outboxGrewForChat(marker, chatId, threadId) {
|
|
67832
67871
|
try {
|
|
67833
|
-
|
|
67834
|
-
|
|
67872
|
+
const fresh = readOutboxSuffix(marker);
|
|
67873
|
+
if (fresh === void 0 || fresh.length === 0) return false;
|
|
67835
67874
|
for (const line of fresh.split(/\r?\n/)) {
|
|
67836
67875
|
if (line.trim().length === 0) continue;
|
|
67837
67876
|
try {
|
|
@@ -67857,7 +67896,8 @@ function scrubSecrets(text) {
|
|
|
67857
67896
|
* log it to outbox.jsonl as kind "reply-fallback". Returns true on success.
|
|
67858
67897
|
*/
|
|
67859
67898
|
async function sendReplyFallback(chatId, rawText, threadId) {
|
|
67860
|
-
|
|
67899
|
+
const cleanText = stripTrailingSilentTurnMarker(rawText);
|
|
67900
|
+
if (cleanText.trim().length === 0) return true;
|
|
67861
67901
|
let topic;
|
|
67862
67902
|
try {
|
|
67863
67903
|
topic = threadId === void 0 ? {} : { message_thread_id: telegramThreadId(threadId) };
|
|
@@ -67866,11 +67906,11 @@ async function sendReplyFallback(chatId, rawText, threadId) {
|
|
|
67866
67906
|
}
|
|
67867
67907
|
const token = botToken();
|
|
67868
67908
|
if (token === void 0) return false;
|
|
67869
|
-
if (isGroupChat(chatId) && isGroupNoiseReply(
|
|
67909
|
+
if (isGroupChat(chatId) && isGroupNoiseReply(cleanText)) {
|
|
67870
67910
|
logLine(`reply-fallback suppressed (group meta-noise): ${rawText.slice(0, 60)}`);
|
|
67871
67911
|
return true;
|
|
67872
67912
|
}
|
|
67873
|
-
const text = scrubSecrets(
|
|
67913
|
+
const text = scrubSecrets(cleanText);
|
|
67874
67914
|
const body = text.length > TELEGRAM_TEXT_LIMIT ? `${text.slice(0, TELEGRAM_TEXT_LIMIT - 1)}…` : text;
|
|
67875
67915
|
try {
|
|
67876
67916
|
const payload = await (await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import process$1 from "node:process";
|
|
3
|
-
import { appendFileSync, chmodSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { appendFileSync, chmodSync, closeSync, fstatSync, mkdirSync, openSync, readFileSync, readSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { extname, join, sep } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
7
|
+
import { StringDecoder } from "node:string_decoder";
|
|
7
8
|
//#region \0rolldown/runtime.js
|
|
8
9
|
var __create = Object.create;
|
|
9
10
|
var __defProp = Object.defineProperty;
|
|
@@ -55633,10 +55634,10 @@ var require_internal = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
55633
55634
|
}
|
|
55634
55635
|
InternalCodec.prototype.encoder = InternalEncoder;
|
|
55635
55636
|
InternalCodec.prototype.decoder = InternalDecoder;
|
|
55636
|
-
var StringDecoder = __require("string_decoder").StringDecoder;
|
|
55637
|
-
if (!StringDecoder.prototype.end) StringDecoder.prototype.end = function() {};
|
|
55637
|
+
var StringDecoder$1 = __require("string_decoder").StringDecoder;
|
|
55638
|
+
if (!StringDecoder$1.prototype.end) StringDecoder$1.prototype.end = function() {};
|
|
55638
55639
|
function InternalDecoder(options, codec) {
|
|
55639
|
-
this.decoder = new StringDecoder(codec.enc);
|
|
55640
|
+
this.decoder = new StringDecoder$1(codec.enc);
|
|
55640
55641
|
}
|
|
55641
55642
|
InternalDecoder.prototype.write = function(buf) {
|
|
55642
55643
|
if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf);
|
|
@@ -73469,6 +73470,13 @@ const SILENT_TURN_MARKER = "[[BLUN_TELEGRAM_SILENT]]";
|
|
|
73469
73470
|
function isSilentTurnMarker(text) {
|
|
73470
73471
|
return text.trim() === SILENT_TURN_MARKER;
|
|
73471
73472
|
}
|
|
73473
|
+
function stripTrailingSilentTurnMarker(text) {
|
|
73474
|
+
const trimmed = text.trimEnd();
|
|
73475
|
+
if (!trimmed.endsWith("[[BLUN_TELEGRAM_SILENT]]")) return text;
|
|
73476
|
+
const prefix = trimmed.slice(0, -24);
|
|
73477
|
+
if (prefix.length > 0 && !/\s$/u.test(prefix)) return text;
|
|
73478
|
+
return prefix.trimEnd();
|
|
73479
|
+
}
|
|
73472
73480
|
function normalize(s) {
|
|
73473
73481
|
return s.toLowerCase().replace(/ä/g, "a").replace(/ö/g, "o").replace(/ü/g, "u").replace(/ß/g, "ss").replace(/[‐-―]/g, " ").replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ").trim();
|
|
73474
73482
|
}
|
|
@@ -73477,6 +73485,7 @@ const EMPTY_CLAUSES = [
|
|
|
73477
73485
|
/^(bin da|bin hier|hier bin ich|ich bin da|was brauchst du|was gibt es|was liegt an|wie kann ich helfen|bereit|ich warte|warte auf|melde mich|freut mich|here|standing by|ready|awaiting|what do you need)$/,
|
|
73478
73486
|
/^(keine (gruppen ?)?antwort|gar keine antwort|keine reaktion|kein wort|no ?action|no (response|reply|answer|comment))( (notig|erforderlich|needed|required))?$/,
|
|
73479
73487
|
/^(ich )?(halte mich raus|(bin|bleibe) (jetzt )?still|schweige|habe nichts zu melden)$/,
|
|
73488
|
+
/^ich best(?:a|ae|ä)tige nichts und beende still$/u,
|
|
73480
73489
|
/^(stille|still|silence|nichts (zu tun|zu melden|fur mich|fuer mich)|nicht meine lane)$/,
|
|
73481
73490
|
/^(kein handlungsbedarf|es gibt keinen handlungsbedarf|hier besteht kein handlungsbedarf)( (fur|fuer) mich)?$/,
|
|
73482
73491
|
/^(i (am |will be )?)?(staying (silent|out|quiet)|keeping (quiet|out)|holding back)$/,
|
|
@@ -73505,6 +73514,40 @@ function isGroupNoiseReply(text) {
|
|
|
73505
73514
|
return text.split(/[.!?;,\r\n]+|\s+[-\u2013\u2014]\s+/u).map(normalize).filter(Boolean).every((clause) => EMPTY_CLAUSES.some((pattern) => pattern.test(clause)) || NOTIFICATION_CONTEXT.test(clause));
|
|
73506
73515
|
}
|
|
73507
73516
|
//#endregion
|
|
73517
|
+
//#region src/outbox-reader.ts
|
|
73518
|
+
/** Read a byte suffix from one outbox descriptor, never the preceding history. */
|
|
73519
|
+
function readOutboxSuffix(start) {
|
|
73520
|
+
if (typeof start === "number" && (!Number.isSafeInteger(start) || start < 0)) return void 0;
|
|
73521
|
+
let fd;
|
|
73522
|
+
try {
|
|
73523
|
+
fd = openSync(outboxLog(), "r");
|
|
73524
|
+
const stat = fstatSync(fd);
|
|
73525
|
+
if (!stat.isFile()) return void 0;
|
|
73526
|
+
const end = stat.size;
|
|
73527
|
+
const offset = typeof start === "number" ? start : start(end);
|
|
73528
|
+
if (!Number.isSafeInteger(offset) || offset < 0) return void 0;
|
|
73529
|
+
if (offset >= end) return "";
|
|
73530
|
+
const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, end - offset));
|
|
73531
|
+
const decoder = new StringDecoder("utf8");
|
|
73532
|
+
const chunks = [];
|
|
73533
|
+
let position = offset;
|
|
73534
|
+
while (position < end) {
|
|
73535
|
+
const count = readSync(fd, buffer, 0, Math.min(buffer.length, end - position), position);
|
|
73536
|
+
if (count === 0) break;
|
|
73537
|
+
position += count;
|
|
73538
|
+
chunks.push(decoder.write(buffer.subarray(0, count)));
|
|
73539
|
+
}
|
|
73540
|
+
chunks.push(decoder.end());
|
|
73541
|
+
return chunks.join("");
|
|
73542
|
+
} catch {
|
|
73543
|
+
return;
|
|
73544
|
+
} finally {
|
|
73545
|
+
if (fd !== void 0) try {
|
|
73546
|
+
closeSync(fd);
|
|
73547
|
+
} catch {}
|
|
73548
|
+
}
|
|
73549
|
+
}
|
|
73550
|
+
//#endregion
|
|
73508
73551
|
//#region src/inbound.ts
|
|
73509
73552
|
function telegramThreadId(value) {
|
|
73510
73553
|
if (value === void 0) return void 0;
|
|
@@ -73625,7 +73668,7 @@ async function runTool(api, token, name, args) {
|
|
|
73625
73668
|
case "download_attachment": return runDownload(api, token, args["file_id"]);
|
|
73626
73669
|
case "edit_message": {
|
|
73627
73670
|
const chatId = resolveAllowedChatId(args["chat_id"]);
|
|
73628
|
-
if (isSilentTurnMarker(args["text"])) throw new Error("silent marker cannot be used to edit a Telegram message");
|
|
73671
|
+
if (stripTrailingSilentTurnMarker(args["text"]) !== args["text"] || isSilentTurnMarker(args["text"])) throw new Error("silent marker cannot be used to edit a Telegram message");
|
|
73629
73672
|
assertOptionalTelegramLanguageRelease(args["text"], args["release_token"]);
|
|
73630
73673
|
const parseMode = args["format"] === "markdownv2" ? "MarkdownV2" : void 0;
|
|
73631
73674
|
const edited = await api.editMessageText(chatId, Number(args["message_id"]), args["text"], parseMode !== void 0 ? { parse_mode: parseMode } : void 0);
|
|
@@ -73650,13 +73693,10 @@ async function runTool(api, token, name, args) {
|
|
|
73650
73693
|
* nicht weiter retryt. Datei-Anhänge sind nie betroffen.
|
|
73651
73694
|
*/
|
|
73652
73695
|
const DUPLICATE_WINDOW_MS = 3e5;
|
|
73653
|
-
const
|
|
73696
|
+
const OUTBOX_TAIL_CHARACTERS = 32768;
|
|
73654
73697
|
function isRecentDuplicate(chatId, text, threadId, replyTo) {
|
|
73655
73698
|
try {
|
|
73656
|
-
const
|
|
73657
|
-
const size = statSync(file).size;
|
|
73658
|
-
const raw = readFileSync(file, "utf8");
|
|
73659
|
-
const tail = size > OUTBOX_TAIL_BYTES ? raw.slice(raw.length - OUTBOX_TAIL_BYTES) : raw;
|
|
73699
|
+
const tail = readOutboxSuffix((size) => Math.max(0, size - 4 * OUTBOX_TAIL_CHARACTERS))?.slice(-OUTBOX_TAIL_CHARACTERS) ?? "";
|
|
73660
73700
|
const wanted = text.trim();
|
|
73661
73701
|
const cutoff = Date.now() - DUPLICATE_WINDOW_MS;
|
|
73662
73702
|
for (const line of tail.split("\n")) {
|
|
@@ -73685,6 +73725,10 @@ async function runReply(api, args) {
|
|
|
73685
73725
|
if (files.length > 0) throw new Error("silent marker cannot accompany files");
|
|
73686
73726
|
return "suppressed: silent marker - not sent";
|
|
73687
73727
|
}
|
|
73728
|
+
if (stripTrailingSilentTurnMarker(text) !== text) {
|
|
73729
|
+
if (files.length === 0 && isGroupChat(chatId) && isGroupNoiseReply(stripTrailingSilentTurnMarker(text))) return "suppressed: group meta/acknowledgement noise - not sent";
|
|
73730
|
+
throw new Error("silent marker cannot accompany Telegram reply text or files; resend the reply without it");
|
|
73731
|
+
}
|
|
73688
73732
|
if (files.length === 0 && isGroupChat(chatId) && isGroupNoiseReply(text)) {
|
|
73689
73733
|
appendJsonl(outboxLog(), {
|
|
73690
73734
|
direction: "out",
|
|
@@ -13,6 +13,13 @@ const SILENT_TURN_MARKER = "[[BLUN_TELEGRAM_SILENT]]";
|
|
|
13
13
|
function isSilentTurnMarker(text) {
|
|
14
14
|
return text.trim() === SILENT_TURN_MARKER;
|
|
15
15
|
}
|
|
16
|
+
function stripTrailingSilentTurnMarker(text) {
|
|
17
|
+
const trimmed = text.trimEnd();
|
|
18
|
+
if (!trimmed.endsWith("[[BLUN_TELEGRAM_SILENT]]")) return text;
|
|
19
|
+
const prefix = trimmed.slice(0, -24);
|
|
20
|
+
if (prefix.length > 0 && !/\s$/u.test(prefix)) return text;
|
|
21
|
+
return prefix.trimEnd();
|
|
22
|
+
}
|
|
16
23
|
function normalize(s) {
|
|
17
24
|
return s.toLowerCase().replace(/ä/g, "a").replace(/ö/g, "o").replace(/ü/g, "u").replace(/ß/g, "ss").replace(/[‐-―]/g, " ").replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ").trim();
|
|
18
25
|
}
|
|
@@ -21,6 +28,7 @@ const EMPTY_CLAUSES = [
|
|
|
21
28
|
/^(bin da|bin hier|hier bin ich|ich bin da|was brauchst du|was gibt es|was liegt an|wie kann ich helfen|bereit|ich warte|warte auf|melde mich|freut mich|here|standing by|ready|awaiting|what do you need)$/,
|
|
22
29
|
/^(keine (gruppen ?)?antwort|gar keine antwort|keine reaktion|kein wort|no ?action|no (response|reply|answer|comment))( (notig|erforderlich|needed|required))?$/,
|
|
23
30
|
/^(ich )?(halte mich raus|(bin|bleibe) (jetzt )?still|schweige|habe nichts zu melden)$/,
|
|
31
|
+
/^ich best(?:a|ae|ä)tige nichts und beende still$/u,
|
|
24
32
|
/^(stille|still|silence|nichts (zu tun|zu melden|fur mich|fuer mich)|nicht meine lane)$/,
|
|
25
33
|
/^(kein handlungsbedarf|es gibt keinen handlungsbedarf|hier besteht kein handlungsbedarf)( (fur|fuer) mich)?$/,
|
|
26
34
|
/^(i (am |will be )?)?(staying (silent|out|quiet)|keeping (quiet|out)|holding back)$/,
|
|
@@ -49,4 +57,4 @@ function isGroupNoiseReply(text) {
|
|
|
49
57
|
return text.split(/[.!?;,\r\n]+|\s+[-\u2013\u2014]\s+/u).map(normalize).filter(Boolean).every((clause) => EMPTY_CLAUSES.some((pattern) => pattern.test(clause)) || NOTIFICATION_CONTEXT.test(clause));
|
|
50
58
|
}
|
|
51
59
|
//#endregion
|
|
52
|
-
export { SILENT_TURN_MARKER, isGroupChat, isGroupNoiseReply, isSilentTurnMarker };
|
|
60
|
+
export { SILENT_TURN_MARKER, isGroupChat, isGroupNoiseReply, isSilentTurnMarker, stripTrailingSilentTurnMarker };
|