blun-king-cli 9.1.307 → 9.1.309

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,143 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('node:crypto');
4
+
5
+ const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
6
+ const SUBJECT_KINDS = new Set(['human', 'agent', 'task']);
7
+ const SIGNAL_KINDS = new Set(['heartbeat', 'commitment', 'task_blocked']);
8
+ const HEARTBEAT_STALE_MS = 5 * 60 * 1000;
9
+ const BLOCKED_TASK_STALE_MS = 10 * 60 * 1000;
10
+ const MAX_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000;
11
+
12
+ function fail() {
13
+ const error = new Error('ATTENTION_INVALID_INPUT');
14
+ error.code = 'ATTENTION_INVALID_INPUT';
15
+ throw error;
16
+ }
17
+
18
+ function exactKeys(value, keys) {
19
+ return value && typeof value === 'object' && !Array.isArray(value)
20
+ && Object.keys(value).every((key) => keys.has(key));
21
+ }
22
+
23
+ function safeId(value) {
24
+ const text = String(value ?? '').trim();
25
+ return SAFE_ID_RE.test(text) ? text : '';
26
+ }
27
+
28
+ function instant(value) {
29
+ if (value === null || value === undefined) return null;
30
+ const milliseconds = Date.parse(String(value));
31
+ return Number.isFinite(milliseconds) ? milliseconds : null;
32
+ }
33
+
34
+ function normalizeQuietHours(value) {
35
+ if (!exactKeys(value, new Set(['startHour', 'endHour']))) fail();
36
+ const startHour = Number(value.startHour);
37
+ const endHour = Number(value.endHour);
38
+ if (!Number.isInteger(startHour) || startHour < 0 || startHour > 23
39
+ || !Number.isInteger(endHour) || endHour < 0 || endHour > 23
40
+ || startHour === endHour) fail();
41
+ return { startHour, endHour };
42
+ }
43
+
44
+ function isQuietTime(nowMs, offsetMinutes, quietHours) {
45
+ const localHour = new Date(nowMs + offsetMinutes * 60 * 1000).getUTCHours();
46
+ return quietHours.startHour < quietHours.endHour
47
+ ? localHour >= quietHours.startHour && localHour < quietHours.endHour
48
+ : localHour >= quietHours.startHour || localHour < quietHours.endHour;
49
+ }
50
+
51
+ function signalReason(subjectKind, signal, nowMs) {
52
+ if (!exactKeys(signal, new Set([
53
+ 'kind', 'heartbeatAt', 'activeTask', 'expectedBy', 'blockedSince',
54
+ 'evidenceId', 'evidenceScope', 'confidence',
55
+ ]))) fail();
56
+ if (!SIGNAL_KINDS.has(signal.kind)) fail();
57
+ const evidenceId = safeId(signal.evidenceId);
58
+ const evidenceScope = safeId(signal.evidenceScope);
59
+ const confidence = Number(signal.confidence);
60
+ if (!evidenceId || !evidenceScope || !Number.isFinite(confidence)
61
+ || confidence < 0 || confidence > 1) fail();
62
+ if (confidence < 0.5) return null;
63
+
64
+ if (signal.kind === 'heartbeat') {
65
+ if (!exactKeys(signal, new Set(['kind', 'heartbeatAt', 'activeTask', 'evidenceId', 'evidenceScope', 'confidence']))
66
+ || subjectKind !== 'agent' || signal.activeTask !== true) return null;
67
+ const heartbeatAt = instant(signal.heartbeatAt);
68
+ if (heartbeatAt === null || heartbeatAt > nowMs) fail();
69
+ return nowMs - heartbeatAt >= HEARTBEAT_STALE_MS
70
+ ? { reason: 'missing_heartbeat', anchor: new Date(heartbeatAt).toISOString(), evidenceId, evidenceScope, confidence }
71
+ : null;
72
+ }
73
+ if (signal.kind === 'commitment') {
74
+ if (!exactKeys(signal, new Set(['kind', 'expectedBy', 'evidenceId', 'evidenceScope', 'confidence']))) fail();
75
+ const expectedBy = instant(signal.expectedBy);
76
+ if (expectedBy === null) fail();
77
+ return nowMs > expectedBy
78
+ ? { reason: 'overdue_commitment', anchor: new Date(expectedBy).toISOString(), evidenceId, evidenceScope, confidence }
79
+ : null;
80
+ }
81
+ if (!exactKeys(signal, new Set(['kind', 'blockedSince', 'evidenceId', 'evidenceScope', 'confidence'])) || subjectKind !== 'task') return null;
82
+ const blockedSince = instant(signal.blockedSince);
83
+ if (blockedSince === null || blockedSince > nowMs) fail();
84
+ return nowMs - blockedSince >= BLOCKED_TASK_STALE_MS
85
+ ? { reason: 'blocked_task', anchor: new Date(blockedSince).toISOString(), evidenceId, evidenceScope, confidence }
86
+ : null;
87
+ }
88
+
89
+ function buildAttentionCandidate(input) {
90
+ const allowed = new Set([
91
+ 'tenantId', 'subjectKind', 'subjectId', 'responsibleAgent', 'currentAgent', 'now', 'signal',
92
+ 'absenceUntil', 'timezoneOffsetMinutes', 'quietHours', 'cooldownMs', 'allowedChannel',
93
+ ]);
94
+ if (!exactKeys(input, allowed)) fail();
95
+ const tenantId = safeId(input.tenantId);
96
+ const subjectKind = String(input.subjectKind ?? '');
97
+ const subjectId = safeId(input.subjectId);
98
+ const responsibleAgent = safeId(input.responsibleAgent);
99
+ const currentAgent = safeId(input.currentAgent);
100
+ const allowedChannel = safeId(input.allowedChannel);
101
+ const nowMs = instant(input.now);
102
+ const offsetMinutes = Number(input.timezoneOffsetMinutes);
103
+ const cooldownMs = Number(input.cooldownMs);
104
+ if (!tenantId || !SUBJECT_KINDS.has(subjectKind) || !subjectId || !responsibleAgent
105
+ || !currentAgent || !allowedChannel || nowMs === null
106
+ || !Number.isInteger(offsetMinutes) || offsetMinutes < -840 || offsetMinutes > 840
107
+ || !Number.isSafeInteger(cooldownMs) || cooldownMs < 1000 || cooldownMs > MAX_COOLDOWN_MS) fail();
108
+ if (currentAgent !== responsibleAgent) return null;
109
+
110
+ const absenceUntil = instant(input.absenceUntil);
111
+ if (input.absenceUntil !== null && absenceUntil === null) fail();
112
+ if (absenceUntil !== null && absenceUntil > nowMs) return null;
113
+ const quietHours = normalizeQuietHours(input.quietHours);
114
+ if (isQuietTime(nowMs, offsetMinutes, quietHours)) return null;
115
+
116
+ const reason = signalReason(subjectKind, input.signal, nowMs);
117
+ if (reason === null) return null;
118
+ const digest = crypto.createHash('sha256')
119
+ .update([tenantId, subjectKind, subjectId, reason.reason, reason.evidenceId].join('\0'))
120
+ .digest('hex')
121
+ .slice(0, 40);
122
+ return {
123
+ candidate_id: `attention-${digest}`,
124
+ subject_kind: subjectKind,
125
+ subject_id: subjectId,
126
+ reason: reason.reason,
127
+ evidence_id: reason.evidenceId,
128
+ evidence_scope: reason.evidenceScope,
129
+ evidence_at: reason.anchor,
130
+ confidence: reason.confidence,
131
+ responsible_agent: responsibleAgent,
132
+ allowed_channel: allowedChannel,
133
+ detected_at: new Date(nowMs).toISOString(),
134
+ cooldown_ms: cooldownMs,
135
+ requires_runtime_authorization: true,
136
+ };
137
+ }
138
+
139
+ module.exports = {
140
+ BLOCKED_TASK_STALE_MS,
141
+ HEARTBEAT_STALE_MS,
142
+ buildAttentionCandidate,
143
+ };
@@ -147,6 +147,14 @@ function initialize(db) {
147
147
  );
