blun-king-cli 9.1.494 → 9.1.496

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 9.1.496 - 2026-08-30
4
+
5
+ - Delivers Telegram user messages automatically one at a time without depending on the manual Ctrl+S or Escape release counter.
6
+ - Retires only the exact accepted or answered Telegram message ID, preserving newer messages from the same chat and removing delivered items from the visible queue.
7
+ - Enforces truthful progress for every existing visible TodoList, including sessions without an active Goal, and rejects unchanged refreshes after extended work.
8
+ - Hides successful internal hook payloads from visible output while keeping blocking hook failures visible.
9
+
10
+ ## 9.1.495 - 2026-08-30
11
+
12
+ - Starts every new or replaced goal with a fresh visible TodoList and requires a truthful refresh after at most eight work batches.
13
+ - Retires answered Telegram group focus immediately after a successful reply or edit instead of waiting for a long-running turn to end.
14
+ - Defers normal inbound checkpoint acknowledgement until turn end, so restart replay cannot lose a message accepted during an early model step.
15
+ - Adds real control-key decoding coverage for Ctrl+S and Ctrl+T, including Kitty keyboard sequences with Caps Lock active.
16
+
3
17
  ## 9.1.494 - 2026-08-30
4
18
 
5
19
  - Removes the conflicting Ctrl+S fallback that opened or collapsed the Todo panel when no queue item was visible.
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.494
12
+ npm install -g blun-king-cli@9.1.496
13
13
 
14
14
  Start
15
15
  -----
@@ -65,6 +65,22 @@ 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.496 stellt Telegram-Nutzernachrichten automatisch einzeln zu, ohne
69
+ die automatische Zustellung an den manuellen Strg+S-/Escape-Zähler zu koppeln.
70
+ Nach Annahme oder Antwort wird nur die exakt verarbeitete Nachrichten-ID aus der
71
+ sichtbaren Queue entfernt; spätere Meldungen desselben Chats bleiben erhalten.
72
+ Vorhandene Todo-Listen bleiben auch ohne aktives Ziel verbindlich und müssen nach
73
+ längerer Arbeit belegbaren Fortschritt zeigen. Erfolgreiche interne Hook-Ausgaben
74
+ bleiben unsichtbar, blockierende Hook-Fehler werden weiterhin angezeigt.
75
+
76
+ Version 9.1.495 startet jedes neue Ziel mit einer frischen sichtbaren Todo-Liste,
77
+ erzwingt regelmäßige wahrheitsgemäße Fortschrittsstände und quittiert normale
78
+ Telegram-Nachrichten erst am Zugende. Dadurch bleiben sie nach einem Neustart
79
+ wiederholbar, bis ihre Verarbeitung wirklich abgeschlossen ist. Erfolgreich
80
+ beantwortete Gruppenmeldungen verschwinden sofort aus der sichtbaren Queue.
81
+ Zusätzliche Regressionstests prüfen Strg+S und Strg+T als echte Terminalsequenzen,
82
+ auch bei aktiver Feststelltaste.
83
+
68
84
  Version 9.1.494 trennt die Tastenkürzel wieder eindeutig: Strg+S steuert nur
69
85
  den Queue-Rückstand, Strg+T nur die Todo-Liste. Version 9.1.493 stellt die
