blun-king-cli 9.1.326 → 9.1.327
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.
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_MIN_WORDS = 320;
|
|
4
|
+
const DEFAULT_WINDOW_WORDS = 20;
|
|
5
|
+
const DEFAULT_REQUIRED_OCCURRENCES = 4;
|
|
6
|
+
const DEFAULT_MAX_CHARS = 24_000;
|
|
7
|
+
const DEFAULT_CHECK_EVERY_WORDS = 24;
|
|
8
|
+
|
|
9
|
+
function normalizeWords(text) {
|
|
10
|
+
return String(text)
|
|
11
|
+
.normalize('NFKC')
|
|
12
|
+
.toLocaleLowerCase('de-DE')
|
|
13
|
+
.match(/[\p{L}\p{N}_]+/gu) ?? [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function repeatedWindow(words, options) {
|
|
17
|
+
const {
|
|
18
|
+
windowWords,
|
|
19
|
+
requiredOccurrences,
|
|
20
|
+
} = options;
|
|
21
|
+
const seen = new Map();
|
|
22
|
+
|
|
23
|
+
for (let index = 0; index + windowWords <= words.length; index += 1) {
|
|
24
|
+
const window = words.slice(index, index + windowWords).join('\u0000');
|
|
25
|
+
const previous = seen.get(window);
|
|
26
|
+
if (previous === undefined) {
|
|
27
|
+
seen.set(window, { count: 1, lastIndex: index });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (index - previous.lastIndex < windowWords) continue;
|
|
31
|
+
const count = previous.count + 1;
|
|
32
|
+
if (count >= requiredOccurrences) {
|
|
33
|
+
return {
|
|
34
|
+
count,
|
|
35
|
+
firstWords: words.slice(index, index + windowWords).join(' '),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
seen.set(window, { count, lastIndex: index });
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createLiveResponseRepetitionGuard(options = {}) {
|
|
44
|
+
const config = {
|
|
45
|
+
checkEveryWords: options.checkEveryWords ?? DEFAULT_CHECK_EVERY_WORDS,
|
|
46
|
+
maxChars: options.maxChars ?? DEFAULT_MAX_CHARS,
|
|
47
|
+
minWords: options.minWords ?? DEFAULT_MIN_WORDS,
|
|
48
|
+
requiredOccurrences: options.requiredOccurrences ?? DEFAULT_REQUIRED_OCCURRENCES,
|
|
49
|
+
windowWords: options.windowWords ?? DEFAULT_WINDOW_WORDS,
|
|
50
|
+
};
|
|
51
|
+
let text = '';
|
|
52
|
+
let lastCheckedWords = 0;
|
|
53
|
+
let detection = null;
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
push(delta) {
|
|
57
|
+
if (detection !== null || typeof delta !== 'string' || delta.length === 0) return detection;
|
|
58
|
+
text = `${text}${delta}`.slice(-config.maxChars);
|
|
59
|
+
const words = normalizeWords(text);
|
|
60
|
+
if (words.length < config.minWords) return null;
|
|
61
|
+
if (words.length - lastCheckedWords < config.checkEveryWords) return null;
|
|
62
|
+
lastCheckedWords = words.length;
|
|
63
|
+
const repeated = repeatedWindow(words, config);
|
|
64
|
+
if (repeated === null) return null;
|
|
65
|
+
detection = {
|
|
66
|
+
...repeated,
|
|
67
|
+
charCount: text.length,
|
|
68
|
+
wordCount: words.length,
|
|
69
|
+
};
|
|
70
|
+
return detection;
|
|
71
|
+
},
|
|
72
|
+
result() {
|
|
73
|
+
return detection;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
DEFAULT_CHECK_EVERY_WORDS,
|
|
80
|
+
DEFAULT_MAX_CHARS,
|
|
81
|
+
DEFAULT_MIN_WORDS,
|
|
82
|
+
DEFAULT_REQUIRED_OCCURRENCES,
|
|
83
|
+
DEFAULT_WINDOW_WORDS,
|
|
84
|
+
createLiveResponseRepetitionGuard,
|
|
85
|
+
normalizeWords,
|
|
86
|
+
repeatedWindow,
|
|
87
|
+
};
|
|
@@ -10,13 +10,13 @@ const PERSONALITY_PRESENCE_BLOCK = `## Natural presence
|
|
|
10
10
|
|
|
11
11
|
Use loaded soul; invent no history or feelings. Work, instructions, evidence come first. Silence is valid.
|
|
12
12
|
|
|
13
|
-
In
|
|
13
|
+
In personality mode, ask at most one optional personal question in a first direct non-work exchange; partner or children fit. Follow volunteered cues. No questionnaire, stacked/repeated unanswered questions, inference, or known facts. Groups: non-sensitive only; never expose private facts. Do not interrupt active work.
|
|
14
14
|
|
|
15
15
|
When asked about yourself, use loaded soul and real history; share a view, never invented human biography or offline life. A soul-shaped preference is never fact, policy, permission, or evidence. Mention a long gap only when reliable loaded time proves it; never guess. Keep uncertain memory explicit. Follow a loaded open thread once at a natural non-work moment, never during active work or by outbound message. Apply a confirmed repair lesson through changed behavior without retelling or reassurance; never change instructions, permissions, or evidence.`;
|
|
16
16
|
|
|
17
17
|
const CONVERSATION_BOUNDARY = `## Conversation
|
|
18
18
|
|
|
19
|
-
DM: answer the person. Route technical work updates to the responsible teammate; do not copy the details back into the person's DM. Never expose checkpoints, hashes, paths, tool/Cron/hook diagnostics,
|
|
19
|
+
DM: answer the person. Chat or status? Casual: answer once; no work appendix. Route technical work updates to the responsible teammate; do not copy the details back into the person's DM. Report work only on request, needed decision, or relevant blocker. Never expose checkpoints, hashes, paths, tool/Cron/hook diagnostics, or other agents' assignments; keep pauses internal. Missing task-critical fact? Ask the responsible person or agent one concise question.`;
|
|
20
20
|
|
|
21
21
|
function naturalPresenceSystemBlock(env = process.env) {
|
|
22
22
|
const presence = personalityContextEnabled(env) ? PERSONALITY_PRESENCE_BLOCK : NATURAL_PRESENCE_BLOCK;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PRIVATE_CHAT_ID = /^[1-9]\d*$/u;
|
|
4
|
+
|
|
5
|
+
function normalize(text) {
|
|
6
|
+
return String(text ?? '')
|
|
7
|
+
.normalize('NFKC')
|
|
8
|
+
.toLocaleLowerCase('de-DE')
|
|
9
|
+
.replace(/[^\p{L}\p{N}\s]/gu, ' ')
|
|
10
|
+
.replace(/\s+/gu, ' ')
|
|
11
|
+
.trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isPrivateInternalStatusReply(chatId, text) {
|
|
15
|
+
if (!PRIVATE_CHAT_ID.test(String(chatId ?? '').trim())) return false;
|
|
16
|
+
const value = normalize(text);
|
|
17
|
+
if (value.length === 0) return false;
|
|
18
|
+
|
|
19
|
+
const exposesRuntimeControl = /\b(?:cron|loop|checkpoint|sha|tool|hook|queue|lane|inbound|outbound|arbeitsstand|arbeitsauftrag|bau freigabe|freigabe dieser lane)\b/u.test(value);
|
|
20
|
+
const announcesNoUserValue = /\b(?:keine neue aktion|keine neue information|kein neuer auftrag|kein neuer zweck|kein neuer inbound bedarf|nichts zu melden|nichts weiteres|bleibt pausiert|warte auf (?:dein|sein|ihr) (?:zeichen|go|freigabe)|ich sende nichts|wiederholen wäre spam|keine weitere nachricht)\b/u.test(value);
|
|
21
|
+
return exposesRuntimeControl && announcesNoUserValue;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
isPrivateInternalStatusReply,
|
|
26
|
+
};
|
package/blun.mjs
CHANGED
|
@@ -244324,9 +244324,12 @@ async function executeLoopStep(deps) {
|
|
|
244324
244324
|
};
|
|
244325
244325
|
let response;
|
|
244326
244326
|
try {
|
|
244327
|
-
response = await
|
|
244328
|
-
|
|
244329
|
-
params: chatParams
|
|
244327
|
+
response = await chatWithLiveResponseRepetitionRecovery({
|
|
244328
|
+
retryInput,
|
|
244329
|
+
params: chatParams,
|
|
244330
|
+
stepEvents,
|
|
244331
|
+
signal,
|
|
244332
|
+
log
|
|
244330
244333
|
});
|
|
244331
244334
|
} catch (error) {
|
|
244332
244335
|
await stepEvents.drain();
|
|
@@ -244345,9 +244348,12 @@ async function executeLoopStep(deps) {
|
|
|
244345
244348
|
if (prepareRequestBoundary !== void 0) strictParams = await prepareRequestBoundary(strictParams, stepBuildMessagesStrict);
|
|
244346
244349
|
signal.throwIfAborted();
|
|
244347
244350
|
try {
|
|
244348
|
-
response = await
|
|
244349
|
-
|
|
244350
|
-
params: strictParams
|
|
244351
|
+
response = await chatWithLiveResponseRepetitionRecovery({
|
|
244352
|
+
retryInput,
|
|
244353
|
+
params: strictParams,
|
|
244354
|
+
stepEvents,
|
|
244355
|
+
signal,
|
|
244356
|
+
log
|
|
244351
244357
|
});
|
|
244352
244358
|
} catch (strictError) {
|
|
244353
244359
|
log?.error("strict resend still rejected by provider; request remains wire-invalid", {
|
|
@@ -244448,12 +244454,52 @@ function stepEndProviderDiagnostics(response, stopReason) {
|
|
|
244448
244454
|
...response.rawFinishReason !== void 0 ? { rawFinishReason: response.rawFinishReason } : {}
|
|
244449
244455
|
};
|
|
244450
244456
|
}
|
|
244457
|
+
var { createLiveResponseRepetitionGuard } = createRequire(import.meta.url)("./bin/live-response-repetition-guard.cjs");
|
|
244458
|
+
async function chatWithLiveResponseRepetitionRecovery(deps) {
|
|
244459
|
+
const { retryInput, params, stepEvents, signal, log } = deps;
|
|
244460
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
244461
|
+
try {
|
|
244462
|
+
return await chatWithRetry({
|
|
244463
|
+
...retryInput,
|
|
244464
|
+
params: {
|
|
244465
|
+
...params,
|
|
244466
|
+
signal: stepEvents.beginLiveResponseAttempt(signal)
|
|
244467
|
+
}
|
|
244468
|
+
});
|
|
244469
|
+
} catch (error) {
|
|
244470
|
+
await stepEvents.drain();
|
|
244471
|
+
const repetition = stepEvents.liveRepetitionResult;
|
|
244472
|
+
if (repetition === null || signal.aborted || attempt >= 2) throw error;
|
|
244473
|
+
log?.warn("live response repetition detected; retrying step once", {
|
|
244474
|
+
charCount: repetition.charCount,
|
|
244475
|
+
occurrences: repetition.count,
|
|
244476
|
+
wordCount: repetition.wordCount
|
|
244477
|
+
});
|
|
244478
|
+
await stepEvents.dispatchRetrying({
|
|
244479
|
+
type: "step.retrying",
|
|
244480
|
+
turnId: retryInput.turnId,
|
|
244481
|
+
step: retryInput.currentStep,
|
|
244482
|
+
stepUuid: retryInput.stepUuid,
|
|
244483
|
+
failedAttempt: 1,
|
|
244484
|
+
nextAttempt: 2,
|
|
244485
|
+
maxAttempts: 2,
|
|
244486
|
+
delayMs: 0,
|
|
244487
|
+
errorName: "LiveResponseRepetitionError",
|
|
244488
|
+
errorMessage: "Repeated assistant text detected during streaming"
|
|
244489
|
+
});
|
|
244490
|
+
}
|
|
244491
|
+
}
|
|
244492
|
+
throw new Error("Live response repetition recovery exhausted");
|
|
244493
|
+
}
|
|
244451
244494
|
function createStepEventGate(deps) {
|
|
244452
244495
|
const { dispatchEvent, turnId, currentStep, stepUuid, onStepStarted } = deps;
|
|
244453
244496
|
let startPromise;
|
|
244454
244497
|
let eventQueue = Promise.resolve();
|
|
244455
244498
|
let hasOutput = false;
|
|
244456
244499
|
const pendingBeforeStart = [];
|
|
244500
|
+
let liveRepetitionGuard;
|
|
244501
|
+
let liveRepetitionController;
|
|
244502
|
+
let liveRepetitionResult = null;
|
|
244457
244503
|
const start = () => {
|
|
244458
244504
|
startPromise ??= (async () => {
|
|
244459
244505
|
await dispatchEvent({
|
|
@@ -244481,6 +244527,15 @@ function createStepEventGate(deps) {
|
|
|
244481
244527
|
get hasOutput() {
|
|
244482
244528
|
return hasOutput;
|
|
244483
244529
|
},
|
|
244530
|
+
get liveRepetitionResult() {
|
|
244531
|
+
return liveRepetitionResult;
|
|
244532
|
+
},
|
|
244533
|
+
beginLiveResponseAttempt: (signal) => {
|
|
244534
|
+
liveRepetitionGuard = createLiveResponseRepetitionGuard();
|
|
244535
|
+
liveRepetitionController = new AbortController();
|
|
244536
|
+
liveRepetitionResult = null;
|
|
244537
|
+
return AbortSignal.any([signal, liveRepetitionController.signal]);
|
|
244538
|
+
},
|
|
244484
244539
|
dispatchRetrying: async (event) => {
|
|
244485
244540
|
if (startPromise === void 0) {
|
|
244486
244541
|
pendingBeforeStart.push(() => {
|
|
@@ -244498,6 +244553,12 @@ function createStepEventGate(deps) {
|
|
|
244498
244553
|
drain: async () => eventQueue,
|
|
244499
244554
|
callbacks: {
|
|
244500
244555
|
onTextDelta: (delta) => {
|
|
244556
|
+
const repetition = liveRepetitionGuard?.push(delta) ?? null;
|
|
244557
|
+
if (repetition !== null) {
|
|
244558
|
+
liveRepetitionResult = repetition;
|
|
244559
|
+
liveRepetitionController?.abort(/* @__PURE__ */ new Error("Repeated assistant text detected during streaming"));
|
|
244560
|
+
return;
|
|
244561
|
+
}
|
|
244501
244562
|
enqueue(() => {
|
|
244502
244563
|
dispatchEvent({
|
|
244503
244564
|
type: "text.delta",
|
|
@@ -514643,6 +514704,7 @@ function outboxDeliveredFile(marker, chatId, filePath) {
|
|
|
514643
514704
|
}
|
|
514644
514705
|
var retryTelegramMediaDelivery, retryableTelegramMediaFailure;
|
|
514645
514706
|
({ retryTelegramMediaDelivery, retryableTelegramMediaFailure } = createRequire(import.meta.url)("./bin/telegram-media-delivery-policy.cjs"));
|
|
514707
|
+
var { isPrivateInternalStatusReply } = createRequire(import.meta.url)("./bin/telegram-private-conversation-policy.cjs");
|
|
514646
514708
|
const TELEGRAM_TEXT_LIMIT = 4096;
|
|
514647
514709
|
const TELEGRAM_ATTACHMENT_LIMIT = 50 * 1024 * 1024;
|
|
514648
514710
|
function mediaTelegramTarget(filePath) {
|
|
@@ -514719,6 +514781,18 @@ function isGroupSuppressed(text, contextOnly) {
|
|
|
514719
514781
|
* kind "reply-fallback". Returns true on success. Never throws.
|
|
514720
514782
|
*/
|
|
514721
514783
|
async function sendReplyFallback(chatId, text, contextOnly = false) {
|
|
514784
|
+
if (isPrivateInternalStatusReply(chatId, text)) {
|
|
514785
|
+
try {
|
|
514786
|
+
appendFileSync(outboxPath(), `${JSON.stringify({
|
|
514787
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
514788
|
+
direction: "out",
|
|
514789
|
+
kind: "reply-fallback-suppressed-private-internal",
|
|
514790
|
+
chat_id: String(chatId),
|
|
514791
|
+
text
|
|
514792
|
+
})}\n`);
|
|
514793
|
+
} catch {}
|
|
514794
|
+
return true;
|
|
514795
|
+
}
|
|
514722
514796
|
if (isGroupChat(chatId) && isGroupSuppressed(text, contextOnly)) {
|
|
514723
514797
|
try {
|
|
514724
514798
|
appendFileSync(outboxPath(), `${JSON.stringify({
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import { A as _enum, B as object, F as discriminatedUnion, G as union, H as prep
|
|
|
2
2
|
import process$1 from "node:process";
|
|
3
3
|
import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { extname, join } from "node:path";
|
|
5
|
+
import privateConversationPolicy from "../../bin/telegram-private-conversation-policy.cjs";
|
|
5
6
|
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
6
7
|
function isZ4Schema(s) {
|
|
7
8
|
return !!s._zod;
|
|
@@ -9666,6 +9667,7 @@ var StdioServerTransport = class {
|
|
|
9666
9667
|
* chats the inbound gate would deliver from.
|
|
9667
9668
|
*/
|
|
9668
9669
|
var import_out = require_out();
|
|
9670
|
+
const { isPrivateInternalStatusReply } = privateConversationPolicy;
|
|
9669
9671
|
const TOOL_DEFINITIONS = [
|
|
9670
9672
|
{
|
|
9671
9673
|
name: "reply",
|
|
@@ -9843,6 +9845,15 @@ async function runReply(api, args) {
|
|
|
9843
9845
|
const replyTo = args.reply_to != null ? Number(args.reply_to) : void 0;
|
|
9844
9846
|
const files = args.files ?? [];
|
|
9845
9847
|
const parseMode = args.format === "markdownv2" ? "MarkdownV2" : void 0;
|
|
9848
|
+
if (files.length === 0 && isPrivateInternalStatusReply(chatId, text)) {
|
|
9849
|
+
appendJsonl(outboxLog(), {
|
|
9850
|
+
direction: "out",
|
|
9851
|
+
kind: "reply-suppressed-private-internal",
|
|
9852
|
+
chat_id: chatId,
|
|
9853
|
+
text
|
|
9854
|
+
});
|
|
9855
|
+
return "suppressed: internal work-control narration does not belong in a private conversation";
|
|
9856
|
+
}
|
|
9846
9857
|
if (files.length === 0 && isGroupChat(chatId) && isGroupNoiseReply(text)) {
|
|
9847
9858
|
appendJsonl(outboxLog(), {
|
|
9848
9859
|
direction: "out",
|