blun-king-cli 9.1.462 → 9.1.463

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.
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ function replayWindowStartIndex(records, activeTurnCount, startsTurn) {
4
+ if (
5
+ !Array.isArray(records)
6
+ || !Number.isSafeInteger(activeTurnCount)
7
+ || activeTurnCount <= 0
8
+ || typeof startsTurn !== 'function'
9
+ ) {
10
+ return 0;
11
+ }
12
+
13
+ let remainingTurns = activeTurnCount;
14
+ for (let index = records.length - 1; index >= 0; index -= 1) {
15
+ if (!startsTurn(records[index])) continue;
16
+ remainingTurns -= 1;
17
+ if (remainingTurns === 0) return index;
18
+ }
19
+ return 0;
20
+ }
21
+
22
+ module.exports = { replayWindowStartIndex };
@@ -41,6 +41,7 @@ class SessionScrollbackArchive {
41
41
  this.loadedChunks = new Map();
42
42
  this.lineCount = 0;
43
43
  this.loadedExisting = false;
44
+ this.activeTurnCount = undefined;
44
45
 
45
46
  if (reset) {
46
47
  this.reset();
@@ -108,6 +109,21 @@ class SessionScrollbackArchive {
108
109
  return output;
109
110
  }
110
111
 
112
+ setActiveTurnCount(count) {
113
+ if (!isNonNegativeInteger(count)) return false;
114
+ if (this.activeTurnCount === count) return true;
115
+ const previous = this.activeTurnCount;
116
+ this.activeTurnCount = count;
117
+ try {
118
+ fs.mkdirSync(this.directory, { recursive: true });
119
+ this.writeIndex(this.chunks, this.lineCount);
120
+ return true;
121
+ } catch {
122
+ this.activeTurnCount = previous;
123
+ return false;
124
+ }
125
+ }
126
+
111
127
  reset() {
112
128
  try {
113
129
  fs.rmSync(this.directory, { recursive: true, force: true });
@@ -116,6 +132,7 @@ class SessionScrollbackArchive {
116
132
  this.loadedChunks.clear();
117
133
  this.lineCount = 0;
118
134
  this.loadedExisting = false;
135
+ this.activeTurnCount = undefined;
119
136
  }
120
137
 
121
138
  clear() {
@@ -165,6 +182,9 @@ class SessionScrollbackArchive {
165
182
 
166
183
  this.chunks = chunks;
167
184
  this.lineCount = parsed.totalLines;
185
+ this.activeTurnCount = isNonNegativeInteger(parsed.activeTurnCount)
186
+ ? parsed.activeTurnCount
187
+ : undefined;
168
188
  this.loadedExisting = true;
169
189
  } catch {}
170
190
  }
@@ -195,6 +215,9 @@ class SessionScrollbackArchive {
195
215
  schemaVersion: ARCHIVE_SCHEMA_VERSION,
196
216
  totalLines,
197
217
  chunks,
218
+ ...(isNonNegativeInteger(this.activeTurnCount)
219
+ ? { activeTurnCount: this.activeTurnCount }
220
+ : {}),
198
221
  }));
199
222
  }
200
223
  }
package/blun.mjs CHANGED
@@ -13,6 +13,7 @@ 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
15
  import sessionScrollbackArchive from "./bin/session-scrollback-archive.cjs";
