blun-king-cli 9.1.497 → 9.1.499
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 +17 -0
- package/LIESMICH.txt +31 -1
- package/README.md +31 -1
- package/bin/historical-media-projection-policy.cjs +48 -0
- package/bin/usage-cache-efficiency-policy.cjs +26 -0
- package/blun.mjs +77 -9
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge.mjs +0 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 9.1.499 - 2026-08-30
|
|
4
|
+
|
|
5
|
+
- Replaces image, audio, and video payloads older than the newest twelve conversation messages with short model-facing references while preserving the complete original session wire and every recent media attachment.
|
|
6
|
+
- Reduces Fredrik's measured repeated model input by 85,748 characters, approximately 21,437 estimated tokens per subsequent work step, without changing the current image, message text, or persisted history.
|
|
7
|
+
- Runs overdue TodoList maintenance as its own low-effort model step before the ninth work batch and exposes only TodoList during that step, preventing the normal workflow from hitting a visible red policy error first.
|
|
8
|
+
- Keeps fresh user steers ahead of TodoList housekeeping while preserving the hard policy gate as a fallback for an actually invalid update.
|
|
9
|
+
|
|
10
|
+
## 9.1.498 - 2026-08-30
|
|
11
|
+
|
|
12
|
+
- Shows prompt-cache hit rate plus cache-read and cache-write tokens per model in `/usage`.
|
|
13
|
+
- Adds `/tokens` as a short alias for the existing detailed context doctor without introducing another model call.
|
|
14
|
+
- Adds `/offload` as an alias for the existing archive-backed context compaction command.
|
|
15
|
+
- Preserves every distinct authorized Telegram bot message instead of dropping same-sender messages that arrive within a fixed 15-second window.
|
|
16
|
+
- Preserves the exact TodoList through full compaction and adds a one-shot recovery reminder to read the archived history path before broad searches when an exact path, command, hash, or requirement is uncertain.
|
|
17
|
+
- Keeps completed TodoList evidence append-only: later uncertainty becomes a separate in-progress step instead of silently reverting or removing completed work.
|
|
18
|
+
- Defers recurring session-loop wakeups while the maintained TodoList still contains active or pending work, coalescing missed intervals until the assignment is genuinely idle without delaying unrelated scheduled reminders.
|
|
19
|
+
|
|
3
20
|
## 9.1.497 - 2026-08-30
|
|
4
21
|
|
|
5
22
|
- Prioritizes Telegram bot messages from shared groups behind direct and explicitly addressed traffic but ahead of ordinary queued context, without forcing a reply.
|
package/LIESMICH.txt
CHANGED
|
@@ -9,7 +9,7 @@ Installation
|
|
|
9
9
|
------------
|
|
10
10
|
Die geprüfte Version exakt global installieren:
|
|
11
11
|
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.499
|
|
13
13
|
|
|
14
14
|
Start
|
|
15
15
|
-----
|
|
@@ -65,6 +65,36 @@ geschriebenen Entwurf zu löschen. Das gilt auch bei
|
|
|
65
65
|
Autovervollständigung, Geistervorschlägen, Bash-Eingabe und einer offenen
|
|
66
66
|
Mehrzeileneingabe.
|
|
67
67
|
|
|
68
|
+
Version 9.1.499 ersetzt Bild-, Audio- und Videodaten ausserhalb der neuesten zwoelf
|
|
69
|
+
Gespraechsnachrichten ausschliesslich in der wiederholten Modellprojektion durch
|
|
70
|
+
kurze Verweise. Aktuelle Medien, Nachrichtentext und vollstaendiger Sitzungsrohverlauf
|
|
71
|
+
bleiben unveraendert. In Fredriks gemessenem Wiederaufnahme-Checkpoint spart der
|
|
72
|
+
Schnitt 85.748 Zeichen beziehungsweise rund 21.437 geschaetzte Eingabetoken je
|
|
73
|
+
weiterem Werkzeugschritt.
|
|
74
|
+
|
|
75
|
+
Faellige Todo-Pflege laeuft in 9.1.499 vor dem naechsten Sachwerkzeug als eigener
|
|
76
|
+
kleiner Modellschritt. In diesem Schritt bietet die Laufzeit ausschliesslich
|
|
77
|
+
TodoList an; erst nach einer gueltigen Aktualisierung werden Datei-, Such- und
|
|
78
|
+
Shell-Werkzeuge wieder freigegeben. Dadurch entsteht im normalen Ablauf kein
|
|
79
|
+
roter Zwischenfehler. Frische Benutzernachrichten behalten Vorrang.
|
|
80
|
+
|
|
81
|
+
Version 9.1.498 zeigt in /usage die gemessene Prompt-Cache-Trefferquote sowie
|
|
82
|
+
gelesene und geschriebene Cache-Token je Modell. /tokens ist ein kurzer Alias
|
|
83
|
+
fuer die bereits vorhandene detaillierte Kontextdiagnose; /offload ruft die
|
|
84
|
+
vorhandene archivierende Kontextverdichtung auf. Unterschiedliche autorisierte
|
|
85
|
+
Botnachrichten werden nicht mehr durch ein starres 15-Sekunden-Fenster verworfen.
|
|
86
|
+
Nach einer vollständigen Verdichtung bleibt die exakte Todo-Liste erhalten. Ein
|
|
87
|
+
einmaliger Wiederherstellungshinweis verlangt bei unsicheren Pfaden, Befehlen,
|
|
88
|
+
Hashes oder Anforderungen zuerst das Lesen des archivierten Verlaufs, bevor eine
|
|
89
|
+
breite Dateisuche oder ein Neubeginn zulässig ist.
|
|
90
|
+
Erledigte Todo-Schritte bleiben als Nachweis erhalten. Neue widersprüchliche
|
|
91
|
+
Erkenntnisse werden als eigener laufender Schritt mit Begründung ergänzt, statt
|
|
92
|
+
einen bereits belegten Abschluss still zurückzustufen oder zu entfernen.
|
|
93
|
+
Wiederkehrende Session-Loops warten, solange die gepflegte Todo-Liste noch
|
|
94
|
+
laufende oder ausstehende Arbeit enthält. Verpasste Intervalle werden bis zum
|
|
95
|
+
echten Leerlauf zusammengefasst; andere geplante Erinnerungen bleiben davon
|
|
96
|
+
unberührt.
|
|
97
|
+
|
|
68
98
|
Version 9.1.497 priorisiert Telegram-Botnachrichten aus gemeinsamen Gruppen
|
|
69
99
|
hinter privaten und ausdrücklich adressierten Nachrichten, aber vor normalem
|
|
70
100
|
Kontext. Die Priorisierung erzwingt keine Antwort. Eine angenommene
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Voraussetzung ist Node.js 24.15 oder neuer. Die geprüfte Version wird exakt
|
|
|
9
9
|
installiert:
|
|
10
10
|
|
|
11
11
|
```powershell
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.499
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Reproduzierbares Staging und Packen
|
|
@@ -81,6 +81,36 @@ konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
|
|
|
81
81
|
FIFO-Warteschlange. Für diesen normalen Wartestatus erscheint kein
|
|
82
82
|
`turn.agent_busy`-Fehler.
|
|
83
83
|
|
|
84
|
+
Version 9.1.499 ersetzt Bild-, Audio- und Videodaten außerhalb der neuesten zwölf
|
|
85
|
+
Gesprächsnachrichten ausschließlich in der wiederholten Modellprojektion durch
|
|
86
|
+
kurze Verweise. Aktuelle Medien, Nachrichtentext und vollständiger Sitzungsrohverlauf
|
|
87
|
+
bleiben unverändert. In Fredriks gemessenem Wiederaufnahme-Checkpoint spart der
|
|
88
|
+
Schnitt 85.748 Zeichen beziehungsweise rund 21.437 geschätzte Eingabetoken je
|
|
89
|
+
weiterem Werkzeugschritt.
|
|
90
|
+
|
|
91
|
+
Faellige Todo-Pflege laeuft in 9.1.499 vor dem naechsten Sachwerkzeug als eigener
|
|
92
|
+
kleiner Modellschritt. In diesem Schritt bietet die Laufzeit ausschliesslich
|
|
93
|
+
`TodoList` an; erst nach einer gueltigen Aktualisierung werden Datei-, Such- und
|
|
94
|
+
Shell-Werkzeuge wieder freigegeben. Dadurch entsteht im normalen Ablauf kein
|
|
95
|
+
roter Zwischenfehler. Frische Benutzernachrichten behalten Vorrang.
|
|
96
|
+
|
|
97
|
+
Version 9.1.498 zeigt in `/usage` die gemessene Prompt-Cache-Trefferquote sowie
|
|
98
|
+
gelesene und geschriebene Cache-Token je Modell. `/tokens` ist ein kurzer Alias
|
|
99
|
+
fuer die bereits vorhandene detaillierte Kontextdiagnose; `/offload` ruft die
|
|
100
|
+
vorhandene archivierende Kontextverdichtung auf. Unterschiedliche autorisierte
|
|
101
|
+
Botnachrichten werden nicht mehr durch ein starres 15-Sekunden-Fenster verworfen.
|
|
102
|
+
Nach einer vollständigen Verdichtung bleibt die exakte Todo-Liste erhalten. Ein
|
|
103
|
+
einmaliger Wiederherstellungshinweis verlangt bei unsicheren Pfaden, Befehlen,
|
|
104
|
+
Hashes oder Anforderungen zuerst das Lesen des archivierten Verlaufs, bevor eine
|
|
105
|
+
breite Dateisuche oder ein Neubeginn zulässig ist.
|
|
106
|
+
Erledigte Todo-Schritte bleiben als Nachweis erhalten. Neue widersprüchliche
|
|
107
|
+
Erkenntnisse werden als eigener laufender Schritt mit Begründung ergänzt, statt
|
|
108
|
+
einen bereits belegten Abschluss still zurückzustufen oder zu entfernen.
|
|
109
|
+
Wiederkehrende Session-Loops warten außerdem, solange die gepflegte Todo-Liste
|
|
110
|
+
noch laufende oder ausstehende Arbeit enthält. Verpasste Intervalle werden bis
|
|
111
|
+
zum echten Leerlauf zusammengefasst; andere geplante Erinnerungen bleiben davon
|
|
112
|
+
unberührt.
|
|
113
|
+
|
|
84
114
|
Version 9.1.497 priorisiert Telegram-Botnachrichten aus gemeinsamen Gruppen
|
|
85
115
|
hinter privaten und ausdrücklich adressierten Nachrichten, aber vor normalem
|
|
86
116
|
Kontext. Die Priorisierung erzwingt keine Antwort. Eine angenommene
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const HISTORICAL_MEDIA_KEEP_RECENT_MESSAGES = 12;
|
|
4
|
+
const HISTORICAL_MEDIA_PLACEHOLDERS = Object.freeze({
|
|
5
|
+
image_url: '[historical image omitted from active model context; original remains in session wire]',
|
|
6
|
+
audio_url: '[historical audio omitted from active model context; original remains in session wire]',
|
|
7
|
+
video_url: '[historical video omitted from active model context; original remains in session wire]',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
function projectHistoricalMediaParts(messages, options = {}) {
|
|
11
|
+
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
12
|
+
const keepRecentMessages = nonNegativeInteger(
|
|
13
|
+
options.keepRecentMessages,
|
|
14
|
+
HISTORICAL_MEDIA_KEEP_RECENT_MESSAGES,
|
|
15
|
+
);
|
|
16
|
+
const historicalEnd = Math.max(0, messages.length - keepRecentMessages);
|
|
17
|
+
let changed = false;
|
|
18
|
+
|
|
19
|
+
const projected = messages.map((message, messageIndex) => {
|
|
20
|
+
if (messageIndex >= historicalEnd || !Array.isArray(message?.content)) return message;
|
|
21
|
+
let nextContent;
|
|
22
|
+
for (let partIndex = 0; partIndex < message.content.length; partIndex += 1) {
|
|
23
|
+
const part = message.content[partIndex];
|
|
24
|
+
const placeholder = HISTORICAL_MEDIA_PLACEHOLDERS[part?.type];
|
|
25
|
+
if (placeholder === undefined) {
|
|
26
|
+
nextContent?.push(part);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
nextContent ??= message.content.slice(0, partIndex);
|
|
30
|
+
nextContent.push({ type: 'text', text: placeholder });
|
|
31
|
+
changed = true;
|
|
32
|
+
}
|
|
33
|
+
return nextContent === undefined ? message : { ...message, content: nextContent };
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return changed ? projected : messages;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function nonNegativeInteger(value, fallback) {
|
|
40
|
+
const number = Number(value);
|
|
41
|
+
return Number.isInteger(number) && number >= 0 ? number : fallback;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
HISTORICAL_MEDIA_KEEP_RECENT_MESSAGES,
|
|
46
|
+
HISTORICAL_MEDIA_PLACEHOLDERS,
|
|
47
|
+
projectHistoricalMediaParts,
|
|
48
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function wholeNonNegative(value) {
|
|
4
|
+
if (!Number.isFinite(value) || value <= 0) return 0;
|
|
5
|
+
return Math.floor(value);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function buildCacheEfficiency(usage = {}) {
|
|
9
|
+
const uncachedTokens = wholeNonNegative(usage.inputOther);
|
|
10
|
+
const readTokens = wholeNonNegative(usage.inputCacheRead);
|
|
11
|
+
const writeTokens = wholeNonNegative(usage.inputCacheCreation);
|
|
12
|
+
const inputTokens = uncachedTokens + readTokens + writeTokens;
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
hasActivity: readTokens > 0 || writeTokens > 0,
|
|
16
|
+
hitRate: inputTokens > 0 ? Math.min(1, readTokens / inputTokens) : 0,
|
|
17
|
+
inputTokens,
|
|
18
|
+
readTokens,
|
|
19
|
+
writeTokens,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
buildCacheEfficiency,
|
|
25
|
+
wholeNonNegative,
|
|
26
|
+
};
|
package/blun.mjs
CHANGED
|
@@ -75375,6 +75375,7 @@ var init_full = __esmMin((() => {
|
|
|
75375
75375
|
"permission_mode",
|
|
75376
75376
|
"plan_mode",
|
|
75377
75377
|
"plugin_session_start",
|
|
75378
|
+
"compaction_recovery",
|
|
75378
75379
|
"todo_list_reminder",
|
|
75379
75380
|
"tool-awareness"
|
|
75380
75381
|
]);
|
|
@@ -77053,6 +77054,7 @@ function createCronScheduler(opts) {
|
|
|
77053
77054
|
const now = clocks.wallNow();
|
|
77054
77055
|
try {
|
|
77055
77056
|
for (const task of tasks) try {
|
|
77057
|
+
if (!isIdle(task)) continue;
|
|
77056
77058
|
if (inFlight.has(task.id)) continue;
|
|
77057
77059
|
if (task.paused === true) continue;
|
|
77058
77060
|
const parsed = getParsed(task.cron);
|
|
@@ -77180,6 +77182,10 @@ var init_telemetry_events = __esmMin((() => {
|
|
|
77180
77182
|
}));
|
|
77181
77183
|
//#endregion
|
|
77182
77184
|
//#region ../../packages/agent-core/src/agent/cron/manager.ts
|
|
77185
|
+
function hasUnfinishedTodoWork(agent) {
|
|
77186
|
+
const todos = agent.tools.storeData()["todo"];
|
|
77187
|
+
return Array.isArray(todos) && todos.some((todo) => todo?.status === "in_progress" || todo?.status === "pending");
|
|
77188
|
+
}
|
|
77183
77189
|
var STALE_THRESHOLD_MS, CronManager;
|
|
77184
77190
|
var init_manager$3 = __esmMin((() => {
|
|
77185
77191
|
init_clock();
|
|
@@ -77239,7 +77245,7 @@ var init_manager$3 = __esmMin((() => {
|
|
|
77239
77245
|
this.scheduler = createCronScheduler({
|
|
77240
77246
|
clocks: this.clocks,
|
|
77241
77247
|
source: () => this.store.list(),
|
|
77242
|
-
isIdle: () => !agent.turn.hasActiveTurn,
|
|
77248
|
+
isIdle: (task) => !agent.turn.hasActiveTurn && (task?.owner !== "session-loop" || !hasUnfinishedTodoWork(agent)),
|
|
77243
77249
|
isKilled: () => process.env["BLUN_DISABLE_CRON"] === "1",
|
|
77244
77250
|
onFire: (task, ctx) => {
|
|
77245
77251
|
this.handleFire(task, ctx);
|
|
@@ -79043,7 +79049,8 @@ var init_context$2 = __esmMin((() => {
|
|
|
79043
79049
|
project(messages, options) {
|
|
79044
79050
|
const anomalies = [];
|
|
79045
79051
|
const userOffloaded = this.agent.userMessageOffload.compact(messages);
|
|
79046
|
-
const
|
|
79052
|
+
const historicalMediaProjected = projectHistoricalMediaParts(userOffloaded);
|
|
79053
|
+
const historicalUserReferencesProjected = compactHistoricalPersistedUserMessageReferences(historicalMediaProjected);
|
|
79047
79054
|
const duplicateUserMessagesProjected = dedupeRepeatedUserMessages(historicalUserReferencesProjected);
|
|
79048
79055
|
const historicalTelegramProjected = projectHistoricalUnaddressedTelegramMessages(duplicateUserMessagesProjected);
|
|
79049
79056
|
const assistantOffloaded = this.agent.assistantMessageOffload.compact(historicalTelegramProjected);
|
|
@@ -233121,7 +233128,7 @@ var init_mission_contract_bridge = __esmMin((() => {
|
|
|
233121
233128
|
}));
|
|
233122
233129
|
//#endregion
|
|
233123
233130
|
//#region ../../packages/agent-core/src/agent/injection/manager.ts
|
|
233124
|
-
var ACTIVE_BACKGROUND_TASK_GUIDANCE, InjectionManager;
|
|
233131
|
+
var ACTIVE_BACKGROUND_TASK_GUIDANCE, POST_COMPACTION_RECOVERY_GUIDANCE, InjectionManager;
|
|
233125
233132
|
var init_manager$2 = __esmMin((() => {
|
|
233126
233133
|
init_task_list();
|
|
233127
233134
|
init_action_style();
|
|
@@ -233136,6 +233143,7 @@ var init_manager$2 = __esmMin((() => {
|
|
|
233136
233143
|
init_validated_learning_signal();
|
|
233137
233144
|
init_tool_awareness();
|
|
233138
233145
|
ACTIVE_BACKGROUND_TASK_GUIDANCE = "The conversation was compacted, so the earlier messages that started these background tasks are gone — but the tasks are still running from before. Do not start duplicates. Use TaskOutput to fetch a task’s result, TaskList to list them, TaskUpdate to send additional instructions to a running agent task, and TaskStop to cancel one.";
|
|
233146
|
+
POST_COMPACTION_RECOVERY_GUIDANCE = "The conversation was compacted. Continue from the persisted TodoList and summary. If an exact file path, command, hash, requirement, or other working detail is missing or uncertain, use Read on the summary's history_path before broad filesystem searches, guessing, or starting work over.";
|
|
233139
233147
|
InjectionManager = class {
|
|
233140
233148
|
agent;
|
|
233141
233149
|
injectors;
|
|
@@ -233193,9 +233201,16 @@ var init_manager$2 = __esmMin((() => {
|
|
|
233193
233201
|
}
|
|
233194
233202
|
async injectAfterCompaction() {
|
|
233195
233203
|
await this.injectGoal();
|
|
233204
|
+
this.injectCompactionRecoveryGuidance();
|
|
233196
233205
|
this.injectActiveBackgroundTasks();
|
|
233197
233206
|
await this.inject();
|
|
233198
233207
|
}
|
|
233208
|
+
injectCompactionRecoveryGuidance() {
|
|
233209
|
+
this.agent.context.appendSystemReminder(POST_COMPACTION_RECOVERY_GUIDANCE, {
|
|
233210
|
+
kind: "injection",
|
|
233211
|
+
variant: "compaction_recovery"
|
|
233212
|
+
});
|
|
233213
|
+
}
|
|
233199
233214
|
/**
|
|
233200
233215
|
* Post-compaction only: re-surface still-running background tasks. Folding the
|
|
233201
233216
|
* live context to [recent user prompts, summary] drops the messages that
|
|
@@ -261265,7 +261280,7 @@ function renderPersistedUserMessage(text, outputPath) {
|
|
|
261265
261280
|
createUserMessagePreview(text)
|
|
261266
261281
|
].join("\n");
|
|
261267
261282
|
}
|
|
261268
|
-
var USER_MESSAGE_MAX_CHARS, USER_MESSAGE_OFFLOAD_MARKER, shouldOffloadUserMessage, shouldOffloadHistoricalUserMessage, createUserMessagePreview, compactHistoricalPersistedUserMessageReferences;
|
|
261283
|
+
var USER_MESSAGE_MAX_CHARS, USER_MESSAGE_OFFLOAD_MARKER, shouldOffloadUserMessage, shouldOffloadHistoricalUserMessage, createUserMessagePreview, compactHistoricalPersistedUserMessageReferences, projectHistoricalMediaParts;
|
|
261269
261284
|
var init_user_message_offload = __esmMin((() => {
|
|
261270
261285
|
const policy = createRequire(import.meta.url)("./bin/user-message-offload-policy.cjs");
|
|
261271
261286
|
USER_MESSAGE_MAX_CHARS = policy.USER_MESSAGE_MAX_CHARS;
|
|
@@ -261273,8 +261288,9 @@ var init_user_message_offload = __esmMin((() => {
|
|
|
261273
261288
|
shouldOffloadUserMessage = policy.shouldOffloadUserMessage;
|
|
261274
261289
|
shouldOffloadHistoricalUserMessage = policy.shouldOffloadHistoricalUserMessage;
|
|
261275
261290
|
createUserMessagePreview = policy.createUserMessagePreview;
|
|
261276
|
-
|
|
261277
|
-
}));
|
|
261291
|
+
compactHistoricalPersistedUserMessageReferences = policy.compactHistoricalPersistedUserMessageReferences;
|
|
261292
|
+
({ projectHistoricalMediaParts } = createRequire(import.meta.url)("./bin/historical-media-projection-policy.cjs"));
|
|
261293
|
+
}));
|
|
261278
261294
|
var UserMessageOffload = class {
|
|
261279
261295
|
agent;
|
|
261280
261296
|
replacements = /* @__PURE__ */ new Map();
|
|
@@ -262770,6 +262786,24 @@ var init_turn = __esmMin((() => {
|
|
|
262770
262786
|
currentStepHadFailure = false;
|
|
262771
262787
|
const pendingSteers = this.matchingSteers(turnId);
|
|
262772
262788
|
if (pendingSteers.length === 0) {
|
|
262789
|
+
const todoMaintenanceMode = goalTodoMaintenanceMode(this.agent);
|
|
262790
|
+
if (todoMaintenanceMode !== null) {
|
|
262791
|
+
const todoTool = eligibleTools.find((tool) => tool.name === "TodoList");
|
|
262792
|
+
if (todoTool === void 0) return {
|
|
262793
|
+
block: true,
|
|
262794
|
+
reason: "TodoList maintenance is due, but the TodoList tool is unavailable."
|
|
262795
|
+
};
|
|
262796
|
+
this.agent.log.info("proactive TodoList maintenance step", {
|
|
262797
|
+
stepNumber,
|
|
262798
|
+
mode: todoMaintenanceMode,
|
|
262799
|
+
workCallsSinceRefresh: this.agent.goalTodoPolicyState?.workCallsSinceRefresh ?? 0
|
|
262800
|
+
});
|
|
262801
|
+
const todoSystemPrompt = buildGoalTodoMaintenanceSystemPrompt(this.agent, turnSystemPrompt ?? this.agent.effectiveSystemPrompt, todoMaintenanceMode);
|
|
262802
|
+
return {
|
|
262803
|
+
llm: this.agent.llmForTurn("low", todoSystemPrompt),
|
|
262804
|
+
tools: [todoTool]
|
|
262805
|
+
};
|
|
262806
|
+
}
|
|
262773
262807
|
if (workStepThinkingEffort === void 0) return;
|
|
262774
262808
|
this.agent.log.info("work step thinking effort", {
|
|
262775
262809
|
stepNumber,
|
|
@@ -403833,7 +403867,7 @@ const BUILTIN_SLASH_COMMAND_DEFINITIONS = [
|
|
|
403833
403867
|
},
|
|
403834
403868
|
{
|
|
403835
403869
|
name: "compact",
|
|
403836
|
-
aliases: [],
|
|
403870
|
+
aliases: ["offload"],
|
|
403837
403871
|
descriptionKey: "command.compact.description",
|
|
403838
403872
|
priority: 80,
|
|
403839
403873
|
argumentHint: "<instruction>"
|
|
@@ -403976,7 +404010,7 @@ const BUILTIN_SLASH_COMMAND_DEFINITIONS = [
|
|
|
403976
404010
|
},
|
|
403977
404011
|
{
|
|
403978
404012
|
name: "context-doctor",
|
|
403979
|
-
aliases: [],
|
|
404013
|
+
aliases: ["tokens"],
|
|
403980
404014
|
descriptionKey: "contextDoctor.description",
|
|
403981
404015
|
priority: 66,
|
|
403982
404016
|
availability: "always"
|
|
@@ -417895,9 +417929,11 @@ function buildSessionUsageSection(usage, error, value, muted, errorStyle) {
|
|
|
417895
417929
|
for (const [model, row] of entries) {
|
|
417896
417930
|
const input = usageInputTotal$2(row);
|
|
417897
417931
|
const output = usageNumber(row.output);
|
|
417932
|
+
const cache = buildCacheEfficiency(row);
|
|
417898
417933
|
totalInput += input;
|
|
417899
417934
|
totalOutput += output;
|
|
417900
417935
|
lines.push(` ${muted(sanitizeTerminalText(model))} ${uiText("usage.input")} ${value(formatExactTokenCount(input, locale))} ${uiText("usage.output")} ${value(formatExactTokenCount(output, locale))} ${uiText("usage.total")} ${value(formatExactTokenCount(input + output, locale))}`);
|
|
417936
|
+
if (cache.hasActivity) lines.push(` ${muted("CACHE")} ${value(`${Math.round(cache.hitRate * 100)}%`)} | R ${value(formatExactTokenCount(cache.readTokens, locale))} | W ${value(formatExactTokenCount(cache.writeTokens, locale))}`);
|
|
417901
417937
|
}
|
|
417902
417938
|
if (entries.length > 1) lines.push(` ${muted(uiText("usage.total"))} ${uiText("usage.input")} ${value(formatExactTokenCount(totalInput, locale))} ${uiText("usage.output")} ${value(formatExactTokenCount(totalOutput, locale))} ${uiText("usage.total")} ${value(formatExactTokenCount(totalInput + totalOutput, locale))}`);
|
|
417903
417939
|
return lines;
|
|
@@ -417964,6 +418000,7 @@ var { buildApprovalRejectionStop } = createRequire(import.meta.url)("./bin/appro
|
|
|
417964
418000
|
var { reconcileContextBudget } = createRequire(import.meta.url)("./bin/context-budget-ledger.cjs");
|
|
417965
418001
|
var { buildContextInsight } = createRequire(import.meta.url)("./bin/context-insight-policy.cjs");
|
|
417966
418002
|
var { buildContextDoctor } = createRequire(import.meta.url)("./bin/context-doctor-policy.cjs");
|
|
418003
|
+
var { buildCacheEfficiency } = createRequire(import.meta.url)("./bin/usage-cache-efficiency-policy.cjs");
|
|
417967
418004
|
var { buildValidatedLearningInsight } = createRequire(import.meta.url)("./bin/validated-learning-insight-policy.cjs");
|
|
417968
418005
|
var { readValidatedLearningCandidates } = createRequire(import.meta.url)("./bin/validated-learning-signal.cjs");
|
|
417969
418006
|
function buildUsageReportLines(options) {
|
|
@@ -423177,6 +423214,7 @@ async function handleGoalCommand(host, args) {
|
|
|
423177
423214
|
}
|
|
423178
423215
|
const IDEA_CONTRACT_MARKER = "Work as a self-directing employee:";
|
|
423179
423216
|
const IDEA_ALLOWED_CHANNELS = Object.freeze([]);
|
|
423217
|
+
const GOAL_TODO_REFRESH_WORK_CALL_LIMIT = 8;
|
|
423180
423218
|
const IDEA_TERMINAL_TODO_STATUSES = Object.freeze([
|
|
423181
423219
|
"done",
|
|
423182
423220
|
"blocked",
|
|
@@ -423204,6 +423242,30 @@ function validInitialIdeaPlan(value) {
|
|
|
423204
423242
|
}
|
|
423205
423243
|
return active === 1;
|
|
423206
423244
|
}
|
|
423245
|
+
function goalTodoMaintenanceMode(agent) {
|
|
423246
|
+
if (isIdeaGoal(agent)) return null;
|
|
423247
|
+
const todos = ideaTodos(agent);
|
|
423248
|
+
if (todos.length === 0) return agent.goal.getActiveGoal() === null ? null : "initial";
|
|
423249
|
+
const progress = agent.goalTodoPolicyState ??= {
|
|
423250
|
+
workCallsSinceRefresh: 0,
|
|
423251
|
+
refreshRequired: false
|
|
423252
|
+
};
|
|
423253
|
+
return progress.refreshRequired || progress.workCallsSinceRefresh >= GOAL_TODO_REFRESH_WORK_CALL_LIMIT ? "refresh" : null;
|
|
423254
|
+
}
|
|
423255
|
+
function buildGoalTodoMaintenanceSystemPrompt(agent, basePrompt, mode) {
|
|
423256
|
+
const todos = ideaTodos(agent);
|
|
423257
|
+
const visibleTodoList = todos.length === 0 ? "The visible TodoList is empty." : ["Current visible TodoList:", ...todos.map((todo) => `- [${String(todo?.status ?? "pending")}] ${String(todo?.title ?? "").trim()}`)].join("\n");
|
|
423258
|
+
const action = mode === "initial" ? "Create the visible task-specific TodoList now. Keep exactly one item in_progress and every later item pending." : "Update the visible TodoList now, before any more task work. Mark only evidenced steps done, keep the actual current step in_progress, and leave later steps pending. If the current title is stale, refine it to the concrete verified substep.";
|
|
423259
|
+
return `<todo-maintenance>
|
|
423260
|
+
TODO MAINTENANCE IS THE ONLY ACTION FOR THIS STEP.
|
|
423261
|
+
${action}
|
|
423262
|
+
Call TodoList as the first and only tool. Do not call a work, search, file, shell, web, messaging, or goal tool in this step. Do not merely describe the update.
|
|
423263
|
+
|
|
423264
|
+
${visibleTodoList}
|
|
423265
|
+
</todo-maintenance>
|
|
423266
|
+
|
|
423267
|
+
${basePrompt}`;
|
|
423268
|
+
}
|
|
423207
423269
|
function enforceGoalTodoPolicy(agent, context) {
|
|
423208
423270
|
if (isIdeaGoal(agent)) return;
|
|
423209
423271
|
if (TELEGRAM_DELIVERY_TOOL_RE.test(String(context.toolCall.name ?? ""))) return;
|
|
@@ -423231,6 +423293,12 @@ function enforceGoalTodoPolicy(agent, context) {
|
|
|
423231
423293
|
if (context.toolCall.name === "TodoList") {
|
|
423232
423294
|
const nextTodos = context.args?.todos;
|
|
423233
423295
|
if (!Array.isArray(nextTodos)) return;
|
|
423296
|
+
const normalizeTitle = (value) => String(value ?? "").trim().toLowerCase().replace(/\s+/gu, " ");
|
|
423297
|
+
const missingCompleted = todos.filter((todo, index) => todo?.status === "done" && !nextTodos.some((nextTodo) => nextTodo?.status === "done" && normalizeTitle(nextTodo.title) === normalizeTitle(todo.title)) && nextTodos[index]?.status !== "done");
|
|
423298
|
+
if (missingCompleted.length > 0) return {
|
|
423299
|
+
block: true,
|
|
423300
|
+
reason: `Completed TodoList evidence is append-only. Keep each completed item present and done. If later evidence reopens work, retain the completed item and add a separate in_progress item with the reason. Missing completed item: ${String(missingCompleted[0]?.title ?? "").trim()}`
|
|
423301
|
+
};
|
|
423234
423302
|
if (nextTodos.length === 0) {
|
|
423235
423303
|
if (activeGoal !== null) return {
|
|
423236
423304
|
block: true,
|
|
@@ -423272,7 +423340,7 @@ function enforceGoalTodoPolicy(agent, context) {
|
|
|
423272
423340
|
}
|
|
423273
423341
|
if (context.toolCall.id === context.toolCalls[0]?.id) {
|
|
423274
423342
|
progress.workCallsSinceRefresh += 1;
|
|
423275
|
-
if (progress.workCallsSinceRefresh >
|
|
423343
|
+
if (progress.workCallsSinceRefresh > GOAL_TODO_REFRESH_WORK_CALL_LIMIT) progress.refreshRequired = true;
|
|
423276
423344
|
}
|
|
423277
423345
|
if (!progress.refreshRequired) return;
|
|
423278
423346
|
return {
|
package/package.json
CHANGED
|
@@ -130,25 +130,6 @@ function matchesAny(text, patterns) {
|
|
|
130
130
|
} catch {}
|
|
131
131
|
return false;
|
|
132
132
|
}
|
|
133
|
-
/**
|
|
134
|
-
* Bot-to-bot loop guard (Telegram's own safety requirement for Bot API 10.0
|
|
135
|
-
* bot-to-bot mode): once King can receive other bots' messages, an unthrottled
|
|
136
|
-
* exchange could ping-pong forever. We throttle deliveries FROM a given bot in
|
|
137
|
-
* a given group to at most one per BOT_COOLDOWN_MS, so two bots can never fast-
|
|
138
|
-
* loop. In-memory per bridge process — resets on restart, which is fine.
|
|
139
|
-
*/
|
|
140
|
-
const BOT_COOLDOWN_MS = 15e3;
|
|
141
|
-
const lastBotDelivery = /* @__PURE__ */ new Map();
|
|
142
|
-
/** Exposed for tests; drops stale entries so the map can't grow unbounded. */
|
|
143
|
-
function botLoopGuardAllows(senderId, now = Date.now(), cooldownMs = BOT_COOLDOWN_MS) {
|
|
144
|
-
const last = lastBotDelivery.get(senderId);
|
|
145
|
-
if (last !== void 0 && now - last < cooldownMs) return false;
|
|
146
|
-
lastBotDelivery.set(senderId, now);
|
|
147
|
-
if (lastBotDelivery.size > 200) {
|
|
148
|
-
for (const [key, ts] of lastBotDelivery) if (now - ts > cooldownMs) lastBotDelivery.delete(key);
|
|
149
|
-
}
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
133
|
function gate(ctx, botUsername) {
|
|
153
134
|
const access = readAccess();
|
|
154
135
|
if (pruneExpired(access)) saveAccess(access);
|
|
@@ -227,7 +208,6 @@ function gate(ctx, botUsername) {
|
|
|
227
208
|
if (groupAllowFrom.length > 0 && !groupAllowFrom.includes(senderId)) return { action: "drop" };
|
|
228
209
|
const mentionBypass = from.isBot !== true && (policy.alwaysAllowFrom ?? []).includes(senderId);
|
|
229
210
|
const addressed = !matchesAny(ctx.text, access.ignorePatterns) && (mentionBypass || isMentioned(ctx, botUsername, access.mentionPatterns));
|
|
230
|
-
if (!addressed && from.isBot === true && !botLoopGuardAllows(senderId)) return { action: "drop" };
|
|
231
211
|
return {
|
|
232
212
|
action: "deliver",
|
|
233
213
|
access,
|