@schilderlabs/pitown-core 0.2.1 → 0.2.7
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/agent-runner.d.mts +1 -0
- package/dist/agent-runner.mjs +236 -0
- package/dist/agent-runner.mjs.map +1 -0
- package/dist/index.d.mts +101 -23
- package/dist/index.mjs +248 -551
- package/dist/index.mjs.map +1 -1
- package/dist/orchestration-D6XeSl8s.mjs +689 -0
- package/dist/orchestration-D6XeSl8s.mjs.map +1 -0
- package/package.json +14 -19
package/dist/index.mjs
CHANGED
|
@@ -1,145 +1,14 @@
|
|
|
1
|
+
import { A as getLatestAgentSession, C as createAgentState, D as getAgentSessionsDir, E as getAgentSessionPath, F as writeAgentState, I as appendJsonl, L as readJsonl, M as listAgentStates, N as readAgentMessages, O as getAgentStatePath, P as readAgentState, S as createAgentSessionRecord, T as getAgentMailboxPath, _ as assertCommandAvailable, a as resolveAgentSession, b as runCommandSync, c as spawnAgentRun, d as getTaskPath, f as getTasksDir, g as writeTaskRecord, h as updateTaskRecordStatus, i as queueAgentMessage, j as getSessionIdFromPath, k as getAgentsDir, l as updateAgentStatus, m as readTaskRecord, n as delegateTask, o as resumeAgentTurnDetached, p as listTaskRecords, r as notifyTaskDelegator, s as runAgentTurn, t as createRolePrompt, u as createTaskRecord, v as assertSuccess, w as getAgentDir, x as appendAgentMessage, y as runCommandInteractive } from "./orchestration-D6XeSl8s.mjs";
|
|
1
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { basename,
|
|
3
|
+
import { basename, join, resolve } from "node:path";
|
|
3
4
|
import { homedir, hostname } from "node:os";
|
|
4
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
6
|
|
|
7
|
-
//#region src/events.ts
|
|
8
|
-
function appendJsonl(filePath, value) {
|
|
9
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
10
|
-
writeFileSync(filePath, `${JSON.stringify(value)}\n`, {
|
|
11
|
-
encoding: "utf-8",
|
|
12
|
-
flag: "a"
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
function readJsonl(filePath) {
|
|
16
|
-
try {
|
|
17
|
-
return readFileSync(filePath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
18
|
-
} catch {
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/agents.ts
|
|
25
|
-
function writeJson$3(path, value) {
|
|
26
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
27
|
-
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
28
|
-
}
|
|
29
|
-
function ensureMailbox(path) {
|
|
30
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
31
|
-
if (!existsSync(path)) writeFileSync(path, "", "utf-8");
|
|
32
|
-
}
|
|
33
|
-
function getAgentsDir(artifactsDir) {
|
|
34
|
-
return join(artifactsDir, "agents");
|
|
35
|
-
}
|
|
36
|
-
function getAgentDir(artifactsDir, agentId) {
|
|
37
|
-
return join(getAgentsDir(artifactsDir), agentId);
|
|
38
|
-
}
|
|
39
|
-
function getAgentStatePath(artifactsDir, agentId) {
|
|
40
|
-
return join(getAgentDir(artifactsDir, agentId), "state.json");
|
|
41
|
-
}
|
|
42
|
-
function getAgentSessionPath(artifactsDir, agentId) {
|
|
43
|
-
return join(getAgentDir(artifactsDir, agentId), "session.json");
|
|
44
|
-
}
|
|
45
|
-
function getAgentMailboxPath(artifactsDir, agentId, box) {
|
|
46
|
-
return join(getAgentDir(artifactsDir, agentId), `${box}.jsonl`);
|
|
47
|
-
}
|
|
48
|
-
function getAgentSessionsDir(artifactsDir, agentId) {
|
|
49
|
-
return join(getAgentDir(artifactsDir, agentId), "sessions");
|
|
50
|
-
}
|
|
51
|
-
function getSessionIdFromPath(sessionPath) {
|
|
52
|
-
if (!sessionPath) return null;
|
|
53
|
-
return /_([0-9a-f-]+)\.jsonl$/i.exec(sessionPath)?.[1] ?? null;
|
|
54
|
-
}
|
|
55
|
-
function createAgentSessionRecord(input) {
|
|
56
|
-
return {
|
|
57
|
-
runtime: "pi",
|
|
58
|
-
persisted: true,
|
|
59
|
-
sessionDir: input?.sessionDir ?? null,
|
|
60
|
-
sessionId: input?.sessionId ?? null,
|
|
61
|
-
sessionPath: input?.sessionPath ?? null,
|
|
62
|
-
lastAttachedAt: input?.lastAttachedAt ?? null
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
function createAgentState(input) {
|
|
66
|
-
return {
|
|
67
|
-
agentId: input.agentId,
|
|
68
|
-
role: input.role,
|
|
69
|
-
status: input.status,
|
|
70
|
-
taskId: input.taskId ?? null,
|
|
71
|
-
task: input.task ?? null,
|
|
72
|
-
branch: input.branch ?? null,
|
|
73
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
74
|
-
lastMessage: input.lastMessage ?? null,
|
|
75
|
-
waitingOn: input.waitingOn ?? null,
|
|
76
|
-
blocked: input.blocked ?? false,
|
|
77
|
-
runId: input.runId ?? null,
|
|
78
|
-
session: input.session ?? createAgentSessionRecord()
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function writeAgentState(artifactsDir, state) {
|
|
82
|
-
mkdirSync(getAgentDir(artifactsDir, state.agentId), { recursive: true });
|
|
83
|
-
ensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, "inbox"));
|
|
84
|
-
ensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, "outbox"));
|
|
85
|
-
writeJson$3(getAgentStatePath(artifactsDir, state.agentId), state);
|
|
86
|
-
writeJson$3(getAgentSessionPath(artifactsDir, state.agentId), state.session);
|
|
87
|
-
}
|
|
88
|
-
function readAgentState(artifactsDir, agentId) {
|
|
89
|
-
const statePath = getAgentStatePath(artifactsDir, agentId);
|
|
90
|
-
try {
|
|
91
|
-
return JSON.parse(readFileSync(statePath, "utf-8"));
|
|
92
|
-
} catch {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function listAgentStates(artifactsDir) {
|
|
97
|
-
const agentsDir = getAgentsDir(artifactsDir);
|
|
98
|
-
let entries;
|
|
99
|
-
try {
|
|
100
|
-
entries = readdirSync(agentsDir);
|
|
101
|
-
} catch {
|
|
102
|
-
return [];
|
|
103
|
-
}
|
|
104
|
-
return entries.map((entry) => readAgentState(artifactsDir, entry)).filter((state) => state !== null).sort((left, right) => left.agentId.localeCompare(right.agentId));
|
|
105
|
-
}
|
|
106
|
-
function appendAgentMessage(input) {
|
|
107
|
-
const record = {
|
|
108
|
-
box: input.box,
|
|
109
|
-
from: input.from,
|
|
110
|
-
body: input.body,
|
|
111
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
112
|
-
};
|
|
113
|
-
appendJsonl(getAgentMailboxPath(input.artifactsDir, input.agentId, input.box), record);
|
|
114
|
-
return record;
|
|
115
|
-
}
|
|
116
|
-
function readAgentMessages(artifactsDir, agentId, box) {
|
|
117
|
-
return readJsonl(getAgentMailboxPath(artifactsDir, agentId, box));
|
|
118
|
-
}
|
|
119
|
-
function getLatestAgentSession(artifactsDir, agentId) {
|
|
120
|
-
const sessionDir = getAgentSessionsDir(artifactsDir, agentId);
|
|
121
|
-
let entries;
|
|
122
|
-
try {
|
|
123
|
-
entries = readdirSync(sessionDir);
|
|
124
|
-
} catch {
|
|
125
|
-
return createAgentSessionRecord({ sessionDir });
|
|
126
|
-
}
|
|
127
|
-
const latestSessionPath = entries.filter((entry) => entry.endsWith(".jsonl")).sort().at(-1) ?? null;
|
|
128
|
-
if (latestSessionPath === null) return createAgentSessionRecord({ sessionDir });
|
|
129
|
-
const sessionPath = join(sessionDir, latestSessionPath);
|
|
130
|
-
return createAgentSessionRecord({
|
|
131
|
-
sessionDir,
|
|
132
|
-
sessionPath,
|
|
133
|
-
sessionId: getSessionIdFromPath(sessionPath)
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
//#endregion
|
|
138
7
|
//#region src/lease.ts
|
|
139
8
|
function sanitize$1(value) {
|
|
140
9
|
return value.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
141
10
|
}
|
|
142
|
-
function processAlive(pid) {
|
|
11
|
+
function processAlive$1(pid) {
|
|
143
12
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
144
13
|
try {
|
|
145
14
|
process.kill(pid, 0);
|
|
@@ -162,7 +31,7 @@ function acquireRepoLease(runId, repoId, branch) {
|
|
|
162
31
|
};
|
|
163
32
|
try {
|
|
164
33
|
const current = JSON.parse(readFileSync(leasePath, "utf-8"));
|
|
165
|
-
if (processAlive(current.pid)) throw new Error(`Pi Town lease already held by pid ${current.pid} on ${current.hostname} for run ${current.runId}.`);
|
|
34
|
+
if (processAlive$1(current.pid)) throw new Error(`Pi Town lease already held by pid ${current.pid} on ${current.hostname} for run ${current.runId}.`);
|
|
166
35
|
rmSync(leasePath, { force: true });
|
|
167
36
|
} catch (error) {
|
|
168
37
|
if (error.code !== "ENOENT") {
|
|
@@ -244,44 +113,6 @@ function createPiAuthHelpMessage() {
|
|
|
244
113
|
return "Pi appears to be installed but not authenticated or configured. Verify Pi works first: pi -p \"hello\". Either set an API key or run `pi` and use `/login`.";
|
|
245
114
|
}
|
|
246
115
|
|
|
247
|
-
//#endregion
|
|
248
|
-
//#region src/shell.ts
|
|
249
|
-
function runCommandSync(command, args, options) {
|
|
250
|
-
const result = spawnSync(command, args, {
|
|
251
|
-
cwd: options?.cwd,
|
|
252
|
-
env: options?.env,
|
|
253
|
-
encoding: "utf-8"
|
|
254
|
-
});
|
|
255
|
-
const errorText = result.error instanceof Error ? `${result.error.message}
|
|
256
|
-
` : "";
|
|
257
|
-
return {
|
|
258
|
-
stdout: result.stdout ?? "",
|
|
259
|
-
stderr: `${errorText}${result.stderr ?? ""}`,
|
|
260
|
-
exitCode: result.status ?? 1
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
function assertCommandAvailable(command) {
|
|
264
|
-
const result = spawnSync(command, ["--help"], {
|
|
265
|
-
encoding: "utf-8",
|
|
266
|
-
stdio: "ignore"
|
|
267
|
-
});
|
|
268
|
-
if (result.error instanceof Error) throw new Error(result.error.message);
|
|
269
|
-
}
|
|
270
|
-
function runCommandInteractive(command, args, options) {
|
|
271
|
-
const result = spawnSync(command, args, {
|
|
272
|
-
cwd: options?.cwd,
|
|
273
|
-
env: options?.env,
|
|
274
|
-
stdio: "inherit"
|
|
275
|
-
});
|
|
276
|
-
if (result.error instanceof Error) throw new Error(result.error.message);
|
|
277
|
-
return result.status ?? 1;
|
|
278
|
-
}
|
|
279
|
-
function assertSuccess(result, context) {
|
|
280
|
-
if (result.exitCode === 0) return;
|
|
281
|
-
const details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
|
|
282
|
-
throw new Error(`${context} failed${details ? `\n${details}` : ""}`);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
116
|
//#endregion
|
|
286
117
|
//#region src/repo.ts
|
|
287
118
|
function gitResult(cwd, args) {
|
|
@@ -336,7 +167,7 @@ function createRepoSlug(repoId, repoRoot) {
|
|
|
336
167
|
function createRunId() {
|
|
337
168
|
return `run-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
338
169
|
}
|
|
339
|
-
function createPiInvocationArgs
|
|
170
|
+
function createPiInvocationArgs(input) {
|
|
340
171
|
const args = [];
|
|
341
172
|
if (input.extensionPath) args.push("--extension", input.extensionPath);
|
|
342
173
|
if (input.appendedSystemPrompt) args.push("--append-system-prompt", input.appendedSystemPrompt);
|
|
@@ -346,7 +177,7 @@ function createPiInvocationArgs$1(input) {
|
|
|
346
177
|
args.push("-p", input.prompt);
|
|
347
178
|
return args;
|
|
348
179
|
}
|
|
349
|
-
function writeJson$
|
|
180
|
+
function writeJson$1(path, value) {
|
|
350
181
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
351
182
|
}
|
|
352
183
|
function writeText(path, value) {
|
|
@@ -355,7 +186,7 @@ function writeText(path, value) {
|
|
|
355
186
|
function createPiPrompt(input) {
|
|
356
187
|
const goal = input.goal ?? "continue from current scaffold state";
|
|
357
188
|
if (input.planPath) return [
|
|
358
|
-
"You are the Pi Town
|
|
189
|
+
"You are the Pi Town mayor agent for this repository.",
|
|
359
190
|
"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.",
|
|
360
191
|
"",
|
|
361
192
|
"Read the private plans in:",
|
|
@@ -369,7 +200,7 @@ function createPiPrompt(input) {
|
|
|
369
200
|
"Keep any persisted run artifacts high-signal and avoid copying private plan contents into them."
|
|
370
201
|
].join("\n");
|
|
371
202
|
return [
|
|
372
|
-
"You are the Pi Town
|
|
203
|
+
"You are the Pi Town mayor agent for this repository.",
|
|
373
204
|
"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.",
|
|
374
205
|
"",
|
|
375
206
|
`Work in the repository at: ${input.repoRoot}`,
|
|
@@ -455,21 +286,21 @@ function runController(options) {
|
|
|
455
286
|
goal,
|
|
456
287
|
recommendedPlanDir
|
|
457
288
|
});
|
|
458
|
-
const
|
|
459
|
-
const
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
agentId: "
|
|
463
|
-
role: "
|
|
289
|
+
const existingMayorState = readAgentState(artifactsDir, "mayor");
|
|
290
|
+
const existingMayorSession = existingMayorState?.session.sessionPath || existingMayorState?.session.sessionDir ? existingMayorState.session : getLatestAgentSession(artifactsDir, "mayor");
|
|
291
|
+
const mayorSessionDir = existingMayorSession.sessionDir ?? getAgentSessionsDir(artifactsDir, "mayor");
|
|
292
|
+
const mayorState = createAgentState({
|
|
293
|
+
agentId: "mayor",
|
|
294
|
+
role: "mayor",
|
|
464
295
|
status: "starting",
|
|
465
296
|
task: goal,
|
|
466
297
|
branch,
|
|
467
|
-
lastMessage: goal ? `Starting
|
|
298
|
+
lastMessage: goal ? `Starting mayor run for goal: ${goal}` : "Starting mayor run",
|
|
468
299
|
runId,
|
|
469
300
|
session: createAgentSessionRecord({
|
|
470
|
-
sessionDir:
|
|
471
|
-
sessionId:
|
|
472
|
-
sessionPath:
|
|
301
|
+
sessionDir: mayorSessionDir,
|
|
302
|
+
sessionId: existingMayorSession.sessionId,
|
|
303
|
+
sessionPath: existingMayorSession.sessionPath
|
|
473
304
|
})
|
|
474
305
|
});
|
|
475
306
|
assertPiRuntimeAvailable(piCommand);
|
|
@@ -477,11 +308,11 @@ function runController(options) {
|
|
|
477
308
|
mkdirSync(latestDir, { recursive: true });
|
|
478
309
|
writeText(join(runDir, "questions.jsonl"), "");
|
|
479
310
|
writeText(join(runDir, "interventions.jsonl"), "");
|
|
480
|
-
writeJson$
|
|
311
|
+
writeJson$1(join(runDir, "agent-state.json"), {
|
|
481
312
|
status: "starting",
|
|
482
313
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
483
314
|
});
|
|
484
|
-
writeAgentState(artifactsDir,
|
|
315
|
+
writeAgentState(artifactsDir, mayorState);
|
|
485
316
|
const lease = acquireRepoLease(runId, repoId, branch);
|
|
486
317
|
try {
|
|
487
318
|
const manifest = createManifest({
|
|
@@ -512,13 +343,13 @@ function runController(options) {
|
|
|
512
343
|
createdAt: piStartedAt
|
|
513
344
|
});
|
|
514
345
|
writeAgentState(artifactsDir, createAgentState({
|
|
515
|
-
...
|
|
346
|
+
...mayorState,
|
|
516
347
|
status: "running",
|
|
517
|
-
lastMessage: goal ? `
|
|
348
|
+
lastMessage: goal ? `Mayor working on: ${goal}` : "Mayor working"
|
|
518
349
|
}));
|
|
519
|
-
const piResult = runCommandSync(piCommand, createPiInvocationArgs
|
|
520
|
-
sessionDir:
|
|
521
|
-
sessionPath:
|
|
350
|
+
const piResult = runCommandSync(piCommand, createPiInvocationArgs({
|
|
351
|
+
sessionDir: mayorState.session.sessionPath === null ? mayorSessionDir : null,
|
|
352
|
+
sessionPath: mayorState.session.sessionPath,
|
|
522
353
|
prompt,
|
|
523
354
|
appendedSystemPrompt: options.appendedSystemPrompt,
|
|
524
355
|
extensionPath: options.extensionPath
|
|
@@ -527,7 +358,7 @@ function runController(options) {
|
|
|
527
358
|
env: process.env
|
|
528
359
|
});
|
|
529
360
|
const piEndedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
530
|
-
const
|
|
361
|
+
const latestMayorSession = getLatestAgentSession(artifactsDir, "mayor");
|
|
531
362
|
writeText(stdoutPath, piResult.stdout);
|
|
532
363
|
writeText(stderrPath, piResult.stderr);
|
|
533
364
|
const piInvocation = {
|
|
@@ -536,9 +367,9 @@ function runController(options) {
|
|
|
536
367
|
repoRoot,
|
|
537
368
|
planPath,
|
|
538
369
|
goal,
|
|
539
|
-
sessionDir:
|
|
540
|
-
sessionId:
|
|
541
|
-
sessionPath:
|
|
370
|
+
sessionDir: latestMayorSession.sessionDir,
|
|
371
|
+
sessionId: latestMayorSession.sessionId,
|
|
372
|
+
sessionPath: latestMayorSession.sessionPath,
|
|
542
373
|
startedAt: piStartedAt,
|
|
543
374
|
endedAt: piEndedAt,
|
|
544
375
|
exitCode: piResult.exitCode,
|
|
@@ -546,7 +377,7 @@ function runController(options) {
|
|
|
546
377
|
stderrPath,
|
|
547
378
|
promptSummary: planPath ? "Read private plan path and continue from current scaffold state." : "Continue from current scaffold state without a configured private plan path."
|
|
548
379
|
};
|
|
549
|
-
writeJson$
|
|
380
|
+
writeJson$1(join(runDir, "pi-invocation.json"), piInvocation);
|
|
550
381
|
appendJsonl(join(runDir, "events.jsonl"), {
|
|
551
382
|
type: "pi_invocation_finished",
|
|
552
383
|
runId,
|
|
@@ -572,27 +403,27 @@ function runController(options) {
|
|
|
572
403
|
stopReason: piInvocation.exitCode === 0 ? "pi invocation completed" : `pi invocation exited with code ${piInvocation.exitCode}`,
|
|
573
404
|
piExitCode: piInvocation.exitCode
|
|
574
405
|
};
|
|
575
|
-
writeJson$
|
|
576
|
-
writeJson$
|
|
577
|
-
writeJson$
|
|
578
|
-
writeJson$
|
|
406
|
+
writeJson$1(join(runDir, "manifest.json"), finalManifest);
|
|
407
|
+
writeJson$1(join(runDir, "metrics.json"), metrics);
|
|
408
|
+
writeJson$1(join(runDir, "run-summary.json"), summary);
|
|
409
|
+
writeJson$1(join(runDir, "agent-state.json"), {
|
|
579
410
|
status: summary.success ? "completed" : "failed",
|
|
580
411
|
updatedAt: piEndedAt,
|
|
581
412
|
exitCode: piInvocation.exitCode
|
|
582
413
|
});
|
|
583
|
-
writeJson$
|
|
584
|
-
writeJson$
|
|
585
|
-
writeJson$
|
|
414
|
+
writeJson$1(join(latestDir, "manifest.json"), finalManifest);
|
|
415
|
+
writeJson$1(join(latestDir, "metrics.json"), metrics);
|
|
416
|
+
writeJson$1(join(latestDir, "run-summary.json"), summary);
|
|
586
417
|
writeAgentState(artifactsDir, createAgentState({
|
|
587
|
-
...
|
|
418
|
+
...mayorState,
|
|
588
419
|
status: piInvocation.exitCode === 0 ? "idle" : "blocked",
|
|
589
|
-
lastMessage: piInvocation.exitCode === 0 ? "
|
|
420
|
+
lastMessage: piInvocation.exitCode === 0 ? "Mayor run completed and is ready for the next instruction" : `Mayor run stopped with exit code ${piInvocation.exitCode}`,
|
|
590
421
|
blocked: piInvocation.exitCode !== 0,
|
|
591
422
|
waitingOn: piInvocation.exitCode === 0 ? null : "human-or-follow-up-run",
|
|
592
423
|
session: createAgentSessionRecord({
|
|
593
|
-
sessionDir:
|
|
594
|
-
sessionId:
|
|
595
|
-
sessionPath:
|
|
424
|
+
sessionDir: latestMayorSession.sessionDir,
|
|
425
|
+
sessionId: latestMayorSession.sessionId,
|
|
426
|
+
sessionPath: latestMayorSession.sessionPath
|
|
596
427
|
})
|
|
597
428
|
}));
|
|
598
429
|
appendJsonl(join(runDir, "events.jsonl"), {
|
|
@@ -638,74 +469,39 @@ function resolveInterrupt(interrupt, options) {
|
|
|
638
469
|
};
|
|
639
470
|
}
|
|
640
471
|
|
|
641
|
-
//#endregion
|
|
642
|
-
//#region src/tasks.ts
|
|
643
|
-
function writeJson$1(path, value) {
|
|
644
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
645
|
-
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
646
|
-
}
|
|
647
|
-
function getTasksDir(artifactsDir) {
|
|
648
|
-
return join(artifactsDir, "tasks");
|
|
649
|
-
}
|
|
650
|
-
function getTaskPath(artifactsDir, taskId) {
|
|
651
|
-
return join(getTasksDir(artifactsDir), `${taskId}.json`);
|
|
652
|
-
}
|
|
653
|
-
function createTaskRecord(input) {
|
|
654
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
655
|
-
return {
|
|
656
|
-
taskId: input.taskId,
|
|
657
|
-
title: input.title,
|
|
658
|
-
status: input.status,
|
|
659
|
-
role: input.role,
|
|
660
|
-
assignedAgentId: input.assignedAgentId,
|
|
661
|
-
createdBy: input.createdBy,
|
|
662
|
-
createdAt: now,
|
|
663
|
-
updatedAt: now
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
function writeTaskRecord(artifactsDir, task) {
|
|
667
|
-
writeJson$1(getTaskPath(artifactsDir, task.taskId), task);
|
|
668
|
-
}
|
|
669
|
-
function updateTaskRecordStatus(artifactsDir, taskId, status) {
|
|
670
|
-
const task = readTaskRecord(artifactsDir, taskId);
|
|
671
|
-
if (task === null) return null;
|
|
672
|
-
const updatedTask = {
|
|
673
|
-
...task,
|
|
674
|
-
status,
|
|
675
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
676
|
-
};
|
|
677
|
-
writeTaskRecord(artifactsDir, updatedTask);
|
|
678
|
-
return updatedTask;
|
|
679
|
-
}
|
|
680
|
-
function readTaskRecord(artifactsDir, taskId) {
|
|
681
|
-
const path = getTaskPath(artifactsDir, taskId);
|
|
682
|
-
try {
|
|
683
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
684
|
-
} catch {
|
|
685
|
-
return null;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
function listTaskRecords(artifactsDir) {
|
|
689
|
-
const tasksDir = getTasksDir(artifactsDir);
|
|
690
|
-
let entries;
|
|
691
|
-
try {
|
|
692
|
-
entries = readdirSync(tasksDir);
|
|
693
|
-
} catch {
|
|
694
|
-
return [];
|
|
695
|
-
}
|
|
696
|
-
return entries.filter((entry) => entry.endsWith(".json")).map((entry) => readTaskRecord(artifactsDir, entry.replace(/\.json$/, ""))).filter((task) => task !== null).sort((left, right) => left.taskId.localeCompare(right.taskId));
|
|
697
|
-
}
|
|
698
|
-
|
|
699
472
|
//#endregion
|
|
700
473
|
//#region src/loop.ts
|
|
701
474
|
const DEFAULT_MAX_ITERATIONS = 10;
|
|
702
475
|
const DEFAULT_MAX_WALL_TIME_MS = 36e5;
|
|
476
|
+
const DEFAULT_BACKGROUND_POLL_MS = 250;
|
|
703
477
|
function createLoopId() {
|
|
704
478
|
return `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
705
479
|
}
|
|
706
480
|
function writeJson(path, value) {
|
|
707
481
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
708
482
|
}
|
|
483
|
+
function sleepMs$1(ms) {
|
|
484
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
485
|
+
}
|
|
486
|
+
function hasBackgroundWork(board) {
|
|
487
|
+
return board.agents.some((agent) => agent.agentId !== "mayor" && (agent.status === "queued" || agent.status === "running" || agent.status === "starting")) || board.tasks.some((task) => task.status === "queued" || task.status === "running");
|
|
488
|
+
}
|
|
489
|
+
function waitForBackgroundWorkToSettle(input) {
|
|
490
|
+
const pollIntervalMs = input.pollIntervalMs ?? DEFAULT_BACKGROUND_POLL_MS;
|
|
491
|
+
let board = snapshotBoard(input.artifactsDir);
|
|
492
|
+
while (hasBackgroundWork(board)) {
|
|
493
|
+
if (Date.now() - input.loopStartedAt >= input.maxWallTimeMs) return {
|
|
494
|
+
timedOut: true,
|
|
495
|
+
board
|
|
496
|
+
};
|
|
497
|
+
sleepMs$1(pollIntervalMs);
|
|
498
|
+
board = snapshotBoard(input.artifactsDir);
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
timedOut: false,
|
|
502
|
+
board
|
|
503
|
+
};
|
|
504
|
+
}
|
|
709
505
|
function snapshotBoard(artifactsDir) {
|
|
710
506
|
const tasks = listTaskRecords(artifactsDir);
|
|
711
507
|
const agents = listAgentStates(artifactsDir);
|
|
@@ -720,8 +516,8 @@ function snapshotBoard(artifactsDir) {
|
|
|
720
516
|
blocked: agent.blocked
|
|
721
517
|
})),
|
|
722
518
|
allTasksCompleted: tasks.length > 0 && tasks.every((task) => task.status === "completed"),
|
|
723
|
-
allRemainingTasksBlocked: tasks.length > 0 && tasks.every((task) => task.status === "completed" || task.status === "blocked"),
|
|
724
|
-
|
|
519
|
+
allRemainingTasksBlocked: tasks.length > 0 && tasks.every((task) => task.status === "completed" || task.status === "blocked" || task.status === "aborted"),
|
|
520
|
+
mayorBlocked: agents.find((agent) => agent.agentId === "mayor")?.blocked === true,
|
|
725
521
|
hasQueuedOrRunningWork: agents.some((agent) => agent.status === "queued" || agent.status === "running" || agent.status === "starting") || tasks.some((task) => task.status === "queued" || task.status === "running")
|
|
726
522
|
};
|
|
727
523
|
}
|
|
@@ -742,8 +538,13 @@ function evaluateStopCondition(input) {
|
|
|
742
538
|
stopReason: "all-tasks-completed",
|
|
743
539
|
continueReason: null
|
|
744
540
|
};
|
|
745
|
-
if (input.board.
|
|
746
|
-
stopReason: "
|
|
541
|
+
if (input.board.mayorBlocked) return {
|
|
542
|
+
stopReason: "mayor-blocked",
|
|
543
|
+
continueReason: null
|
|
544
|
+
};
|
|
545
|
+
const mayor = input.board.agents.find((agent) => agent.agentId === "mayor");
|
|
546
|
+
if (input.stopOnMayorIdleNoWork && mayor && !input.board.hasQueuedOrRunningWork && input.board.tasks.length === 0 && mayor.status === "idle") return {
|
|
547
|
+
stopReason: "mayor-idle-no-work",
|
|
747
548
|
continueReason: null
|
|
748
549
|
};
|
|
749
550
|
if (input.board.allRemainingTasksBlocked) return {
|
|
@@ -757,7 +558,7 @@ function evaluateStopCondition(input) {
|
|
|
757
558
|
const reasons = [];
|
|
758
559
|
if (input.board.hasQueuedOrRunningWork) reasons.push("queued or running work remains");
|
|
759
560
|
if (input.board.tasks.length === 0) reasons.push("no tasks tracked yet");
|
|
760
|
-
if (reasons.length === 0) reasons.push("
|
|
561
|
+
if (reasons.length === 0) reasons.push("mayor idle, no stop condition met");
|
|
761
562
|
return {
|
|
762
563
|
stopReason: null,
|
|
763
564
|
continueReason: reasons.join("; ")
|
|
@@ -813,6 +614,7 @@ function runLoop(options) {
|
|
|
813
614
|
const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
814
615
|
const maxWallTimeMs = options.maxWallTimeMs ?? DEFAULT_MAX_WALL_TIME_MS;
|
|
815
616
|
const stopOnPiFailure = options.stopOnPiFailure ?? true;
|
|
617
|
+
const stopOnMayorIdleNoWork = options.stopOnMayorIdleNoWork ?? false;
|
|
816
618
|
const interruptRateThreshold = options.interruptRateThreshold ?? null;
|
|
817
619
|
const loopId = createLoopId();
|
|
818
620
|
const artifactsDir = options.runOptions.artifactsDir;
|
|
@@ -821,6 +623,7 @@ function runLoop(options) {
|
|
|
821
623
|
const loopStartedAt = Date.now();
|
|
822
624
|
const iterations = [];
|
|
823
625
|
let finalStopReason = "max-iterations-reached";
|
|
626
|
+
let needsMayorFollowUp = false;
|
|
824
627
|
appendJsonl(join(loopDir, "events.jsonl"), {
|
|
825
628
|
type: "loop_started",
|
|
826
629
|
loopId,
|
|
@@ -830,6 +633,26 @@ function runLoop(options) {
|
|
|
830
633
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
831
634
|
});
|
|
832
635
|
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
|
636
|
+
if (needsMayorFollowUp) {
|
|
637
|
+
const settled = waitForBackgroundWorkToSettle({
|
|
638
|
+
artifactsDir,
|
|
639
|
+
maxWallTimeMs,
|
|
640
|
+
loopStartedAt
|
|
641
|
+
});
|
|
642
|
+
appendJsonl(join(loopDir, "events.jsonl"), {
|
|
643
|
+
type: "loop_background_work_settled",
|
|
644
|
+
loopId,
|
|
645
|
+
iteration,
|
|
646
|
+
timedOut: settled.timedOut,
|
|
647
|
+
boardSnapshot: settled.board,
|
|
648
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
649
|
+
});
|
|
650
|
+
if (settled.timedOut) {
|
|
651
|
+
finalStopReason = "max-wall-time-reached";
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
needsMayorFollowUp = false;
|
|
655
|
+
}
|
|
833
656
|
const iterationStart = Date.now();
|
|
834
657
|
let controllerResult;
|
|
835
658
|
try {
|
|
@@ -856,6 +679,7 @@ function runLoop(options) {
|
|
|
856
679
|
maxWallTimeMs,
|
|
857
680
|
piExitCode: controllerResult.piInvocation.exitCode,
|
|
858
681
|
stopOnPiFailure,
|
|
682
|
+
stopOnMayorIdleNoWork,
|
|
859
683
|
board,
|
|
860
684
|
metrics,
|
|
861
685
|
interruptRateThreshold
|
|
@@ -894,9 +718,11 @@ function runLoop(options) {
|
|
|
894
718
|
finalStopReason = stopReason;
|
|
895
719
|
break;
|
|
896
720
|
}
|
|
721
|
+
needsMayorFollowUp = hasBackgroundWork(board);
|
|
897
722
|
}
|
|
898
723
|
const totalElapsedMs = Date.now() - loopStartedAt;
|
|
899
|
-
const
|
|
724
|
+
const lastIteration = iterations.at(-1);
|
|
725
|
+
const finalBoard = lastIteration ? lastIteration.boardSnapshot : snapshotBoard(artifactsDir);
|
|
900
726
|
const aggregate = aggregateMetrics(iterations);
|
|
901
727
|
const loopResult = {
|
|
902
728
|
loopId,
|
|
@@ -927,301 +753,172 @@ function runLoop(options) {
|
|
|
927
753
|
}
|
|
928
754
|
|
|
929
755
|
//#endregion
|
|
930
|
-
//#region src/
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
else throw new Error("Pi invocation requires a session path or session directory");
|
|
938
|
-
if (input.prompt) args.push("-p", input.prompt);
|
|
939
|
-
return args;
|
|
756
|
+
//#region src/stop.ts
|
|
757
|
+
const DEFAULT_GRACE_MS = 750;
|
|
758
|
+
function sleepMs(ms) {
|
|
759
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
760
|
+
}
|
|
761
|
+
function getLocksDir() {
|
|
762
|
+
return join(homedir(), ".pi-town", "locks");
|
|
940
763
|
}
|
|
941
|
-
function
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
`Repository: ${input.repoRoot}`,
|
|
949
|
-
`Task: ${task}`,
|
|
950
|
-
"Keep updates concise, choose bounded next steps, and leave a durable artifact trail."
|
|
951
|
-
].join("\n");
|
|
952
|
-
case "reviewer": return [
|
|
953
|
-
"You are the Pi Town reviewer.",
|
|
954
|
-
"You review work for correctness, safety, and completeness.",
|
|
955
|
-
"",
|
|
956
|
-
`Repository: ${input.repoRoot}`,
|
|
957
|
-
`Task: ${task}`,
|
|
958
|
-
"Focus on validation confidence, regressions, and whether the output is ready for a human handoff."
|
|
959
|
-
].join("\n");
|
|
960
|
-
case "docs-keeper": return [
|
|
961
|
-
"You are the Pi Town docs keeper.",
|
|
962
|
-
"You summarize outcomes, blockers, and continuity in compact factual language.",
|
|
963
|
-
"",
|
|
964
|
-
`Repository: ${input.repoRoot}`,
|
|
965
|
-
`Task: ${task}`,
|
|
966
|
-
"Keep the output concise and useful for the next run or human review."
|
|
967
|
-
].join("\n");
|
|
968
|
-
default: return [
|
|
969
|
-
"You are the Pi Town worker.",
|
|
970
|
-
"You implement one bounded task at a time.",
|
|
971
|
-
"",
|
|
972
|
-
`Repository: ${input.repoRoot}`,
|
|
973
|
-
`Task: ${task}`,
|
|
974
|
-
"Keep scope tight, prefer explicit validations, and summarize what changed and what still needs follow-up."
|
|
975
|
-
].join("\n");
|
|
764
|
+
function processAlive(pid) {
|
|
765
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
766
|
+
try {
|
|
767
|
+
process.kill(pid, 0);
|
|
768
|
+
return true;
|
|
769
|
+
} catch {
|
|
770
|
+
return false;
|
|
976
771
|
}
|
|
977
772
|
}
|
|
978
|
-
function
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
773
|
+
function terminateProcess(pid, options) {
|
|
774
|
+
if (!processAlive(pid)) return {
|
|
775
|
+
signal: null,
|
|
776
|
+
exited: true
|
|
777
|
+
};
|
|
778
|
+
try {
|
|
779
|
+
process.kill(pid, "SIGTERM");
|
|
780
|
+
} catch {
|
|
781
|
+
return {
|
|
782
|
+
signal: null,
|
|
783
|
+
exited: !processAlive(pid)
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
const graceMs = options.graceMs ?? DEFAULT_GRACE_MS;
|
|
787
|
+
const deadline = Date.now() + graceMs;
|
|
788
|
+
while (Date.now() < deadline) {
|
|
789
|
+
if (!processAlive(pid)) return {
|
|
790
|
+
signal: "SIGTERM",
|
|
791
|
+
exited: true
|
|
792
|
+
};
|
|
793
|
+
sleepMs(25);
|
|
794
|
+
}
|
|
795
|
+
if (!options.force) return {
|
|
796
|
+
signal: "SIGTERM",
|
|
797
|
+
exited: !processAlive(pid)
|
|
798
|
+
};
|
|
799
|
+
try {
|
|
800
|
+
process.kill(pid, "SIGKILL");
|
|
801
|
+
} catch {
|
|
802
|
+
return {
|
|
803
|
+
signal: "SIGTERM",
|
|
804
|
+
exited: !processAlive(pid)
|
|
805
|
+
};
|
|
806
|
+
}
|
|
986
807
|
return {
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
sessionDir,
|
|
990
|
-
sessionId,
|
|
991
|
-
sessionPath,
|
|
992
|
-
lastAttachedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
993
|
-
})
|
|
808
|
+
signal: "SIGKILL",
|
|
809
|
+
exited: !processAlive(pid)
|
|
994
810
|
};
|
|
995
811
|
}
|
|
996
|
-
function
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
body: input.body
|
|
1005
|
-
});
|
|
1006
|
-
writeAgentState(input.artifactsDir, createAgentState({
|
|
1007
|
-
...state,
|
|
1008
|
-
status: state.status === "idle" ? "queued" : state.status,
|
|
1009
|
-
lastMessage: input.body,
|
|
1010
|
-
waitingOn: null,
|
|
1011
|
-
blocked: false,
|
|
1012
|
-
session: createAgentSessionRecord({
|
|
1013
|
-
sessionDir: state.session.sessionDir ?? getAgentSessionsDir(input.artifactsDir, input.agentId),
|
|
1014
|
-
sessionId: state.session.sessionId,
|
|
1015
|
-
sessionPath: state.session.sessionPath,
|
|
1016
|
-
lastAttachedAt: state.session.lastAttachedAt
|
|
1017
|
-
})
|
|
1018
|
-
}));
|
|
1019
|
-
}
|
|
1020
|
-
function updateAgentStatus(input) {
|
|
1021
|
-
const state = readAgentState(input.artifactsDir, input.agentId);
|
|
1022
|
-
if (state === null) throw new Error(`Unknown agent: ${input.agentId}`);
|
|
1023
|
-
if (state.taskId) {
|
|
1024
|
-
const taskStatus = input.status === "completed" ? "completed" : input.status === "blocked" || input.status === "failed" ? "blocked" : input.status === "running" || input.status === "queued" ? "running" : null;
|
|
1025
|
-
if (taskStatus) updateTaskRecordStatus(input.artifactsDir, state.taskId, taskStatus);
|
|
812
|
+
function readLease(path) {
|
|
813
|
+
try {
|
|
814
|
+
return {
|
|
815
|
+
...JSON.parse(readFileSync(path, "utf-8")),
|
|
816
|
+
path
|
|
817
|
+
};
|
|
818
|
+
} catch {
|
|
819
|
+
return null;
|
|
1026
820
|
}
|
|
1027
|
-
writeAgentState(input.artifactsDir, createAgentState({
|
|
1028
|
-
...state,
|
|
1029
|
-
status: input.status,
|
|
1030
|
-
lastMessage: input.lastMessage ?? state.lastMessage,
|
|
1031
|
-
waitingOn: input.waitingOn ?? state.waitingOn,
|
|
1032
|
-
blocked: input.blocked ?? state.blocked,
|
|
1033
|
-
session: state.session
|
|
1034
|
-
}));
|
|
1035
821
|
}
|
|
1036
|
-
function
|
|
1037
|
-
|
|
1038
|
-
if (
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
role: options.role,
|
|
1043
|
-
status: "queued",
|
|
1044
|
-
taskId: options.taskId ?? null,
|
|
1045
|
-
task: options.task,
|
|
1046
|
-
lastMessage: options.task ? `Spawned with task: ${options.task}` : `Spawned ${options.role} agent`,
|
|
1047
|
-
session: createAgentSessionRecord({ sessionDir })
|
|
1048
|
-
});
|
|
1049
|
-
writeAgentState(options.artifactsDir, state);
|
|
1050
|
-
if (options.task) appendAgentMessage({
|
|
1051
|
-
artifactsDir: options.artifactsDir,
|
|
1052
|
-
agentId: options.agentId,
|
|
1053
|
-
box: "inbox",
|
|
1054
|
-
from: "system",
|
|
1055
|
-
body: options.task
|
|
1056
|
-
});
|
|
1057
|
-
writeAgentState(options.artifactsDir, createAgentState({
|
|
1058
|
-
...state,
|
|
1059
|
-
status: "running",
|
|
1060
|
-
lastMessage: options.task ? `Running ${options.role} task: ${options.task}` : `Running ${options.role} agent`
|
|
1061
|
-
}));
|
|
1062
|
-
const piArgs = createPiInvocationArgs({
|
|
1063
|
-
sessionDir,
|
|
1064
|
-
prompt: createRolePrompt({
|
|
1065
|
-
role: options.role,
|
|
1066
|
-
task: options.task,
|
|
1067
|
-
repoRoot: options.repoRoot
|
|
1068
|
-
}),
|
|
1069
|
-
appendedSystemPrompt: options.appendedSystemPrompt,
|
|
1070
|
-
extensionPath: options.extensionPath
|
|
1071
|
-
});
|
|
1072
|
-
const piResult = runCommandSync("pi", piArgs, {
|
|
1073
|
-
cwd: options.repoRoot,
|
|
1074
|
-
env: process.env
|
|
1075
|
-
});
|
|
1076
|
-
const latestSession = getLatestAgentSession(options.artifactsDir, options.agentId);
|
|
1077
|
-
const agentArtifactsDir = getAgentDir(options.artifactsDir, options.agentId);
|
|
1078
|
-
writeFileSync(`${agentArtifactsDir}/latest-stdout.txt`, piResult.stdout, "utf-8");
|
|
1079
|
-
writeFileSync(`${agentArtifactsDir}/latest-stderr.txt`, piResult.stderr, "utf-8");
|
|
1080
|
-
writeFileSync(`${agentArtifactsDir}/latest-invocation.json`, `${JSON.stringify({
|
|
1081
|
-
command: "pi",
|
|
1082
|
-
args: piArgs,
|
|
1083
|
-
exitCode: piResult.exitCode,
|
|
1084
|
-
sessionDir,
|
|
1085
|
-
sessionPath: latestSession.sessionPath,
|
|
1086
|
-
sessionId: latestSession.sessionId
|
|
1087
|
-
}, null, 2)}\n`, "utf-8");
|
|
1088
|
-
const completionMessage = piResult.stdout.trim() || (piResult.exitCode === 0 ? `${options.role} run completed` : `${options.role} run exited with code ${piResult.exitCode}`);
|
|
1089
|
-
appendAgentMessage({
|
|
1090
|
-
artifactsDir: options.artifactsDir,
|
|
1091
|
-
agentId: options.agentId,
|
|
1092
|
-
box: "outbox",
|
|
1093
|
-
from: options.agentId,
|
|
1094
|
-
body: completionMessage
|
|
1095
|
-
});
|
|
1096
|
-
writeAgentState(options.artifactsDir, createAgentState({
|
|
1097
|
-
...state,
|
|
1098
|
-
status: piResult.exitCode === 0 ? "idle" : "blocked",
|
|
1099
|
-
lastMessage: completionMessage,
|
|
1100
|
-
blocked: piResult.exitCode !== 0,
|
|
1101
|
-
waitingOn: piResult.exitCode === 0 ? null : "human-or-follow-up-run",
|
|
1102
|
-
session: createAgentSessionRecord({
|
|
1103
|
-
sessionDir: latestSession.sessionDir,
|
|
1104
|
-
sessionId: latestSession.sessionId,
|
|
1105
|
-
sessionPath: latestSession.sessionPath
|
|
1106
|
-
})
|
|
1107
|
-
}));
|
|
822
|
+
function createStopMessage(options) {
|
|
823
|
+
if (options.reason) return options.reason;
|
|
824
|
+
if (options.actorId) return `Stopped by ${options.actorId}`;
|
|
825
|
+
return "Stopped by operator";
|
|
826
|
+
}
|
|
827
|
+
function createStopMessageInput(options) {
|
|
1108
828
|
return {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
completionMessage
|
|
829
|
+
...options.actorId === void 0 ? {} : { actorId: options.actorId },
|
|
830
|
+
...options.reason === void 0 ? {} : { reason: options.reason }
|
|
1112
831
|
};
|
|
1113
832
|
}
|
|
1114
|
-
function
|
|
1115
|
-
assertCommandAvailable("pi");
|
|
1116
|
-
const resolved = resolveAgentSession(options.agentId, options.artifactsDir);
|
|
1117
|
-
const messageSource = options.from ?? "human";
|
|
1118
|
-
writeAgentState(options.artifactsDir, createAgentState({
|
|
1119
|
-
...resolved.state,
|
|
1120
|
-
status: "running",
|
|
1121
|
-
lastMessage: `Responding to ${messageSource}: ${options.message}`,
|
|
1122
|
-
waitingOn: null,
|
|
1123
|
-
blocked: false,
|
|
1124
|
-
session: resolved.session
|
|
1125
|
-
}));
|
|
1126
|
-
const piArgs = options.runtimeArgs && options.runtimeArgs.length > 0 ? options.runtimeArgs : createPiInvocationArgs({
|
|
1127
|
-
sessionPath: resolved.session.sessionPath,
|
|
1128
|
-
prompt: options.message
|
|
1129
|
-
});
|
|
1130
|
-
const piResult = runCommandSync("pi", piArgs, {
|
|
1131
|
-
cwd: options.repoRoot,
|
|
1132
|
-
env: process.env
|
|
1133
|
-
});
|
|
1134
|
-
const latestSession = getLatestAgentSession(options.artifactsDir, options.agentId);
|
|
1135
|
-
const agentArtifactsDir = getAgentDir(options.artifactsDir, options.agentId);
|
|
1136
|
-
writeFileSync(`${agentArtifactsDir}/latest-stdout.txt`, piResult.stdout, "utf-8");
|
|
1137
|
-
writeFileSync(`${agentArtifactsDir}/latest-stderr.txt`, piResult.stderr, "utf-8");
|
|
1138
|
-
writeFileSync(`${agentArtifactsDir}/latest-invocation.json`, `${JSON.stringify({
|
|
1139
|
-
command: "pi",
|
|
1140
|
-
args: piArgs,
|
|
1141
|
-
exitCode: piResult.exitCode,
|
|
1142
|
-
sessionDir: latestSession.sessionDir,
|
|
1143
|
-
sessionPath: latestSession.sessionPath,
|
|
1144
|
-
sessionId: latestSession.sessionId
|
|
1145
|
-
}, null, 2)}\n`, "utf-8");
|
|
1146
|
-
const completionMessage = piResult.stdout.trim() || (piResult.exitCode === 0 ? `${resolved.state.role} turn completed` : `${resolved.state.role} turn exited with code ${piResult.exitCode}`);
|
|
1147
|
-
appendAgentMessage({
|
|
1148
|
-
artifactsDir: options.artifactsDir,
|
|
1149
|
-
agentId: options.agentId,
|
|
1150
|
-
box: "outbox",
|
|
1151
|
-
from: options.agentId,
|
|
1152
|
-
body: completionMessage
|
|
1153
|
-
});
|
|
1154
|
-
writeAgentState(options.artifactsDir, createAgentState({
|
|
1155
|
-
...resolved.state,
|
|
1156
|
-
status: piResult.exitCode === 0 ? "idle" : "blocked",
|
|
1157
|
-
lastMessage: completionMessage,
|
|
1158
|
-
waitingOn: piResult.exitCode === 0 ? null : "human-or-follow-up-run",
|
|
1159
|
-
blocked: piResult.exitCode !== 0,
|
|
1160
|
-
session: createAgentSessionRecord({
|
|
1161
|
-
sessionDir: latestSession.sessionDir,
|
|
1162
|
-
sessionId: latestSession.sessionId,
|
|
1163
|
-
sessionPath: latestSession.sessionPath
|
|
1164
|
-
})
|
|
1165
|
-
}));
|
|
833
|
+
function createTerminateOptions(options) {
|
|
1166
834
|
return {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
completionMessage
|
|
835
|
+
...options.force === void 0 ? {} : { force: options.force },
|
|
836
|
+
...options.graceMs === void 0 ? {} : { graceMs: options.graceMs }
|
|
1170
837
|
};
|
|
1171
838
|
}
|
|
1172
|
-
function
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
role: options.role,
|
|
1195
|
-
agentId,
|
|
1196
|
-
appendedSystemPrompt: options.appendedSystemPrompt,
|
|
1197
|
-
extensionPath: options.extensionPath,
|
|
1198
|
-
task: options.task,
|
|
1199
|
-
taskId: task.taskId
|
|
1200
|
-
});
|
|
1201
|
-
appendAgentMessage({
|
|
1202
|
-
artifactsDir: options.artifactsDir,
|
|
1203
|
-
agentId,
|
|
1204
|
-
box: "inbox",
|
|
1205
|
-
from: options.fromAgentId,
|
|
1206
|
-
body: `Delegated by ${options.fromAgentId} as ${task.taskId}: ${options.task}`
|
|
839
|
+
function listRepoLeases(repoId) {
|
|
840
|
+
let entries;
|
|
841
|
+
try {
|
|
842
|
+
entries = readdirSync(getLocksDir());
|
|
843
|
+
} catch {
|
|
844
|
+
return [];
|
|
845
|
+
}
|
|
846
|
+
return entries.filter((entry) => entry.endsWith(".json")).map((entry) => readLease(join(getLocksDir(), entry))).filter((record) => record !== null).filter((record) => repoId === void 0 || repoId === null || record.repoId === repoId);
|
|
847
|
+
}
|
|
848
|
+
function stopRepoLeases(options) {
|
|
849
|
+
const results = listRepoLeases(options.repoId).map((lease) => {
|
|
850
|
+
const termination = terminateProcess(lease.pid, options);
|
|
851
|
+
if (termination.exited) rmSync(lease.path, { force: true });
|
|
852
|
+
return {
|
|
853
|
+
path: lease.path,
|
|
854
|
+
runId: lease.runId,
|
|
855
|
+
repoId: lease.repoId,
|
|
856
|
+
branch: lease.branch,
|
|
857
|
+
processId: lease.pid,
|
|
858
|
+
signal: termination.signal,
|
|
859
|
+
exited: termination.exited
|
|
860
|
+
};
|
|
1207
861
|
});
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
862
|
+
return {
|
|
863
|
+
results,
|
|
864
|
+
signaledProcesses: results.filter((result) => result.signal !== null).length
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
function stopManagedAgents(options) {
|
|
868
|
+
const reason = createStopMessage(createStopMessageInput(options));
|
|
869
|
+
const excluded = new Set(options.excludeAgentIds ?? []);
|
|
870
|
+
const results = listAgentStates(options.artifactsDir).filter((agent) => {
|
|
871
|
+
if (excluded.has(agent.agentId)) return false;
|
|
872
|
+
if (options.agentId && agent.agentId !== options.agentId) return false;
|
|
873
|
+
return ![
|
|
874
|
+
"completed",
|
|
875
|
+
"failed",
|
|
876
|
+
"stopped"
|
|
877
|
+
].includes(agent.status);
|
|
878
|
+
}).map((state) => {
|
|
879
|
+
const processId = state.session.processId;
|
|
880
|
+
const termination = processId === null ? {
|
|
881
|
+
signal: null,
|
|
882
|
+
exited: true
|
|
883
|
+
} : terminateProcess(processId, createTerminateOptions(options));
|
|
884
|
+
if (state.taskId) updateTaskRecordStatus(options.artifactsDir, state.taskId, "aborted");
|
|
885
|
+
appendAgentMessage({
|
|
886
|
+
artifactsDir: options.artifactsDir,
|
|
887
|
+
agentId: state.agentId,
|
|
888
|
+
box: "outbox",
|
|
889
|
+
from: options.actorId ?? "system",
|
|
890
|
+
body: reason
|
|
891
|
+
});
|
|
892
|
+
writeAgentState(options.artifactsDir, createAgentState({
|
|
893
|
+
...state,
|
|
894
|
+
status: "stopped",
|
|
895
|
+
lastMessage: reason,
|
|
896
|
+
waitingOn: "stopped",
|
|
897
|
+
blocked: true,
|
|
898
|
+
session: createAgentSessionRecord({
|
|
899
|
+
sessionDir: state.session.sessionDir,
|
|
900
|
+
sessionId: state.session.sessionId,
|
|
901
|
+
sessionPath: state.session.sessionPath,
|
|
902
|
+
processId: null,
|
|
903
|
+
lastAttachedAt: state.session.lastAttachedAt
|
|
904
|
+
})
|
|
905
|
+
}));
|
|
906
|
+
return {
|
|
907
|
+
agentId: state.agentId,
|
|
908
|
+
previousStatus: state.status,
|
|
909
|
+
nextStatus: "stopped",
|
|
910
|
+
processId,
|
|
911
|
+
signal: termination.signal,
|
|
912
|
+
exited: termination.exited
|
|
913
|
+
};
|
|
1212
914
|
});
|
|
1213
915
|
return {
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1218
|
-
},
|
|
1219
|
-
agentId,
|
|
1220
|
-
piResult,
|
|
1221
|
-
latestSession
|
|
916
|
+
results,
|
|
917
|
+
stoppedAgents: results.length,
|
|
918
|
+
signaledProcesses: results.filter((result) => result.signal !== null).length
|
|
1222
919
|
};
|
|
1223
920
|
}
|
|
1224
921
|
|
|
1225
922
|
//#endregion
|
|
1226
|
-
export { acquireRepoLease, appendAgentMessage, appendJsonl, assertCommandAvailable, assertSuccess, computeAutonomousCompletionRate, computeContextCoverageScore, computeFeedbackToDemoCycleTime, computeInterruptRate, computeMeanTimeToCorrect, computeMetrics, createAgentSessionRecord, createAgentState, createInterruptRecord, createPiAuthHelpMessage, createRepoSlug, createRolePrompt, createTaskRecord, delegateTask, detectPiAuthFailure, evaluateStopCondition, getAgentDir, getAgentMailboxPath, getAgentSessionPath, getAgentSessionsDir, getAgentStatePath, getAgentsDir, getCurrentBranch, getLatestAgentSession, getRepoIdentity, getRepoRoot, getSessionIdFromPath, getTaskPath, getTasksDir, isGitRepo, listAgentStates, listTaskRecords, queueAgentMessage, readAgentMessages, readAgentState, readJsonl, readTaskRecord, resolveAgentSession, resolveInterrupt, runAgentTurn, runCommandInteractive, runCommandSync, runController, runLoop, snapshotBoard, spawnAgentRun, updateAgentStatus, updateTaskRecordStatus, writeAgentState, writeTaskRecord };
|
|
923
|
+
export { acquireRepoLease, appendAgentMessage, appendJsonl, assertCommandAvailable, assertSuccess, computeAutonomousCompletionRate, computeContextCoverageScore, computeFeedbackToDemoCycleTime, computeInterruptRate, computeMeanTimeToCorrect, computeMetrics, createAgentSessionRecord, createAgentState, createInterruptRecord, createPiAuthHelpMessage, createRepoSlug, createRolePrompt, createTaskRecord, delegateTask, detectPiAuthFailure, evaluateStopCondition, getAgentDir, getAgentMailboxPath, getAgentSessionPath, getAgentSessionsDir, getAgentStatePath, getAgentsDir, getCurrentBranch, getLatestAgentSession, getRepoIdentity, getRepoRoot, getSessionIdFromPath, getTaskPath, getTasksDir, isGitRepo, listAgentStates, listRepoLeases, listTaskRecords, notifyTaskDelegator, queueAgentMessage, readAgentMessages, readAgentState, readJsonl, readTaskRecord, resolveAgentSession, resolveInterrupt, resumeAgentTurnDetached, runAgentTurn, runCommandInteractive, runCommandSync, runController, runLoop, snapshotBoard, spawnAgentRun, stopManagedAgents, stopRepoLeases, updateAgentStatus, updateTaskRecordStatus, writeAgentState, writeTaskRecord };
|
|
1227
924
|
//# sourceMappingURL=index.mjs.map
|