blun-king-cli 9.1.219 → 9.1.221

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,41 @@
1
+ 'use strict';
2
+
3
+ function createPendingTokenEstimateCache(estimateMessages) {
4
+ if (typeof estimateMessages !== 'function') {
5
+ throw new TypeError('estimateMessages must be a function');
6
+ }
7
+
8
+ let cachedHistory = null;
9
+ let cachedCoveredCount = -1;
10
+ let cachedRevision = -1;
11
+ let cachedEstimate = 0;
12
+
13
+ return {
14
+ read(history, coveredMessageCount, revision) {
15
+ const messages = Array.isArray(history) ? history : [];
16
+ const covered = Math.max(
17
+ 0,
18
+ Math.min(messages.length, Math.floor(Number(coveredMessageCount) || 0)),
19
+ );
20
+ const currentRevision = Number.isSafeInteger(revision) ? revision : 0;
21
+
22
+ if (
23
+ messages === cachedHistory
24
+ && covered === cachedCoveredCount
25
+ && currentRevision === cachedRevision
26
+ ) {
27
+ return cachedEstimate;
28
+ }
29
+
30
+ cachedEstimate = estimateMessages(messages.slice(covered));
31
+ cachedHistory = messages;
32
+ cachedCoveredCount = covered;
33
+ cachedRevision = currentRevision;
34
+ return cachedEstimate;
35
+ },
36
+ };
37
+ }
38
+
39
+ module.exports = {
40
+ createPendingTokenEstimateCache,
41
+ };
@@ -10,6 +10,7 @@ const INTRA_MESSAGE_MIN_BLOCK_CHARS = 40;
10
10
  const INTRA_MESSAGE_MIN_SAVED_CHARS = 1_000;
11
11
  const INTRA_MESSAGE_SIMILARITY_THRESHOLD = 0.88;
12
12
  const INTRA_MESSAGE_REPEAT_PLACEHOLDER = '[Exact repeated assistant paragraphs omitted from model projection; raw history preserved.]';
13
+ const intraMessageProjectionCache = new WeakMap();
13
14
 
14
15
  function isUserPrompt(message) {
15
16
  if (message?.role !== 'user') return false;
@@ -132,8 +133,24 @@ function compactRepeatedAssistantParagraphs(message) {
132
133
  )) return null;
133
134
 
134
135
  const text = textParts[0].text;
136
+ const cached = intraMessageProjectionCache.get(message);
137
+ if (
138
+ cached?.content === message.content
139
+ && cached.textPart === textParts[0]
140
+ && cached.text === text
141
+ ) return cached.projected;
142
+
143
+ const cacheProjection = (projected) => {
144
+ intraMessageProjectionCache.set(message, {
145
+ content: message.content,
146
+ projected,
147
+ text,
148
+ textPart: textParts[0],
149
+ });
150
+ return projected;
151
+ };
135
152
  const blocks = text.split(/\n\s*\n/u).map((block) => block.trim()).filter(Boolean);
136
- if (blocks.length < INTRA_MESSAGE_MIN_REPEATS + 1) return null;
153
+ if (blocks.length < INTRA_MESSAGE_MIN_REPEATS + 1) return cacheProjection(null);
137
154
  const protectedIndexes = fencedBlockIndexes(blocks);
138
155
 
139
156
  const clusters = [];
@@ -150,7 +167,7 @@ function compactRepeatedAssistantParagraphs(message) {
150
167
  const repeatedIndexes = new Set(clusters
151
168
  .filter((cluster) => cluster.indexes.length >= INTRA_MESSAGE_MIN_REPEATS)
152
169
  .flatMap((cluster) => cluster.indexes.slice(1)));
153
- if (repeatedIndexes.size === 0) return null;
170
+ if (repeatedIndexes.size === 0) return cacheProjection(null);
154
171
 
155
172
  const projectedBlocks = [];
156
173
  let markerAdded = false;
@@ -166,13 +183,13 @@ function compactRepeatedAssistantParagraphs(message) {
166
183
  }
167
184
 
168
185
  const projectedText = projectedBlocks.join('\n\n');
169
- if (text.length - projectedText.length < INTRA_MESSAGE_MIN_SAVED_CHARS) return null;
170
- return {
186
+ if (text.length - projectedText.length < INTRA_MESSAGE_MIN_SAVED_CHARS) return cacheProjection(null);
187
+ return cacheProjection({
171
188
  ...message,
172
189
  content: message.content.map((part) => (
173
190
  part === textParts[0] ? { ...part, text: projectedText } : part
174
191
  )),
175
- };
192
+ });
176
193
  }
177
194
 
178
195
  function projectRepeatedAssistantResponses(history) {
package/blun.mjs CHANGED
@@ -78650,12 +78650,13 @@ function formatUndoUnavailableMessage(requestedCount, undoableCount, stoppedAtCo
78650
78650
  return `${String(count)} ${count === 1 ? "prompt" : "prompts"}`;
78651
78651
  }
78652
78652
  }
