blun-king-cli 9.1.435 → 9.1.437

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.
@@ -6,6 +6,7 @@ const { join } = require('node:path');
6
6
  const STATUS_FILE = 'telegram-console-status.json';
7
7
  const MAX_STATUS_BYTES = 16_384;
8
8
  const MAX_TASK_CHARS = 240;
9
+ const MAX_TOOL_NAME_CHARS = 80;
9
10
 
10
11
  function validInstant(value) {
11
12
  if (typeof value !== 'string' || value.length === 0) return undefined;
@@ -18,6 +19,39 @@ function boundedTask(value) {
18
19
  return title.length > 0 ? title.slice(0, MAX_TASK_CHARS) : undefined;
19
20
  }
20
21
 
22
+ function boundedToolName(value) {
23
+ if (typeof value !== 'string') return undefined;
24
+ const name = value.replace(/\s+/gu, ' ').trim();
25
+ return name.length > 0 ? name.slice(0, MAX_TOOL_NAME_CHARS) : undefined;
26
+ }
27
+
28
+ function instantFromMilliseconds(value) {
29
+ if (!Number.isFinite(value) || value < 0) return undefined;
30
+ try {
31
+ return new Date(value).toISOString();
32
+ } catch {
33
+ return undefined;
34
+ }
35
+ }
36
+
37
+ function buildTurnDiagnostics(turnActive, activity) {
38
+ if (!turnActive || activity?.active !== true) return {};
39
+ const turnStartedAt = instantFromMilliseconds(activity.startedAtMs);
40
+ const lastProgressAt = instantFromMilliseconds(activity.lastProgressAtMs);
41
+ const lastToolName = boundedToolName(activity.lastToolName);
42
+ const failedRepetitions = Number.isSafeInteger(activity.failedRepetitions)
43
+ && activity.failedRepetitions >= 0
44
+ && activity.failedRepetitions <= 100
45
+ ? activity.failedRepetitions
46
+ : undefined;
47
+ return {
48
+ ...(turnStartedAt === undefined ? {} : { turnStartedAt }),
49
+ ...(lastProgressAt === undefined ? {} : { lastProgressAt }),
50
+ ...(lastToolName === undefined ? {} : { lastToolName }),
51
+ ...(failedRepetitions === undefined ? {} : { failedRepetitions }),
52
+ };
53
+ }
54
+
21
55
  function buildTelegramConsoleStatus(input = {}) {
22
56
  const capturedAt = validInstant(input.capturedAt);
23
57
  const processStartedAt = validInstant(input.processStartedAt);
@@ -38,6 +72,7 @@ function buildTelegramConsoleStatus(input = {}) {
38
72
  turnActive: input.turnActive,
39
73
  step,
40
74
  ...(activeTask === undefined ? {} : { activeTask }),
75
+ ...buildTurnDiagnostics(input.turnActive, input.turnActivity),
41
76
  };
42
77
  }
43
78
 
@@ -53,6 +88,25 @@ function parseTelegramConsoleStatus(value, options = {}) {
53
88
  || typeof parsed.turnActive !== 'boolean'
54
89
  || !Number.isSafeInteger(parsed.step) || parsed.step < 0) return undefined;
55
90
  const activeTask = boundedTask(parsed.activeTask);
91
+ const hasTurnStartedAt = Object.hasOwn(parsed, 'turnStartedAt');
92
+ const hasLastProgressAt = Object.hasOwn(parsed, 'lastProgressAt');
93
+ const hasLastToolName = Object.hasOwn(parsed, 'lastToolName');
94
+ const hasFailedRepetitions = Object.hasOwn(parsed, 'failedRepetitions');
95
+ const turnStartedAt = validInstant(parsed.turnStartedAt);
96
+ const lastProgressAt = validInstant(parsed.lastProgressAt);
97
+ const lastToolName = boundedToolName(parsed.lastToolName);
98
+ const failedRepetitions = parsed.failedRepetitions;
99
+ if ((hasTurnStartedAt && turnStartedAt === undefined)
100
+ || (hasLastProgressAt && lastProgressAt === undefined)
101
+ || (hasLastToolName && lastToolName === undefined)
102
+ || (hasFailedRepetitions && (!Number.isSafeInteger(failedRepetitions)
103
+ || failedRepetitions < 0 || failedRepetitions > 100))) return undefined;
104
+ const diagnostics = parsed.turnActive ? {
105
+ ...(turnStartedAt === undefined ? {} : { turnStartedAt }),
106
+ ...(lastProgressAt === undefined ? {} : { lastProgressAt }),
107
+ ...(lastToolName === undefined ? {} : { lastToolName }),
108
+ ...(hasFailedRepetitions ? { failedRepetitions } : {}),
109
+ } : {};
56
110
  return {
57
111
  version: 1,
58
112
  pid: parsed.pid,
@@ -61,6 +115,7 @@ function parseTelegramConsoleStatus(value, options = {}) {
61
115
  turnActive: parsed.turnActive,
62
116
  step: parsed.step,
63
117
  ...(parsed.turnActive && activeTask !== undefined ? { activeTask } : {}),
118
+ ...diagnostics,
64
119
  };
65
120
  } catch {
66
121
  return undefined;
@@ -76,6 +76,14 @@ function buildTelegramRemoteStatus(input) {
76
76
  ? [
77
77
  `Arbeitsstatus: aktiv, Schritt ${input.consoleStatus.step}`,
78
78
  ...(input.consoleStatus.activeTask === undefined ? [] : [`Aktive Aufgabe: ${input.consoleStatus.activeTask}`]),
79
+ ...(formatAge(input.consoleStatus.turnStartedAt, input.capturedAt) === undefined
80
+ ? [] : [`Zugbeginn: ${formatAge(input.consoleStatus.turnStartedAt, input.capturedAt)}`]),
81
+ ...(formatAge(input.consoleStatus.lastProgressAt, input.capturedAt) === undefined
82
+ ? [] : [`Letzter Fortschritt: ${formatAge(input.consoleStatus.lastProgressAt, input.capturedAt)}`]),
83
+ ...(input.consoleStatus.lastToolName === undefined
84
+ ? [] : [`Letztes Werkzeug: ${input.consoleStatus.lastToolName}`]),
85
+ ...(input.consoleStatus.failedRepetitions === undefined
86
+ ? [] : [`Wiederholte Fehler: ${input.consoleStatus.failedRepetitions}`]),
79
87
  ]
80
88
  : ['Arbeitsstatus: Leerlauf'];
81
89
  return [
package/blun.mjs CHANGED
@@ -419609,6 +419609,7 @@ var TelegramChannelController = class {
419609
419609
  }
419610
419610
  try {
419611
419611
  writeFileSync(this.leaseFile, String(process.pid));
419612
+ this.host.streamingUI?.publishTelegramConsoleStatus?.();
419612
419613
  } catch (error) {
419613
419614
  this.host.warn(uiText("telegramChannel.leaseWriteFailed", { error: String(error) }));
419614
419615
  }
@@ -507622,6 +507623,7 @@ var TurnWatchdogController = class {
507622
507623
  clearTimer;
507623
507624
  onIdle;
507624
507625
  onAbort;
507626
+ onSnapshot;
507625
507627
  toolCalls = /* @__PURE__ */ new Map();
507626
507628
  active = false;
507627
507629
  startedAtMs = 0;
@@ -507638,6 +507640,7 @@ var TurnWatchdogController = class {
507638
507640
  this.clearTimer = options.clearTimer ?? clearTimeout;
507639
507641
  this.onIdle = options.onIdle;
507640
507642
  this.onAbort = options.onAbort;
507643
+ this.onSnapshot = options.onSnapshot ?? (() => {});
507641
507644
  }
507642
507645
  start() {
507643
507646
  this.stop();
@@ -507649,20 +507652,23 @@ var TurnWatchdogController = class {
507649
507652
  this.lastFailedKey = void 0;
507650
507653
  this.failedRepetitions = 0;
507651
507654
  this.idleReportedForProgressAtMs = void 0;
507655
+ this.publishSnapshot();
507652
507656
  this.scheduleIdleCheck();
507653
507657
  }
507654
507658
  stop() {
507659
+ const wasActive = this.active;
507655
507660
  this.active = false;
507656
507661
  this.toolCalls.clear();
507657
507662
  if (this.timer !== void 0) {
507658
507663
  this.clearTimer(this.timer);
507659
507664
  this.timer = void 0;
507660
507665
  }
507666
+ if (wasActive) this.publishSnapshot();
507661
507667
  }
507662
507668
  recordToolCall(toolCallId, name, args) {
507663
507669
  if (!this.active) return;
507664
- this.recordProgress();
507665
507670
  this.lastToolName = name;
507671
+ this.recordProgress();
507666
507672
  this.toolCalls.set(toolCallId, {
507667
507673
  name,
507668
507674
  key: `${name}\0${canonicalJson(args)}`
@@ -507677,6 +507683,7 @@ var TurnWatchdogController = class {
507677
507683
  if (!isError || toolCall === void 0) {
507678
507684
  this.lastFailedKey = void 0;
507679
507685
  this.failedRepetitions = 0;
507686
+ this.publishSnapshot();
507680
507687
  return;
507681
507688
  }
507682
507689
  if (toolCall.key === this.lastFailedKey) this.failedRepetitions += 1;
@@ -507684,6 +507691,7 @@ var TurnWatchdogController = class {
507684
507691
  this.lastFailedKey = toolCall.key;
507685
507692
  this.failedRepetitions = 1;
507686
507693
  }
507694
+ this.publishSnapshot();
507687
507695
  if (this.failedRepetitions < this.config.maxFailedRepetitions) return;
507688
507696
  const snapshot = {
507689
507697
  ...this.snapshot(),
@@ -507696,8 +507704,20 @@ var TurnWatchdogController = class {
507696
507704
  if (!this.active) return;
507697
507705
  this.lastProgressAtMs = this.now();
507698
507706
  this.idleReportedForProgressAtMs = void 0;
507707
+ this.publishSnapshot();
507699
507708
  this.scheduleIdleCheck();
507700
507709
  }
507710
+ publishSnapshot() {
507711
+ try {
507712
+ this.onSnapshot(this.active ? {
507713
+ active: true,
507714
+ startedAtMs: this.startedAtMs,
507715
+ lastProgressAtMs: this.lastProgressAtMs,
507716
+ lastToolName: this.lastToolName,
507717
+ failedRepetitions: this.failedRepetitions
507718
+ } : { active: false });
507719
+ } catch {}
507720
+ }
507701
507721
  scheduleIdleCheck() {
507702
507722
  if (!this.active) return;
507703
507723
  if (this.timer !== void 0) this.clearTimer(this.timer);
@@ -507775,7 +507795,8 @@ var SessionEventHandler = class {
507775
507795
  });
507776
507796
  this.turnWatchdog = new TurnWatchdogController({
507777
507797
  onIdle: (snapshot) => this.reportWatchdogIdle(snapshot),
507778
- onAbort: (snapshot) => this.abortWatchdogLoop(snapshot)
507798
+ onAbort: (snapshot) => this.abortWatchdogLoop(snapshot),
507799
+ onSnapshot: (snapshot) => this.host.streamingUI.setRemoteTurnActivity(snapshot)
507779
507800
  });
507780
507801
  }
507781
507802
  backgroundTasks = /* @__PURE__ */ new Map();
@@ -508327,8 +508348,10 @@ var SessionEventHandler = class {
508327
508348
  this.host.sendChannelTurnStatus?.(message);
508328
508349
  }
508329
508350
  abortWatchdogLoop(snapshot) {
508330
- const message = uiText(snapshot.reason === "idle_timeout" ? "sessionEvent.watchdog.timeout" : "sessionEvent.watchdog.aborted", {
508351
+ const message = snapshot.reason === "idle_timeout" ? uiText("sessionEvent.watchdog.timeout", {
508331
508352
  minutes: snapshot.idleMinutes,
508353
+ tool: snapshot.lastToolName
508354
+ }) : uiText("sessionEvent.watchdog.aborted", {
508332
508355
  tool: snapshot.lastToolName,
508333
508356
  repetitions: snapshot.failedRepetitions
508334
508357
  });
@@ -511183,6 +511206,7 @@ var StreamingUIController = class {
511183
511206
  _currentTurnId = void 0;
511184
511207
  _currentStep = 0;
511185
511208
  _remoteTodos = [];
511209
+ _remoteTurnActivity = { active: false };
511186
511210
  _remoteProcessStartedAt = new Date(Date.now() - process.uptime() * 1e3).toISOString();
511187
511211
  _liveTurnStartedAtMs = void 0;
511188
511212
  _liveOutputTokens = new LiveOutputTokenCounter();
@@ -511215,6 +511239,13 @@ var StreamingUIController = class {
511215
511239
  this._currentStep = step;
511216
511240
  this.publishTelegramConsoleStatus();
511217
511241
  }
511242
+ setRemoteTurnActivity(activity) {
511243
+ const previous = this._remoteTurnActivity;
511244
+ this._remoteTurnActivity = activity?.active === true ? activity : { active: false };
511245
+ if (previous.active !== this._remoteTurnActivity.active
511246
+ || previous.lastToolName !== this._remoteTurnActivity.lastToolName
511247
+ || previous.failedRepetitions !== this._remoteTurnActivity.failedRepetitions) this.publishTelegramConsoleStatus();
511248
+ }
511218
511249
  publishTelegramConsoleStatus() {
511219
511250
  if (!telegramChannelAttachEnabled()) return;
511220
511251
  try {
@@ -511225,6 +511256,7 @@ var StreamingUIController = class {
511225
511256
  processStartedAt: this._remoteProcessStartedAt,
511226
511257
  step: this._currentStep,
511227
511258
  todos: this._remoteTodos,
511259
+ turnActivity: this._remoteTurnActivity,
511228
511260
  turnActive: this._currentTurnId !== void 0
511229
511261
  });
511230
511262
  } catch {}
@@ -511536,6 +511568,7 @@ var StreamingUIController = class {
511536
511568
  this._currentTurnId = void 0;
511537
511569
  this._currentStep = 0;
511538
511570
  this._remoteTodos = [];
511571
+ this._remoteTurnActivity = { active: false };
511539
511572
  this._streamingToolCallArguments.clear();
511540
511573
  this.pendingToolCallFlushIds.clear();
511541
511574
  this.publishTelegramConsoleStatus();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.435",
3
+ "version": "9.1.437",
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": {