instar 1.3.647 → 1.3.648

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.
Files changed (67) hide show
  1. package/dist/commands/reflect.d.ts.map +1 -1
  2. package/dist/commands/reflect.js +7 -3
  3. package/dist/commands/reflect.js.map +1 -1
  4. package/dist/commands/server.d.ts.map +1 -1
  5. package/dist/commands/server.js +35 -0
  6. package/dist/commands/server.js.map +1 -1
  7. package/dist/commands/setup.d.ts.map +1 -1
  8. package/dist/commands/setup.js +17 -0
  9. package/dist/commands/setup.js.map +1 -1
  10. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  11. package/dist/config/ConfigDefaults.js +14 -0
  12. package/dist/config/ConfigDefaults.js.map +1 -1
  13. package/dist/core/CoherenceGate.d.ts.map +1 -1
  14. package/dist/core/CoherenceGate.js +24 -0
  15. package/dist/core/CoherenceGate.js.map +1 -1
  16. package/dist/core/CoherenceReviewer.d.ts +9 -0
  17. package/dist/core/CoherenceReviewer.d.ts.map +1 -1
  18. package/dist/core/CoherenceReviewer.js +18 -2
  19. package/dist/core/CoherenceReviewer.js.map +1 -1
  20. package/dist/core/InputGuard.d.ts.map +1 -1
  21. package/dist/core/InputGuard.js +17 -3
  22. package/dist/core/InputGuard.js.map +1 -1
  23. package/dist/core/MessageSentinel.d.ts.map +1 -1
  24. package/dist/core/MessageSentinel.js +19 -2
  25. package/dist/core/MessageSentinel.js.map +1 -1
  26. package/dist/core/MessagingToneGate.d.ts +8 -0
  27. package/dist/core/MessagingToneGate.d.ts.map +1 -1
  28. package/dist/core/MessagingToneGate.js +16 -1
  29. package/dist/core/MessagingToneGate.js.map +1 -1
  30. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  31. package/dist/core/PostUpdateMigrator.js +10 -0
  32. package/dist/core/PostUpdateMigrator.js.map +1 -1
  33. package/dist/core/SingleInstanceLock.d.ts +103 -0
  34. package/dist/core/SingleInstanceLock.d.ts.map +1 -0
  35. package/dist/core/SingleInstanceLock.js +300 -0
  36. package/dist/core/SingleInstanceLock.js.map +1 -0
  37. package/dist/core/SpawnCapIntelligenceProvider.d.ts +89 -0
  38. package/dist/core/SpawnCapIntelligenceProvider.d.ts.map +1 -0
  39. package/dist/core/SpawnCapIntelligenceProvider.js +164 -0
  40. package/dist/core/SpawnCapIntelligenceProvider.js.map +1 -0
  41. package/dist/core/hostSpawnSemaphore.d.ts +188 -0
  42. package/dist/core/hostSpawnSemaphore.d.ts.map +1 -0
  43. package/dist/core/hostSpawnSemaphore.js +471 -0
  44. package/dist/core/hostSpawnSemaphore.js.map +1 -0
  45. package/dist/core/intelligenceProviderFactory.d.ts +0 -12
  46. package/dist/core/intelligenceProviderFactory.d.ts.map +1 -1
  47. package/dist/core/intelligenceProviderFactory.js +27 -9
  48. package/dist/core/intelligenceProviderFactory.js.map +1 -1
  49. package/dist/core/types.d.ts +18 -0
  50. package/dist/core/types.d.ts.map +1 -1
  51. package/dist/core/types.js.map +1 -1
  52. package/dist/scaffold/templates.d.ts.map +1 -1
  53. package/dist/scaffold/templates.js +5 -0
  54. package/dist/scaffold/templates.js.map +1 -1
  55. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  56. package/dist/server/CapabilityIndex.js +9 -0
  57. package/dist/server/CapabilityIndex.js.map +1 -1
  58. package/dist/server/routes.d.ts.map +1 -1
  59. package/dist/server/routes.js +28 -0
  60. package/dist/server/routes.js.map +1 -1
  61. package/package.json +4 -2
  62. package/scripts/lint-no-direct-destructive.js +4 -0
  63. package/scripts/lint-no-unbounded-llm-spawn.js +137 -0
  64. package/src/data/builtin-manifest.json +63 -63
  65. package/src/scaffold/templates.ts +5 -0
  66. package/upgrades/1.3.648.md +99 -0
  67. package/upgrades/side-effects/forkbomb-prevention-simple.md +138 -0