78653
- var TOOL_ERROR_STATUS$2, TOOL_EMPTY_STATUS$2, TOOL_EMPTY_ERROR_STATUS$2, TOOL_OUTPUT_EMPTY_TEXT$2, TOOL_INTERRUPTED_ON_RESUME_OUTPUT$1, ContextMemory;
78653
+ var TOOL_ERROR_STATUS$2, TOOL_EMPTY_STATUS$2, TOOL_EMPTY_ERROR_STATUS$2, TOOL_OUTPUT_EMPTY_TEXT$2, TOOL_INTERRUPTED_ON_RESUME_OUTPUT$1, createPendingTokenEstimateCache, ContextMemory;
78654
78654
  var init_context$2 = __esmMin((() => {
78655
- init_src$4();
78656
- init_errors$8();
78657
- init_image_compress();
78658
- init_tokens();
78655
+ init_src$4();
78656
+ init_errors$8();
78657
+ init_image_compress();
78658
+ init_tokens();
78659
+ ({ createPendingTokenEstimateCache } = createRequire(import.meta.url)("./bin/pending-token-estimate-policy.cjs"));
78659
78660
  init_xml_escape();
78660
78661
  init_compaction();
78661
78662
  init_projector();
@@ -78669,8 +78670,10 @@ var init_context$2 = __esmMin((() => {
78669
78670
  ContextMemory = class {
78670
78671
  agent;
78671
78672
  _history = [];
78672
- _tokenCount = 0;
78673
- tokenCountCoveredMessageCount = 0;
78673
+ _tokenCount = 0;
78674
+ tokenCountCoveredMessageCount = 0;
78675
+ pendingTokenRevision = 0;
78676
+ pendingTokenEstimateCache = createPendingTokenEstimateCache(estimateTokensForMessages);
78674
78677
  openSteps = /* @__PURE__ */ new Map();
78675
78678
  pendingToolResultIds = /* @__PURE__ */ new Set();
78676
78679
  deferredMessages = [];
@@ -78679,9 +78682,12 @@ var init_context$2 = __esmMin((() => {
78679
78682
  constructor(agent) {
78680
78683
  this.agent = agent;
78681
78684
  }
78682
- get lastAssistantAt() {
78683
- return this._lastAssistantAt;
78684
- }
78685
+ get lastAssistantAt() {
78686
+ return this._lastAssistantAt;
78687
+ }
78688
+ markPendingTokenEstimateDirty() {
78689
+ this.pendingTokenRevision = (this.pendingTokenRevision + 1) % Number.MAX_SAFE_INTEGER;
78690
+ }
78685
78691
  appendUserMessage(content, origin = USER_PROMPT_ORIGIN) {
78686
78692
  if (content.length === 0) return;
78687
78693
  const { captions, parts } = origin.kind === "user" ? splitImageCompressionCaptions(content) : {
@@ -78783,8 +78789,11 @@ var init_context$2 = __esmMin((() => {
78783
78789
  const last = lastDeferred ?? this._history.at(-1);
78784
78790
  if (last === void 0) return false;
78785
78791
  if (!matcher(last.origin)) return false;
78786
- if (lastDeferred !== void 0) this.deferredMessages.pop();
78787
- else this._history.pop();
78792
+ if (lastDeferred !== void 0) this.deferredMessages.pop();
78793
+ else {
78794
+ this._history.pop();
78795
+ this.markPendingTokenEstimateDirty();
78796
+ }
78788
78797
  return true;
78789
78798
  }
78790
78799
  /** Remove transient system reminders before the next model projection. */
@@ -78806,7 +78815,8 @@ var init_context$2 = __esmMin((() => {
78806
78815
  removedMessages.add(message);
78807
78816
  return false;
78808
78817
  });
78809
- if (removedMessages.size === 0) return 0;
78818
+ if (removedMessages.size === 0) return 0;
78819
+ this.markPendingTokenEstimateDirty();
78810
78820
  this.agent.replayBuilder.removeLastMessages(removedMessages);
78811
78821
  this.agent.microCompaction.reset(this._history.length);
78812
78822
  this.agent.toolResultBatchOffload.reset(this._history);
@@ -78817,7 +78827,8 @@ var init_context$2 = __esmMin((() => {
78817
78827
  }
78818
78828
  clear() {
78819
78829
  this.agent.records.logRecord({ type: "context.clear" });
78820
- this._history = [];
78830
+ this._history = [];
78831
+ this.markPendingTokenEstimateDirty();
78821
78832
  this._tokenCount = 0;
78822
78833
  this.tokenCountCoveredMessageCount = 0;
78823
78834
  this.openSteps.clear();
@@ -78861,7 +78872,8 @@ var init_context$2 = __esmMin((() => {
78861
78872
  if (removedUserCount >= count) break;
78862
78873
  }
78863
78874
  }
78864
- this.agent.replayBuilder.removeLastMessages(removedMessages);
78875
+ this.agent.replayBuilder.removeLastMessages(removedMessages);
78876
+ if (removedMessages.size > 0) this.markPendingTokenEstimateDirty();
78865
78877
  this.openSteps.clear();
78866
78878
  this.pendingToolResultIds.clear();
78867
78879
  this.deferredMessages = [];
@@ -78940,7 +78952,8 @@ var init_context$2 = __esmMin((() => {
78940
78952
  origin: { kind: "compaction_summary" }
78941
78953
  };
78942
78954
  const isLegacyRestore = this.agent.records.restoring !== null && input.keptUserMessageCount === void 0 && input.compactedCount < this._history.length;
78943
- this._history = isLegacyRestore ? [summaryMessage, ...this._history.slice(input.compactedCount)] : [...keptMessages, summaryMessage];
78955
+ this._history = isLegacyRestore ? [summaryMessage, ...this._history.slice(input.compactedCount)] : [...keptMessages, summaryMessage];
78956
+ this.markPendingTokenEstimateDirty();
78944
78957
  this.openSteps.clear();
78945
78958
  this.pendingToolResultIds.clear();
78946
78959
  this.deferredMessages = [];
@@ -78963,9 +78976,12 @@ var init_context$2 = __esmMin((() => {
78963
78976
  get tokenCount() {
78964
78977
  return this._tokenCount;
78965
78978
  }
78966
- get tokenCountWithPending() {
78967
- const pendingMessages = this._history.slice(this.tokenCountCoveredMessageCount);
78968
- return this._tokenCount + estimateTokensForMessages(pendingMessages);
78979
+ get tokenCountWithPending() {
78980
+ return this._tokenCount + this.pendingTokenEstimateCache.read(
78981
+ this._history,
78982
+ this.tokenCountCoveredMessageCount,
78983
+ this.pendingTokenRevision
78984
+ );
78969
78985
  }
78970
78986
  get history() {
78971
78987
  return this._history;
@@ -79120,6 +79136,7 @@ var init_context$2 = __esmMin((() => {
79120
79136
  this._tokenCount += estimateTokensForMessages(this._history.slice(previousCoveredCount, coveredCount));
79121
79137
  }
79122
79138
  this.tokenCountCoveredMessageCount = coveredCount;
79139
+ this.markPendingTokenEstimateDirty();
79123
79140
  }
79124
79141
  this.flushDeferredMessagesIfToolExchangeClosed();
79125
79142
  return;
@@ -79127,18 +79144,20 @@ var init_context$2 = __esmMin((() => {
79127
79144
  case "content.part": {
79128
79145
  const openStep = this.openSteps.get(event.stepUuid);
79129
79146
  if (openStep === void 0) throw new Error(`Received content_part for unknown step_uuid '${event.stepUuid}' (no open step_begin)`);
79130
- openStep.content.push(event.part);
79147
+ openStep.content.push(event.part);
79148
+ this.markPendingTokenEstimateDirty();
79131
79149
  return;
79132
79150
  }
79133
79151
  case "tool.call": {
79134
79152
  const openStep = this.openSteps.get(event.stepUuid);
79135
79153
  if (openStep === void 0) throw new Error(`Received tool_call for unknown step_uuid '${event.stepUuid}' (no open step_begin)`);
79136
- openStep.toolCalls.push({
79154
+ openStep.toolCalls.push({
79137
79155
  type: "function",
79138
79156
  id: event.toolCallId,
79139
79157
  name: event.name,
79140
- arguments: event.args === void 0 ? null : JSON.stringify(event.args)
79141
- });
79158
+ arguments: event.args === void 0 ? null : JSON.stringify(event.args)
79159
+ });
79160
+ this.markPendingTokenEstimateDirty();
79142
79161
  this.pendingToolResultIds.add(event.toolCallId);
79143
79162
  return;
79144
79163
  }
@@ -79175,8 +79194,9 @@ var init_context$2 = __esmMin((() => {
79175
79194
  hasOpenToolExchange() {
79176
79195
  return this.pendingToolResultIds.size > 0;
79177
79196
  }
79178
- pushHistory(...messages) {
79179
- this._history.push(...messages);
79197
+ pushHistory(...messages) {
79198
+ this._history.push(...messages);
79199
+ this.markPendingTokenEstimateDirty();
79180
79200
  for (const message of messages) {
79181
79201
  if (message.role === "assistant") this._lastAssistantAt = this.agent.records.restoring?.time ?? Date.now();
79182
79202
  if (message.origin?.kind === "background_task") this.agent.background.markDeliveredNotification(message.origin);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.219",
3
+ "version": "9.1.221",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {