blun-king-cli 9.1.213 → 9.1.215

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.
@@ -30,6 +30,7 @@ const { acquireSharedRuntimeLease, tryAcquireUpdateLease } = require('./update-l
30
30
  const { compareSemver, runExplicitUpdate, runUpdateNotice } = require('./update-notice');
31
31
  const {
32
32
  RUNNING_UPDATE_HANDOFF_EXIT_CODE,
33
+ RUNNING_UPDATE_HANDOFF_ACK_MESSAGE,
33
34
  RUNNING_UPDATE_HANDOFF_MESSAGE,
34
35
  RUNNING_UPDATE_MODE_MESSAGE,
35
36
  RUNNING_UPDATE_PREPARED_MESSAGE,
@@ -397,12 +398,26 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
397
398
  handoffSessionId = message.sessionId;
398
399
  handoffMode = runningUpdateMode;
399
400
  handoffCwd = resolveHandoffCwd(message.cwd, cwd);
400
- if (preparedTarget !== undefined) {
401
- (options.stageRuntime || stageRuntime)(env.BLUN_SHARED_HOME, preparedTarget, {
402
- mode: handoffMode,
403
- sessionId: handoffSessionId,
404
- cwd: handoffCwd,
405
- });
401
+ if (preparedTarget !== undefined && message.version === preparedTarget.version) {
402
+ try {
403
+ (options.stageRuntime || stageRuntime)(env.BLUN_SHARED_HOME, preparedTarget, {
404
+ mode: handoffMode,
405
+ sessionId: handoffSessionId,
406
+ cwd: handoffCwd,
407
+ });
408
+ child.send({
409
+ type: RUNNING_UPDATE_HANDOFF_ACK_MESSAGE,
410
+ version: preparedTarget.version,
411
+ mode: handoffMode,
412
+ sessionId: handoffSessionId,
413
+ }, (error) => {
414
+ if (error) options.onRunningUpdateError?.(error);
415
+ });
416
+ } catch (error) {
417
+ handoffSessionId = undefined;
418
+ handoffMode = undefined;
419
+ options.onRunningUpdateError?.(error);
420
+ }
406
421
  }
407
422
  }
408
423
  };
@@ -471,6 +486,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
471
486
  },
472
487
  activateTarget: async (target) => {
473
488
  await (options.activateRuntime || activateRuntime)(env.BLUN_SHARED_HOME, target);
489
+ (options.clearPendingRuntime || clearPendingRuntime)(env.BLUN_SHARED_HOME, target);
474
490
  (options.recordRunningUpdateEvent || recordRunningUpdateEvent)(env.BLUN_SHARED_HOME, {
475
491
  event: 'activated',
476
492
  fromVersion: readPackageVersionAt(packageRoot),
@@ -485,6 +501,9 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
485
501
  }
486
502
  },
487
503
  });
504
+ if (handoff.kind !== 'activated') {
505
+ (options.clearPendingRuntime || clearPendingRuntime)(env.BLUN_SHARED_HOME, preparedTarget);
506
+ }
488
507
  const resumed = handoff.core;
489
508
  if (resumed === undefined || resumed.ready !== true) return 1;
490
509
  const resumedArgs = handoffArgsForMode(args, handoffMode, handoffSessionId);
@@ -16,6 +16,7 @@ const REGISTRY_URL = 'https://registry.npmjs.org/blun-king-cli';
16
16
  const ACTIVE_RUNTIME_FILE = 'active-runtime.json';
17
17
  const PENDING_RUNTIME_FILE = 'pending-runtime.json';
18
18
  const RUNNING_UPDATE_HANDOFF_EXIT_CODE = 76;
19
+ const RUNNING_UPDATE_HANDOFF_ACK_MESSAGE = 'blun-running-update-handoff-ack';
19
20
  const RUNNING_UPDATE_PREPARED_MESSAGE = 'blun-running-update-prepared';
20
21
  const RUNNING_UPDATE_HANDOFF_MESSAGE = 'blun-running-update-handoff';
