blun-king-cli 9.1.459 → 9.1.461

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.
@@ -4,7 +4,7 @@ const crypto = require('node:crypto');
4
4
  const fs = require('node:fs');
5
5
  const path = require('node:path');
6
6
 
7
- const RESUME_CHECKPOINT_SCHEMA_VERSION = 1;
7
+ const RESUME_CHECKPOINT_SCHEMA_VERSION = 2;
8
8
  const MAX_RESUME_SNAPSHOT_BYTES = 4 * 1024 * 1024;
9
9
  const MAX_RESUME_CHECKPOINT_BYTES = MAX_RESUME_SNAPSHOT_BYTES + 256 * 1024;
10
10
  const WIRE_SAMPLE_BYTES = 4096;
@@ -0,0 +1,206 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('node:crypto');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+
7
+ const ARCHIVE_SCHEMA_VERSION = 1;
8
+ const INDEX_FILE = 'index.json';
9
+
10
+ function sessionScrollbackArchiveDirectory(wirePath) {
11
+ return path.join(path.dirname(wirePath), 'tui-scrollback-v1');
12
+ }
13
+
14
+ function isNonNegativeInteger(value) {
15
+ return Number.isSafeInteger(value) && value >= 0;
16
+ }
17
+
18
+ function writeAtomic(filePath, value) {
19
+ const temporaryPath = `${filePath}.tmp-${process.pid}-${crypto.randomBytes(8).toString('hex')}`;
20
+ try {
21
+ fs.writeFileSync(temporaryPath, value, {
22
+ encoding: 'utf8',
23
+ flag: 'wx',
24
+ mode: 0o600,
25
+ });
26
+ fs.renameSync(temporaryPath, filePath);
27
+ } finally {
28
+ try {
29
+ fs.rmSync(temporaryPath, { force: true });
30
+ } catch {}
31
+ }
32
+ }
33
+
34
+ class SessionScrollbackArchive {
35
+ constructor({ directory, reset = false, reuse = false }) {
36
+ if (typeof directory !== 'string' || directory.length === 0) {
37
+ throw new TypeError('session scrollback directory is required');
38
+ }
39
+ this.directory = path.resolve(directory);
40
+ this.chunks = [];
41
+ this.loadedChunks = new Map();
42
+ this.lineCount = 0;
43
+ this.loadedExisting = false;
44
+
45
+ if (reset) {
46
+ this.reset();
47
+ } else if (reuse) {
48
+ this.loadExisting();
49
+ }
50
+ }
51
+
52
+ get totalLines() {
53
+ return this.lineCount;
54
+ }
55
+
56
+ get loadedChunkCount() {
57
+ return this.loadedChunks.size;
58
+ }
59
+
60
+ append(lines) {
61
+ if (!Array.isArray(lines) || !lines.every((line) => typeof line === 'string')) {
62
+ return false;
63
+ }
64
+ if (lines.length === 0) return true;
65
+
66
+ fs.mkdirSync(this.directory, { recursive: true });
67
+ const file = `chunk-${String(this.chunks.length).padStart(8, '0')}-${crypto.randomBytes(6).toString('hex')}.json`;
68
+ const chunkPath = path.join(this.directory, file);
69
+ const chunk = {
70
+ file,
71
+ startLine: this.lineCount,
72
+ lineCount: lines.length,
73
+ };
74
+
75
+ try {
76
+ writeAtomic(chunkPath, JSON.stringify(lines));
77
+ const nextChunks = [...this.chunks, chunk];
78
+ const nextLineCount = this.lineCount + lines.length;
79
+ this.writeIndex(nextChunks, nextLineCount);
80
+ this.chunks = nextChunks;
81
+ this.lineCount = nextLineCount;
82
+ return true;
83
+ } catch {
84
+ try {
85
+ fs.rmSync(chunkPath, { force: true });
86
+ } catch {}
87
+ return false;
88
+ }
89
+ }
90
+
91
+ readRange(start, end) {
92
+ if (!Number.isFinite(start) || !Number.isFinite(end)) return [];
93
+ if (start >= end || start >= this.lineCount) return [];
94
+ const boundedStart = Math.max(0, Math.floor(start));
95
+ const boundedEnd = Math.min(this.lineCount, Math.floor(end));
96
+ const output = [];
97
+
98
+ for (let index = 0; index < this.chunks.length; index++) {
99
+ const chunk = this.chunks[index];
100
+ const chunkEnd = chunk.startLine + chunk.lineCount;
101
+ if (chunkEnd <= boundedStart) continue;
102
+ if (chunk.startLine >= boundedEnd) break;
103
+ const lines = this.readChunk(index);
104
+ const sliceStart = Math.max(0, boundedStart - chunk.startLine);
105
+ const sliceEnd = Math.min(lines.length, boundedEnd - chunk.startLine);
106
+ output.push(...lines.slice(sliceStart, sliceEnd));
107
+ }
108
+ return output;
109
+ }
110
+
111
+ reset() {
112
+ try {
113
+ fs.rmSync(this.directory, { recursive: true, force: true });
114
+ } catch {}
115
+ this.chunks = [];
116
+ this.loadedChunks.clear();
117
+ this.lineCount = 0;
118
+ this.loadedExisting = false;
119
+ }
120
+
121
+ clear() {
122
+ this.reset();
123
+ }
124
+
125
+ dispose() {
126
+ this.loadedChunks.clear();
127
+ }
128
+
129
+ loadExisting() {
130
+ const indexPath = path.join(this.directory, INDEX_FILE);
131
+ try {
132
+ const parsed = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
133
+ if (
134
+ parsed?.schemaVersion !== ARCHIVE_SCHEMA_VERSION
135
+ || !isNonNegativeInteger(parsed.totalLines)
136
+ || !Array.isArray(parsed.chunks)
137
+ ) {
138
+ return;
139
+ }
140
+
141
+ let nextLine = 0;
142
+ const chunks = [];
143
+ for (const candidate of parsed.chunks) {
144
+ if (
145
+ typeof candidate?.file !== 'string'
146
+ || path.basename(candidate.file) !== candidate.file
147
+ || !candidate.file.startsWith('chunk-')
148
+ || !candidate.file.endsWith('.json')
149
+ || candidate.startLine !== nextLine
150
+ || !Number.isSafeInteger(candidate.lineCount)
151
+ || candidate.lineCount <= 0
152
+ ) {
153
+ return;
154
+ }
155
+ const chunkPath = path.join(this.directory, candidate.file);
156
+ if (!fs.statSync(chunkPath).isFile()) return;
157
+ chunks.push({
158
+ file: candidate.file,
159
+ startLine: candidate.startLine,
160
+ lineCount: candidate.lineCount,
161
+ });
162
+ nextLine += candidate.lineCount;
163
+ }
164
+ if (nextLine !== parsed.totalLines) return;
165
+
166
+ this.chunks = chunks;
167
+ this.lineCount = parsed.totalLines;
168
+ this.loadedExisting = true;
169
+ } catch {}
170
+ }
171
+
172
+ readChunk(index) {
173
+ const loaded = this.loadedChunks.get(index);
174
+ if (loaded !== undefined) return loaded;
175
+ const chunk = this.chunks[index];
176
+ if (chunk === undefined) return [];
177
+ try {
178
+ const value = JSON.parse(fs.readFileSync(path.join(this.directory, chunk.file), 'utf8'));
179
+ if (
180
+ !Array.isArray(value)
181
+ || value.length !== chunk.lineCount
182
+ || !value.every((line) => typeof line === 'string')
183
+ ) {
184
+ return [];
185
+ }
186
+ this.loadedChunks.set(index, value);
187
+ return value;
188
+ } catch {
189
+ return [];
190
+ }
191
+ }
192
+
193
+ writeIndex(chunks, totalLines) {
194
+ writeAtomic(path.join(this.directory, INDEX_FILE), JSON.stringify({
195
+ schemaVersion: ARCHIVE_SCHEMA_VERSION,
196
+ totalLines,
197
+ chunks,
198
+ }));
199
+ }
200
+ }
201
+
202
+ module.exports = {
203
+ ARCHIVE_SCHEMA_VERSION,
204
+ SessionScrollbackArchive,
205
+ sessionScrollbackArchiveDirectory,
206
+ };
@@ -7,6 +7,7 @@ const STATUS_FILE = 'telegram-console-status.json';
7
7
  const MAX_STATUS_BYTES = 16_384;
