@mindfoldhq/trellis 0.6.0-beta.17 → 0.6.0-beta.18
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/README.md +1 -1
- package/dist/commands/channel/adapters/claude.d.ts +7 -16
- package/dist/commands/channel/adapters/claude.d.ts.map +1 -1
- package/dist/commands/channel/adapters/claude.js +19 -25
- package/dist/commands/channel/adapters/claude.js.map +1 -1
- package/dist/commands/channel/adapters/codex.d.ts +5 -1
- package/dist/commands/channel/adapters/codex.d.ts.map +1 -1
- package/dist/commands/channel/adapters/codex.js +8 -15
- package/dist/commands/channel/adapters/codex.js.map +1 -1
- package/dist/commands/channel/adapters/index.d.ts +6 -1
- package/dist/commands/channel/adapters/index.d.ts.map +1 -1
- package/dist/commands/channel/adapters/index.js +12 -6
- package/dist/commands/channel/adapters/index.js.map +1 -1
- package/dist/commands/channel/guard.d.ts +150 -0
- package/dist/commands/channel/guard.d.ts.map +1 -0
- package/dist/commands/channel/guard.js +474 -0
- package/dist/commands/channel/guard.js.map +1 -0
- package/dist/commands/channel/index.d.ts +1 -1
- package/dist/commands/channel/index.d.ts.map +1 -1
- package/dist/commands/channel/index.js +38 -10
- package/dist/commands/channel/index.js.map +1 -1
- package/dist/commands/channel/interrupt.d.ts +10 -0
- package/dist/commands/channel/interrupt.d.ts.map +1 -0
- package/dist/commands/channel/interrupt.js +22 -0
- package/dist/commands/channel/interrupt.js.map +1 -0
- package/dist/commands/channel/messages.d.ts +0 -1
- package/dist/commands/channel/messages.d.ts.map +1 -1
- package/dist/commands/channel/messages.js +2 -6
- package/dist/commands/channel/messages.js.map +1 -1
- package/dist/commands/channel/run.d.ts +0 -1
- package/dist/commands/channel/run.d.ts.map +1 -1
- package/dist/commands/channel/run.js +5 -12
- package/dist/commands/channel/run.js.map +1 -1
- package/dist/commands/channel/send.d.ts +0 -2
- package/dist/commands/channel/send.d.ts.map +1 -1
- package/dist/commands/channel/send.js +0 -2
- package/dist/commands/channel/send.js.map +1 -1
- package/dist/commands/channel/spawn.d.ts +10 -0
- package/dist/commands/channel/spawn.d.ts.map +1 -1
- package/dist/commands/channel/spawn.js +57 -7
- package/dist/commands/channel/spawn.js.map +1 -1
- package/dist/commands/channel/supervisor/idle.d.ts +46 -0
- package/dist/commands/channel/supervisor/idle.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/idle.js +72 -0
- package/dist/commands/channel/supervisor/idle.js.map +1 -0
- package/dist/commands/channel/supervisor/inbox.d.ts +4 -4
- package/dist/commands/channel/supervisor/inbox.d.ts.map +1 -1
- package/dist/commands/channel/supervisor/inbox.js +22 -22
- package/dist/commands/channel/supervisor/inbox.js.map +1 -1
- package/dist/commands/channel/supervisor/shutdown.d.ts +3 -1
- package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -1
- package/dist/commands/channel/supervisor/shutdown.js +4 -1
- package/dist/commands/channel/supervisor/shutdown.js.map +1 -1
- package/dist/commands/channel/supervisor/turns.d.ts +11 -0
- package/dist/commands/channel/supervisor/turns.d.ts.map +1 -1
- package/dist/commands/channel/supervisor/turns.js +19 -2
- package/dist/commands/channel/supervisor/turns.js.map +1 -1
- package/dist/commands/channel/supervisor.d.ts +6 -0
- package/dist/commands/channel/supervisor.d.ts.map +1 -1
- package/dist/commands/channel/supervisor.js +43 -3
- package/dist/commands/channel/supervisor.js.map +1 -1
- package/dist/commands/channel/wait.d.ts +0 -1
- package/dist/commands/channel/wait.d.ts.map +1 -1
- package/dist/commands/channel/wait.js +0 -1
- package/dist/commands/channel/wait.js.map +1 -1
- package/dist/migrations/manifests/0.5.16.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.18.json +16 -0
- package/dist/templates/trellis/config.yaml +20 -0
- package/dist/templates/trellis/scripts/common/task_store.py +24 -7
- package/package.json +2 -2
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel worker OOM guard policy.
|
|
3
|
+
*
|
|
4
|
+
* Resolves idle-cleanup TTL and live-worker budget from CLI flag /
|
|
5
|
+
* environment / project config / built-in defaults (in that precedence
|
|
6
|
+
* order), then scans the live worker registry inside a project bucket
|
|
7
|
+
* to enforce both constraints at spawn time.
|
|
8
|
+
*
|
|
9
|
+
* Boundary: this module lives in the CLI runtime layer. Core owns
|
|
10
|
+
* worker activity / `idleSince` projection; the supervisor owns process
|
|
11
|
+
* launch + signals. The guard only consumes that durable state and
|
|
12
|
+
* sends OS signals through pid files.
|
|
13
|
+
*/
|
|
14
|
+
import { execFileSync } from "node:child_process";
|
|
15
|
+
import fs from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { isTerminalLifecycle, reduceWorkerRegistry, } from "@mindfoldhq/trellis-core/channel";
|
|
18
|
+
import { DIR_NAMES } from "../../constants/paths.js";
|
|
19
|
+
import { channelRoot, currentProjectKey, projectDir, workerFile, } from "./store/paths.js";
|
|
20
|
+
import { parseDuration } from "./wait.js";
|
|
21
|
+
/** Built-in default idle-cleanup TTL for spawned workers (5 minutes). */
|
|
22
|
+
export const DEFAULT_IDLE_TTL_MS = 5 * 60 * 1000;
|
|
23
|
+
/** Built-in default live-worker budget per project/scope. */
|
|
24
|
+
export const DEFAULT_MAX_LIVE_WORKERS = 6;
|
|
25
|
+
/** Env var override for the idle-cleanup TTL. */
|
|
26
|
+
export const ENV_IDLE_TIMEOUT = "TRELLIS_CHANNEL_WORKER_IDLE_TIMEOUT";
|
|
27
|
+
/** Env var override for the live-worker budget. */
|
|
28
|
+
export const ENV_MAX_LIVE_WORKERS = "TRELLIS_CHANNEL_MAX_LIVE_WORKERS";
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the effective guard policy. Precedence:
|
|
31
|
+
* 1. CLI flag (`flag*Ms` / `flagMaxLiveWorkers`)
|
|
32
|
+
* 2. environment variable
|
|
33
|
+
* 3. `.trellis/config.yaml` `channel.worker_guard`
|
|
34
|
+
* 4. built-in default constant
|
|
35
|
+
*/
|
|
36
|
+
export function resolveWorkerGuardConfig(opts = {}) {
|
|
37
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
38
|
+
const env = opts.env ?? process.env;
|
|
39
|
+
const fromConfig = loadWorkerGuardConfig(cwd);
|
|
40
|
+
const idleTimeoutMs = pickNonNegativeMs(opts.flagIdleTimeoutMs, parseEnvDuration(env[ENV_IDLE_TIMEOUT], ENV_IDLE_TIMEOUT), fromConfig?.idleTimeoutMs, DEFAULT_IDLE_TTL_MS);
|
|
41
|
+
const maxLiveWorkers = pickNonNegativeInt(opts.flagMaxLiveWorkers, parseEnvInt(env[ENV_MAX_LIVE_WORKERS], ENV_MAX_LIVE_WORKERS), fromConfig?.maxLiveWorkers, DEFAULT_MAX_LIVE_WORKERS);
|
|
42
|
+
return { idleTimeoutMs, maxLiveWorkers };
|
|
43
|
+
}
|
|
44
|
+
function pickNonNegativeMs(...candidates) {
|
|
45
|
+
for (const c of candidates) {
|
|
46
|
+
if (c === undefined)
|
|
47
|
+
continue;
|
|
48
|
+
if (!Number.isFinite(c) || c < 0) {
|
|
49
|
+
throw new Error(`Idle timeout must be a non-negative duration (got ${c})`);
|
|
50
|
+
}
|
|
51
|
+
return c;
|
|
52
|
+
}
|
|
53
|
+
return DEFAULT_IDLE_TTL_MS;
|
|
54
|
+
}
|
|
55
|
+
function pickNonNegativeInt(...candidates) {
|
|
56
|
+
for (const c of candidates) {
|
|
57
|
+
if (c === undefined)
|
|
58
|
+
continue;
|
|
59
|
+
if (!Number.isInteger(c) || c < 0) {
|
|
60
|
+
throw new Error(`Max live workers must be a non-negative integer (got ${c})`);
|
|
61
|
+
}
|
|
62
|
+
return c;
|
|
63
|
+
}
|
|
64
|
+
return DEFAULT_MAX_LIVE_WORKERS;
|
|
65
|
+
}
|
|
66
|
+
function parseEnvDuration(raw, envName) {
|
|
67
|
+
if (raw === undefined || raw === "")
|
|
68
|
+
return undefined;
|
|
69
|
+
try {
|
|
70
|
+
return parseDuration(raw);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
throw new Error(`${envName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function parseEnvInt(raw, envName) {
|
|
77
|
+
if (raw === undefined || raw === "")
|
|
78
|
+
return undefined;
|
|
79
|
+
const n = Number(raw);
|
|
80
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
81
|
+
throw new Error(`${envName} must be a non-negative integer (got '${raw}')`);
|
|
82
|
+
}
|
|
83
|
+
return n;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Parse the `channel.worker_guard` section out of `.trellis/config.yaml`.
|
|
87
|
+
* Mirrors the lightweight line-scanner used elsewhere in update.ts so we
|
|
88
|
+
* don't pull in a YAML dependency just for this two-field section.
|
|
89
|
+
*/
|
|
90
|
+
export function loadWorkerGuardConfig(cwd) {
|
|
91
|
+
const configPath = path.join(cwd, DIR_NAMES.WORKFLOW, "config.yaml");
|
|
92
|
+
if (!fs.existsSync(configPath))
|
|
93
|
+
return undefined;
|
|
94
|
+
let content;
|
|
95
|
+
try {
|
|
96
|
+
content = fs.readFileSync(configPath, "utf-8");
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return parseWorkerGuardSection(content);
|
|
102
|
+
}
|
|
103
|
+
/** Exposed for unit tests. */
|
|
104
|
+
export function parseWorkerGuardSection(content) {
|
|
105
|
+
const lines = content.split("\n");
|
|
106
|
+
let inChannel = false;
|
|
107
|
+
let inGuard = false;
|
|
108
|
+
const found = {};
|
|
109
|
+
let any = false;
|
|
110
|
+
for (const raw of lines) {
|
|
111
|
+
const line = raw.replace(/\r$/, "");
|
|
112
|
+
const trimmed = line.trimEnd();
|
|
113
|
+
if (trimmed === "" || trimmed.trimStart().startsWith("#"))
|
|
114
|
+
continue;
|
|
115
|
+
if (/^channel:\s*$/.test(trimmed)) {
|
|
116
|
+
inChannel = true;
|
|
117
|
+
inGuard = false;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (inChannel && /^ {2}worker_guard:\s*$/.test(trimmed)) {
|
|
121
|
+
inGuard = true;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (inGuard) {
|
|
125
|
+
const idle = trimmed.match(/^ {4}idle_timeout:\s*(.+)$/);
|
|
126
|
+
if (idle) {
|
|
127
|
+
const val = stripValue(idle[1]);
|
|
128
|
+
found.idleTimeoutMs = parseGuardDuration(val, "idle_timeout");
|
|
129
|
+
any = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const max = trimmed.match(/^ {4}max_live_workers:\s*(.+)$/);
|
|
133
|
+
if (max) {
|
|
134
|
+
const val = stripValue(max[1]);
|
|
135
|
+
const n = Number(val);
|
|
136
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
137
|
+
throw new Error(`channel.worker_guard.max_live_workers must be a non-negative integer (got '${val}')`);
|
|
138
|
+
}
|
|
139
|
+
found.maxLiveWorkers = n;
|
|
140
|
+
any = true;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// Anything else at the same indent (or shallower) ends the section.
|
|
144
|
+
if (!/^ {4}\S/.test(line)) {
|
|
145
|
+
inGuard = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (inChannel && !/^ {2}\S/.test(line) && /^\S/.test(line)) {
|
|
149
|
+
inChannel = false;
|
|
150
|
+
inGuard = false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return any ? found : undefined;
|
|
154
|
+
}
|
|
155
|
+
function stripValue(s) {
|
|
156
|
+
return s
|
|
157
|
+
.trim()
|
|
158
|
+
.replace(/\s*#.*$/, "")
|
|
159
|
+
.trim()
|
|
160
|
+
.replace(/^['"]|['"]$/g, "");
|
|
161
|
+
}
|
|
162
|
+
function parseGuardDuration(raw, key) {
|
|
163
|
+
// Allow bare integer = milliseconds (so `0` disables cleanly).
|
|
164
|
+
const asInt = Number(raw);
|
|
165
|
+
if (Number.isFinite(asInt) && /^\d+$/.test(raw)) {
|
|
166
|
+
if (asInt < 0) {
|
|
167
|
+
throw new Error(`channel.worker_guard.${key} must be non-negative (got '${raw}')`);
|
|
168
|
+
}
|
|
169
|
+
// Bare integer 0 = disabled; >0 with no unit = milliseconds.
|
|
170
|
+
return asInt;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
return parseDuration(raw) ?? 0;
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
throw new Error(`channel.worker_guard.${key}: ${err instanceof Error ? err.message : String(err)}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Enumerate live (non-terminal + supervisor-pid alive) workers across
|
|
181
|
+
* every channel in the given project bucket.
|
|
182
|
+
*/
|
|
183
|
+
export function scanLiveWorkers(opts = {}) {
|
|
184
|
+
const project = opts.projectKey ?? currentProjectKey();
|
|
185
|
+
const bucket = opts.root
|
|
186
|
+
? path.join(opts.root, project)
|
|
187
|
+
: projectDir(project);
|
|
188
|
+
if (!fs.existsSync(bucket))
|
|
189
|
+
return [];
|
|
190
|
+
let entries;
|
|
191
|
+
try {
|
|
192
|
+
entries = fs.readdirSync(bucket);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
const out = [];
|
|
198
|
+
for (const entry of entries) {
|
|
199
|
+
if (entry.startsWith("."))
|
|
200
|
+
continue;
|
|
201
|
+
const dir = path.join(bucket, entry);
|
|
202
|
+
try {
|
|
203
|
+
if (!fs.statSync(dir).isDirectory())
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const events = path.join(dir, "events.jsonl");
|
|
210
|
+
if (!fs.existsSync(events))
|
|
211
|
+
continue;
|
|
212
|
+
let workers;
|
|
213
|
+
try {
|
|
214
|
+
const all = readFileEventsSync(events);
|
|
215
|
+
workers = reduceWorkerRegistry(all).workers;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
for (const state of workers) {
|
|
221
|
+
if (state.terminal || isTerminalLifecycle(state.lifecycle))
|
|
222
|
+
continue;
|
|
223
|
+
const supervisorPid = readPid(workerFile(entry, state.workerId, "pid", project));
|
|
224
|
+
if (supervisorPid === undefined || !pidAlive(supervisorPid)) {
|
|
225
|
+
// Supervisor pid file missing or dead → not a live OS process,
|
|
226
|
+
// even if durable state still shows running. Reconciler / future
|
|
227
|
+
// CLI cleanup will catch up; the guard ignores it.
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const supervisorVerified = opts.isSupervisorProcess
|
|
231
|
+
? opts.isSupervisorProcess(supervisorPid, entry, state.workerId)
|
|
232
|
+
: isSupervisorProcess(supervisorPid, entry, state.workerId);
|
|
233
|
+
const workerPid = readPid(workerFile(entry, state.workerId, "worker-pid", project));
|
|
234
|
+
out.push({
|
|
235
|
+
channel: entry,
|
|
236
|
+
workerId: state.workerId,
|
|
237
|
+
state,
|
|
238
|
+
supervisorPid,
|
|
239
|
+
supervisorVerified,
|
|
240
|
+
...(workerPid !== undefined ? { workerPid } : {}),
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
for (const state of readReservationWorkers(entry, project)) {
|
|
244
|
+
if (out.some((w) => w.channel === entry && w.workerId === state.workerId)) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const supervisorPid = readPid(workerFile(entry, state.workerId, "pid", project));
|
|
248
|
+
if (supervisorPid === undefined || !pidAlive(supervisorPid))
|
|
249
|
+
continue;
|
|
250
|
+
const supervisorVerified = opts.isSupervisorProcess
|
|
251
|
+
? opts.isSupervisorProcess(supervisorPid, entry, state.workerId)
|
|
252
|
+
: isSupervisorProcess(supervisorPid, entry, state.workerId);
|
|
253
|
+
out.push({
|
|
254
|
+
channel: entry,
|
|
255
|
+
workerId: state.workerId,
|
|
256
|
+
state,
|
|
257
|
+
supervisorPid,
|
|
258
|
+
supervisorVerified,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return out;
|
|
263
|
+
}
|
|
264
|
+
function readReservationWorkers(channel, project) {
|
|
265
|
+
const dir = path.join(projectDir(project), channel);
|
|
266
|
+
let files;
|
|
267
|
+
try {
|
|
268
|
+
files = fs.readdirSync(dir);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
const workers = [];
|
|
274
|
+
for (const file of files) {
|
|
275
|
+
if (!file.endsWith(".reservation"))
|
|
276
|
+
continue;
|
|
277
|
+
const worker = file.slice(0, -".reservation".length);
|
|
278
|
+
workers.push({
|
|
279
|
+
workerId: worker,
|
|
280
|
+
lifecycle: "starting",
|
|
281
|
+
terminal: false,
|
|
282
|
+
activity: "idle",
|
|
283
|
+
pendingMessageCount: 0,
|
|
284
|
+
inboxPolicy: "explicitOnly",
|
|
285
|
+
updatedAt: new Date(0).toISOString(),
|
|
286
|
+
lastSeq: 0,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
return workers;
|
|
290
|
+
}
|
|
291
|
+
function readFileEventsSync(file) {
|
|
292
|
+
const text = fs.readFileSync(file, "utf-8");
|
|
293
|
+
const events = [];
|
|
294
|
+
for (const line of text.split("\n")) {
|
|
295
|
+
if (!line.trim())
|
|
296
|
+
continue;
|
|
297
|
+
try {
|
|
298
|
+
events.push(JSON.parse(line));
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return events;
|
|
305
|
+
}
|
|
306
|
+
function readPid(p) {
|
|
307
|
+
try {
|
|
308
|
+
const n = Number(fs.readFileSync(p, "utf-8").trim());
|
|
309
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function pidAlive(pid) {
|
|
316
|
+
try {
|
|
317
|
+
process.kill(pid, 0);
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
export function isSupervisorProcess(pid, channel, worker) {
|
|
325
|
+
if (process.platform === "win32")
|
|
326
|
+
return false;
|
|
327
|
+
try {
|
|
328
|
+
const command = execFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
329
|
+
encoding: "utf-8",
|
|
330
|
+
timeout: 1000,
|
|
331
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
332
|
+
}).trim();
|
|
333
|
+
const pattern = new RegExp([
|
|
334
|
+
"(?:^|\\s)channel\\s+__supervisor\\s+",
|
|
335
|
+
escapeRegExp(channel),
|
|
336
|
+
"\\s+",
|
|
337
|
+
escapeRegExp(worker),
|
|
338
|
+
"(?:\\s|$)",
|
|
339
|
+
].join(""));
|
|
340
|
+
return pattern.test(command);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function escapeRegExp(s) {
|
|
347
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Pure predicate: is this live worker eligible for idle-cleanup right
|
|
351
|
+
* now? Mid-turn workers and workers without `idleSince` (e.g. they
|
|
352
|
+
* never spawned cleanly) are never killed by the guard.
|
|
353
|
+
*/
|
|
354
|
+
export function isIdleCleanupEligible(live, idleTimeoutMs, now) {
|
|
355
|
+
if (idleTimeoutMs <= 0)
|
|
356
|
+
return false;
|
|
357
|
+
const { state } = live;
|
|
358
|
+
if (state.activity !== "idle")
|
|
359
|
+
return false;
|
|
360
|
+
if (!state.idleSince)
|
|
361
|
+
return false;
|
|
362
|
+
if (state.terminal)
|
|
363
|
+
return false;
|
|
364
|
+
const idleSinceMs = Date.parse(state.idleSince);
|
|
365
|
+
if (!Number.isFinite(idleSinceMs))
|
|
366
|
+
return false;
|
|
367
|
+
return now - idleSinceMs >= idleTimeoutMs;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Kill workers whose idle TTL has expired. Writes a one-shot shutdown
|
|
371
|
+
* reason sidecar before signalling the supervisor so the supervisor's
|
|
372
|
+
* existing shutdown funnel emits the single terminal `killed` event.
|
|
373
|
+
*
|
|
374
|
+
* Returns the workers that were signalled. Failures are collected — a
|
|
375
|
+
* dead pid or read race is not fatal; the next scan re-evaluates.
|
|
376
|
+
*/
|
|
377
|
+
export async function cleanupExpiredIdleWorkers(candidates, idleTimeoutMs, opts = {}) {
|
|
378
|
+
const result = { killed: [], failed: [] };
|
|
379
|
+
if (idleTimeoutMs <= 0)
|
|
380
|
+
return result;
|
|
381
|
+
const now = opts.now ?? Date.now();
|
|
382
|
+
for (const live of candidates) {
|
|
383
|
+
if (!isIdleCleanupEligible(live, idleTimeoutMs, now))
|
|
384
|
+
continue;
|
|
385
|
+
try {
|
|
386
|
+
const project = opts.project ?? currentProjectKey();
|
|
387
|
+
if (live.supervisorPid === undefined ||
|
|
388
|
+
live.supervisorVerified !== true ||
|
|
389
|
+
!pidAlive(live.supervisorPid)) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
const reasonFile = workerFile(live.channel, live.workerId, "shutdown-reason", project);
|
|
393
|
+
fs.writeFileSync(reasonFile, "idle-timeout\n", "utf-8");
|
|
394
|
+
try {
|
|
395
|
+
process.kill(live.supervisorPid, "SIGTERM");
|
|
396
|
+
}
|
|
397
|
+
catch (err) {
|
|
398
|
+
try {
|
|
399
|
+
fs.unlinkSync(reasonFile);
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
// already gone
|
|
403
|
+
}
|
|
404
|
+
throw err;
|
|
405
|
+
}
|
|
406
|
+
result.killed.push(live);
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
result.failed.push({
|
|
410
|
+
worker: live,
|
|
411
|
+
error: err instanceof Error ? err.message : String(err),
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return result;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Run the spawn-time guard for a project bucket. Cleans expired idle
|
|
419
|
+
* workers, re-scans, then decides whether the live worker budget has
|
|
420
|
+
* room for one more spawn.
|
|
421
|
+
*/
|
|
422
|
+
export async function enforceSpawnBudget(input) {
|
|
423
|
+
const project = input.projectKey ?? currentProjectKey();
|
|
424
|
+
const scanOpts = {
|
|
425
|
+
projectKey: project,
|
|
426
|
+
...(input.root !== undefined ? { root: input.root } : {}),
|
|
427
|
+
...(input.isSupervisorProcess !== undefined
|
|
428
|
+
? { isSupervisorProcess: input.isSupervisorProcess }
|
|
429
|
+
: {}),
|
|
430
|
+
};
|
|
431
|
+
const initial = scanLiveWorkers(scanOpts);
|
|
432
|
+
const cleanup = await cleanupExpiredIdleWorkers(initial, input.policy.idleTimeoutMs, { project, ...(input.now !== undefined ? { now: input.now } : {}) });
|
|
433
|
+
// Re-probe after cleanup so we don't double-count workers that have
|
|
434
|
+
// been signalled but haven't actually torn down their pid files yet.
|
|
435
|
+
// Wait briefly for the SIGTERM to translate into pid-file removal; if
|
|
436
|
+
// a worker is taking its grace period, just exclude killed workers
|
|
437
|
+
// from the count.
|
|
438
|
+
const killedIds = new Set(cleanup.killed.map((w) => `${w.channel}::${w.workerId}`));
|
|
439
|
+
const remaining = scanLiveWorkers(scanOpts).filter((w) => !killedIds.has(`${w.channel}::${w.workerId}`));
|
|
440
|
+
const allowed = input.policy.maxLiveWorkers <= 0 ||
|
|
441
|
+
remaining.length < input.policy.maxLiveWorkers;
|
|
442
|
+
return { cleaned: cleanup.killed, remaining, allowed };
|
|
443
|
+
}
|
|
444
|
+
/** Build a multi-line, actionable overflow error string. */
|
|
445
|
+
export function formatBudgetOverflowError(args) {
|
|
446
|
+
const { projectKey, live, limit } = args;
|
|
447
|
+
const header = `Live worker budget exhausted for project '${projectKey}': ${live.length}/${limit} live worker(s).`;
|
|
448
|
+
const rows = live
|
|
449
|
+
.map((w) => {
|
|
450
|
+
const provider = w.state.provider ?? "?";
|
|
451
|
+
const lifecycle = w.state.lifecycle;
|
|
452
|
+
const activity = w.state.activity;
|
|
453
|
+
const pid = w.supervisorPid ?? "?";
|
|
454
|
+
const verified = w.supervisorVerified === false ? " supervisor=unverified" : "";
|
|
455
|
+
return ` • channel='${w.channel}' worker='${w.workerId}' provider=${provider} lifecycle=${lifecycle} activity=${activity} pid=${pid}${verified}`;
|
|
456
|
+
})
|
|
457
|
+
.join("\n");
|
|
458
|
+
const hint = [
|
|
459
|
+
"Free a slot before spawning, e.g.:",
|
|
460
|
+
` trellis channel kill <channel> --as <worker>`,
|
|
461
|
+
"Or override per spawn:",
|
|
462
|
+
` trellis channel spawn ... --max-live-workers ${live.length + 1}`,
|
|
463
|
+
"Or raise the default in .trellis/config.yaml under channel.worker_guard.max_live_workers.",
|
|
464
|
+
].join("\n");
|
|
465
|
+
return [header, rows, hint].join("\n");
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Convenience helper: ensure `channelRoot()` is initialised before scan
|
|
469
|
+
* (otherwise the project dir may not yet exist on first spawn).
|
|
470
|
+
*/
|
|
471
|
+
export function ensureRootExists() {
|
|
472
|
+
fs.mkdirSync(channelRoot(), { recursive: true });
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../../src/commands/channel/guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/channel/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/channel/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAuC/D,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAo4B7D"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
+
import { InvalidArgumentError } from "commander";
|
|
2
3
|
import { isProvider, listProviders } from "./adapters/index.js";
|
|
3
4
|
import { channelContextAdd, channelContextDelete, channelContextList, } from "./context.js";
|
|
4
5
|
import { createChannel } from "./create.js";
|
|
5
6
|
import { parseTrace } from "./dev-parse-trace.js";
|
|
6
7
|
import { channelKill } from "./kill.js";
|
|
8
|
+
import { channelInterrupt } from "./interrupt.js";
|
|
7
9
|
import { channelList } from "./list.js";
|
|
8
10
|
import { channelMessages } from "./messages.js";
|
|
9
11
|
import { channelPrune, channelRm } from "./rm.js";
|
|
@@ -16,6 +18,12 @@ import { runSupervisor } from "./supervisor.js";
|
|
|
16
18
|
import { channelWait, parseDuration } from "./wait.js";
|
|
17
19
|
import { parseCsv } from "./store/schema.js";
|
|
18
20
|
import { parseInboxPolicy } from "@mindfoldhq/trellis-core/channel";
|
|
21
|
+
function parseNonNegativeInteger(value) {
|
|
22
|
+
if (!/^\d+$/.test(value)) {
|
|
23
|
+
throw new InvalidArgumentError(`expected a non-negative integer, got '${value}'`);
|
|
24
|
+
}
|
|
25
|
+
return Number(value);
|
|
26
|
+
}
|
|
19
27
|
export function registerChannelCommand(program) {
|
|
20
28
|
const channel = program
|
|
21
29
|
.command("channel")
|
|
@@ -51,8 +59,6 @@ export function registerChannelCommand(program) {
|
|
|
51
59
|
.description("Send a message into the channel")
|
|
52
60
|
.requiredOption("--as <agent>", "agent name sending")
|
|
53
61
|
.option("--scope <scope>", "channel scope: project | global")
|
|
54
|
-
.option("--tag <tag>", "tag (e.g. interrupt / phase_done / question)")
|
|
55
|
-
.option("--kind <tag>", "legacy alias for --tag")
|
|
56
62
|
.option("--to <agents>", "comma-separated target agents (default: broadcast)")
|
|
57
63
|
.option("--stdin", "read message body from stdin")
|
|
58
64
|
.option("--text-file <path>", "read message body from file")
|
|
@@ -67,8 +73,6 @@ export function registerChannelCommand(program) {
|
|
|
67
73
|
stdin: opts.stdin,
|
|
68
74
|
textFile: opts.textFile,
|
|
69
75
|
scope: opts.scope,
|
|
70
|
-
tag: opts.tag,
|
|
71
|
-
kind: opts.kind,
|
|
72
76
|
to: opts.to,
|
|
73
77
|
deliveryMode: opts.deliveryMode,
|
|
74
78
|
});
|
|
@@ -86,7 +90,6 @@ export function registerChannelCommand(program) {
|
|
|
86
90
|
.option("--timeout <duration>", "max wait (e.g. 30s, 2m, 1h)")
|
|
87
91
|
.option("--from <agents>", "only wake on events from these agents (CSV)")
|
|
88
92
|
.option("--kind <kind[,kind...]>", "only wake on these event kinds (CSV, OR semantics)")
|
|
89
|
-
.option("--tag <tag>", "only wake on this user tag")
|
|
90
93
|
.option("--thread <key>", "only wake on this thread key")
|
|
91
94
|
.option("--action <action>", "only wake on this thread action")
|
|
92
95
|
.option("--to <target>", "only wake on events targeted to this name (default: own agent)")
|
|
@@ -100,7 +103,6 @@ export function registerChannelCommand(program) {
|
|
|
100
103
|
timeoutMs: parseDuration(opts.timeout),
|
|
101
104
|
from: opts.from,
|
|
102
105
|
kind: opts.kind,
|
|
103
|
-
tag: opts.tag,
|
|
104
106
|
scope: opts.scope,
|
|
105
107
|
thread: opts.thread,
|
|
106
108
|
action: opts.action,
|
|
@@ -114,6 +116,32 @@ export function registerChannelCommand(program) {
|
|
|
114
116
|
process.exit(1);
|
|
115
117
|
}
|
|
116
118
|
});
|
|
119
|
+
channel
|
|
120
|
+
.command("interrupt <name>")
|
|
121
|
+
.description("Interrupt a worker turn and send a replacement instruction")
|
|
122
|
+
.requiredOption("--as <agent>", "agent name requesting the interrupt")
|
|
123
|
+
.requiredOption("--to <agent>", "target worker name")
|
|
124
|
+
.option("--scope <scope>", "channel scope: project | global")
|
|
125
|
+
.option("--stdin", "read interrupt message body from stdin")
|
|
126
|
+
.option("--text-file <path>", "read interrupt message body from file")
|
|
127
|
+
.argument("[text]", "inline interrupt message (otherwise use --stdin / --text-file)")
|
|
128
|
+
.action(async (name, text, raw) => {
|
|
129
|
+
const opts = raw;
|
|
130
|
+
try {
|
|
131
|
+
await channelInterrupt(name, {
|
|
132
|
+
as: opts.as,
|
|
133
|
+
to: opts.to,
|
|
134
|
+
text,
|
|
135
|
+
stdin: opts.stdin,
|
|
136
|
+
textFile: opts.textFile,
|
|
137
|
+
scope: opts.scope,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
117
145
|
channel
|
|
118
146
|
.command("spawn <name>")
|
|
119
147
|
.description("Register a worker (claude/codex) into the channel — the worker stays idle until the first `channel send --to <worker>` arrives")
|
|
@@ -130,6 +158,8 @@ export function registerChannelCommand(program) {
|
|
|
130
158
|
.option("--jsonl <path>", "parse a Trellis jsonl manifest ({file, reason} per line) and include each referenced file (repeatable)", (val, prev) => [...(prev ?? []), val], [])
|
|
131
159
|
.option("--by <agent>", "identity recorded as the spawn author (defaults to TRELLIS_CHANNEL_AS env or 'main')")
|
|
132
160
|
.option("--inbox-policy <policy>", "worker inbox delivery policy: explicitOnly | broadcastAndExplicit (default explicitOnly)")
|
|
161
|
+
.option("--idle-timeout <duration>", "OOM-guard idle-cleanup TTL for this worker (default 5m; 0 disables)")
|
|
162
|
+
.option("--max-live-workers <n>", "spawn-time live-worker budget for this project/scope (default 6; 0 disables)", parseNonNegativeInteger)
|
|
133
163
|
.action(async (name, raw) => {
|
|
134
164
|
const opts = raw;
|
|
135
165
|
if (opts.provider !== undefined && !isProvider(opts.provider)) {
|
|
@@ -151,6 +181,8 @@ export function registerChannelCommand(program) {
|
|
|
151
181
|
by: opts.by,
|
|
152
182
|
scope: opts.scope,
|
|
153
183
|
inboxPolicy: parseInboxPolicy(opts.inboxPolicy),
|
|
184
|
+
idleTimeoutMs: parseDuration(opts.idleTimeout),
|
|
185
|
+
maxLiveWorkers: opts.maxLiveWorkers,
|
|
154
186
|
});
|
|
155
187
|
}
|
|
156
188
|
catch (err) {
|
|
@@ -171,7 +203,6 @@ export function registerChannelCommand(program) {
|
|
|
171
203
|
.option("--message <text>", "inline prompt text")
|
|
172
204
|
.option("--message-file <path>", "read prompt body from file")
|
|
173
205
|
.option("--stdin", "read prompt body from stdin")
|
|
174
|
-
.option("--tag <tag>", "user tag (e.g. interrupt / phase_done / question)")
|
|
175
206
|
.option("--timeout <duration>", "max time to wait for done (e.g. 30s, 5m, 1h; default 5m)")
|
|
176
207
|
.action(async (name, raw) => {
|
|
177
208
|
const opts = raw;
|
|
@@ -192,7 +223,6 @@ export function registerChannelCommand(program) {
|
|
|
192
223
|
message: opts.message,
|
|
193
224
|
textFile: opts.messageFile,
|
|
194
225
|
stdin: opts.stdin,
|
|
195
|
-
tag: opts.tag,
|
|
196
226
|
timeoutMs: parseDuration(opts.timeout),
|
|
197
227
|
});
|
|
198
228
|
}
|
|
@@ -273,7 +303,6 @@ export function registerChannelCommand(program) {
|
|
|
273
303
|
.option("--kind <kind>", "filter by event kind (e.g. message, done, killed)")
|
|
274
304
|
.option("--from <agents>", "filter by author (CSV)")
|
|
275
305
|
.option("--to <target>", "filter by routing target")
|
|
276
|
-
.option("--tag <tag>", "filter by user tag (e.g. interrupt, final_answer)")
|
|
277
306
|
.option("--thread <key>", "filter by thread key")
|
|
278
307
|
.option("--action <action>", "filter by thread action")
|
|
279
308
|
.option("--no-progress", "hide progress events (tool calls, deltas)")
|
|
@@ -288,7 +317,6 @@ export function registerChannelCommand(program) {
|
|
|
288
317
|
kind: opts.kind,
|
|
289
318
|
from: opts.from,
|
|
290
319
|
to: opts.to,
|
|
291
|
-
tag: opts.tag,
|
|
292
320
|
scope: opts.scope,
|
|
293
321
|
thread: opts.thread,
|
|
294
322
|
action: opts.action,
|