21
22
  const RUNNING_UPDATE_MODE_MESSAGE = 'blun-running-update-mode';
@@ -559,6 +560,7 @@ module.exports = {
559
560
  PENDING_RUNTIME_FILE,
560
561
  AUTO_UPDATE_SETTLE_MS,
561
562
  RUNNING_UPDATE_HANDOFF_EXIT_CODE,
563
+ RUNNING_UPDATE_HANDOFF_ACK_MESSAGE,
562
564
  RUNNING_UPDATE_HANDOFF_MESSAGE,
563
565
  RUNNING_UPDATE_MODE_MESSAGE,
564
566
  RUNNING_UPDATE_PREPARED_MESSAGE,
@@ -0,0 +1,107 @@
1
+ 'use strict';
2
+
3
+ const COMMAND_TOOLS = new Set([
4
+ 'bash',
5
+ 'command',
6
+ 'exec_command',
7
+ 'shell',
8
+ ]);
9
+
10
+ const VERIFICATION_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:node\s+--(?:test|check))|(?:(?:npm|pnpm|yarn)\s+(?:test|(?:run\s+)?(?:test|lint|check|typecheck|build)))|(?:python(?:3)?\s+-m\s+pytest)|(?:pytest)|(?:go\s+test)|(?:cargo\s+test)|(?:dotnet\s+test)|(?:npx\s+)?(?:tsc|eslint|biome\s+check))\b/iu;
11
+
12
+ function isRealUserMessage(message) {
13
+ if (message?.role !== 'user') return false;
14
+ const kind = message.origin?.kind;
15
+ return kind !== 'injection' && kind !== 'compaction_summary' && kind !== 'system_trigger';
16
+ }
17
+
18
+ function currentUserTurn(history) {
19
+ if (!Array.isArray(history)) return [];
20
+ for (let index = history.length - 1; index >= 0; index -= 1) {
21
+ if (isRealUserMessage(history[index])) return history.slice(index + 1);
22
+ }
23
+ return [];
24
+ }
25
+
26
+ function parseArguments(value) {
27
+ if (value && typeof value === 'object') return value;
28
+ if (typeof value !== 'string') return null;
29
+ try {
30
+ const parsed = JSON.parse(value);
31
+ return parsed && typeof parsed === 'object' ? parsed : null;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ function normalizeCommand(value) {
38
+ return typeof value === 'string' ? value.trim().replace(/\s+/gu, ' ') : '';
39
+ }
40
+
41
+ function describeVerificationCall(call) {
42
+ const toolName = typeof call?.name === 'string' ? call.name : '';
43
+ if (!COMMAND_TOOLS.has(toolName.toLowerCase())) return null;
44
+ const args = parseArguments(call.arguments);
45
+ const command = normalizeCommand(args?.command ?? args?.cmd);
46
+ if (!command || !VERIFICATION_COMMAND.test(command)) return null;
47
+ return { toolName, command };
48
+ }
49
+
50
+ function toolCallsFrom(message) {
51
+ if (message?.role !== 'assistant') return [];
52
+ if (Array.isArray(message.toolCalls)) return message.toolCalls;
53
+ if (Array.isArray(message.tool_calls)) return message.tool_calls;
54
+ return [];
55
+ }
56
+
57
+ function detectValidatedLearningSignal(history) {
58
+ const turn = currentUserTurn(history);
59
+ const calls = new Map();
60
+ const failures = new Map();
61
+ let signal = null;
62
+
63
+ for (const message of turn) {
64
+ for (const call of toolCallsFrom(message)) {
65
+ if (typeof call?.id !== 'string') continue;
66
+ const verification = describeVerificationCall(call);
67
+ calls.set(call.id, {
68
+ toolName: typeof call.name === 'string' ? call.name : '',
69
+ verification,
70
+ });
71
+ }
72
+
73
+ if (message?.role !== 'tool' || typeof message.toolCallId !== 'string') continue;
74
+ const call = calls.get(message.toolCallId);
75
+ if (!call) continue;
76
+
77
+ if (call.toolName.toLowerCase() === 'mistakerecord' && message.isError !== true) {
78
+ signal = null;
79
+ failures.clear();
80
+ continue;
81
+ }
82
+
83
+ if (!call.verification) continue;
84
+ const key = `${call.verification.toolName.toLowerCase()}\0${call.verification.command}`;
85
+ if (message.isError === true) {
86
+ failures.set(key, message.toolCallId);
87
+ signal = null;
88
+ continue;
89
+ }
90
+
91
+ const failedToolCallId = failures.get(key);
92
+ if (!failedToolCallId) continue;
93
+ signal = {
94
+ toolName: call.verification.toolName,
95
+ command: call.verification.command,
96
+ failedToolCallId,
97
+ successfulToolCallId: message.toolCallId,
98
+ };
99
+ }
100
+
101
+ return signal;
102
+ }
103
+
104
+ module.exports = {
105
+ detectValidatedLearningSignal,
106
+ normalizeCommand,
107
+ };
package/blun.mjs CHANGED
@@ -232133,6 +232133,47 @@ var init_repeated_assistant_response = __esmMin((() => {
232133
232133
  };
232134
232134
  }));
232135
232135
  //#endregion
232136
+ //#region ../../packages/agent-core/src/agent/injection/validated-learning-signal.ts
232137
+ function isValidatedLearningSignalReminder(message) {
232138
+ return message.origin?.kind === "injection" && message.origin.variant === "validated_learning_signal";
232139
+ }
232140
+ function buildValidatedLearningSignalReminder() {
232141
+ return [
232142
+ "<validated-learning-signal>",
232143
+ "The same verification command failed and now succeeds in this user turn.",
232144
+ "Use MistakeRecord only when the red-to-green result exposes a reusable lesson.",
232145
+ "Do not record transient environment failures, raw command output, or a success with no general repetition guard.",
232146
+ "If there is a reusable lesson, record the incorrect assumption, the measured correction, and the concrete condition that would cause it again.",
232147
+ "</validated-learning-signal>"
232148
+ ].join("\n");
232149
+ }
232150
+ var ValidatedLearningSignalInjector, detectValidatedLearningSignal;
232151
+ var init_validated_learning_signal = __esmMin((() => {
232152
+ init_injector();
232153
+ ({ detectValidatedLearningSignal } = createRequire(import.meta.url)("./bin/validated-learning-signal.cjs"));
232154
+ ValidatedLearningSignalInjector = class extends DynamicInjector {
232155
+ injectionVariant = "validated_learning_signal";
232156
+ async inject() {
232157
+ const signal = detectValidatedLearningSignal(this.agent.context.history);
232158
+ const existing = this.agent.context.history.filter(isValidatedLearningSignalReminder);
232159
+ if (signal === null) {
232160
+ if (existing.length > 0) this.agent.context.removeSystemRemindersMatching(isValidatedLearningSignalReminder);
232161
+ return;
232162
+ }
232163
+ const injection = buildValidatedLearningSignalReminder();
232164
+ const expected = `<system-reminder>\n${injection}\n</system-reminder>`;
232165
+ if (existing.length === 1 && reminderText(existing[0]) === expected) return;
232166
+ this.agent.context.removeSystemRemindersMatching(isValidatedLearningSignalReminder);
232167
+ this.injectedAt = this.agent.context.history.length;
232168
+ this.lastInjection = injection;
232169
+ this.agent.context.appendSystemReminder(injection, {
232170
+ kind: "injection",
232171
+ variant: this.injectionVariant
232172
+ });
232173
+ }
232174
+ };
232175
+ }));
232176
+ //#endregion
232136
232177
  //#region ../../packages/agent-core/src/agent/injection/mission-contract-evidence.js
232137
232178
  function createEvidenceApi(dependencies) {
232138
232179
  "use strict";
@@ -232932,6 +232973,7 @@ var init_manager$2 = __esmMin((() => {
232932
232973
  init_plugin_session_start();
232933
232974
  init_plan_mode();
232934
232975
  init_repeated_assistant_response();
232976
+ init_validated_learning_signal();
232935
232977
  init_tool_awareness();
232936
232978
  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.";
232937
232979
  InjectionManager = class {
@@ -232948,6 +232990,7 @@ var init_manager$2 = __esmMin((() => {
232948
232990
  new ToolAwarenessInjector(agent),
232949
232991
  new MistakeMdInjector(agent),
232950
232992
  new RepeatedAssistantResponseInjector(agent),
232993
+ new ValidatedLearningSignalInjector(agent),
232951
232994
  new ActionStyleInjector(agent),
232952
232995
  new PlanModeInjector(agent),
232953
232996
  new PermissionModeInjector(agent)
@@ -502512,7 +502555,10 @@ var ChannelQueueDeadlineController = class {
502512
502555
  }
502513
502556
  requestDeliveryNow() {
502514
502557
  if (this.retainReleaseWhileDeliveryIsInFlight()) return;
502515
- if (this.host.hasWaitingWork() && !this.host.canDeliverWork()) return;
502558
+ if (this.host.hasWaitingWork() && !this.host.canDeliverWork()) {
502559
+ this.scheduleRetry();
502560
+ return;
502561
+ }
502516
502562
  this.requestDelivery();
502517
502563
  }
502518
502564
  retainReleaseWhileDeliveryIsInFlight() {
@@ -515043,6 +515089,7 @@ function turnsToTrim(turns, maxTurns, hysteresis) {
515043
515089
  const {
515044
515090
  AUTO_UPDATE_SETTLE_MS,
515045
515091
  RUNNING_UPDATE_HANDOFF_EXIT_CODE,
515092
+ RUNNING_UPDATE_HANDOFF_ACK_MESSAGE,
515046
515093
  RUNNING_UPDATE_HANDOFF_MESSAGE,
515047
515094
  RUNNING_UPDATE_MODE_MESSAGE,
515048
515095
  RUNNING_UPDATE_PREPARED_MESSAGE,
@@ -515105,10 +515152,29 @@ function requestRunningUpdateAtSafeBoundary(tui) {
515105
515152
  }
515106
515153
  function installRunningUpdateListener(tui) {
515107
515154
  const handler = (message) => {
515155
+ if (message?.type === RUNNING_UPDATE_HANDOFF_ACK_MESSAGE) {
515156
+ if (!tui.runningUpdateHandoffStarted || message.version !== tui.runningUpdatePreparedVersion || message.mode !== tui.runningUpdatePreparedMode || message.sessionId !== tui.getCurrentSessionId()) return;
515157
+ if (!isSafeRuntimeBoundary({
515158
+ isShuttingDown: tui.isShuttingDown,
515159
+ streamingPhase: tui.state.appState.streamingPhase,
515160
+ isCompacting: tui.state.appState.isCompacting,
515161
+ queuedMessages: tui.state.queuedMessages.length,
515162
+ activeToolCalls: tui.streamingUI.hasActiveToolCalls() ? 1 : 0,
515163
+ shellCommands: tui.shellOutputStreams.size,
515164
+ queueCommandRunning: tui.queueCommandRunning
515165
+ })) {
515166
+ tui.runningUpdateHandoffStarted = false;
515167
+ return;
515168
+ }
515169
+ tui.showStatus("Ein Update wird geladen und die TUI wird neu gestartet.", "success");
515170
+ void tui.stop(RUNNING_UPDATE_HANDOFF_EXIT_CODE).catch(() => {
515171
+ tui.runningUpdateHandoffStarted = false;
515172
+ });
515173
+ return;
515174
+ }
515108
515175
  if (message?.type !== RUNNING_UPDATE_PREPARED_MESSAGE || typeof message.version !== "string" || message.mode !== RUNNING_UPDATE_MODES.RESUME && message.mode !== RUNNING_UPDATE_MODES.NEW) return;
515109
515176
  tui.runningUpdatePreparedVersion = message.version;
515110
515177
  tui.runningUpdatePreparedMode = message.mode;
515111
- tui.showStatus(`Update ${message.version} ist bereit und wird beim nächsten Start aktiviert.`, "success");
515112
515178
  requestRunningUpdateAtSafeBoundary(tui);
515113
515179
  };
515114
515180
  process.on("message", handler);
@@ -515990,7 +516056,8 @@ var BlunTUI = class {
515990
516056
  }
515991
516057
  canDeliverQueuedChannelHead() {
515992
516058
  const item = this.state.queuedMessages[0];
515993
- if (this.isShuttingDown || this.queueCommandRunning || this.queueSteerInFlight !== void 0 || this.editorReplacementActive || this.deferUserMessages || this.session === void 0 || this.state.appState.model.trim().length === 0 || this.state.appState.streamingPhase === "idle" || this.state.appState.streamingPhase === "shell" || item?.mode !== "channel") return false;
516059
+ const hasActiveTurn = this.streamingUI.hasActiveTurn() || (this.state.appState.streamingPhase !== "idle" && this.state.appState.streamingPhase !== "shell");
516060
+ 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;
515994
516061
  return true;
515995
516062
  }
515996
516063
  async deliverQueuedChannelHead() {
@@ -516181,7 +516248,8 @@ var BlunTUI = class {
516181
516248
  this.scheduleQueueDrain();
516182
516249
  }
516183
516250
  steerQueueFlushPrefix() {
516184
- if (this.queueSteerInFlight !== void 0 || this.queueFlushBatchRemaining === 0 || this.deferUserMessages || this.state.appState.isCompacting || this.state.appState.streamingPhase === "idle" || this.state.appState.streamingPhase === "shell") return;
516251
+ const hasActiveTurn = this.streamingUI.hasActiveTurn() || this.state.appState.streamingPhase !== "idle";
516252
+ if (this.queueSteerInFlight !== void 0 || this.queueFlushBatchRemaining === 0 || this.deferUserMessages || this.state.appState.isCompacting || this.state.appState.streamingPhase === "shell" || !hasActiveTurn) return;
516185
516253
  const session = this.session;
516186
516254
  if (session === void 0 || this.state.appState.model.trim().length === 0) {
516187
516255
  this.showError(llmNotSetMessage());
@@ -516294,7 +516362,7 @@ var BlunTUI = class {
516294
516362
  channelAcknowledge: acknowledge,
516295
516363
  ...envelope.meta["image_path"] !== void 0 ? { channelImagePath: envelope.meta["image_path"] } : {}
516296
516364
  });
516297
- this.syncChannelQueueDeadline();
516365
+ this.channelQueueDeadline.requestDeliveryNow();
516298
516366
  this.scheduleQueueDrain();
516299
516367
  this.track("input_queue");
516300
516368
  this.updateQueueDisplay();
@@ -516766,7 +516834,7 @@ var BlunTUI = class {
516766
516834
  this.sendTelegramRemoteCommandMessage(session, input, options, telegramContext);
516767
516835
  return;
516768
516836
  }
516769
- if (this.deferUserMessages || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting) {
516837
+ if (this.deferUserMessages || this.streamingUI.hasActiveTurn() || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting) {
516770
516838
  this.enqueueMessage(input, options);
516771
516839
  return;
516772
516840
  }
@@ -516777,7 +516845,8 @@ var BlunTUI = class {
516777
516845
  for (const part of input) this.enqueueMessage(part);
516778
516846
  return;
516779
516847
  }
516780
- if (this.state.appState.streamingPhase === "idle") {
516848
+ const hasActiveTurn = this.streamingUI.hasActiveTurn() || this.state.appState.streamingPhase !== "idle";
516849
+ if (!hasActiveTurn) {
516781
516850
  for (const part of input) this.sendMessageInternal(session, part);
516782
516851
  return;
516783
516852
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.213",
3
+ "version": "9.1.215",
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": {