@@ -0,0 +1,471 @@
1
+ /**
2
+ * hostSpawnSemaphore — P1 of the SIMPLE fork-bomb prevention design.
3
+ *
4
+ * Spec: docs/specs/forkbomb-prevention-simple.md (§P1, §P3, §D-CAP).
5
+ * Source postmortem: the-portal/docs/postmortems/2026-06-20-echo-instar-forkbomb-oom.md.
6
+ *
7
+ * THE PRIMARY CONTROL. A single host-local COUNTING SEMAPHORE that bounds how
8
+ * many LLM subprocesses ("claude -p" / "codex exec" / …) run AT ONCE across
9
+ * EVERY compliant Instar agent + server instance on this host. The 2026-06-20
10
+ * incident fork-bombed a 128GB macOS host into OOM twice (~230-289 concurrent
11
+ * `claude -p` ≈ 90-115GB) because `evaluate()` spawned one subprocess per call
12
+ * with ZERO concurrency control and CoherenceGate fans ~10 reviewers in
13
+ * parallel per message. This bounds that.
14
+ *
15
+ * MECHANISM — a holder-SET model (NOT decrement/increment counter math):
16
+ * A host-local file (`~/.instar/host-spawn-holders.json`, NOT a synced
17
+ * volume), guarded by an exclusive O_CREAT|O_EXCL lock (the in-tree
18
+ * ProjectRoundLock pattern). The cap is enforced by COUNTING LIVE holder
19
+ * records — never by mutating a shared integer:
20
+ * - acquire(id): under the lock, prune dead holders, and if
21
+ * liveHolders < cap append `{id, pid, hostname, heartbeat}` (atomic
22
+ * temp+rename) → true; else false.
23
+ * - release(id): under the lock, remove THIS id.
24
+ * Crash-safe by construction: a double-release is a no-op (id already gone),
25
+ * a pid-reuse can't steal a slot (unique id, not pid), a partial write is
26
+ * discarded (temp+rename), a crashed holder is reclaimed by prune-dead
27
+ * (pid not alive AND heartbeat stale, on THIS host only).
28
+ *
29
+ * HOST-LOCAL-LOCK CONTRACT (mirrors ResumeQueue.ts, the 2026-06-15 lesson):
30
+ * - A FOREIGN-hostname holder is NEVER pruned/reclaimed (refuse-loud — a
31
+ * pid check is meaningless cross-host on a shared volume).
32
+ * - A `df -P` host-local-disk confirmation gates reclaim (fail-closed: if we
33
+ * cannot positively confirm the holders file is on a local disk, we NEVER
34
+ * prune a holder — we only ever decline to reclaim, never decline to bound).
35
+ *
36
+ * BOUNDED INGRESS (P3) lives in the wrapper (SpawnCapIntelligenceProvider) via
37
+ * poll-retry against `acquire()` — this module is the pure counting primitive.
38
+ */
39
+ import fs from 'node:fs';
40
+ import os from 'node:os';
41
+ import path from 'node:path';
42
+ import { execFileSync } from 'node:child_process';
43
+ import { SafeFsExecutor } from './SafeFsExecutor.js';
44
+ import { isAlive } from './ProjectRoundLock.js';
45
+ /** Heartbeat staleness window — a holder is reclaim-eligible (pid dead OR, on a
46
+ * provably host-local disk, heartbeat older than this) only past it. Kept long
47
+ * (a slow `claude -p` cold-start + run can legitimately exceed a minute), and
48
+ * pid-liveness is the PRIMARY signal — heartbeat staleness is secondary and
49
+ * only consulted when the pid is also gone. */
50
+ export const HOLDER_STALE_MS = 5 * 60_000;
51
+ /**
52
+ * FD1 (from ResumeQueue) — is `p` on a HOST-LOCAL filesystem? FAIL-CLOSED:
53
+ * anything we cannot positively confirm as local returns false, so a holder on
54
+ * a genuine shared/network volume is NEVER reclaimed (the two-hosts-one-volume
55
+ * corruption the host-lock invariant protects against). `df -P` device-column
56
+ * classification — re-implemented here (not imported from monitoring/) so
57
+ * core/ never depends on monitoring/.
58
+ */
59
+ export function isPathHostLocalDefault(p) {
60
+ let out;
61
+ try {
62
+ // lint-allow-sync-spawn: a bounded (3s) one-shot host-FS classification, run
63
+ // ONCE per process and then MEMOIZED (HostSpawnSemaphore._fsLocalCache) — it
64
+ // never runs on the hot acquire() fan-out path. Mirrors ResumeQueue's
65
+ // isStateDirHostLocalDefault host-lock contract; a path's FS type can't
66
+ // change at runtime, so there is nothing to re-probe.
67
+ out = execFileSync('df', ['-P', p], { timeout: 3000, encoding: 'utf-8' });
68
+ }
69
+ catch {
70
+ // @silent-fallback-ok: df unavailable/failed ⇒ cannot confirm local ⇒
71
+ // fail-closed to NOT-local (the safe direction: never reclaim on doubt).
72
+ return false;
73
+ }
74
+ const lines = out.trim().split('\n');
75
+ if (lines.length < 2)
76
+ return false; // unparseable → fail-closed
77
+ const source = lines[1]?.trim().split(/\s+/)[0] ?? '';
78
+ return classifyDfSourceLocal(source);
79
+ }
80
+ /** Pure FD1 classifier over the `df -P` device-source column. FAIL-CLOSED. */
81
+ export function classifyDfSourceLocal(source) {
82
+ if (!source)
83
+ return false;
84
+ if (source.startsWith('//'))
85
+ return false; // SMB/CIFS //host/share → network
86
+ if (/^[^/][^:]*:/.test(source))
87
+ return false; // NFS host:/path → network
88
+ if (source.startsWith('/dev/'))
89
+ return true; // a real block device → local
90
+ return false; // map/tmpfs/anything unrecognized → fail-closed
91
+ }
92
+ /**
93
+ * Resolve the concurrent-spawn cap. Precedence (D-CAP):
94
+ * INSTAR_HOST_SPAWN_MAX env > config intelligence.spawnCap.maxConcurrent > 8.
95
+ * A safety FLOOR — read with a plain `??` default, NEVER resolveDevAgentGate
96
+ * (it is ON by default, ships never-dark). A non-positive / non-finite value
97
+ * is ignored (falls through to the next source) so a typo can't disable the cap.
98
+ */
99
+ export function resolveSpawnCap(configCap, env = process.env) {
100
+ const fromEnv = env['INSTAR_HOST_SPAWN_MAX'];
101
+ if (fromEnv !== undefined) {
102
+ const n = Number(fromEnv);
103
+ if (Number.isFinite(n) && n > 0)
104
+ return Math.floor(n);
105
+ }
106
+ if (typeof configCap === 'number' && Number.isFinite(configCap) && configCap > 0) {
107
+ return Math.floor(configCap);
108
+ }
109
+ return 8;
110
+ }
111
+ /**
112
+ * Resolve the bounded-acquire poll budget in ms (P3). Precedence (D-CAP):
113
+ * INSTAR_SPAWN_ACQUIRE_MS env > config intelligence.spawnCap.acquireMs > 5000.
114
+ * Read with a plain `??` default (safety floor, never resolveDevAgentGate).
115
+ */
116
+ export function resolveSpawnAcquireMs(configMs, env = process.env) {
117
+ const fromEnv = env['INSTAR_SPAWN_ACQUIRE_MS'];
118
+ if (fromEnv !== undefined) {
119
+ const n = Number(fromEnv);
120
+ if (Number.isFinite(n) && n >= 0)
121
+ return Math.floor(n);
122
+ }
123
+ if (typeof configMs === 'number' && Number.isFinite(configMs) && configMs >= 0) {
124
+ return Math.floor(configMs);
125
+ }
126
+ return 5000;
127
+ }
128
+ /**
129
+ * Resolve the concurrent-pollers ceiling (P3). Precedence (D-CAP):
130
+ * INSTAR_SPAWN_WAITERS_MAX env > config intelligence.spawnCap.waitersMax > 64.
131
+ */
132
+ export function resolveSpawnWaitersMax(configMax, env = process.env) {
133
+ const fromEnv = env['INSTAR_SPAWN_WAITERS_MAX'];
134
+ if (fromEnv !== undefined) {
135
+ const n = Number(fromEnv);
136
+ if (Number.isFinite(n) && n > 0)
137
+ return Math.floor(n);
138
+ }
139
+ if (typeof configMax === 'number' && Number.isFinite(configMax) && configMax > 0) {
140
+ return Math.floor(configMax);
141
+ }
142
+ return 64;
143
+ }
144
+ /**
145
+ * Host-wide counting semaphore over LLM-subprocess spawns. One instance per
146
+ * process is fine — the cross-process coordination is the file + flock, not
147
+ * the object. Stateless beyond the file.
148
+ */
149
+ export class HostSpawnSemaphore {
150
+ holdersPath;
151
+ cap;
152
+ now;
153
+ host;
154
+ pidAlive;
155
+ isPathHostLocal;
156
+ genId;
157
+ /** Memoized host-local determination (a fixed path's FS type can't change at
158
+ * runtime; the `df -P` probe is expensive + synchronous, so it runs ONCE per
159
+ * instance — never per acquire() on the hot fan-out path). */
160
+ _fsLocalCache;
161
+ constructor(deps = {}) {
162
+ this.holdersPath = deps.holdersPath ?? defaultHoldersPath();
163
+ this.cap = deps.cap ?? resolveSpawnCap();
164
+ this.now = deps.now ?? (() => Date.now());
165
+ this.host = (deps.hostname ?? (() => os.hostname()))();
166
+ this.pidAlive = deps.pidAlive ?? isAlive;
167
+ this.isPathHostLocal = deps.isPathHostLocal ?? isPathHostLocalDefault;
168
+ this.genId =
169
+ deps.genId ??
170
+ (() => `${this.host}:${process.pid}:${this.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`);
171
+ }
172
+ getCap() {
173
+ return this.cap;
174
+ }
175
+ /**
176
+ * Try to take a slot under id `id`. Returns true if a slot was appended
177
+ * (liveHolders < cap after pruning), false if the host is at the cap.
178
+ * Holds the exclusive flock for the whole read-prune-decide-write window.
179
+ */
180
+ acquire(id) {
181
+ return this.withLock(() => {
182
+ const file = this.readHolders();
183
+ const live = this.pruneDead(file.holders);
184
+ if (live.length >= this.cap) {
185
+ // At the cap — write back the pruned set (cheap GC) but take no slot.
186
+ this.writeHolders({ version: 1, holders: live });
187
+ return false;
188
+ }
189
+ // Crash-safe: an id already present (a retry that already landed) is a
190
+ // no-op-append, not a duplicate slot.
191
+ if (!live.some((h) => h.id === id)) {
192
+ live.push({ id, pid: process.pid, hostname: this.host, heartbeat: this.now() });
193
+ }
194
+ this.writeHolders({ version: 1, holders: live });
195
+ return true;
196
+ }, /* fallbackOnLockFail */ false);
197
+ }
198
+ /** Release the slot held under `id`. A double-release / unknown id is a no-op. */
199
+ release(id) {
200
+ this.withLock(() => {
201
+ const file = this.readHolders();
202
+ const remaining = file.holders.filter((h) => h.id !== id);
203
+ // Opportunistically GC dead holders on release too (cheap, keeps the file small).
204
+ const live = this.pruneDead(remaining);
205
+ this.writeHolders({ version: 1, holders: live });
206
+ return undefined;
207
+ }, /* fallbackOnLockFail */ undefined);
208
+ }
209
+ /** Refresh a long-held slot's heartbeat so a slow legit spawn is never reclaimed. */
210
+ heartbeat(id) {
211
+ this.withLock(() => {
212
+ const file = this.readHolders();
213
+ let changed = false;
214
+ for (const h of file.holders) {
215
+ if (h.id === id) {
216
+ h.heartbeat = this.now();
217
+ changed = true;
218
+ }
219
+ }
220
+ if (changed)
221
+ this.writeHolders(file);
222
+ return undefined;
223
+ }, /* fallbackOnLockFail */ undefined);
224
+ }
225
+ /** Read-only status (count of live holders after a prune). Best-effort: never throws. */
226
+ status() {
227
+ let live = [];
228
+ try {
229
+ live = this.withLock(() => {
230
+ const file = this.readHolders();
231
+ const pruned = this.pruneDead(file.holders);
232
+ this.writeHolders({ version: 1, holders: pruned });
233
+ return pruned;
234
+ }, /* fallbackOnLockFail */ this.readHolders().holders);
235
+ }
236
+ catch {
237
+ // @silent-fallback-ok: status is observability — a read error reports
238
+ // zero holders rather than throwing into the /spawn-limiter route.
239
+ live = [];
240
+ }
241
+ const localHolders = live.filter((h) => h.hostname === this.host).length;
242
+ return {
243
+ cap: this.cap,
244
+ liveHolders: live.length,
245
+ localHolders,
246
+ foreignHolders: live.length - localHolders,
247
+ holdersPath: this.holdersPath,
248
+ };
249
+ }
250
+ // ── Internals ─────────────────────────────────────────────────────
251
+ /**
252
+ * Prune dead holders. A holder is reclaimable ONLY if:
253
+ * - it is on THIS host (a foreign-hostname holder is NEVER reclaimed —
254
+ * refuse-loud — the cross-host shared-volume hazard), AND
255
+ * - its pid is no longer alive (PRIMARY signal), AND
256
+ * - its heartbeat is stale past HOLDER_STALE_MS (SECONDARY signal — a slow
257
+ * spawn whose pid is alive is NEVER reclaimed regardless of heartbeat).
258
+ * AND a `df -P` host-local confirmation gates ALL reclaim (fail-closed: if we
259
+ * cannot confirm the holders file is on a local disk, we keep ALL holders —
260
+ * over-counting is the safe direction for a cap; it never under-bounds).
261
+ */
262
+ pruneDead(holders) {
263
+ if (this._fsLocalCache === undefined) {
264
+ this._fsLocalCache = this.isPathHostLocal(path.dirname(this.holdersPath));
265
+ }
266
+ const fsLocal = this._fsLocalCache;
267
+ if (!fsLocal) {
268
+ // Cannot confirm host-local → reclaim NOTHING (fail-closed). Still return
269
+ // the well-formed set so the file stays clean.
270
+ return holders.filter((h) => isWellFormedHolder(h));
271
+ }
272
+ const nowMs = this.now();
273
+ return holders.filter((h) => {
274
+ if (!isWellFormedHolder(h))
275
+ return false; // drop garbage rows
276
+ if (h.hostname !== this.host)
277
+ return true; // foreign — NEVER reclaimed (keep)
278
+ const pidDead = !this.pidAlive(h.pid);
279
+ const heartbeatStale = nowMs - h.heartbeat >= HOLDER_STALE_MS;
280
+ // Reclaim only when BOTH dead AND stale (pid primary, heartbeat secondary).
281
+ const reclaim = pidDead && heartbeatStale;
282
+ return !reclaim;
283
+ });
284
+ }
285
+ readHolders() {
286
+ try {
287
+ const raw = fs.readFileSync(this.holdersPath, 'utf-8');
288
+ const obj = JSON.parse(raw);
289
+ if (!obj || typeof obj !== 'object' || !Array.isArray(obj.holders)) {
290
+ return { version: 1, holders: [] };
291
+ }
292
+ return { version: 1, holders: obj.holders.filter(isWellFormedHolder) };
293
+ }
294
+ catch {
295
+ // @silent-fallback-ok: a missing/corrupt holders file is an EMPTY set —
296
+ // the safe direction is to bound from zero, never to crash the spawn path.
297
+ return { version: 1, holders: [] };
298
+ }
299
+ }
300
+ writeHolders(file) {
301
+ this.ensureDir();
302
+ const body = JSON.stringify(file);
303
+ const tmp = `${this.holdersPath}.tmp.${process.pid}.${Math.random().toString(36).slice(2, 8)}`;
304
+ try {
305
+ const fd = fs.openSync(tmp, 'wx', 0o600);
306
+ fs.writeSync(fd, body);
307
+ fs.closeSync(fd);
308
+ fs.renameSync(tmp, this.holdersPath); // atomic on the same filesystem
309
+ }
310
+ catch (err) {
311
+ try {
312
+ SafeFsExecutor.safeUnlinkSync(tmp, { operation: 'HostSpawnSemaphore.writeHolders:cleanup-tmp' });
313
+ }
314
+ catch {
315
+ /* ignore */
316
+ }
317
+ throw err;
318
+ }
319
+ }
320
+ /**
321
+ * Run `fn` while holding the exclusive O_CREAT|O_EXCL lock on `<holdersPath>.lock`.
322
+ * On a lock-contention failure (another process holds it), returns
323
+ * `fallbackOnLockFail` — for `acquire`, fallback=false is the SAFE direction
324
+ * (refuse the slot under contention rather than over-grant past the cap).
325
+ * The lock is short-held (a file read + JSON parse + write) so contention is
326
+ * brief; a crashed lock-holder's stale lock is reclaimed by pid-death.
327
+ */
328
+ withLock(fn, fallbackOnLockFail) {
329
+ this.ensureDir();
330
+ const lockPath = `${this.holdersPath}.lock`;
331
+ const deadline = this.now() + 2000; // bounded spin — never block the event loop forever
332
+ // Spin-acquire the lock with a tiny busy-wait; the critical section is sub-ms.
333
+ for (;;) {
334
+ let fd = null;
335
+ try {
336
+ fd = fs.openSync(lockPath, 'wx', 0o600); // O_CREAT|O_EXCL
337
+ fs.writeSync(fd, JSON.stringify({ pid: process.pid, at: this.now() }));
338
+ }
339
+ catch (err) {
340
+ const e = err;
341
+ if (e.code === 'EEXIST') {
342
+ // Lock held. Reclaim it if its holder's pid is dead (crash-safe).
343
+ if (this.reclaimStaleLock(lockPath))
344
+ continue;
345
+ if (this.now() >= deadline)
346
+ return fallbackOnLockFail; // give up safely
347
+ busyWaitTiny();
348
+ continue;
349
+ }
350
+ // Any other open error — fail to the safe fallback rather than throw.
351
+ if (fd !== null) {
352
+ try {
353
+ fs.closeSync(fd);
354
+ }
355
+ catch { /* ignore */ }
356
+ }
357
+ return fallbackOnLockFail;
358
+ }
359
+ try {
360
+ return fn();
361
+ }
362
+ finally {
363
+ try {
364
+ fs.closeSync(fd);
365
+ }
366
+ catch { /* @silent-fallback-ok: closing an already-closed/invalid fd on lock-release is benign */ }
367
+ try {
368
+ SafeFsExecutor.safeUnlinkSync(lockPath, { operation: 'HostSpawnSemaphore.withLock:release' });
369
+ }
370
+ catch {
371
+ /* @silent-fallback-ok: a missing lock on release is fine — release is idempotent */
372
+ }
373
+ }
374
+ }
375
+ }
376
+ /** Remove the lock file if its recorded holder pid is dead (crash recovery). */
377
+ reclaimStaleLock(lockPath) {
378
+ try {
379
+ const raw = fs.readFileSync(lockPath, 'utf-8');
380
+ const obj = JSON.parse(raw);
381
+ const pid = typeof obj?.pid === 'number' ? obj.pid : null;
382
+ if (pid !== null && this.pidAlive(pid))
383
+ return false; // live holder — wait
384
+ // Dead (or unparseable) lock holder — reclaim.
385
+ SafeFsExecutor.safeUnlinkSync(lockPath, { operation: 'HostSpawnSemaphore.reclaimStaleLock' });
386
+ return true;
387
+ }
388
+ catch {
389
+ // @silent-fallback-ok: a read/parse failure on the lock means we couldn't
390
+ // confirm a live holder; treat as reclaimable so a corrupt lock can't wedge
391
+ // the cap permanently. The O_EXCL re-create still races safely.
392
+ try {
393
+ SafeFsExecutor.safeUnlinkSync(lockPath, { operation: 'HostSpawnSemaphore.reclaimStaleLock:corrupt' });
394
+ return true;
395
+ }
396
+ catch {
397
+ // @silent-fallback-ok: couldn't remove the corrupt lock (a race with
398
+ // another reclaimer) — report not-reclaimed; the caller waits + retries.
399
+ return false;
400
+ }
401
+ }
402
+ }
403
+ ensureDir() {
404
+ const dir = path.dirname(this.holdersPath);
405
+ if (!fs.existsSync(dir))
406
+ fs.mkdirSync(dir, { recursive: true });
407
+ }
408
+ }
409
+ /** Default holders file path: `~/.instar/host-spawn-holders.json` (host-local). */
410
+ export function defaultHoldersPath() {
411
+ return path.join(os.homedir(), '.instar', 'host-spawn-holders.json');
412
+ }
413
+ function isWellFormedHolder(h) {
414
+ if (!h || typeof h !== 'object')
415
+ return false;
416
+ const r = h;
417
+ return (typeof r.id === 'string' &&
418
+ typeof r.pid === 'number' &&
419
+ typeof r.hostname === 'string' &&
420
+ typeof r.heartbeat === 'number');
421
+ }
422
+ /** A sub-millisecond busy-wait so the spin-lock doesn't peg a core. */
423
+ function busyWaitTiny() {
424
+ const end = Date.now() + 1;
425
+ while (Date.now() < end) {
426
+ /* spin ~1ms */
427
+ }
428
+ }
429
+ // ── Process-wide singleton ────────────────────────────────────────────
430
+ //
431
+ // The cross-process coordination is the FILE; this singleton just avoids
432
+ // re-resolving deps per call within one process. Config-cap is injected once
433
+ // at boot via `configureHostSpawnSemaphore`; absent that, the env/8 default holds.
434
+ let _singleton = null;
435
+ let _configuredCap;
436
+ let _configuredAcquireMs;
437
+ let _configuredWaitersMax;
438
+ /**
439
+ * Inject the config-resolved spawn-cap knobs once at server boot (server.ts).
440
+ * Idempotent: re-resolves the singleton so a later getter sees the configured
441
+ * cap. The env vars still win inside the `resolve*` helpers.
442
+ */
443
+ export function configureHostSpawnSemaphore(cfg) {
444
+ _configuredCap = cfg?.maxConcurrent;
445
+ _configuredAcquireMs = cfg?.acquireMs;
446
+ _configuredWaitersMax = cfg?.waitersMax;
447
+ _singleton = new HostSpawnSemaphore({ cap: resolveSpawnCap(_configuredCap) });
448
+ }
449
+ /** The process-wide semaphore. Lazily constructed with env/config/8 cap. */
450
+ export function getHostSpawnSemaphore() {
451
+ if (!_singleton) {
452
+ _singleton = new HostSpawnSemaphore({ cap: resolveSpawnCap(_configuredCap) });
453
+ }
454
+ return _singleton;
455
+ }
456
+ /** Config-aware acquire-budget resolver (env > injected config > 5000). */
457
+ export function configuredSpawnAcquireMs() {
458
+ return resolveSpawnAcquireMs(_configuredAcquireMs);
459
+ }
460
+ /** Config-aware waiters-ceiling resolver (env > injected config > 64). */
461
+ export function configuredSpawnWaitersMax() {
462
+ return resolveSpawnWaitersMax(_configuredWaitersMax);
463
+ }
464
+ /** Test seam — reset the singleton + injected config. */
465
+ export function _resetHostSpawnSemaphoreForTest() {
466
+ _singleton = null;
467
+ _configuredCap = undefined;
468
+ _configuredAcquireMs = undefined;
469
+ _configuredWaitersMax = undefined;
470
+ }
471
+ //# sourceMappingURL=hostSpawnSemaphore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostSpawnSemaphore.js","sourceRoot":"","sources":["../../src/core/hostSpawnSemaphore.ts"],"names":[],"mappings":"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"}
@@ -100,18 +100,6 @@ export interface BuildIntelligenceProviderOptions {
100
100
  */
101
101
  resolveExecJson?: () => boolean;
102
102
  }
