@schilderlabs/pitown 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/README.md +6 -0
- package/dist/{config-CUpe9o0x.mjs → config-BG1v4iIi.mjs} +6 -7
- package/dist/config-BG1v4iIi.mjs.map +1 -0
- package/dist/doctor.mjs +2 -2
- package/dist/{entrypoint-CyJDLudQ.mjs → entrypoint-WBAQmFbT.mjs} +1 -1
- package/dist/{entrypoint-CyJDLudQ.mjs.map → entrypoint-WBAQmFbT.mjs.map} +1 -1
- package/dist/index.mjs +443 -407
- package/dist/index.mjs.map +1 -1
- package/dist/{controller-9ihAZj3V.mjs → loop-CocC9qO1.mjs} +327 -174
- package/dist/loop-CocC9qO1.mjs.map +1 -0
- package/dist/{pi-C0fURZj7.mjs → pi-C7HRNjBG.mjs} +1 -1
- package/dist/{pi-C0fURZj7.mjs.map → pi-C7HRNjBG.mjs.map} +1 -1
- package/dist/repo-context-BuA2JqPm.mjs +45 -0
- package/dist/repo-context-BuA2JqPm.mjs.map +1 -0
- package/dist/run.d.mts +3 -72
- package/dist/run.mjs +38 -23
- package/dist/run.mjs.map +1 -1
- package/dist/status.mjs +2 -2
- package/dist/tasks-De4IAy3x.mjs +195 -0
- package/dist/tasks-De4IAy3x.mjs.map +1 -0
- package/dist/types-COGNGvsY.d.mts +142 -0
- package/dist/watch.d.mts +35 -1
- package/dist/watch.mjs +129 -17
- package/dist/watch.mjs.map +1 -1
- package/package.json +20 -24
- package/dist/config-CUpe9o0x.mjs.map +0 -1
- package/dist/controller-9ihAZj3V.mjs.map +0 -1
|
@@ -1,141 +1,11 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { d as listAgentStates, h as appendJsonl, l as getAgentSessionsDir, m as writeAgentState, n as listTaskRecords, o as createAgentSessionRecord, p as readAgentState, s as createAgentState, u as getLatestAgentSession } from "./tasks-De4IAy3x.mjs";
|
|
2
|
+
import { n as detectPiAuthFailure, t as createPiAuthHelpMessage } from "./pi-C7HRNjBG.mjs";
|
|
3
|
+
import { a as runCommandSync, n as assertCommandAvailable } from "./entrypoint-WBAQmFbT.mjs";
|
|
4
|
+
import { d as createRepoSlug, f as getCurrentBranch, m as getRepoRoot, p as getRepoIdentity } from "./config-BG1v4iIi.mjs";
|
|
5
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
6
7
|
import { homedir, hostname } from "node:os";
|
|
7
8
|
|
|
8
|
-
//#region ../core/src/events.ts
|
|
9
|
-
function appendJsonl(filePath, value) {
|
|
10
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
11
|
-
writeFileSync(filePath, `${JSON.stringify(value)}\n`, {
|
|
12
|
-
encoding: "utf-8",
|
|
13
|
-
flag: "a"
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
function readJsonl(filePath) {
|
|
17
|
-
try {
|
|
18
|
-
return readFileSync(filePath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
19
|
-
} catch {
|
|
20
|
-
return [];
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region ../core/src/agents.ts
|
|
26
|
-
function writeJson$1(path, value) {
|
|
27
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
28
|
-
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
29
|
-
}
|
|
30
|
-
function ensureMailbox(path) {
|
|
31
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
32
|
-
if (!existsSync(path)) writeFileSync(path, "", "utf-8");
|
|
33
|
-
}
|
|
34
|
-
function getAgentsDir(artifactsDir) {
|
|
35
|
-
return join(artifactsDir, "agents");
|
|
36
|
-
}
|
|
37
|
-
function getAgentDir(artifactsDir, agentId) {
|
|
38
|
-
return join(getAgentsDir(artifactsDir), agentId);
|
|
39
|
-
}
|
|
40
|
-
function getAgentStatePath(artifactsDir, agentId) {
|
|
41
|
-
return join(getAgentDir(artifactsDir, agentId), "state.json");
|
|
42
|
-
}
|
|
43
|
-
function getAgentSessionPath(artifactsDir, agentId) {
|
|
44
|
-
return join(getAgentDir(artifactsDir, agentId), "session.json");
|
|
45
|
-
}
|
|
46
|
-
function getAgentMailboxPath(artifactsDir, agentId, box) {
|
|
47
|
-
return join(getAgentDir(artifactsDir, agentId), `${box}.jsonl`);
|
|
48
|
-
}
|
|
49
|
-
function getAgentSessionsDir(artifactsDir, agentId) {
|
|
50
|
-
return join(getAgentDir(artifactsDir, agentId), "sessions");
|
|
51
|
-
}
|
|
52
|
-
function getSessionIdFromPath(sessionPath) {
|
|
53
|
-
if (!sessionPath) return null;
|
|
54
|
-
return /_([0-9a-f-]+)\.jsonl$/i.exec(sessionPath)?.[1] ?? null;
|
|
55
|
-
}
|
|
56
|
-
function createAgentSessionRecord(input) {
|
|
57
|
-
return {
|
|
58
|
-
runtime: "pi",
|
|
59
|
-
persisted: true,
|
|
60
|
-
sessionDir: input?.sessionDir ?? null,
|
|
61
|
-
sessionId: input?.sessionId ?? null,
|
|
62
|
-
sessionPath: input?.sessionPath ?? null,
|
|
63
|
-
lastAttachedAt: input?.lastAttachedAt ?? null
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function createAgentState(input) {
|
|
67
|
-
return {
|
|
68
|
-
agentId: input.agentId,
|
|
69
|
-
role: input.role,
|
|
70
|
-
status: input.status,
|
|
71
|
-
taskId: input.taskId ?? null,
|
|
72
|
-
task: input.task ?? null,
|
|
73
|
-
branch: input.branch ?? null,
|
|
74
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
75
|
-
lastMessage: input.lastMessage ?? null,
|
|
76
|
-
waitingOn: input.waitingOn ?? null,
|
|
77
|
-
blocked: input.blocked ?? false,
|
|
78
|
-
runId: input.runId ?? null,
|
|
79
|
-
session: input.session ?? createAgentSessionRecord()
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
function writeAgentState(artifactsDir, state) {
|
|
83
|
-
mkdirSync(getAgentDir(artifactsDir, state.agentId), { recursive: true });
|
|
84
|
-
ensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, "inbox"));
|
|
85
|
-
ensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, "outbox"));
|
|
86
|
-
writeJson$1(getAgentStatePath(artifactsDir, state.agentId), state);
|
|
87
|
-
writeJson$1(getAgentSessionPath(artifactsDir, state.agentId), state.session);
|
|
88
|
-
}
|
|
89
|
-
function readAgentState(artifactsDir, agentId) {
|
|
90
|
-
const statePath = getAgentStatePath(artifactsDir, agentId);
|
|
91
|
-
try {
|
|
92
|
-
return JSON.parse(readFileSync(statePath, "utf-8"));
|
|
93
|
-
} catch {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function listAgentStates(artifactsDir) {
|
|
98
|
-
const agentsDir = getAgentsDir(artifactsDir);
|
|
99
|
-
let entries;
|
|
100
|
-
try {
|
|
101
|
-
entries = readdirSync(agentsDir);
|
|
102
|
-
} catch {
|
|
103
|
-
return [];
|
|
104
|
-
}
|
|
105
|
-
return entries.map((entry) => readAgentState(artifactsDir, entry)).filter((state) => state !== null).sort((left, right) => left.agentId.localeCompare(right.agentId));
|
|
106
|
-
}
|
|
107
|
-
function appendAgentMessage(input) {
|
|
108
|
-
const record = {
|
|
109
|
-
box: input.box,
|
|
110
|
-
from: input.from,
|
|
111
|
-
body: input.body,
|
|
112
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
113
|
-
};
|
|
114
|
-
appendJsonl(getAgentMailboxPath(input.artifactsDir, input.agentId, input.box), record);
|
|
115
|
-
return record;
|
|
116
|
-
}
|
|
117
|
-
function readAgentMessages(artifactsDir, agentId, box) {
|
|
118
|
-
return readJsonl(getAgentMailboxPath(artifactsDir, agentId, box));
|
|
119
|
-
}
|
|
120
|
-
function getLatestAgentSession(artifactsDir, agentId) {
|
|
121
|
-
const sessionDir = getAgentSessionsDir(artifactsDir, agentId);
|
|
122
|
-
let entries;
|
|
123
|
-
try {
|
|
124
|
-
entries = readdirSync(sessionDir);
|
|
125
|
-
} catch {
|
|
126
|
-
return createAgentSessionRecord({ sessionDir });
|
|
127
|
-
}
|
|
128
|
-
const latestSessionPath = entries.filter((entry) => entry.endsWith(".jsonl")).sort().at(-1) ?? null;
|
|
129
|
-
if (latestSessionPath === null) return createAgentSessionRecord({ sessionDir });
|
|
130
|
-
const sessionPath = join(sessionDir, latestSessionPath);
|
|
131
|
-
return createAgentSessionRecord({
|
|
132
|
-
sessionDir,
|
|
133
|
-
sessionPath,
|
|
134
|
-
sessionId: getSessionIdFromPath(sessionPath)
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
//#endregion
|
|
139
9
|
//#region ../core/src/lease.ts
|
|
140
10
|
function sanitize(value) {
|
|
141
11
|
return value.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
@@ -250,7 +120,7 @@ function createPiInvocationArgs(input) {
|
|
|
250
120
|
args.push("-p", input.prompt);
|
|
251
121
|
return args;
|
|
252
122
|
}
|
|
253
|
-
function writeJson(path, value) {
|
|
123
|
+
function writeJson$1(path, value) {
|
|
254
124
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
255
125
|
}
|
|
256
126
|
function writeText(path, value) {
|
|
@@ -259,7 +129,7 @@ function writeText(path, value) {
|
|
|
259
129
|
function createPiPrompt(input) {
|
|
260
130
|
const goal = input.goal ?? "continue from current scaffold state";
|
|
261
131
|
if (input.planPath) return [
|
|
262
|
-
"You are the Pi Town
|
|
132
|
+
"You are the Pi Town mayor agent for this repository.",
|
|
263
133
|
"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.",
|
|
264
134
|
"",
|
|
265
135
|
"Read the private plans in:",
|
|
@@ -273,7 +143,7 @@ function createPiPrompt(input) {
|
|
|
273
143
|
"Keep any persisted run artifacts high-signal and avoid copying private plan contents into them."
|
|
274
144
|
].join("\n");
|
|
275
145
|
return [
|
|
276
|
-
"You are the Pi Town
|
|
146
|
+
"You are the Pi Town mayor agent for this repository.",
|
|
277
147
|
"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.",
|
|
278
148
|
"",
|
|
279
149
|
`Work in the repository at: ${input.repoRoot}`,
|
|
@@ -359,21 +229,21 @@ function runController(options) {
|
|
|
359
229
|
goal,
|
|
360
230
|
recommendedPlanDir
|
|
361
231
|
});
|
|
362
|
-
const
|
|
363
|
-
const
|
|
364
|
-
const
|
|
365
|
-
const
|
|
366
|
-
agentId: "
|
|
367
|
-
role: "
|
|
232
|
+
const existingMayorState = readAgentState(artifactsDir, "mayor");
|
|
233
|
+
const existingMayorSession = existingMayorState?.session.sessionPath || existingMayorState?.session.sessionDir ? existingMayorState.session : getLatestAgentSession(artifactsDir, "mayor");
|
|
234
|
+
const mayorSessionDir = existingMayorSession.sessionDir ?? getAgentSessionsDir(artifactsDir, "mayor");
|
|
235
|
+
const mayorState = createAgentState({
|
|
236
|
+
agentId: "mayor",
|
|
237
|
+
role: "mayor",
|
|
368
238
|
status: "starting",
|
|
369
239
|
task: goal,
|
|
370
240
|
branch,
|
|
371
|
-
lastMessage: goal ? `Starting
|
|
241
|
+
lastMessage: goal ? `Starting mayor run for goal: ${goal}` : "Starting mayor run",
|
|
372
242
|
runId,
|
|
373
243
|
session: createAgentSessionRecord({
|
|
374
|
-
sessionDir:
|
|
375
|
-
sessionId:
|
|
376
|
-
sessionPath:
|
|
244
|
+
sessionDir: mayorSessionDir,
|
|
245
|
+
sessionId: existingMayorSession.sessionId,
|
|
246
|
+
sessionPath: existingMayorSession.sessionPath
|
|
377
247
|
})
|
|
378
248
|
});
|
|
379
249
|
assertPiRuntimeAvailable(piCommand);
|
|
@@ -381,11 +251,11 @@ function runController(options) {
|
|
|
381
251
|
mkdirSync(latestDir, { recursive: true });
|
|
382
252
|
writeText(join(runDir, "questions.jsonl"), "");
|
|
383
253
|
writeText(join(runDir, "interventions.jsonl"), "");
|
|
384
|
-
writeJson(join(runDir, "agent-state.json"), {
|
|
254
|
+
writeJson$1(join(runDir, "agent-state.json"), {
|
|
385
255
|
status: "starting",
|
|
386
256
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
387
257
|
});
|
|
388
|
-
writeAgentState(artifactsDir,
|
|
258
|
+
writeAgentState(artifactsDir, mayorState);
|
|
389
259
|
const lease = acquireRepoLease(runId, repoId, branch);
|
|
390
260
|
try {
|
|
391
261
|
const manifest = createManifest({
|
|
@@ -416,13 +286,13 @@ function runController(options) {
|
|
|
416
286
|
createdAt: piStartedAt
|
|
417
287
|
});
|
|
418
288
|
writeAgentState(artifactsDir, createAgentState({
|
|
419
|
-
...
|
|
289
|
+
...mayorState,
|
|
420
290
|
status: "running",
|
|
421
|
-
lastMessage: goal ? `
|
|
291
|
+
lastMessage: goal ? `Mayor working on: ${goal}` : "Mayor working"
|
|
422
292
|
}));
|
|
423
293
|
const piResult = runCommandSync(piCommand, createPiInvocationArgs({
|
|
424
|
-
sessionDir:
|
|
425
|
-
sessionPath:
|
|
294
|
+
sessionDir: mayorState.session.sessionPath === null ? mayorSessionDir : null,
|
|
295
|
+
sessionPath: mayorState.session.sessionPath,
|
|
426
296
|
prompt,
|
|
427
297
|
appendedSystemPrompt: options.appendedSystemPrompt,
|
|
428
298
|
extensionPath: options.extensionPath
|
|
@@ -431,7 +301,7 @@ function runController(options) {
|
|
|
431
301
|
env: process.env
|
|
432
302
|
});
|
|
433
303
|
const piEndedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
434
|
-
const
|
|
304
|
+
const latestMayorSession = getLatestAgentSession(artifactsDir, "mayor");
|
|
435
305
|
writeText(stdoutPath, piResult.stdout);
|
|
436
306
|
writeText(stderrPath, piResult.stderr);
|
|
437
307
|
const piInvocation = {
|
|
@@ -440,9 +310,9 @@ function runController(options) {
|
|
|
440
310
|
repoRoot,
|
|
441
311
|
planPath,
|
|
442
312
|
goal,
|
|
443
|
-
sessionDir:
|
|
444
|
-
sessionId:
|
|
445
|
-
sessionPath:
|
|
313
|
+
sessionDir: latestMayorSession.sessionDir,
|
|
314
|
+
sessionId: latestMayorSession.sessionId,
|
|
315
|
+
sessionPath: latestMayorSession.sessionPath,
|
|
446
316
|
startedAt: piStartedAt,
|
|
447
317
|
endedAt: piEndedAt,
|
|
448
318
|
exitCode: piResult.exitCode,
|
|
@@ -450,7 +320,7 @@ function runController(options) {
|
|
|
450
320
|
stderrPath,
|
|
451
321
|
promptSummary: planPath ? "Read private plan path and continue from current scaffold state." : "Continue from current scaffold state without a configured private plan path."
|
|
452
322
|
};
|
|
453
|
-
writeJson(join(runDir, "pi-invocation.json"), piInvocation);
|
|
323
|
+
writeJson$1(join(runDir, "pi-invocation.json"), piInvocation);
|
|
454
324
|
appendJsonl(join(runDir, "events.jsonl"), {
|
|
455
325
|
type: "pi_invocation_finished",
|
|
456
326
|
runId,
|
|
@@ -476,27 +346,27 @@ function runController(options) {
|
|
|
476
346
|
stopReason: piInvocation.exitCode === 0 ? "pi invocation completed" : `pi invocation exited with code ${piInvocation.exitCode}`,
|
|
477
347
|
piExitCode: piInvocation.exitCode
|
|
478
348
|
};
|
|
479
|
-
writeJson(join(runDir, "manifest.json"), finalManifest);
|
|
480
|
-
writeJson(join(runDir, "metrics.json"), metrics);
|
|
481
|
-
writeJson(join(runDir, "run-summary.json"), summary);
|
|
482
|
-
writeJson(join(runDir, "agent-state.json"), {
|
|
349
|
+
writeJson$1(join(runDir, "manifest.json"), finalManifest);
|
|
350
|
+
writeJson$1(join(runDir, "metrics.json"), metrics);
|
|
351
|
+
writeJson$1(join(runDir, "run-summary.json"), summary);
|
|
352
|
+
writeJson$1(join(runDir, "agent-state.json"), {
|
|
483
353
|
status: summary.success ? "completed" : "failed",
|
|
484
354
|
updatedAt: piEndedAt,
|
|
485
355
|
exitCode: piInvocation.exitCode
|
|
486
356
|
});
|
|
487
|
-
writeJson(join(latestDir, "manifest.json"), finalManifest);
|
|
488
|
-
writeJson(join(latestDir, "metrics.json"), metrics);
|
|
489
|
-
writeJson(join(latestDir, "run-summary.json"), summary);
|
|
357
|
+
writeJson$1(join(latestDir, "manifest.json"), finalManifest);
|
|
358
|
+
writeJson$1(join(latestDir, "metrics.json"), metrics);
|
|
359
|
+
writeJson$1(join(latestDir, "run-summary.json"), summary);
|
|
490
360
|
writeAgentState(artifactsDir, createAgentState({
|
|
491
|
-
...
|
|
361
|
+
...mayorState,
|
|
492
362
|
status: piInvocation.exitCode === 0 ? "idle" : "blocked",
|
|
493
|
-
lastMessage: piInvocation.exitCode === 0 ? "
|
|
363
|
+
lastMessage: piInvocation.exitCode === 0 ? "Mayor run completed and is ready for the next instruction" : `Mayor run stopped with exit code ${piInvocation.exitCode}`,
|
|
494
364
|
blocked: piInvocation.exitCode !== 0,
|
|
495
365
|
waitingOn: piInvocation.exitCode === 0 ? null : "human-or-follow-up-run",
|
|
496
366
|
session: createAgentSessionRecord({
|
|
497
|
-
sessionDir:
|
|
498
|
-
sessionId:
|
|
499
|
-
sessionPath:
|
|
367
|
+
sessionDir: latestMayorSession.sessionDir,
|
|
368
|
+
sessionId: latestMayorSession.sessionId,
|
|
369
|
+
sessionPath: latestMayorSession.sessionPath
|
|
500
370
|
})
|
|
501
371
|
}));
|
|
502
372
|
appendJsonl(join(runDir, "events.jsonl"), {
|
|
@@ -521,5 +391,288 @@ function runController(options) {
|
|
|
521
391
|
}
|
|
522
392
|
|
|
523
393
|
//#endregion
|
|
524
|
-
|
|
525
|
-
|
|
394
|
+
//#region ../core/src/loop.ts
|
|
395
|
+
const DEFAULT_MAX_ITERATIONS = 10;
|
|
396
|
+
const DEFAULT_MAX_WALL_TIME_MS = 36e5;
|
|
397
|
+
const DEFAULT_BACKGROUND_POLL_MS = 250;
|
|
398
|
+
function createLoopId() {
|
|
399
|
+
return `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
400
|
+
}
|
|
401
|
+
function writeJson(path, value) {
|
|
402
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
403
|
+
}
|
|
404
|
+
function sleepMs(ms) {
|
|
405
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
406
|
+
}
|
|
407
|
+
function hasBackgroundWork(board) {
|
|
408
|
+
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");
|
|
409
|
+
}
|
|
410
|
+
function waitForBackgroundWorkToSettle(input) {
|
|
411
|
+
const pollIntervalMs = input.pollIntervalMs ?? DEFAULT_BACKGROUND_POLL_MS;
|
|
412
|
+
let board = snapshotBoard(input.artifactsDir);
|
|
413
|
+
while (hasBackgroundWork(board)) {
|
|
414
|
+
if (Date.now() - input.loopStartedAt >= input.maxWallTimeMs) return {
|
|
415
|
+
timedOut: true,
|
|
416
|
+
board
|
|
417
|
+
};
|
|
418
|
+
sleepMs(pollIntervalMs);
|
|
419
|
+
board = snapshotBoard(input.artifactsDir);
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
timedOut: false,
|
|
423
|
+
board
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
function snapshotBoard(artifactsDir) {
|
|
427
|
+
const tasks = listTaskRecords(artifactsDir);
|
|
428
|
+
const agents = listAgentStates(artifactsDir);
|
|
429
|
+
return {
|
|
430
|
+
tasks: tasks.map((task) => ({
|
|
431
|
+
taskId: task.taskId,
|
|
432
|
+
status: task.status
|
|
433
|
+
})),
|
|
434
|
+
agents: agents.map((agent) => ({
|
|
435
|
+
agentId: agent.agentId,
|
|
436
|
+
status: agent.status,
|
|
437
|
+
blocked: agent.blocked
|
|
438
|
+
})),
|
|
439
|
+
allTasksCompleted: tasks.length > 0 && tasks.every((task) => task.status === "completed"),
|
|
440
|
+
allRemainingTasksBlocked: tasks.length > 0 && tasks.every((task) => task.status === "completed" || task.status === "blocked" || task.status === "aborted"),
|
|
441
|
+
mayorBlocked: agents.find((agent) => agent.agentId === "mayor")?.blocked === true,
|
|
442
|
+
hasQueuedOrRunningWork: agents.some((agent) => agent.status === "queued" || agent.status === "running" || agent.status === "starting") || tasks.some((task) => task.status === "queued" || task.status === "running")
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function evaluateStopCondition(input) {
|
|
446
|
+
if (input.iteration >= input.maxIterations) return {
|
|
447
|
+
stopReason: "max-iterations-reached",
|
|
448
|
+
continueReason: null
|
|
449
|
+
};
|
|
450
|
+
if (input.elapsedMs >= input.maxWallTimeMs) return {
|
|
451
|
+
stopReason: "max-wall-time-reached",
|
|
452
|
+
continueReason: null
|
|
453
|
+
};
|
|
454
|
+
if (input.stopOnPiFailure && input.piExitCode !== 0) return {
|
|
455
|
+
stopReason: "pi-exit-nonzero",
|
|
456
|
+
continueReason: null
|
|
457
|
+
};
|
|
458
|
+
if (input.board.allTasksCompleted) return {
|
|
459
|
+
stopReason: "all-tasks-completed",
|
|
460
|
+
continueReason: null
|
|
461
|
+
};
|
|
462
|
+
if (input.board.mayorBlocked) return {
|
|
463
|
+
stopReason: "mayor-blocked",
|
|
464
|
+
continueReason: null
|
|
465
|
+
};
|
|
466
|
+
const mayor = input.board.agents.find((agent) => agent.agentId === "mayor");
|
|
467
|
+
if (input.stopOnMayorIdleNoWork && mayor && !input.board.hasQueuedOrRunningWork && input.board.tasks.length === 0 && mayor.status === "idle") return {
|
|
468
|
+
stopReason: "mayor-idle-no-work",
|
|
469
|
+
continueReason: null
|
|
470
|
+
};
|
|
471
|
+
if (input.board.allRemainingTasksBlocked) return {
|
|
472
|
+
stopReason: "all-remaining-tasks-blocked",
|
|
473
|
+
continueReason: null
|
|
474
|
+
};
|
|
475
|
+
if (input.interruptRateThreshold !== null && input.metrics.interruptRate > input.interruptRateThreshold) return {
|
|
476
|
+
stopReason: "high-interrupt-rate",
|
|
477
|
+
continueReason: null
|
|
478
|
+
};
|
|
479
|
+
const reasons = [];
|
|
480
|
+
if (input.board.hasQueuedOrRunningWork) reasons.push("queued or running work remains");
|
|
481
|
+
if (input.board.tasks.length === 0) reasons.push("no tasks tracked yet");
|
|
482
|
+
if (reasons.length === 0) reasons.push("mayor idle, no stop condition met");
|
|
483
|
+
return {
|
|
484
|
+
stopReason: null,
|
|
485
|
+
continueReason: reasons.join("; ")
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function aggregateMetrics(iterations) {
|
|
489
|
+
if (iterations.length === 0) return computeMetrics({
|
|
490
|
+
taskAttempts: [],
|
|
491
|
+
interrupts: []
|
|
492
|
+
});
|
|
493
|
+
let totalTaskAttempts = 0;
|
|
494
|
+
let totalCompletedTasks = 0;
|
|
495
|
+
let totalInterrupts = 0;
|
|
496
|
+
let totalObservedInterruptCategories = 0;
|
|
497
|
+
let totalCoveredInterruptCategories = 0;
|
|
498
|
+
let interruptRateSum = 0;
|
|
499
|
+
let autonomousCompletionRateSum = 0;
|
|
500
|
+
let contextCoverageScoreSum = 0;
|
|
501
|
+
let mttcValues = [];
|
|
502
|
+
let ftdValues = [];
|
|
503
|
+
for (const iter of iterations) {
|
|
504
|
+
const m = iter.metrics;
|
|
505
|
+
totalTaskAttempts += m.totals.taskAttempts;
|
|
506
|
+
totalCompletedTasks += m.totals.completedTasks;
|
|
507
|
+
totalInterrupts += m.totals.interrupts;
|
|
508
|
+
totalObservedInterruptCategories += m.totals.observedInterruptCategories;
|
|
509
|
+
totalCoveredInterruptCategories += m.totals.coveredInterruptCategories;
|
|
510
|
+
interruptRateSum += m.interruptRate;
|
|
511
|
+
autonomousCompletionRateSum += m.autonomousCompletionRate;
|
|
512
|
+
contextCoverageScoreSum += m.contextCoverageScore;
|
|
513
|
+
if (m.meanTimeToCorrectHours !== null) mttcValues.push(m.meanTimeToCorrectHours);
|
|
514
|
+
if (m.feedbackToDemoCycleTimeHours !== null) ftdValues.push(m.feedbackToDemoCycleTimeHours);
|
|
515
|
+
}
|
|
516
|
+
const count = iterations.length;
|
|
517
|
+
const round = (v) => Math.round(v * 1e3) / 1e3;
|
|
518
|
+
const avg = (values) => values.length === 0 ? null : round(values.reduce((s, v) => s + v, 0) / values.length);
|
|
519
|
+
return {
|
|
520
|
+
interruptRate: round(interruptRateSum / count),
|
|
521
|
+
autonomousCompletionRate: round(autonomousCompletionRateSum / count),
|
|
522
|
+
contextCoverageScore: round(contextCoverageScoreSum / count),
|
|
523
|
+
meanTimeToCorrectHours: avg(mttcValues),
|
|
524
|
+
feedbackToDemoCycleTimeHours: avg(ftdValues),
|
|
525
|
+
totals: {
|
|
526
|
+
taskAttempts: totalTaskAttempts,
|
|
527
|
+
completedTasks: totalCompletedTasks,
|
|
528
|
+
interrupts: totalInterrupts,
|
|
529
|
+
observedInterruptCategories: totalObservedInterruptCategories,
|
|
530
|
+
coveredInterruptCategories: totalCoveredInterruptCategories
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function runLoop(options) {
|
|
535
|
+
const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
536
|
+
const maxWallTimeMs = options.maxWallTimeMs ?? DEFAULT_MAX_WALL_TIME_MS;
|
|
537
|
+
const stopOnPiFailure = options.stopOnPiFailure ?? true;
|
|
538
|
+
const stopOnMayorIdleNoWork = options.stopOnMayorIdleNoWork ?? false;
|
|
539
|
+
const interruptRateThreshold = options.interruptRateThreshold ?? null;
|
|
540
|
+
const loopId = createLoopId();
|
|
541
|
+
const artifactsDir = options.runOptions.artifactsDir;
|
|
542
|
+
const loopDir = join(artifactsDir, "loops", loopId);
|
|
543
|
+
mkdirSync(loopDir, { recursive: true });
|
|
544
|
+
const loopStartedAt = Date.now();
|
|
545
|
+
const iterations = [];
|
|
546
|
+
let finalStopReason = "max-iterations-reached";
|
|
547
|
+
let needsMayorFollowUp = false;
|
|
548
|
+
appendJsonl(join(loopDir, "events.jsonl"), {
|
|
549
|
+
type: "loop_started",
|
|
550
|
+
loopId,
|
|
551
|
+
maxIterations,
|
|
552
|
+
maxWallTimeMs,
|
|
553
|
+
stopOnPiFailure,
|
|
554
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
555
|
+
});
|
|
556
|
+
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
|
557
|
+
if (needsMayorFollowUp) {
|
|
558
|
+
const settled = waitForBackgroundWorkToSettle({
|
|
559
|
+
artifactsDir,
|
|
560
|
+
maxWallTimeMs,
|
|
561
|
+
loopStartedAt
|
|
562
|
+
});
|
|
563
|
+
appendJsonl(join(loopDir, "events.jsonl"), {
|
|
564
|
+
type: "loop_background_work_settled",
|
|
565
|
+
loopId,
|
|
566
|
+
iteration,
|
|
567
|
+
timedOut: settled.timedOut,
|
|
568
|
+
boardSnapshot: settled.board,
|
|
569
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
570
|
+
});
|
|
571
|
+
if (settled.timedOut) {
|
|
572
|
+
finalStopReason = "max-wall-time-reached";
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
needsMayorFollowUp = false;
|
|
576
|
+
}
|
|
577
|
+
const iterationStart = Date.now();
|
|
578
|
+
let controllerResult;
|
|
579
|
+
try {
|
|
580
|
+
controllerResult = runController(options.runOptions);
|
|
581
|
+
} catch (error) {
|
|
582
|
+
appendJsonl(join(loopDir, "events.jsonl"), {
|
|
583
|
+
type: "loop_iteration_error",
|
|
584
|
+
loopId,
|
|
585
|
+
iteration,
|
|
586
|
+
error: error.message,
|
|
587
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
588
|
+
});
|
|
589
|
+
finalStopReason = "pi-exit-nonzero";
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
const iterationElapsedMs = Date.now() - iterationStart;
|
|
593
|
+
const totalElapsedMs = Date.now() - loopStartedAt;
|
|
594
|
+
const board = snapshotBoard(artifactsDir);
|
|
595
|
+
const metrics = controllerResult.metrics;
|
|
596
|
+
const { stopReason, continueReason } = evaluateStopCondition({
|
|
597
|
+
iteration,
|
|
598
|
+
maxIterations,
|
|
599
|
+
elapsedMs: totalElapsedMs,
|
|
600
|
+
maxWallTimeMs,
|
|
601
|
+
piExitCode: controllerResult.piInvocation.exitCode,
|
|
602
|
+
stopOnPiFailure,
|
|
603
|
+
stopOnMayorIdleNoWork,
|
|
604
|
+
board,
|
|
605
|
+
metrics,
|
|
606
|
+
interruptRateThreshold
|
|
607
|
+
});
|
|
608
|
+
const iterationResult = {
|
|
609
|
+
iteration,
|
|
610
|
+
controllerResult,
|
|
611
|
+
boardSnapshot: board,
|
|
612
|
+
metrics,
|
|
613
|
+
elapsedMs: iterationElapsedMs,
|
|
614
|
+
continueReason,
|
|
615
|
+
stopReason
|
|
616
|
+
};
|
|
617
|
+
iterations.push(iterationResult);
|
|
618
|
+
writeJson(join(loopDir, `iteration-${iteration}.json`), {
|
|
619
|
+
iteration,
|
|
620
|
+
runId: controllerResult.runId,
|
|
621
|
+
boardSnapshot: board,
|
|
622
|
+
metrics,
|
|
623
|
+
elapsedMs: iterationElapsedMs,
|
|
624
|
+
continueReason,
|
|
625
|
+
stopReason
|
|
626
|
+
});
|
|
627
|
+
appendJsonl(join(loopDir, "events.jsonl"), {
|
|
628
|
+
type: "loop_iteration_completed",
|
|
629
|
+
loopId,
|
|
630
|
+
iteration,
|
|
631
|
+
runId: controllerResult.runId,
|
|
632
|
+
piExitCode: controllerResult.piInvocation.exitCode,
|
|
633
|
+
stopReason,
|
|
634
|
+
continueReason,
|
|
635
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
636
|
+
});
|
|
637
|
+
if (options.onIterationComplete) options.onIterationComplete(iterationResult);
|
|
638
|
+
if (stopReason !== null) {
|
|
639
|
+
finalStopReason = stopReason;
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
needsMayorFollowUp = hasBackgroundWork(board);
|
|
643
|
+
}
|
|
644
|
+
const totalElapsedMs = Date.now() - loopStartedAt;
|
|
645
|
+
const lastIteration = iterations.at(-1);
|
|
646
|
+
const finalBoard = lastIteration ? lastIteration.boardSnapshot : snapshotBoard(artifactsDir);
|
|
647
|
+
const aggregate = aggregateMetrics(iterations);
|
|
648
|
+
const loopResult = {
|
|
649
|
+
loopId,
|
|
650
|
+
iterations,
|
|
651
|
+
stopReason: finalStopReason,
|
|
652
|
+
totalIterations: iterations.length,
|
|
653
|
+
totalElapsedMs,
|
|
654
|
+
finalBoardSnapshot: finalBoard,
|
|
655
|
+
aggregateMetrics: aggregate
|
|
656
|
+
};
|
|
657
|
+
writeJson(join(loopDir, "loop-summary.json"), {
|
|
658
|
+
loopId,
|
|
659
|
+
stopReason: finalStopReason,
|
|
660
|
+
totalIterations: iterations.length,
|
|
661
|
+
totalElapsedMs,
|
|
662
|
+
finalBoardSnapshot: finalBoard,
|
|
663
|
+
aggregateMetrics: aggregate
|
|
664
|
+
});
|
|
665
|
+
appendJsonl(join(loopDir, "events.jsonl"), {
|
|
666
|
+
type: "loop_finished",
|
|
667
|
+
loopId,
|
|
668
|
+
stopReason: finalStopReason,
|
|
669
|
+
totalIterations: iterations.length,
|
|
670
|
+
totalElapsedMs,
|
|
671
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
672
|
+
});
|
|
673
|
+
return loopResult;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
//#endregion
|
|
677
|
+
export { runLoop as t };
|
|
678
|
+
//# sourceMappingURL=loop-CocC9qO1.mjs.map
|