blun-king-cli 9.1.488 → 9.1.490

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 ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## 9.1.490 - 2026-08-30
4
+
5
+ - Corrects the packaged release history by recording the unpublished 9.1.487 build and replacing the incomplete 9.1.488 summary with the changes verified from the release commits.
6
+ - Carries forward the TodoList, queue-control, Telegram-delivery, AgentSpine profile bootstrap, and managed RTX image-reading repairs from 9.1.489 without runtime changes.
7
+
8
+ ## 9.1.489 - 2026-08-30
9
+
10
+ - Restores the visible TodoList as a core tool and requires active goals to keep it truthful from first action through completion.
11
+ - Makes Ctrl+S release the current queue batch exactly once and Escape release one queued message per press before cancelling an empty-queue run.
12
+ - Defers Telegram acknowledgement until real execution starts, restores unexpectedly cancelled pre-step work, and prevents duplicate queue execution or rendering.
13
+ - Installs and enables the bundled AgentSpine plugin for every existing local BLUN profile, including Fredrik, Werner, and Manfred profiles on the same installation.
14
+ - Routes image understanding through the managed BLUN media service on RTX infrastructure before any optional experimental local reader.
15
+
16
+ ## 9.1.488 - 2026-08-30
17
+
18
+ - Published the managed AgentSpine core-plugin bundle prepared in the 9.1.487 build.
19
+ - Advanced returning relationship status, kept telemetry disabled when no endpoint is configured, and preserved the selected reasoning effort across successful tool steps.
20
+ - Cleared duplicate Telegram ghost turns and added regression coverage for Telegram intake, reload queues, session resume, and effort preservation.
21
+
22
+ ## 9.1.487 - 2026-08-30 (not published)
23
+
24
+ - Prepared AgentSpine as a managed core plugin in the BLUN King package and installer.
25
+ - This version was never published to npm; 9.1.488 was the next public package after 9.1.486.
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.488
12
+ npm install -g blun-king-cli@9.1.490
13
13
 
14
14
  Start
15
15
  -----
@@ -63,12 +63,14 @@ ab, ohne den geschriebenen Entwurf zu löschen. Das gilt auch bei
63
63
  Autovervollständigung, Geistervorschlägen, Bash-Eingabe und einer offenen
64
64
  Mehrzeileneingabe.
65
65
 
66
- Version 9.1.488 stellt außerdem die interne Todo-Liste wieder her. Deutsche
66
+ Version 9.1.490 stellt außerdem die interne Todo-Liste wieder her. Deutsche
67
67
  Aufgabenlisten-Befehle wählen wieder TodoList statt der getrennten TaskList für
68
68
  Hintergrundprozesse. Lesen, Ersetzen, Aktualisieren und Leeren funktionieren in
69
69
  derselben Sitzung; eine vollständig erledigte Liste wird aus der Anzeige
70
- entfernt. Adressierte Telegram-Nachrichten erscheinen nicht mehr gleichzeitig
71
- im Fokusbereich und in der Warteschlange.
70
+ entfernt. AgentSpine wird beim Start in allen vorhandenen lokalen Profilen
71
+ installiert und aktiviert. Bilder laufen vorrangig über den verwalteten
72
+ BLUN-Bildlesedienst auf der RTX-Infrastruktur; ein lokaler experimenteller
73
+ Leser ist nur noch ein Fallback, wenn kein verwalteter Mediadienst existiert.
72
74
 
73
75
  Telegram-Nachrichten, die bei bereits aktivem Kernzug eintreffen, werden ohne
