agent-lyceum 0.4.14 → 0.6.0

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.
Files changed (47) hide show
  1. package/README.md +21 -21
  2. package/README.zh-TW.md +21 -21
  3. package/dist/adapters/claude.js +2 -0
  4. package/dist/ask-answer.d.ts +10 -0
  5. package/dist/ask-answer.js +21 -0
  6. package/dist/ask-reply.d.ts +65 -0
  7. package/dist/ask-reply.js +165 -0
  8. package/dist/assert.d.ts +2 -0
  9. package/dist/assert.js +6 -0
  10. package/dist/cli.js +202 -16
  11. package/dist/config.d.ts +21 -16
  12. package/dist/config.js +19 -7
  13. package/dist/dispatcher.d.ts +3 -34
  14. package/dist/dispatcher.js +4 -464
  15. package/dist/doctor.js +3 -0
  16. package/dist/format.js +7 -5
  17. package/dist/fs-util.d.ts +2 -0
  18. package/dist/fs-util.js +15 -0
  19. package/dist/guard.d.ts +8 -0
  20. package/dist/guard.js +24 -3
  21. package/dist/mailbox.d.ts +11 -1
  22. package/dist/mailbox.js +21 -8
  23. package/dist/memory-tidy.d.ts +115 -0
  24. package/dist/memory-tidy.js +351 -0
  25. package/dist/policy.js +3 -3
  26. package/dist/process-runner.js +1 -2
  27. package/dist/project-lock.d.ts +10 -1
  28. package/dist/project-lock.js +39 -4
  29. package/dist/prompt.d.ts +20 -0
  30. package/dist/prompt.js +76 -3
  31. package/dist/run-cleanup.js +4 -3
  32. package/dist/run-log.d.ts +5 -0
  33. package/dist/run-log.js +18 -0
  34. package/dist/run-outcome.d.ts +22 -0
  35. package/dist/run-outcome.js +28 -0
  36. package/dist/run-session.d.ts +92 -0
  37. package/dist/run-session.js +602 -0
  38. package/dist/run-store.d.ts +3 -1
  39. package/dist/run-store.js +7 -6
  40. package/dist/scaffold.js +1 -0
  41. package/dist/schema.d.ts +72 -239
  42. package/dist/schema.js +8 -3
  43. package/dist/status.d.ts +9 -1
  44. package/dist/status.js +51 -15
  45. package/dist/validate.js +12 -8
  46. package/dist/worktree.js +7 -6
  47. package/package.json +14 -8
package/dist/cli.js CHANGED
@@ -4,6 +4,8 @@ import path from "node:path";
4
4
  import readline from "node:readline/promises";
5
5
  import { Command } from "commander";
6
6
  import { ConfigError, findProjectForCwd, flattenResolved, listProjects, resolveProject, resolveProjectWithSources } from "./config.js";
7
+ import { openInEditor, prepareAnswers } from "./ask-answer.js";
8
+ import { restoreMemory, tidyMemory, tidyStamp } from "./memory-tidy.js";
7
9
  import { RESULT_FILE, runTeam } from "./dispatcher.js";
8
10
  import { executeRunCleanup, planRunCleanup } from "./run-cleanup.js";
9
11
  import { diagnoseProject, formatDoctor, preflightRuntimes } from "./doctor.js";
@@ -15,6 +17,7 @@ import { addProject, initHome, removeProject } from "./scaffold.js";
15
17
  import { buildStatusReport, buildTaskListReport, formatMonitor, formatRunDetail, formatStatusWithLog, formatTaskList, latestUnfinishedRun, runIsAlive } from "./status.js";
16
18
  import { prepareTask, readTaskFile } from "./task.js";
17
19
  import { formatEnforcement, validateProject } from "./validate.js";
20
+ import { must } from "./assert.js";
18
21
  const program = new Command();
19
22
  program
20
23
  .name("agent-lyceum")
