blun-king-cli 9.1.193 → 9.1.195

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.
@@ -256,11 +256,23 @@ async function waitForRuntimeReady(core, timeoutMs = RUNTIME_READY_TIMEOUT_MS) {
256
256
  }
257
257
  }
258
258
 
259
+ function resolveHandoffCwd(candidate, fallback) {
260
+ if (typeof candidate !== 'string' || candidate.trim().length === 0 || !path.isAbsolute(candidate)) {
261
+ return fallback;
262
+ }
263
+ try {
264
+ return fs.statSync(candidate).isDirectory() ? path.resolve(candidate) : fallback;
265
+ } catch {
266
+ return fallback;
267
+ }
268
+ }
269
+
259
270
  async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {}) {
260
271
  const packageRoot = path.resolve(options.packageRoot || PKG);
261
272
  let preparedTarget;
262
273
  let handoffSessionId;
263
274
  let handoffMode;
275
+ let handoffCwd = cwd;
264
276
  let updateStarted = false;
265
277
  let runtimeReady = false;
266
278
  let runningUpdatePollTimer;
@@ -273,6 +285,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
273
285
  runningUpdateMode = persistedMode;
274
286
  handoffSessionId = undefined;
275
287
  handoffMode = undefined;
288
+ handoffCwd = cwd;
276
289
  }
277
290
  };
278
291
  const clearRunningUpdatePoll = () => {
@@ -337,6 +350,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
337
350
  runningUpdateMode = normalizeRunningUpdateMode(message.mode);
338
351
  handoffSessionId = undefined;
339
352
  handoffMode = undefined;
353
+ handoffCwd = cwd;
340
354
  clearRunningUpdatePoll();
341
355
  if (automaticMode()) {
342
356
  if (preparedTarget !== undefined) announcePrepared(child);
@@ -353,6 +367,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
353
367
  && automaticMode()) {
354
368
  handoffSessionId = message.sessionId;
355
369
  handoffMode = runningUpdateMode;
370
+ handoffCwd = resolveHandoffCwd(message.cwd, cwd);
356
371
  }
357
372
  };
358
373
  const spawnCore = options.spawnProtectedCore || spawnProtectedCore;
@@ -394,7 +409,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
394
409
  probeTarget: options.probeRunningUpdate || (() => true),
395
410
  stopOld: async () => {},
