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.
- package/bin/session-resume-checkpoint.cjs +1 -1
- package/bin/session-scrollback-archive.cjs +206 -0
- package/bin/telegram-console-status-policy.cjs +18 -1
- package/bin/telegram-remote-status-policy.cjs +11 -1
- package/blun.mjs +92 -6
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge.mjs +1 -0
|
@@ -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 =
|
|
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 ((
|
|
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
|
|
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?.
|
|
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.
|
|
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
|
@@ -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,
|