@@ -170,9 +173,13 @@ function cancelOnSignals() {
170
173
  const onTerm = handler("Terminated");
171
174
  process.on("SIGINT", onInt);
172
175
  process.on("SIGTERM", onTerm);
173
- return { signal: ac.signal, dispose: () => (process.off("SIGINT", onInt), process.off("SIGTERM", onTerm)) };
176
+ return { signal: ac.signal, dispose: () => {
177
+ process.off("SIGINT", onInt);
178
+ process.off("SIGTERM", onTerm);
179
+ },
180
+ };
174
181
  }
175
- function reportRun(summary, runDir) {
182
+ function printRun(summary, runDir) {
176
183
  console.log(`\nRun ${summary.runId} ended: ${summary.endReason}, outcome: ${summary.outcome}, after ${summary.rounds} round(s). Logs: ${path.join(runDir, "log.jsonl")}`);
177
184
  if (summary.outcomeNote)
178
185
  console.log(`Note: ${summary.outcomeNote}`);
@@ -182,13 +189,72 @@ function reportRun(summary, runDir) {
182
189
  console.log(`\nLead's final message — ${summary.doneMessage.subject}\n\n${summary.doneMessage.body}`);
183
190
  console.log(`\nResult saved to: ${path.join(runDir, RESULT_FILE)}`);
184
191
  }
192
+ }
193
+ function reportRun(summary, runDir) {
194
+ printRun(summary, runDir);
185
195
  process.exit(exitCodeForOutcome(summary.outcome));
186
196
  }
197
+ /** Take the lock and continue a run; the lock is released when it stops (also when it stops because it waits for answers). */
198
+ async function continueRun(pr, dir, runId) {
199
+ const lease = takeLock(pr, runId);
200
+ const cancel = cancelOnSignals();
201
+ try {
202
+ console.log(`Project ${pr.name} — repo ${pr.dir}`);
203
+ const state = loadRunState(dir);
204
+ console.log(`Resuming run ${state.run_id} (${state.end_reason ?? "interrupted"}) at round ${state.rounds}/${pr.dispatcher.max_rounds}: ${state.task_summary}`);
205
+ // Read the state again under the lock: another process may have changed it while we were checking.
206
+ return await runTeam({ project: pr, resume: state, runDir: dir, signal: cancel.signal });
207
+ }
208
+ finally {
209
+ cancel.dispose();
210
+ lease.release();
211
+ }
212
+ }
213
+ const interactive = () => !!process.stdin.isTTY && !!process.stdout.isTTY;
214
+ function printWaiting(pr, runId, file, problems) {
215
+ console.log(`\nRun ${runId} is waiting for your answers.\n File: ${file}`);
216
+ for (const p of problems)
217
+ console.log(` - ${p}`);
218
+ console.log(`Answer with: agent-lyceum answer ${runId} -p ${pr.name} (or edit the file, then: agent-lyceum resume ${runId} -p ${pr.name})`);
219
+ }
220
+ /**
221
+ * Keep going while the run stops to ask and the questions can be settled right now: by --assume-defaults, or (in a terminal)
222
+ * by opening the editor. Otherwise print where to answer and exit 3.
223
+ */
224
+ async function driveRun(pr, runDir, first, assumeDefaults) {
225
+ let summary = first;
226
+ while (summary.outcome === "waiting") {
227
+ let prep = prepareAnswers(runDir, { assumeDefaults });
228
+ while (!prep.complete && interactive()) {
229
+ console.log(`\nRun ${summary.runId} needs your answers (${prep.path}).`);
230
+ openInEditor(prep.path);
231
+ prep = prepareAnswers(runDir, {});
232
+ if (prep.complete)
233
+ break;
234
+ console.log("Some answers are missing or invalid:");
235
+ for (const p of prep.problems)
236
+ console.log(` - ${p}`);
237
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
238
+ const again = (await rl.question("Edit again? [Y/n] ")).trim().toLowerCase();
239
+ rl.close();
240
+ if (again === "n" || again === "no")
241
+ break;
242
+ }
243
+ if (!prep.complete) {
244
+ printRun(summary, runDir);
245
+ printWaiting(pr, summary.runId, prep.path, prep.problems);
246
+ process.exit(3);
247
+ }
248
+ summary = await continueRun(pr, runDir, summary.runId);
249
+ }
250
+ return reportRun(summary, runDir);
251
+ }
187
252
  program
188
253
  .command("run [task]")
189
254
  .description('Start the dispatcher: give the task to the lead. Use "<task>" or --task-file <path>.')
190
255
  .option("-p, --project <name>")
191
256
  .option("--task-file <path>", "read the task from a file")
257
+ .option("--assume-defaults", "when the run stops to ask, use the suggested answer for every question that has one")
192
258
  .action(async (task, opts) => {
193
259
  try {
194
260
  const pr = loadProject(opts.project);
@@ -213,7 +279,7 @@ program
213
279
  cancel.dispose();
214
280
  lease.release();
215
281
  }
216
- reportRun(summary, runDir);
282
+ await driveRun(pr, runDir, summary, !!opts.assumeDefaults);
217
283
  }
218
284
  catch (e) {
219
285
  fail(e instanceof ConfigError ? e.message : e.message);
@@ -223,6 +289,7 @@ program
223
289
  .command("resume [run-id]")
224
290
  .description("Continue an interrupted (or failed) run: same run dir, sessions and round count; the task is not re-sent. Without an id, picks the newest run that is not done or running; with an id, continues that run.")
225
291
  .option("-p, --project <name>")
292
+ .option("--assume-defaults", "if the run waits for answers, use the suggested answer for every unanswered question that has one")
226
293
  .action(async (runId, opts) => {
227
294
  try {
228
295
  const pr = loadProject(opts.project);
@@ -258,20 +325,51 @@ program
258
325
  }
259
326
  if (state.rounds >= pr.dispatcher.max_rounds)
260
327
  fail(`Run ${state.run_id} used ${state.rounds}/${pr.dispatcher.max_rounds} rounds; raise dispatcher.max_rounds in project.yaml first.`);
261
- const lease = takeLock(pr, state.run_id);
262
- let summary;
263
- const cancel = cancelOnSignals();
264
- try {
265
- console.log(`Project ${pr.name} — repo ${pr.dir}`);
266
- console.log(`Resuming run ${state.run_id} (${state.end_reason ?? "interrupted"}) at round ${state.rounds}/${pr.dispatcher.max_rounds}: ${state.task_summary}`);
267
- // Read the state again under the lock: another process may have changed it while we were checking.
268
- summary = await runTeam({ project: pr, resume: loadRunState(dir), runDir: dir, signal: cancel.signal });
328
+ if (state.end_reason === "waiting") {
329
+ const prep = prepareAnswers(dir, { assumeDefaults: !!opts.assumeDefaults });
330
+ if (!prep.complete) {
331
+ printWaiting(pr, state.run_id, prep.path, prep.problems);
332
+ process.exit(3);
333
+ }
269
334
  }
270
- finally {
271
- cancel.dispose();
272
- lease.release();
335
+ const summary = await continueRun(pr, dir, state.run_id);
336
+ await driveRun(pr, dir, summary, !!opts.assumeDefaults);
337
+ }
338
+ catch (e) {
339
+ fail(e instanceof ConfigError ? e.message : e.message);
340
+ }
341
+ });
342
+ program
343
+ .command("answer <run-id>")
344
+ .description("Answer the questions a waiting run asked: opens ask-reply.md in $EDITOR, checks the answers, then continues the run. With --no-edit it only checks a file you already edited.")
345
+ .option("-p, --project <name>")
346
+ .option("--no-edit", "do not open the editor; check the file as it is")
347
+ .option("--assume-defaults", "use the suggested answer for every unanswered question that has one")
348
+ .action(async (runId, opts) => {
349
+ try {
350
+ assertName("run", runId);
351
+ const pr = loadProject(opts.project);
352
+ const dir = path.join(pr.paths.runs, runId);
353
+ if (!fs.existsSync(path.join(dir, "state.json")))
354
+ fail(`Run "${runId}" not found in ${pr.paths.runs}.`);
355
+ const state = loadRunState(dir);
356
+ if (state.end_reason !== "waiting")
357
+ fail(`Run ${runId} is not waiting for answers (${state.end_reason ?? "not finished"}).`);
358
+ if (opts.edit)
359
+ openInEditor(prepareAnswers(dir, {}).path);
360
+ const prep = prepareAnswers(dir, { assumeDefaults: !!opts.assumeDefaults });
361
+ if (!prep.complete) {
362
+ printWaiting(pr, runId, prep.path, prep.problems);
363
+ process.exit(3);
273
364
  }
274
- reportRun(summary, dir);
365
+ const res = validateProject(pr);
366
+ for (const i of res.issues)
367
+ console.error(`${i.level === "error" ? "ERROR" : "warn "} ${i.message}`);
368
+ if (!res.ok)
369
+ fail("Configuration is invalid; fix the errors above (see `agent-lyceum validate`).");
370
+ await preflight(pr);
371
+ const summary = await continueRun(pr, dir, runId);
372
+ await driveRun(pr, dir, summary, !!opts.assumeDefaults);
275
373
  }
276
374
  catch (e) {
277
375
  fail(e instanceof ConfigError ? e.message : e.message);
@@ -354,7 +452,7 @@ configCmd
354
452
  const keys = Object.keys(sources).filter((k) => values[k] !== undefined);
355
453
  const w = Math.max(...keys.map((k) => `${k} = ${show(values[k])}`.length));
356
454
  for (const k of keys)
357
- console.log(`${`${k} = ${show(values[k])}`.padEnd(w)} ${where(sources[k])}`);
455
+ console.log(`${`${k} = ${show(values[k])}`.padEnd(w)} ${where(must(sources[k], `source of ${k}`))}`);
358
456
  }
359
457
  catch (e) {
360
458
  fail(e instanceof ConfigError ? e.message : e.message);
@@ -402,6 +500,94 @@ program
402
500
  fail(e.message);
403
501
  }
404
502
  });
503
+ const memoryCmd = program.command("memory").description("Look after the agents' long-term memory");
504
+ function layerOpt(v) {
505
+ if (v === undefined || v === "project")
506
+ return "project";
507
+ if (v === "global")
508
+ return "global";
509
+ return fail(`--layer must be "project" or "global" (task memory is not tidied: \`clear\` deletes it).`);
510
+ }
511
+ memoryCmd
512
+ .command("tidy")
513
+ .description("Have agents tidy their own memory: merge duplicates, fix stale entries, archive what is obsolete (never deletes). Manual only; nothing triggers it automatically.")
514
+ .option("-p, --project <name>")
515
+ .option("--agent <name>", "tidy only this agent (default: every agent)")
516
+ .option("--layer <layer>", "project (default) or global; global memory is shared by every project, so it is never tidied unless you ask")
517
+ .option("--dry-run", "only show the memory sizes and what changed in the project; wake nobody, change nothing")
518
+ .action(async (opts) => {
519
+ try {
520
+ const layer = layerOpt(opts.layer);
521
+ const pr = loadProject(opts.project);
522
+ if (opts.dryRun) {
523
+ const dry = await tidyMemory({ project: pr, layer, agent: opts.agent, dryRun: true });
524
+ if (dry.length === 0)
525
+ console.log(`No agent has ${layer} memory configured. ${layer === "project" ? "The template gives agents global memory only: set `memory.project` for an agent in project.yaml (or use --layer global)." : "Set `memory.global` for an agent."}`);
526
+ return;
527
+ }
528
+ const res = validateProject(pr);
529
+ for (const i of res.issues)
530
+ console.error(`${i.level === "error" ? "ERROR" : "warn "} ${i.message}`);
531
+ if (!res.ok)
532
+ fail("Configuration is invalid; fix the errors above (see `agent-lyceum validate`).");
533
+ await preflight(pr);
534
+ const lease = takeLock(pr, `tidy-${tidyStamp()}`);
535
+ const cancel = cancelOnSignals();
536
+ let results;
537
+ try {
538
+ results = await tidyMemory({ project: pr, layer, agent: opts.agent, signal: cancel.signal });
539
+ }
540
+ finally {
541
+ cancel.dispose();
542
+ lease.release();
543
+ }
544
+ if (results.length === 0)
545
+ console.log(`No agent has ${layer} memory configured. ${layer === "project" ? "The template gives agents global memory only: set `memory.project` for an agent in project.yaml (or tidy global memory with --layer global)." : "Set `memory.global` for an agent."}`);
546
+ for (const r of results) {
547
+ if (r.status === "tidied")
548
+ console.log(` ${r.agent} (${r.layer}): tidied. Archive: ${path.join(r.dir, ".archive", r.archive ?? "")} — undo with: agent-lyceum memory restore ${r.archive} -p ${pr.name} --agent ${r.agent}${r.layer === "global" ? " --layer global" : ""}`);
549
+ else if (r.status === "skipped")
550
+ console.log(` ${r.agent} (${r.layer}): skipped — ${r.problems.join("; ")}`);
551
+ else if (r.status === "failed") {
552
+ console.error(` ${r.agent} (${r.layer}): FAILED, memory restored to how it was`);
553
+ for (const p of r.problems)
554
+ console.error(` - ${p}`);
555
+ }
556
+ }
557
+ process.exit(results.some((r) => r.status === "failed") ? 1 : 0);
558
+ }
559
+ catch (e) {
560
+ fail(e instanceof ConfigError ? e.message : e.message);
561
+ }
562
+ });
563
+ memoryCmd
564
+ .command("restore <stamp>")
565
+ .description("Move the files of one tidy's archive back (nothing that exists now is overwritten) and re-add them to MEMORY.md")
566
+ .option("-p, --project <name>")
567
+ .requiredOption("--agent <name>")
568
+ .option("--layer <layer>", "project (default) or global")
569
+ .action((stamp, opts) => {
570
+ try {
571
+ const layer = layerOpt(opts.layer);
572
+ const pr = loadProject(opts.project);
573
+ const lease = takeLock(pr, `restore-${tidyStamp()}`);
574
+ try {
575
+ const r = restoreMemory({ project: pr, agent: opts.agent, layer, stamp });
576
+ for (const f of r.restored)
577
+ console.log(` restored ${f}`);
578
+ for (const s of r.skipped)
579
+ console.log(` skipped ${s.rel}: ${s.reason}`);
580
+ if (!r.restored.length && !r.skipped.length)
581
+ console.log(`Archive ${stamp} holds no files to restore.`);
582
+ }
583
+ finally {
584
+ lease.release();
585
+ }
586
+ }
587
+ catch (e) {
588
+ fail(e.message);
589
+ }
590
+ });
405
591
  function useColor() {
406
592
  if (process.env.NO_COLOR)
407
593
  return false;
package/dist/config.d.ts CHANGED
@@ -18,6 +18,8 @@ export interface ResolvedAgent {
18
18
  resume: boolean;
19
19
  canMessage: "all" | string[];
20
20
  canEditAgentMd: boolean;
21
+ /** May send `type: ask` mail to the user (the run then waits for the answer). */
22
+ canAskUser: boolean;
21
23
  owns: string[];
22
24
  /** Where each field's value came from (for `validate` output). */
23
25
  sources: Record<string, Source>;
@@ -44,45 +46,48 @@ export declare function loadGlobal(home: string): {
44
46
  agents: Record<string, {
45
47
  runtime?: "claude-code" | "codex" | undefined;
46
48
  model?: string | undefined;
47
- effort?: "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
49
+ effort?: "high" | "low" | "max" | "medium" | "minimal" | "xhigh" | undefined;
48
50
  agent_md?: string | undefined;
49
51
  memory?: {
50
52
  global?: string | undefined;
51
53
  project?: string | undefined;
52
54
  } | undefined;
53
55
  resume?: boolean | undefined;
54
- can_message?: string[] | "all" | undefined;
56
+ can_message?: "all" | string[] | undefined;
55
57
  can_edit_agent_md?: boolean | undefined;
58
+ can_ask_user?: boolean | undefined;
56
59
  owns?: string[] | undefined;
57
60
  }>;
58
61
  };
59
62
  export declare function loadProjectRaw(home: string, name: string): {
63
+ dir: string;
64
+ team: {
65
+ lead: string;
66
+ };
67
+ dispatcher: {
68
+ max_rounds?: number | undefined;
69
+ max_parallel?: number | undefined;
70
+ wake_timeout_sec?: number | undefined;
71
+ retry?: number | undefined;
72
+ strict?: boolean | undefined;
73
+ workspace_mode?: "auto" | "shared" | "worktree" | undefined;
74
+ log_max_bytes?: number | undefined;
75
+ };
60
76
  agents: Record<string, {
61
77
  runtime?: "claude-code" | "codex" | undefined;
62
78
  model?: string | undefined;
63
- effort?: "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
79
+ effort?: "high" | "low" | "max" | "medium" | "minimal" | "xhigh" | undefined;
64
80
  agent_md?: string | undefined;
65
81
  memory?: {
66
82
  global?: string | undefined;
67
83
  project?: string | undefined;
68
84
  } | undefined;
69
85
  resume?: boolean | undefined;
70
- can_message?: string[] | "all" | undefined;
86
+ can_message?: "all" | string[] | undefined;
71
87
  can_edit_agent_md?: boolean | undefined;
88
+ can_ask_user?: boolean | undefined;
72
89
  owns?: string[] | undefined;
73
90
  }>;
74
- dir: string;
75
- team: {
76
- lead: string;
77
- };
78
- dispatcher: {
79
- strict?: boolean | undefined;
80
- max_rounds?: number | undefined;
81
- max_parallel?: number | undefined;
82
- wake_timeout_sec?: number | undefined;
83
- retry?: number | undefined;
84
- workspace_mode?: "shared" | "auto" | "worktree" | undefined;
85
- };
86
91
  };
87
92
  export declare function resolveProject(home: string, name: string): ResolvedProject;
88
93
  /** Registered projects: name -> repo dir (best effort; skips broken configs). */
package/dist/config.js CHANGED
@@ -112,21 +112,31 @@ export function resolveProject(home, name) {
112
112
  let runtime = pl.runtime;
113
113
  if (runtime !== undefined)
114
114
  sources.runtime = "project";
115
- else if ((runtime = inferRuntime(pl.model)))
116
- sources.runtime = "model";
117
- else if ((runtime = gl.runtime))
118
- sources.runtime = "global";
119
- else if ((runtime = inferRuntime(gl.model)))
120
- sources.runtime = "model";
115
+ else {
116
+ const candidates = [
117
+ [inferRuntime(pl.model), "model"],
118
+ [gl.runtime, "global"],
119
+ [inferRuntime(gl.model), "model"],
120
+ ];
121
+ for (const [r, from] of candidates) {
122
+ if (!r)
123
+ continue;
124
+ runtime = r;
125
+ sources.runtime = from;
126
+ break;
127
+ }
128
+ }
121
129
  const isLead = agentName === lead;
122
130
  const resume = pick("resume");
123
131
  const canMessage = pick("can_message");
124
132
  const canEdit = pick("can_edit_agent_md");
133
+ const canAsk = pick("can_ask_user");
125
134
  const owns = pick("owns");
126
135
  for (const [k, v] of [
127
136
  ["resume", resume],
128
137
  ["can_message", canMessage],
129
138
  ["can_edit_agent_md", canEdit],
139
+ ["can_ask_user", canAsk],
130
140
  ["owns", owns],
131
141
  ]) {
132
142
  if (v === undefined)
@@ -142,6 +152,7 @@ export function resolveProject(home, name) {
142
152
  resume: resume ?? isLead,
143
153
  canMessage: canMessage ?? (isLead ? "all" : [lead]),
144
154
  canEditAgentMd: canEdit ?? isLead,
155
+ canAskUser: canAsk ?? isLead,
145
156
  owns: owns ?? [],
146
157
  sources,
147
158
  };
@@ -190,7 +201,7 @@ export function findProjectForCwd(home, cwd) {
190
201
  }
191
202
  return best?.name;
192
203
  }
193
- const AGENT_FIELDS = ["runtime", "model", "effort", "agent_md", "memory.global", "memory.project", "resume", "can_message", "can_edit_agent_md", "owns"];
204
+ const AGENT_FIELDS = ["runtime", "model", "effort", "agent_md", "memory.global", "memory.project", "resume", "can_message", "can_edit_agent_md", "can_ask_user", "owns"];
194
205
  /** The resolved project plus, for every setting, the file and key it was taken from (or "default"). */
195
206
  export function resolveProjectWithSources(home, name) {
196
207
  const project = resolveProject(home, name);
@@ -247,6 +258,7 @@ export function flattenResolved(project) {
247
258
  v[`${p}.resume`] = a.resume;
248
259
  v[`${p}.can_message`] = a.canMessage;
249
260
  v[`${p}.can_edit_agent_md`] = a.canEditAgentMd;
261
+ v[`${p}.can_ask_user`] = a.canAskUser;
250
262
  v[`${p}.owns`] = a.owns;
251
263
  }
252
264
  return v;
@@ -1,37 +1,6 @@
1
- import { type Invoker } from "./adapters/index.js";
2
- import type { ResolvedProject } from "./config.js";
3
- import type { RunOutcome } from "./schema.js";
4
- import { newRunId, type EndReason, type RunState } from "./run-store.js";
5
- import { type PreparedTask } from "./task.js";
1
+ import { type RunOptions, type RunSummary } from "./run-session.js";
2
+ import { newRunId } from "./run-store.js";
6
3
  export { newRunId };
7
4
  export type { ActiveWake, EndReason, RunState, SentTopic, WakeRecord, WakeTopic } from "./run-store.js";
8
- export declare const RESULT_FILE = "result.md";
9
- /** How many times a non-conforming `done` goes back to the lead before it is accepted as `partial`. */
10
- export declare const MAX_DONE_REJECTIONS = 2;
11
- export interface RunSummary {
12
- runId: string;
13
- runDir: string;
14
- rounds: number;
15
- endReason: EndReason;
16
- /** How the work turned out; only "completed" means success. */
17
- outcome: RunOutcome;
18
- outcomeNote?: string;
19
- verification?: string;
20
- doneMessage?: {
21
- subject: string;
22
- body: string;
23
- };
24
- }
25
- export interface RunOptions {
26
- project: ResolvedProject;
27
- /** The task for a new run; omit when resuming. */
28
- task?: PreparedTask;
29
- /** Continue this earlier run (same run dir, sessions and counters) instead of starting a new one. */
30
- resume?: RunState;
31
- runDir: string;
32
- invoker?: Invoker;
33
- /** Stop the run when this fires: running agents are killed, unread mail is kept, the run ends `cancelled` and can be resumed. */
34
- signal?: AbortSignal;
35
- log?: (line: string) => void;
36
- }
5
+ export { MAX_DONE_REJECTIONS, RESULT_FILE, type RunOptions, type RunSummary } from "./run-session.js";
37
6
  export declare function runTeam(opts: RunOptions): Promise<RunSummary>;