396
411
  startCore: async ({ packageRoot: nextRoot, args: nextArgs }) => {
397
- const nextCore = spawnCore(nextArgs, env, cwd, { packageRoot: nextRoot });
412
+ const nextCore = spawnCore(nextArgs, env, handoffCwd, { packageRoot: nextRoot });
398
413
  const nextLoaded = await nextCore.loaded;
399
414
  const ready = nextLoaded && await waitForRuntimeReady(nextCore, options.readyTimeoutMs);
400
415
  if (!ready) {
@@ -408,7 +423,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
408
423
  const resumed = handoff.core;
409
424
  if (resumed === undefined || resumed.ready !== true) return 1;
410
425
  const resumedArgs = handoffArgsForMode(args, handoffMode, handoffSessionId);
411
- return superviseProtectedCore(resumedArgs, env, cwd, async () => {}, {
426
+ return superviseProtectedCore(resumedArgs, env, handoffCwd, async () => {}, {
412
427
  ...options,
413
428
  existingCore: resumed,
414
429
  packageRoot: handoff.runtime.packageRoot,
@@ -745,6 +760,7 @@ module.exports = {
745
760
  RUNNING_UPDATE_RECHECK_MS,
746
761
  createManagedNodeEnvironment,
747
762
  resolveLauncherPrivatePaths,
763
+ resolveHandoffCwd,
748
764
  runningUpdateRecheckDelay,
749
765
  runLauncher,
750
766
  scheduleRunningUpdateRecheck,
package/blun.mjs CHANGED
@@ -505310,6 +505310,7 @@ registerUiCatalogFragment({
505310
505310
  "sessionEvent.mcp.connecting": "MCP server \"{name}\" connecting…",
505311
505311
  "sessionEvent.skill.activated": "Activated skill: {skillName}",
505312
505312
  "sessionEvent.watchdog.idle": "Turn running for {minutes} min; last tool: {tool}; repeated failures: {repetitions}.",
505313
+ "sessionEvent.watchdog.timeout": "Turn stopped after {minutes} min without progress; last tool: {tool}.",
505313
505314
  "sessionEvent.watchdog.aborted": "Turn stopped after {repetitions} repeated failed calls to {tool}."
505314
505315
  },
505315
505316
  de: {
@@ -505339,6 +505340,7 @@ registerUiCatalogFragment({
505339
505340
  "sessionEvent.mcp.connecting": "MCP-Server „{name}“ wird verbunden…",
505340
505341
  "sessionEvent.skill.activated": "Skill aktiviert: {skillName}",
505341
505342
  "sessionEvent.watchdog.idle": "Zug läuft seit {minutes} Min.; letztes Werkzeug: {tool}; wiederholte Fehler: {repetitions}.",
505343
+ "sessionEvent.watchdog.timeout": "Zug nach {minutes} Min. ohne Fortschritt beendet; letztes Werkzeug: {tool}.",
505342
505344
  "sessionEvent.watchdog.aborted": "Zug nach {repetitions} wiederholten fehlgeschlagenen Aufrufen von {tool} beendet."
505343
505345
  },
505344
505346
  es: {
@@ -505368,6 +505370,7 @@ registerUiCatalogFragment({
505368
505370
  "sessionEvent.mcp.connecting": "Conectando el servidor MCP «{name}»…",
505369
505371
  "sessionEvent.skill.activated": "Skill activado: {skillName}",
505370
505372
  "sessionEvent.watchdog.idle": "El turno lleva {minutes} min en curso; última herramienta: {tool}; fallos repetidos: {repetitions}.",
505373
+ "sessionEvent.watchdog.timeout": "Turno detenido tras {minutes} min sin progreso; última herramienta: {tool}.",
505371
505374
  "sessionEvent.watchdog.aborted": "Turno detenido tras {repetitions} llamadas fallidas repetidas a {tool}."
505372
505375
  },
505373
505376
  fr: {
@@ -505397,6 +505400,7 @@ registerUiCatalogFragment({
505397
505400
  "sessionEvent.mcp.connecting": "Connexion du serveur MCP « {name} »…",
505398
505401
  "sessionEvent.skill.activated": "Skill activé : {skillName}",
505399
505402
  "sessionEvent.watchdog.idle": "Tour en cours depuis {minutes} min ; dernier outil : {tool} ; échecs répétés : {repetitions}.",
505403
+ "sessionEvent.watchdog.timeout": "Tour arrêté après {minutes} min sans progression ; dernier outil : {tool}.",
505400
505404
  "sessionEvent.watchdog.aborted": "Tour arrêté après {repetitions} appels répétés ayant échoué pour l'outil {tool}."
505401
505405
  },
505402
505406
  sv: {
@@ -505426,6 +505430,7 @@ registerUiCatalogFragment({
505426
505430
  "sessionEvent.mcp.connecting": "Ansluter MCP-servern ”{name}”…",
505427
505431
  "sessionEvent.skill.activated": "Skill aktiverad: {skillName}",
505428
505432
  "sessionEvent.watchdog.idle": "Körningen har pågått i {minutes} min. Senaste verktyg: {tool}. Upprepade fel: {repetitions}.",
505433
+ "sessionEvent.watchdog.timeout": "Körningen stoppades efter {minutes} min utan framsteg. Senaste verktyg: {tool}.",
505429
505434
  "sessionEvent.watchdog.aborted": "Körningen stoppades efter {repetitions} upprepade misslyckade anrop till {tool}."
505430
505435
  },
505431
505436
  cs: {
@@ -505455,6 +505460,7 @@ registerUiCatalogFragment({
505455
505460
  "sessionEvent.mcp.connecting": "Server MCP \"{name}\" se připojuje…",
505456
505461
  "sessionEvent.skill.activated": "Aktivována dovednost: {skillName}",
505457
505462
  "sessionEvent.watchdog.idle": "Kolo běží {minutes} min; poslední nástroj: {tool}; opakovaná selhání: {repetitions}.",
505463
+ "sessionEvent.watchdog.timeout": "Kolo bylo zastaveno po {minutes} min bez pokroku; poslední nástroj: {tool}.",
505458
505464
  "sessionEvent.watchdog.aborted": "Kolo bylo zastaveno po {repetitions} opakovaných neúspěšných voláních nástroje {tool}."
505459
505465
  }
505460
505466
  });
@@ -506606,18 +506612,22 @@ function isUserCancelledSubagentError(error) {
506606
506612
  //#endregion
506607
506613
  //#region src/tui/controllers/turn-watchdog.ts
506608
506614
  const DEFAULT_IDLE_MINUTES = 20;
506615
+ const DEFAULT_HARD_IDLE_MINUTES = 30;
506609
506616
  const DEFAULT_MAX_FAILED_REPETITIONS = 5;
506610
506617
  const MIN_IDLE_MINUTES = 1;
506611
506618
  const MAX_IDLE_MINUTES = 1440;
506612
506619
  const MIN_FAILED_REPETITIONS = 2;
506613
506620
  const MAX_FAILED_REPETITIONS = 100;
506614
506621
  const TURN_WATCHDOG_IDLE_MINUTES_ENV = "BLUN_TURN_WATCHDOG_IDLE_MINUTES";
506622
+ const TURN_WATCHDOG_HARD_IDLE_MINUTES_ENV = "BLUN_TURN_WATCHDOG_HARD_IDLE_MINUTES";
506615
506623
  const TURN_WATCHDOG_MAX_FAILED_REPETITIONS_ENV = "BLUN_TURN_WATCHDOG_MAX_FAILED_REPETITIONS";
506616
506624
  function resolveTurnWatchdogConfig(env = process.env) {
506617
506625
  const idleMinutes = parseBoundedInteger(env[TURN_WATCHDOG_IDLE_MINUTES_ENV], DEFAULT_IDLE_MINUTES, MIN_IDLE_MINUTES, MAX_IDLE_MINUTES);
506626
+ const hardIdleMinutes = parseBoundedInteger(env[TURN_WATCHDOG_HARD_IDLE_MINUTES_ENV], DEFAULT_HARD_IDLE_MINUTES, MIN_IDLE_MINUTES, MAX_IDLE_MINUTES);
506618
506627
  const maxFailedRepetitions = parseBoundedInteger(env[TURN_WATCHDOG_MAX_FAILED_REPETITIONS_ENV], DEFAULT_MAX_FAILED_REPETITIONS, MIN_FAILED_REPETITIONS, MAX_FAILED_REPETITIONS);
506619
506628
  return {
506620
506629
  idleMs: idleMinutes * 6e4,
506630
+ hardIdleMs: hardIdleMinutes * 6e4,
506621
506631
  maxFailedRepetitions
506622
506632
  };
506623
506633
  }
@@ -506667,6 +506677,7 @@ var TurnWatchdogController = class {
506667
506677
  }
506668
506678
  recordToolCall(toolCallId, name, args) {
506669
506679
  if (!this.active) return;
506680
+ this.recordProgress();
506670
506681
  this.lastToolName = name;
506671
506682
  this.toolCalls.set(toolCallId, {
506672
506683
  name,
@@ -506678,9 +506689,7 @@ var TurnWatchdogController = class {
506678
506689
  const toolCall = this.toolCalls.get(toolCallId);
506679
506690
  this.toolCalls.delete(toolCallId);
506680
506691
  if (toolCall !== void 0) this.lastToolName = toolCall.name;
506681
- this.lastProgressAtMs = this.now();
506682
- this.idleReportedForProgressAtMs = void 0;
506683
- this.scheduleIdleCheck();
506692
+ this.recordProgress();
506684
506693
  if (!isError || toolCall === void 0) {
506685
506694
  this.lastFailedKey = void 0;
506686
506695
  this.failedRepetitions = 0;
@@ -506692,14 +506701,26 @@ var TurnWatchdogController = class {
506692
506701
  this.failedRepetitions = 1;
506693
506702
  }
506694
506703
  if (this.failedRepetitions < this.config.maxFailedRepetitions) return;
506695
- const snapshot = this.snapshot();
506704
+ const snapshot = {
506705
+ ...this.snapshot(),
506706
+ reason: "repeated_failures"
506707
+ };
506696
506708
  this.stop();
506697
506709
  this.onAbort(snapshot);
506698
506710
  }
506711
+ recordProgress() {
506712
+ if (!this.active) return;
506713
+ this.lastProgressAtMs = this.now();
506714
+ this.idleReportedForProgressAtMs = void 0;
506715
+ this.scheduleIdleCheck();
506716
+ }
506699
506717
  scheduleIdleCheck() {
506700
506718
  if (!this.active) return;
506701
506719
  if (this.timer !== void 0) this.clearTimer(this.timer);
506702
- const remaining = Math.max(0, this.config.idleMs - (this.now() - this.lastProgressAtMs));
506720
+ const idleForMs = this.now() - this.lastProgressAtMs;
506721
+ const warningRemaining = this.idleReportedForProgressAtMs === this.lastProgressAtMs ? Number.POSITIVE_INFINITY : this.config.idleMs - idleForMs;
506722
+ const hardRemaining = this.config.hardIdleMs - idleForMs;
506723
+ const remaining = Math.max(0, Math.min(warningRemaining, hardRemaining));
506703
506724
  this.timer = this.setTimer(() => {
506704
506725
  this.timer = void 0;
506705
506726
  this.checkIdle();
@@ -506707,16 +506728,26 @@ var TurnWatchdogController = class {
506707
506728
  }
506708
506729
  checkIdle() {
506709
506730
  if (!this.active) return;
506710
- if (this.now() - this.lastProgressAtMs >= this.config.idleMs && this.idleReportedForProgressAtMs !== this.lastProgressAtMs) {
506731
+ const idleForMs = this.now() - this.lastProgressAtMs;
506732
+ if (idleForMs >= this.config.hardIdleMs) {
506733
+ const snapshot = {
506734
+ ...this.snapshot(),
506735
+ reason: "idle_timeout"
506736
+ };
506737
+ this.stop();
506738
+ this.onAbort(snapshot);
506739
+ return;
506740
+ }
506741
+ if (idleForMs >= this.config.idleMs && this.idleReportedForProgressAtMs !== this.lastProgressAtMs) {
506711
506742
  this.idleReportedForProgressAtMs = this.lastProgressAtMs;
506712
506743
  this.onIdle(this.snapshot());
506713
- return;
506714
506744
  }
506715
506745
  this.scheduleIdleCheck();
506716
506746
  }
506717
506747
  snapshot() {
506718
506748
  return {
506719
506749
  elapsedMinutes: Math.max(1, Math.floor((this.now() - this.startedAtMs) / 6e4)),
506750
+ idleMinutes: Math.max(1, Math.floor((this.now() - this.lastProgressAtMs) / 6e4)),
506720
506751
  lastToolName: this.lastToolName,
506721
506752
  failedRepetitions: this.failedRepetitions
506722
506753
  };
@@ -507035,6 +507066,7 @@ var SessionEventHandler = class {
507035
507066
  requestRunningUpdateAtSafeBoundary(this.host);
507036
507067
  }
507037
507068
  handleStepBegin(event) {
507069
+ this.turnWatchdog.recordProgress();
507038
507070
  this.host.commitQueuedSteerAtStepStart(event.turnId);
507039
507071
  this.host.streamingUI.flushNow();
507040
507072
  this.host.streamingUI.setStep(event.step);
@@ -507108,6 +507140,7 @@ var SessionEventHandler = class {
507108
507140
  handleThinkingDelta(event) {
507109
507141
  const { state, streamingUI } = this.host;
507110
507142
  if (event.delta.length === 0 && !streamingUI.hasThinkingDraft()) return;
507143
+ if (event.delta.length > 0) this.turnWatchdog.recordProgress();
507111
507144
  streamingUI.appendThinkingDelta(event.delta);
507112
507145
  this.host.patchLivePane({ mode: "idle" });
507113
507146
  if (state.appState.streamingPhase !== "thinking") this.host.setAppState({
@@ -507120,6 +507153,7 @@ var SessionEventHandler = class {
507120
507153
  const { state, streamingUI } = this.host;
507121
507154
  if (streamingUI.hasThinkingDraft()) streamingUI.flushThinkingToTranscript("idle");
507122
507155
  if (event.delta.trim().length > 0) {
507156
+ this.turnWatchdog.recordProgress();
507123
507157
  this.currentTurnHasAssistantText = true;
507124
507158
  this.pendingModelBlockedFallback = void 0;
507125
507159
  }
@@ -507180,6 +507214,7 @@ var SessionEventHandler = class {
507180
507214
  }
507181
507215
  handleToolCallDelta(event) {
507182
507216
  if (event.toolCallId.length === 0) return;
507217
+ if (event.argumentsPart.length > 0) this.turnWatchdog.recordProgress();
507183
507218
  const { state, streamingUI } = this.host;
507184
507219
  streamingUI.accumulateToolCallDelta(event.toolCallId, event.name, event.argumentsPart);
507185
507220
  const preview = streamingUI.getStreamingToolCallPreview(event.toolCallId);
@@ -507196,6 +507231,7 @@ var SessionEventHandler = class {
507196
507231
  streamingUI.scheduleFlush();
507197
507232
  }
507198
507233
  handleToolProgress(event) {
507234
+ this.turnWatchdog.recordProgress();
507199
507235
  if (event.update.kind === "custom" && event.update.customKind === MEDIA_PROGRESS_CUSTOM_KIND) {
507200
507236
  this.host.updateMediaActivity(event.toolCallId, event.update.customData);
507201
507237
  return;
@@ -507251,7 +507287,8 @@ var SessionEventHandler = class {
507251
507287
  this.host.sendChannelTurnStatus?.(message);
507252
507288
  }
507253
507289
  abortWatchdogLoop(snapshot) {
507254
- const message = uiText("sessionEvent.watchdog.aborted", {
507290
+ const message = uiText(snapshot.reason === "idle_timeout" ? "sessionEvent.watchdog.timeout" : "sessionEvent.watchdog.aborted", {
507291
+ minutes: snapshot.idleMinutes,
507255
507292
  tool: snapshot.lastToolName,
507256
507293
  repetitions: snapshot.failedRepetitions
507257
507294
  });
@@ -514970,7 +515007,8 @@ function requestRunningUpdateAtSafeBoundary(tui) {
514970
515007
  type: RUNNING_UPDATE_HANDOFF_MESSAGE,
514971
515008
  sessionId,
514972
515009
  version: tui.runningUpdatePreparedVersion,
514973
- mode: tui.runningUpdatePreparedMode
515010
+ mode: tui.runningUpdatePreparedMode,
515011
+ cwd: tui.state.appState.workDir
514974
515012
  }, (error) => {
514975
515013
  if (error || !isSafeRuntimeBoundary(boundaryState())) {
514976
515014
  tui.runningUpdateHandoffStarted = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.193",
3
+ "version": "9.1.195",
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": {