@cleocode/core 2026.6.18 → 2026.7.1
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.
- package/dist/docs/export-document.js +1173 -334
- package/dist/docs/export-document.js.map +3 -3
- package/dist/lifecycle/effective-stage.js +3 -3
- package/dist/lifecycle/rollup.js +3 -3
- package/dist/llm/plugin-facade.js +1218 -383
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/memory/brain-writer-handlers.d.ts.map +1 -1
- package/dist/memory/brain-writer-handlers.js +39 -0
- package/dist/memory/brain-writer-handlers.js.map +1 -1
- package/dist/orchestrate/admission.d.ts +65 -0
- package/dist/orchestrate/admission.d.ts.map +1 -0
- package/dist/orchestrate/admission.js +54 -0
- package/dist/orchestrate/admission.js.map +1 -0
- package/dist/orchestrate/lifecycle-ops.d.ts.map +1 -1
- package/dist/orchestrate/lifecycle-ops.js +10 -0
- package/dist/orchestrate/lifecycle-ops.js.map +1 -1
- package/dist/orchestrate/query-ops.d.ts.map +1 -1
- package/dist/orchestrate/query-ops.js +37 -8
- package/dist/orchestrate/query-ops.js.map +1 -1
- package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
- package/dist/orchestrate/spawn-ops.js +24 -0
- package/dist/orchestrate/spawn-ops.js.map +1 -1
- package/dist/release/pr-evidence.d.ts +14 -3
- package/dist/release/pr-evidence.d.ts.map +1 -1
- package/dist/release/pr-evidence.js +44 -4
- package/dist/release/pr-evidence.js.map +1 -1
- package/dist/resources/governor.d.ts +133 -0
- package/dist/resources/governor.d.ts.map +1 -0
- package/dist/resources/governor.js +377 -0
- package/dist/resources/governor.js.map +1 -0
- package/dist/resources/supervisor-admit.d.ts +42 -0
- package/dist/resources/supervisor-admit.d.ts.map +1 -0
- package/dist/resources/supervisor-admit.js +136 -0
- package/dist/resources/supervisor-admit.js.map +1 -0
- package/dist/selfimprove/fix-gen.js +1216 -381
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/sentient/tick.d.ts.map +1 -1
- package/dist/sentient/tick.js +29 -0
- package/dist/sentient/tick.js.map +1 -1
- package/dist/store/agent-registry-store.d.ts.map +1 -1
- package/dist/store/agent-registry-store.js +17 -2
- package/dist/store/agent-registry-store.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +35 -0
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +15 -0
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/tasks-schema.d.ts +2 -0
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.js +17 -0
- package/dist/store/tasks-schema.js.map +1 -1
- package/dist/store/validation-schemas.d.ts +98 -98
- package/dist/tasks/compute-task-view.js +2 -2
- package/dist/tasks/evidence.d.ts +19 -0
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +32 -2
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/tool-semaphore.d.ts +25 -0
- package/dist/tasks/tool-semaphore.d.ts.map +1 -1
- package/dist/tasks/tool-semaphore.js +68 -1
- package/dist/tasks/tool-semaphore.js.map +1 -1
- package/dist/tasks/update.d.ts +2 -0
- package/dist/tasks/update.d.ts.map +1 -1
- package/dist/tasks/update.js +1 -0
- package/dist/tasks/update.js.map +1 -1
- package/dist/validation/engine-ops.d.ts.map +1 -1
- package/dist/validation/engine-ops.js +33 -7
- package/dist/validation/engine-ops.js.map +1 -1
- package/package.json +12 -12
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResourceGovernor — Never-OOM class-based admission (T11999, Epic T11992).
|
|
3
|
+
*
|
|
4
|
+
* Admits resource-intensive work through priority classes whose slot budgets
|
|
5
|
+
* are computed at acquire time from host memory + memory-pressure (PSI). A
|
|
6
|
+
* denial returns a structured, retryable {@link ResourceDeferral} — never a
|
|
7
|
+
* silent drop, never a crash. Existing grants are NEVER revoked.
|
|
8
|
+
*
|
|
9
|
+
* Three modes (verbatim writer-lease shape, `writer-lease.ts` `resolveLeaseMode`):
|
|
10
|
+
* - `supervisor` — defer to the Rust `cleo-supervisor` `resource_admit` verb.
|
|
11
|
+
* Demotes to `local` (log-once) until the IPC client is wired — a
|
|
12
|
+
* dead/absent arbiter must never deadlock work.
|
|
13
|
+
* - `local` — DEFAULT, daemon-off. Per-class slot directories under
|
|
14
|
+
* `getCleoHome()/locks/resource-<class>/` arbitrated by `proper-lockfile`
|
|
15
|
+
* (crash-stale auto-release ⇒ genuinely cross-process without a daemon),
|
|
16
|
+
* plus a point-sample of the {@link ResourceMonitor} taken INSIDE acquire.
|
|
17
|
+
* Generalizes the tool-semaphore engine (`tool-semaphore.ts`).
|
|
18
|
+
* - `off` — pure pass-through.
|
|
19
|
+
*
|
|
20
|
+
* `interactive-cli` is NEVER gated; `full-build` is pinned to one machine-wide
|
|
21
|
+
* slot regardless of pressure.
|
|
22
|
+
*
|
|
23
|
+
* @task T11999
|
|
24
|
+
* @epic T11992
|
|
25
|
+
* @adr resource-governor-never-oom-architecture §3.4
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
|
28
|
+
import { availableParallelism, totalmem } from 'node:os';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import { DEFAULT_RESOURCE_RETRY_AFTER_MS, } from '@cleocode/contracts';
|
|
31
|
+
import lockfile from 'proper-lockfile';
|
|
32
|
+
import { getLogger } from '../logger.js';
|
|
33
|
+
import { getCleoHome } from '../paths.js';
|
|
34
|
+
import { ResourceMonitor } from './monitor.js';
|
|
35
|
+
import { resolveSupervisorSocketPath, sendResourceAdmit, sendResourceRelease, } from './supervisor-admit.js';
|
|
36
|
+
let _log = null;
|
|
37
|
+
function log() {
|
|
38
|
+
if (_log === null)
|
|
39
|
+
_log = getLogger('resource-governor');
|
|
40
|
+
return _log;
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Mode resolution (mirrors writer-lease.ts:192)
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
let _cachedMode = null;
|
|
46
|
+
let _supervisorDegradeLogged = false;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the governor mode from `CLEO_RESOURCES_MODE`, once per process.
|
|
49
|
+
* Unknown / unset values resolve to `'local'` — the production-safe default
|
|
50
|
+
* while the supervisor daemon is disabled.
|
|
51
|
+
*
|
|
52
|
+
* @task T11999
|
|
53
|
+
*/
|
|
54
|
+
export function resolveGovernorMode() {
|
|
55
|
+
if (_cachedMode !== null)
|
|
56
|
+
return _cachedMode;
|
|
57
|
+
const raw = process.env.CLEO_RESOURCES_MODE;
|
|
58
|
+
_cachedMode = raw === 'supervisor' || raw === 'local' || raw === 'off' ? raw : 'local';
|
|
59
|
+
return _cachedMode;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Log (once) that `supervisor` mode degraded to the local slot engine because
|
|
63
|
+
* the arbiter was unreachable — a dead/absent supervisor must never deadlock
|
|
64
|
+
* work (T12001). Mirrors the writer-lease degrade-once behaviour.
|
|
65
|
+
*/
|
|
66
|
+
function logSupervisorDegradeOnce(reason) {
|
|
67
|
+
if (_supervisorDegradeLogged)
|
|
68
|
+
return;
|
|
69
|
+
_supervisorDegradeLogged = true;
|
|
70
|
+
log().info(`CLEO_RESOURCES_MODE=supervisor but the arbiter is unreachable (${reason}); ` +
|
|
71
|
+
'degrading to local-mode admission for the process lifetime.');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reset cached process-global state (mode + degrade flag). Tests only.
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
export function _resetGovernorStateForTest() {
|
|
78
|
+
_cachedMode = null;
|
|
79
|
+
_supervisorDegradeLogged = false;
|
|
80
|
+
}
|
|
81
|
+
const MB = 1024 * 1024;
|
|
82
|
+
/** Extract `some avg10` (0–100) from a sample; 0 when unavailable. */
|
|
83
|
+
function someAvg10(sample) {
|
|
84
|
+
const some = sample.globalPressure?.some ?? sample.slicePressure?.some;
|
|
85
|
+
return some?.avg10 ?? 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Compute the slot budget for a class given a point-sample.
|
|
89
|
+
*
|
|
90
|
+
* - `interactive-cli` → `Infinity` (never gated).
|
|
91
|
+
* - `full-build` → `1` machine-wide, pressure-independent.
|
|
92
|
+
* - `agent-session` → `clamp(1, ⌊(MemAvailable − headroom)/estRamMb⌋, cpus−2)`.
|
|
93
|
+
* - `test-run` / `scoped-build` → `max(1, ⌊cpus/4⌋)`, ×0.5 when `some>hold`,
|
|
94
|
+
* floored to 1 when `some>floor`.
|
|
95
|
+
* - `llm-call` → `max(1, cpus−2)` (primarily gated by the llm-queue elsewhere).
|
|
96
|
+
* - `db-heavy` → `1`, deferred (→0) under `backoff`-level pressure.
|
|
97
|
+
* - `background-autonomous` → `1` only when pressure is `ok`, else `0`.
|
|
98
|
+
*
|
|
99
|
+
* @adr resource-governor-never-oom-architecture §3.4 (budgets)
|
|
100
|
+
*/
|
|
101
|
+
export function computeClassBudget(cls, sample, opts = {}) {
|
|
102
|
+
if (cls === 'interactive-cli')
|
|
103
|
+
return Number.POSITIVE_INFINITY;
|
|
104
|
+
const cpus = Math.max(1, opts.cpuCount ?? availableParallelism());
|
|
105
|
+
const totalBytes = opts.totalMemBytes ?? totalmem();
|
|
106
|
+
const headroomBytes = (opts.headroomMb ?? 2048) * MB;
|
|
107
|
+
const hold = opts.holdSomeAvg10 ?? 10;
|
|
108
|
+
const floor = opts.floorSomeAvg10 ?? 25;
|
|
109
|
+
const some = someAvg10(sample);
|
|
110
|
+
// MemAvailable can be null on non-Linux / read error — fall back to total.
|
|
111
|
+
const availBytes = sample.memAvailableBytes ?? totalBytes;
|
|
112
|
+
const fullStall = sample.globalPressure?.full?.avg10 ?? sample.slicePressure?.full?.avg10 ?? 0;
|
|
113
|
+
const backoff = some > floor || fullStall > 10;
|
|
114
|
+
switch (cls) {
|
|
115
|
+
case 'full-build':
|
|
116
|
+
return 1;
|
|
117
|
+
case 'agent-session': {
|
|
118
|
+
const estRamBytes = (opts.agentEstRamMb ?? 4096) * MB;
|
|
119
|
+
const byMem = Math.floor((availBytes - headroomBytes) / estRamBytes);
|
|
120
|
+
return clamp(1, byMem, Math.max(1, cpus - 2));
|
|
121
|
+
}
|
|
122
|
+
case 'llm-call':
|
|
123
|
+
return Math.max(1, cpus - 2);
|
|
124
|
+
case 'test-run':
|
|
125
|
+
case 'scoped-build': {
|
|
126
|
+
const base = Math.max(1, Math.floor(cpus / 4));
|
|
127
|
+
if (some > floor)
|
|
128
|
+
return 1;
|
|
129
|
+
if (some > hold)
|
|
130
|
+
return Math.max(1, Math.floor(base / 2));
|
|
131
|
+
return base;
|
|
132
|
+
}
|
|
133
|
+
case 'db-heavy':
|
|
134
|
+
return backoff ? 0 : 1;
|
|
135
|
+
case 'background-autonomous':
|
|
136
|
+
return some > hold || fullStall > 5 ? 0 : 1;
|
|
137
|
+
default:
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function clamp(lo, v, hi) {
|
|
142
|
+
return Math.max(lo, Math.min(v, hi));
|
|
143
|
+
}
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Local-mode slot engine (generalizes tool-semaphore.ts)
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
/**
|
|
148
|
+
* Machine-wide slot directory for a class, under
|
|
149
|
+
* `getCleoHome()/locks/resource-<class>/`. Shared across projects, worktrees,
|
|
150
|
+
* and PIDs — exactly like the tool semaphore.
|
|
151
|
+
*/
|
|
152
|
+
export function governorSlotDir(cls) {
|
|
153
|
+
return join(getCleoHome(), 'locks', `resource-${cls}`);
|
|
154
|
+
}
|
|
155
|
+
function ensureSlotFiles(dir, count) {
|
|
156
|
+
if (!existsSync(dir))
|
|
157
|
+
mkdirSync(dir, { recursive: true });
|
|
158
|
+
const paths = [];
|
|
159
|
+
for (let i = 0; i < count; i++) {
|
|
160
|
+
const p = join(dir, `slot-${i}.lock`);
|
|
161
|
+
if (!existsSync(p))
|
|
162
|
+
writeFileSync(p, '', { flag: 'a' });
|
|
163
|
+
paths.push(p);
|
|
164
|
+
}
|
|
165
|
+
return paths;
|
|
166
|
+
}
|
|
167
|
+
const STALE_MS = 600_000;
|
|
168
|
+
function sleep(ms) {
|
|
169
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
170
|
+
}
|
|
171
|
+
function passThroughGrant(cls) {
|
|
172
|
+
return {
|
|
173
|
+
deferred: false,
|
|
174
|
+
class: cls,
|
|
175
|
+
slot: -1,
|
|
176
|
+
acquiredAtMs: Date.now(),
|
|
177
|
+
release: async () => { },
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function deferral(cls, reason, retryAfterMs) {
|
|
181
|
+
return { deferred: true, class: cls, retryAfterMs, reason };
|
|
182
|
+
}
|
|
183
|
+
let _supervisorHolderSeq = 0;
|
|
184
|
+
/**
|
|
185
|
+
* A process-unique holder id for a supervisor `resource_admit`, so each acquire
|
|
186
|
+
* is a distinct slot (matched 1:1 by its `release`). `pid` scopes the holder to
|
|
187
|
+
* this process; the sequence distinguishes concurrent holds of the same class.
|
|
188
|
+
*/
|
|
189
|
+
function supervisorHolderId(cls) {
|
|
190
|
+
_supervisorHolderSeq += 1;
|
|
191
|
+
return `${process.pid}:${cls}:${_supervisorHolderSeq}`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The Never-OOM admission gate. Stateless wrapper over the mode-resolved
|
|
195
|
+
* backend (local slot dirs today; supervisor IPC when wired). Construct once
|
|
196
|
+
* and share, or use the module-level {@link governor} singleton.
|
|
197
|
+
*/
|
|
198
|
+
export class ResourceGovernor {
|
|
199
|
+
/**
|
|
200
|
+
* Acquire one slot of `cls`. Returns a {@link ResourceGrant} on success or a
|
|
201
|
+
* {@link ResourceDeferral} on denial. Never throws for admission control;
|
|
202
|
+
* only genuinely unexpected I/O errors propagate.
|
|
203
|
+
*/
|
|
204
|
+
async acquire(cls, opts = {}) {
|
|
205
|
+
// Ungated fast paths: off mode + interactive-cli are pure pass-through.
|
|
206
|
+
const mode = resolveGovernorMode();
|
|
207
|
+
if (mode === 'off' || cls === 'interactive-cli') {
|
|
208
|
+
return passThroughGrant(cls);
|
|
209
|
+
}
|
|
210
|
+
const sample = opts.sample ?? (await (opts.monitor ?? new ResourceMonitor()).sample());
|
|
211
|
+
const budget = computeClassBudget(cls, sample, opts);
|
|
212
|
+
if (!Number.isFinite(budget))
|
|
213
|
+
return passThroughGrant(cls);
|
|
214
|
+
if (budget <= 0) {
|
|
215
|
+
return deferral(cls, `class '${cls}' budget is 0 under current pressure (some avg10=${someAvg10(sample).toFixed(1)})`, DEFAULT_RESOURCE_RETRY_AFTER_MS);
|
|
216
|
+
}
|
|
217
|
+
// Supervisor mode (T12001): route the count enforcement through the central
|
|
218
|
+
// Rust arbiter so heavy ops are bounded machine-wide. The client computes the
|
|
219
|
+
// budget (above) from its local pressure sample; the supervisor enforces the
|
|
220
|
+
// in-flight COUNT. An unreachable supervisor degrades to the local slot
|
|
221
|
+
// engine below — never a deadlock.
|
|
222
|
+
if (mode === 'supervisor') {
|
|
223
|
+
const viaSupervisor = await this.acquireViaSupervisor(cls, Math.floor(budget));
|
|
224
|
+
if (viaSupervisor !== null)
|
|
225
|
+
return viaSupervisor;
|
|
226
|
+
}
|
|
227
|
+
const dir = governorSlotDir(cls);
|
|
228
|
+
const slots = ensureSlotFiles(dir, budget);
|
|
229
|
+
const blocking = opts.blocking ?? true;
|
|
230
|
+
const timeoutMs = opts.timeoutMs ?? 3_600_000;
|
|
231
|
+
const pollMs = opts.pollMs ?? 200;
|
|
232
|
+
const startedAt = Date.now();
|
|
233
|
+
do {
|
|
234
|
+
// Re-shuffle each pass so concurrent acquirers don't collide on slot 0.
|
|
235
|
+
const order = shuffledIndices(slots.length);
|
|
236
|
+
for (const idx of order) {
|
|
237
|
+
const path = slots[idx];
|
|
238
|
+
if (!path)
|
|
239
|
+
continue;
|
|
240
|
+
try {
|
|
241
|
+
const release = await lockfile.lock(path, {
|
|
242
|
+
retries: 0,
|
|
243
|
+
stale: STALE_MS,
|
|
244
|
+
realpath: false,
|
|
245
|
+
});
|
|
246
|
+
let released = false;
|
|
247
|
+
return {
|
|
248
|
+
deferred: false,
|
|
249
|
+
class: cls,
|
|
250
|
+
slot: idx,
|
|
251
|
+
acquiredAtMs: Date.now(),
|
|
252
|
+
release: async () => {
|
|
253
|
+
if (released)
|
|
254
|
+
return;
|
|
255
|
+
released = true;
|
|
256
|
+
try {
|
|
257
|
+
await release();
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Already released (e.g. stale recovery) — post-condition holds.
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// slot busy — try next
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!blocking)
|
|
270
|
+
break;
|
|
271
|
+
await sleep(pollMs);
|
|
272
|
+
} while (Date.now() - startedAt < timeoutMs);
|
|
273
|
+
return deferral(cls, `class '${cls}' is at capacity (${budget} slot(s)); ` +
|
|
274
|
+
(blocking ? `timed out after ${timeoutMs}ms` : 'no slot free'), Math.min(pollMs * 4, DEFAULT_RESOURCE_RETRY_AFTER_MS));
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Route an admission through the supervisor's central `resource_admit` verb.
|
|
278
|
+
* Returns a grant (whose `release` calls `resource_release`) or a deferral, or
|
|
279
|
+
* `null` when the supervisor is unreachable so the caller degrades to the
|
|
280
|
+
* local slot engine. Never throws — a dead arbiter never deadlocks work.
|
|
281
|
+
*
|
|
282
|
+
* @task T12001
|
|
283
|
+
*/
|
|
284
|
+
async acquireViaSupervisor(cls, budget) {
|
|
285
|
+
const socketPath = resolveSupervisorSocketPath();
|
|
286
|
+
const holderId = supervisorHolderId(cls);
|
|
287
|
+
const reply = await sendResourceAdmit(socketPath, cls, holderId, budget);
|
|
288
|
+
if ('unavailable' in reply) {
|
|
289
|
+
logSupervisorDegradeOnce(reply.reason);
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
if (reply.disposition === 'deferred') {
|
|
293
|
+
return deferral(cls, `class '${cls}' deferred by supervisor (budget ${budget})`, reply.retry_after_ms || DEFAULT_RESOURCE_RETRY_AFTER_MS);
|
|
294
|
+
}
|
|
295
|
+
let released = false;
|
|
296
|
+
return {
|
|
297
|
+
deferred: false,
|
|
298
|
+
class: cls,
|
|
299
|
+
slot: 0,
|
|
300
|
+
acquiredAtMs: Date.now(),
|
|
301
|
+
release: async () => {
|
|
302
|
+
if (released)
|
|
303
|
+
return;
|
|
304
|
+
released = true;
|
|
305
|
+
await sendResourceRelease(socketPath, cls, holderId).catch(() => {
|
|
306
|
+
// Best-effort: the arbiter reclaims the slot on process death anyway.
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
/** Non-blocking single-pass acquire (admission semantics). */
|
|
312
|
+
async tryAcquire(cls, opts = {}) {
|
|
313
|
+
return this.acquire(cls, { ...opts, blocking: false });
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Currently-grantable slot count for `cls` = budget − held. Held is the count
|
|
317
|
+
* of slot files currently locked. `Infinity` for ungated classes.
|
|
318
|
+
*/
|
|
319
|
+
async available(cls, opts = {}) {
|
|
320
|
+
if (resolveGovernorMode() === 'off' || cls === 'interactive-cli') {
|
|
321
|
+
return Number.POSITIVE_INFINITY;
|
|
322
|
+
}
|
|
323
|
+
const sample = opts.sample ?? (await (opts.monitor ?? new ResourceMonitor()).sample());
|
|
324
|
+
const budget = computeClassBudget(cls, sample, opts);
|
|
325
|
+
if (!Number.isFinite(budget))
|
|
326
|
+
return Number.POSITIVE_INFINITY;
|
|
327
|
+
if (budget <= 0)
|
|
328
|
+
return 0;
|
|
329
|
+
const held = await countHeldSlots(cls, budget);
|
|
330
|
+
return Math.max(0, budget - held);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function shuffledIndices(n) {
|
|
334
|
+
const order = Array.from({ length: n }, (_, i) => i);
|
|
335
|
+
for (let i = order.length - 1; i > 0; i--) {
|
|
336
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
337
|
+
const a = order[i];
|
|
338
|
+
const b = order[j];
|
|
339
|
+
if (a !== undefined && b !== undefined) {
|
|
340
|
+
order[i] = b;
|
|
341
|
+
order[j] = a;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return order;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Count how many of `budget` slots are currently held, by probing each with a
|
|
348
|
+
* non-blocking lock. A successful probe-lock is released immediately — it never
|
|
349
|
+
* holds the slot, so it cannot starve a real acquirer.
|
|
350
|
+
*/
|
|
351
|
+
async function countHeldSlots(cls, budget) {
|
|
352
|
+
const dir = governorSlotDir(cls);
|
|
353
|
+
if (!existsSync(dir))
|
|
354
|
+
return 0;
|
|
355
|
+
const slots = ensureSlotFiles(dir, budget);
|
|
356
|
+
let held = 0;
|
|
357
|
+
for (const path of slots) {
|
|
358
|
+
try {
|
|
359
|
+
const release = await lockfile.lock(path, { retries: 0, stale: STALE_MS, realpath: false });
|
|
360
|
+
await release();
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
held++;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return held;
|
|
367
|
+
}
|
|
368
|
+
/** Count of slot files for a class (debug/introspection). */
|
|
369
|
+
export function slotFileCount(cls) {
|
|
370
|
+
const dir = governorSlotDir(cls);
|
|
371
|
+
if (!existsSync(dir))
|
|
372
|
+
return 0;
|
|
373
|
+
return readdirSync(dir).filter((f) => f.startsWith('slot-') && f.endsWith('.lock')).length;
|
|
374
|
+
}
|
|
375
|
+
/** Process-wide governor singleton. */
|
|
376
|
+
export const governor = new ResourceGovernor();
|
|
377
|
+
//# sourceMappingURL=governor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"governor.js","sourceRoot":"","sources":["../../src/resources/governor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supervisor-mode resource admission client (T12001 · Epic T11992).
|
|
3
|
+
*
|
|
4
|
+
* The `supervisor`-mode counterpart of the {@link ResourceGovernor}'s local slot
|
|
5
|
+
* engine. Where local mode arbitrates heavy-op concurrency through cross-process
|
|
6
|
+
* lockfiles, supervisor mode routes the `resource_admit` / `resource_release`
|
|
7
|
+
* lease-ipc verbs to the Rust `cleo-supervisor` so a single in-memory counter is
|
|
8
|
+
* the machine-wide source of truth.
|
|
9
|
+
*
|
|
10
|
+
* A dead/absent supervisor must NEVER deadlock work: every send degrades to
|
|
11
|
+
* `{ unavailable }` on any transport error, and the governor falls back to the
|
|
12
|
+
* local slot engine. Mirrors the `llm-queue-admit.ts` transport (core cannot
|
|
13
|
+
* import `@cleocode/runtime` — a cycle — so it speaks the NDJSON codec directly
|
|
14
|
+
* through the shared `@cleocode/contracts` lease-ipc Zod schemas).
|
|
15
|
+
*
|
|
16
|
+
* @task T12001
|
|
17
|
+
* @epic T11992
|
|
18
|
+
*/
|
|
19
|
+
import { type ResourceAdmitResultResponse, type ResourceReleaseResultResponse } from '@cleocode/contracts';
|
|
20
|
+
/** A transport-unavailable sentinel — the caller degrades to the local engine. */
|
|
21
|
+
export interface SupervisorUnavailable {
|
|
22
|
+
readonly unavailable: true;
|
|
23
|
+
readonly reason: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the supervisor socket path: `$CLEO_SUPERVISOR_SOCKET` overrides,
|
|
27
|
+
* otherwise `<cleoHome>/cleo-supervisor.sock`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveSupervisorSocketPath(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Admit (or defer) a heavy op of `cls` for `holderId` against `budget` through
|
|
32
|
+
* the supervisor's central arbiter. Resolves the `resource_admit_result` reply,
|
|
33
|
+
* or `{ unavailable }` when the supervisor cannot arbitrate (degrade to local).
|
|
34
|
+
*/
|
|
35
|
+
export declare function sendResourceAdmit(socketPath: string, cls: string, holderId: string, budget: number): Promise<ResourceAdmitResultResponse | SupervisorUnavailable>;
|
|
36
|
+
/**
|
|
37
|
+
* Release a previously-admitted slot of `cls` for `holderId`. Resolves the
|
|
38
|
+
* `resource_release_result` reply, or `{ unavailable }` on transport failure
|
|
39
|
+
* (the slot self-heals via the arbiter's process-death reclaim / stale recovery).
|
|
40
|
+
*/
|
|
41
|
+
export declare function sendResourceRelease(socketPath: string, cls: string, holderId: string): Promise<ResourceReleaseResultResponse | SupervisorUnavailable>;
|
|
42
|
+
//# sourceMappingURL=supervisor-admit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor-admit.d.ts","sourceRoot":"","sources":["../../src/resources/supervisor-admit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAKL,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EACnC,MAAM,qBAAqB,CAAC;AAY7B,kFAAkF;AAClF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,2BAA2B,GAAG,qBAAqB,CAAC,CAS9D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,6BAA6B,GAAG,qBAAqB,CAAC,CAShE"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supervisor-mode resource admission client (T12001 · Epic T11992).
|
|
3
|
+
*
|
|
4
|
+
* The `supervisor`-mode counterpart of the {@link ResourceGovernor}'s local slot
|
|
5
|
+
* engine. Where local mode arbitrates heavy-op concurrency through cross-process
|
|
6
|
+
* lockfiles, supervisor mode routes the `resource_admit` / `resource_release`
|
|
7
|
+
* lease-ipc verbs to the Rust `cleo-supervisor` so a single in-memory counter is
|
|
8
|
+
* the machine-wide source of truth.
|
|
9
|
+
*
|
|
10
|
+
* A dead/absent supervisor must NEVER deadlock work: every send degrades to
|
|
11
|
+
* `{ unavailable }` on any transport error, and the governor falls back to the
|
|
12
|
+
* local slot engine. Mirrors the `llm-queue-admit.ts` transport (core cannot
|
|
13
|
+
* import `@cleocode/runtime` — a cycle — so it speaks the NDJSON codec directly
|
|
14
|
+
* through the shared `@cleocode/contracts` lease-ipc Zod schemas).
|
|
15
|
+
*
|
|
16
|
+
* @task T12001
|
|
17
|
+
* @epic T11992
|
|
18
|
+
*/
|
|
19
|
+
import { connect } from 'node:net';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { LEASE_IPC_PROTOCOL_VERSION, LeaseIpcRequestEnvelopeSchema, LeaseIpcResponseEnvelopeSchema, } from '@cleocode/contracts';
|
|
22
|
+
import { getCleoHome } from '@cleocode/paths';
|
|
23
|
+
/** The supervisor IPC socket filename — MUST equal `SOCKET_NAME` in `crates/cleo-supervisor/src/paths.rs`. */
|
|
24
|
+
const SUPERVISOR_SOCKET_NAME = 'cleo-supervisor.sock';
|
|
25
|
+
/** Connect timeout (ms). A slow/absent socket degrades fast — never a deadlock. */
|
|
26
|
+
const CONNECT_TIMEOUT_MS = 250;
|
|
27
|
+
/** Per-request read timeout for a result frame (ms). */
|
|
28
|
+
const REQUEST_TIMEOUT_MS = 2_000;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the supervisor socket path: `$CLEO_SUPERVISOR_SOCKET` overrides,
|
|
31
|
+
* otherwise `<cleoHome>/cleo-supervisor.sock`.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveSupervisorSocketPath() {
|
|
34
|
+
return process.env.CLEO_SUPERVISOR_SOCKET ?? join(getCleoHome(), SUPERVISOR_SOCKET_NAME);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Admit (or defer) a heavy op of `cls` for `holderId` against `budget` through
|
|
38
|
+
* the supervisor's central arbiter. Resolves the `resource_admit_result` reply,
|
|
39
|
+
* or `{ unavailable }` when the supervisor cannot arbitrate (degrade to local).
|
|
40
|
+
*/
|
|
41
|
+
export async function sendResourceAdmit(socketPath, cls, holderId, budget) {
|
|
42
|
+
const result = await sendLeaseRequest(socketPath, { kind: 'resource_admit', class: cls, holder_id: holderId, budget }, 'resource_admit_result');
|
|
43
|
+
if ('unavailable' in result)
|
|
44
|
+
return result;
|
|
45
|
+
if (result.kind === 'resource_admit_result')
|
|
46
|
+
return result;
|
|
47
|
+
return { unavailable: true, reason: `unexpected response kind: ${result.kind}` };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Release a previously-admitted slot of `cls` for `holderId`. Resolves the
|
|
51
|
+
* `resource_release_result` reply, or `{ unavailable }` on transport failure
|
|
52
|
+
* (the slot self-heals via the arbiter's process-death reclaim / stale recovery).
|
|
53
|
+
*/
|
|
54
|
+
export async function sendResourceRelease(socketPath, cls, holderId) {
|
|
55
|
+
const result = await sendLeaseRequest(socketPath, { kind: 'resource_release', class: cls, holder_id: holderId }, 'resource_release_result');
|
|
56
|
+
if ('unavailable' in result)
|
|
57
|
+
return result;
|
|
58
|
+
if (result.kind === 'resource_release_result')
|
|
59
|
+
return result;
|
|
60
|
+
return { unavailable: true, reason: `unexpected response kind: ${result.kind}` };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* One-shot NDJSON lease-ipc round-trip over a fresh Unix socket. Connection per
|
|
64
|
+
* request (the verbs are stateless); the socket is closed before resolution.
|
|
65
|
+
* Any transport error resolves to `{ unavailable }` — never throws, never
|
|
66
|
+
* deadlocks. `expectedKind` is the result `kind` the caller correlates.
|
|
67
|
+
*/
|
|
68
|
+
function sendLeaseRequest(socketPath, request, expectedKind) {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const id = `ra-${process.pid}-${request.kind}`;
|
|
71
|
+
const envelope = LeaseIpcRequestEnvelopeSchema.parse({
|
|
72
|
+
protocol_version: LEASE_IPC_PROTOCOL_VERSION,
|
|
73
|
+
id,
|
|
74
|
+
direction: 'request',
|
|
75
|
+
request,
|
|
76
|
+
});
|
|
77
|
+
const line = `${JSON.stringify(envelope)}\n`;
|
|
78
|
+
let settled = false;
|
|
79
|
+
let buffer = '';
|
|
80
|
+
const socket = connect(socketPath);
|
|
81
|
+
const done = (value) => {
|
|
82
|
+
if (settled)
|
|
83
|
+
return;
|
|
84
|
+
settled = true;
|
|
85
|
+
socket.removeAllListeners();
|
|
86
|
+
socket.destroy();
|
|
87
|
+
resolve(value);
|
|
88
|
+
};
|
|
89
|
+
const degrade = (reason) => done({ unavailable: true, reason });
|
|
90
|
+
socket.setTimeout(REQUEST_TIMEOUT_MS);
|
|
91
|
+
const connectTimer = setTimeout(() => degrade('supervisor connect timeout'), CONNECT_TIMEOUT_MS);
|
|
92
|
+
socket.on('connect', () => {
|
|
93
|
+
clearTimeout(connectTimer);
|
|
94
|
+
socket.write(line);
|
|
95
|
+
});
|
|
96
|
+
socket.on('timeout', () => degrade('supervisor request timeout'));
|
|
97
|
+
socket.on('error', (err) => {
|
|
98
|
+
clearTimeout(connectTimer);
|
|
99
|
+
degrade(err.message);
|
|
100
|
+
});
|
|
101
|
+
socket.on('close', () => {
|
|
102
|
+
if (!settled)
|
|
103
|
+
degrade('supervisor closed the connection before responding');
|
|
104
|
+
});
|
|
105
|
+
socket.on('data', (chunk) => {
|
|
106
|
+
buffer += chunk.toString('utf8');
|
|
107
|
+
const nl = buffer.indexOf('\n');
|
|
108
|
+
if (nl < 0)
|
|
109
|
+
return; // wait for a full NDJSON line
|
|
110
|
+
let json;
|
|
111
|
+
try {
|
|
112
|
+
json = JSON.parse(buffer.slice(0, nl));
|
|
113
|
+
}
|
|
114
|
+
catch (cause) {
|
|
115
|
+
degrade(`malformed lease-ipc frame: ${cause instanceof Error ? cause.message : cause}`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const parsed = LeaseIpcResponseEnvelopeSchema.safeParse(json);
|
|
119
|
+
if (!parsed.success) {
|
|
120
|
+
degrade(`off-contract lease-ipc response: ${parsed.error.message}`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const { response } = parsed.data;
|
|
124
|
+
if (response.kind === expectedKind) {
|
|
125
|
+
done(response);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (response.kind === 'error') {
|
|
129
|
+
degrade(`${response.code}: ${response.message}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
degrade(`unexpected lease-ipc response kind for ${request.kind}: ${response.kind}`);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=supervisor-admit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor-admit.js","sourceRoot":"","sources":["../../src/resources/supervisor-admit.ts"],"names":[],"mappings":"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"}
|