74
76
  konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
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.488
12
+ npm install -g blun-king-cli@9.1.490
13
13
  ```
14
14
 
15
15
  ## Reproduzierbares Staging und Packen
@@ -79,12 +79,14 @@ konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
79
79
  FIFO-Warteschlange. Für diesen normalen Wartestatus erscheint kein
80
80
  `turn.agent_busy`-Fehler.
81
81
 
82
- Version 9.1.488 stellt außerdem die interne Todo-Liste wieder her. Deutsche
82
+ Version 9.1.490 stellt außerdem die interne Todo-Liste wieder her. Deutsche
83
83
  Aufgabenlisten-Befehle wählen wieder TodoList statt der getrennten TaskList für
84
84
  Hintergrundprozesse. Lesen, Ersetzen, Aktualisieren und Leeren funktionieren in
85
85
  derselben Sitzung; eine vollständig erledigte Liste wird aus der Anzeige
86
- entfernt. Adressierte Telegram-Nachrichten erscheinen nicht mehr gleichzeitig
87
- im Fokusbereich und in der Warteschlange.
86
+ entfernt. AgentSpine wird beim Start in allen vorhandenen lokalen Profilen
87
+ installiert und aktiviert. Bilder laufen vorrangig über den verwalteten
88
+ BLUN-Bildlesedienst auf der RTX-Infrastruktur; ein lokaler experimenteller
89
+ Leser ist nur noch ein Fallback, wenn kein verwalteter Mediadienst existiert.
88
90
 
89
91
  Ab BLUN King 9.1.416 bleiben alle Ergebnisse des jüngsten Werkzeugaufrufs bis
90
92
  zur nächsten Assistentenantwort vollständig im Modellkontext. Das gilt auch für
@@ -282,6 +282,38 @@ function installAgentSpineForProfile(packageRoot, blunDir) {
282
282
  installManagedAgentSpinePlugin({ packageRoot, blunDir });
283
283
  }
284
284
 
285
+ function installAgentSpineForExistingProfiles(packageRoot, sharedHome, currentProfileHome) {
286
+ const profileRoot = path.join(sharedHome, 'profile');
287
+ const profileHomes = new Map();
288
+ const addProfileHome = (profileHome) => {
289
+ const resolved = path.resolve(profileHome);
290
+ const key = process.platform === 'win32' ? resolved.toLowerCase() : resolved;
291
+ profileHomes.set(key, resolved);
292
+ };
293
+
294
+ addProfileHome(currentProfileHome);
295
+ if (fs.existsSync(profileRoot)) {
296
+ for (const entry of fs.readdirSync(profileRoot, { withFileTypes: true })) {
297
+ if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
298
+ let profileHome;
299
+ try {
300
+ profileHome = resolveProfilePaths(sharedHome, entry.name).home;
301
+ } catch {
302
+ continue;
303
+ }
304
+ const stat = fs.lstatSync(profileHome);
305
+ if (!stat.isDirectory() || stat.isSymbolicLink()) continue;
306
+ addProfileHome(profileHome);
307
+ }
308
+ }
309
+
310
+ for (const profileHome of profileHomes.values()) {
311
+ ensurePrivateDirectory(profileHome);
312
+ installAgentSpineForProfile(packageRoot, profileHome);
313
+ }
314
+ return [...profileHomes.values()];
315
+ }
316
+
285
317
  function createManagedNodeEnvironment(binary, env = process.env, platform = process.platform) {
286
318
  const pathImpl = platform === 'win32' ? path.win32 : path.posix;
287
319
  const separator = platform === 'win32' ? path.win32.delimiter : path.posix.delimiter;
@@ -511,7 +543,7 @@ async function runLauncher(options = {}) {
511
543
  });
512
544
  seedConfiguredMnemoMcp({ blunDir, env: process.env });
513
545
  seedStandardSkills({ packageRoot: PKG, blunDir: privatePaths.sharedHome });
514
- installAgentSpineForProfile(PKG, blunDir);
546
+ installAgentSpineForExistingProfiles(PKG, privatePaths.sharedHome, blunDir);
515
547
  if (mode === LAUNCHER_MODES.KING) installTelegramForProfile(PKG, blunDir);
516
548
 
517
549
  const nativeModules = repairConfiguredNativeModules({
@@ -555,6 +587,7 @@ async function runLauncher(options = {}) {
555
587
  module.exports = {
556
588
  clearStartupUpdateViewport,
557
589
  createManagedNodeEnvironment,
590
+ installAgentSpineForExistingProfiles,
558
591
  resolveLauncherPrivatePaths,
559
592
  runLauncher,
560
593
  shouldResumeAfterStartupUpdate,
@@ -68,6 +68,12 @@ class TelegramDeliveryLedger {
68
68
  return identity;
69
69
  }
70
70
 
71
+ forget(input, origin) {
72
+ const identity = this.identity(input, origin);
73
+ if (!identity) return false;
74
+ return this.identities.delete(identity);
75
+ }
76
+
71
77
  snapshot() {
72
78
  return [...this.identities];
73
79
  }
@@ -46,6 +46,7 @@ const CORE_TOOL_NAMES = Object.freeze([
46
46
  'Grep',
47
47
  'Write',
48
48
  'Glob',
49
+ 'TodoList',
49
50
  'mcp__plugin-telegram_telegram__reply',
50
51
  ]);
51
52
 
package/blun.mjs CHANGED
@@ -30516,7 +30516,7 @@ var init_input_schema = __esmMin((() => {
30516
30516
  var todo_list_default;
30517
30517
  var init_todo_list$1 = __esmMin((() => {
30518
30518
  todo_list_default = "Use this tool to maintain a structured TODO list as you work through a multi-step task. Use it proactively and often when progress tracking helps the current work. This is especially useful in long-running investigations and implementation tasks with several tool calls; in plan mode, write the plan to the plan file rather than tracking it here.\n\n**When to use:**\n- Multi-step tasks that span several tool calls\n- Tracking investigation progress across a large codebase search\n- Planning a sequence of edits before making them\n- After receiving new multi-step instructions, capture the requirements as todos\n- Before starting a tracked task, mark exactly one item as `in_progress`\n- Immediately after finishing a tracked task, record its truthful terminal state; do not batch completions at the end\n\n**When NOT to use:**\n- Single-shot answers that complete in one or two tool calls\n- Trivial requests where tracking adds no clarity\n- Purely conversational or informational replies\n\n**Avoid churn:**\n- Do not re-call this tool when nothing meaningful has changed since the last call — update the list only after real progress.\n- When unsure of the current state, call query mode first (omit `todos`) to check the list before deciding what to update.\n- If no available tool can move any task forward, tell the user where you are stuck instead of repeatedly re-ordering the same todos.\n\n**How to use:**\n- Call with `todos: [...]` to replace the full list. Statuses: pending / in_progress / done / blocked / waiting_approval / aborted.\n- Call with no `todos` argument to retrieve the current list without changing it.\n- Call with `todos: []` to clear the list.\n- Keep titles short and actionable (e.g. \"Read session-control.ts\", \"Add planMode flag to TurnManager\").\n- Update statuses as you make progress.\n- When work is underway, keep exactly one task `in_progress`.\n- Use `done` only when fully accomplished. Use `blocked` with the reason and the action that would unblock it, `waiting_approval` with the prepared result that awaits approval, and `aborted` with the last reached state.\n- Never mark a task `done` if tests are failing, implementation is partial, unresolved errors remain, or required files/dependencies could not be found.\n";
30519
- todo_list_default = "Maintain a structured TODO list when it adds clarity to multi-step work; skip it for conversation or a single-shot task. In plan mode, use the plan file instead.\n\nCall with `todos: [...]` to replace the full list. Statuses are `pending`, `in_progress`, `done`, `blocked`, `waiting_approval`, and `aborted`. Omit `todos` to query the current list; use `todos: []` to clear it.\n\nKeep titles short and actionable. Capture the real requirements, keep exactly one item `in_progress` while tracked work is active, and update a status immediately after real progress instead of batching or rewriting an unchanged list. Use `done` only for fully verified work. A `blocked` item names the reason and the action that can unblock it; `waiting_approval` names the prepared result awaiting approval; `aborted` records the last reached state. Before finishing, reconcile every tracked item truthfully. If no available tool can move the work forward, report the blocker to the user instead of churning the list.";
30519
+ todo_list_default = "Maintain the agent's visible BLUN TUI task/TODO list as you work through a multi-step task. This is the Aufgaben/Task/Todo panel shown by the interface, not a file, Cron job, scheduled task, or background-agent list. Use it proactively for investigations and implementation work spanning several tool calls; skip it only for conversation or a genuinely single-shot task. In plan mode, use the plan file instead.\n\nStart multi-step work by replacing the list with the real requirements. Keep exactly one item `in_progress`, update it immediately after real progress, and move the next item to `in_progress` instead of batching status changes at the end. Call with `todos: [...]` to replace the full list. Statuses are `pending`, `in_progress`, `done`, `blocked`, `waiting_approval`, and `aborted`. Omit `todos` to query the current list; use `todos: []` to clear a finished non-goal list.\n\nKeep titles short and actionable. Use `done` only for fully verified work. A `blocked` item names the reason and the action that can unblock it; `waiting_approval` names the prepared result awaiting approval; `aborted` records the last reached state. Before finishing, reconcile every tracked item truthfully. If no available tool can move the work forward, report the blocker to the user instead of churning the list.";
30520
30520
  }));
30521
30521
  //#endregion
30522
30522
  //#region ../../packages/agent-core/src/tools/builtin/state/todo-list.ts
@@ -261582,7 +261582,7 @@ function blunPendingMediaSystemPrompt(systemPrompt, pendingMediaJobIds) {
261582
261582
  function blunFastConversationTools(tools, input, pendingMediaJobIds = [], requiredToolNames = /* @__PURE__ */ new Set()) {
261583
261583
  const telegramTurn = BLUN_TELEGRAM_CHANNEL_RE.test(blunExtractText(input));
261584
261584
  const mediaPending = pendingMediaJobIds.length > 0;
261585
- return tools.filter((tool) => telegramTurn && BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name) || mediaPending && tool.name === "GetMedia" || requiredToolNames.has(tool.name));
261585
+ return tools.filter((tool) => tool.name === "TodoList" || telegramTurn && BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name) || mediaPending && tool.name === "GetMedia" || requiredToolNames.has(tool.name));
261586
261586
  }
261587
261587
  /**
261588
261588
  * Keep frequently used native tools resident and expose every other tool by
@@ -262474,6 +262474,9 @@ var init_turn = __esmMin((() => {
262474
262474
  this.agent.usage.endTurn();
262475
262475
  this.flushCognitiveToolEvidence(turnId, true);
262476
262476
  this.recordCognitiveStage("endTurn", { turnId, reason: ended.reason, durationMs: ended.durationMs ?? 0 });
262477
+ if (ended.reason === "cancelled" && (this.currentStepByTurn.get(turnId) ?? 0) === 0) {
262478
+ this.telegramDeliveryLedger.forget(input, origin);
262479
+ }
262477
262480
  this.agent.emitEvent(ended);
262478
262481
  return ended;
262479
262482
  }
@@ -262856,6 +262859,8 @@ var init_turn = __esmMin((() => {
262856
262859
  return { continue: false };
262857
262860
  },
262858
262861
  prepareToolExecution: async (ctx) => {
262862
+ const goalTodoPolicy = enforceGoalTodoPolicy(this.agent, ctx);
262863
+ if (goalTodoPolicy !== void 0) return goalTodoPolicy;
262859
262864
  const ideaPolicy = enforceIdeaToolPolicy(this.agent, ctx);
262860
262865
  if (ideaPolicy !== void 0) return ideaPolicy;
262861
262866
  const cached = deduper.checkSameStep(ctx.toolCall.id, ctx.toolCall.name, ctx.args);
@@ -263186,6 +263191,7 @@ var init_update_goal = __esmMin((() => {
263186
263191
  }
263187
263192
  if (args.status === "complete") {
263188
263193
  const completed = await goal.markComplete({}, "model");
263194
+ if (completed !== null) this.agent.tools.updateStore(TODO_STORE_KEY, []);
263189
263195
  if (completed !== null) this.agent.context.appendSystemReminder(buildGoalCompletionSummaryPrompt(completed), {
263190
263196
  kind: "system_trigger",
263191
263197
  name: GOAL_COMPLETION_REMINDER_NAME
@@ -265806,6 +265812,7 @@ var init_agent = __esmMin((() => {
265806
265812
 
265807
265813
  - Answer the user's current conversational message directly and concisely.
265808
265814
  - Use the language of the user's latest conversational message unless the persona or current session specifies a language override.
265815
+ - When the user assigns multi-step work, start and maintain the visible BLUN TodoList before doing the work; update it after real progress and reconcile it before finishing. TodoList is not Cron, a file, or a background-task list.
265809
265816
  - Do not claim to have inspected files, run commands, used tools, or checked current external state.
265810
265817
  - If the message actually requires work or live information, say that plainly instead of inventing a result.`].filter((block) => block.length > 0).join("\n\n");