16
+ import sessionReplayWindowPolicy from "./bin/session-replay-window-policy.cjs";
16
17
  import crypto$1, { createHash, randomBytes, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
17
18
  import * as fs$16 from "node:fs";
18
19
  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";
@@ -50,6 +51,7 @@ const { resolveCompactionModelAlias } = compactionModelPolicy;
50
51
  const { createAgentResumeSnapshot, restoreAgentResumeSnapshot } = agentResumeSnapshot;
51
52
  const { loadResumeCheckpoint, writeResumeCheckpoint } = sessionResumeCheckpoint;
52
53
  const { SessionScrollbackArchive, sessionScrollbackArchiveDirectory } = sessionScrollbackArchive;
54
+ const { replayWindowStartIndex } = sessionReplayWindowPolicy;
53
55
  import { EventEmitter as EventEmitter$1 } from "node:events";
54
56
  import { StringDecoder } from "node:string_decoder";
55
57
  import co from "node:assert";
@@ -506116,9 +506118,15 @@ var ScrollbackBuffer = class {
506116
506118
  get totalLines() {
506117
506119
  return this.archive.totalLines + this.snapshotLines.length;
506118
506120
  }
506121
+ get activeTurnCount() {
506122
+ return this.archive instanceof SessionScrollbackArchive ? this.archive.activeTurnCount : void 0;
506123
+ }
506119
506124
  archiveLines(lines) {
506120
506125
  return this.archive.append(lines);
506121
506126
  }
506127
+ setActiveTurnCount(count) {
506128
+ return this.archive instanceof SessionScrollbackArchive ? this.archive.setActiveTurnCount(count) : false;
506129
+ }
506122
506130
  usePersistentArchive(directory, options) {
506123
506131
  this.releaseArchive();
506124
506132
  const archive = new SessionScrollbackArchive({
@@ -506515,12 +506523,17 @@ var ScrollbackController = class {
506515
506523
  reset: !resumeCheckpointRestored,
506516
506524
  reuse: resumeCheckpointRestored
506517
506525
  });
506518
- this.suppressReplayArchiveWrites = resumeCheckpointRestored && loadedExisting;
506519
- return true;
506526
+ const activeTurnCount = resumeCheckpointRestored && loadedExisting && buffer.totalLines > 0 ? buffer.activeTurnCount : void 0;
506527
+ this.suppressReplayArchiveWrites = resumeCheckpointRestored && loadedExisting && activeTurnCount === void 0;
506528
+ return activeTurnCount;
506520
506529
  }
506521
506530
  finishSessionReplay() {
506531
+ this.persistActiveTurnCount(groupTurns(this.state.transcriptEntries).length);
506522
506532
  this.suppressReplayArchiveWrites = false;
506523
506533
  }
506534
+ persistActiveTurnCount(count) {
506535
+ this.buffer?.setActiveTurnCount(count);
506536
+ }
506524
506537
  archiveComponents(components) {
506525
506538
  const buffer = this.buffer;
506526
506539
  if (buffer === void 0 || components.length === 0) return components.length === 0;
@@ -509544,6 +509557,21 @@ function createReplayRenderContext() {
509544
509557
  suppressNextPlanModeOffNotice: false
509545
509558
  };
509546
509559
  }
509560
+ function replayRecordStartsVisibleTurn(record) {
509561
+ if (record?.type !== "message" || record.message?.role !== "user") return false;
509562
+ const message = record.message;
509563
+ if (backgroundOrigin(message) !== void 0) return false;
509564
+ if (message.origin?.kind === "hook_result" || message.origin?.kind === "injection") return false;
509565
+ if (message.origin?.kind === "shell_command") return message.origin.phase === "input";
509566
+ if (message.origin?.kind === "cron_job" || message.origin?.kind === "cron_missed") return false;
509567
+ if (isGoalForkClearedSystemReminder(message)) return false;
509568
+ if (goalOutcomeReminderFromSystemMessage(message) !== null) return false;
509569
+ const skill = skillActivationFromOrigin(message.origin);
509570
+ if (skill !== void 0) return message.origin?.kind === "skill_activation" && message.origin.trigger === "user-slash";
509571
+ const pluginCommand = pluginCommandFromOrigin(message.origin);
509572
+ if (pluginCommand !== void 0) return message.origin?.kind === "plugin_command" && message.origin.trigger === "user-slash";
509573
+ return true;
509574
+ }
509547
509575
  function replayEntry(context, kind, content, renderMode, extras = {}) {
509548
509576
  return {
509549
509577
  id: nextTranscriptId(),
@@ -509810,9 +509838,9 @@ var SessionReplayRenderer = class {
509810
509838
  this.host.showError(uiText("replay.error.unavailable"));
509811
509839
  return false;
509812
509840
  }
509813
- this.host.scrollbackController.prepareSessionReplay(main);
509841
+ const activeTurnCount = this.host.scrollbackController.prepareSessionReplay(main);
509814
509842
  this.hydrateSnapshot(main);
509815
- await this.renderRecords(main);
509843
+ await this.renderRecords(main, activeTurnCount);
509816
509844
  this.applyTerminalBackgroundAgentStatuses(main);
509817
509845
  this.host.mergeAllTurnSteps();
509818
509846
  this.host.requestTranscriptRender();
@@ -509880,9 +509908,11 @@ var SessionReplayRenderer = class {
509880
509908
  state.footer.setBackgroundCounts(countActiveBackgroundTasks(sessionEventHandler.backgroundTasks));
509881
509909
  state.ui.requestRender();
509882
509910
  }
509883
- async renderRecords(agent) {
509911
+ async renderRecords(agent, activeTurnCount) {
509884
509912
  const context = createReplayRenderContext();
509885
- for (const [recordIndex, record] of agent.replay.entries()) {
509913
+ const startIndex = replayWindowStartIndex(agent.replay, activeTurnCount, replayRecordStartsVisibleTurn);
509914
+ for (let recordIndex = startIndex; recordIndex < agent.replay.length; recordIndex++) {
509915
+ const record = agent.replay[recordIndex];
509886
509916
  this.renderRecord(context, record);
509887
509917
  if (shouldYieldSessionReplay(recordIndex + 1, agent.replay.length)) await yieldSessionReplayControl();
509888
509918
  }
@@ -519134,9 +519164,11 @@ var BlunTUI = class {
519134
519164
  markTranscriptComponent(component, entry);
519135
519165
  this.state.transcriptContainer.addChild(component);
519136
519166
  }
519137
- const trimmed = component !== null && this.isTurnBoundaryComponent(component) ? this.trimTranscriptWindow() : false;
519167
+ const turnBoundary = component !== null && this.isTurnBoundaryComponent(component);
519168
+ const trimmed = turnBoundary ? this.trimTranscriptWindow() : false;
519138
519169
  const deferReplayWork = this.state.appState.isReplaying;
519139
519170
  const merged = deferReplayWork ? false : this.mergeCurrentTurnSteps();
519171
+ if (turnBoundary && !deferReplayWork) this.scrollbackController.persistActiveTurnCount(groupTurns(this.state.transcriptEntries).length);
519140
519172
  if ((component || trimmed || merged) && !deferReplayWork) this.state.ui.requestRender();
519141
519173
  }
519142
519174
  appendApprovalTranscriptEntry(request, response) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.462",
3
+ "version": "9.1.463",
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": {