@ordewell/core 0.11.3 → 0.12.1

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 (49) hide show
  1. package/AUTHORS.md +3 -1
  2. package/dist/{ModeResolver-DyXeZ9ib.d.ts → ModeResolver-B64aUZXJ.d.ts} +1 -1
  3. package/dist/{ModeResolver-BDAgouuE.d.mts → ModeResolver-CA2fUU9h.d.mts} +1 -1
  4. package/dist/{Task-BL4Idm9r.d.mts → Task-CxGgN6I0.d.mts} +801 -39
  5. package/dist/{Task-BL4Idm9r.d.ts → Task-CxGgN6I0.d.ts} +801 -39
  6. package/dist/{chunk-YAFNYNFX.mjs → chunk-2IFKPYPO.mjs} +453 -17
  7. package/dist/chunk-2IFKPYPO.mjs.map +1 -0
  8. package/dist/{chunk-MYXMIVWA.mjs → chunk-FX33UKAD.mjs} +8 -6
  9. package/dist/chunk-FX33UKAD.mjs.map +1 -0
  10. package/dist/{chunk-QTZ5XTBY.mjs → chunk-PKJTN2PD.mjs} +458 -13
  11. package/dist/chunk-PKJTN2PD.mjs.map +1 -0
  12. package/dist/{chunk-7WNLN63J.mjs → chunk-RZ23BMVR.mjs} +599 -124
  13. package/dist/chunk-RZ23BMVR.mjs.map +1 -0
  14. package/dist/chunk-UDUATSCJ.mjs +48 -0
  15. package/dist/chunk-UDUATSCJ.mjs.map +1 -0
  16. package/dist/{IRunner-DOMtDTVt.d.ts → eventStore-CTCSKS9E.d.ts} +976 -145
  17. package/dist/{IRunner-ihalYfTo.d.mts → eventStore-CcB8yELZ.d.mts} +976 -145
  18. package/dist/index.d.mts +905 -164
  19. package/dist/index.d.ts +905 -164
  20. package/dist/index.js +12496 -3308
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +10830 -2919
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/parsing.d.mts +2 -2
  25. package/dist/parsing.d.ts +2 -2
  26. package/dist/parsing.js +277 -21
  27. package/dist/parsing.js.map +1 -1
  28. package/dist/parsing.mjs +3 -3
  29. package/dist/plan-utils.d.mts +8 -5
  30. package/dist/plan-utils.d.ts +8 -5
  31. package/dist/plan-utils.js +262 -33
  32. package/dist/plan-utils.js.map +1 -1
  33. package/dist/plan-utils.mjs +39 -3
  34. package/dist/{runSettingsText-DVOd7vKC.d.ts → runSettingsText-DFVpe6aU.d.mts} +1996 -895
  35. package/dist/{runSettingsText-BuLVBlXS.d.mts → runSettingsText-lKLDsyli.d.ts} +1996 -895
  36. package/dist/testing.d.mts +99 -4
  37. package/dist/testing.d.ts +99 -4
  38. package/dist/testing.js +213 -8
  39. package/dist/testing.js.map +1 -1
  40. package/dist/testing.mjs +193 -8
  41. package/dist/testing.mjs.map +1 -1
  42. package/docker/task.Dockerfile +22 -0
  43. package/package.json +3 -1
  44. package/skills/diagnose/SKILL.md +2 -2
  45. package/skills/explore/SKILL.md +1 -1
  46. package/dist/chunk-7WNLN63J.mjs.map +0 -1
  47. package/dist/chunk-MYXMIVWA.mjs.map +0 -1
  48. package/dist/chunk-QTZ5XTBY.mjs.map +0 -1
  49. package/dist/chunk-YAFNYNFX.mjs.map +0 -1
@@ -1,119 +1,7 @@
1
1
  import { ChildProcess } from 'child_process';
2
- import { aS as Task, az as RunnerId, b6 as UsageRecord, aQ as SubagentOutcome, aA as RunnerSessionCost, i as DiscoveredModel, a$ as TaskSkillSnapshot, p as HandoffCheck } from './Task-BL4Idm9r.js';
2
+ import { aB as PlannerAllowlist, b3 as RunnerId, az as PlanStatus, aj as LegacyPlanState, ay as PlanState, bk as SkillsService, bf as SkillInfo, aS as RepoGroupLayout, T as IWorktreeIsolation, ab as IsolationRun, X as IsolationHandoff, ax as PlanIsolation, ac as IsolationTaskRecord, bt as TaskIsolation, ag as IsolationView, a6 as IsolationRemoval, bp as Task, aH as PrepareIntent, aL as PreviousWork, J as HandoffCommit, a3 as IsolationOutcome, aP as RepairEvidence, bE as TreeSnapshot, bq as TaskDiffRead, br as TaskDiffResult, a2 as IsolationMergeResult, ai as LandedWork, b0 as RevertWatch, a9 as IsolationRevertResult, bG as UsageRecord, bm as SubagentOutcome, b4 as RunnerSessionCost, p as DiscoveredModel, bJ as VerificationCheck, bz as TaskSkillSnapshot, b7 as ScriptBody, I as HandoffCheck } from './Task-CxGgN6I0.js';
3
3
  import { z } from 'zod/v4';
4
4
 
5
- /** A script task's time limit when it sets none. */
6
- declare const DEFAULT_SCRIPT_TIMEOUT_MS: number;
7
- /** What a script task runs, its time limit filled in. */
8
- type ScriptBody =
9
- /** Argument vectors run in order with no shell, stopping at the first that fails. */
10
- {
11
- commands: string[][];
12
- timeoutMs: number;
13
- }
14
- /** One script, run by the research shell's interpreter. */
15
- | {
16
- script: string;
17
- timeoutMs: number;
18
- };
19
- type BodyFields = Pick<Task, 'type' | 'commands' | 'script' | 'timeoutMs'>;
20
- /** The body a script task runs; null for any other task, or one whose body the plan's rules refuse. */
21
- declare function scriptBodyOf(task: BodyFields): ScriptBody | null;
22
- /**
23
- * An argument vector as a reader would type it in a shell: a word is quoted
24
- * only where it needs to be, and one holding a character a reader cannot see
25
- * is written with escapes (`$'…'`), so a command is always one line and reads
26
- * as what runs. For reading only; nothing runs it.
27
- */
28
- declare function commandLine(argv: readonly string[]): string;
29
- /**
30
- * A script task's body as the approval card shows it (ADR-0035), so the
31
- * consent is to what will run: each command on a line of its own, or the
32
- * script as written, then the time limit. A character a reader cannot see in
33
- * a script, but for its line breaks and tabs, is shown as its code.
34
- */
35
- declare function scriptBodyText(body: ScriptBody): string;
36
- /**
37
- * A script task as a row shows it: the body {@link scriptBodyText} gives the
38
- * approval card, so what the user read when consenting is what the plan lists.
39
- * A task whose body the plan's rules refuse says so rather than showing nothing.
40
- */
41
- declare function scriptTaskText(task: BodyFields): string;
42
- /** A time limit in the largest units it is a whole number of: "10 minutes", "90 seconds". */
43
- declare function timeLimitText(ms: number): string;
44
-
45
- /**
46
- * The planner's standing approvals (ADR-0026): what runs without asking
47
- * anyone, under every approval mode.
48
- *
49
- * One entry is one of:
50
- *
51
- * - a **command rule**, `gh issue list` or `gcloud * * list`: the binary,
52
- * then words matched against the command's leading arguments, the ones
53
- * before its first flag. A command whose leading words match the rule's
54
- * runs, whatever arguments follow — but the rule must spell out at least
55
- * the words the command's approval scope holds (ADR-0008, T1), so
56
- * `az group` never covers `az group delete`, and a one-word read takes a
57
- * `*` for its argument (`kubectl get *`). `*` stands for one word, and
58
- * inside a word for any run of characters (`describe-*`). An entry equal
59
- * to a scope also matches that scope, as pre-approved scopes always did.
60
- * - an **MCP tool rule**, `mcp:find-*`: a glob over the tool's own name,
61
- * without the server, case-insensitive.
62
- * - a **scope pattern**, anything starting with `/`, `~`, `http://`,
63
- * `https://`, `web_search:`, `mcp__`, `mcp-server:` or a drive letter: matched against an approval's
64
- * scope as ADR-0008 always matched pre-approved entries, `*` matching any
65
- * run of characters.
66
- *
67
- * A leading `!` excludes instead, and an exclusion wins over every rule. A
68
- * command exclusion's words are looked for anywhere among the arguments, in
69
- * order, flags included and case ignored, so `!kubectl *secret*` and
70
- * `!* --endpoint*` hold wherever the word appears; a lone short flag (`-s`)
71
- * also holds inside a bundle (`-As`, `-shost`). `*` as its binary covers
72
- * every command.
73
- */
74
- /** The words a command rule is matched against: a lexed segment's binary and arguments. */
75
- interface CommandWords {
76
- binary: string;
77
- args: readonly string[];
78
- /** How many leading arguments the command's approval scope holds; a rule must cover at least that many. */
79
- scopeWords?: number;
80
- }
81
- interface RuleWord {
82
- matches: (word: string) => boolean;
83
- wildcard: boolean;
84
- }
85
- interface CommandRule {
86
- text: string;
87
- binary: string;
88
- words: RuleWord[];
89
- }
90
- interface PlannerAllowlist {
91
- commands: CommandRule[];
92
- commandExclusions: CommandRule[];
93
- tools: RegExp[];
94
- toolExclusions: RegExp[];
95
- scopes: RegExp[];
96
- scopeExclusions: RegExp[];
97
- }
98
- declare function parseAllowlist(entries: readonly string[]): PlannerAllowlist;
99
- /** The rule that lets this command run unasked, or undefined when none does or an exclusion holds. */
100
- declare function commandAllowedBy(list: PlannerAllowlist, command: CommandWords): string | undefined;
101
- /** Whether an MCP tool, by its own name without the server, runs unasked. */
102
- declare function toolAllowed(list: PlannerAllowlist, tool: string): boolean;
103
- /** Whether an approval scope — a directory, an origin, a tool's full name — is pre-approved. */
104
- declare function scopeAllowed(list: PlannerAllowlist, scope: string): boolean;
105
- /**
106
- * Read-only command families that are safe to run unasked: they read, never
107
- * write a file or change a resource, and need no argument that runs code.
108
- * Absent on purpose: commands that run the repository's own code (test
109
- * runners, builds, package scripts, a package manager the repository pins or
110
- * extends — on an untrusted repository someone else wrote that code), commands
111
- * a repository's own config can point at another endpoint with the user's
112
- * credentials (`.npmrc`, `.sentryclirc`, a Terraform or Pulumi backend), and
113
- * commands whose usual output is a secret.
114
- */
115
- declare const DEFAULT_PLANNER_ALLOWLIST: readonly string[];
116
-
117
5
  /**
118
6
  * Which interpreter runs the planner's `bash` tool, and in which language.
119
7
  *
@@ -174,6 +62,8 @@ interface ShellLaunch {
174
62
  * timeout, and stops waiting for output a moment after it exits.
175
63
  */
176
64
  wallTimeMs?: number;
65
+ /** A sealed run's memory limit, which its result names if the run was ended at it (ADR-0036 S1). */
66
+ memoryBytes?: number;
177
67
  }
