@yemi33/minions 0.1.2307 → 0.1.2308

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.
@@ -1114,8 +1114,11 @@ async function saveSettings() {
1114
1114
  // Settings UI presents the idle timeout in minutes for readability; the
1115
1115
  // engine stores ms (ENGINE_DEFAULTS.ccWorkerIdleTimeoutMs). Convert here;
1116
1116
  // the server clamps to [60000, 28800000] ms. undefined when blank so the
1117
- // server keeps the prior value rather than zeroing it.
1117
+ // server keeps the prior value rather than zeroing it. W-mr1qbj90000d30bd:
1118
+ // the "Never idle-reap CC workers" toggle wins and submits the explicit
1119
+ // `0` sentinel (bypasses the clamp server-side), ignoring the minutes field.
1118
1120
  ccWorkerIdleTimeoutMs: (function () {
1121
+ if (document.getElementById('set-ccNeverIdleReap')?.checked) return 0;
1119
1122
  var m = Number(document.getElementById('set-ccWorkerIdleTimeoutMs')?.value);
1120
1123
  return (Number.isFinite(m) && m > 0) ? Math.round(m * 60000) : undefined;
1121
1124
  })(),
package/dashboard.js CHANGED
@@ -258,8 +258,12 @@ function reloadConfig() {
258
258
  // Settings change takes effect without a dashboard restart (W-mr0qs0vw). The
259
259
  // pool stays dependency-free at load and relies on this push.
260
260
  try {
261
- const idleMs = Number(CONFIG.engine?.ccWorkerIdleTimeoutMs)
262
- || shared.ENGINE_DEFAULTS.ccWorkerIdleTimeoutMs;
261
+ // W-mr1qbj90000d30bd 0 is the explicit "never idle-reap" sentinel and
262
+ // must NOT fall through to the default via `||` (0 is falsy in JS).
263
+ const cfgIdle = CONFIG.engine?.ccWorkerIdleTimeoutMs;
264
+ const idleMs = Number(cfgIdle) === 0
265
+ ? 0
266
+ : (Number(cfgIdle) || shared.ENGINE_DEFAULTS.ccWorkerIdleTimeoutMs);
263
267
  ccWorkerPool.setIdleTimeoutMs(idleMs);
264
268
  } catch { /* invalid value or pool unavailable — keep prior window */ }
265
269
  }
@@ -11063,12 +11067,6 @@ What would you like to discuss or change? When you're happy, say "approve" and I
11063
11067
  // larger models); max 1h (matches CC_CALL_TIMEOUT_MS so the watchdog
11064
11068
  // never outlives the outer abort).
11065
11069
  ccTurnTimeoutMs: [10000, 3600000],
11066
- // W-mr0qs0vw — ACP worker idle-reaper window. 1min floor (anything
11067
- // shorter would reap a warm worker between a user's own messages and
11068
- // thrash cold-spawns); 8h ceiling (a config typo shouldn't pin a warm
11069
- // copilot process in memory indefinitely). Stored in ms; the Settings
11070
- // UI presents minutes and converts.
11071
- ccWorkerIdleTimeoutMs: [60000, 28800000],
11072
11070
  // W-mq9acoo800177bcb — bounded-concurrency for pre-dispatch validator.
11073
11071
  // 1 floor (sequential fallback) and 20 ceiling (above this the LLM
11074
11072
  // provider's per-second rate limits dominate; throughput gains taper).
@@ -11084,6 +11082,29 @@ What would you like to discuss or change? When you're happy, say "approve" and I
11084
11082
  _setEngineConfig(key, val);
11085
11083
  }
11086
11084
  }