265811
265818
  }
@@ -297715,7 +297722,7 @@ var init_session$1 = __esmMin((() => {
297715
297722
  this.log = this.logHandle?.logger ?? (options.id === void 0 ? log : log.createChild({ sessionId: options.id }));
297716
297723
  this.rpc = options.rpc;
297717
297724
  this.experimentalFlags = options.experimentalFlags ?? new FlagResolver();
297718
- this.visionReader = (this.experimentalFlags.enabled("vision_reader") ? createVisionReader(options.config?.services?.visionReader) : void 0) ?? createManagedMediaVisionReader(options.toolServices?.media);
297725
+ this.visionReader = createManagedMediaVisionReader(options.toolServices?.media) ?? (this.experimentalFlags.enabled("vision_reader") ? createVisionReader(options.config?.services?.visionReader) : void 0);
297719
297726
  this.hookEngine = new HookEngine(options.hooks, {
297720
297727
  cwd: options.kaos.getcwd(),
297721
297728
  sessionId: options.id
@@ -423194,6 +423201,31 @@ function validInitialIdeaPlan(value) {
423194
423201
  }
423195
423202
  return active === 1;
423196
423203
  }
423204
+ function enforceGoalTodoPolicy(agent, context) {
423205
+ if (isIdeaGoal(agent) || agent.goal.getActiveGoal() === null) return;
423206
+ const todos = ideaTodos(agent);
423207
+ if (todos.length === 0) {
423208
+ const firstCall = context.toolCalls[0];
423209
+ if (context.toolCall.id !== firstCall?.id || context.toolCall.name !== "TodoList") return {
423210
+ block: true,
423211
+ reason: "Active multi-step goals require TodoList to be the first tool call. Publish and maintain the visible task list before research or action."
423212
+ };
423213
+ if (!validInitialIdeaPlan(context.args?.todos)) return {
423214
+ block: true,
423215
+ reason: "The first goal TodoList must contain at least one item, exactly one in_progress item, and only pending items otherwise."
423216
+ };
423217
+ return;
423218
+ }
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 {
423225
+ 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."
423227
+ };
423228
+ }
423197
423229
  function ideaOutboundChannel(toolName, args) {
423198
423230
  if (toolName === "Bash") {
423199
423231
  const command = typeof args?.command === "string" ? args.command : "";
@@ -505490,6 +505522,7 @@ var EditorKeyboardController = class {
505490
505522
  this.clearPendingUndoEsc();
505491
505523
  return;
505492
505524
  }
505525
+ host.markCurrentRunCancelledByUser();
505493
505526
  this.cancelCurrentStream();
505494
505527
  this.clearPendingUndoEsc();
505495
505528
  return;
@@ -505508,6 +505541,7 @@ var EditorKeyboardController = class {
505508
505541
  this.clearPendingUndoEsc();
505509
505542
  return;
505510
505543
  }
505544
+ host.markCurrentRunCancelledByUser();
505511
505545
  this.cancelCurrentStream();
505512
505546
  this.clearPendingUndoEsc();
505513
505547
  return;
@@ -508774,6 +508808,7 @@ var SessionEventHandler = class {
508774
508808
  this.clearModelRetryHint();
508775
508809
  this.turnWatchdog.stop();
508776
508810
  this.host.restoreQueuedSteerAtTurnEnd(event.turnId);
508811
+ this.host.settleChannelPromptAtTurnEnd(event.turnId, event.reason);
508777
508812
  const sessionId = this.host.session?.id;
508778
508813
  if (sessionId !== void 0) finishPersonalMemoryRememberIntentTurn(sessionId, event.turnId);
508779
508814
  this.host.streamingUI.flushNow();
@@ -508800,6 +508835,7 @@ var SessionEventHandler = class {
508800
508835
  this.turnWatchdog.recordProgress();
508801
508836
  this.clearModelRetryHint();
508802
508837
  this.host.commitQueuedSteerAtStepStart(event.turnId);
508838
+ this.host.commitChannelPromptAtStepStart(event.turnId);
508803
508839
  this.host.streamingUI.flushNow();
508804
508840
  this.host.streamingUI.setStep(event.step);
508805
508841
  this.host.streamingUI.resetToolUi();
@@ -509110,6 +509146,7 @@ var SessionEventHandler = class {
509110
509146
  const { state } = this.host;
509111
509147
  if (change.kind === "completion" && event.snapshot !== null) {
509112
509148
  this.pendingModelBlockedFallback = void 0;
509149
+ this.host.streamingUI.setTodoList([]);
509113
509150
  this.goalCompletionAwaitingClear = true;
509114
509151
  this.goalCompletionTurnEnded = false;
509115
509152
  this.host.appendTranscriptEntry({
@@ -517555,7 +517592,7 @@ var BlunTUI = class {
517555
517592
  sessionId: this.session?.id ?? "",
517556
517593
  goal: this.state.appState.goal,
517557
517594
  permissionMode: this.state.appState.permissionMode,
517558
- idle: this.session !== void 0 && !this.isShuttingDown && !this.aborted && !this.queueCommandRunning && this.queueSteerInFlight === void 0 && !this.editorReplacementActive && !this.deferUserMessages && !this.streamingUI.hasActiveTurn() && this.state.appState.streamingPhase === "idle" && !this.state.appState.isCompacting && this.state.queuedMessages.length === 0
517595
+ idle: this.session !== void 0 && !this.isShuttingDown && !this.aborted && !this.queueCommandRunning && this.queueSteerInFlight === void 0 && this.channelPromptInFlight === void 0 && !this.editorReplacementActive && !this.deferUserMessages && !this.streamingUI.hasActiveTurn() && this.state.appState.streamingPhase === "idle" && !this.state.appState.isCompacting && this.state.queuedMessages.length === 0
517559
517596
  };
517560
517597
  }
517561
517598
  syncGoalTimeTrigger() {
@@ -517894,6 +517931,7 @@ var BlunTUI = class {
517894
517931
  telegramRemoteCommandContext;
517895
517932
  queueFlushBatchRemaining = 0;
517896
517933
  queueSteerInFlight;
517934
+ channelPromptInFlight;
517897
517935
  editorReplacementActive = false;
517898
517936
  editorReplacement;
517899
517937
  editorReplacementGeneration = 0;
@@ -518064,14 +518102,38 @@ var BlunTUI = class {
518064
518102
  this.state.ui.requestRender();
518065
518103
  return true;
518066
518104
  }
518105
+ markCurrentRunCancelledByUser() {
518106
+ if (this.channelPromptInFlight !== void 0) this.channelPromptInFlight.userCancelled = true;
518107
+ if (this.queueSteerInFlight !== void 0) this.queueSteerInFlight.userCancelled = true;
518108
+ }
518067
518109
  restoreQueuedSteerAtTurnEnd(turnId) {
518068
518110
  const turnKey = String(turnId);
518069
518111
  const inFlight = this.queueSteerInFlight;
518070
518112
  if (inFlight === void 0 || inFlight.turnId !== turnKey) return;
518071
518113
  inFlight.turnEnded = true;
518072
- if (inFlight.stepStarted) this.commitQueuedSteer(inFlight);
518114
+ if (inFlight.stepStarted || inFlight.userCancelled === true) this.commitQueuedSteer(inFlight);
518073
518115
  else if (inFlight.accepted) this.restoreQueuedSteer(inFlight);
518074
518116
  }
518117
+ commitChannelPromptAtStepStart(turnId) {
518118
+ const inFlight = this.channelPromptInFlight;
518119
+ if (inFlight === void 0 || inFlight.turnId !== String(turnId)) return;
518120
+ this.channelPromptInFlight = void 0;
518121
+ inFlight.acknowledge();
518122
+ }
518123
+ settleChannelPromptAtTurnEnd(turnId, reason) {
518124
+ const inFlight = this.channelPromptInFlight;
518125
+ if (inFlight === void 0 || inFlight.turnId !== String(turnId)) return;
518126
+ this.channelPromptInFlight = void 0;
518127
+ if (reason !== "cancelled" || inFlight.userCancelled === true) {
518128
+ inFlight.acknowledge();
518129
+ return;
518130
+ }
518131
+ inFlight.onRestore?.();
518132
+ this.state.queuedMessages = [inFlight.item, ...this.state.queuedMessages];
518133
+ this.syncChannelQueueDeadline();
518134
+ this.updateQueueDisplay();
518135
+ this.state.ui.requestRender();
518136
+ }
518075
518137
  commitQueuedSteerIfReady(inFlight) {
518076
518138
  if (this.queueSteerInFlight !== inFlight || !inFlight.accepted || !inFlight.stepStarted || inFlight.turnEnded) return;
518077
518139
  this.commitQueuedSteer(inFlight);
@@ -518181,7 +518243,7 @@ var BlunTUI = class {
518181
518243
  canDrainQueue() {
518182
518244
  const head = this.state.queuedMessages[0];
518183
518245
  const directWork = head?.channelDirect === true || head?.channelUrgent === true || head?.mode === "channel-command" && /^[1-9]\d*$/u.test(String(head.channelChatId ?? ""));
518184
- return !this.isShuttingDown && !this.queueCommandRunning && this.queueSteerInFlight === void 0 && !this.editorReplacementActive && !this.deferUserMessages && !this.streamingUI.hasActiveTurn() && this.state.appState.streamingPhase === "idle" && !this.state.appState.isCompacting && (!this.directFocusController.isPaused() || directWork);
518246
+ return !this.isShuttingDown && !this.queueCommandRunning && this.queueSteerInFlight === void 0 && this.channelPromptInFlight === void 0 && !this.editorReplacementActive && !this.deferUserMessages && !this.streamingUI.hasActiveTurn() && this.state.appState.streamingPhase === "idle" && !this.state.appState.isCompacting && (!this.directFocusController.isPaused() || directWork);
518185
518247
  }
518186
518248
  scheduleQueueDrain() {
518187
518249
  if (this.queueDrainTimer !== void 0 || !this.canDrainQueue()) return;
@@ -518585,7 +518647,32 @@ var BlunTUI = class {
518585
518647
  return;
518586
518648
  }
518587
518649
  if (result.accepted) {
518588
- acknowledge?.();
518650
+ if (acknowledge !== void 0 && result.turnId !== null && result.turnId !== void 0) {
518651
+ this.channelPromptInFlight = {
518652
+ turnId: String(result.turnId),
518653
+ acknowledge,
518654
+ item: {
518655
+ text: modelInput,
518656
+ displayText,
518657
+ origin,
518658
+ agentId: this.harness.interactiveAgentId,
518659
+ mode: "channel",
518660
+ channelChatId,
518661
+ channelReportSources: channelReportSources,
518662
+ channelContextOnly: contextOnly,
518663
+ channelTranscriptRendered: true,
518664
+ channelAcknowledge: acknowledge,
518665
+ ...channelFocusId === void 0 ? {} : { channelFocusId, channelAddressed: true },
518666
+ ...channelAttention === true ? { channelAttention: true } : {},
518667
+ ...channelDirect === true ? { channelDirect: true, channelDirectResume } : {},
518668
+ ...queueKey === void 0 ? {} : { queueKey },
518669
+ ...channelImagePath === void 0 ? {} : { channelImagePath }
518670
+ },
518671
+ onRestore: () => {
518672
+ if (installedGuard !== void 0 && this.pendingChannelReplyGuard === installedGuard) this.pendingChannelReplyGuard = previousGuard;
518673
+ }
518674
+ };
518675
+ } else acknowledge?.();
518589
518676
  return;
518590
518677
  }
518591
518678
  if (installedGuard !== void 0 && this.pendingChannelReplyGuard === installedGuard) this.pendingChannelReplyGuard = previousGuard;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.488",
3
+ "version": "9.1.490",
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": {
@@ -39,6 +39,7 @@
39
39
  "standard-tools/",
40
40
  "agent-spine-plugin/",
41
41
  "telegram-plugin/",
42
+ "CHANGELOG.md",
42
43
  "LIESMICH.txt"
43
44
  ],
44
45
  "keywords": [