178
68
  interface ResearchShellDeps {
179
69
  platform?: NodeJS.Platform;
@@ -708,6 +598,12 @@ interface IConfig {
708
598
  approvalDefaults: boolean;
709
599
  /** The user's effect allowlist rules (ADR-0035, `effectAllowlist.ts`), read on every request. It has no defaults. */
710
600
  effectAllowlist: string[];
601
+ /**
602
+ * The user's notify command as an argument vector (ADR-0053), read as each
603
+ * new escalation appears; empty when there is none. A repository never sets
604
+ * it, because it would then run a command on the user's machine.
605
+ */
606
+ notifyCommand: string[];
711
607
  /** How many fetches and searches one planner turn may make (ADR-0027). User-only, like the approval settings. */
712
608
  webReadBudget: number;
713
609
  /**
@@ -728,6 +624,17 @@ interface IConfig {
728
624
  }
729
625
  declare function enabledRunners(cfg: IConfig): RunnerId[];
730
626
 
627
+ type NotificationAction = {
628
+ label: string;
629
+ value: string;
630
+ };
631
+ interface INotification {
632
+ info(msg: string): void;
633
+ warn(msg: string): void;
634
+ error(msg: string): void;
635
+ confirm(msg: string, options: string[]): Promise<string | undefined>;
636
+ }
637
+
731
638
  interface ToolOutcome {
732
639
  success: boolean;
733
640
  output: string;
@@ -738,6 +645,12 @@ interface ToolOutcome {
738
645
  * names the limit it ran out of rather than blaming the command.
739
646
  */
740
647
  timedOut?: boolean;
648
+ /**
649
+ * The signal that ended the child, if one did. The adapters report it, and a
650
+ * sealed run's result reads it for the one that means its memory limit ended
651
+ * the run (ADR-0036 S1).
652
+ */
653
+ signal?: NodeJS.Signals;
741
654
  }
742
655
  interface ReadFileOpts {
743
656
  maxBytes?: number;
@@ -864,15 +777,18 @@ interface IFileSystem {
864
777
  setOutboundSecrets?(secrets: () => Iterable<string>): void;
865
778
  /** Where the filesystem tells the session, once, that the planner's shell runs without an OS sandbox here (ADR-0011 B5). */
866
779
  setNotice?(notice: (message: string) => void): void;
867
- /** The session ended: its scratch directory is removed, and the next command starts a fresh one (ADR-0011 B2). */
780
+ /** The session ended: its scratch directories are removed, and the next command starts fresh ones (ADR-0011 B2). */
868
781
  endSession?(): void;
869
782
  /** Whether a command can run sealed on this host (ADR-0036 S6); where it cannot, the session's one notice says so. */
870
783
  canSeal?(): Promise<boolean>;
784
+ /** The same adapter at another workspace: what a child's planner reads, its code root being a task's worktree (ADR-0055 D5). */
785
+ rootedAt?(workspace: string): IFileSystem;
871
786
  /** A read-only task's shell at `site`, held to this session's allowlist and never asking (ADR-0046). */
872
787
  taskShell?(site: TaskShellSite): TaskShell;
873
788
  }
874
789
 
875
- declare const taskCompleteInput: z.ZodObject<{
790
+ /** A subplanner's report (ADR-0057 R2): it passes a finding up only by reference, so nothing is reworded on the way, and only its token takes these. */
791
+ declare const subplannerCompleteInput: z.ZodObject<{
876
792
  status: z.ZodEnum<{
877
793
  failed: "failed";
878
794
  done: "done";
@@ -927,6 +843,8 @@ declare const taskCompleteInput: z.ZodObject<{
927
843
  url: z.ZodString;
928
844
  }, z.core.$strip>]>>>;
929
845
  report: z.ZodOptional<z.ZodString>;
846
+ promote: z.ZodOptional<z.ZodArray<z.ZodString>>;
847
+ open: z.ZodOptional<z.ZodArray<z.ZodString>>;
930
848
  }, z.core.$strip>;
931
849
  declare const checkpointInput: z.ZodObject<{
932
850
  question: z.ZodString;
@@ -956,6 +874,24 @@ declare const readTaskInput: z.ZodObject<{
956
874
  attempt: z.ZodOptional<z.ZodNumber>;
957
875
  include_report: z.ZodOptional<z.ZodBoolean>;
958
876
  }, z.core.$strip>;
877
+ declare const plannerPostInput: z.ZodObject<{
878
+ type: z.ZodEnum<{
879
+ attempt: "attempt";
880
+ finding: "finding";
881
+ pin: "pin";
882
+ decision: "decision";
883
+ claim: "claim";
884
+ artifact: "artifact";
885
+ contract: "contract";
886
+ assume: "assume";
887
+ scope_change: "scope_change";
888
+ }>;
889
+ title: z.ZodString;
890
+ key: z.ZodOptional<z.ZodString>;
891
+ value: z.ZodOptional<z.ZodString>;
892
+ detail: z.ZodOptional<z.ZodString>;
893
+ files: z.ZodOptional<z.ZodArray<z.ZodString>>;
894
+ }, z.core.$strip>;
959
895
  declare const savePatchInput: z.ZodObject<{}, z.core.$strip>;
960
896
  declare const applyPatchInput: z.ZodObject<{
961
897
  task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
@@ -1131,17 +1067,10 @@ declare const answerApprovalInput: z.ZodObject<{
1131
1067
  answer: z.ZodString;
1132
1068
  note: z.ZodOptional<z.ZodString>;
1133
1069
  }, z.core.$strip>;
1134
- declare const answerCheckpointInput: z.ZodObject<{
1135
- task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
1136
- answer: z.ZodEnum<{
1137
- approve: "approve";
1138
- reject: "reject";
1139
- }>;
1140
- reason: z.ZodOptional<z.ZodString>;
1141
- }, z.core.$strip>;
1142
- type TaskCompleteArgs = z.infer<typeof taskCompleteInput>;
1070
+ type TaskCompleteArgs = z.infer<typeof subplannerCompleteInput>;
1143
1071
  type CheckpointArgs = z.infer<typeof checkpointInput>;
1144
1072
  type ReadTaskArgs = z.infer<typeof readTaskInput>;
1073
+ type PostArgs = z.infer<typeof plannerPostInput>;
1145
1074
  type SavePatchArgs = z.infer<typeof savePatchInput>;
1146
1075
  type ApplyPatchArgs = z.infer<typeof applyPatchInput>;
1147
1076
  type ListRunnersArgs = z.infer<typeof listRunnersInput>;
@@ -1162,7 +1091,7 @@ type RetryTaskArgs = z.infer<typeof retryTaskInput>;
1162
1091
  type MessageTaskArgs = z.infer<typeof messageTaskInput>;
1163
1092
  type SkipTaskArgs = z.infer<typeof skipTaskInput>;
1164
1093
  type AnswerApprovalArgs = z.infer<typeof answerApprovalInput>;
1165
- type AnswerCheckpointArgs = z.infer<typeof answerCheckpointInput>;
1094
+ type AnswerCheckpointArgs = z.infer<typeof delegatingAnswerCheckpointInput>;
1166
1095
  type RunScriptArgs = z.infer<typeof runScriptInput>;
1167
1096
  /** What a handler answers with. `isError` marks a refusal the caller should act on. */
1168
1097
  interface McpToolReply {
@@ -1176,6 +1105,7 @@ interface McpToolReply {
1176
1105
  */
1177
1106
  type CheckpointAnswer = {
1178
1107
  kind: 'continue';
1108
+ note?: string;
1179
1109
  } | {
1180
1110
  kind: 'rejected';
1181
1111
  reason: string;
@@ -1193,6 +1123,8 @@ interface TaskToolHandler {
1193
1123
  checkpoint?: Run<CheckpointArgs>;
1194
1124
  /** A handoff the task may read (ADR-0047), from its attempt's log. */
1195
1125
  readTask?: Run<ReadTaskArgs>;
1126
+ /** An entry on its plan's board (ADR-0057 R8); only a token with a board lists `post`. */
1127
+ post?: Run<PostArgs>;
1196
1128
  /** A read-only task's shell (ADR-0046); only its token lists `run_command`. */
1197
1129
  runCommand?: Run<TaskRunCommandArgs>;
1198
1130
  /** A read-only task's prototype kept as a patch (ADR-0050); only its token lists `save_patch`. */
@@ -1204,6 +1136,10 @@ interface TaskToolHandler {
1204
1136
  webSearch?: Run<WebSearchArgs>;
1205
1137
  }
1206
1138
  interface PlannerToolHandler {
1139
+ /** A handoff below this plan by reference (ADR-0057 R11); only a planner that may hold delegated tasks lists `read`. */
1140
+ readTask?: Run<ReadTaskArgs>;
1141
+ /** An entry on this node's board (ADR-0057 R8); a planner lists it where `read` is. */
1142
+ post?: Run<PostArgs>;
1207
1143
  loadSkill?: Run<LoadSkillArgs>;
1208
1144
  runCommand?: Run<RunCommandArgs>;
1209
1145
  fetch?: Run<FetchArgs>;
@@ -1226,6 +1162,9 @@ interface PlannerToolHandler {
1226
1162
  answerCheckpoint?: Run<AnswerCheckpointArgs>;
1227
1163
  mergeAll?: Run<RunPlanArgs>;
1228
1164
  runScript?: Run<RunScriptArgs>;
1165
+ /** A subplanner's end and its questions, handled by the parent's attempt (ADR-0055 D5); only its token lists them. */
1166
+ taskComplete?: Run<TaskCompleteArgs>;
1167
+ checkpoint?: Run<CheckpointArgs>;
1229
1168
  }
1230
1169
  interface McpTool<H> {
1231
1170
  name: string;
@@ -1237,9 +1176,23 @@ interface McpTool<H> {
1237
1176
  annotations?: {
1238
1177
  readOnlyHint: boolean;
1239
1178
  };
1179
+ /** An older name a call may still use: it is in no list, so it costs nothing and breaks no prompt cache (ADR-0057 R11). */
1180
+ alias?: string;
1240
1181
  /** Validate `args` and hand them to the handler's method for this tool. */
1241
1182
  call(handler: H, args: unknown, context: McpToolContext): Promise<McpToolReply>;
1242
1183
  }
1184
+ /** A tool set as `tools/list` answers with it: what a runner is sent, and pays for. */
1185
+ declare function toolListing<H>(tools: readonly McpTool<H>[]): {
1186
+ annotations?: {
1187
+ readOnlyHint: boolean;
1188
+ } | undefined;
1189
+ name: string;
1190
+ description: string;
1191
+ inputSchema: {
1192
+ [key: string]: unknown;
1193
+ type: "object";
1194
+ };
1195
+ }[];
1243
1196
  /** The task tool whose call settles the attempt (ADR-0022, V1). */
1244
1197
  declare const TASK_COMPLETE_TOOL = "task_complete";
1245
1198
  /**
@@ -1266,6 +1219,11 @@ declare const TASK_TOOLS: readonly McpTool<TaskToolHandler>[];
1266
1219
  * plan's branches.
1267
1220
  */
1268
1221
  declare const READ_ONLY_TASK_TOOLS: readonly McpTool<TaskToolHandler>[];
1222
+ /** What a task's token serves (ADR-0057 R11): the flat list itself where it has no board, so a flat run's does not move by a byte. */
1223
+ declare function taskToolSet({ readOnly, board }: {
1224
+ readOnly?: boolean;
1225
+ board?: boolean;
1226
+ }): readonly McpTool<TaskToolHandler>[];
1269
1227
  /**
1270
1228
  * The planner's execution tools (ADR-0029, #91): each call goes to the
1271
1229
  * session's command seam, which checks it, asks the user where consent needs
@@ -1273,6 +1231,25 @@ declare const READ_ONLY_TASK_TOOLS: readonly McpTool<TaskToolHandler>[];
1273
1231
  */
1274
1232
  declare const PLANNER_EXECUTION_TOOLS: readonly McpTool<PlannerToolHandler>[];
1275
1233
  declare const PLANNER_TOOLS: readonly McpTool<PlannerToolHandler>[];
1234
+ declare const delegatingAnswerCheckpointInput: z.ZodObject<{
1235
+ task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
1236
+ answer: z.ZodEnum<{
1237
+ approve: "approve";
1238
+ reject: "reject";
1239
+ }>;
1240
+ reason: z.ZodOptional<z.ZodString>;
1241
+ raiseTokens: z.ZodOptional<z.ZodNumber>;
1242
+ }, z.core.$strip>;
1243
+ /**
1244
+ * What a planner's token serves (ADR-0056 M6): the list as a flat planner has
1245
+ * it unless delegation reaches it, so a flat session's tools do not change by
1246
+ * a byte (ADR-0055 D21). A subplanner ends and asks with the tools a runner
1247
+ * does (D4), read-only by the hint since they change no file of the workspace.
1248
+ */
1249
+ declare function plannerToolSet({ delegates, subplanner }?: {
1250
+ delegates?: boolean;
1251
+ subplanner?: boolean;
1252
+ }): readonly McpTool<PlannerToolHandler>[];
1276
1253
 
1277
1254
  /**
1278
1255
  * The Ordewell MCP server (ADR-0022): one loopback listener per process,
@@ -1291,6 +1268,8 @@ interface TaskTokenScope {
1291
1268
  attempt: number;
1292
1269
  /** A read-only task's attempt (ADR-0046): its token lists `run_command`, `fetch` and `web_search` too, and no other task's does. */
1293
1270
  readOnly?: boolean;
1271
+ /** Its plan has a board (ADR-0057 R8): its token lists `post`, and a flat task's does not. */
1272
+ board?: boolean;
1294
1273
  }
1295
1274
  interface PlannerTokenScope {
1296
1275
  sessionId: string;
@@ -1300,6 +1279,10 @@ interface PlannerTokenScope {
1300
1279
  * model would otherwise pick over its own.
1301
1280
  */
1302
1281
  hidden?: readonly string[];
1282
+ /** Its `submit_plan` takes a delegated task (ADR-0055 D1): a flat planner's list does not change. */
1283
+ delegates?: boolean;
1284
+ /** A subplanner's token (ADR-0055 D5): it serves `task_complete` and `checkpoint` too, which its handler sends to the parent's attempt. */
1285
+ subplanner?: boolean;
1303
1286
  }
1304
1287
  /** Where a runner reaches the server and what it presents there. Never put either half in a prompt. */
1305
1288
  interface McpCredential {
@@ -1307,6 +1290,8 @@ interface McpCredential {
1307
1290
  token: string;
1308
1291
  /** The planner tools the token hides ({@link PlannerTokenScope.hidden}), so the runner is told of and pre-allows only what it is served. */
1309
1292
  hidden?: readonly string[];
1293
+ /** Tools it serves past a flat role's, by their names on the server: a subplanner's (ADR-0055 D5), `read` where it may delegate, and `post` where it has a board (ADR-0057 R11). */
1294
+ extraTools?: readonly string[];
1310
1295
  /** The token serves a read-only task's tools ({@link TaskTokenScope.readOnly}), so the runner is told of and pre-allows `run_command`, `fetch` and `web_search`. */
1311
1296
  readOnly?: true;
1312
1297
  }
@@ -1361,6 +1346,8 @@ interface McpClientConfig {
1361
1346
  headers: Record<string, string>;
1362
1347
  /** Planner tools the token hides: a runner must neither pre-allow nor mention them. */
1363
1348
  hidden?: readonly string[];
1349
+ /** Tools served past a planner's: a subplanner's, to pre-allow with the rest (ADR-0055 D5). */
1350
+ extraTools?: readonly string[];
1364
1351
  /** A read-only task's token (ADR-0046): `run_command`, `fetch` and `web_search` are among its tools, for the runner to pre-allow and name. */
1365
1352
  readOnly?: true;
1366
1353
  }
@@ -1533,18 +1520,26 @@ interface SandboxProfile {
1533
1520
  limits?: SandboxLimits;
1534
1521
  }
1535
1522
  /**
1536
- * Held by the kernel's resource limits, set before the launcher starts, and
1537
- * by the caller's clock (ADR-0036 S1).
1523
+ * Held by the kernel's resource limits, set before the launcher starts, by a
1524
+ * watcher beside the launcher, outside the sandbox, and by the caller's clock
1525
+ * (ADR-0036 S1).
1538
1526
  */
1539
1527
  interface SandboxLimits {
1540
1528
  /** Processes the command may start beyond those its user already runs, which the kernel's limit counts too. */
1541
1529
  processes: number;
1542
1530
  /**
1543
- * Bytes of address space any one of its processes may map. On macOS, where
1544
- * every program maps hundreds of gigabytes as it starts, it is counted on
1545
- * top of that.
1531
+ * Bytes of real memory the command may hold: resident memory summed across
1532
+ * its processes, which the watcher reads about once a second and ends the
1533
+ * command for passing ({@link MEMORY_LIMIT_SIGNAL}).
1546
1534
  */
1547
1535
  memoryBytes: number;
1536
+ /**
1537
+ * Bytes of address space any one of its processes may map: a backstop
1538
+ * against a runaway allocator, not a measure of memory, since V8 reserves
1539
+ * far more than it touches. On macOS, where every program maps hundreds of
1540
+ * gigabytes as it starts, it is counted on top of that.
1541
+ */
1542
+ addressSpaceBytes: number;
1548
1543
  /** Milliseconds before the caller kills the command, and with it every process it started. */
1549
1544
  wallTimeMs: number;
1550
1545
  }
@@ -1559,9 +1554,12 @@ interface SandboxInvocation {
1559
1554
  interface SandboxLaunch {
1560
1555
  file: string;
1561
1556
  args: string[];
1557
+ /** What `file` starts with. A launcher that runs before the sandbox gets only what the profile keeps of the host's. */
1562
1558
  env: NodeJS.ProcessEnv;
1563
1559
  /** The profile's wall-time limit: the caller kills the launch with SIGKILL once it has run this long. */
1564
1560
  wallTimeMs?: number;
1561
+ /** The profile's memory limit, past which the launch ends by {@link MEMORY_LIMIT_SIGNAL}. */
1562
+ memoryBytes?: number;
1565
1563
  }
1566
1564
  type SandboxProbe = {
1567
1565
  ok: true;
@@ -1676,7 +1674,12 @@ interface PlannerSandboxDeps {
1676
1674
  uid?: number;
1677
1675
  exec?: ProbeExec;
1678
1676
  }
1679
- /** Enough for a loop over a repository's files, a pipeline or a short script; not for a build. */
1677
+ /**
1678
+ * Time enough for a loop over a repository's files, a pipeline or a short
1679
+ * script, not for a build. The memory is real memory across the run; the
1680
+ * address space is per process and far above what V8 reserves for ordinary
1681
+ * programs (ADR-0036 S1, which records the measurement).
1682
+ */
1680
1683
  declare const SEALED_LIMITS: SandboxLimits;
1681
1684
  /**
1682
1685
  * A read-only task's sealed runs (ADR-0046): the same resources, with the ten
@@ -1699,18 +1702,18 @@ interface PlannerShellLaunch {
1699
1702
  sandboxed: boolean;
1700
1703
  }
1701
1704
  /**
1702
- * One session's view of the sandbox: its scratch directory, made on the first
1703
- * command and removed when the session ends, and its one notice when the
1704
- * sandbox, or a sealed run in it, cannot run here.
1705
+ * One session's view of the sandbox: a scratch directory for each profile,
1706
+ * made on its first command and removed when the session ends, and its one
1707
+ * notice when the sandbox, or a sealed run in it, cannot run here.
1705
1708
  */
1706
1709
  declare class PlannerShellSandbox {
1707
1710
  private readonly resolve;
1708
- private scratch;
1711
+ private readonly scratch;
1709
1712
  private noticed;
1710
1713
  private notice;
1711
1714
  constructor(resolve?: () => Promise<PlannerSandboxAvailability>);
1712
1715
  setNotice(notice: (message: string) => void): void;
1713
- /** The same host's sandbox for another caller, with a scratch directory of its own: a read-only task's (ADR-0046). */
1716
+ /** The same host's sandbox for another caller, with scratch directories of its own: a read-only task's (ADR-0046). */
1714
1717
  withOwnScratch(): PlannerShellSandbox;
1715
1718
  /**
1716
1719
  * The launch for one command (B6): the research shell wrapped in the
@@ -1733,8 +1736,10 @@ declare class PlannerShellSandbox {
1733
1736
  /**
1734
1737
  * The launch for a harness's own shell (ADR-0036 N2): the same sealed profile
1735
1738
  * as a sealed run, with the network the harness gives it (N3), plus the
1736
- * harness's short temporary directory. The workspace stays read-only. Throws
1737
- * where the sandbox cannot run, so a caller never falls back to the bare shell.
1739
+ * harness's short temporary directory. The workspace stays read-only. Its
1740
+ * home is a scratch directory of its own, so whatever startup files its shell
1741
+ * reads, a sealed run wrote none of them (S8). Throws where the sandbox
1742
+ * cannot run, so a caller never falls back to the bare shell.
1738
1743
  */
1739
1744
  launchNativeShell(opts: {
1740
1745
  workspace: string;
@@ -1743,9 +1748,9 @@ declare class PlannerShellSandbox {
1743
1748
  shellArgs?: readonly string[];
1744
1749
  env?: NodeJS.ProcessEnv;
1745
1750
  }): Promise<SandboxLaunch>;
1746
- /** The scratch directory every launch here gets as `$TMPDIR` and `$HOME`, made if it is not there. */
1751
+ /** The scratch directory a sealed run gets as `$TMPDIR` and `$HOME`, made if it is not there. */
1747
1752
  scratchPath(): string;
1748
- /** The session ended: its scratch directory goes, and a next session starts with a fresh one and its own notice. */
1753
+ /** The session ended: its scratch directories go, and a next session starts with fresh ones and its own notice. */
1749
1754
  end(): void;
1750
1755
  private say;
1751
1756
  private invocation;
@@ -1755,6 +1760,689 @@ declare class PlannerShellSandbox {
1755
1760
  /** A runner's report on the Ordewell server's connection, in one vocabulary for every runner. */
1756
1761
  type AttachState = 'connected' | 'pending' | 'failed';
1757
1762
 
1763
+ interface SessionMeta {
1764
+ id: string;
1765
+ goal: string;
1766
+ runners: RunnerId[];
1767
+ taskCount: number;
1768
+ status: PlanStatus;
1769
+ createdAt: string;
1770
+ updatedAt: string;
1771
+ }
1772
+ interface SessionData {
1773
+ meta: SessionMeta;
1774
+ /** The full plan state — including conversationHistory, prdMarkdown, and queuedMessages. */
1775
+ plan: LegacyPlanState;
1776
+ }
1777
+
1778
+ interface ILogger {
1779
+ warn(scope: string, message: string, err?: unknown): void;
1780
+ }
1781
+ declare class ConsoleLogger implements ILogger {
1782
+ warn(scope: string, message: string, err?: unknown): void;
1783
+ }
1784
+ declare const defaultLogger: ILogger;
1785
+
1786
+ /**
1787
+ * A path segment for an id that came from outside — a session id from a URL,
1788
+ * a task id a planner wrote. A plain id is used as is; anything that could
1789
+ * climb out of its directory, or is not portable as a file name, is hex-encoded.
1790
+ */
1791
+ declare function idSegment(id: string): string;
1792
+ /**
1793
+ * The directory beside a session's file for what it keeps outside that file —
1794
+ * its task logs (ADR-0018, P1). It goes when the session does. A child's sits
1795
+ * under its root's at every depth, so it goes with its root and no prune or
1796
+ * sweep of a worktree reaches it (ADR-0055 D11).
1797
+ */
1798
+ declare function sessionDataDir(sessionId: string, baseDir?: string, rootSessionId?: string): string;
1799
+ /** Where a child session's state lives. `baseDir` is the state root: the root Session's workspace, never a worktree. */
1800
+ interface ChildSessionPlace {
1801
+ baseDir: string;
1802
+ rootSessionId: string;
1803
+ sessionId: string;
1804
+ }
1805
+ declare function saveSession(plan: LegacyPlanState, goal: string, baseDir?: string, id?: string): SessionMeta;
1806
+ /** Saved beside the child's logs, in the root's directory; `listSessions` never sees it (ADR-0055 D11). */
1807
+ declare function saveChildSession(plan: LegacyPlanState, goal: string, place: ChildSessionPlace): SessionMeta;
1808
+ /** The ids of every child saved under a root, at any depth. */
1809
+ declare function listChildSessions(rootSessionId: string, baseDir?: string): string[];
1810
+ declare function listSessions(baseDir?: string, logger?: ILogger): SessionMeta[];
1811
+ /** A child is read only through its root, and only when asked for (ADR-0055 D17). */
1812
+ declare function loadChildSession(place: ChildSessionPlace, logger?: ILogger): {
1813
+ meta: SessionMeta;
1814
+ plan: LegacyPlanState;
1815
+ } | null;
1816
+ declare function loadSession(sessionId: string, baseDir?: string, logger?: ILogger): {
1817
+ meta: SessionMeta;
1818
+ plan: LegacyPlanState;
1819
+ } | null;
1820
+ declare function loadSessionPlanState(sessionId: string, baseDir?: string, logger?: ILogger): {
1821
+ meta: SessionMeta;
1822
+ plan: PlanState;
1823
+ } | null;
1824
+ declare function getLatestSession(baseDir?: string, logger?: ILogger): {
1825
+ meta: SessionMeta;
1826
+ plan: LegacyPlanState;
1827
+ } | null;
1828
+ declare function deleteSession(sessionId: string, baseDir?: string, logger?: ILogger): boolean;
1829
+
1830
+ /**
1831
+ * Task skills end to end (ADR-0024): a skill reaches a runner only by being
1832
+ * attached to its task in the plan, and Ordewell puts its body in the task's
1833
+ * prompt — so it works the same on every runner, and nothing is written to a
1834
+ * harness's own skills directory.
1835
+ */
1836
+ /** Where a task's skill names are looked up: global first, then the workspace folders it reads. */
1837
+ interface SkillLookup extends Pick<SkillsService, 'findSkill' | 'searchedDirs'> {
1838
+ /**
1839
+ * Set when tasks get worktrees: of `skills`, the workspace ones git will not
1840
+ * carry into them, each SKILL.md path mapped to the folder to commit,
1841
+ * relative to the workspace root.
1842
+ */
1843
+ uncommitted?(skills: readonly SkillInfo[]): Promise<Map<string, string>>;
1844
+ }
1845
+ /**
1846
+ * Where a workspace's skills are read, known without asking git: the repos
1847
+ * of its group in layout order (`['.']` for one repository), and whether
1848
+ * tasks will get worktrees, which only a committed skill reaches.
1849
+ */
1850
+ interface SkillLayout {
1851
+ repos: readonly string[];
1852
+ worktrees: boolean;
1853
+ }
1854
+
1855
+ /**
1856
+ * Where the run's tasks will work: `false` in the shared workspace root;
1857
+ * otherwise each in its own worktree of every repo of the group (ADR-0013,
1858
+ * ADR-0014), which is what decides if tasks on the same file must be ordered.
1859
+ */
1860
+ type IsolatedExecution = false | RepoGroupLayout;
1861
+ interface PlannerModes {
1862
+ autonomousDefault: boolean;
1863
+ /** Not a toggle: a fact about the run. */
1864
+ isolatedExecution: IsolatedExecution;
1865
+ /**
1866
+ * Where the run's tasks will look their skills up (ADR-0024), also a fact
1867
+ * about the run. A plan attaching a planner skill is refused against it,
1868
+ * and a coding agent's submit_plan call is refused in its turn rather than
1869
+ * accepted and then failed. Absent: the plan is not checked.
1870
+ */
1871
+ taskSkills?: SkillLookup;
1872
+ }
1873
+
1874
+ type IsolationNoticeLevel = 'info' | 'warn' | 'error';
1875
+ /**
1876
+ * How an attempt that may run in a worktree gets one: fresh, the kept one for
1877
+ * a repair, restoring the previous attempt's work, or fresh and thrown away
1878
+ * whatever it changes, for a read-only task (ADR-0046).
1879
+ */
1880
+ type AttemptIntent = 'fresh' | 'repair' | 'readOnly' | PrepareIntent;
1881
+ /**
1882
+ * What adopting a persisted run found: nothing to change, landed work git
1883
+ * shows the user has merged since, or a settled run that had nothing left to
1884
+ * hand over and was cleared up.
1885
+ */
1886
+ type AdoptOutcome = 'unchanged' | 'changed' | 'cleared';
1887
+ interface AttemptWorkspace {
1888
+ cwd: string;
1889
+ worktree: boolean;
1890
+ /** The task workspace holding the attempt's worktrees, which `cwd` is in; set with `worktree` when it was prepared. */
1891
+ workspace?: string;
1892
+ /** Set for a `restore` in a worktree; absent in the checkout, where the previous attempt's edits remain. */
1893
+ previousWork?: PreviousWork;
1894
+ }
1895
+ /**
1896
+ * What the controller reports. It never schedules or emits on its own: the
1897
+ * orchestrator turns these into observer events and decides what runs next.
1898
+ */
1899
+ interface IsolationRunListener {
1900
+ /**
1901
+ * The run record changed and should be persisted with the plan. `detached`
1902
+ * is set instead when the record that changed is no longer the plan's own —
1903
+ * a release that outlived the plan load that replaced its run — and belongs
1904
+ * to the session that was left.
1905
+ */
1906
+ changed(detached?: IsolationRun): void;
1907
+ /** A run did not start: `repos` of the group have tracked changes. */
1908
+ blocked(repos: string[]): void;
1909
+ /** An isolated run closed and handed its integration branches over. */
1910
+ handoff(handoff: IsolationHandoff): void;
1911
+ /** The run was cleared up and forgotten: nothing was handed over, or it was discarded. */
1912
+ closed(): void;
1913
+ /** The run is over, discarded or cleared up, whether or not its record stays: nothing it carried goes on (ADR-0045 L5). A Merge all it goes on after is not this. */
1914
+ ended(): void;
1915
+ /** Already said through the notifications; also for a surface that shows no toasts. */
1916
+ notice(level: IsolationNoticeLevel, message: string): void;
1917
+ /** A clean-up took the branches these resolver tasks would merge, and their links with them. */
1918
+ resolversEnded(resolverIds: readonly string[]): void;
1919
+ }
1920
+ interface IsolationRunControllerDeps {
1921
+ isolation: IWorktreeIsolation;
1922
+ config: IConfig;
1923
+ notifications: INotification;
1924
+ workspaceRoot: () => string;
1925
+ /** A child session's state root, which every run it mints keeps (ADR-0055 D11); absent on a flat run. */
1926
+ stateRoot?: string;
1927
+ listener: IsolationRunListener;
1928
+ /**
1929
+ * The tasks no clean-up of the whole run may take a worktree from: an
1930
+ * attempt is running them, or the plan has them in progress or waiting on
1931
+ * the user mid-attempt.
1932
+ */
1933
+ liveTasks: () => ReadonlySet<string>;
1934
+ /** What a task depends on now, directly or not, read as it lands: the plan may let go of that later, and a revert may not (ADR-0054). */
1935
+ landedOn: (taskId: string) => readonly string[];
1936
+ }
1937
+ /**
1938
+ * The lifecycle of a plan's isolation run (ADR-0013, ADR-0014, ADR-0020),
1939
+ * between the scheduler and the git layer: deciding, at a run's first change
1940
+ * task, whether it executes in worktrees, a blocked run and how it goes on,
1941
+ * each attempt's working directory, releasing worktrees, which landed work the
1942
+ * user has merged, the tree check of an ops task, the handoff when the run
1943
+ * closes, and what the user does with it afterwards. The record it keeps
1944
+ * outlives one run — a resumed plan continues it.
1945
+ */
1946
+ declare class IsolationRunController {
1947
+ private readonly isolation;
1948
+ private readonly config;
1949
+ private readonly notifications;
1950
+ private readonly workspaceRoot;
1951
+ private readonly stateRoot;
1952
+ private readonly listener;
1953
+ private readonly liveTasks;
1954
+ private readonly landedOn;
1955
+ private run;
1956
+ /** See {@link mergedRun}. */
1957
+ private merged;
1958
+ /** An adoption stays scoped even while cleanup temporarily leaves no run record. */
1959
+ private adoptions;
1960
+ /** Copied paths already reported for the current run: every task gets the same copies. */
1961
+ private reportedCopies;
1962
+ private gitShortfallSaid;
1963
+ private reportedSetupFailures;
1964
+ /**
1965
+ * How the open run executes; null while no run is open, `undecided` until
1966
+ * its first change task asks. A run is one Execute-Plan or one manual task
1967
+ * run, from its start until it settles or is stopped.
1968
+ */
1969
+ private mode;
1970
+ /** Resolver task id → the conflicted task it resolves; see {@link linkResolver}. */
1971
+ private resolvers;
1972
+ private opening;
1973
+ private opened;
1974
+ /** Bumped by an interrupt, so an activation in flight across it does not open the run it ended. */
1975
+ private activations;
1976
+ /** The start a dirty tree turned away, handed back once the user chooses how to go on. */
1977
+ private blockedStart;
1978
+ /** How many starts it has turned away; a replay can be turned away again in the same run. */
1979
+ private turnedAway;
1980
+ /** The dirty repos behind {@link blockedStart}, for the stash notice. */
1981
+ private blockedRepos;
1982
+ constructor(deps: IsolationRunControllerDeps);
1983
+ /** The plan's run record, open or not; null when the plan has not isolated. */
1984
+ get current(): IsolationRun | null;
1985
+ /** What a child works through: this run's own, so a tree takes one admin lock (ADR-0055 D11). */
1986
+ get childIsolation(): IWorktreeIsolation;
1987
+ /**
1988
+ * The id of the plan's last isolated run once it closed with everything it
1989
+ * landed merged into the user's branch and was cleared up: what tells a
1990
+ * Merge all asked for after it, which has nothing to merge, from one in a
1991
+ * plan that never isolated (ADR-0029). Null while the plan holds a run
1992
+ * record, and once a later run decides how it executes. Saved with the plan.
1993
+ */
1994
+ get mergedRun(): string | null;
1995
+ get isOpen(): boolean;
1996
+ /** How many runs have opened: what tells one run's launches from the next one's. */
1997
+ get epoch(): number;
1998
+ /** The open run has decided whether it isolates. */
1999
+ get decided(): boolean;
2000
+ /** The open run executes in worktrees. */
2001
+ get isolating(): boolean;
2002
+ /** A run is waiting on the user to stash or to go on without isolation. */
2003
+ get blocked(): boolean;
2004
+ /** How many starts a dirty tree has turned away: what tells one wait on it from the next (ADR-0053). */
2005
+ get blockedStarts(): number;
2006
+ /** What the plan persists of isolated execution; null when no run ever isolated. */
2007
+ get planIsolation(): PlanIsolation | null;
2008
+ /** The one guard of every action on the run: a plan that never isolated is a request refused, not a fault. */
2009
+ requireRun(): IsolationRun;
2010
+ /** A task's record in the open run, only while that run isolates. */
2011
+ openRecord(taskId: string): IsolationTaskRecord | undefined;
2012
+ /** Where a task's isolated work stands; null when the plan has no isolation run to speak of. */
2013
+ taskIsolation(taskId: string): TaskIsolation | null;
2014
+ view(): IsolationView | null;
2015
+ /**
2016
+ * Take over a plan's persisted isolation, or none for a plan that has not
2017
+ * isolated yet. Whatever a crashed process left behind for the run — a
2018
+ * worktree still marked active, a directory no record owns — is pruned,
2019
+ * while kept, failed and conflicted worktrees stay for the user. Then git is
2020
+ * asked which landed work the user has merged since the record was saved:
2021
+ * a settled run whose landed work is all in HEAD has nothing left to hand
2022
+ * over, so it is cleared up as Merge all would have, unless it still holds
2023
+ * work that never landed (ADR-0013).
2024
+ *
2025
+ * Deliberately not reported to the listener: adopting is not a change of the
2026
+ * session's own, and a host that adopts without persisting (VS Code's
2027
+ * restore) would otherwise write a new session file on every reload. What
2028
+ * git changed is returned instead, for the caller to save where it can and
2029
+ * announce. A plan loaded meanwhile supersedes this adoption, which then
2030
+ * touches nothing and resolves `unchanged`. `mergedRun` is the plan's saved
2031
+ * {@link mergedRun}.
2032
+ */
2033
+ adopt(state: PlanIsolation | null, mergedRun?: string): Promise<AdoptOutcome>;
2034
+ /** Throws, as {@link hostChild} does, for a task with no worktree: read before a carve, so one that cannot seat a child spends nothing. */
2035
+ requireChildSeat(taskId: string): void;
2036
+ /**
2037
+ * The child session a task's worktree is the code root of, named on the
2038
+ * task's record so a reopen knows that worktree is a child's (ADR-0055 D17),
2039
+ * and the same child again once it is. A task with no worktree has nothing
2040
+ * to host one in: a run that shares the workspace root would root the child
2041
+ * in the user's checkout (D12).
2042
+ */
2043
+ hostChild(taskId: string, mintSessionId: () => string): string;
2044
+ /** The child session seated in a task's worktree, if one is. */
2045
+ childSession(taskId: string): string | undefined;
2046
+ /** A child's run goes, and what that kept or could not let go is said, as a run's own discard says it (ADR-0055 D15). */
2047
+ discardChild(place: ChildSessionPlace): Promise<IsolationRemoval>;
2048
+ /** Open a run if none is, leaving whether it isolates to its first change task. */
2049
+ open(): void;
2050
+ /**
2051
+ * Decide once per run, as its first change task starts, whether it executes
2052
+ * in worktrees (ADR-0020): an ops task needs none, so a run of only ops
2053
+ * tasks never asks. `resume` is what a dirty tree parks until the user
2054
+ * chooses how to go on. Resolves false while the run waits on that choice.
2055
+ */
2056
+ decide(resume: () => Promise<void>): Promise<boolean>;
2057
+ /**
2058
+ * Whether the run in force, or else the next one, gives each task its own
2059
+ * worktrees, and of which repo group — what the planner is told, since it
2060
+ * decides whether tasks on the same file have to be ordered and which shared
2061
+ * paths two tasks must not edit at once. A tree that would block counts as
2062
+ * not isolating: the user may yet run without isolation, and ordering is
2063
+ * the safe rule then.
2064
+ */
2065
+ plannerLayout(): Promise<IsolatedExecution>;
2066
+ /**
2067
+ * Where the workspace's skills are read, and whether tasks get worktrees.
2068
+ * Read synchronously inside a planner turn or a chip edit, so it comes from
2069
+ * the files, not git or the planner's last layout: a restored session is
2070
+ * right from its first edit. The run's repos while it isolates; otherwise
2071
+ * the group the workspace holds, in worktrees unless isolation is off or
2072
+ * this run chose to share the root.
2073
+ */
2074
+ skillLayout(): SkillLayout;
2075
+ /**
2076
+ * Go on with the start a dirty tree turned away. `stash` puts the user's
2077
+ * tracked changes on the git stash first, so the run isolates; `shared` runs
2078
+ * this one run in the workspace root, knowingly; `reread` does nothing to the
2079
+ * tree, so the replay asks about it again (ADR-0053). Returns the parked start
2080
+ * for the caller to replay; null when nothing was parked.
2081
+ */
2082
+ continueBlocked(how: 'stash' | 'shared' | 'reread'): Promise<(() => Promise<void>) | null>;
2083
+ /**
2084
+ * The working directory of an attempt that may run in a worktree — which
2085
+ * attempts do is the attempt kind's to say (see `attemptKind.attemptWorkspace`):
2086
+ * the worktree prepared for it in an isolated run, the kept one for a
2087
+ * conflict repair, else the workspace root. `worktree` says which. Does not
2088
+ * itself report a worktree it creates as a change — the caller does once
2089
+ * the attempt is committed as running, so a spawn abandoned or failed after
2090
+ * this settles is not misreported as a change that stuck.
2091
+ */
2092
+ attemptCwd(task: Task, intent: AttemptIntent): Promise<AttemptWorkspace>;
2093
+ /** The workspace root, for an attempt that runs outside any worktree. */
2094
+ workspaceCwd(task: Task): Promise<{
2095
+ cwd: string;
2096
+ worktree: boolean;
2097
+ }>;
2098
+ private reopenCwd;
2099
+ private prepareCwd;
2100
+ /**
2101
+ * Land a task's work on the run's integration branches. The record is
2102
+ * reported changed once the landing is set and before the first merge, so
2103
+ * a crash mid-landing leaves the tips to roll back to, and again once it
2104
+ * settles. A git error is `failed`, never a throw.
2105
+ */
2106
+ integrate(task: Task, commit?: HandoffCommit): Promise<IsolationOutcome>;
2107
+ private keepLandedOn;
2108
+ /** What a conflict repair's work shows (ADR-0015); git that cannot tell counts against it. */
2109
+ verifyRepair(task: Task): Promise<RepairEvidence>;
2110
+ /** What a repair brings onto the tip it was repaired against (ADR-0055 D14); throws where git cannot say, so nothing lands on a guess. */
2111
+ repairPaths(task: Task): Promise<string[]>;
2112
+ /**
2113
+ * Let go of a task's worktree. `keep` leaves it and its branch for
2114
+ * inspection; otherwise both go. A landing still in flight settles first, so
2115
+ * the worktree is never torn down underneath its merge. Bound to the run
2116
+ * the call was made in: a plan loaded meanwhile adopts its own run, whose
2117
+ * records share these task ids and are not this release's to touch.
2118
+ */
2119
+ release(taskId: string, opts: {
2120
+ keep: boolean;
2121
+ }, landing?: Promise<unknown> | null): Promise<void>;
2122
+ /**
2123
+ * Close the open run. An isolated one hands its integration branch over for
2124
+ * review — unless everything it landed is merged already, at merge gates or
2125
+ * by the run's automatic Merge all, which leaves nothing to hand over, so it
2126
+ * is cleared up as a Merge all would have.
2127
+ */
2128
+ close(): Promise<void>;
2129
+ /**
2130
+ * Whether a task's landed work still waits to be merged into the user's
2131
+ * branch — what holds an ops or user task at its merge gate (ADR-0020). A
2132
+ * task with no record never landed anything to wait for.
2133
+ */
2134
+ awaitsMerge(taskId: string): boolean;
2135
+ /**
2136
+ * Look again for landed work the user has merged — by Merge all, or by hand
2137
+ * with git — or has taken back out. Resolves true when a mark changed.
2138
+ */
2139
+ refreshInHead(): Promise<boolean>;
2140
+ /** Whether any landed work reads as merged: an answer git can take back, if the user takes the merge out. */
2141
+ get readsMerged(): boolean;
2142
+ /** Mark the run's landed work as git finds it now; true when a mark changed. */
2143
+ private findLanded;
2144
+ /** The run landed something and every landing is in the user's HEAD. */
2145
+ private allMerged;
2146
+ /**
2147
+ * The tracked state of the workspace as an attempt whose tree is checked
2148
+ * starts (see `attemptKind.checksTree`), for {@link filesChangedSince}
2149
+ * (ADR-0020). Null where the check does not run: a run that shares the
2150
+ * workspace root, where every task's edits land in it, or a workspace git
2151
+ * cannot isolate at all.
2152
+ */
2153
+ snapshotWorkspace(): Promise<TreeSnapshot | null>;
2154
+ /** Tracked files changed since `snapshot`; none when the run has since gone on in the workspace root. */
2155
+ filesChangedSince(snapshot: TreeSnapshot | null): Promise<string[]>;
2156
+ /**
2157
+ * A stop or a plan load: the open run ends without a handoff and a parked
2158
+ * start is dropped. `keepOpen` spares a run the scheduler is still driving.
2159
+ */
2160
+ interrupt(opts?: {
2161
+ keepOpen?: boolean;
2162
+ }): void;
2163
+ /**
2164
+ * Mark which added task resolves which conflict. The resolver merges the
2165
+ * conflicted task's branch by hand in its own worktree; once that lands, the
2166
+ * conflicted task can land in turn — through the same merge, so a resolver
2167
+ * that did not really bring it along conflicts again instead of being taken
2168
+ * at its word.
2169
+ */
2170
+ linkResolver(resolverId: string, conflictedId: string): void;
2171
+ /** The conflicted task a landed resolver was added for, forgotten as it is returned. */
2172
+ takeResolver(resolverId: string): string | undefined;
2173
+ /** The tasks added to resolve this task's conflict: one, but for a plan saved before that was the rule (ADR-0015). */
2174
+ resolversOf(conflictedId: string): string[];
2175
+ /** The paths a task's branch changed since its base, by their place in the workspace. Throws where they cannot be read: a verdict that rests on them fails closed (ADR-0055 D6). */
2176
+ changedPaths(taskId: string): Promise<string[]>;
2177
+ /** The branch this run's work lands on, which a board's files are compared with (ADR-0057 R9). */
2178
+ get integrationBranch(): string | undefined;
2179
+ /** See {@link IWorktreeIsolation.blobOn}. */
2180
+ blobOn(branch: string, file: string): Promise<string | null | undefined>;
2181
+ taskDiff(taskId: string, options: TaskDiffRead): Promise<TaskDiffResult>;
2182
+ reviewDiff(): Promise<string>;
2183
+ /**
2184
+ * "Merge all": the run's integration branches into whatever the user has
2185
+ * checked out, in every repo or none. During a run it merges what has
2186
+ * landed so far and the run goes on (ADR-0020): its branches stay for the
2187
+ * tasks still to land. Once a settled run merged everything, it has nothing
2188
+ * left to hand over, so it is cleared up and forgotten; a branch the user's
2189
+ * HEAD somehow does not contain stays for the next run's sweep.
2190
+ */
2191
+ merge(): Promise<IsolationMergeResult>;
2192
+ /** What git says of landed work a revert would take; see {@link IWorktreeIsolation.readLanded}. */
2193
+ readLanded(taskIds: readonly string[]): Promise<LandedWork>;
2194
+ /**
2195
+ * Revert tasks' landings on the integration branches (ADR-0054). The record
2196
+ * is reported changed once the tips are set and before the first commit,
2197
+ * again as each commit is made, so a crash leaves it to tell them from
2198
+ * anyone's, and once it settles. Bound to the run it was
2199
+ * asked in: a plan loaded meanwhile has records of its own under these ids,
2200
+ * and is saved without what this would write to that run's branches, so it
2201
+ * stops the revert and leaves them as they were.
2202
+ */
2203
+ revert(taskIds: readonly string[], watch: Omit<RevertWatch, 'persist'>): Promise<IsolationRevertResult>;
2204
+ /**
2205
+ * Worktrees and task branches go; the integration branch and the record stay
2206
+ * for review and merge. Refused while a task of the run is still live.
2207
+ */
2208
+ cleanup(): Promise<void>;
2209
+ /**
2210
+ * The run and everything it made go, and the plan forgets it; the next run
2211
+ * starts afresh. Refused while a task of the run is still live.
2212
+ */
2213
+ discard(): Promise<void>;
2214
+ /** The records of tasks still live, whose worktrees no clean-up of the whole run may take. */
2215
+ private liveRecords;
2216
+ private refuseWhileLive;
2217
+ /**
2218
+ * Clear a settled run up once everything it landed is merged — and only
2219
+ * when nothing else is left in it: a live task, or a worktree holding work
2220
+ * that never landed, keeps the whole run, worktrees and branches, rather
2221
+ * than leaving that work to a deletion.
2222
+ */
2223
+ private clearMerged;
2224
+ /** The records that keep a settled run from being cleared up: live, or holding work that never landed. */
2225
+ private holdouts;
2226
+ /** The run's worktrees and merged branches go; false, said, when git would not let them. */
2227
+ private discardMerged;
2228
+ /** Say what a removal kept back instead of deleting: where the work went, or which worktree stayed. */
2229
+ private report;
2230
+ /** The branches the links' resolvers would merge have gone: the links go, and the scheduler is told of the tasks left. */
2231
+ private endResolvers;
2232
+ /** The run goes from the plan; `merged` names it as {@link mergedRun} when everything it landed was merged. */
2233
+ private forget;
2234
+ private assess;
2235
+ private activate;
2236
+ private begin;
2237
+ /** What earlier runs left merged in the group goes; a failure here is worth a word, never a stopped run. */
2238
+ private sweep;
2239
+ /**
2240
+ * The plan's run carries on while it still has any record: anything landed
2241
+ * means a resumed plan's dependents must start from a tip that holds their
2242
+ * predecessors' work, and anything held — a kept attempt, a conflict, a
2243
+ * repair — is work the user may still want, which a fresh run's mint would
2244
+ * delete. Only a run with no records at all is superseded, and not one that
2245
+ * reverted the tasks it had: a retry starts from its tip (ADR-0054). A record
2246
+ * that names a child holds a code root, on a path that child's own run is tied
2247
+ * to, whether or not a Stop has yet taken it off `active` (ADR-0055 D17).
2248
+ */
2249
+ private continuableRun;
2250
+ /**
2251
+ * A run with no records at all holds only superseded attempts, so it goes —
2252
+ * but its integration branch only where the user's HEAD contains it, as does
2253
+ * that of a run that cannot be continued for another reason (it ran from a
2254
+ * different workspace path): only the user gives landed work up.
2255
+ *
2256
+ * False when `interrupted` came while git was minting: a newer activation
2257
+ * may be minting its own run, so this one is never installed over it, and
2258
+ * is discarded rather than left as branches no record names.
2259
+ */
2260
+ private mint;
2261
+ private reportCopies;
2262
+ private reportSetupFailures;
2263
+ /** Said at the first isolated run, where Ordewell first leans on git and where `git-missing` is said; a child's parent has said it already. */
2264
+ private sayGitShortfall;
2265
+ private tell;
2266
+ }
2267
+
2268
+ /** What a backend can be asked to prove (ADR-0059 L3). */
2269
+ type Guarantee = 'fs.hostHidden' | 'fs.writeScope' | 'net.allowlist' | 'net.noMetadata' | 'cred.unreadable' | 'kernel.separate' | 'limits.memory';
2270
+ /** What a probe found: how, and when. Never declared, so an unproven entry relaxes nothing (L3). */
2271
+ interface GuaranteeProof {
2272
+ proven: boolean;
2273
+ by: string;
2274
+ at: string;
2275
+ }
2276
+ type GuaranteeReport = Record<Guarantee, GuaranteeProof>;
2277
+ /**
2278
+ * Who asks, and in what role, which is policy input: a role is resolved to a
2279
+ * {@link LeaseSpec} before a backend sees one. `child-plan` reserves a node
2280
+ * and no slot (ADR-0055 D18). So does `merge`: it starts no session, and it
2281
+ * reaches a checkout, so a backend hears of it.
2282
+ */
2283
+ interface LeaseRequest {
2284
+ node: {
2285
+ sessionId: string;
2286
+ taskId?: string;
2287
+ };
2288
+ role: 'planner' | 'task' | 'script' | 'child-plan' | 'merge';
2289
+ runner?: RunnerId;
2290
+ }
2291
+ /** Where a lease puts its session. */
2292
+ type LeaseWorkspace = {
2293
+ kind: 'checkout';
2294
+ } | {
2295
+ kind: 'worktree';
2296
+ intent: AttemptIntent;
2297
+ };
2298
+ interface LeaseSpec {
2299
+ /** The attempt's task, as the claim read it; a planner's lease has none. */
2300
+ task?: Task;
2301
+ workspace: LeaseWorkspace;
2302
+ /** Open fails if one of these cannot be proven. */
2303
+ required: readonly Guarantee[];
2304
+ }
2305
+ /** A runner's session, which is what a Continue resumes: the id on the host today, and more behind a container (L2). */
2306
+ interface LeaseSessionState {
2307
+ runner: string;
2308
+ nativeSessionId: string | null;
2309
+ }
2310
+ /**
2311
+ * What outlives a lease (L2): a branch is not the whole result. A synchronous
2312
+ * read: the attempt that ends saves the session before anything can read it.
2313
+ */
2314
+ interface LeaseResult {
2315
+ /** Each repo's branch, by repo path; empty for work with no worktree. */
2316
+ refs: Record<string, string>;
2317
+ sessionState: LeaseSessionState;
2318
+ }
2319
+ /** How a lease's session ended where the host could not watch it (L3); `expired` comes with the first backend that has a time limit. */
2320
+ type LeaseExit = 'oom' | 'killed';
2321
+ /** A runner's launch, as its adapter wrote it for this host. */
2322
+ interface RunnerLaunch {
2323
+ command: string;
2324
+ args: readonly string[];
2325
+ env: Readonly<Record<string, string>>;
2326
+ /** Paths on this host, named in `args`, that the process reads, the one holding its token among them. */
2327
+ files: readonly string[];
2328
+ }
2329
+ /** What this host launches instead. */
2330
+ interface PlacedLaunch {
2331
+ command: string;
2332
+ args: string[];
2333
+ env: Record<string, string>;
2334
+ /** End the process where it runs: killing what this host launched does not reach it. */
2335
+ stop(): void;
2336
+ /** The process has ended. */
2337
+ ended(): void;
2338
+ }
2339
+ /**
2340
+ * Where a lease that does not run its session on this host puts its runner. A
2341
+ * runner is still handed a cwd and nothing more (ADR-0013): only {@link RunnerProcess}
2342
+ * reads this, and an adapter writes the launch it always wrote.
2343
+ */
2344
+ interface RunnerPlacement {
2345
+ place(launch: RunnerLaunch): Promise<PlacedLaunch>;
2346
+ /** Make a listener on this host's loopback reachable from there, and answer the URL to reach it by (`endpoints.mcp`). */
2347
+ reach(url: string): Promise<string>;
2348
+ /** The blob a file holds there, as git would name it, read where the attempt's work is; undefined for what cannot be read. */
2349
+ blob(file: string): Promise<string | undefined>;
2350
+ }
2351
+ /** One repository of an attempt's worktree, for a backend that keeps a copy of it elsewhere. */
2352
+ interface LeaseRepo {
2353
+ /** Relative to the workspace; `.` for a lone repository. */
2354
+ path: string;
2355
+ /** The repository on this host. */
2356
+ root: string;
2357
+ /** Its worktree for this task. */
2358
+ worktree: string;
2359
+ branch: string;
2360
+ integrationBranch: string;
2361
+ /** What Ordewell linked into the worktree, which is no attempt's work. */
2362
+ linked: readonly string[];
2363
+ }
2364
+ interface OpenLease {
2365
+ workspace: {
2366
+ cwd: string;
2367
+ /** The task workspace holding its worktrees, when it has any. */
2368
+ writable?: string;
2369
+ /** Whether it runs in a worktree prepared for it rather than at the root. */
2370
+ worktree: boolean;
2371
+ /** What became of the previous attempt's work, where a restore was asked for. */
2372
+ previousWork?: PreviousWork;
2373
+ /** Each repository of its worktree, plural as a repo group's are (ADR-0014). */
2374
+ repos?: readonly LeaseRepo[];
2375
+ };
2376
+ guarantees: GuaranteeReport;
2377
+ exportResult(session: LeaseSessionState): LeaseResult;
2378
+ /** Set where the runner does not run on this host. */
2379
+ placement?: RunnerPlacement;
2380
+ /** How its session ended, once it has, where the backend can tell (L3). */
2381
+ ended?(): Promise<LeaseExit | null>;
2382
+ }
2383
+ /**
2384
+ * Taken in the claim's synchronous step (L1). What it reserves is given back at
2385
+ * {@link release}; the workspace it opened need not go with it: a worktree
2386
+ * outlives the lease that made it, for a repair or a continue, so what becomes
2387
+ * of it is {@link ExecutionBackend.letGo}.
2388
+ */
2389
+ interface ReservedLease {
2390
+ readonly id: string;
2391
+ /** Whether this lease holds a slot: a delegated task's and a merge's do not. */
2392
+ readonly holdsSlot: boolean;
2393
+ /** Runs after the claim, where the workspace is made. Rejects without making one once `signal` has aborted. */
2394
+ open(spec: LeaseSpec, signal: AbortSignal): Promise<OpenLease>;
2395
+ /** Idempotent and synchronous: an attempt's end frees its slot before anything awaits (ADR-0029). */
2396
+ release(reason: string): void;
2397
+ }
2398
+ /** One task's workspace, as a restart finds it. */
2399
+ interface LeaseRecord {
2400
+ taskId: string;
2401
+ status: string;
2402
+ worktrees: readonly string[];
2403
+ }
2404
+ interface ExecutionBackend {
2405
+ /** What it can prove now. */
2406
+ probe(): Promise<GuaranteeReport>;
2407
+ /** Why this may not be reserved now, or null. Synchronous, and changes nothing: it is also asked with no claim to follow. */
2408
+ refusal(request: LeaseRequest): string | null;
2409
+ /** Synchronous, inside the claim. A refusal keeps the Session that asked, for what frees next. */
2410
+ reserve(request: LeaseRequest): {
2411
+ lease: ReservedLease;
2412
+ } | {
2413
+ refused: string;
2414
+ };
2415
+ /** What becomes of a task's workspace, with no lease needed: a kept worktree outlives the one that opened it. */
2416
+ letGo(taskId: string, how: {
2417
+ keep: boolean;
2418
+ landing?: Promise<unknown> | null;
2419
+ reason: string;
2420
+ }): Promise<void>;
2421
+ /** What a restart finds. Starts nothing (ADR-0044). */
2422
+ list(): Promise<readonly LeaseRecord[]>;
2423
+ /**
2424
+ * Bring a task's work to its worktree, for a backend that keeps it elsewhere:
2425
+ * the half of a result that takes time and can be refused (L2). By task, since
2426
+ * a landing can come after its attempt, as a Mark complete does. Absent, the
2427
+ * work is there already. Rejects with why it must not land.
2428
+ */
2429
+ collect?(taskId: string): Promise<void>;
2430
+ /**
2431
+ * Everything it has running has gone and what was on its way has arrived, as
2432
+ * the Session around it closes. Absent, nothing outlives an attempt's runner.
2433
+ * What a next attempt comes back to stays, and it never rejects.
2434
+ */
2435
+ settle?(): Promise<void>;
2436
+ /** The workspace root's repositories, where a run knows them: a repo group's root is no repository to ask. */
2437
+ checkoutRepos?(): readonly LeaseRepo[];
2438
+ /**
2439
+ * Let go of what its tree left where only it can reach, as a Clean up gives the
2440
+ * run up. Absent, there is nothing but the worktrees. Reports what it could not
2441
+ * clear, and never rejects: it is no reason for a clean-up to fail.
2442
+ */
2443
+ reclaim?(): Promise<void>;
2444
+ }
2445
+
1758
2446
  /**
1759
2447
  * The harness-planner transport contract (ADR-0009).
1760
2448
  *
@@ -2058,6 +2746,10 @@ interface PlannerStartOptions extends AgentStartCommon {
2058
2746
  * these are the only ones it gets, and each is handed over as parsed.
2059
2747
  */
2060
2748
  projectServers?: ProjectMcpServer[];
2749
+ /** The harness's own subagents are off for this session (ADR-0056 M6), each runner by its own switch. Absent means as the harness has them. */
2750
+ noSubagents?: true;
2751
+ /** What a subplanner's process loads of the user's setup (ADR-0057 R11); absent means `user`. */
2752
+ runnerConfig?: TaskRunnerConfig;
2061
2753
  }
2062
2754
  /**
2063
2755
  * What a runner manifest says the task's mode and effort mean (ADR-0001),
@@ -2074,6 +2766,14 @@ interface TaskRunnerFlags {
2074
2766
  /** The manifest's further settings for the task's mode, by setting name — see `RunnerFeatures.modeSettings`. */
2075
2767
  modeSettings: Record<string, string>;
2076
2768
  }
2769
+ /**
2770
+ * What a task runner loads of the user's own setup (the `taskRunnerConfig`
2771
+ * setting): `user` is everything the runner would load for the user,
2772
+ * `minimal` only what a task needs — Ordewell's server, the repository's
2773
+ * instructions — so the user's connectors, skills and settings do not ride in
2774
+ * every attempt's context. Runners that cannot narrow their setup ignore it.
2775
+ */
2776
+ type TaskRunnerConfig = 'user' | 'minimal';
2077
2777
  /** A plan task driven over the runner's programmatic protocol (ADR-0018, C1). */
2078
2778
  interface TaskStartOptions extends AgentStartCommon {
2079
2779
  kind: 'task';
@@ -2082,6 +2782,8 @@ interface TaskStartOptions extends AgentStartCommon {
2082
2782
  /** The task's runner mode id, as the plan names it. */
2083
2783
  mode: string;
2084
2784
  flags: TaskRunnerFlags;
2785
+ /** What the runner loads of the user's setup; absent means `user`. */
2786
+ runnerConfig?: TaskRunnerConfig;
2085
2787
  /** The attempt's work lands as a commit (ADR-0047): its completion contract asks for the message. */
2086
2788
  commits?: true;
2087
2789
  /**
@@ -2255,6 +2957,8 @@ interface AgentProcessDeps {
2255
2957
  workspaceEnv?: (cwd: string) => Promise<Record<string, string>>;
2256
2958
  /** Builds the sandbox around a planner's own shell (ADR-0036 N2). Defaults to the host's probe. */
2257
2959
  nativeShellSandbox?: () => PlannerShellSandbox;
2960
+ /** Where a lease put this runner, when not on this host (ADR-0059 L5). */
2961
+ placement?: RunnerPlacement;
2258
2962
  }
2259
2963
  /** Builds the adapter for one runner id, or null when that runner cannot plan. */
2260
2964
  type AgentAdapterFactory = (runner: string, deps: AgentProcessDeps) => AgentAdapter | null;
@@ -2740,7 +3444,7 @@ interface IRunnerSession {
2740
3444
  * The runner's `task_complete` calls on this attempt's token (ADR-0022):
2741
3445
  * the only completion evidence, for `VerdictEngine` to weigh.
2742
3446
  */
2743
- onTaskComplete(listener: (report: TaskCompleteArgs) => void): void;
3447
+ onTaskComplete(listener: (report: TaskCompleteArgs, refuted?: VerificationCheck) => void): void;
2744
3448
  /**
2745
3449
  * Answers the runner's `checkpoint` calls (ADR-0022, V5): the call stays
2746
3450
  * open until the returned promise settles, and its result is the answer.
@@ -2782,6 +3486,8 @@ interface IRunner {
2782
3486
  * manifest's env still wins over them.
2783
3487
  */
2784
3488
  env?: Record<string, string>;
3489
+ /** A delegated task's attempt (ADR-0055 D3): the task, for {@link ChildPlanRunner} to give its child. */
3490
+ delegated?: Task;
2785
3491
  /** The runner's own session to continue in (ADR-0018, K1). */
2786
3492
  resumeSessionId?: string;
2787
3493
  /** What was counted of that session's running cost before, so its first turn is counted from there. */
@@ -2806,6 +3512,8 @@ interface IRunner {
2806
3512
  * attempt. Runners ignore it; it is for what wraps one to record (task logs).
2807
3513
  */
2808
3514
  skills?: readonly TaskSkillSnapshot[];
3515
+ /** Where the attempt's lease put this runner, when not on this host (ADR-0059 L5). */
3516
+ placement?: RunnerPlacement;
2809
3517
  /** Something the user should know about how the spawn went that did not stop it — a respawn, most often. */
2810
3518
  onNotice?: (message: string) => void;
2811
3519
  /**
@@ -2823,8 +3531,12 @@ interface IRunner {
2823
3531
  cancelled?: AbortSignal;
2824
3532
  /** The attempt's work lands as a commit (ADR-0047): its runner is told to write the message in `task_complete`. */
2825
3533
  commits?: true;
3534
+ /** What the runner loads of the user's own setup (the `taskRunnerConfig` setting); absent means all of it. Runners that cannot narrow it ignore it. */
3535
+ runnerConfig?: TaskRunnerConfig;
2826
3536
  /** A script task's body (ADR-0035): only the script runner reads it, and it runs nothing else. */
2827
3537
  script?: ScriptBody;
3538
+ /** A read-only script's shell (ADR-0055 D8): its body runs sealed there, in the worktree it was cut. */
3539
+ sealed?: TaskShell;
2828
3540
  /**
2829
3541
  * A read-only attempt's shell (ADR-0046): its token serves `run_command`
2830
3542
  * from it, and only an attempt that has one is given that tool.
@@ -2835,12 +3547,14 @@ interface IRunner {
2835
3547
  * arrives (ADR-0047): a refusal is the call's result, and nothing is
2836
3548
  * recorded. Without one, a `task` reference resolves against no plan.
2837
3549
  */
2838
- checkHandoff?: (completion: TaskCompleteArgs) => HandoffCheck<TaskCompleteArgs>;
3550
+ checkHandoff?: (completion: TaskCompleteArgs) => HandoffCheck<TaskCompleteArgs> | Promise<HandoffCheck<TaskCompleteArgs>>;
2839
3551
  /**
2840
- * Answers the attempt's `read_task` calls (ADR-0047), against the plan
3552
+ * Answers the attempt's `read` calls (ADR-0047), against the plan
2841
3553
  * and the session's task logs. Without one the call says it is not available.
2842
3554
  */
2843
- readTask?: (args: ReadTaskArgs) => McpToolReply;
3555
+ readTask?: (args: ReadTaskArgs) => McpToolReply | Promise<McpToolReply>;
3556
+ /** Answers the attempt's `post` calls (ADR-0057 R8). Given only where its plan has a board, which is what lists the tool. */
3557
+ post?: (args: PostArgs) => McpToolReply | Promise<McpToolReply>;
2844
3558
  /**
2845
3559
  * Answers a read-only attempt's `save_patch` calls (ADR-0050): its shell
2846
3560
  * captures the worktree's changes and the attempt keeps them. Without one
@@ -2868,4 +3582,121 @@ interface IRunner {
2868
3582
  activeCount: number;
2869
3583
  }
2870
3584
 
2871
- export { type McpToolContext as $, AUTO_COMMANDS as A, type BashOptions as B, type CatalogModel as C, DEFAULT_PLANNER_ALLOWLIST as D, type DiscoveryCommand as E, type EditPlanArgs as F, type FetchAllProviderModelsOptions as G, type FindSymbolOptions as H, GIT_READONLY_SUBCOMMANDS as I, GREP_DEFAULT_HEAD_LIMIT as J, type GlobOptions as K, type GrepOptions as L, type GrepOutputMode as M, type IApproval as N, type IConfig as O, type IFileSystem as P, type IRunner as Q, type IRunnerSession as R, type InvokedCommand as S, LineBuffer as T, type ListModelsArgs as U, type ListRunnersArgs as V, type LoadSkillArgs as W, type McpClientConfig as X, type McpCredential as Y, type McpRefusal as Z, type McpTool as _, type AgentAdapter as a, type SealedAvailability as a$, type McpToolReply as a0, type MessageTaskArgs as a1, ModelCatalog as a2, type ModelShortcut as a3, ORCHESTRATOR_SHORTCUTS as a4, ORDEWELL_MCP_PATH as a5, ORDEWELL_MCP_SERVER_NAME as a6, type OneTaskArgs as a7, type OrchestratorOption as a8, OrdewellMcpServer as a9, type RunCommandArgs as aA, type RunPlanArgs as aB, type RunnerAuthSpec as aC, type RunnerAuthStatus as aD, type RunnerCommand as aE, type RunnerCommandKind as aF, type RunnerEntry as aG, type RunnerEnv as aH, type RunnerFeatures as aI, type RunnerManifest as aJ, type RunnerMcpServer as aK, type RunnerMode as aL, type RunnerModelDiscovery as aM, type RunnerModelRef as aN, RunnerRegistry as aO, SEALED_LIMITS as aP, SEARCH_EXCLUSIONS as aQ, type SandboxBackend as aR, type SandboxInvocation as aS, type SandboxLaunch as aT, type SandboxLimits as aU, type SandboxProfile as aV, type ScriptBody as aW, type ScriptExit as aX, type ScriptFullOutput as aY, type ScriptRun as aZ, type ScriptStatus as a_, type OwnSealedRuns as aa, type OwnerOnlyFile as ab, PLANNER_EXECUTION_TOOLS as ac, PLANNER_SANDBOX_DOCS as ad, PLANNER_TOOLS as ae, type PlannerAllowlist as af, type PlannerBackendId as ag, type PlannerSandboxAvailability as ah, type PlannerSandboxDeps as ai, PlannerShellSandbox as aj, type PlannerStartOptions as ak, type PlannerTokenScope as al, type PlannerToolHandler as am, type ProjectMcpServer as an, type ProviderCredentialSource as ao, type ProviderModelLists as ap, type ProviderModelsResult as aq, type QueuedTaskMessage as ar, READ_ONLY_TASK_TOOLS as as, REFUSED_COMMANDS as at, type ReadFileOpts as au, type ReadTaskArgs as av, type ResearchShell as aw, type ResearchShellDeps as ax, type RetryTaskArgs as ay, type RuleWord as az, type AgentAdapterFactory as b, type SealedRunOptions as b0, type ShellDialect as b1, type ShellLaunch as b2, type SpawnFn as b3, type StandingDecision as b4, type StructuredEvent as b5, type StructuredTurnEnd as b6, type SubmitPlanArgs as b7, TASK_COMPLETE_TOOL as b8, TASK_COMPLETION_CONTRACT as b9, effectiveApprovalMode as bA, enabledRunners as bB, fetchAllProviderModels as bC, findRunnerCommand as bD, isGranted as bE, isRunnerApproval as bF, knownModelId as bG, mcpClientConfig as bH, ownerOnlyConfigFile as bI, parseAllowlist as bJ, parseApprovalModeSetting as bK, researchShellWarning as bL, researchToolsPath as bM, resolveModelShortcut as bN, resolvePlannerSandbox as bO, resolveProvider as bP, resolveResearchShell as bQ, scopeAllowed as bR, scriptBodyOf as bS, scriptBodyText as bT, scriptTaskText as bU, sharedMcpServer as bV, taskCompletionContract as bW, timeLimitText as bX, toApprovalDecision as bY, toOrchestratorOptions as bZ, toolAllowed as b_, TASK_SEALED_LIMITS as ba, TASK_TOOLS as bb, type TaskCompleteArgs as bc, type TaskDiffArgs as bd, type TaskMessageFrom as be, type TaskModeAgentAdapter as bf, TaskModeUnsupportedError as bg, type TaskOutputArgs as bh, type TaskQueryArgs as bi, type TaskRunCommandArgs as bj, type TaskRunnerFlags as bk, type TaskShell as bl, type TaskShellSite as bm, type TaskStartOptions as bn, type TaskTokenScope as bo, type TaskToolHandler as bp, type TaskWebReads as bq, type ToolOutcome as br, type UsageLimitSignal as bs, approvalScopes as bt, classifyCommand as bu, clearPlannerSandboxCache as bv, clearResearchShellCache as bw, collectProviderCredentials as bx, commandAllowedBy as by, commandLine as bz, type AgentEvent as c, type AgentProcessDeps as d, type AgentStartOptions as e, type AiProvider as f, type AllProviderModels as g, type AnswerApprovalArgs as h, type ApplyPatchArgs as i, type ApprovalAnswer as j, type ApprovalDecision as k, type ApprovalKind as l, type ApprovalMode as m, type ApprovalModeSetting as n, ApprovalPolicy as o, type ApprovalPolicyOptions as p, type ApprovalRequest as q, type ApprovalSource as r, type AttachState as s, type CheckpointAnswer as t, type CheckpointArgs as u, type CommandClassification as v, type CommandTier as w, type CommandWords as x, DEFAULT_SCRIPT_TIMEOUT_MS as y, DENY_ALL as z };
3585
+ declare const BOARD_TYPES: readonly ["finding", "claim", "attempt", "artifact", "contract", "assume", "scope_change", "decision", "pin"];
3586
+ type BoardEntryType = (typeof BOARD_TYPES)[number];
3587
+ /** `file` evidence, with the blobs R9 compares: Ordewell reads both as the entry is posted, so neither is an author's claim. */
3588
+ interface BoardFile {
3589
+ kind: 'file';
3590
+ path: string;
3591
+ /** The file as its author had it, where it could be read. */
3592
+ sha?: string;
3593
+ /** What the board's branch held then, absent where it held no such file. */
3594
+ tip?: string;
3595
+ }
3596
+ /** What an author sends. */
3597
+ interface BoardPost {
3598
+ type: BoardEntryType;
3599
+ title: string;
3600
+ detail?: string;
3601
+ /** What `contested` compares (R9): entries with one key and different values. */
3602
+ key?: string;
3603
+ value?: string;
3604
+ files?: string[];
3605
+ }
3606
+ /** Whose words an entry is, as the token that posted it says and never the entry. */
3607
+ interface BoardAuthor {
3608
+ node: NodePath;
3609
+ /** Its task's label in that plan; absent for that plan's planner. */
3610
+ task?: string;
3611
+ attempt?: number;
3612
+ }
3613
+ interface BoardEntry extends Omit<BoardPost, 'files'> {
3614
+ /** The board it is on, and its number there: together its reference. */
3615
+ node: NodePath;
3616
+ id: number;
3617
+ by: BoardAuthor;
3618
+ at: string;
3619
+ files?: BoardFile[];
3620
+ /** The branch `files` was read against, which is the same for every reader. */
3621
+ branch?: string;
3622
+ /** The entry it was raised from at a landing (R6), which keeps its author. */
3623
+ raisedFrom?: {
3624
+ node: NodePath;
3625
+ id: number;
3626
+ };
3627
+ }
3628
+ /** An entry as the log is told of it: the ledger numbers it and the clock stamps it. */
3629
+ type BoardDraft = Omit<BoardEntry, 'node' | 'id' | 'at'>;
3630
+
3631
+ /** `root/3/2`: the ids from the root down. A node's parent never changes, so neither does this. */
3632
+ type NodePath = string;
3633
+ interface OfNode {
3634
+ /** When it was written: for a reader, and never read by a fold. */
3635
+ at: string;
3636
+ node: NodePath;
3637
+ }
3638
+ type TreeFact = OfNode & (
3639
+ /** A node joined the tree. Below the root its `limit` is carved from its parent, and `kept` of it stays for the subplanner's next turn (ADR-0056 M5). */
3640
+ {
3641
+ kind: 'node_opened';
3642
+ sessionId: string;
3643
+ taskId?: string;
3644
+ limit: number;
3645
+ kept: number;
3646
+ }
3647
+ /** `node` confirmed closed: its parent holds its `limit` no longer, and is charged what it spent. */
3648
+ | {
3649
+ kind: 'node_closed';
3650
+ }
3651
+ /** Tokens as a runner or planner reported them. A turn spends what its node kept first. */
3652
+ | {
3653
+ kind: 'tokens_spent';
3654
+ tokens: number;
3655
+ turn?: true;
3656
+ taskId?: string;
3657
+ attempt?: number;
3658
+ }
3659
+ /** A raise: `tokens` more on `node`'s limit, from its parent's room. */
3660
+ | {
3661
+ kind: 'budget_raised';
3662
+ tokens: number;
3663
+ }
3664
+ /** Its parent's own spending took `tokens` of `node`'s room: `node`'s limit and its parent's `committed` fall together. */
3665
+ | {
3666
+ kind: 'budget_taken';
3667
+ tokens: number;
3668
+ }
3669
+ /** A subplanner parked, keeping `tokens` more for its next turn. */
3670
+ | {
3671
+ kind: 'reserve_kept';
3672
+ tokens: number;
3673
+ }
3674
+ /** The user set the root's limit, the tree's budget, to `limit`. */
3675
+ | {
3676
+ kind: 'limit_set';
3677
+ limit: number;
3678
+ }
3679
+ /** An entry on `node`'s board, whole: a board is a fold of these (ADR-0057 R8). */
3680
+ | {
3681
+ kind: 'board_posted';
3682
+ entry: BoardEntry;
3683
+ });
3684
+ /** A fact as the log holds it: `seq` is its place in the tree's one order, from 1. */
3685
+ type TreeEvent = TreeFact & {
3686
+ seq: number;
3687
+ };
3688
+
3689
+ /**
3690
+ * One append-only log per root (ADR-0058 S3), behind a port so that a daemon
3691
+ * host can back it with SQLite later. Only the host holding the root's session
3692
+ * lease writes it (ADR-0055 D13).
3693
+ */
3694
+ interface EventStore {
3695
+ /** In order, each batch whole: after a crash a reader finds all of a batch or none of it. Numbers them. */
3696
+ append(events: readonly TreeFact[]): Promise<void>;
3697
+ /** What came after the cursor, at `path` and below when one is named. */
3698
+ read(after: number, path?: NodePath): AsyncIterable<TreeEvent>;
3699
+ close(): Promise<void>;
3700
+ }
3701
+
3702
+ export { type InvokedCommand as $, AUTO_COMMANDS as A, type BashOptions as B, type CatalogModel as C, type CommandClassification as D, type CommandTier as E, ConsoleLogger as F, DENY_ALL as G, type DiscoveryCommand as H, type EditPlanArgs as I, type EventStore as J, type ExecutionBackend as K, type FetchAllProviderModelsOptions as L, type FindSymbolOptions as M, GIT_READONLY_SUBCOMMANDS as N, GREP_DEFAULT_HEAD_LIMIT as O, type GlobOptions as P, type GrepOptions as Q, type GrepOutputMode as R, type Guarantee as S, type GuaranteeReport as T, type IApproval as U, type IConfig as V, type IFileSystem as W, type ILogger as X, type INotification as Y, type IRunner as Z, type IRunnerSession as _, type AgentAdapter as a, type RunnerFeatures as a$, type IsolatedExecution as a0, IsolationRunController as a1, type LeaseRecord as a2, type LeaseRepo as a3, type LeaseRequest as a4, type LeaseSpec as a5, LineBuffer as a6, type ListModelsArgs as a7, type ListRunnersArgs as a8, type LoadSkillArgs as a9, type PlannerSandboxDeps as aA, PlannerShellSandbox as aB, type PlannerStartOptions as aC, type PlannerTokenScope as aD, type PlannerToolHandler as aE, type PostArgs as aF, type ProjectMcpServer as aG, type ProviderCredentialSource as aH, type ProviderModelLists as aI, type ProviderModelsResult as aJ, type QueuedTaskMessage as aK, READ_ONLY_TASK_TOOLS as aL, REFUSED_COMMANDS as aM, type ReadFileOpts as aN, type ReadTaskArgs as aO, type ResearchShell as aP, type ResearchShellDeps as aQ, type ReservedLease as aR, type RetryTaskArgs as aS, type RunCommandArgs as aT, type RunPlanArgs as aU, type RunnerAuthSpec as aV, type RunnerAuthStatus as aW, type RunnerCommand as aX, type RunnerCommandKind as aY, type RunnerEntry as aZ, type RunnerEnv as a_, type McpClientConfig as aa, type McpCredential as ab, type McpRefusal as ac, type McpTool as ad, type McpToolContext as ae, type McpToolReply as af, type MessageTaskArgs as ag, ModelCatalog as ah, type ModelShortcut as ai, type NodePath as aj, type NotificationAction as ak, ORCHESTRATOR_SHORTCUTS as al, ORDEWELL_MCP_PATH as am, ORDEWELL_MCP_SERVER_NAME as an, type OneTaskArgs as ao, type OpenLease as ap, type OrchestratorOption as aq, OrdewellMcpServer as ar, type OwnSealedRuns as as, type OwnerOnlyFile as at, PLANNER_EXECUTION_TOOLS as au, PLANNER_SANDBOX_DOCS as av, PLANNER_TOOLS as aw, type PlannerBackendId as ax, type PlannerModes as ay, type PlannerSandboxAvailability as az, type AgentAdapterFactory as b, fetchAllProviderModels as b$, type RunnerManifest as b0, type RunnerMcpServer as b1, type RunnerMode as b2, type RunnerModelDiscovery as b3, type RunnerModelRef as b4, type RunnerPlacement as b5, RunnerRegistry as b6, SEALED_LIMITS as b7, SEARCH_EXCLUSIONS as b8, type SandboxBackend as b9, type TaskMessageFrom as bA, type TaskModeAgentAdapter as bB, TaskModeUnsupportedError as bC, type TaskOutputArgs as bD, type TaskQueryArgs as bE, type TaskRunCommandArgs as bF, type TaskRunnerConfig as bG, type TaskRunnerFlags as bH, type TaskShell as bI, type TaskShellSite as bJ, type TaskStartOptions as bK, type TaskTokenScope as bL, type TaskToolHandler as bM, type TaskWebReads as bN, type ToolOutcome as bO, type TreeEvent as bP, type TreeFact as bQ, type UsageLimitSignal as bR, approvalScopes as bS, classifyCommand as bT, clearPlannerSandboxCache as bU, clearResearchShellCache as bV, collectProviderCredentials as bW, defaultLogger as bX, deleteSession as bY, effectiveApprovalMode as bZ, enabledRunners as b_, type SandboxInvocation as ba, type SandboxLaunch as bb, type SandboxLimits as bc, type SandboxProfile as bd, type ScriptExit as be, type ScriptFullOutput as bf, type ScriptRun as bg, type ScriptStatus as bh, type SealedAvailability as bi, type SealedRunOptions as bj, type SessionData as bk, type SessionMeta as bl, type ShellDialect as bm, type ShellLaunch as bn, type SkillLookup as bo, type SpawnFn as bp, type StandingDecision as bq, type StructuredEvent as br, type StructuredTurnEnd as bs, type SubmitPlanArgs as bt, TASK_COMPLETE_TOOL as bu, TASK_COMPLETION_CONTRACT as bv, TASK_SEALED_LIMITS as bw, TASK_TOOLS as bx, type TaskCompleteArgs as by, type TaskDiffArgs as bz, type AgentEvent as c, findRunnerCommand as c0, getLatestSession as c1, idSegment as c2, isGranted as c3, isRunnerApproval as c4, knownModelId as c5, listChildSessions as c6, listSessions as c7, loadChildSession as c8, loadSession as c9, loadSessionPlanState as ca, mcpClientConfig as cb, ownerOnlyConfigFile as cc, parseApprovalModeSetting as cd, plannerToolSet as ce, researchShellWarning as cf, researchToolsPath as cg, resolveModelShortcut as ch, resolvePlannerSandbox as ci, resolveProvider as cj, resolveResearchShell as ck, saveChildSession as cl, saveSession as cm, sessionDataDir as cn, sharedMcpServer as co, taskCompletionContract as cp, taskToolSet as cq, toApprovalDecision as cr, toOrchestratorOptions as cs, toolListing as ct, type AgentProcessDeps as d, type AgentStartOptions as e, type AiProvider as f, type AllProviderModels as g, type AnswerApprovalArgs as h, type ApplyPatchArgs as i, type ApprovalAnswer as j, type ApprovalDecision as k, type ApprovalKind as l, type ApprovalMode as m, type ApprovalModeSetting as n, ApprovalPolicy as o, type ApprovalPolicyOptions as p, type ApprovalRequest as q, type ApprovalSource as r, type AttachState as s, type BoardAuthor as t, type BoardDraft as u, type BoardEntry as v, type BoardPost as w, type CheckpointAnswer as x, type CheckpointArgs as y, type ChildSessionPlace as z };