@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.
@@ -1,141 +1,11 @@
1
- import { n as detectPiAuthFailure, t as createPiAuthHelpMessage } from "./pi-C0fURZj7.mjs";
2
- import { a as runCommandSync, n as assertCommandAvailable } from "./entrypoint-CyJDLudQ.mjs";
3
- import { d as createRepoSlug, f as getCurrentBranch, m as getRepoRoot, p as getRepoIdentity } from "./config-CUpe9o0x.mjs";
4
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
5
- import { dirname, join } from "node:path";
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 leader agent for this repository.",
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 leader agent for this repository.",
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 existingLeaderState = readAgentState(artifactsDir, "leader");
363
- const existingLeaderSession = existingLeaderState?.session.sessionPath || existingLeaderState?.session.sessionDir ? existingLeaderState.session : getLatestAgentSession(artifactsDir, "leader");
364
- const leaderSessionDir = existingLeaderSession.sessionDir ?? getAgentSessionsDir(artifactsDir, "leader");
365
- const leaderState = createAgentState({
366
- agentId: "leader",
367
- role: "leader",
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 leader run for goal: ${goal}` : "Starting leader run",
241
+ lastMessage: goal ? `Starting mayor run for goal: ${goal}` : "Starting mayor run",
372
242
  runId,
373
243
  session: createAgentSessionRecord({
374
- sessionDir: leaderSessionDir,
375
- sessionId: existingLeaderSession.sessionId,
376
- sessionPath: existingLeaderSession.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, leaderState);
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
- ...leaderState,
289
+ ...mayorState,
420
290
  status: "running",
421
- lastMessage: goal ? `Leader working on: ${goal}` : "Leader working"
291
+ lastMessage: goal ? `Mayor working on: ${goal}` : "Mayor working"
422
292
  }));
423
293
  const piResult = runCommandSync(piCommand, createPiInvocationArgs({
424
- sessionDir: leaderState.session.sessionPath === null ? leaderSessionDir : null,
425
- sessionPath: leaderState.session.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 latestLeaderSession = getLatestAgentSession(artifactsDir, "leader");
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: latestLeaderSession.sessionDir,
444
- sessionId: latestLeaderSession.sessionId,
445
- sessionPath: latestLeaderSession.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
- ...leaderState,
361
+ ...mayorState,
492
362
  status: piInvocation.exitCode === 0 ? "idle" : "blocked",
493
- lastMessage: piInvocation.exitCode === 0 ? "Leader run completed and is ready for the next instruction" : `Leader run stopped with exit code ${piInvocation.exitCode}`,
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: latestLeaderSession.sessionDir,
498
- sessionId: latestLeaderSession.sessionId,
499
- sessionPath: latestLeaderSession.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
- export { createAgentState as a, getLatestAgentSession as c, readAgentState as d, writeAgentState as f, createAgentSessionRecord as i, listAgentStates as l, computeMetrics as n, getAgentDir as o, appendJsonl as p, appendAgentMessage as r, getAgentSessionsDir as s, runController as t, readAgentMessages as u };
525
- //# sourceMappingURL=controller-9ihAZj3V.mjs.map
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