103
- /**
104
- * Build an IntelligenceProvider for the given framework. Returns null
105
- * if the required binary can't be located. The caller decides what
106
- * happens next — fatal error, fall-back to a different framework, or
107
- * disable LLM-backed paths entirely.
108
- *
109
- * @example
110
- * const intel = buildIntelligenceProvider({ framework: 'codex-cli' });
111
- * if (intel === null) {
112
- * console.warn('Codex CLI not found — LLM-backed paths disabled.');
113
- * }
114
- */
115
103
  export declare function buildIntelligenceProvider(options?: BuildIntelligenceProviderOptions): IntelligenceProvider | null;
116
104
  /**
117
105
  * Convenience: read the framework selection from the environment.
@@ -1 +1 @@
1
- {"version":3,"file":"intelligenceProviderFactory.d.ts","sourceRoot":"","sources":["../../src/core/intelligenceProviderFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAOvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC7B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6DAA6D,CAAC;AAE1G;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;AAE1F,MAAM,WAAW,gCAAgC;IAC/C;;;;OAIG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE;QACjB,IAAI,EAAE,oBAAoB,CAAC;QAC3B,4EAA4E;QAC5E,WAAW,EAAE,eAAe,CAAC;QAC7B,gEAAgE;QAChE,aAAa,EAAE,MAAM,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;QAC5D,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;QAChD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,IAAI,CAAC;KACrD,CAAC;IACF;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,oBAAoB,GAAG,IAAI,CA0G7B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,qBAAqB,GAAG,IAAI,CAOnG"}
1
+ {"version":3,"file":"intelligenceProviderFactory.d.ts","sourceRoot":"","sources":["../../src/core/intelligenceProviderFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAQvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC7B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6DAA6D,CAAC;AAE1G;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;AAE1F,MAAM,WAAW,gCAAgC;IAC/C;;;;OAIG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE;QACjB,IAAI,EAAE,oBAAoB,CAAC;QAC3B,4EAA4E;QAC5E,WAAW,EAAE,eAAe,CAAC;QAC7B,gEAAgE;QAChE,aAAa,EAAE,MAAM,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;QAC5D,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;QAChD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,IAAI,CAAC;KACrD,CAAC;IACF;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;CACjC;AA4BD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,oBAAoB,GAAG,IAAI,CAiH7B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,qBAAqB,GAAG,IAAI,CAOnG"}
@@ -19,6 +19,7 @@ import { CodexCliIntelligenceProvider } from './CodexCliIntelligenceProvider.js'
19
19
  import { GeminiCliIntelligenceProvider } from './GeminiCliIntelligenceProvider.js';
20
20
  import { PiCliIntelligenceProvider } from './PiCliIntelligenceProvider.js';
21
21
  import { wrapIntelligenceWithCircuitBreaker } from './CircuitBreakingIntelligenceProvider.js';
22
+ import { wrapIntelligenceWithSpawnCap } from './SpawnCapIntelligenceProvider.js';
22
23
  import { AnthropicSubscriptionRouter, } from './AnthropicSubscriptionRouter.js';
23
24
  import { InteractivePoolIntelligenceProvider } from './InteractivePoolIntelligenceProvider.js';
24
25
  /**
@@ -33,12 +34,29 @@ import { InteractivePoolIntelligenceProvider } from './InteractivePoolIntelligen
33
34
  * console.warn('Codex CLI not found — LLM-backed paths disabled.');
34
35
  * }
35
36
  */
