blun-king-cli 9.1.335 → 9.1.336

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.
@@ -56,6 +56,7 @@ function isExplicitPauseRequest(text) {
56
56
  function createDirectFocusController(options = {}) {
57
57
  const setTimer = options.setTimer ?? setTimeout;
58
58
  const clearTimer = options.clearTimer ?? clearTimeout;
59
+ const now = options.now ?? Date.now;
59
60
  const checkpoint = options.checkpoint ?? (() => {});
60
61
  const resume = options.resume ?? (() => {});
61
62
  const silenceMs = options.silenceMs ?? DEFAULT_SILENCE_MS;
@@ -89,16 +90,23 @@ function createDirectFocusController(options = {}) {
89
90
  finishConversation(chatId, existing);
90
91
  return { resumeGranted: true };
91
92
  }
92
- if (conversations.size === 0) {
93
+ const startsConversation = conversations.size === 0;
94
+ if (startsConversation) {
93
95
  savedCheckpoint = { ...checkpointValue, chatId };
94
- checkpoint(savedCheckpoint);
95
96
  }
96
97
  const conversation = existing ?? { chatId };
97
98
  clearConversationTimer(conversation);
99
+ const wasExplicitPause = conversation.explicitPause === true;
98
100
  conversation.explicitPause = conversation.explicitPause === true || isExplicitPauseRequest(text);
99
101
  conversation.waitingPermission = conversation.explicitPause;
100
102
  conversation.closeAfterReply = isConversationClose(text);
101
103
  conversations.set(chatId, conversation);
104
+ savedCheckpoint = {
105
+ ...savedCheckpoint,
106
+ explicitPause: conversation.explicitPause,
107
+ resumeAfterAt: null,
108
+ };
109
+ if (startsConversation || wasExplicitPause !== conversation.explicitPause) checkpoint(savedCheckpoint);
102
110
  return { resumeGranted: false, explicitPause: conversation.explicitPause };
103
111
  }
104
112
 
@@ -109,10 +117,14 @@ function createDirectFocusController(options = {}) {
109
117
  clearConversationTimer(conversation);
110
118
  if (conversation.explicitPause === true) {
111
119
  conversation.waitingPermission = true;
120
+ checkpoint({ ...savedCheckpoint, explicitPause: true, resumeAfterAt: null });
112
121
  return true;
113
122
  }
114
123
  const delay = conversation.closeAfterReply ? 0 : silenceMs;
115
124
  conversation.closeAfterReply = false;
125
+ const resumeAfterAt = new Date(now() + delay).toISOString();
126
+ savedCheckpoint = { ...savedCheckpoint, explicitPause: false, resumeAfterAt };
127
+ checkpoint(savedCheckpoint);
116
128
  conversation.timer = setTimer(() => {
117
129
  finishConversation(chatId, conversation);
118
130
  }, delay);
@@ -125,6 +137,26 @@ function createDirectFocusController(options = {}) {
125
137
  savedCheckpoint = undefined;
126
138
  }
127
139
 
140
+ const initialCheckpoint = options.initialCheckpoint;
141
+ if (initialCheckpoint?.status === 'paused' && isPrivateTelegramChat(initialCheckpoint.chatId)) {
142
+ const chatId = String(initialCheckpoint.chatId);
143
+ const explicitPause = initialCheckpoint.explicitPause === true;
144
+ const conversation = {
145
+ chatId,
146
+ explicitPause,
147
+ waitingPermission: explicitPause,
148
+ };
149
+ savedCheckpoint = { ...initialCheckpoint, chatId };
150
+ conversations.set(chatId, conversation);
151
+ const resumeAfterMs = Date.parse(String(initialCheckpoint.resumeAfterAt ?? ''));
152
+ if (!explicitPause && Number.isFinite(resumeAfterMs)) {
153
+ conversation.timer = setTimer(
154
+ () => finishConversation(chatId, conversation),
155
+ Math.max(0, resumeAfterMs - now()),
156
+ );
157
+ }
158
+ }
159
+
128
160
  return {
129
161
  dispose,
130
162
  isPaused: () => conversations.size > 0,
@@ -145,7 +177,13 @@ function writeDirectFocusCheckpoint(value, env = process.env) {
145
177
  const record = {
146
178
  version: 1,
147
179
  status: value?.status === 'resumed' ? 'resumed' : 'paused',
148
- pausedAt: new Date().toISOString(),
180
+ pausedAt: Number.isFinite(Date.parse(String(value?.pausedAt ?? '')))
181
+ ? new Date(value.pausedAt).toISOString()
182
+ : new Date().toISOString(),
183
+ explicitPause: value?.explicitPause === true,
184
+ resumeAfterAt: Number.isFinite(Date.parse(String(value?.resumeAfterAt ?? '')))
185
+ ? new Date(value.resumeAfterAt).toISOString()
186
+ : null,
149
187
  chatId: isPrivateTelegramChat(value?.chatId) ? String(value.chatId) : null,
150
188
  sessionId: value?.sessionId === undefined ? null : String(value.sessionId).slice(0, 160),
151
189
  turnId: value?.turnId === undefined ? null : String(value.turnId),
@@ -160,6 +198,18 @@ function writeDirectFocusCheckpoint(value, env = process.env) {
160
198
  return { path: target, record };
161
199
  }
162
200
 
201
+ function readDirectFocusCheckpoint(env = process.env) {
202
+ const target = directFocusCheckpointPath(env);
203
+ try {
204
+ const record = JSON.parse(fs.readFileSync(target, 'utf8'));
205
+ if (record?.version !== 1 || (record.status !== 'paused' && record.status !== 'resumed')) return undefined;
206
+ return record;
207
+ } catch (error) {
208
+ if (error?.code === 'ENOENT') return undefined;
209
+ throw error;
210
+ }
211
+ }
212
+
163
213
  module.exports = {
164
214
  DEFAULT_SILENCE_MS,
165
215
  createDirectFocusController,
@@ -169,6 +219,7 @@ module.exports = {
169
219
  isExplicitPauseRequest,
170
220
  isPrivateTelegramChat,
171
221
  isResumeApproval,
222
+ readDirectFocusCheckpoint,
172
223
  rewriteTelegramDirectEnvelope,
173
224
  telegramDirectMessage,
174
225
  writeDirectFocusCheckpoint,
package/blun.mjs CHANGED
@@ -419036,7 +419036,7 @@ registerUiCatalogFragment({
419036
419036
  */
419037
419037
  var { projectUnaddressedTelegramContext } = createRequire(import.meta.url)("./bin/telegram-context-projection-policy.cjs");
419038
419038
  var { enqueueTelegramUrgent, rewriteTelegramUrgentEnvelope, telegramUrgentMessage } = createRequire(import.meta.url)("./bin/telegram-urgent-policy.cjs");
419039
- var { createDirectFocusController, enqueueTelegramDirect, rewriteTelegramDirectEnvelope, telegramDirectMessage, writeDirectFocusCheckpoint } = createRequire(import.meta.url)("./bin/telegram-direct-focus-policy.cjs");
419039
+ var { createDirectFocusController, enqueueTelegramDirect, readDirectFocusCheckpoint, rewriteTelegramDirectEnvelope, telegramDirectMessage, writeDirectFocusCheckpoint } = createRequire(import.meta.url)("./bin/telegram-direct-focus-policy.cjs");
419040
419040
  var { removeQueuedReloadCommands, removeQueuedSlashCommands } = createRequire(import.meta.url)("./bin/reload-queue-policy.cjs");
419041
419041
  const MAX_BUFFERED_CONTEXT_MESSAGES = 20;
419042
419042
  const MAX_BUFFERED_CONTEXT_CHARS = 24e3;
@@ -509485,6 +509485,9 @@ var SessionReplayRenderer = class {
509485
509485
  cleanupRuntime(context) {
509486
509486
  this.flushAssistant(context);
509487
509487
  this.host.streamingUI.cleanupAfterReplay(context.completedToolCallIds);
509488
+ this.host.setAppState({ streamingPhase: "idle" });
509489
+ this.host.resetLivePane();
509490
+ this.host.requestQueueDrain();
509488
509491
  }
509489
509492
  renderSkillActivation(context, skill) {
509490
509493
  const { sessionEventHandler } = this.host;
@@ -516082,6 +516085,7 @@ var BlunTUI = class {
516082
516085
  deliverOne: () => this.deliverQueuedChannelHead()
516083
516086
  });
516084
516087
  this.directFocusController = createDirectFocusController({
516088
+ initialCheckpoint: readDirectFocusCheckpoint(),
516085
516089
  checkpoint: (checkpoint) => {
516086
516090
  try {
516087
516091
  writeDirectFocusCheckpoint(checkpoint);
@@ -516929,7 +516933,7 @@ var BlunTUI = class {
516929
516933
  return this.restoreQueuedSteer(inFlight, error);
516930
516934
  };
516931
516935
  try {
516932
- if (!(await this.harness.withInteractiveAgent(item.agentId ?? "main", () => session.steerActive(input, expectedTurnId === void 0 ? {} : { expectedTurnId }))).accepted) return restoreHead();
516936
+ if (!(await this.harness.withInteractiveAgent(item.agentId ?? "main", () => session.steerActive(input, expectedTurnId === void 0 ? {} : { expectedTurnId }))).accepted) return this.recoverRejectedActiveSteer(inFlight);
516933
516937
  inFlight.accepted = true;
516934
516938
  if (inFlight.turnEnded) return restoreHead();
516935
516939
  this.commitQueuedSteerIfReady(inFlight);
@@ -516981,6 +516985,15 @@ var BlunTUI = class {
516981
516985
  this.steerQueueFlushPrefix();
516982
516986
  this.scheduleQueueDrain();
516983
516987
  }
516988
+ recoverRejectedActiveSteer(inFlight) {
516989
+ const restored = this.restoreQueuedSteer(inFlight);
516990
+ if (!this.streamingUI.hasActiveTurn()) {
516991
+ this.setAppState({ streamingPhase: "idle" });
516992
+ this.resetLivePane();
516993
+ this.scheduleQueueDrain();
516994
+ }
516995
+ return restored;
516996
+ }
516984
516997
  restoreQueuedSteer(inFlight, error) {
516985
516998
  if (this.queueSteerInFlight !== inFlight) return false;
516986
516999
  this.queueSteerInFlight = void 0;
@@ -517071,7 +517084,7 @@ var BlunTUI = class {
517071
517084
  const expectedTurnId = turnId !== void 0 && /^\d+$/.test(turnId) ? Number(turnId) : void 0;
517072
517085
  session.steerActive(items.map((item) => item.text.trim()).join("\n\n"), expectedTurnId === void 0 ? {} : { expectedTurnId }).then((result) => {
517073
517086
  if (this.queueSteerInFlight !== inFlight) return;
517074
- if (!result.accepted) this.restoreQueuedSteer(inFlight);
517087
+ if (!result.accepted) this.recoverRejectedActiveSteer(inFlight);
517075
517088
  else {
517076
517089
  inFlight.accepted = true;
517077
517090
  if (inFlight.turnEnded) this.restoreQueuedSteer(inFlight);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.335",
3
+ "version": "9.1.336",
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": {