148
148
  CREATE INDEX IF NOT EXISTS cognitive_observations_stream
149
149
  ON cognitive_observations (tenant_id, agent_id, occurred_at, observation_id);
150
+ CREATE TABLE IF NOT EXISTS cognitive_attention_claims (
151
+ tenant_id TEXT NOT NULL,
152
+ candidate_id TEXT NOT NULL,
153
+ claimed_by TEXT NOT NULL,
154
+ claimed_at TEXT NOT NULL,
155
+ next_allowed_at TEXT NOT NULL,
156
+ PRIMARY KEY (tenant_id, candidate_id)
157
+ );
150
158
  `);
151
159
  }
152
160
 
@@ -235,10 +243,57 @@ function openCognitiveStateStore({ home } = {}) {
235
243
  return { valid: true, events: rows.length, head_hash: previousHash };
236
244
  }
237
245
 
246
+ function claimAttention(input) {
247
+ const allowedInput = new Set(['tenantId', 'candidate', 'claimedBy', 'claimedAt']);
248
+ const allowedCandidate = new Set([
249
+ 'candidate_id', 'subject_kind', 'subject_id', 'reason', 'responsible_agent',
250
+ 'evidence_id', 'evidence_scope', 'evidence_at', 'confidence', 'allowed_channel',
251
+ 'detected_at', 'cooldown_ms', 'requires_runtime_authorization',
252
+ ]);
253
+ if (!exactKeys(input, allowedInput) || !exactKeys(input.candidate, allowedCandidate)
254
+ || hasForbiddenKey(input)) fail('COGNITIVE_FORBIDDEN_FIELD');
255
+ const tenant = safeId(input.tenantId);
256
+ const candidateId = safeId(input.candidate.candidate_id);
257
+ const claimedBy = safeId(input.claimedBy);
258
+ const claimedAtMs = Date.parse(String(input.claimedAt ?? ''));
259
+ const cooldownMs = Number(input.candidate.cooldown_ms);
260
+ if (!tenant || !candidateId || !claimedBy || !Number.isFinite(claimedAtMs)
261
+ || !Number.isSafeInteger(cooldownMs) || cooldownMs < 1000
262
+ || input.candidate.requires_runtime_authorization !== true) fail('COGNITIVE_INVALID_EVENT');
263
+ const claimedAt = new Date(claimedAtMs).toISOString();
264
+ const nextAllowedAt = new Date(claimedAtMs + cooldownMs).toISOString();
265
+ db.exec('BEGIN IMMEDIATE');
266
+ try {
267
+ const existing = db.prepare(`SELECT claimed_by, next_allowed_at FROM cognitive_attention_claims
268
+ WHERE tenant_id = ? AND candidate_id = ?`).get(tenant, candidateId);
269
+ if (existing && Date.parse(existing.next_allowed_at) > claimedAtMs) {
270
+ db.exec('COMMIT');
271
+ return {
272
+ claimed: false,
273
+ claimed_by: existing.claimed_by,
274
+ next_allowed_at: existing.next_allowed_at,
275
+ };
276
+ }
277
+ db.prepare(`INSERT INTO cognitive_attention_claims
278
+ (tenant_id, candidate_id, claimed_by, claimed_at, next_allowed_at) VALUES (?, ?, ?, ?, ?)
279
+ ON CONFLICT(tenant_id, candidate_id) DO UPDATE SET
280
+ claimed_by = excluded.claimed_by,
281
+ claimed_at = excluded.claimed_at,
282
+ next_allowed_at = excluded.next_allowed_at`)
283
+ .run(tenant, candidateId, claimedBy, claimedAt, nextAllowedAt);
284
+ db.exec('COMMIT');
285
+ return { claimed: true, claimed_by: claimedBy, next_allowed_at: nextAllowedAt };
286
+ } catch (error) {
287
+ try { db.exec('ROLLBACK'); } catch {}
288
+ throw error;
289
+ }
290
+ }
291
+
238
292
  return {
239
293
  commit,
240
294
  read,
241
295
  verify,
296
+ claimAttention,
242
297
  close: () => {
243
298
  try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch {}
244
299
  db.close();
@@ -68,6 +68,7 @@ const RAW_ARGS = process.argv.slice(2);
68
68
  const PROFILE = parseProfileLaunchArgs(RAW_ARGS, launcherModeFromArgv(process.argv));
69
69
  const ARGS = PROFILE.args;
70
70
  const CORE_LOAD_TIMEOUT_MS = 30_000;
71
+ const RUNNING_UPDATE_RESUME_SESSION_ENV = 'BLUN_RUNNING_UPDATE_RESUME_SESSION_ID';
71
72
  const RUNTIME_READY_TIMEOUT_MS = 60_000;
72
73
  const RUNNING_UPDATE_RECHECK_MS = 5 * 60_000;
73
74
  const RUNNING_UPDATE_RECHECK_JITTER_MS = 2 * 60_000;
@@ -312,6 +313,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
312
313
  let pendingHandoff;
313
314
  let updateStarted = false;
314
315
  let runtimeReady = false;
316
+ let activeSession;
315
317
  let runtimeExitIntent = false;
316
318
  let supervisionCompleted = false;
317
319
  let runningUpdatePollTimer;
@@ -329,6 +331,23 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
329
331
  (options.clearTimeoutImpl || clearTimeout)(runningUpdatePollTimer);
330
332
  runningUpdatePollTimer = undefined;
331
333
  };
334
+ const validSessionMessage = (message) => typeof message?.sessionId === 'string'
335
+ && message.sessionId.length > 0
336
+ && message.sessionId.length <= 256
337
+ && !/[\u0000-\u001f\u007f]/u.test(message.sessionId)
338
+ && resolveHandoffCwd(message.cwd, '') !== '';
339
+ const stagedRuntimeOptions = () => ({
340
+ mode: runningUpdateMode,
341
+ ...(runningUpdateMode === RUNNING_UPDATE_MODES.RESUME && activeSession !== undefined
342
+ ? { cwd: activeSession.cwd, sessionId: activeSession.sessionId }
343
+ : {}),
344
+ });
345
+ const persistPreparedRuntime = () => {
346
+ if (preparedTarget === undefined) return;
347
+ const sharedHome = env.BLUN_SHARED_HOME;
348
+ if (typeof sharedHome !== 'string' || sharedHome.length === 0) return;
349
+ (options.stageRuntime || stageRuntime)(sharedHome, preparedTarget, stagedRuntimeOptions());
350
+ };
332
351
  const scheduleNextPreparation = (child) => {
333
352
  clearRunningUpdatePoll();
334
353
  if (supervisionCompleted || pendingHandoff !== undefined || updateStarted) return;
@@ -367,7 +386,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
367
386
  return;
368
387
  }
369
388
  preparedTarget = target;
370
- (options.stageRuntime || stageRuntime)(sharedHome, target, { mode: runningUpdateMode });
389
+ persistPreparedRuntime();
371
390
  const runningVersion = readPackageVersionAt(packageRoot);
372
391
  (options.recordRunningUpdateEvent || recordRunningUpdateEvent)(sharedHome, previousTarget === undefined ? {
373
392
  event: 'prepared-for-next-start',
@@ -396,13 +415,20 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
396
415
  && preparedTarget !== undefined
397
416
  && message.version === preparedTarget.version
398
417
  && message.mode === runningUpdateMode
399
- && typeof message.sessionId === 'string'
400
- && message.sessionId.length > 0
401
- && message.sessionId.length <= 256
402
- && !/[\u0000-\u001f\u007f]/u.test(message.sessionId)
403
- && resolveHandoffCwd(message.cwd, '') !== '') {
404
- pendingHandoff = Object.freeze({
418
+ && validSessionMessage(message)) {
419
+ activeSession = Object.freeze({
405
420
  cwd: resolveHandoffCwd(message.cwd, cwd),
421
+ sessionId: message.sessionId,
422
+ });
423
+ try {
424
+ persistPreparedRuntime();
425
+ } catch (error) {
426
+ options.onRunningUpdateError?.(error);
427
+ scheduleNextPreparation(child);
428
+ return;
429
+ }
430
+ pendingHandoff = Object.freeze({
431
+ cwd: activeSession.cwd,
406
432
  mode: message.mode,
407
433
  sessionId: message.sessionId,
408
434
  target: preparedTarget,
@@ -423,6 +449,17 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
423
449
  }
424
450
  }
425
451
  if (message?.type === RUNTIME_READY_MESSAGE) {
452
+ if (validSessionMessage(message)) {
453
+ activeSession = Object.freeze({
454
+ cwd: resolveHandoffCwd(message.cwd, cwd),
455
+ sessionId: message.sessionId,
456
+ });
457
+ try {
458
+ persistPreparedRuntime();
459
+ } catch (error) {
460
+ options.onRunningUpdateError?.(error);
461
+ }
462
+ }
426
463
  runtimeReady = true;
427
464
  refreshRunningUpdateMode();
428
465
  if (automaticMode()) startPreparation(child);
@@ -482,7 +519,11 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
482
519
  const handoffArgs = handoffArgsForMode(args, pendingHandoff.mode, pendingHandoff.sessionId);
483
520
  const handoffCwd = pendingHandoff.cwd;
484
521
  const previousActive = (options.readActiveRuntime || readActiveRuntime)(sharedHome);
485
- const nextCore = spawnCore(handoffArgs, env, handoffCwd, { packageRoot: target.packageRoot });
522
+ const resumeEnv = {
523
+ ...env,
524
+ [RUNNING_UPDATE_RESUME_SESSION_ENV]: pendingHandoff.sessionId,
525
+ };
526
+ const nextCore = spawnCore(handoffArgs, resumeEnv, handoffCwd, { packageRoot: target.packageRoot });
486
527
  const nextLoaded = await nextCore.loaded;
487
528
  const nextReady = nextLoaded && await (options.waitForRuntimeReady || waitForRuntimeReady)(
488
529
  nextCore,
@@ -498,7 +539,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
498
539
  toVersion: target.version,
499
540
  mode: pendingHandoff.mode,
500
541
  }, { now: options.nowImpl });
501
- return superviseProtectedCore(handoffArgs, env, handoffCwd, async () => {}, {
542
+ return superviseProtectedCore(handoffArgs, resumeEnv, handoffCwd, async () => {}, {
502
543
  ...options,
503
544
  existingCore: nextCore,
504
545
  packageRoot: target.packageRoot,
@@ -519,14 +560,14 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
519
560
  fromVersion: readPackageVersionAt(packageRoot),
520
561
  toVersion: target.version,
521
562
  }, { now: options.nowImpl });
522
- const fallback = spawnCore(handoffArgs, env, handoffCwd, { packageRoot });
563
+ const fallback = spawnCore(handoffArgs, resumeEnv, handoffCwd, { packageRoot });
523
564
  const fallbackLoaded = await fallback.loaded;
524
565
  const fallbackReady = fallbackLoaded && await (options.waitForRuntimeReady || waitForRuntimeReady)(
525
566
  fallback,
526
567
  options.readyTimeoutMs,
527
568
  );
528
569
  if (!fallbackReady) return 1;
529
- return superviseProtectedCore(handoffArgs, env, handoffCwd, async () => {}, {
570
+ return superviseProtectedCore(handoffArgs, resumeEnv, handoffCwd, async () => {}, {
530
571
  ...options,
531
572
  existingCore: fallback,
532
573
  packageRoot,
@@ -641,14 +682,20 @@ function spawnManagedLauncher(binary, cwd) {
641
682
  });
642
683
  }
643
684
 
644
- function spawnActiveLauncher(packageRoot, cwd, args = RAW_ARGS) {
685
+ function spawnActiveLauncher(packageRoot, cwd, args = RAW_ARGS, options = {}) {
645
686
  const entryName = path.basename(process.argv[1] || 'king.js').toLowerCase() === 'blun.js'
646
687
  ? 'blun.js'
647
688
  : 'king.js';
648
689
  return new Promise((resolve, reject) => {
649
690
  const child = spawn(process.execPath, [path.join(packageRoot, 'bin', entryName), ...args], {
650
691
  cwd,
651
- env: { ...process.env, BLUN_ACTIVE_RUNTIME_ROOT: packageRoot },
692
+ env: {
693
+ ...process.env,
694
+ BLUN_ACTIVE_RUNTIME_ROOT: packageRoot,
695
+ ...(typeof options.resumeSessionId === 'string' && options.resumeSessionId.length > 0
696
+ ? { [RUNNING_UPDATE_RESUME_SESSION_ENV]: options.resumeSessionId }
697
+ : {}),
698
+ },
652
699
  stdio: 'inherit',
653
700
  windowsHide: true,
654
701
  });
@@ -873,6 +920,9 @@ async function runLauncher(options = {}) {
873
920
  deferredPendingRuntime.packageRoot,
874
921
  deferredPendingRuntime.cwd || callerCwd,
875
922
  pendingArgs,
923
+ deferredPendingRuntime.mode === RUNNING_UPDATE_MODES.RESUME
924
+ ? { resumeSessionId: deferredPendingRuntime.sessionId }
925
+ : {},
876
926
  );
877
927
  return;
878
928
  }
package/blun.mjs CHANGED
@@ -515728,20 +515728,28 @@ const {
515728
515728
  readRunningUpdateMode,
515729
515729
  writeRunningUpdateMode
515730
515730
  } = __require("./bin/running-update-preference.cjs");
515731
- function notifyRunningRuntimeReady(tui) {
515732
- if (typeof process.env["BLUN_SHARED_HOME"] === "string" && process.env["BLUN_SHARED_HOME"].length > 0) void pruneRunningUpdateReleases(process.env["BLUN_SHARED_HOME"], {
515733
- activePackageRoot: __dirname
515734
- }).catch(() => {});
515731
+ const RUNNING_UPDATE_RESUME_SESSION_ENV = "BLUN_RUNNING_UPDATE_RESUME_SESSION_ID";
515732
+ function shouldContinueActiveGoalAfterRunningUpdate(expectedSessionId, currentSessionId, goalStatus) {
515733
+ return typeof expectedSessionId === "string" && expectedSessionId.length > 0 && currentSessionId === expectedSessionId && goalStatus === "active";
515734
+ }
515735
+ function sendRunningRuntimeSession(tui) {
515735
515736
  if (!process.connected) return;
515736
515737
  const sessionId = tui.getCurrentSessionId();
515737
515738
  if (sessionId.length === 0) return;
515738
515739
  try {
515739
515740
  process.send({
515740
515741
  type: RUNTIME_READY_MESSAGE,
515741
- sessionId
515742
+ sessionId,
515743
+ cwd: tui.state.appState.workDir
515742
515744
  });
515743
515745
  } catch {}
515744
515746
  }
515747
+ function notifyRunningRuntimeReady(tui) {
515748
+ if (typeof process.env["BLUN_SHARED_HOME"] === "string" && process.env["BLUN_SHARED_HOME"].length > 0) void pruneRunningUpdateReleases(process.env["BLUN_SHARED_HOME"], {
515749
+ activePackageRoot: __dirname
515750
+ }).catch(() => {});
515751
+ sendRunningRuntimeSession(tui);
515752
+ }
515745
515753
  function requestRunningUpdateAtSafeBoundary(tui) {
515746
515754
  if (tui.runningUpdateHandoffStarted || tui.isShuttingDown || !process.connected) return false;
515747
515755
  const version = tui.runningUpdatePreparedVersion;
@@ -516268,7 +516276,8 @@ var BlunTUI = class {
516268
516276
  if (this.session !== void 0) {
516269
516277
  this.sessionEventHandler.startSubscription();
516270
516278
  if (shouldReplayHistory) {
516271
- await this.promptStartupResumeGoalIfNeeded();
516279
+ const continuedAfterUpdate = this.continueActiveGoalAfterRunningUpdateIfNeeded();
516280
+ if (!continuedAfterUpdate) await this.promptStartupResumeGoalIfNeeded();
516272
516281
  if (this.aborted) return;
516273
516282
  }
516274
516283
  this.showSessionWarnings(this.session);
@@ -516418,6 +516427,13 @@ var BlunTUI = class {
516418
516427
  }
516419
516428
  if (choice === "resume") await handleGoalCommand(this, "resume");
516420
516429
  }
516430
+ continueActiveGoalAfterRunningUpdateIfNeeded() {
516431
+ const expectedSessionId = process.env[RUNNING_UPDATE_RESUME_SESSION_ENV];
516432
+ delete process.env[RUNNING_UPDATE_RESUME_SESSION_ENV];
516433
+ if (!shouldContinueActiveGoalAfterRunningUpdate(expectedSessionId, this.session?.id, this.state.appState.goal?.status)) return false;
516434
+ this.sendNormalUserInput(RESUME_GOAL_INPUT);
516435
+ return true;
516436
+ }
516421
516437
  async stop(exitCode) {
516422
516438
  if (this.isShuttingDown) return;
516423
516439
  this.isShuttingDown = true;
@@ -517913,6 +517929,7 @@ var BlunTUI = class {
517913
517929
  if (resumeState?.warning !== void 0) this.showStatus(uiText("blunTui.warning", { warning: resumeState.warning }), "warning");
517914
517930
  this.showStatus(statusMessage);
517915
517931
  this.showSessionWarnings(session);
517932
+ sendRunningRuntimeSession(this);
517916
517933
  }
517917
517934
  async reloadCurrentSessionView(session, statusMessage) {
517918
517935
  await this.personalMemoryController.clear(session);
@@ -517976,6 +517993,7 @@ var BlunTUI = class {
517976
517993
  this.showStatus(uiText("blunTui.session.started", { sessionId: session.id }));
517977
517994
  this.showSessionWarnings(session);
517978
517995
  this.showConfigWarningsIfAny();
517996
+ sendRunningRuntimeSession(this);
517979
517997
  }
517980
517998
  /** Surface config.toml load warnings (degraded or kept-previous config) in the status bar. */
517981
517999
  async showConfigWarningsIfAny() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.307",
3
+ "version": "9.1.309",
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": {