blun-king-cli 9.1.577 → 9.1.578
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 +8 -0
- package/bin/telegram-mcp-compatibility.cjs +49 -0
- package/blun.mjs +15 -3
- package/package.json +1 -1
- package/telegram-plugin/bin/telegram-direct-reply-policy.cjs +48 -0
- package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +1 -0
- package/telegram-plugin/compat/mcp-server-fa511cd1.mjs +73825 -0
- package/telegram-plugin/dist/mcp-server.mjs +9 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 9.1.578 - 2026-09-08
|
|
2
|
+
|
|
3
|
+
- Bound thinking-only recovery to one additional attempt per step. A second thinking-only timeout stops that step instead of repeatedly issuing the same request. The chosen reasoning effort and existing timeout remain unchanged; this is not a promise of faster first-token response times.
|
|
4
|
+
- Preserve short Telegram group presence answers when reply_to binds them to a recent same-chat input explicitly addressed to the agent, or a supported German/English presence question beginning with the agent's own configured name. Unsolicited presence and silence-only narration remain filtered; ordinary automatic group fallback stays disabled.
|
|
5
|
+
- Include reply_to in outgoing reply and suppression diagnostics. Missing or ambiguous context does not grant a presence exception. This does not add general recipient recognition or prevent turns caused by legacy semantic addressing.
|
|
6
|
+
- Apply the outbound repair to two exact known managed Telegram MCP revisions, including the final plugin configuration merge. The legacy sender receives a narrowly changed compatibility copy. Existing incoming bridges, access settings, profiles and registers are not rewritten; unknown/custom senders and explicit overrides are preserved. A newly started King process is required.
|
|
7
|
+
|
|
8
|
+
|
|
1
9
|
## 9.1.577 - 2026-09-07
|
|
2
10
|
|
|
3
11
|
- Stop automatically forwarding ordinary assistant turn-end text to Telegram groups, in both the CLI core and the standalone bridge. This prevents the automatic fallback from posting explanations that there is nothing to report or that a message addresses someone else.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { createHash } = require('node:crypto');
|
|
5
|
+
const REPAIRS = {
|
|
6
|
+
fa511cd1e2a0a911aafa100d7a41b3ebd0abd0599c3e2e66daca1c8421298573: {
|
|
7
|
+
file: 'compat/mcp-server-fa511cd1.mjs', sha256: '0d2d078722245e1e559ea146ba5139d999f9ec1f3d4f21ce2dc6d68f6783d0a9',
|
|
8
|
+
},
|
|
9
|
+
'4ed83af87c8d4d89b1f21172aeabfd609bd7772242a9f7d046685acdd24d316c': {
|
|
10
|
+
file: 'dist/mcp-server.mjs', sha256: 'fa9ab7a79a0a98dad505d761f5e1e20a8f72ae640f67b0e9151bcce2bccd0998',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
function fileHash(file) {
|
|
14
|
+
const info = fs.lstatSync(file);
|
|
15
|
+
if (!info.isFile() || info.size > 3 * 1024 * 1024) return null;
|
|
16
|
+
return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
17
|
+
}
|
|
18
|
+
function resolveKnownTelegramMcp(selected, { explicitOverride = false } = {}) {
|
|
19
|
+
if (explicitOverride) return selected;
|
|
20
|
+
try {
|
|
21
|
+
const repair = REPAIRS[fileHash(selected)];
|
|
22
|
+
if (!repair) return selected;
|
|
23
|
+
const replacement = path.join(__dirname, '..', 'telegram-plugin', repair.file);
|
|
24
|
+
// Never replace unknown/custom code or launch a damaged compatibility payload.
|
|
25
|
+
return fileHash(replacement) === repair.sha256 ? replacement : selected;
|
|
26
|
+
} catch {
|
|
27
|
+
return selected;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function repairKnownTelegramPluginConfig(entry, { recipientName = '' } = {}) {
|
|
31
|
+
const config = entry.config;
|
|
32
|
+
if (entry.pluginId !== 'telegram' || entry.serverName !== 'telegram' || config?.transport !== 'stdio') return config;
|
|
33
|
+
if (process.env.BLUN_TELEGRAM_MCP_SERVER) return config;
|
|
34
|
+
const args = config.args;
|
|
35
|
+
if (!Array.isArray(args) || typeof config.cwd !== 'string') return config;
|
|
36
|
+
const native = args.length === 2 && args[0] === '__plugin_run_node';
|
|
37
|
+
if (!native && !(args.length === 1 && /^node(?:\.exe)?$/iu.test(path.basename(config.command || '')))) return config;
|
|
38
|
+
const index = native ? 1 : 0;
|
|
39
|
+
if (typeof args[index] !== 'string') return config;
|
|
40
|
+
const selected = path.resolve(config.cwd, args[index]);
|
|
41
|
+
const replacement = resolveKnownTelegramMcp(selected);
|
|
42
|
+
if (selected === replacement) return config;
|
|
43
|
+
return { ...config, args: args.map((value, i) => i === index ? replacement : value), env: {
|
|
44
|
+
...config.env,
|
|
45
|
+
BLUN_TELEGRAM_PERSONA_NAME: typeof recipientName === 'string' ? recipientName : '',
|
|
46
|
+
...(native ? { BLUN_PLUGIN_ROOT: path.dirname(path.dirname(replacement)) } : {}),
|
|
47
|
+
} };
|
|
48
|
+
}
|
|
49
|
+
module.exports = { resolveKnownTelegramMcp, repairKnownTelegramPluginConfig };
|
package/blun.mjs
CHANGED
|
@@ -245623,6 +245623,7 @@ async function chatWithLiveResponseRepetitionRecovery(deps) {
|
|
|
245623
245623
|
const { retryInput, params, stepEvents, signal, log } = deps;
|
|
245624
245624
|
let currentParams = params;
|
|
245625
245625
|
let repetitionRetries = 0;
|
|
245626
|
+
let thinkingRetries = 0;
|
|
245626
245627
|
for (let attempt = 1;; attempt++) {
|
|
245627
245628
|
try {
|
|
245628
245629
|
const response = await chatWithRetry({
|
|
@@ -245640,6 +245641,15 @@ async function chatWithLiveResponseRepetitionRecovery(deps) {
|
|
|
245640
245641
|
const thinkingTimeout = stepEvents.thinkingOnlyTimeoutResult;
|
|
245641
245642
|
stepEvents.finishLiveResponseAttempt();
|
|
245642
245643
|
if (thinkingTimeout !== null && !signal.aborted) {
|
|
245644
|
+
if (thinkingRetries >= 1) {
|
|
245645
|
+
log?.warn("thinking-only recovery exhausted; stopping this step", {
|
|
245646
|
+
elapsedMs: thinkingTimeout.elapsedMs,
|
|
245647
|
+
turnStep: `${retryInput.turnId}.${String(retryInput.currentStep)}`,
|
|
245648
|
+
thinkingRetries
|
|
245649
|
+
});
|
|
245650
|
+
throw error;
|
|
245651
|
+
}
|
|
245652
|
+
thinkingRetries += 1;
|
|
245643
245653
|
log?.warn("thinking-only phase exceeded limit; forcing concrete action", {
|
|
245644
245654
|
elapsedMs: thinkingTimeout.elapsedMs,
|
|
245645
245655
|
turnStep: `${retryInput.turnId}.${String(retryInput.currentStep)}`
|
|
@@ -319635,7 +319645,7 @@ agentSpineRuntime: this.agentSpineRuntimeRecipients(pluginHooks, mcpConfig),
|
|
|
319635
319645
|
mergePluginMcpConfig(base) {
|
|
319636
319646
|
const pluginEntries = this.plugins.mcpServerConfigs();
|
|
319637
319647
|
if (pluginEntries.length === 0) return base;
|
|
319638
|
-
const pluginServers = this.withManagedBlunPluginEnv(Object.fromEntries(pluginEntries.map((entry) => [entry.runtimeName, entry.
|
|
319648
|
+
const pluginServers = this.withManagedBlunPluginEnv(Object.fromEntries(pluginEntries.map((entry) => [entry.runtimeName, repairKnownTelegramPluginConfig(entry, { recipientName: readProfilePersona({ ...process.env, BLUN_HOME: this.homeDir }).persona?.name || "" })])));
|
|
319639
319649
|
return {
|
|
319640
319650
|
servers: {
|
|
319641
319651
|
...base?.servers,
|
|
@@ -422188,7 +422198,8 @@ function telegramChannelMcpServers(dir = telegramStateDir()) {
|
|
|
422188
422198
|
args: launch.args,
|
|
422189
422199
|
env: {
|
|
422190
422200
|
...launch.env,
|
|
422191
|
-
BLUN_TELEGRAM_STATE_DIR: dir
|
|
422201
|
+
BLUN_TELEGRAM_STATE_DIR: dir,
|
|
422202
|
+
BLUN_TELEGRAM_PERSONA_NAME: readPersona$1()?.name || ""
|
|
422192
422203
|
}
|
|
422193
422204
|
} };
|
|
422194
422205
|
}
|
|
@@ -422209,6 +422220,7 @@ function managedTelegramPluginRoot() {
|
|
|
422209
422220
|
return void 0;
|
|
422210
422221
|
}
|
|
422211
422222
|
}
|
|
422223
|
+
var { resolveKnownTelegramMcp, repairKnownTelegramPluginConfig } = createRequire(import.meta.url)("./bin/telegram-mcp-compatibility.cjs");
|
|
422212
422224
|
function resolveTelegramMcpEntry() {
|
|
422213
422225
|
const main = process.argv[1];
|
|
422214
422226
|
const managedRoot = managedTelegramPluginRoot();
|
|
@@ -422218,7 +422230,7 @@ function resolveTelegramMcpEntry() {
|
|
|
422218
422230
|
join(homedir(), ".blun", "plugins", "managed", "telegram", "dist", "mcp-server.mjs"),
|
|
422219
422231
|
main === void 0 ? void 0 : join(dirname(main), "..", "..", "..", "plugins", "telegram", "dist", "mcp-server.mjs")
|
|
422220
422232
|
];
|
|
422221
|
-
for (const candidate of candidates) if (candidate !== void 0 && existsSync(candidate)) return candidate;
|
|
422233
|
+
for (const candidate of candidates) if (candidate !== void 0 && existsSync(candidate)) return resolveKnownTelegramMcp(candidate, { explicitOverride: candidate === process.env["BLUN_TELEGRAM_MCP_SERVER"] });
|
|
422222
422234
|
}
|
|
422223
422235
|
var TelegramChannelController = class {
|
|
422224
422236
|
host;
|
package/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { readJsonlTail } = require('./telegram-private-conversation-policy.cjs');
|
|
4
|
+
const MAX_AGE_MS = 30 * 60 * 1000;
|
|
5
|
+
const PRESENCE_ANSWER = /^(?:(?:ja )?(?:bin da|bin hier|ich bin da|hier bin ich|ich bin hier|bereit|i am here|i m here|here|ready))(?: (?:was brauchst du|was gibt es|was liegt an|wie kann ich helfen|what do you need|how can i help))?$/u;
|
|
6
|
+
const PRESENCE_QUESTION = /^(?:bist du da|bist du hier|bist du erreichbar|are you there|are you here)$/u;
|
|
7
|
+
|
|
8
|
+
function normalize(value) {
|
|
9
|
+
return String(value ?? '').normalize('NFKC').toLocaleLowerCase('de-DE')
|
|
10
|
+
.replace(/[^\p{L}\p{N}\s]/gu, ' ').replace(/\s+/gu, ' ').trim();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function messageId(value) {
|
|
14
|
+
if (typeof value !== 'string' && typeof value !== 'number') return null;
|
|
15
|
+
if (!/^[1-9]\d*$/u.test(String(value))) return null;
|
|
16
|
+
const number = Number(value);
|
|
17
|
+
return Number.isSafeInteger(number) ? number : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ownPresenceQuestion(content, recipientName) {
|
|
21
|
+
if (typeof content !== 'string' || content.length > 512) return false;
|
|
22
|
+
if (typeof recipientName !== 'string' || recipientName.length > 128) return false;
|
|
23
|
+
const name = normalize(recipientName), text = normalize(content);
|
|
24
|
+
if (!name) return false;
|
|
25
|
+
if (text === name) return true;
|
|
26
|
+
return text.startsWith(name + ' ') && PRESENCE_QUESTION.test(text.slice(name.length + 1));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function permitsPresenceReply({ text, chatId, replyTo, inboxFile,
|
|
30
|
+
recipientName = process.env.BLUN_TELEGRAM_PERSONA_NAME || '', now = Date.now() }) {
|
|
31
|
+
if (typeof text !== 'string' || text.length > 160 || !PRESENCE_ANSWER.test(normalize(text))) return false;
|
|
32
|
+
const wantedId = messageId(replyTo), wantedChat = String(chatId ?? '');
|
|
33
|
+
if (wantedId === null || !/^-[1-9]\d*$/u.test(wantedChat) || !Number.isFinite(now)) return false;
|
|
34
|
+
// A semantic marker is shared by legacy recipients; only a bound own-name ping can refine it.
|
|
35
|
+
const records = readJsonlTail(inboxFile);
|
|
36
|
+
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
37
|
+
const record = records[index], meta = record.meta;
|
|
38
|
+
if (record.direction !== 'in' || !meta || String(meta.chat_id) !== wantedChat || messageId(meta.message_id) !== wantedId) continue;
|
|
39
|
+
const timestamp = Date.parse(String(meta.ts ?? record.ts ?? ''));
|
|
40
|
+
if (!Number.isFinite(timestamp) || timestamp > now || now - timestamp > MAX_AGE_MS) return false;
|
|
41
|
+
return meta.addressed === true || meta.addressed === 'true'
|
|
42
|
+
|| meta.addressed === 1 || meta.addressed === '1'
|
|
43
|
+
|| (meta.addressed === 'semantic' && ownPresenceQuestion(record.content, recipientName));
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = { MAX_AGE_MS, messageId, ownPresenceQuestion, permitsPresenceReply };
|