37
+ /**
38
+ * Apply the two universal funnel wrappers in the correct order: the spawn cap
39
+ * INSIDE the circuit breaker (so a breaker-open shed never holds a spawn slot),
40
+ * then the breaker OUTSIDE. Both are per-`evaluate()` wrappers, so the acquire
41
+ * is per-call — load-bearing for CoherenceGate's shared-instance fan-out.
42
+ * No-ops on null (passes a possibly-null factory result through unchanged).
43
+ */
44
+ function wrapForFunnel(provider, breaker) {
45
+ return wrapIntelligenceWithCircuitBreaker(wrapIntelligenceWithSpawnCap(provider), breaker);
46
+ }
36
47
  export function buildIntelligenceProvider(options = {}) {
37
48
  const framework = options.framework ?? 'claude-code';
38
- // Every provider the factory hands out is wrapped with the account-global
39
- // LLM circuit breaker (CircuitBreakingIntelligenceProvider). A closed breaker
40
- // is a transparent passthrough, so this is always safe; it only acts when the
41
- // provider reports a usage/rate limit. See LlmCircuitBreaker for the why.
49
+ // Every provider the factory hands out is wrapped with TWO universal funnels
50
+ // (Structure > Willpower every consumer inherits both from one place):
51
+ // 1. the host-wide SPAWN CAP (SpawnCapIntelligenceProvider) the SIMPLE
52
+ // fork-bomb prevention P1 chokepoint. Layered INSIDE the breaker so a
53
+ // breaker-open shed never even reaches the spawn-cap acquire (no slot is
54
+ // held for a call that won't spawn); the cap binds the ACTUAL spawn.
55
+ // 2. the account-global LLM circuit breaker (CircuitBreakingIntelligenceProvider).
56
+ // A closed breaker is a transparent passthrough; it only acts on a
57
+ // usage/rate limit. See LlmCircuitBreaker for the why.
58
+ // `wrapForFunnel` applies both in the correct order at EVERY return arm so a
59
+ // new framework case can't accidentally ship un-capped.
42
60
  switch (framework) {
43
61
  case 'claude-code': {
44
62
  const path = options.binaryPath ?? detectClaudePath();
@@ -63,15 +81,15 @@ export function buildIntelligenceProvider(options = {}) {
63
81
  ...(sp.onRoute ? { onRoute: sp.onRoute } : {}),
64
82
  ...(sp.onDegrade ? { onDegrade: sp.onDegrade } : {}),
65
83
  });
66
- return wrapIntelligenceWithCircuitBreaker(routed, options.breaker);
84
+ return wrapForFunnel(routed, options.breaker);
67
85
  }
68
- return wrapIntelligenceWithCircuitBreaker(headless, options.breaker);
86
+ return wrapForFunnel(headless, options.breaker);
69
87
  }