70
86
  automatische Zustellung von User-Nachrichten auch
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.494
12
+ npm install -g blun-king-cli@9.1.496
13
13
  ```
14
14
 
15
15
  ## Reproduzierbares Staging und Packen
@@ -81,6 +81,22 @@ 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.496 stellt Telegram-Nutzernachrichten automatisch einzeln zu, ohne
85
+ die automatische Zustellung an den manuellen Strg+S-/Escape-Zähler zu koppeln.
86
+ Nach Annahme oder Antwort wird nur die exakt verarbeitete Nachrichten-ID aus der
87
+ sichtbaren Queue entfernt; spätere Meldungen desselben Chats bleiben erhalten.
88
+ Vorhandene Todo-Listen bleiben auch ohne aktives Ziel verbindlich und müssen nach
89
+ längerer Arbeit belegbaren Fortschritt zeigen. Erfolgreiche interne Hook-Ausgaben
90
+ bleiben unsichtbar, blockierende Hook-Fehler werden weiterhin angezeigt.
91
+
92
+ Version 9.1.495 startet jedes neue Ziel mit einer frischen sichtbaren Todo-Liste,
93
+ erzwingt regelmäßige wahrheitsgemäße Fortschrittsstände und quittiert normale
94
+ Telegram-Nachrichten erst am Zugende. Dadurch bleiben sie nach einem Neustart
95
+ wiederholbar, bis ihre Verarbeitung wirklich abgeschlossen ist. Erfolgreich
96
+ beantwortete Gruppenmeldungen verschwinden sofort aus der sichtbaren Queue.
97
+ Zusätzliche Regressionstests prüfen Strg+S und Strg+T als echte Terminalsequenzen,
98
+ auch bei aktiver Feststelltaste.
99
+
84
100
  Version 9.1.494 trennt die Tastenkürzel wieder eindeutig: Strg+S steuert nur
85
101
  den Queue-Rückstand, Strg+T nur die Todo-Liste. Version 9.1.493 stellt die
86
102
  automatische Zustellung von User-Nachrichten auch
@@ -36,6 +36,16 @@ function createAddressedChannelFocusStore(options = {}) {
36
36
  return true;
37
37
  },
38
38
 
39
+ clearIds(ids) {
40
+ const expected = new Set(Array.from(ids ?? [], (id) => String(id)));
41
+ if (expected.size === 0) return false;
42
+ const next = items.filter((item) => !expected.has(item.id));
43
+ if (next.length === items.length) return false;
44
+ items = next;
45
+ publish();
46
+ return true;
47
+ },
48
+
39
49
  snapshot,
40
50
  };
41
51
  }
package/blun.mjs CHANGED
@@ -230323,6 +230323,7 @@ var init_goal$1 = __esmMin((() => {
230323
230323
  });
230324
230324
  }
230325
230325
  this.persistState(state);
230326
+ this.agent.tools.updateStore(TODO_STORE_KEY, []);
230326
230327
  this.agent.records.logRecord({
230327
230328
  type: "goal.create",
230328
230329
  goalId: state.goalId,
@@ -423202,9 +423203,15 @@ function validInitialIdeaPlan(value) {
423202
423203
  return active === 1;
423203
423204
  }
423204
423205
  function enforceGoalTodoPolicy(agent, context) {
423205
- if (isIdeaGoal(agent) || agent.goal.getActiveGoal() === null) return;
423206
+ if (isIdeaGoal(agent)) return;
423207
+ const activeGoal = agent.goal.getActiveGoal();
423206
423208
  const todos = ideaTodos(agent);
423209
+ const progress = agent.goalTodoPolicyState ??= {
423210
+ workCallsSinceRefresh: 0,
423211
+ refreshRequired: false
423212
+ };
423207
423213
  if (todos.length === 0) {
423214
+ if (activeGoal === null) return;
423208
423215
  const firstCall = context.toolCalls[0];
423209
423216
  if (context.toolCall.id !== firstCall?.id || context.toolCall.name !== "TodoList") return {
423210
423217
  block: true,
@@ -423214,16 +423221,60 @@ function enforceGoalTodoPolicy(agent, context) {
423214
423221
  block: true,
423215
423222
  reason: "The first goal TodoList must contain at least one item, exactly one in_progress item, and only pending items otherwise."
423216
423223
  };
423224
+ progress.workCallsSinceRefresh = 0;
423225
+ progress.refreshRequired = false;
423217
423226
  return;
423218
423227
  }
423219
- if (context.toolCall.name !== "UpdateGoal" || context.args?.status !== "complete" && context.args?.status !== "blocked") return;
423220
- if (todos.some((todo) => !IDEA_TERMINAL_TODO_STATUSES.includes(todo.status))) return {
423221
- block: true,
423222
- reason: "The goal cannot finish while a visible TodoList item has no truthful terminal status. Reconcile the list first."
423223
- };
423224
- if (context.args.status === "complete" && todos.some((todo) => todo.status !== "done")) return {
423228
+ if (context.toolCall.name === "TodoList") {
423229
+ const nextTodos = context.args?.todos;
423230
+ if (!Array.isArray(nextTodos)) return;
423231
+ if (nextTodos.length === 0) {
423232
+ if (activeGoal !== null) return {
423233
+ block: true,
423234
+ reason: "An active goal keeps its TodoList until UpdateGoal records a truthful terminal state. Reconcile the list instead of clearing it."
423235
+ };
423236
+ if (todos.some((todo) => !IDEA_TERMINAL_TODO_STATUSES.includes(todo.status))) return {
423237
+ block: true,
423238
+ reason: "Do not clear unfinished TodoList work. Record a truthful terminal status for every item first."
423239
+ };
423240
+ } else {
423241
+ const inProgressCount = nextTodos.filter((todo) => todo?.status === "in_progress").length;
423242
+ const allTerminal = nextTodos.every((todo) => IDEA_TERMINAL_TODO_STATUSES.includes(todo?.status));
423243
+ if (!allTerminal && inProgressCount !== 1) return {
423244
+ block: true,
423245
+ reason: "A maintained TodoList must keep exactly one item in_progress while unfinished work remains."
423246
+ };
423247
+ }
423248
+ if (progress.refreshRequired) {
423249
+ const normalized = (value) => value.map((todo) => ({ title: String(todo?.title ?? "").trim(), status: todo?.status }));
423250
+ if (JSON.stringify(normalized(nextTodos)) === JSON.stringify(normalized(todos))) return {
423251
+ block: true,
423252
+ reason: "TodoList is unchanged after extended work. Mark evidenced progress, advance the active item, or refine its title to the concrete verified substep before continuing."
423253
+ };
423254
+ }
423255
+ progress.workCallsSinceRefresh = 0;
423256
+ progress.refreshRequired = false;
423257
+ return;
423258
+ }
423259
+ if (activeGoal !== null && context.toolCall.name === "UpdateGoal" && (context.args?.status === "complete" || context.args?.status === "blocked")) {
423260
+ if (todos.some((todo) => !IDEA_TERMINAL_TODO_STATUSES.includes(todo.status))) return {
423261
+ block: true,
423262
+ reason: "The goal cannot finish while a visible TodoList item has no truthful terminal status. Reconcile the list first."
423263
+ };
423264
+ if (context.args.status === "complete" && todos.some((todo) => todo.status !== "done")) return {
423265
+ block: true,
423266
+ reason: "The goal can complete only when every visible TodoList item is done. Use blocked when an item is blocked, waiting_approval, or aborted."
423267
+ };
423268
+ return;
423269
+ }
423270
+ if (context.toolCall.id === context.toolCalls[0]?.id) {
423271
+ progress.workCallsSinceRefresh += 1;
423272
+ if (progress.workCallsSinceRefresh > 8) progress.refreshRequired = true;
423273
+ }
423274
+ if (!progress.refreshRequired) return;
423275
+ return {
423225
423276
  block: true,
423226
- reason: "The goal can complete only when every visible TodoList item is done. Use blocked when an item is blocked, waiting_approval, or aborted."
423277
+ reason: "Refresh TodoList before more work. Mark only evidenced steps done, keep the actual current step in_progress, and leave later steps pending."
423227
423278
  };
423228
423279
  }
423229
423280
  function ideaOutboundChannel(toolName, args) {
@@ -497053,7 +497104,7 @@ function runPromptTurn(session, prompt, outputFormat, stdout, stderr, model) {
497053
497104
  outputWriter.writeAssistantDelta(event.delta);
497054
497105
  return;
497055
497106
  case "hook.result":
497056
- outputWriter.writeHookResult(event);
497107
+ if (shouldDisplayHookResult(event)) outputWriter.writeHookResult(event);
497057
497108
  return;
497058
497109
  case "thinking.delta":
497059
497110
  outputWriter.writeThinkingDelta(event.delta);
@@ -497117,6 +497168,9 @@ function promptInputText(input) {
497117
497168
  const text = input.filter((part) => part.type === "text").map((part) => part.text).join("\n\n");
497118
497169
  return text.trim().length > 0 ? text : void 0;
497119
497170
  }
497171
+ function shouldDisplayHookResult(event) {
497172
+ return event.blocked === true;
497173
+ }
497120
497174
  var PromptTranscriptWriter = class {
497121
497175
  assistantWriter;
497122
497176
  thinkingWriter;
@@ -503861,6 +503915,7 @@ var BtwPanelController = class {
503861
503915
  this.armInactivityTimer(this.activeForAgent(event.agentId));
503862
503916
  return true;
503863
503917
  case "hook.result":
503918
+ if (!shouldDisplayHookResult(event)) return true;
503864
503919
  this.clearInactivityTimer(this.activeForAgent(event.agentId));
503865
503920
  panel.appendAnswer(formatHookResultPlain(event));
503866
503921
  this.host.state.ui.requestRender();
@@ -507937,7 +507992,7 @@ var SubAgentEventHandler = class {
507937
507992
  const toolCall = this.host.streamingUI.getToolComponent(parentToolCallId);
507938
507993
  if (toolCall === void 0) return true;
507939
507994
  toolCall.setSubagentMeta(childAgentId, info.name);
507940
- if (event.type === "hook.result") toolCall.appendSubagentText(formatHookResultPlain(event), "text");
507995
+ if (event.type === "hook.result" && shouldDisplayHookResult(event)) toolCall.appendSubagentText(formatHookResultPlain(event), "text");
507941
507996
  else if (event.type === "assistant.delta") toolCall.appendSubagentText(event.delta, "text");
507942
507997
  else if (event.type === "thinking.delta") toolCall.appendSubagentText(event.delta, "thinking");
507943
507998
  else if (event.type === "tool.call.started") toolCall.appendSubToolCall({
@@ -508810,6 +508865,7 @@ var SessionEventHandler = class {
508810
508865
  this.turnWatchdog.stop();
508811
508866
  this.host.restoreQueuedSteerAtTurnEnd(event.turnId);
508812
508867
  this.host.settleChannelPromptAtTurnEnd(event.turnId, event.reason);
508868
+ this.host.flushDeferredChannelAcknowledgements(event.turnId);
508813
508869
  const sessionId = this.host.session?.id;
508814
508870
  if (sessionId !== void 0) finishPersonalMemoryRememberIntentTurn(sessionId, event.turnId);
508815
508871
  this.host.streamingUI.flushNow();
@@ -508972,6 +509028,7 @@ var SessionEventHandler = class {
508972
509028
  streamingUI.scheduleFlush();
508973
509029
  }
508974
509030
  handleHookResult(event) {
509031
+ if (!shouldDisplayHookResult(event)) return;
508975
509032
  this.host.streamingUI.flushNow();
508976
509033
  if (this.host.streamingUI.hasThinkingDraft()) this.host.streamingUI.flushThinkingToTranscript("idle");
508977
509034
  this.host.streamingUI.finalizeAssistantStream();
@@ -509074,6 +509131,7 @@ var SessionEventHandler = class {
509074
509131
  });
509075
509132
  if (matchedCall !== void 0 && matchedCall.name !== "GetMedia" && isMediaToolName(matchedCall.name) && event.isError !== true) this.host.trackChannelMediaJob(event.output);
509076
509133
  if (matchedCall?.name === "GetMedia" && event.isError !== true) this.host.runChannelMediaFallback(event.output);
509134
+ this.host.settleChannelReplyToolResult(matchedCall, event.isError === true);
509077
509135
  this.subAgentEventHandler.handleAgentSwarmToolResult(event.toolCallId, resultData, event.isError === true);
509078
509136
  if (matchedCall !== void 0 && matchedCall.name === "TodoList" && !event.isError) {
509079
509137
  const rawTodos = matchedCall.args.todos;
@@ -510555,6 +510613,7 @@ var SessionReplayRenderer = class {
510555
510613
  }
510556
510614
  renderHookResult(context, message) {
510557
510615
  if (message.origin?.kind !== "hook_result") return;
510616
+ if (message.origin.blocked !== true) return;
510558
510617
  this.flushAssistant(context);
510559
510618
  this.host.appendTranscriptEntry(replayEntry(context, "assistant", formatHookResultMessageForTranscript(contentPartsToText(message.content), message.origin.event, message.origin.blocked === true), "markdown"));
510560
510619
  }
@@ -517933,6 +517992,7 @@ var BlunTUI = class {
517933
517992
  queueFlushBatchRemaining = 0;
517934
517993
  queueSteerInFlight;
517935
517994
  channelPromptInFlight;
517995
+ deferredChannelAcknowledgements = /* @__PURE__ */ new Map();
517936
517996
  editorReplacementActive = false;
517937
517997
  editorReplacement;
517938
517998
  editorReplacementGeneration = 0;
@@ -517943,7 +518003,7 @@ var BlunTUI = class {
517943
518003
  canDeliverQueuedChannelHead() {
517944
518004
  const item = this.state.queuedMessages[0];
517945
518005
  const hasActiveTurn = this.streamingUI.hasActiveTurn() || (this.state.appState.streamingPhase !== "idle" && this.state.appState.streamingPhase !== "shell");
517946
- if (this.isShuttingDown || this.queueCommandRunning || this.queueSteerInFlight !== void 0 || this.queueFlushBatchRemaining === 0 || this.editorReplacementActive || this.deferUserMessages || this.session === void 0 || this.state.appState.model.trim().length === 0 || !hasActiveTurn || item?.mode !== "channel") return false;
518006
+ if (this.isShuttingDown || this.queueCommandRunning || this.queueSteerInFlight !== void 0 || this.editorReplacementActive || this.deferUserMessages || this.session === void 0 || this.state.appState.model.trim().length === 0 || !hasActiveTurn || item?.mode !== "channel") return false;
517947
518007
  return true;
517948
518008
  }
517949
518009
  async deliverQueuedChannelHead() {
@@ -517972,7 +518032,8 @@ var BlunTUI = class {
517972
518032
  }
517973
518033
  const batchEnd = item.channelAttention === true ? 1 : item.channelUrgent === true ? this.state.queuedMessages.findIndex((queued) => queued.channelUrgent !== true) : item.channelDirect === true ? this.state.queuedMessages.findIndex((queued) => queued.channelDirect !== true) : item.channelAddressed === true ? this.state.queuedMessages.findIndex((queued) => queued.channelAddressed !== true) : this.state.queuedMessages.findIndex((queued) => queued.mode !== "channel" || queued.channelContextOnly === true || queued.channelAttention === true);
517974
518034
  const candidateCount = batchEnd < 0 ? this.state.queuedMessages.length : batchEnd;
517975
- const items = this.state.queuedMessages.slice(0, Math.min(candidateCount, this.queueFlushBatchRemaining));
518035
+ const deliveryLimit = this.queueFlushBatchRemaining > 0 ? this.queueFlushBatchRemaining : 1;
518036
+ const items = this.state.queuedMessages.slice(0, Math.min(candidateCount, deliveryLimit));
517976
518037
  if (items.length === 0) return false;
517977
518038
  this.state.queuedMessages = this.state.queuedMessages.slice(items.length);
517978
518039
  this.updateQueueDisplay();
@@ -518011,9 +518072,14 @@ var BlunTUI = class {
518011
518072
  stepStarted: false,
518012
518073
  turnEnded: false,
518013
518074
  onCommit: () => {
518075
+ this.clearAddressedChannelFocusIds(items.map((queued) => queued.channelFocusId));
518014
518076
  renderTranscripts();
518015
518077
  for (const queued of queuedItems) queued.channelTranscriptRendered = true;
518016
- for (const queued of items) queued.channelAcknowledge?.();
518078
+ const acknowledge = () => {
518079
+ for (const queued of items) queued.channelAcknowledge?.();
518080
+ };
518081
+ if (inFlight.duplicate === true) acknowledge();
518082
+ else this.deferChannelAcknowledgement(inFlight.turnId, acknowledge);
518017
518083
  if (this.queueFlushBatchRemaining > 0) this.queueFlushBatchRemaining = Math.max(0, this.queueFlushBatchRemaining - items.length);
518018
518084
  },
518019
518085
  onRestore: () => {
@@ -518042,7 +518108,10 @@ var BlunTUI = class {
518042
518108
  if (!result.accepted) return this.recoverRejectedActiveSteer(inFlight);
518043
518109
  inFlight.turnId = String(result.turnId);
518044
518110
  inFlight.accepted = true;
518045
- if (result.duplicate === true) return this.commitQueuedSteer(inFlight);
518111
+ if (result.duplicate === true) {
518112
+ inFlight.duplicate = true;
518113
+ return this.commitQueuedSteer(inFlight);
518114
+ }
518046
518115
  if (inFlight.turnEnded) return restoreHead();
518047
518116
  if (!inFlight.stepStarted) {
518048
518117
  inFlight.confirmationTimer = setTimeout(() => {
@@ -518071,12 +518140,27 @@ var BlunTUI = class {
518071
518140
  const index = this.pendingChannelReplyGuards.indexOf(guard);
518072
518141
  if (index >= 0) this.pendingChannelReplyGuards.splice(index, 1);
518073
518142
  }
518143
+ settleChannelReplyToolResult(matchedCall, isError) {
518144
+ if (matchedCall === void 0 || isError) return;
518145
+ if (!/(?:^|__|:)(?:reply|edit_message)$/i.test(matchedCall.name ?? "")) return;
518146
+ const chatId = String(matchedCall.args?.["chat_id"] ?? matchedCall.args?.["chatId"] ?? "");
518147
+ if (chatId.length === 0) return;
518148
+ const delivered = this.pendingChannelReplyGuards.filter((guard) => guard.chatId === chatId);
518149
+ if (delivered.length === 0) return;
518150
+ this.pendingChannelReplyGuards = this.pendingChannelReplyGuards.filter((guard) => guard.chatId !== chatId);
518151
+ this.clearAddressedChannelFocusIds(delivered.map((guard) => guard.channelFocusId));
518152
+ for (const guard of delivered) if (guard.directFocus === true) this.directFocusController.noteReplyDelivered(chatId);
518153
+ this.updateAddressedChannelFocus();
518154
+ }
518074
518155
  pinAddressedChannelFocus(chatId, origin, text) {
518075
518156
  return this.addressedChannelFocusStore.pin({ chatId, origin, text });
518076
518157
  }
518077
518158
  clearAddressedChannelFocus(chatId) {
518078
518159
  return this.addressedChannelFocusStore.clearChat(chatId);
518079
518160
  }
518161
+ clearAddressedChannelFocusIds(ids) {
518162
+ return this.addressedChannelFocusStore.clearIds(ids.filter((id) => id !== void 0));
518163
+ }
518080
518164
  updateAddressedChannelFocus() {
518081
518165
  this.state.channelFocusContainer.clear();
518082
518166
  const queuedFocusIds = new Set([
@@ -518115,11 +518199,24 @@ var BlunTUI = class {
518115
518199
  if (inFlight.stepStarted || inFlight.userCancelled === true) this.commitQueuedSteer(inFlight);
518116
518200
  else if (inFlight.accepted) this.restoreQueuedSteer(inFlight);
518117
518201
  }
518202
+ deferChannelAcknowledgement(turnId, acknowledge) {
518203
+ const key = String(turnId);
518204
+ const pending = this.deferredChannelAcknowledgements.get(key) ?? [];
518205
+ pending.push(acknowledge);
518206
+ this.deferredChannelAcknowledgements.set(key, pending);
518207
+ }
518208
+ flushDeferredChannelAcknowledgements(turnId) {
518209
+ const key = String(turnId);
518210
+ const pending = this.deferredChannelAcknowledgements.get(key);
518211
+ if (pending === void 0) return;
518212
+ this.deferredChannelAcknowledgements.delete(key);
518213
+ for (const acknowledge of pending) acknowledge();
518214
+ }
518118
518215
  commitChannelPromptAtStepStart(turnId) {
518119
518216
  const inFlight = this.channelPromptInFlight;
518120
518217
  if (inFlight === void 0 || inFlight.turnId !== String(turnId)) return;
518121
518218
  this.channelPromptInFlight = void 0;
518122
- inFlight.acknowledge();
518219
+ this.deferChannelAcknowledgement(inFlight.turnId, inFlight.acknowledge);
518123
518220
  }
518124
518221
  settleChannelPromptAtTurnEnd(turnId, reason) {
518125
518222
  const inFlight = this.channelPromptInFlight;
@@ -518442,9 +518539,7 @@ var BlunTUI = class {
518442
518539
  else if (directEnvelope !== void 0) enqueueTelegramDirect(this.state.queuedMessages, item);
518443
518540
  else if (addressed) enqueueTelegramAddressed(this.state.queuedMessages, item);
518444
518541
  else this.state.queuedMessages.push(item);
518445
- const activeTurn = this.streamingUI.hasActiveTurn() || this.state.appState.streamingPhase !== "idle" && this.state.appState.streamingPhase !== "shell";
518446
- if (activeTurn) this.releaseNextQueuedMessage();
518447
- if (this.queueFlushBatchRemaining > 0) this.channelQueueDeadline.requestDeliveryNow();
518542
+ this.channelQueueDeadline.requestDeliveryNow();
518448
518543
  this.scheduleQueueDrain();
518449
518544
  this.track("input_queue");
518450
518545
  this.updateQueueDisplay();
@@ -518639,6 +518734,7 @@ var BlunTUI = class {
518639
518734
  }, imagePart] : focusedModelInput;
518640
518735
  session.promptAccepted(promptInput, channelPromptOrigin(channelReportSources)).then((result) => {
518641
518736
  if (result.duplicate === true) {
518737
+ if (channelFocusId !== void 0) this.clearAddressedChannelFocusIds([channelFocusId]);
518642
518738
  finishPersonalMemoryRememberIntentTurn(session.id);
518643
518739
  if (installedGuard !== void 0 && this.pendingChannelReplyGuard === installedGuard) this.pendingChannelReplyGuard = previousGuard;
518644
518740
  this.setAppState({ streamingPhase: "idle" });
@@ -518650,6 +518746,7 @@ var BlunTUI = class {
518650
518746
  return;
518651
518747
  }
518652
518748
  if (result.accepted) {
518749
+ if (channelFocusId !== void 0) this.clearAddressedChannelFocusIds([channelFocusId]);
518653
518750
  if (acknowledge !== void 0 && result.turnId !== null && result.turnId !== void 0) {
518654
518751
  this.channelPromptInFlight = {
518655
518752
  turnId: String(result.turnId),
@@ -518783,7 +518880,7 @@ var BlunTUI = class {
518783
518880
  const args = entry.toolCallData?.args;
518784
518881
  return String(args?.["chat_id"] ?? args?.["chatId"] ?? "") === guard.chatId;
518785
518882
  }) || outboxGrewForChat(guard.outboxMarker, guard.chatId)) {
518786
- this.clearAddressedChannelFocus?.(guard.chatId);
518883
+ this.clearAddressedChannelFocusIds?.([guard.channelFocusId]);
518787
518884
  if (guard.directFocus === true) this.directFocusController.noteReplyDelivered(guard.chatId);
518788
518885
  continue;
518789
518886
  }
@@ -518809,7 +518906,7 @@ var BlunTUI = class {
518809
518906
  if (finalText.length === 0) continue;
518810
518907
  sendReplyFallback(guard.chatId, finalText, guard.contextOnly).then((sent) => {
518811
518908
  if (!sent) return;
518812
- this.clearAddressedChannelFocus?.(guard.chatId);
518909
+ this.clearAddressedChannelFocusIds?.([guard.channelFocusId]);
518813
518910
  if (guard.directFocus === true) this.directFocusController.noteReplyDelivered(guard.chatId);
518814
518911
  this.showStatus(uiText("blunTui.telegram.fallbackDelivered"));
518815
518912
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.494",
3
+ "version": "9.1.496",
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": {