blun-king-cli 9.1.415 → 9.1.417
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/LIESMICH.txt +16 -0
- package/README.md +17 -0
- package/bin/telegram-text-chunk-policy.cjs +63 -0
- package/bin/tool-result-offload-policy.cjs +29 -0
- package/blun.mjs +25 -23
- package/package.json +1 -1
- package/telegram-plugin/bin/telegram-text-chunk-policy.cjs +63 -0
- package/telegram-plugin/dist/bridge.mjs +18 -12
package/LIESMICH.txt
CHANGED
|
@@ -59,6 +59,22 @@ konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
|
|
|
59
59
|
FIFO-Warteschlange. Für diesen normalen Wartestatus erscheint kein
|
|
60
60
|
`turn.agent_busy`-Fehler.
|
|
61
61
|
|
|
62
|
+
Ab BLUN King 9.1.416 bleiben alle Ergebnisse des jüngsten Werkzeugaufrufs bis
|
|
63
|
+
zur nächsten Assistentenantwort vollständig im Modellkontext. Das gilt auch für
|
|
64
|
+
große parallele Read-, Grep- und Bash-Aufrufe sowie für nachträglich
|
|
65
|
+
eingespielte Telegram- oder Steuerungsnachrichten. Offloader, historische
|
|
66
|
+
Bereinigung und Mikrokompaktierung verwenden dafür dieselbe semantische Grenze.
|
|
67
|
+
Erst nach der nachweislichen Auswertung darf ein Ergebnis archiviert oder
|
|
68
|
+
gekürzt werden.
|
|
69
|
+
|
|
70
|
+
Ab BLUN King 9.1.417 schneiden automatische Telegram-Rückfallantworten Texte
|
|
71
|
+
nicht mehr bei 4.096 Zeichen ab. Längere Antworten werden in
|
|
72
|
+
aufeinanderfolgenden Nachrichten vollständig zugestellt; dabei bleiben jedes
|
|
73
|
+
Zeichen und jedes Unicode-Surrogatpaar erhalten. Im Ausgangsprotokoll stehen
|
|
74
|
+
sämtliche zurückgegebenen Nachrichten-IDs zusammen mit dem vollständigen Text.
|
|
75
|
+
Schlägt ein Teil fehl, endet die Zustellung an dieser Stelle, statt die Antwort
|
|
76
|
+
fälschlich als vollständig zu melden.
|
|
77
|
+
|
|
62
78
|
Zuverlässiger King-Start
|
|
63
79
|
-----------------------
|
|
64
80
|
Bei einer ausdrücklich als wiederholbar gekennzeichneten Serverüberlastung
|
package/README.md
CHANGED
|
@@ -73,6 +73,23 @@ Telegram-Nachrichten, die bei bereits aktivem Kernzug eintreffen, werden ohne
|
|
|
73
73
|
konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
|
|
74
74
|
FIFO-Warteschlange. Für diesen normalen Wartestatus erscheint kein
|
|
75
75
|
`turn.agent_busy`-Fehler.
|
|
76
|
+
|
|
77
|
+
Ab BLUN King 9.1.416 bleiben alle Ergebnisse des jüngsten Werkzeugaufrufs bis
|
|
78
|
+
zur nächsten Assistentenantwort vollständig im Modellkontext. Das gilt auch für
|
|
79
|
+
große parallele Read-, Grep- und Bash-Aufrufe sowie für nachträglich
|
|
80
|
+
eingespielte Telegram- oder Steuerungsnachrichten. Offloader, historische
|
|
81
|
+
Bereinigung und Mikrokompaktierung verwenden dafür dieselbe semantische Grenze.
|
|
82
|
+
Erst nach der nachweislichen Auswertung darf ein Ergebnis archiviert oder
|
|
83
|
+
gekürzt werden.
|
|
84
|
+
|
|
85
|
+
Ab BLUN King 9.1.417 schneiden automatische Telegram-Rückfallantworten Texte
|
|
86
|
+
nicht mehr bei 4.096 Zeichen ab. Längere Antworten werden in
|
|
87
|
+
aufeinanderfolgenden Nachrichten vollständig zugestellt; dabei bleiben jedes
|
|
88
|
+
Zeichen und jedes Unicode-Surrogatpaar erhalten. Im Ausgangsprotokoll stehen
|
|
89
|
+
sämtliche zurückgegebenen Nachrichten-IDs zusammen mit dem vollständigen Text.
|
|
90
|
+
Schlägt ein Teil fehl, endet die Zustellung an dieser Stelle, statt die Antwort
|
|
91
|
+
fälschlich als vollständig zu melden.
|
|
92
|
+
|
|
76
93
|
`Strg+C` und `Esc` brechen einen aktiven Zug zuverlässig ab, ohne den bereits
|
|
77
94
|
geschriebenen Entwurf zu löschen. Das gilt auch bei Autovervollständigung,
|
|
78
95
|
Geistervorschlägen, Bash-Eingabe und einer noch offenen Mehrzeileneingabe.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TELEGRAM_TEXT_LIMIT = 4096;
|
|
4
|
+
|
|
5
|
+
function preferredCut(text, start, hardEnd) {
|
|
6
|
+
const minimum = start + Math.floor((hardEnd - start) / 2);
|
|
7
|
+
const boundaries = [
|
|
8
|
+
['\n\n', 2],
|
|
9
|
+
['\n', 1],
|
|
10
|
+
[' ', 1],
|
|
11
|
+
];
|
|
12
|
+
for (const [boundary, width] of boundaries) {
|
|
13
|
+
const index = text.lastIndexOf(boundary, hardEnd - 1);
|
|
14
|
+
if (index >= minimum) return index + width;
|
|
15
|
+
}
|
|
16
|
+
return hardEnd;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function preserveSurrogatePair(text, start, end) {
|
|
20
|
+
if (end <= start || end >= text.length) return end;
|
|
21
|
+
const before = text.charCodeAt(end - 1);
|
|
22
|
+
const after = text.charCodeAt(end);
|
|
23
|
+
const splitsPair = before >= 0xD800 && before <= 0xDBFF && after >= 0xDC00 && after <= 0xDFFF;
|
|
24
|
+
return splitsPair && end - 1 > start ? end - 1 : end;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function splitTelegramText(value, limit = TELEGRAM_TEXT_LIMIT) {
|
|
28
|
+
const text = typeof value === 'string' ? value : String(value ?? '');
|
|
29
|
+
if (text.length === 0) return [];
|
|
30
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
31
|
+
throw new TypeError('Telegram text chunk limit must be a positive safe integer');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const chunks = [];
|
|
35
|
+
let start = 0;
|
|
36
|
+
while (start < text.length) {
|
|
37
|
+
const hardEnd = Math.min(text.length, start + limit);
|
|
38
|
+
let end = hardEnd < text.length ? preferredCut(text, start, hardEnd) : hardEnd;
|
|
39
|
+
end = preserveSurrogatePair(text, start, end);
|
|
40
|
+
if (end <= start) end = hardEnd;
|
|
41
|
+
chunks.push(text.slice(start, end));
|
|
42
|
+
start = end;
|
|
43
|
+
}
|
|
44
|
+
return chunks;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function sendTelegramTextChunks(value, sendChunk, limit = TELEGRAM_TEXT_LIMIT) {
|
|
48
|
+
if (typeof sendChunk !== 'function') {
|
|
49
|
+
throw new TypeError('Telegram chunk sender must be a function');
|
|
50
|
+
}
|
|
51
|
+
const chunks = splitTelegramText(value, limit);
|
|
52
|
+
const results = [];
|
|
53
|
+
for (let index = 0; index < chunks.length; index += 1) {
|
|
54
|
+
results.push(await sendChunk(chunks[index], index, chunks.length));
|
|
55
|
+
}
|
|
56
|
+
return { chunks, results };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
TELEGRAM_TEXT_LIMIT,
|
|
61
|
+
sendTelegramTextChunks,
|
|
62
|
+
splitTelegramText,
|
|
63
|
+
};
|
|
@@ -43,6 +43,32 @@ function isPersistedToolResultReference(content) {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function freshToolResultIds(messages) {
|
|
47
|
+
if (!Array.isArray(messages) || messages.length === 0) return new Set();
|
|
48
|
+
|
|
49
|
+
let assistantIndex = -1;
|
|
50
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
51
|
+
if (messages[index]?.role !== 'assistant') continue;
|
|
52
|
+
assistantIndex = index;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (assistantIndex < 0 || !Array.isArray(messages[assistantIndex].toolCalls)) return new Set();
|
|
56
|
+
|
|
57
|
+
const requestedIds = new Set(messages[assistantIndex].toolCalls
|
|
58
|
+
.map((call) => call?.id ?? call?.toolCallId)
|
|
59
|
+
.filter((id) => typeof id === 'string' && id.length > 0));
|
|
60
|
+
if (requestedIds.size === 0) return new Set();
|
|
61
|
+
|
|
62
|
+
const resultIds = new Set();
|
|
63
|
+
for (let index = assistantIndex + 1; index < messages.length; index += 1) {
|
|
64
|
+
const message = messages[index];
|
|
65
|
+
if (message?.role === 'tool' && requestedIds.has(message.toolCallId)) {
|
|
66
|
+
resultIds.add(message.toolCallId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return resultIds;
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
function compactPersistedToolResultReference(content) {
|
|
47
73
|
if (!isPersistedToolResultReference(content)) return content;
|
|
48
74
|
|
|
@@ -84,11 +110,13 @@ function compactHistoricalSuccessfulToolResults(messages) {
|
|
|
84
110
|
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
85
111
|
|
|
86
112
|
const recentStart = Math.max(0, messages.length - TOOL_RESULT_SUCCESS_KEEP_RECENT_MESSAGES);
|
|
113
|
+
const freshIds = freshToolResultIds(messages);
|
|
87
114
|
let changed = false;
|
|
88
115
|
const projected = messages.map((message, index) => {
|
|
89
116
|
if (
|
|
90
117
|
index >= recentStart
|
|
91
118
|
|| message?.role !== 'tool'
|
|
119
|
+
|| freshIds.has(message.toolCallId)
|
|
92
120
|
|| message.isError === true
|
|
93
121
|
|| !Array.isArray(message.content)
|
|
94
122
|
|| isPersistedToolResultReference(message.content)
|
|
@@ -268,6 +296,7 @@ module.exports = {
|
|
|
268
296
|
compactPersistedToolResultReference,
|
|
269
297
|
createToolResultPreview,
|
|
270
298
|
dedupeRepeatedSuccessfulToolResults,
|
|
299
|
+
freshToolResultIds,
|
|
271
300
|
isPersistedToolResultReference,
|
|
272
301
|
selectHistoricalToolResultOffloads,
|
|
273
302
|
selectToolResultBatchOffloads,
|
package/blun.mjs
CHANGED
|
@@ -75994,10 +75994,10 @@ var init_full = __esmMin((() => {
|
|
|
75994
75994
|
}));
|
|
75995
75995
|
//#endregion
|
|
75996
75996
|
//#region ../../packages/agent-core/src/agent/compaction/micro.ts
|
|
75997
|
-
var selectMicroCompactionCutoff, MICRO_COMPACTION_RECENT_MESSAGES, MICRO_COMPACTION_TOOL_ARGUMENTS_ENABLED, MICRO_COMPACTION_TOOL_ARGUMENT_MIN_TOKENS, isPersistedToolResultReference, projectHistoricalUnaddressedTelegramMessages, dedupeRepeatedUserMessages, projectRepeatedAssistantResponses, compactHistoricalSkillActivations, dedupeRecurringCronWakeups, dedupeRepeatedInjections, projectLoopEventForRecord, projectUsageModelForRecord, projectUsageForRecord, resolveCompletedStepUuid, resolveStepEventUuid, restoreUsageFromRecord, restoreUsageModelFromRecord, DEFAULT_CONFIG, MicroCompaction;
|
|
75997
|
+
var selectMicroCompactionCutoff, MICRO_COMPACTION_RECENT_MESSAGES, MICRO_COMPACTION_TOOL_ARGUMENTS_ENABLED, MICRO_COMPACTION_TOOL_ARGUMENT_MIN_TOKENS, isPersistedToolResultReference, freshToolResultIds, projectHistoricalUnaddressedTelegramMessages, dedupeRepeatedUserMessages, projectRepeatedAssistantResponses, compactHistoricalSkillActivations, dedupeRecurringCronWakeups, dedupeRepeatedInjections, projectLoopEventForRecord, projectUsageModelForRecord, projectUsageForRecord, resolveCompletedStepUuid, resolveStepEventUuid, restoreUsageFromRecord, restoreUsageModelFromRecord, DEFAULT_CONFIG, MicroCompaction;
|
|
75998
75998
|
var init_micro = __esmMin((() => {
|
|
75999
75999
|
({ selectMicroCompactionCutoff, MICRO_COMPACTION_RECENT_MESSAGES, MICRO_COMPACTION_TOOL_ARGUMENTS_ENABLED, MICRO_COMPACTION_TOOL_ARGUMENT_MIN_TOKENS } = createRequire(import.meta.url)("./bin/micro-compaction-policy.cjs"));
|
|
76000
|
-
({ isPersistedToolResultReference } = createRequire(import.meta.url)("./bin/tool-result-offload-policy.cjs"));
|
|
76000
|
+
({ isPersistedToolResultReference, freshToolResultIds } = createRequire(import.meta.url)("./bin/tool-result-offload-policy.cjs"));
|
|
76001
76001
|
({ projectHistoricalUnaddressedTelegramMessages } = createRequire(import.meta.url)("./bin/telegram-context-projection-policy.cjs"));
|
|
76002
76002
|
({ dedupeRepeatedUserMessages } = createRequire(import.meta.url)("./bin/repeated-user-message-projection.cjs"));
|
|
76003
76003
|
({ projectRepeatedAssistantResponses } = createRequire(import.meta.url)("./bin/repeated-assistant-response-policy.cjs"));
|
|
@@ -76093,11 +76093,12 @@ var init_micro = __esmMin((() => {
|
|
|
76093
76093
|
const config = this.config;
|
|
76094
76094
|
const historicalArgumentCutoff = Math.max(this.cutoff, messages.length - config.keepRecentMessages);
|
|
76095
76095
|
const completedToolCallIds = new Set(messages.filter((message) => message?.role === "tool" && message.toolCallId !== void 0).map((message) => message.toolCallId));
|
|
76096
|
+
const protectedToolResultIds = freshToolResultIds(messages);
|
|
76096
76097
|
const serializedArgumentsMarker = JSON.stringify({ _blun_compacted: config.truncatedArgumentsMarker });
|
|
76097
76098
|
const result = [];
|
|
76098
76099
|
let i = 0;
|
|
76099
76100
|
for (const msg of messages) {
|
|
76100
|
-
if (i < this.cutoff && msg.role === "tool" && msg.toolCallId !== void 0 && !isPersistedToolResultReference(msg.content) && estimateTokensForContentParts(msg.content) >= config.minContentTokens) result.push({
|
|
76101
|
+
if (i < this.cutoff && msg.role === "tool" && msg.toolCallId !== void 0 && !protectedToolResultIds.has(msg.toolCallId) && !isPersistedToolResultReference(msg.content) && estimateTokensForContentParts(msg.content) >= config.minContentTokens) result.push({
|
|
76101
76102
|
...msg,
|
|
76102
76103
|
content: [{
|
|
76103
76104
|
type: "text",
|
|
@@ -76139,10 +76140,11 @@ var init_micro = __esmMin((() => {
|
|
|
76139
76140
|
let truncatedToolArgumentTokensBefore = 0;
|
|
76140
76141
|
let truncatedToolArgumentTokensAfter = 0;
|
|
76141
76142
|
const completedToolCallIds = new Set(messages.filter((message) => message?.role === "tool" && message.toolCallId !== void 0).map((message) => message.toolCallId));
|
|
76143
|
+
const protectedToolResultIds = freshToolResultIds(messages);
|
|
76142
76144
|
const serializedArgumentsMarker = JSON.stringify({ _blun_compacted: this.config.truncatedArgumentsMarker });
|
|
76143
76145
|
for (let i = 0; i < messages.length && i < cutoff; i++) {
|
|
76144
76146
|
const message = messages[i];
|
|
76145
|
-
if (message?.role === "tool" && message.toolCallId !== void 0 && !isPersistedToolResultReference(message.content)) {
|
|
76147
|
+
if (message?.role === "tool" && message.toolCallId !== void 0 && !protectedToolResultIds.has(message.toolCallId) && !isPersistedToolResultReference(message.content)) {
|
|
76146
76148
|
const contentTokens = estimateTokensForContentParts(message.content);
|
|
76147
76149
|
if (contentTokens >= this.config.minContentTokens) {
|
|
76148
76150
|
markerTokenCount ??= estimateTokensForContentParts([{
|
|
@@ -260827,6 +260829,7 @@ var init_tool_result_budget = __esmMin((() => {
|
|
|
260827
260829
|
compactPersistedToolResultReference = toolResultOffloadPolicy.compactPersistedToolResultReference;
|
|
260828
260830
|
compactHistoricalSuccessfulToolResults = toolResultOffloadPolicy.compactHistoricalSuccessfulToolResults;
|
|
260829
260831
|
dedupeRepeatedSuccessfulToolResults = toolResultOffloadPolicy.dedupeRepeatedSuccessfulToolResults;
|
|
260832
|
+
freshToolResultIds = toolResultOffloadPolicy.freshToolResultIds;
|
|
260830
260833
|
selectToolResultBatchOffloads = toolResultOffloadPolicy.selectToolResultBatchOffloads;
|
|
260831
260834
|
selectHistoricalToolResultOffloads = toolResultOffloadPolicy.selectHistoricalToolResultOffloads;
|
|
260832
260835
|
}));
|
|
@@ -260840,13 +260843,7 @@ var ToolResultBatchOffload = class {
|
|
|
260840
260843
|
}
|
|
260841
260844
|
async detect() {
|
|
260842
260845
|
const history = this.agent.context.history;
|
|
260843
|
-
const
|
|
260844
|
-
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
260845
|
-
const message = history[index];
|
|
260846
|
-
if (message?.role !== "tool") break;
|
|
260847
|
-
tail.unshift(message);
|
|
260848
|
-
}
|
|
260849
|
-
const tailIds = new Set(tail.map((message) => message.toolCallId).filter((id) => id !== void 0));
|
|
260846
|
+
const tailIds = freshToolResultIds(history);
|
|
260850
260847
|
const recentStart = Math.max(0, history.length - TOOL_RESULT_HISTORICAL_KEEP_RECENT_MESSAGES);
|
|
260851
260848
|
const historical = history.slice(0, recentStart).filter((message) => message?.role === "tool" && !tailIds.has(message.toolCallId));
|
|
260852
260849
|
const candidateBatches = [{
|
|
@@ -514833,6 +514830,7 @@ var createMediaAutoRetrievalController, parseAcceptedMediaJob, validateCompleted
|
|
|
514833
514830
|
({ createMediaAutoRetrievalController, parseAcceptedMediaJob } = createRequire(import.meta.url)("./bin/media-auto-retrieval-policy.cjs"));
|
|
514834
514831
|
({ validateCompletedMedia } = createRequire(import.meta.url)("./bin/media-result-policy.cjs"));
|
|
514835
514832
|
var { findDuplicateReplyWithoutNewInbound, isPrivateInternalStatusReply, sanitizePrivateConversationReply } = createRequire(import.meta.url)("./bin/telegram-private-conversation-policy.cjs");
|
|
514833
|
+
var { sendTelegramTextChunks } = createRequire(import.meta.url)("./bin/telegram-text-chunk-policy.cjs");
|
|
514836
514834
|
const TELEGRAM_TEXT_LIMIT = 4096;
|
|
514837
514835
|
const TELEGRAM_ATTACHMENT_LIMIT = 50 * 1024 * 1024;
|
|
514838
514836
|
function mediaTelegramTarget(filePath) {
|
|
@@ -514973,25 +514971,29 @@ async function sendReplyFallback(chatId, text, contextOnly = false) {
|
|
|
514973
514971
|
}
|
|
514974
514972
|
const token = botToken();
|
|
514975
514973
|
if (token === void 0) return false;
|
|
514976
|
-
const
|
|
514974
|
+
const messageIds = [];
|
|
514977
514975
|
try {
|
|
514978
|
-
|
|
514979
|
-
|
|
514980
|
-
|
|
514981
|
-
|
|
514982
|
-
|
|
514983
|
-
|
|
514984
|
-
|
|
514985
|
-
|
|
514986
|
-
|
|
514976
|
+
await sendTelegramTextChunks(privateSafeText, async (body) => {
|
|
514977
|
+
const payload = await (await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
|
|
514978
|
+
method: "POST",
|
|
514979
|
+
headers: { "Content-Type": "application/json" },
|
|
514980
|
+
body: JSON.stringify({
|
|
514981
|
+
chat_id: chatId,
|
|
514982
|
+
text: body
|
|
514983
|
+
})
|
|
514984
|
+
})).json();
|
|
514985
|
+
if (payload.ok !== true) throw new Error("Telegram fallback chunk delivery failed");
|
|
514986
|
+
if (payload.result?.message_id !== void 0) messageIds.push(payload.result.message_id);
|
|
514987
|
+
return payload;
|
|
514988
|
+
}, TELEGRAM_TEXT_LIMIT);
|
|
514987
514989
|
try {
|
|
514988
514990
|
appendFileSync(outboxPath(), `${JSON.stringify({
|
|
514989
514991
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
514990
514992
|
direction: "out",
|
|
514991
514993
|
kind: "reply-fallback",
|
|
514992
514994
|
chat_id: String(chatId),
|
|
514993
|
-
message_ids:
|
|
514994
|
-
text:
|
|
514995
|
+
message_ids: messageIds,
|
|
514996
|
+
text: privateSafeText
|
|
514995
514997
|
})}\n`);
|
|
514996
514998
|
} catch {}
|
|
514997
514999
|
return true;
|
package/package.json
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TELEGRAM_TEXT_LIMIT = 4096;
|
|
4
|
+
|
|
5
|
+
function preferredCut(text, start, hardEnd) {
|
|
6
|
+
const minimum = start + Math.floor((hardEnd - start) / 2);
|
|
7
|
+
const boundaries = [
|
|
8
|
+
['\n\n', 2],
|
|
9
|
+
['\n', 1],
|
|
10
|
+
[' ', 1],
|
|
11
|
+
];
|
|
12
|
+
for (const [boundary, width] of boundaries) {
|
|
13
|
+
const index = text.lastIndexOf(boundary, hardEnd - 1);
|
|
14
|
+
if (index >= minimum) return index + width;
|
|
15
|
+
}
|
|
16
|
+
return hardEnd;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function preserveSurrogatePair(text, start, end) {
|
|
20
|
+
if (end <= start || end >= text.length) return end;
|
|
21
|
+
const before = text.charCodeAt(end - 1);
|
|
22
|
+
const after = text.charCodeAt(end);
|
|
23
|
+
const splitsPair = before >= 0xD800 && before <= 0xDBFF && after >= 0xDC00 && after <= 0xDFFF;
|
|
24
|
+
return splitsPair && end - 1 > start ? end - 1 : end;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function splitTelegramText(value, limit = TELEGRAM_TEXT_LIMIT) {
|
|
28
|
+
const text = typeof value === 'string' ? value : String(value ?? '');
|
|
29
|
+
if (text.length === 0) return [];
|
|
30
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
31
|
+
throw new TypeError('Telegram text chunk limit must be a positive safe integer');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const chunks = [];
|
|
35
|
+
let start = 0;
|
|
36
|
+
while (start < text.length) {
|
|
37
|
+
const hardEnd = Math.min(text.length, start + limit);
|
|
38
|
+
let end = hardEnd < text.length ? preferredCut(text, start, hardEnd) : hardEnd;
|
|
39
|
+
end = preserveSurrogatePair(text, start, end);
|
|
40
|
+
if (end <= start) end = hardEnd;
|
|
41
|
+
chunks.push(text.slice(start, end));
|
|
42
|
+
start = end;
|
|
43
|
+
}
|
|
44
|
+
return chunks;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function sendTelegramTextChunks(value, sendChunk, limit = TELEGRAM_TEXT_LIMIT) {
|
|
48
|
+
if (typeof sendChunk !== 'function') {
|
|
49
|
+
throw new TypeError('Telegram chunk sender must be a function');
|
|
50
|
+
}
|
|
51
|
+
const chunks = splitTelegramText(value, limit);
|
|
52
|
+
const results = [];
|
|
53
|
+
for (let index = 0; index < chunks.length; index += 1) {
|
|
54
|
+
results.push(await sendChunk(chunks[index], index, chunks.length));
|
|
55
|
+
}
|
|
56
|
+
return { chunks, results };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
TELEGRAM_TEXT_LIMIT,
|
|
61
|
+
sendTelegramTextChunks,
|
|
62
|
+
splitTelegramText,
|
|
63
|
+
};
|
|
@@ -10,10 +10,12 @@ import remoteStatusPolicy from "../../bin/telegram-remote-status-policy.cjs";
|
|
|
10
10
|
import consoleStatusPolicy from "../../bin/telegram-console-status-policy.cjs";
|
|
11
11
|
import telegramApprovalRelay from "../../bin/telegram-approval-relay.cjs";
|
|
12
12
|
import privateConversationPolicy from "../bin/telegram-private-conversation-policy.cjs";
|
|
13
|
+
import telegramTextChunkPolicy from "../bin/telegram-text-chunk-policy.cjs";
|
|
13
14
|
const { buildTelegramRemoteStatus, resolveTelegramQueueSnapshot, resolveTelegramRemoteVersion } = remoteStatusPolicy;
|
|
14
15
|
const { parseTelegramConsoleStatus } = consoleStatusPolicy;
|
|
15
16
|
const { buildTelegramApprovalCard, isAuthorizedTelegramApprovalCallback, listPendingTelegramApprovals, markTelegramApprovalSent, parseTelegramApprovalCallback, resolveTelegramApprovalTarget, wasTelegramApprovalSent, writeTelegramApprovalResponse } = telegramApprovalRelay;
|
|
16
17
|
const { findDuplicateReplyWithoutNewInbound, isPrivateInternalStatusReply, sanitizePrivateConversationReply } = privateConversationPolicy;
|
|
18
|
+
const { sendTelegramTextChunks } = telegramTextChunkPolicy;
|
|
17
19
|
//#region ../../node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
|
|
18
20
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
19
21
|
function normalizeWindowsPath(input = "") {
|
|
@@ -4223,25 +4225,29 @@ async function sendReplyFallback(chatId, rawText) {
|
|
|
4223
4225
|
return true;
|
|
4224
4226
|
}
|
|
4225
4227
|
const text = scrubSecrets(privateSafeText);
|
|
4226
|
-
const
|
|
4228
|
+
const messageIds = [];
|
|
4227
4229
|
try {
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4230
|
+
await sendTelegramTextChunks(text, async (body) => {
|
|
4231
|
+
const payload = await (await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
|
|
4232
|
+
method: "POST",
|
|
4233
|
+
headers: { "Content-Type": "application/json" },
|
|
4234
|
+
body: JSON.stringify({
|
|
4235
|
+
chat_id: chatId,
|
|
4236
|
+
text: body
|
|
4237
|
+
})
|
|
4238
|
+
})).json();
|
|
4239
|
+
if (payload.ok !== true) throw new Error("Telegram fallback chunk delivery failed");
|
|
4240
|
+
if (payload.result?.message_id !== void 0) messageIds.push(payload.result.message_id);
|
|
4241
|
+
return payload;
|
|
4242
|
+
}, TELEGRAM_TEXT_LIMIT);
|
|
4237
4243
|
try {
|
|
4238
4244
|
appendFileSync(outboxLog(), `${JSON.stringify({
|
|
4239
4245
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4240
4246
|
direction: "out",
|
|
4241
4247
|
kind: "reply-fallback",
|
|
4242
4248
|
chat_id: String(chatId),
|
|
4243
|
-
message_ids:
|
|
4244
|
-
text
|
|
4249
|
+
message_ids: messageIds,
|
|
4250
|
+
text
|
|
4245
4251
|
})}\n`);
|
|
4246
4252
|
} catch {}
|
|
4247
4253
|
return true;
|