8
8
  const MAX_TASK_CHARS = 240;
9
9
  const MAX_TOOL_NAME_CHARS = 80;
10
+ const MAX_VERSION_CHARS = 80;
10
11
 
11
12
  function validInstant(value) {
12
13
  if (typeof value !== 'string' || value.length === 0) return undefined;
@@ -25,6 +26,16 @@ function boundedToolName(value) {
25
26
  return name.length > 0 ? name.slice(0, MAX_TOOL_NAME_CHARS) : undefined;
26
27
  }
27
28
 
29
+ function boundedVersion(value) {
30
+ if (typeof value !== 'string') return undefined;
31
+ const version = value.trim();
32
+ return version.length > 0
33
+ && version.length <= MAX_VERSION_CHARS
34
+ && /^[0-9A-Za-z][0-9A-Za-z.+-]*$/u.test(version)
35
+ ? version
36
+ : undefined;
37
+ }
38
+
28
39
  function instantFromMilliseconds(value) {
29
40
  if (!Number.isFinite(value) || value < 0) return undefined;
30
41
  try {
@@ -54,6 +65,7 @@ function buildTurnDiagnostics(turnActive, activity) {
54
65
 
55
66
  function buildTelegramConsoleStatus(input = {}) {
56
67
  const capturedAt = validInstant(input.capturedAt);
68
+ const loadedVersion = boundedVersion(input.loadedVersion);
57
69
  const processStartedAt = validInstant(input.processStartedAt);
58
70
  const pid = Number.isSafeInteger(input.pid) && input.pid > 1 ? input.pid : undefined;
59
71
  const step = Number.isSafeInteger(input.step) && input.step >= 0 ? input.step : 0;
@@ -66,6 +78,7 @@ function buildTelegramConsoleStatus(input = {}) {
66
78
  : undefined;
67
79
  return {
68
80
  version: 1,
81
+ ...(loadedVersion === undefined ? {} : { loadedVersion }),
69
82
  pid,
70
83
  capturedAt,
71
84
  processStartedAt,
@@ -88,6 +101,8 @@ function parseTelegramConsoleStatus(value, options = {}) {
88
101
  || typeof parsed.turnActive !== 'boolean'
89
102
  || !Number.isSafeInteger(parsed.step) || parsed.step < 0) return undefined;
90
103
  const activeTask = boundedTask(parsed.activeTask);
104
+ const hasLoadedVersion = Object.hasOwn(parsed, 'loadedVersion');
105
+ const loadedVersion = boundedVersion(parsed.loadedVersion);
91
106
  const hasTurnStartedAt = Object.hasOwn(parsed, 'turnStartedAt');
92
107
  const hasLastProgressAt = Object.hasOwn(parsed, 'lastProgressAt');
93
108
  const hasLastToolName = Object.hasOwn(parsed, 'lastToolName');
@@ -96,7 +111,8 @@ function parseTelegramConsoleStatus(value, options = {}) {
96
111
  const lastProgressAt = validInstant(parsed.lastProgressAt);
97
112
  const lastToolName = boundedToolName(parsed.lastToolName);
98
113
  const failedRepetitions = parsed.failedRepetitions;
99
- if ((hasTurnStartedAt && turnStartedAt === undefined)
114
+ if ((hasLoadedVersion && loadedVersion === undefined)
115
+ || (hasTurnStartedAt && turnStartedAt === undefined)
100
116
  || (hasLastProgressAt && lastProgressAt === undefined)
101
117
  || (hasLastToolName && lastToolName === undefined)
102
118
  || (hasFailedRepetitions && (!Number.isSafeInteger(failedRepetitions)
@@ -109,6 +125,7 @@ function parseTelegramConsoleStatus(value, options = {}) {
109
125
  } : {};
110
126
  return {
111
127
  version: 1,
128
+ ...(loadedVersion === undefined ? {} : { loadedVersion }),
112
129
  pid: parsed.pid,
113
130
  capturedAt: parsed.capturedAt,
114
131
  processStartedAt: parsed.processStartedAt,
@@ -13,6 +13,16 @@ function resolveTelegramRemoteVersion(input = {}) {
13
13
  || 'unbekannt';
14
14
  }
15
15
 
16
+ function resolveVersionDisplay(input = {}) {
17
+ const loaded = trustedVersion(input.consoleStatus?.loadedVersion)
18
+ || trustedVersion(input.version)
19
+ || 'unbekannt';
20
+ const installed = trustedVersion(input.installedVersion);
21
+ return installed !== undefined && installed !== loaded
22
+ ? `${loaded} -> ${installed}`
23
+ : loaded;
24
+ }
25
+
16
26
  function formatDuration(seconds) {
17
27
  const minutes = Math.max(0, Math.floor(Number(seconds) / 60));
18
28
  if (minutes < 60) return `${minutes} min`;
@@ -89,7 +99,7 @@ function buildTelegramRemoteStatus(input) {
89
99
  return [
90
100
  `BLUN-Fernstatus für ${username}`,
91
101
  `Rechner: ${input.machineName || 'unbekannt'}`,
92
- `Version: ${input.version || 'unbekannt'}`,
102
+ `Version: ${resolveVersionDisplay(input)}`,
93
103
  `Brücke: aktiv (PID ${input.bridgePid}, ${formatDuration(input.bridgeUptimeSeconds)})`,
94
104
  `Konsole: ${tui}`,
95
105
  `Zustellung: ${delivery}`,
package/blun.mjs CHANGED
@@ -12,6 +12,7 @@ import runtimeExitLedger from "./bin/runtime-exit-ledger.cjs";
12
12
  import compactionModelPolicy from "./bin/compaction-model-policy.cjs";
13
13
  import agentResumeSnapshot from "./bin/agent-resume-snapshot.cjs";
14
14
  import sessionResumeCheckpoint from "./bin/session-resume-checkpoint.cjs";
15
+ import sessionScrollbackArchive from "./bin/session-scrollback-archive.cjs";
15
16
  import crypto$1, { createHash, randomBytes, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
16
17
  import * as fs$16 from "node:fs";
17
18
  import Kt, { accessSync, appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, promises, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
@@ -48,6 +49,7 @@ const { recordRuntimeExit } = runtimeExitLedger;
48
49
  const { resolveCompactionModelAlias } = compactionModelPolicy;
49
50
  const { createAgentResumeSnapshot, restoreAgentResumeSnapshot } = agentResumeSnapshot;
50
51
  const { loadResumeCheckpoint, writeResumeCheckpoint } = sessionResumeCheckpoint;
52
+ const { SessionScrollbackArchive, sessionScrollbackArchiveDirectory } = sessionScrollbackArchive;
51
53
  import { EventEmitter as EventEmitter$1 } from "node:events";
52
54
  import { StringDecoder } from "node:string_decoder";
53
55
  import co from "node:assert";
@@ -235236,6 +235238,7 @@ var init_records = __esmMin((() => {
235236
235238
  persistence;
235237
235239
  _restoring = null;
235238
235240
  metadataInitialized = false;
235241
+ resumeCheckpointRestored = false;
235239
235242
  messageTimes = new WeakMap();
235240
235243
  constructor(agent, persistence) {
235241
235244
  this.agent = agent;
@@ -235274,6 +235277,7 @@ var init_records = __esmMin((() => {
235274
235277
  }
235275
235278
  async replay(options = {}) {
235276
235279
  if (!this.persistence) throw new Error("No persistence provided for AgentRecords");
235280
+ this.resumeCheckpointRestored = false;
235277
235281
  const rewriteMigratedRecords = options.rewriteMigratedRecords ?? true;
235278
235282
  if (rewriteMigratedRecords && await this.tryReplayResumeCheckpoint()) return {};
235279
235283
  let migrations = [];
@@ -235331,6 +235335,7 @@ var init_records = __esmMin((() => {
235331
235335
  }
235332
235336
  this.persistence.persistedRecordCount = checkpoint.recordCount + tailRecordCount;
235333
235337
  if (this.agent.blobStore !== void 0) for (const msg of this.agent.context.history) await this.agent.blobStore.rehydrateParts(msg.content);
235338
+ this.resumeCheckpointRestored = true;
235334
235339
  return true;
235335
235340
  }
235336
235341
  async writeResumeCheckpoint() {
@@ -316822,6 +316827,8 @@ async function resumeSessionResult(summary, session, warning) {
316822
316827
  const usage = await api.getUsage({ agentId });
316823
316828
  agents[agentId] = {
316824
316829
  type: agent.type,
316830
+ resumeCheckpointRestored: agent.records.resumeCheckpointRestored === true,
316831
+ sessionWirePath: agent.records.persistence instanceof FileSystemAgentRecordPersistence ? agent.records.persistence.filePath : void 0,
316825
316832
  config,
316826
316833
  context,
316827
316834
  replay: agent.replayBuilder.buildResult(),
@@ -341013,6 +341020,14 @@ function getVersion() {
341013
341020
  if (BLUN_BUILD_INFO.version !== void 0) return BLUN_BUILD_INFO.version;
341014
341021
  return JSON.parse(readFileSync(getHostPackageJsonPath(), "utf-8")).version;
341015
341022
  }
341023
+ function getInstalledPackageVersion() {
341024
+ try {
341025
+ const version = JSON.parse(readFileSync(getHostPackageJsonPath(), "utf-8")).version;
341026
+ return typeof version === "string" && version.trim().length > 0 ? version.trim() : void 0;
341027
+ } catch {
341028
+ return void 0;
341029
+ }
341030
+ }
341016
341031
  function createBlunHostIdentity(version = getVersion()) {
341017
341032
  return {
341018
341033
  userAgentProduct: CLI_USER_AGENT_PRODUCT,
@@ -417979,6 +417994,10 @@ function buildStatusReportLines(options) {
417979
417994
  value: sessionId
417980
417995
  }
417981
417996
  ];
417997
+ if (options.installedVersion !== void 0 && options.installedVersion !== options.version) rows.push({
417998
+ label: uiText("status.label.warning"),
417999
+ value: uiText("plugins.market.status.installedVersion", { version: options.installedVersion })
418000
+ });
417982
418001
  const title = options.sessionTitle?.trim();
417983
418002
  if (title !== void 0 && title.length > 0) rows.push({
417984
418003
  label: uiText("status.label.title"),
@@ -418875,6 +418894,7 @@ async function showStatusReport(host) {
418875
418894
  const appState = host.state.appState;
418876
418895
  const reportArgs = {
418877
418896
  version: appState.version,
418897
+ installedVersion: getInstalledPackageVersion(),
418878
418898
  model: appState.model,
418879
418899
  workDir: appState.workDir,
418880
418900
  sessionId: appState.sessionId,
@@ -506093,6 +506113,27 @@ var ScrollbackBuffer = class {
506093
506113
  archiveLines(lines) {
506094
506114
  return this.archive.append(lines);
506095
506115
  }
506116
+ usePersistentArchive(directory, options) {
506117
+ this.releaseArchive();
506118
+ const archive = new SessionScrollbackArchive({
506119
+ directory,
506120
+ reset: options.reset,
506121
+ reuse: options.reuse
506122
+ });
506123
+ this.archive = archive;
506124
+ return archive.loadedExisting;
506125
+ }
506126
+ releaseArchive() {
506127
+ if (this.archive instanceof DiskBackedLineArchive) this.archive.clear();
506128
+ else this.archive.dispose();
506129
+ this.archive = new DiskBackedLineArchive();
506130
+ this.resetViewState();
506131
+ }
506132
+ dispose() {
506133
+ if (this.archive instanceof DiskBackedLineArchive) this.archive.clear();
506134
+ else this.archive.dispose();
506135
+ this.resetViewState();
506136
+ }
506096
506137
  activate() {
506097
506138
  this.active = true;
506098
506139
  this.scrollOffset = 0;
@@ -506103,6 +506144,9 @@ var ScrollbackBuffer = class {
506103
506144
  }
506104
506145
  clear() {
506105
506146
  this.archive.clear();
506147
+ this.resetViewState();
506148
+ }
506149
+ resetViewState() {
506106
506150
  this.snapshotLines = [];
506107
506151
  this.publishedLineCount = 0;
506108
506152
  this.deactivate();
@@ -506414,6 +506458,7 @@ var ScrollbackController = class {
506414
506458
  state;
506415
506459
  buffer;
506416
506460
  disposeListener;
506461
+ suppressReplayArchiveWrites = false;
506417
506462
  constructor(state) {
506418
506463
  this.state = state;
506419
506464
  }
@@ -506428,16 +506473,52 @@ var ScrollbackController = class {
506428
506473
  dispose() {
506429
506474
  this.disposeListener?.();
506430
506475
  this.disposeListener = void 0;
506431
- this.buffer?.clear();
506476
+ this.buffer?.dispose();
506432
506477
  if (this.state.ui instanceof BottomPinnedTUI) this.state.ui.scrollbackBuffer = void 0;
506433
506478
  this.buffer = void 0;
506434
506479
  }
506435
506480
  reset() {
506436
506481
  this.buffer?.clear();
506482
+ this.suppressReplayArchiveWrites = false;
506483
+ }
506484
+ releaseSessionArchive() {
506485
+ this.buffer?.releaseArchive();
506486
+ this.suppressReplayArchiveWrites = false;
506487
+ }
506488
+ prepareNewSession(session) {
506489
+ const sessionDirectory = session?.summary?.sessionDir;
506490
+ if (typeof sessionDirectory !== "string" || sessionDirectory.length === 0) return false;
506491
+ const wirePath = path.join(sessionDirectory, "agents", "main", "wire.jsonl");
506492
+ this.buffer?.usePersistentArchive(sessionScrollbackArchiveDirectory(wirePath), {
506493
+ reset: true,
506494
+ reuse: false
506495
+ });
506496
+ this.suppressReplayArchiveWrites = false;
506497
+ return true;
506498
+ }
506499
+ prepareSessionReplay(agent) {
506500
+ const buffer = this.buffer;
506501
+ if (buffer === void 0) return false;
506502
+ const persistence = agent?.records?.persistence;
506503
+ let directory;
506504
+ if (typeof persistence?.filePath === "string") directory = sessionScrollbackArchiveDirectory(persistence.filePath);
506505
+ else if (typeof agent?.sessionWirePath === "string") directory = sessionScrollbackArchiveDirectory(agent.sessionWirePath);
506506
+ else return false;
506507
+ const resumeCheckpointRestored = agent?.records?.resumeCheckpointRestored === true || agent?.resumeCheckpointRestored === true;
506508
+ const loadedExisting = buffer.usePersistentArchive(directory, {
506509
+ reset: !resumeCheckpointRestored,
506510
+ reuse: resumeCheckpointRestored
506511
+ });
506512
+ this.suppressReplayArchiveWrites = resumeCheckpointRestored && loadedExisting;
506513
+ return true;
506514
+ }
506515
+ finishSessionReplay() {
506516
+ this.suppressReplayArchiveWrites = false;
506437
506517
  }
506438
506518
  archiveComponents(components) {
506439
506519
  const buffer = this.buffer;
506440
506520
  if (buffer === void 0 || components.length === 0) return components.length === 0;
506521
+ if (this.suppressReplayArchiveWrites && this.state.appState.isReplaying) return true;
506441
506522
  try {
506442
506523
  const width = Math.max(1, this.state.terminal.columns);
506443
506524
  const lines = components.flatMap((component) => component.render(width));
@@ -509723,6 +509804,7 @@ var SessionReplayRenderer = class {
509723
509804
  this.host.showError(uiText("replay.error.unavailable"));
509724
509805
  return false;
509725
509806
  }
509807
+ this.host.scrollbackController.prepareSessionReplay(main);
509726
509808
  this.hydrateSnapshot(main);
509727
509809
  await this.renderRecords(main);
509728
509810
  this.applyTerminalBackgroundAgentStatuses(main);
@@ -509734,6 +509816,7 @@ var SessionReplayRenderer = class {
509734
509816
  this.host.showError(uiText("replay.error.failed", { error: message }));
509735
509817
  return false;
509736
509818
  } finally {
509819
+ this.host.scrollbackController.finishSessionReplay();
509737
509820
  this.host.setAppState({ isReplaying: false });
509738
509821
  }
509739
509822
  }
@@ -511539,6 +511622,7 @@ var StreamingUIController = class {
511539
511622
  writeTelegramConsoleStatus({
511540
511623
  directory: telegramStateDir(),
511541
511624
  capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
511625
+ loadedVersion: this.host.state.appState.version,
511542
511626
  pid: process.pid,
511543
511627
  processStartedAt: this._remoteProcessStartedAt,
511544
511628
  step: this._currentStep,
@@ -516940,7 +517024,7 @@ var BlunTUI = class {
516940
517024
  if (shouldReplayHistory) {
516941
517025
  await this.sessionReplay.hydrateFromReplay(this.requireSession());
516942
517026
  this.applyStartupPermissionAndPlanToAppState();
516943
- }
517027
+ } else if (this.session !== void 0) this.scrollbackController.prepareNewSession(this.session);
516944
517028
  const resumeState = this.session?.getResumeState();
516945
517029
  if (resumeState?.warning !== void 0) this.showStatus(uiText("blunTui.warning", { warning: resumeState.warning }), "warning");
516946
517030
  if (this.session !== void 0) {
@@ -518902,7 +518986,7 @@ var BlunTUI = class {
518902
518986
  await this.refreshSkillCommands(this.session);
518903
518987
  await this.refreshPluginCommands(this.session);
518904
518988
  } catch {}
518905
- this.clearTranscriptAndRedraw();
518989
+ this.clearTranscriptAndRedraw({ preserveScrollbackArchive: true });
518906
518990
  try {
518907
518991
  await this.sessionReplay.hydrateFromReplay(session);
518908
518992
  } catch (error) {
@@ -518974,7 +519058,8 @@ var BlunTUI = class {
518974
519058
  await this.refreshPluginCommands(this.session);
518975
519059
  } catch {}
518976
519060
  this.sessionEventHandler.startSubscription();
518977
- this.clearTranscriptAndRedraw();
519061
+ this.clearTranscriptAndRedraw({ preserveScrollbackArchive: true });
519062
+ this.scrollbackController.prepareNewSession(session);
518978
519063
  this.showStatus(uiText("blunTui.session.started", { sessionId: session.id }));
518979
519064
  this.showSessionWarnings(session);
518980
519065
  this.showConfigWarningsIfAny();
@@ -519090,9 +519175,10 @@ var BlunTUI = class {
519090
519175
  disposeTranscriptChildren() {
519091
519176
  for (const child of this.state.transcriptContainer.children) if (hasDispose(child)) child.dispose();
519092
519177
  }
519093
- clearTranscriptAndRedraw() {
519178
+ clearTranscriptAndRedraw(options = {}) {
519094
519179
  this.streamingUI.discardPending();
519095
- this.scrollbackController.reset();
519180
+ if (options.preserveScrollbackArchive === true) this.scrollbackController.releaseSessionArchive();
519181
+ else this.scrollbackController.reset();
519096
519182
  this.state.transcriptEntries = [];
519097
519183
  this.streamingUI.disposeActiveCompactionBlock();
519098
519184
  this.streamingUI.resetLiveText();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.459",
3
+ "version": "9.1.461",
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": {
@@ -4809,6 +4809,7 @@ bot.command("status", async (ctx) => {
4809
4809
  queueCheckpointAt,
4810
4810
  queueUnreadBytes: queueSnapshot.unreadBytes,
4811
4811
  senderId,
4812
+ installedVersion: version,
4812
4813
  tuiFresh: isTuiLeaseFresh(),
4813
4814
  tuiHeartbeatAt,
4814
4815
  tuiPid: Number.isInteger(tuiPid) ? tuiPid : void 0,