70
88
  case 'codex-cli': {
71
89
  const path = options.binaryPath ?? detectCodexPath();
72
90
  if (!path)
73
91
  return null;
74
- return wrapIntelligenceWithCircuitBreaker(new CodexCliIntelligenceProvider({
92
+ return wrapForFunnel(new CodexCliIntelligenceProvider({
75
93
  codexPath: path,
76
94
  ...(options.workingDirectory ? { workingDirectory: options.workingDirectory } : {}),
77
95
  ...(options.resolveExecJson ? { resolveExecJson: options.resolveExecJson } : {}),
@@ -81,7 +99,7 @@ export function buildIntelligenceProvider(options = {}) {
81
99
  const path = options.binaryPath ?? detectGeminiPath();
82
100
  if (!path)
83
101
  return null;
84
- return wrapIntelligenceWithCircuitBreaker(new GeminiCliIntelligenceProvider({
102
+ return wrapForFunnel(new GeminiCliIntelligenceProvider({
85
103
  geminiPath: path,
86
104
  ...(options.workingDirectory ? { workingDirectory: options.workingDirectory } : {}),
87
105
  ...(options.quotaStateFile ? { capacityPolicy: { quotaStateFile: options.quotaStateFile } } : {}),
@@ -104,7 +122,7 @@ export function buildIntelligenceProvider(options = {}) {
104
122
  return null;
105
123
  }
106
124
  try {
107
- return wrapIntelligenceWithCircuitBreaker(new PiCliIntelligenceProvider({
125
+ return wrapForFunnel(new PiCliIntelligenceProvider({
108
126
  piPath: path,
109
127
  model: options.piModel,
110
128
  ...(options.piAllowAnthropicProviders !== undefined
@@ -1 +1 @@
1
- {"version":3,"file":"intelligenceProviderFactory.js","sourceRoot":"","sources":["../../src/core/intelligenceProviderFactory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"intelligenceProviderFactory.js","sourceRoot":"","sources":["../../src/core/intelligenceProviderFactory.ts"],"names":[],"mappings":"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"}
@@ -3016,6 +3016,24 @@ export interface InstarConfig {
3016
3016
  * injected sentinel and the REPL would otherwise never be reaped. */
3017
3017
  maxReroutedLifetimeMinutes?: number;
3018
3018
  };
3019
+ /**
3020
+ * Fork-bomb prevention — host-wide concurrent-LLM-subprocess cap (the SIMPLE
3021
+ * design, docs/specs/forkbomb-prevention-simple.md §D-CAP). A SAFETY FLOOR:
3022
+ * read with a plain `?? default` (env > this config > the hardcoded default),
3023
+ * NEVER `resolveDevAgentGate` — it ships ON for every agent, never dark. The
3024
+ * cap bounds how many `claude -p`/`codex exec` subprocesses run AT ONCE
3025
+ * across every compliant Instar process on the host (the primary control for
3026
+ * the 2026-06-20 OOM fork-bomb). Env overrides: INSTAR_HOST_SPAWN_MAX,
3027
+ * INSTAR_SPAWN_ACQUIRE_MS, INSTAR_SPAWN_WAITERS_MAX.
3028
+ */
3029
+ spawnCap?: {
3030
+ /** Concurrent-spawn cap across the whole host. Default 8. */
3031
+ maxConcurrent?: number;
3032
+ /** Bounded-acquire poll budget (ms) before a saturated call sheds. Default 5000. */
3033
+ acquireMs?: number;
3034
+ /** Concurrent-poller ceiling (bounds the waiters, no queue-node heap). Default 64. */
3035
+ waitersMax?: number;
3036
+ };
3019
3037
  };
3020
3038
  /**
3021
3039
  * Agent-level set of frameworks this install actively uses. Drives