11085
+ // W-mr0qs0vw / W-mr1qbj90000d30bd — ACP worker idle-reaper window.
11086
+ // 1min floor (anything shorter would reap a warm worker between a
11087
+ // user's own messages and thrash cold-spawns); 8h ceiling (a config
11088
+ // typo shouldn't pin a warm copilot process in memory indefinitely).
11089
+ // Handled OUTSIDE the generic numericFields loop because `0` is a
11090
+ // valid, deliberate sentinel meaning "never idle-reap" (only closeTab
11091
+ // kills the warm worker) — it must bypass the [60000, 28800000]
11092
+ // clamp for that one value instead of being clamped up to 60000 or
11093
+ // falling back to the default via `Number(e[key]) || D[key]`
11094
+ // (0 is falsy in JS). Stored in ms; the Settings UI presents minutes.
11095
+ if (e.ccWorkerIdleTimeoutMs !== undefined) {
11096
+ const rawIdle = Number(e.ccWorkerIdleTimeoutMs);
11097
+ if (rawIdle === 0) {
11098
+ _setEngineConfig('ccWorkerIdleTimeoutMs', 0);
11099
+ } else {
11100
+ let val = rawIdle || D.ccWorkerIdleTimeoutMs;
11101
+ const raw = val;
11102
+ val = Math.max(60000, val);
11103
+ val = Math.min(28800000, val);
11104
+ if (val !== raw) _clamped.push(`ccWorkerIdleTimeoutMs: ${raw} → ${val} (range: 60000–28800000)`);
11105
+ _setEngineConfig('ccWorkerIdleTimeoutMs', val);
11106
+ }
11107
+ }
11087
11108
  // String fields
11088
11109
  if (e.worktreeRoot !== undefined) _setEngineConfig('worktreeRoot', String(e.worktreeRoot || D.worktreeRoot));
11089
11110
  // W-mpejf0fq000e84d6: operator login override. Empty string clears
@@ -38,7 +38,10 @@
38
38
  * - closeTab → `session/cancel` if inflight, then kill proc
39
39
  * - Idle > timeout → kill proc (reaper sweep every 60 s; window is
40
40
  * ENGINE_DEFAULTS.ccWorkerIdleTimeoutMs, default
41
- * 30 min, pushed in via setIdleTimeoutMs())
41
+ * 30 min, pushed in via setIdleTimeoutMs()). A
42
+ * timeout of 0 is the "never idle-reap" sentinel
43
+ * (W-mr1qbj90000d30bd) — the warm worker is only
44
+ * ever killed by an explicit closeTab.
42
45
  *
43
46
  * Auth precheck: when `copilot --acp` exits before the initialize handshake
44
47
  * completes, surface
@@ -171,11 +174,18 @@ let _reaperTimer = null;
171
174
  const _reapedTabs = new Map();
172
175
 
173
176
  // Update the idle-reaper window at runtime. Called by dashboard.js on every
174
- // reloadConfig() with `engine.ccWorkerIdleTimeoutMs`. Ignores non-finite /
175
- // non-positive values so a config typo can't disable reaping or set a negative
176
- // window; the floor of 1000ms keeps a misconfigured tiny value from thrashing.
177
+ // reloadConfig() with `engine.ccWorkerIdleTimeoutMs`. `0` is the explicit
178
+ // "never idle-reap" sentinel (W-mr1qbj90000d30bd) it sets the window to
179
+ // Infinity so `_reapIdleTabs()` never kills a warm worker for inactivity;
180
+ // only an explicit `closeTab` does. Ignores non-finite / negative values so a
181
+ // config typo can't disable reaping or set a negative window; the floor of
182
+ // 1000ms keeps a misconfigured tiny positive value from thrashing.
177
183
  function setIdleTimeoutMs(ms) {
178
184
  const n = Number(ms);
185
+ if (n === 0) {
186
+ _idleReaperMs = Infinity;
187
+ return _idleReaperMs;
188
+ }
179
189
  if (!Number.isFinite(n) || n <= 0) return _idleReaperMs;
180
190
  _idleReaperMs = Math.max(1000, n);
181
191
  return _idleReaperMs;
@@ -876,6 +886,8 @@ function _ensureReaper() {
876
886
  }
877
887
 
878
888
  function _reapIdleTabs() {
889
+ // W-mr1qbj90000d30bd — idle-reap disabled entirely (sentinel: 0 → Infinity).
890
+ if (_idleReaperMs === Infinity) return;
879
891
  const now = _internals.now();
880
892
  for (const [tabId, worker] of [..._tabs]) {
881
893
  if (worker.inflight) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2307",
3
+ "version": "0.1.2308",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"