@ordewell/core 0.12.0 → 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 (48) hide show
  1. package/dist/{ModeResolver-CHsaoV7M.d.ts → ModeResolver-B64aUZXJ.d.ts} +1 -1
  2. package/dist/{ModeResolver-b_7bPlBS.d.mts → ModeResolver-CA2fUU9h.d.mts} +1 -1
  3. package/dist/{Task-BTEH-ztX.d.mts → Task-CxGgN6I0.d.mts} +503 -16
  4. package/dist/{Task-BTEH-ztX.d.ts → Task-CxGgN6I0.d.ts} +503 -16
  5. package/dist/{chunk-BKTDYGIP.mjs → chunk-2IFKPYPO.mjs} +441 -17
  6. package/dist/chunk-2IFKPYPO.mjs.map +1 -0
  7. package/dist/{chunk-BFLVOYNR.mjs → chunk-FX33UKAD.mjs} +8 -6
  8. package/dist/chunk-FX33UKAD.mjs.map +1 -0
  9. package/dist/{chunk-F6WS3JLQ.mjs → chunk-PKJTN2PD.mjs} +20 -11
  10. package/dist/chunk-PKJTN2PD.mjs.map +1 -0
  11. package/dist/{chunk-HP2LNKST.mjs → chunk-RZ23BMVR.mjs} +345 -134
  12. package/dist/chunk-RZ23BMVR.mjs.map +1 -0
  13. package/dist/chunk-UDUATSCJ.mjs +48 -0
  14. package/dist/chunk-UDUATSCJ.mjs.map +1 -0
  15. package/dist/{IRunner-BwzyUVIL.d.mts → eventStore-CTCSKS9E.d.ts} +916 -129
  16. package/dist/{IRunner-DjJkYwko.d.ts → eventStore-CcB8yELZ.d.mts} +916 -129
  17. package/dist/index.d.mts +677 -97
  18. package/dist/index.d.ts +677 -97
  19. package/dist/index.js +8233 -2450
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +6951 -1772
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/parsing.d.mts +2 -2
  24. package/dist/parsing.d.ts +2 -2
  25. package/dist/parsing.js +277 -21
  26. package/dist/parsing.js.map +1 -1
  27. package/dist/parsing.mjs +3 -3
  28. package/dist/plan-utils.d.mts +8 -5
  29. package/dist/plan-utils.d.ts +8 -5
  30. package/dist/plan-utils.js +129 -16
  31. package/dist/plan-utils.js.map +1 -1
  32. package/dist/plan-utils.mjs +17 -3
  33. package/dist/{runSettingsText-x-YML2ay.d.mts → runSettingsText-DFVpe6aU.d.mts} +1142 -934
  34. package/dist/{runSettingsText-CvZ40IPv.d.ts → runSettingsText-lKLDsyli.d.ts} +1142 -934
  35. package/dist/testing.d.mts +83 -4
  36. package/dist/testing.d.ts +83 -4
  37. package/dist/testing.js +142 -1
  38. package/dist/testing.js.map +1 -1
  39. package/dist/testing.mjs +126 -2
  40. package/dist/testing.mjs.map +1 -1
  41. package/docker/task.Dockerfile +22 -0
  42. package/package.json +2 -1
  43. package/skills/diagnose/SKILL.md +2 -2
  44. package/skills/explore/SKILL.md +1 -1
  45. package/dist/chunk-BFLVOYNR.mjs.map +0 -1
  46. package/dist/chunk-BKTDYGIP.mjs.map +0 -1
  47. package/dist/chunk-F6WS3JLQ.mjs.map +0 -1
  48. package/dist/chunk-HP2LNKST.mjs.map +0 -1
@@ -1,18 +1,234 @@
1
- import { a_ as StoredHandoff, b1 as Task, aK as RunnerId, bb as TaskSnapshot, b as AwaitingReason, bh as UsageRecord, aL as RunnerSessionCost, i as DiscoveredModel, aZ as SkillsService, aU as SkillInfo, az as RepoGroupLayout, z as IWorktreeIsolation, a0 as IsolationRun, M as IsolationHandoff, af as PlanIsolation, a1 as IsolationTaskRecord, b4 as TaskIsolation, a5 as IsolationView, ao as PrepareIntent, as as PreviousWork, q as HandoffCommit, U as IsolationOutcome, aw as RepairEvidence, bf as TreeSnapshot, b2 as TaskDiffRead, b3 as TaskDiffResult, T as IsolationMergeResult, a7 as LandedWork, aI as RevertWatch, _ as IsolationRevertResult, bj as Verdict, a8 as LegacyPlanState, b8 as TaskReportTo, ba as TaskSkillSnapshot, b9 as TaskSkillNotice, a$ as SubagentOutcome, a2 as IsolationTaskRef, aH as RevertCommand, $ as IsolationRevertTask, d as ConversationMessage, aW as SkillLoadNotice, ae as PendingPlanTurn, r as HandoffDigest, a as AttemptUsage, b6 as TaskLoop, aJ as RunSettings, ai as PlanUsage, ay as ReplyRun, aP as SessionBilling, ab as ModelUsage, bi as UsageTotals, c as ContextFill, aj as PlannerCompaction, k as ExecutionRecord, aE as ResearchStep, ag as PlanState, ax as RepairWait, b0 as SupervisorLevel, bc as TaskStatus, E as ExecutionActor, l as GuardTrip, G as GuardTarget, j as ExecutionCommandName, aD as ResearchProgress, aC as ResearchLogEntry, aV as SkillLoad, al as PlannerContextPause, ah as PlanStatus, R as IsolationMergeBlock, P as IsolationLandedTask, V as IsolationPreview, aG as ResearchToolType, a9 as LoopSubject, aF as ResearchStepOutcome, ak as PlannerCompactionOutcome, an as PlannerUsage, F as FindingSeverity, bd as TaskType, ad as PatchView, b5 as TaskIsolationState, be as TaskUsageSummary } from './Task-BTEH-ztX.js';
2
- import { q as ApprovalRequest, r as ApprovalSource, N as IApproval, bs as ToolOutcome, R as IRunnerSession, O as IConfig, bt as UsageLimitSignal, be as TaskMessageFrom, aN as RunnerModelRef, aE as RunnerCommand, aK as RunnerMcpServer, b6 as StructuredTurnEnd, k as ApprovalDecision, aZ as ScriptRun, a_ as ScriptStatus, b5 as StructuredEvent, j as ApprovalAnswer, ar as QueuedTaskMessage, l as ApprovalKind, Q as IRunner, aO as RunnerRegistry, bn as TaskShellSite, bm as TaskShell, az as RuleWord, af as PlannerAllowlist, aW as ScriptBody, d as AgentProcessDeps, a as AgentAdapter, a9 as OrdewellMcpServer, P as IFileSystem, am as PlannerToolHandler, aJ as RunnerManifest, bk as TaskRunnerConfig, f as AiProvider, ag as PlannerBackendId, a8 as OrchestratorOption } from './IRunner-DjJkYwko.js';
3
- import { R as RunnerModeInfo } from './ModeResolver-CHsaoV7M.js';
1
+ import { b3 as RunnerId, bp as Task, p as DiscoveredModel, bl as StoredHandoff, bA as TaskSnapshot, b as AwaitingReason, bG as UsageRecord, b4 as RunnerSessionCost, J as HandoffCommit, bI as Verdict, aj as LegacyPlanState, bx as TaskReportTo, bz as TaskSkillSnapshot, by as TaskSkillNotice, bm as SubagentOutcome, c as BelowPlan, C as CheckWord, am as Mandate, aq as MandateRequest, ap as MandateRefusal, ad as IsolationTaskRef, a$ as RevertCommand, aa as IsolationRevertTask, h as ConversationMessage, bh as SkillLoadNotice, aw as PendingPlanTurn, K as HandoffDigest, a as AttemptUsage, bv as TaskLoop, bt as TaskIsolation, o as DelegatedRow, b2 as RunSettings, X as IsolationHandoff, aA as PlanUsage, a2 as IsolationMergeResult, aR as ReplyRun, ba as SessionBilling, ar as ModelUsage, bH as UsageTotals, g as ContextFill, aC as PlannerCompaction, s as ExecutionRecord, aY as ResearchStep, ay as PlanState, ab as IsolationRun, T as IWorktreeIsolation, aQ as RepairWait, ag as IsolationView, a9 as IsolationRevertResult, bB as TaskStatus, q as ExecutionActor, w as GuardTrip, at as NodeAuthority, v as GuardTarget, b7 as ScriptBody, r as ExecutionCommandName, aX as ResearchProgress, aW as ResearchLogEntry, bf as SkillInfo, bg as SkillLoad, bk as SkillsService, aE as PlannerContextPause, a0 as IsolationMergeBlock, _ as IsolationLandedTask, a4 as IsolationPreview, a_ as ResearchToolType, bo as SupervisorLevel, ak as LoopSubject, aZ as ResearchStepOutcome, aD as PlannerCompactionOutcome, aG as PlannerUsage, t as FindingSeverity, bC as TaskType, bn as SubtreeReport, av as PatchView, bu as TaskIsolationState, bD as TaskUsageSummary } from './Task-CxGgN6I0.js';
2
+ import { q as ApprovalRequest, r as ApprovalSource, U as IApproval, bO as ToolOutcome, _ as IRunnerSession, a1 as IsolationRunController, V as IConfig, bR as UsageLimitSignal, bA as TaskMessageFrom, b4 as RunnerModelRef, aX as RunnerCommand, b1 as RunnerMcpServer, bs as StructuredTurnEnd, k as ApprovalDecision, bg as ScriptRun, bh as ScriptStatus, br as StructuredEvent, aj as NodePath, u as BoardDraft, v as BoardEntry, w as BoardPost, t as BoardAuthor, af as McpToolReply, J as EventStore, j as ApprovalAnswer, aK as QueuedTaskMessage, l as ApprovalKind, Y as INotification, Z as IRunner, b6 as RunnerRegistry, bo as SkillLookup, bJ as TaskShellSite, bI as TaskShell, K as ExecutionBackend, aR as ReservedLease, d as AgentProcessDeps, a as AgentAdapter, ar as OrdewellMcpServer, ap as OpenLease, W as IFileSystem, ay as PlannerModes, a0 as IsolatedExecution, aE as PlannerToolHandler, bG as TaskRunnerConfig, b0 as RunnerManifest, f as AiProvider, ax as PlannerBackendId, aq as OrchestratorOption, bl as SessionMeta } from './eventStore-CTCSKS9E.js';
3
+ import { R as RunnerModeInfo } from './ModeResolver-B64aUZXJ.js';
4
4
 
5
- type NotificationAction = {
5
+ /** What a planned task may be given: the runners it may run on and their modes. */
6
+ interface IntakeCatalog {
7
+ runners: readonly RunnerId[];
8
+ /** Absent when no runner's modes are known: the mode is then guessed (ADR-0031). */
9
+ runnerModes?: Partial<Record<RunnerId, readonly RunnerModeInfo[]>>;
10
+ autonomousDefault?: boolean;
11
+ /**
12
+ * An edit's assignments are resolved against the catalog as it is planned,
13
+ * and a model it names that the runner does not offer is refused; a
14
+ * submitted plan's models are resolved as it lands instead (ADR-0003).
15
+ */
16
+ models?: AssignmentCatalog;
17
+ /**
18
+ * Why this planner may not write a delegated task or a read-only script
19
+ * (ADR-0055 D1, D8), or null when it may. Absent, there is no node to read:
20
+ * a one-shot planner, or a route that holds no mandate.
21
+ */
22
+ delegationRefusal?: string | null;
23
+ /** Which of a child's script tasks run sealed (ADR-0056 M9): each such lands read-only, on every route. */
24
+ sealsScripts?: (task: Pick<Task, 'commands' | 'script' | 'timeoutMs'>) => boolean;
25
+ }
26
+
27
+ /**
28
+ * Who writes a task's assignment. It decides what a model the writer names
29
+ * meets where the task's runner does not offer it or the allowlist excludes it
30
+ * (ADR-0003), which runners it may put a task on, and whether a live runner's
31
+ * hold applies (ADR-0041):
32
+ *
33
+ * - `plan`: a whole plan the planner submitted, and every planner task as it
34
+ * lands. A stray model is replaced, and the planner is told what moved.
35
+ * - `edit`: the planner's edit. A stray model is refused, which the planner
36
+ * answers in the same reply.
37
+ * - `user`: a user's direct edit. A stray model is refused, with the reason.
38
+ * - `userAdd`: a task the user adds. A stray model is replaced, and the user
39
+ * is told what moved.
40
+ * - `runner`: a switch made with the runner's own `/model`, written as the
41
+ * user's edit. The runner already runs it, so neither the allowlist nor the
42
+ * live runner's hold applies.
43
+ */
44
+ type AssignmentWriter = 'plan' | 'edit' | 'user' | 'userAdd' | 'runner';
45
+ /**
46
+ * What an assignment is resolved against: the discovered models, the manifest
47
+ * modes and the allowlist, as the session holds them when the write lands —
48
+ * the same catalog the planner's per-turn block shows, so nothing is refused
49
+ * that the planner was never told about.
50
+ */
51
+ interface AssignmentCatalog {
52
+ /** A runner absent, or listed with nothing, has models discovery could not find, not none. */
53
+ modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>;
54
+ /** Absent when no runner's modes are known: no mode is then derived. */
55
+ runnerModes?: Partial<Record<RunnerId, readonly RunnerModeInfo[]>>;
56
+ /** Raw (unfiltered) allowlist, keyed by runner; read through {@link effectiveAllowlist}. */
57
+ perRunnerAllowlist?: Partial<Record<RunnerId, string[]>>;
58
+ /** Why an edit may not write a delegated task, or null when it may; absent, no node is behind it (`IntakeCatalog`). */
59
+ delegationRefusal?: string | null;
60
+ /** Which script tasks an edit adds land sealed (`IntakeCatalog`). */
61
+ sealsScripts?: IntakeCatalog['sealsScripts'];
62
+ /** Full (true) or Guarded: the mode a derived task lands on unless it is read-only (ADR-0001). Full when absent. */
63
+ autonomousDefault?: boolean;
64
+ /**
65
+ * Whether a task's runner is live: an attempt of it is claimed, running,
66
+ * quiet or waiting on someone. Its model, effort and runner are then that
67
+ * runner's (ADR-0041): the process was started on them, and only the
68
+ * runner's own command moves them. Absent means none is.
69
+ */
70
+ liveRunner?: (taskId: string) => boolean;
71
+ }
72
+ /** What a runner offers a task, as discovery and its manifest have it. */
73
+ interface RunnerCatalog {
74
+ models: DiscoveredModel[];
75
+ modes: RunnerModeInfo[];
76
+ }
77
+ /** A change the resolver made to what was written, named so it is never silent (ADR-0001). */
78
+ interface AssignmentAdjustment {
79
+ taskId: string;
80
+ title: string;
81
+ field: 'assignedModel' | 'thinkingEffort';
82
+ from: string | null;
83
+ to: string | null;
84
+ }
85
+
86
+ /** Plan placement and dependency rules, shared with browser-bundled surfaces. */
87
+ interface TreeTask<T> {
88
+ id: string;
89
+ order: number;
90
+ title: string;
91
+ subtasks?: readonly T[];
92
+ }
93
+ interface DependencyTask<T> extends TreeTask<T> {
94
+ dependencies: readonly string[];
95
+ }
96
+ interface TaskPlacement<T> {
97
+ task: T;
98
+ parent?: T;
99
+ ancestors: readonly T[];
100
+ siblings: readonly T[];
6
101
  label: string;
7
- value: string;
102
+ }
103
+ declare function walkPlan<T extends TreeTask<T>>(tasks: readonly T[]): Generator<TaskPlacement<T>>;
104
+ declare function taskPlacement<T extends TreeTask<T>>(tasks: readonly T[], id: string): TaskPlacement<T> | undefined;
105
+ /** Includes the root: deletion has to detach every edge into the removed tree. */
106
+ declare function subtreeIds<T extends TreeTask<T>>(tasks: readonly T[], id: string): Set<string>;
107
+ /** Pickers offer earlier siblings; API validity is independent of display order. */
108
+ declare function dependencyCandidates<T extends TreeTask<T>>(tasks: readonly T[], taskId?: string): T[];
109
+ /** The surviving tasks whose dependency lists a subtree removal will change. */
110
+ declare function dependentsOf<T extends DependencyTask<T>>(tasks: readonly T[], taskId: string): T[];
111
+ interface DependencyCheck {
112
+ ok: boolean;
113
+ error?: string;
114
+ }
115
+ declare function canSetDependencies<T extends DependencyTask<T>>(tasks: readonly T[], taskId: string, dependencies: readonly string[]): DependencyCheck;
116
+
117
+ /**
118
+ * Targeted task edits emitted by the planner conversation (the 'task_ops'
119
+ * ConversationTurn). Task references accept a task id, a "#<order>" ref, or a
120
+ * bare order number — cheap models rarely echo UUIDs correctly.
121
+ */
122
+ /**
123
+ * A task as the planner writes it: its fields, less the mode, plus whether it
124
+ * must not change files (ADR-0031), and `thinkingEffort` as a name for its
125
+ * model's effort, so an effort can change without restating the model.
126
+ */
127
+ type PlannerTaskSpec = Partial<Task> & {
128
+ readOnly?: boolean;
129
+ thinkingEffort?: string | null;
130
+ };
131
+ type TaskOp = {
132
+ op: 'update';
133
+ taskId: string;
134
+ changes: PlannerTaskSpec;
135
+ } | {
136
+ op: 'add';
137
+ task: PlannerTaskSpec;
138
+ handle?: string;
139
+ } | {
140
+ op: 'remove';
141
+ taskId: string;
142
+ } | {
143
+ op: 'reorder';
144
+ taskIds: string[];
145
+ } | {
146
+ op: 'merge';
147
+ taskIds: string[];
148
+ merged: PlannerTaskSpec;
149
+ handle?: string;
150
+ } | {
151
+ op: 'split';
152
+ taskId: string;
153
+ parts: PlannerTaskSpec[];
154
+ handle?: string;
155
+ } | {
156
+ op: 'rearm';
157
+ taskId: string;
158
+ changes?: PlannerTaskSpec;
8
159
  };
9
- interface INotification {
10
- info(msg: string): void;
11
- warn(msg: string): void;
12
- error(msg: string): void;
13
- confirm(msg: string, options: string[]): Promise<string | undefined>;
160
+ declare function textHasTaskOps(text: string): boolean;
161
+ /** Parse a `{"taskOps":[...]}` reply. Throws PlanParseError when the JSON is unusable. */
162
+ declare function parseTaskOpsJson(text: string): TaskOp[];
163
+ /** Which op of a batch a refusal is about: its number among the ops the call wrote, from 1. */
164
+ interface OpRef {
165
+ number: number;
166
+ kind: string;
167
+ }
168
+ /** One reason a batch was refused, and the op it is about; absent for the batch as a whole, its order say. */
169
+ interface OpRefusal {
170
+ op?: OpRef;
171
+ message: string;
172
+ }
173
+ /** A refusal as the planner reads it, its op named first. */
174
+ declare function opRefusalText({ op, message }: OpRefusal): string;
175
+ interface ApplyTaskOpsResult {
176
+ ok: boolean;
177
+ tasks: readonly Task[];
178
+ /** {@link refusals} as the planner reads them. */
179
+ errors: string[];
180
+ refusals: OpRefusal[];
181
+ /** Human-readable summary of what changed, for the chat transcript. */
182
+ summary: string[];
183
+ /** On ok, what the batch's tasks were adjusted to beyond what the planner wrote, as they stand at its end. */
184
+ adjustments?: AssignmentAdjustment[];
185
+ /** On ok, the id of the task each batch handle names. */
186
+ handles?: ReadonlyMap<string, string>;
187
+ }
188
+ /**
189
+ * Pre-flight check for a merge: would collapsing the selected tasks into one
190
+ * (rewiring every dependent of any selected task to the survivor) keep the
191
+ * dependency graph valid? Rejects locked tasks and merges whose rewiring would
192
+ * introduce a cycle. Display order is NOT checked here — neither
193
+ * consecutiveness (a UI concern) nor a dependency the merge pulls out of order,
194
+ * which {@link applyTaskOps} repairs; refusing on order would make this
195
+ * pre-flight stricter than the applier it stands in for.
196
+ */
197
+ declare function canMergeTasks(tasks: readonly Task[], selectedIds: string[]): {
198
+ ok: boolean;
199
+ error?: string;
200
+ };
201
+ /**
202
+ * The shape the dependency helpers below read. Structural rather than `Task`,
203
+ * because the TUI projects tasks into its own `TaskView`: a surface's
204
+ * dependency picker and the API's validation must agree on which dependencies
205
+ * are legal, and a signature only core can satisfy would have forced the TUI
206
+ * to keep a second copy of the rule.
207
+ */
208
+ interface TaskRef {
209
+ id: string;
210
+ order: number;
211
+ title: string;
212
+ dependencies: string[];
14
213
  }
15
214
 
215
+ /** Pre-flight check for a split: the task exists and is not locked by execution. */
216
+ declare function canSplitTask(tasks: readonly Task[], taskId: string): {
217
+ ok: boolean;
218
+ error?: string;
219
+ };
220
+ /**
221
+ * Apply task ops to a snapshot of the plan, atomically: either every op
222
+ * applies and the result validates (deps resolve, no cycles, running and
223
+ * completed tasks untouched), or nothing is returned and `errors` explains
224
+ * why. The caller commits `tasks` on ok.
225
+ *
226
+ * `earlierHandles` are the handles an earlier batch of the same planner turn
227
+ * gave, each with the id of the task it made (ADR-0022 P2): this batch may
228
+ * name those tasks by them, and may not give one of them again.
229
+ */
230
+ declare function applyTaskOps(currentTasks: readonly Task[], ops: TaskOp[], runners: RunnerId[], catalog?: AssignmentCatalog, earlierHandles?: ReadonlyMap<string, string>): ApplyTaskOpsResult;
231
+
16
232
  /**
17
233
  * The SSRF guard for anything that leaves the machine on the planner's behalf.
18
234
  * An unguarded `http://169.254.169.254/` would hand a prompt-injected planner
@@ -289,692 +505,96 @@ declare class PlanStore {
289
505
  * `preserveCompleted: false` is for a freshly committed plan, where
290
506
  * everything starts over. `edited` is an edit adopted between runs, which is
291
507
  * not the user starting over: a failed task stays failed, and a task waiting
292
- * on the user, whatever for, stays waiting. `keepFailed` is what failed in
293
- * the reply autorun's run follows: nobody asked for those to run again.
294
- */
295
- resetForRun(opts?: {
296
- preserveCompleted?: boolean;
297
- edited?: boolean;
298
- keepFailed?: ReadonlySet<string>;
299
- }): void;
300
- /**
301
- * Execute's fresh chance with work already live (ADR-0053), which
302
- * `resetForRun` would take back: only what failed and is not at work is
303
- * readied, and a check between rounds still leaves its body to run first.
304
- */
305
- readyFailed(live: (taskId: string) => boolean): void;
306
- /** See {@link releaseMadeManual}; `live` names what a runner still holds. */
307
- releaseMadeManual(live?: (taskId: string) => boolean): void;
308
- private ready;
309
- markCompleted(id: string): void;
310
- markFailed(id: string): void;
311
- markSkipped(id: string, reason?: string): void;
312
- markInProgress(id: string): void;
313
- /** Its first attempt is starting: from now on its chat exists, whatever becomes of the attempt. */
314
- markAttempted(id: string): void;
315
- /** Its attempt starts: the number now tells its waits from the last attempt's, so they count from nothing (ADR-0053). */
316
- markAttemptStarted(id: string): void;
317
- markAwaitingUser(id: string, reason?: AwaitingReason): void;
318
- /** When a task paused on a usage limit resumes on its own; undefined leaves it to a manual retry. */
319
- setTaskResumeAt(id: string, resumeAt: string | undefined): void;
320
- markPending(id: string): void;
321
- /**
322
- * A new attempt of the task, from nothing it earned. A loop's check starts
323
- * its rounds over (ADR-0045 L7), unless `sameRound`: a resume after a usage
324
- * limit goes on with the attempt the limit cut short.
325
- */
326
- retry(id: string, opts?: {
327
- sameRound?: boolean;
328
- }): void;
329
- /** Its landed work was reverted (ADR-0054): as a retry leaves it, marked so until it starts. */
330
- markReverted(id: string): void;
331
- /** A loop's check counts its rounds afresh from its next run (ADR-0045 L7). */
332
- startRoundsOver(id: string): void;
333
- /**
334
- * The loop runs its check again, as its next round (ADR-0045 L5): a new
335
- * attempt, the round counted on, and nothing of the round before kept on
336
- * the task — its report went to the body, and stays in its attempt's log.
337
- */
338
- repeat(id: string): void;
339
- /** Kept on the task: it was force-started past its merge gate, before these ancestors' work was merged (ADR-0020). */
340
- setForcedPastGate(id: string, dependencies: string[]): void;
341
- /** One of the task's waits is over, so what it waits on next is another (ADR-0053). */
342
- endWait(id: string): void;
343
- /** Work under way is the one status nobody could be waiting on, so moving off any other ends a wait. */
344
- private leaveStatus;
345
- /** A reason outlives nothing: any status but `awaiting_user` drops it, and the resume time with it; any status drops a skip's reason. */
346
- private setStatus;
347
- setTaskVerdict(id: string, verdict: Task['verdict']): void;
348
- /**
349
- * How the task's latest attempt ended, and the handoff it left (ADR-0047),
350
- * kept as its digest. An attempt that left no handoff clears the one before
351
- * it: the plan holds the latest attempt's, as it does the summary.
352
- */
353
- setTaskOutputSummary(id: string, summary: Task['outputSummary'], handoff?: StoredHandoff): void;
354
- /** The owner is saved with the session it belongs to, and dropped with it. */
355
- setTaskRunnerSessionId(id: string, runnerSessionId: string | undefined, owner?: RunnerId): void;
356
- setCarriedFrom(id: string, runner: RunnerId): void;
357
- clearCarriedFrom(id: string): void;
358
- startAttemptUsage(id: string, attempt: number, runner: string, startedAt: string): void;
359
- recordAttemptUsage(id: string, attempt: number, record: UsageRecord, now: string): void;
360
- endAttemptUsage(id: string, attempt: number, now: string, runnerSessionCost?: RunnerSessionCost, sessionId?: string): void;
361
- setTaskAttemptSkills(id: string, skills: Task['attemptSkills']): void;
362
- /**
363
- * Widen the plan's runner set. Retargeting a task onto a runner the plan has
364
- * not used before has to land here as well as on the plan state, or
365
- * {@link resolveTaskRunner} reads the new runner as foreign and spawns the
366
- * plan's first one instead.
367
- */
368
- admitRunner(runner: RunnerId): void;
369
- resolveTaskRunner(task: Readonly<Task>): RunnerId;
370
- private rebuild;
371
- }
372
-
373
- /**
374
- * Who writes a task's assignment. It decides what a model the writer names
375
- * meets where the task's runner does not offer it or the allowlist excludes it
376
- * (ADR-0003), which runners it may put a task on, and whether a live runner's
377
- * hold applies (ADR-0041):
378
- *
379
- * - `plan`: a whole plan the planner submitted, and every planner task as it
380
- * lands. A stray model is replaced, and the planner is told what moved.
381
- * - `edit`: the planner's edit. A stray model is refused, which the planner
382
- * answers in the same reply.
383
- * - `user`: a user's direct edit. A stray model is refused, with the reason.
384
- * - `userAdd`: a task the user adds. A stray model is replaced, and the user
385
- * is told what moved.
386
- * - `runner`: a switch made with the runner's own `/model`, written as the
387
- * user's edit. The runner already runs it, so neither the allowlist nor the
388
- * live runner's hold applies.
389
- */
390
- type AssignmentWriter = 'plan' | 'edit' | 'user' | 'userAdd' | 'runner';
391
- /**
392
- * What an assignment is resolved against: the discovered models, the manifest
393
- * modes and the allowlist, as the session holds them when the write lands —
394
- * the same catalog the planner's per-turn block shows, so nothing is refused
395
- * that the planner was never told about.
396
- */
397
- interface AssignmentCatalog {
398
- /** A runner absent, or listed with nothing, has models discovery could not find, not none. */
399
- modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>;
400
- /** Absent when no runner's modes are known: no mode is then derived. */
401
- runnerModes?: Partial<Record<RunnerId, readonly RunnerModeInfo[]>>;
402
- /** Raw (unfiltered) allowlist, keyed by runner; read through {@link effectiveAllowlist}. */
403
- perRunnerAllowlist?: Partial<Record<RunnerId, string[]>>;
404
- /** Full (true) or Guarded: the mode a derived task lands on unless it is read-only (ADR-0001). Full when absent. */
405
- autonomousDefault?: boolean;
406
- /**
407
- * Whether a task's runner is live: an attempt of it is claimed, running,
408
- * quiet or waiting on someone. Its model, effort and runner are then that
409
- * runner's (ADR-0041): the process was started on them, and only the
410
- * runner's own command moves them. Absent means none is.
411
- */
412
- liveRunner?: (taskId: string) => boolean;
413
- }
414
- /** What a runner offers a task, as discovery and its manifest have it. */
415
- interface RunnerCatalog {
416
- models: DiscoveredModel[];
417
- modes: RunnerModeInfo[];
418
- }
419
- /** A change the resolver made to what was written, named so it is never silent (ADR-0001). */
420
- interface AssignmentAdjustment {
421
- taskId: string;
422
- title: string;
423
- field: 'assignedModel' | 'thinkingEffort';
424
- from: string | null;
425
- to: string | null;
426
- }
427
-
428
- /** Plan placement and dependency rules, shared with browser-bundled surfaces. */
429
- interface TreeTask<T> {
430
- id: string;
431
- order: number;
432
- title: string;
433
- subtasks?: readonly T[];
434
- }
435
- interface DependencyTask<T> extends TreeTask<T> {
436
- dependencies: readonly string[];
437
- }
438
- interface TaskPlacement<T> {
439
- task: T;
440
- parent?: T;
441
- ancestors: readonly T[];
442
- siblings: readonly T[];
443
- label: string;
444
- }
445
- declare function walkPlan<T extends TreeTask<T>>(tasks: readonly T[]): Generator<TaskPlacement<T>>;
446
- declare function taskPlacement<T extends TreeTask<T>>(tasks: readonly T[], id: string): TaskPlacement<T> | undefined;
447
- /** Includes the root: deletion has to detach every edge into the removed tree. */
448
- declare function subtreeIds<T extends TreeTask<T>>(tasks: readonly T[], id: string): Set<string>;
449
- /** Pickers offer earlier siblings; API validity is independent of display order. */
450
- declare function dependencyCandidates<T extends TreeTask<T>>(tasks: readonly T[], taskId?: string): T[];
451
- /** The surviving tasks whose dependency lists a subtree removal will change. */
452
- declare function dependentsOf<T extends DependencyTask<T>>(tasks: readonly T[], taskId: string): T[];
453
- interface DependencyCheck {
454
- ok: boolean;
455
- error?: string;
456
- }
457
- declare function canSetDependencies<T extends DependencyTask<T>>(tasks: readonly T[], taskId: string, dependencies: readonly string[]): DependencyCheck;
458
-
459
- /**
460
- * Targeted task edits emitted by the planner conversation (the 'task_ops'
461
- * ConversationTurn). Task references accept a task id, a "#<order>" ref, or a
462
- * bare order number — cheap models rarely echo UUIDs correctly.
463
- */
464
- /**
465
- * A task as the planner writes it: its fields, less the mode, plus whether it
466
- * must not change files (ADR-0031), and `thinkingEffort` as a name for its
467
- * model's effort, so an effort can change without restating the model.
468
- */
469
- type PlannerTaskSpec = Partial<Task> & {
470
- readOnly?: boolean;
471
- thinkingEffort?: string | null;
472
- };
473
- type TaskOp = {
474
- op: 'update';
475
- taskId: string;
476
- changes: PlannerTaskSpec;
477
- } | {
478
- op: 'add';
479
- task: PlannerTaskSpec;
480
- handle?: string;
481
- } | {
482
- op: 'remove';
483
- taskId: string;
484
- } | {
485
- op: 'reorder';
486
- taskIds: string[];
487
- } | {
488
- op: 'merge';
489
- taskIds: string[];
490
- merged: PlannerTaskSpec;
491
- handle?: string;
492
- } | {
493
- op: 'split';
494
- taskId: string;
495
- parts: PlannerTaskSpec[];
496
- handle?: string;
497
- } | {
498
- op: 'rearm';
499
- taskId: string;
500
- changes?: PlannerTaskSpec;
501
- };
502
- declare function textHasTaskOps(text: string): boolean;
503
- /** Parse a `{"taskOps":[...]}` reply. Throws PlanParseError when the JSON is unusable. */
504
- declare function parseTaskOpsJson(text: string): TaskOp[];
505
- /** Which op of a batch a refusal is about: its number among the ops the call wrote, from 1. */
506
- interface OpRef {
507
- number: number;
508
- kind: string;
509
- }
510
- /** One reason a batch was refused, and the op it is about; absent for the batch as a whole, its order say. */
511
- interface OpRefusal {
512
- op?: OpRef;
513
- message: string;
514
- }
515
- /** A refusal as the planner reads it, its op named first. */
516
- declare function opRefusalText({ op, message }: OpRefusal): string;
517
- interface ApplyTaskOpsResult {
518
- ok: boolean;
519
- tasks: readonly Task[];
520
- /** {@link refusals} as the planner reads them. */
521
- errors: string[];
522
- refusals: OpRefusal[];
523
- /** Human-readable summary of what changed, for the chat transcript. */
524
- summary: string[];
525
- /** On ok, what the batch's tasks were adjusted to beyond what the planner wrote, as they stand at its end. */
526
- adjustments?: AssignmentAdjustment[];
527
- /** On ok, the id of the task each batch handle names. */
528
- handles?: ReadonlyMap<string, string>;
529
- }
530
- /**
531
- * Pre-flight check for a merge: would collapsing the selected tasks into one
532
- * (rewiring every dependent of any selected task to the survivor) keep the
533
- * dependency graph valid? Rejects locked tasks and merges whose rewiring would
534
- * introduce a cycle. Display order is NOT checked here — neither
535
- * consecutiveness (a UI concern) nor a dependency the merge pulls out of order,
536
- * which {@link applyTaskOps} repairs; refusing on order would make this
537
- * pre-flight stricter than the applier it stands in for.
538
- */
539
- declare function canMergeTasks(tasks: readonly Task[], selectedIds: string[]): {
540
- ok: boolean;
541
- error?: string;
542
- };
543
- /**
544
- * The shape the dependency helpers below read. Structural rather than `Task`,
545
- * because the TUI projects tasks into its own `TaskView`: a surface's
546
- * dependency picker and the API's validation must agree on which dependencies
547
- * are legal, and a signature only core can satisfy would have forced the TUI
548
- * to keep a second copy of the rule.
549
- */
550
- interface TaskRef {
551
- id: string;
552
- order: number;
553
- title: string;
554
- dependencies: string[];
555
- }
556
-
557
- /** Pre-flight check for a split: the task exists and is not locked by execution. */
558
- declare function canSplitTask(tasks: readonly Task[], taskId: string): {
559
- ok: boolean;
560
- error?: string;
561
- };
562
- /**
563
- * Apply task ops to a snapshot of the plan, atomically: either every op
564
- * applies and the result validates (deps resolve, no cycles, running and
565
- * completed tasks untouched), or nothing is returned and `errors` explains
566
- * why. The caller commits `tasks` on ok.
567
- *
568
- * `earlierHandles` are the handles an earlier batch of the same planner turn
569
- * gave, each with the id of the task it made (ADR-0022 P2): this batch may
570
- * name those tasks by them, and may not give one of them again.
571
- */
572
- declare function applyTaskOps(currentTasks: readonly Task[], ops: TaskOp[], runners: RunnerId[], catalog?: AssignmentCatalog, earlierHandles?: ReadonlyMap<string, string>): ApplyTaskOpsResult;
573
-
574
- /**
575
- * Task skills end to end (ADR-0024): a skill reaches a runner only by being
576
- * attached to its task in the plan, and Ordewell puts its body in the task's
577
- * prompt — so it works the same on every runner, and nothing is written to a
578
- * harness's own skills directory.
579
- */
580
- /** Where a task's skill names are looked up: global first, then the workspace folders it reads. */
581
- interface SkillLookup extends Pick<SkillsService, 'findSkill' | 'searchedDirs'> {
582
- /**
583
- * Set when tasks get worktrees: of `skills`, the workspace ones git will not
584
- * carry into them, each SKILL.md path mapped to the folder to commit,
585
- * relative to the workspace root.
508
+ * on the user, whatever for, stays waiting. `keepFailed` is what failed in
509
+ * the reply autorun's run follows: nobody asked for those to run again.
586
510
  */
587
- uncommitted?(skills: readonly SkillInfo[]): Promise<Map<string, string>>;
588
- }
589
- /**
590
- * Where a workspace's skills are read, known without asking git: the repos
591
- * of its group in layout order (`['.']` for one repository), and whether
592
- * tasks will get worktrees, which only a committed skill reaches.
593
- */
594
- interface SkillLayout {
595
- repos: readonly string[];
596
- worktrees: boolean;
597
- }
598
-
599
- /**
600
- * Where the run's tasks will work: `false` in the shared workspace root;
601
- * otherwise each in its own worktree of every repo of the group (ADR-0013,
602
- * ADR-0014), which is what decides if tasks on the same file must be ordered.
603
- */
604
- type IsolatedExecution = false | RepoGroupLayout;
605
- interface PlannerModes {
606
- autonomousDefault: boolean;
607
- /** Not a toggle: a fact about the run. */
608
- isolatedExecution: IsolatedExecution;
511
+ resetForRun(opts?: {
512
+ preserveCompleted?: boolean;
513
+ edited?: boolean;
514
+ keepFailed?: ReadonlySet<string>;
515
+ }): void;
609
516
  /**
610
- * Where the run's tasks will look their skills up (ADR-0024), also a fact
611
- * about the run. A plan attaching a planner skill is refused against it,
612
- * and a coding agent's submit_plan call is refused in its turn rather than
613
- * accepted and then failed. Absent: the plan is not checked.
517
+ * Execute's fresh chance with work already live (ADR-0053), which
518
+ * `resetForRun` would take back: only what failed and is not at work is
519
+ * readied, and a check between rounds still leaves its body to run first.
614
520
  */
615
- taskSkills?: SkillLookup;
616
- }
617
-
618
- type IsolationNoticeLevel = 'info' | 'warn' | 'error';
619
- /**
620
- * How an attempt that may run in a worktree gets one: fresh, the kept one for
621
- * a repair, restoring the previous attempt's work, or fresh and thrown away
622
- * whatever it changes, for a read-only task (ADR-0046).
623
- */
624
- type AttemptIntent = 'fresh' | 'repair' | 'readOnly' | PrepareIntent;
625
- /**
626
- * What adopting a persisted run found: nothing to change, landed work git
627
- * shows the user has merged since, or a settled run that had nothing left to
628
- * hand over and was cleared up.
629
- */
630
- type AdoptOutcome = 'unchanged' | 'changed' | 'cleared';
631
- interface AttemptWorkspace {
632
- cwd: string;
633
- worktree: boolean;
634
- /** The task workspace holding the attempt's worktrees, which `cwd` is in; set with `worktree` when it was prepared. */
635
- workspace?: string;
636
- /** Set for a `restore` in a worktree; absent in the checkout, where the previous attempt's edits remain. */
637
- previousWork?: PreviousWork;
638
- }
639
- /**
640
- * What the controller reports. It never schedules or emits on its own: the
641
- * orchestrator turns these into observer events and decides what runs next.
642
- */
643
- interface IsolationRunListener {
644
- /**
645
- * The run record changed and should be persisted with the plan. `detached`
646
- * is set instead when the record that changed is no longer the plan's own —
647
- * a release that outlived the plan load that replaced its run — and belongs
648
- * to the session that was left.
649
- */
650
- changed(detached?: IsolationRun): void;
651
- /** A run did not start: `repos` of the group have tracked changes. */
652
- blocked(repos: string[]): void;
653
- /** An isolated run closed and handed its integration branches over. */
654
- handoff(handoff: IsolationHandoff): void;
655
- /** The run was cleared up and forgotten: nothing was handed over, or it was discarded. */
656
- closed(): void;
657
- /** 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. */
658
- ended(): void;
659
- /** Already said through the notifications; also for a surface that shows no toasts. */
660
- notice(level: IsolationNoticeLevel, message: string): void;
661
- /** A clean-up took the branches these resolver tasks would merge, and their links with them. */
662
- resolversEnded(resolverIds: readonly string[]): void;
663
- }
664
- interface IsolationRunControllerDeps {
665
- isolation: IWorktreeIsolation;
666
- config: IConfig;
667
- notifications: INotification;
668
- workspaceRoot: () => string;
669
- listener: IsolationRunListener;
521
+ readyFailed(live: (taskId: string) => boolean): void;
522
+ /** See {@link releaseMadeManual}; `live` names what a runner still holds. */
523
+ releaseMadeManual(live?: (taskId: string) => boolean): void;
524
+ private ready;
525
+ /** A delegated task's checks; its scheduler, which knows what makes a task read-only, says which those are. */
526
+ checksOf: (task: Readonly<Task>) => readonly Task[];
527
+ markCompleted(id: string): void;
528
+ markFailed(id: string): void;
529
+ markSkipped(id: string, reason?: string): void;
530
+ markInProgress(id: string): void;
531
+ /** Its first attempt is starting: from now on its chat exists, whatever becomes of the attempt. */
532
+ markAttempted(id: string): void;
533
+ /** Its attempt starts: the number now tells its waits from the last attempt's, so they count from nothing (ADR-0053). */
534
+ markAttemptStarted(id: string): void;
535
+ /** Saved beside the wait it explains: see {@link Task.parkedQuestion}. */
536
+ parkQuestion(id: string, question: string): void;
537
+ markAwaitingUser(id: string, reason?: AwaitingReason): void;
538
+ /** When a task paused on a usage limit resumes on its own; undefined leaves it to a manual retry. */
539
+ setTaskResumeAt(id: string, resumeAt: string | undefined): void;
540
+ markPending(id: string): void;
670
541
  /**
671
- * The tasks no clean-up of the whole run may take a worktree from: an
672
- * attempt is running them, or the plan has them in progress or waiting on
673
- * the user mid-attempt.
542
+ * A new attempt of the task, from nothing it earned. A loop's check starts
543
+ * its rounds over (ADR-0045 L7), unless `sameRound`: a resume after a usage
544
+ * limit goes on with the attempt the limit cut short.
674
545
  */
675
- liveTasks: () => ReadonlySet<string>;
676
- /** 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). */
677
- landedOn: (taskId: string) => readonly string[];
678
- }
679
- /**
680
- * The lifecycle of a plan's isolation run (ADR-0013, ADR-0014, ADR-0020),
681
- * between the scheduler and the git layer: deciding, at a run's first change
682
- * task, whether it executes in worktrees, a blocked run and how it goes on,
683
- * each attempt's working directory, releasing worktrees, which landed work the
684
- * user has merged, the tree check of an ops task, the handoff when the run
685
- * closes, and what the user does with it afterwards. The record it keeps
686
- * outlives one run — a resumed plan continues it.
687
- */
688
- declare class IsolationRunController {
689
- private readonly isolation;
690
- private readonly config;
691
- private readonly notifications;
692
- private readonly workspaceRoot;
693
- private readonly listener;
694
- private readonly liveTasks;
695
- private readonly landedOn;
696
- private run;
697
- /** See {@link mergedRun}. */
698
- private merged;
699
- /** An adoption stays scoped even while cleanup temporarily leaves no run record. */
700
- private adoptions;
701
- /** Copied paths already reported for the current run: every task gets the same copies. */
702
- private reportedCopies;
703
- private reportedSetupFailures;
704
- /**
705
- * How the open run executes; null while no run is open, `undecided` until
706
- * its first change task asks. A run is one Execute-Plan or one manual task
707
- * run, from its start until it settles or is stopped.
708
- */
709
- private mode;
710
- /** Resolver task id → the conflicted task it resolves; see {@link linkResolver}. */
711
- private resolvers;
712
- private opening;
713
- private opened;
714
- /** Bumped by an interrupt, so an activation in flight across it does not open the run it ended. */
715
- private activations;
716
- /** The start a dirty tree turned away, handed back once the user chooses how to go on. */
717
- private blockedStart;
718
- /** How many starts it has turned away; a replay can be turned away again in the same run. */
719
- private turnedAway;
720
- /** The dirty repos behind {@link blockedStart}, for the stash notice. */
721
- private blockedRepos;
722
- constructor(deps: IsolationRunControllerDeps);
723
- /** The plan's run record, open or not; null when the plan has not isolated. */
724
- get current(): IsolationRun | null;
725
- /**
726
- * The id of the plan's last isolated run once it closed with everything it
727
- * landed merged into the user's branch and was cleared up: what tells a
728
- * Merge all asked for after it, which has nothing to merge, from one in a
729
- * plan that never isolated (ADR-0029). Null while the plan holds a run
730
- * record, and once a later run decides how it executes. Saved with the plan.
731
- */
732
- get mergedRun(): string | null;
733
- get isOpen(): boolean;
734
- /** How many runs have opened: what tells one run's launches from the next one's. */
735
- get epoch(): number;
736
- /** The open run has decided whether it isolates. */
737
- get decided(): boolean;
738
- /** The open run executes in worktrees. */
739
- get isolating(): boolean;
740
- /** A run is waiting on the user to stash or to go on without isolation. */
741
- get blocked(): boolean;
742
- /** How many starts a dirty tree has turned away: what tells one wait on it from the next (ADR-0053). */
743
- get blockedStarts(): number;
744
- /** What the plan persists of isolated execution; null when no run ever isolated. */
745
- get planIsolation(): PlanIsolation | null;
746
- /** The one guard of every action on the run: a plan that never isolated is a request refused, not a fault. */
747
- requireRun(): IsolationRun;
748
- /** A task's record in the open run, only while that run isolates. */
749
- openRecord(taskId: string): IsolationTaskRecord | undefined;
750
- /** Where a task's isolated work stands; null when the plan has no isolation run to speak of. */
751
- taskIsolation(taskId: string): TaskIsolation | null;
752
- view(): IsolationView | null;
753
- /**
754
- * Take over a plan's persisted isolation, or none for a plan that has not
755
- * isolated yet. Whatever a crashed process left behind for the run — a
756
- * worktree still marked active, a directory no record owns — is pruned,
757
- * while kept, failed and conflicted worktrees stay for the user. Then git is
758
- * asked which landed work the user has merged since the record was saved:
759
- * a settled run whose landed work is all in HEAD has nothing left to hand
760
- * over, so it is cleared up as Merge all would have, unless it still holds
761
- * work that never landed (ADR-0013).
762
- *
763
- * Deliberately not reported to the listener: adopting is not a change of the
764
- * session's own, and a host that adopts without persisting (VS Code's
765
- * restore) would otherwise write a new session file on every reload. What
766
- * git changed is returned instead, for the caller to save where it can and
767
- * announce. A plan loaded meanwhile supersedes this adoption, which then
768
- * touches nothing and resolves `unchanged`. `mergedRun` is the plan's saved
769
- * {@link mergedRun}.
770
- */
771
- adopt(state: PlanIsolation | null, mergedRun?: string): Promise<AdoptOutcome>;
772
- /** Open a run if none is, leaving whether it isolates to its first change task. */
773
- open(): void;
774
- /**
775
- * Decide once per run, as its first change task starts, whether it executes
776
- * in worktrees (ADR-0020): an ops task needs none, so a run of only ops
777
- * tasks never asks. `resume` is what a dirty tree parks until the user
778
- * chooses how to go on. Resolves false while the run waits on that choice.
779
- */
780
- decide(resume: () => Promise<void>): Promise<boolean>;
781
- /**
782
- * Whether the run in force, or else the next one, gives each task its own
783
- * worktrees, and of which repo group — what the planner is told, since it
784
- * decides whether tasks on the same file have to be ordered and which shared
785
- * paths two tasks must not edit at once. A tree that would block counts as
786
- * not isolating: the user may yet run without isolation, and ordering is
787
- * the safe rule then.
788
- */
789
- plannerLayout(): Promise<IsolatedExecution>;
790
- /**
791
- * Where the workspace's skills are read, and whether tasks get worktrees.
792
- * Read synchronously inside a planner turn or a chip edit, so it comes from
793
- * the files, not git or the planner's last layout: a restored session is
794
- * right from its first edit. The run's repos while it isolates; otherwise
795
- * the group the workspace holds, in worktrees unless isolation is off or
796
- * this run chose to share the root.
797
- */
798
- skillLayout(): SkillLayout;
799
- /**
800
- * Go on with the start a dirty tree turned away. `stash` puts the user's
801
- * tracked changes on the git stash first, so the run isolates; `shared` runs
802
- * this one run in the workspace root, knowingly; `reread` does nothing to the
803
- * tree, so the replay asks about it again (ADR-0053). Returns the parked start
804
- * for the caller to replay; null when nothing was parked.
805
- */
806
- continueBlocked(how: 'stash' | 'shared' | 'reread'): Promise<(() => Promise<void>) | null>;
807
- /**
808
- * The working directory of an attempt that may run in a worktree — which
809
- * attempts do is the attempt kind's to say (see `attemptKind.attemptCwd`):
810
- * the worktree prepared for it in an isolated run, the kept one for a
811
- * conflict repair, else the workspace root. `worktree` says which. Does not
812
- * itself report a worktree it creates as a change — the caller does once
813
- * the attempt is committed as running, so a spawn abandoned or failed after
814
- * this settles is not misreported as a change that stuck.
815
- */
816
- attemptCwd(task: Task, intent: AttemptIntent): Promise<AttemptWorkspace>;
817
- /** The workspace root, for an attempt that runs outside any worktree. */
818
- workspaceCwd(task: Task): Promise<{
819
- cwd: string;
820
- worktree: boolean;
821
- }>;
822
- private reopenCwd;
823
- private prepareCwd;
824
- /**
825
- * Land a task's work on the run's integration branches. The record is
826
- * reported changed once the landing is set and before the first merge, so
827
- * a crash mid-landing leaves the tips to roll back to, and again once it
828
- * settles. A git error is `failed`, never a throw.
829
- */
830
- integrate(task: Task, commit?: HandoffCommit): Promise<IsolationOutcome>;
831
- private keepLandedOn;
832
- /** What a conflict repair's work shows (ADR-0015); git that cannot tell counts against it. */
833
- verifyRepair(task: Task): Promise<RepairEvidence>;
834
- /**
835
- * Let go of a task's worktree. `keep` leaves it and its branch for
836
- * inspection; otherwise both go. A landing still in flight settles first, so
837
- * the worktree is never torn down underneath its merge. Bound to the run
838
- * the call was made in: a plan loaded meanwhile adopts its own run, whose
839
- * records share these task ids and are not this release's to touch.
840
- */
841
- release(taskId: string, opts: {
842
- keep: boolean;
843
- }, landing?: Promise<unknown> | null): Promise<void>;
844
- /**
845
- * Close the open run. An isolated one hands its integration branch over for
846
- * review — unless everything it landed is merged already, at merge gates or
847
- * by the run's automatic Merge all, which leaves nothing to hand over, so it
848
- * is cleared up as a Merge all would have.
849
- */
850
- close(): Promise<void>;
851
- /**
852
- * Whether a task's landed work still waits to be merged into the user's
853
- * branch — what holds an ops or user task at its merge gate (ADR-0020). A
854
- * task with no record never landed anything to wait for.
855
- */
856
- awaitsMerge(taskId: string): boolean;
857
- /**
858
- * Look again for landed work the user has merged — by Merge all, or by hand
859
- * with git — or has taken back out. Resolves true when a mark changed.
860
- */
861
- refreshInHead(): Promise<boolean>;
862
- /** Whether any landed work reads as merged: an answer git can take back, if the user takes the merge out. */
863
- get readsMerged(): boolean;
864
- /** Mark the run's landed work as git finds it now; true when a mark changed. */
865
- private findLanded;
866
- /** The run landed something and every landing is in the user's HEAD. */
867
- private allMerged;
868
- /**
869
- * The tracked state of the workspace as an attempt whose tree is checked
870
- * starts (see `attemptKind.checksTree`), for {@link filesChangedSince}
871
- * (ADR-0020). Null where the check does not run: a run that shares the
872
- * workspace root, where every task's edits land in it, or a workspace git
873
- * cannot isolate at all.
874
- */
875
- snapshotWorkspace(): Promise<TreeSnapshot | null>;
876
- /** Tracked files changed since `snapshot`; none when the run has since gone on in the workspace root. */
877
- filesChangedSince(snapshot: TreeSnapshot | null): Promise<string[]>;
878
- /**
879
- * A stop or a plan load: the open run ends without a handoff and a parked
880
- * start is dropped. `keepOpen` spares a run the scheduler is still driving.
881
- */
882
- interrupt(opts?: {
883
- keepOpen?: boolean;
546
+ retry(id: string, opts?: {
547
+ sameRound?: boolean;
884
548
  }): void;
885
549
  /**
886
- * Mark which added task resolves which conflict. The resolver merges the
887
- * conflicted task's branch by hand in its own worktree; once that lands, the
888
- * conflicted task can land in turn — through the same merge, so a resolver
889
- * that did not really bring it along conflicts again instead of being taken
890
- * at its word.
891
- */
892
- linkResolver(resolverId: string, conflictedId: string): void;
893
- /** The conflicted task a landed resolver was added for, forgotten as it is returned. */
894
- takeResolver(resolverId: string): string | undefined;
895
- /** The tasks added to resolve this task's conflict: one, but for a plan saved before that was the rule (ADR-0015). */
896
- resolversOf(conflictedId: string): string[];
897
- taskDiff(taskId: string, options: TaskDiffRead): Promise<TaskDiffResult>;
898
- reviewDiff(): Promise<string>;
899
- /**
900
- * "Merge all": the run's integration branches into whatever the user has
901
- * checked out, in every repo or none. During a run it merges what has
902
- * landed so far and the run goes on (ADR-0020): its branches stay for the
903
- * tasks still to land. Once a settled run merged everything, it has nothing
904
- * left to hand over, so it is cleared up and forgotten; a branch the user's
905
- * HEAD somehow does not contain stays for the next run's sweep.
906
- */
907
- merge(): Promise<IsolationMergeResult>;
908
- /** What git says of landed work a revert would take; see {@link IWorktreeIsolation.readLanded}. */
909
- readLanded(taskIds: readonly string[]): Promise<LandedWork>;
910
- /**
911
- * Revert tasks' landings on the integration branches (ADR-0054). The record
912
- * is reported changed once the tips are set and before the first commit,
913
- * again as each commit is made, so a crash leaves it to tell them from
914
- * anyone's, and once it settles. Bound to the run it was
915
- * asked in: a plan loaded meanwhile has records of its own under these ids,
916
- * and is saved without what this would write to that run's branches, so it
917
- * stops the revert and leaves them as they were.
918
- */
919
- revert(taskIds: readonly string[], watch: Omit<RevertWatch, 'persist'>): Promise<IsolationRevertResult>;
920
- /**
921
- * Worktrees and task branches go; the integration branch and the record stay
922
- * for review and merge. Refused while a task of the run is still live.
923
- */
924
- cleanup(): Promise<void>;
925
- /**
926
- * The run and everything it made go, and the plan forgets it; the next run
927
- * starts afresh. Refused while a task of the run is still live.
928
- */
929
- discard(): Promise<void>;
930
- /** The records of tasks still live, whose worktrees no clean-up of the whole run may take. */
931
- private liveRecords;
932
- private refuseWhileLive;
933
- /**
934
- * Clear a settled run up once everything it landed is merged — and only
935
- * when nothing else is left in it: a live task, or a worktree holding work
936
- * that never landed, keeps the whole run, worktrees and branches, rather
937
- * than leaving that work to a deletion.
938
- */
939
- private clearMerged;
940
- /** The records that keep a settled run from being cleared up: live, or holding work that never landed. */
941
- private holdouts;
942
- /** The run's worktrees and merged branches go; false, said, when git would not let them. */
943
- private discardMerged;
944
- /** Say what a removal kept back instead of deleting: where the work went, or which worktree stayed. */
945
- private report;
946
- /** The branches the links' resolvers would merge have gone: the links go, and the scheduler is told of the tasks left. */
947
- private endResolvers;
948
- /** The run goes from the plan; `merged` names it as {@link mergedRun} when everything it landed was merged. */
949
- private forget;
950
- private assess;
951
- private activate;
952
- private begin;
953
- /** What earlier runs left merged in the group goes; a failure here is worth a word, never a stopped run. */
954
- private sweep;
955
- /**
956
- * The plan's run carries on while it still has any record: anything landed
957
- * means a resumed plan's dependents must start from a tip that holds their
958
- * predecessors' work, and anything held — a kept attempt, a conflict, a
959
- * repair — is work the user may still want, which a fresh run's mint would
960
- * delete. Only a run with no records at all is superseded, and not one that
961
- * reverted the tasks it had: a retry starts from its tip (ADR-0054).
962
- */
963
- private continuableRun;
964
- /**
965
- * A run with no records at all holds only superseded attempts, so it goes —
966
- * but its integration branch only where the user's HEAD contains it, as does
967
- * that of a run that cannot be continued for another reason (it ran from a
968
- * different workspace path): only the user gives landed work up.
969
- *
970
- * False when `interrupted` came while git was minting: a newer activation
971
- * may be minting its own run, so this one is never installed over it, and
972
- * is discarded rather than left as branches no record names.
550
+ * A pass judged one result, and a new attempt of a delegated task makes
551
+ * another (ADR-0055 D8): its checks run again, whatever their status, their
552
+ * rounds starting over. A check not started yet has nothing to take back. The
553
+ * retries of every kind pass here; the claim asks for the starts that do not.
973
554
  */
974
- private mint;
975
- private reportCopies;
976
- private reportSetupFailures;
977
- private tell;
555
+ takeBackChecks(id: string): void;
556
+ /** Its landed work was reverted (ADR-0054): as a retry leaves it, marked so until it starts. */
557
+ markReverted(id: string): void;
558
+ /** A loop's check counts its rounds afresh from its next run (ADR-0045 L7). */
559
+ startRoundsOver(id: string): void;
560
+ /**
561
+ * The loop runs its check again, as its next round (ADR-0045 L5): a new
562
+ * attempt, the round counted on, and nothing of the round before kept on
563
+ * the task — its report went to the body, and stays in its attempt's log.
564
+ */
565
+ repeat(id: string): void;
566
+ /** Kept on the task: it was force-started past its merge gate, before these ancestors' work was merged (ADR-0020). */
567
+ setForcedPastGate(id: string, dependencies: string[]): void;
568
+ /** One of the task's waits is over, so what it waits on next is another (ADR-0053). */
569
+ endWait(id: string): void;
570
+ /** Work under way is the one status nobody could be waiting on, so moving off any other ends a wait. */
571
+ private leaveStatus;
572
+ /** A reason outlives nothing: any status but `awaiting_user` drops it, and the resume time with it; any status drops a skip's reason. */
573
+ private setStatus;
574
+ setTaskVerdict(id: string, verdict: Task['verdict']): void;
575
+ /**
576
+ * How the task's latest attempt ended, and the handoff it left (ADR-0047),
577
+ * kept as its digest. An attempt that left no handoff clears the one before
578
+ * it: the plan holds the latest attempt's, as it does the summary.
579
+ */
580
+ setTaskOutputSummary(id: string, summary: Task['outputSummary'], handoff?: StoredHandoff): void;
581
+ /** The owner is saved with the session it belongs to, and dropped with it. */
582
+ setTaskRunnerSessionId(id: string, runnerSessionId: string | undefined, owner?: RunnerId): void;
583
+ setCarriedFrom(id: string, runner: RunnerId): void;
584
+ clearCarriedFrom(id: string): void;
585
+ startAttemptUsage(id: string, attempt: number, runner: string, startedAt: string): void;
586
+ recordAttemptUsage(id: string, attempt: number, record: UsageRecord, now: string): void;
587
+ endAttemptUsage(id: string, attempt: number, now: string, runnerSessionCost?: RunnerSessionCost, sessionId?: string): void;
588
+ setTaskAttemptSkills(id: string, skills: Task['attemptSkills']): void;
589
+ /**
590
+ * Widen the plan's runner set. Retargeting a task onto a runner the plan has
591
+ * not used before has to land here as well as on the plan state, or
592
+ * {@link resolveTaskRunner} reads the new runner as foreign and spawns the
593
+ * plan's first one instead.
594
+ */
595
+ admitRunner(runner: RunnerId): void;
596
+ resolveTaskRunner(task: Readonly<Task>): RunnerId;
597
+ private rebuild;
978
598
  }
979
599
 
980
600
  /** A continue the user asked for: their message, and the runner session it resumes (ADR-0018, K1). */
@@ -1001,6 +621,9 @@ interface Continuation {
1001
621
  * - `carryOver`: a change task paused on `from`'s usage limit and moved to
1002
622
  * another runner (ADR-0018, U1). A fresh session — none can cross runners —
1003
623
  * in a worktree that starts from the paused attempt's work.
624
+ * - `delegated`: a delegated task's attempt, a child plan in its worktree
625
+ * (ADR-0055 D3). `readOnly` lands nothing, and its worktree goes as a
626
+ * read-only task's does.
1004
627
  */
1005
628
  type AttemptKind = {
1006
629
  readonly kind: 'change';
@@ -1017,13 +640,17 @@ type AttemptKind = {
1017
640
  } & Readonly<Continuation>) | {
1018
641
  readonly kind: 'carryOver';
1019
642
  readonly from: RunnerId;
643
+ } | {
644
+ readonly kind: 'delegated';
645
+ readonly readOnly: boolean;
1020
646
  };
1021
647
  /**
1022
648
  * What a task's work is, whichever attempt does it: a change that lands, an
1023
649
  * ops task's effects from the checkout (ADR-0020), or a read-only task's,
1024
- * which lands nothing (ADR-0046).
650
+ * which lands nothing (ADR-0046), or a child plan's (ADR-0055), which lands
651
+ * unless it is read-only.
1025
652
  */
1026
- type TaskWork = 'change' | 'ops' | 'readOnly';
653
+ type TaskWork = 'change' | 'ops' | 'readOnly' | 'delegated' | 'delegatedReadOnly';
1027
654
 
1028
655
  /** Which repair of a task an attempt is, of the most `conflictRepairAttempts` allows. */
1029
656
  interface RepairAttempt {
@@ -1075,6 +702,8 @@ interface LandingDeps {
1075
702
  tasks: Pick<PlanStore, 'get' | 'planTasks'>;
1076
703
  /** Whether a repair merge's message ends with the Planned-with trailer; read as the prompt is built. */
1077
704
  plannedWith?: () => boolean;
705
+ /** What this node's ancestors protect, which accumulates down a tree (ADR-0056 M4). */
706
+ inheritedProtectedPaths?: () => readonly string[];
1078
707
  }
1079
708
  /**
1080
709
  * Landing and Conflict repair (ADR-0013, ADR-0014, ADR-0015): a passed
@@ -1091,6 +720,7 @@ declare class Landing {
1091
720
  private readonly config;
1092
721
  private readonly tasks;
1093
722
  private readonly plannedWith;
723
+ private readonly inheritedProtectedPaths;
1094
724
  constructor(deps: LandingDeps);
1095
725
  /**
1096
726
  * Land a passed attempt's work. One whose worktree has no unlanded record
@@ -1115,6 +745,9 @@ declare class Landing {
1115
745
  * must not bring it in anyway.
1116
746
  */
1117
747
  settleRepair(task: Task, verdict: Verdict, current: () => boolean): Promise<LandingOutcome>;
748
+ /** A repair lands after the verdict that held the work to its protected paths, so it is held to them here (ADR-0055 D7, D14). */
749
+ private protectedPathsTouched;
750
+ private protectedPaths;
1118
751
  /** A repair that ended without landing leaves the task as its conflict did: waiting on the user, worktree and refs kept. */
1119
752
  unrepaired(task: Task, why: string): Promise<Extract<LandingOutcome, {
1120
753
  kind: 'awaiting_user';
@@ -1506,7 +1139,56 @@ declare function scriptExitText(exit: Omit<Extract<TaskLogEvent, {
1506
1139
  */
1507
1140
  declare function digestTaskLog(events: readonly TaskLogEvent[], maxCalls: number): string;
1508
1141
 
1509
- /** The session's saved task logs, as `read_task` reads them (ADR-0018, P1). */
1142
+ /** One node as a tree's log names it (ADR-0058 S3). */
1143
+ interface SubtreeNode {
1144
+ path: NodePath;
1145
+ sessionId: string;
1146
+ /** The delegated task of the plan above it. */
1147
+ taskId?: string;
1148
+ }
1149
+ /** A plan of the tree, read afresh: its tasks, and the logs of its attempts. */
1150
+ interface NodePlan {
1151
+ node: NodePath;
1152
+ tasks: readonly Task[];
1153
+ logs: TaskLogReader;
1154
+ }
1155
+ interface SubtreeSource {
1156
+ /** The node reading: what is below it is what this reads. */
1157
+ here: NodePath;
1158
+ /** Every node ever opened below `here`, closed ones too: a reference outlives the child it names (ADR-0057 R1). */
1159
+ nodes(): readonly SubtreeNode[];
1160
+ /** What a node's session file holds of its plan, which is read only when asked for (ADR-0055 D17). */
1161
+ plan(node: SubtreeNode): readonly Task[] | undefined;
1162
+ logs(node: SubtreeNode): TaskLogReader;
1163
+ }
1164
+ /**
1165
+ * The plans below one node, as a reference reads them (ADR-0057 R1): a
1166
+ * reader's reach goes down and never up, or sideways, so a child's tools see
1167
+ * nothing of the plan that holds its check (ADR-0055 D8).
1168
+ */
1169
+ declare class Subtree {
1170
+ private readonly source;
1171
+ private readonly reach?;
1172
+ constructor(source: SubtreeSource, reach?: ReadonlySet<string> | undefined);
1173
+ /** What a task reads: only what hangs from a delegated task its readable set holds. */
1174
+ reachedBy(readable: ReadonlySet<string>): Subtree;
1175
+ private nodes;
1176
+ private planOf;
1177
+ /** Every plan below that has been saved, nearest first, each once: a Continue opens a new node for the same child and its one plan (ADR-0055 D17). */
1178
+ plans(): NodePlan[];
1179
+ /** The plan a pinned reference names, whichever of a child's nodes it was pinned against. */
1180
+ planAt(path: NodePath): NodePlan | undefined;
1181
+ /**
1182
+ * The plan a written path names. A report shows a path from the root and an
1183
+ * author writes one from its own plan, so a path that starts at this node
1184
+ * and goes on is read as shown; anything else is read from here.
1185
+ */
1186
+ at(path: string): NodePlan | undefined;
1187
+ /** The same as a reference is pinned against it. */
1188
+ readonly below: (path: string) => BelowPlan | undefined;
1189
+ }
1190
+
1191
+ /** The session's saved task logs, as `read` reads them (ADR-0018, P1). */
1510
1192
  interface TaskLogReader {
1511
1193
  /** The attempts of a task that have a saved log, oldest first. */
1512
1194
  attempts(taskId: string): number[];
@@ -1515,6 +1197,262 @@ interface TaskLogReader {
1515
1197
  patch?(taskId: string, attempt: number): Buffer | undefined;
1516
1198
  }
1517
1199
 
1200
+ /** Where the board's `file` evidence is compared (ADR-0057 R9). */
1201
+ interface BoardGit {
1202
+ /** The branch this node's plan lands on; undefined with no run yet. */
1203
+ branch(): string | undefined;
1204
+ /** The blob `file` holds on `branch`: null where it holds none, undefined where the branch cannot be read. */
1205
+ blob(branch: string, file: string): Promise<string | null | undefined>;
1206
+ }
1207
+ /** The log's side of a board. */
1208
+ interface BoardLog {
1209
+ post(node: NodePath, draft: BoardDraft): BoardEntry | null;
1210
+ board(node: NodePath): readonly BoardEntry[];
1211
+ }
1212
+ interface NodeBoardDeps {
1213
+ log: BoardLog;
1214
+ node: NodePath;
1215
+ git: BoardGit;
1216
+ /** What this plan's checks read and run: what no pin may hold (R10). */
1217
+ checks?(): readonly CheckWord[];
1218
+ }
1219
+ /** The blob of a file where its author works, as git would name it; undefined for what cannot be read, which goes unpinned. */
1220
+ type WorkBlobs = (file: string) => Promise<string | undefined>;
1221
+ /**
1222
+ * One node's view of the tree's boards (ADR-0057 R8 to R10): it posts to its
1223
+ * own, and reads that and its ancestors' by pull. Nothing here wakes or tells
1224
+ * anyone, so it is no channel between siblings (R7). What is stale or contested
1225
+ * is worked out here, as a reader asks, from what the log and git hold.
1226
+ */
1227
+ declare class NodeBoard {
1228
+ private readonly deps;
1229
+ constructor(deps: NodeBoardDeps);
1230
+ get node(): NodePath;
1231
+ /** Nearest first: this node, then each ancestor. */
1232
+ private chain;
1233
+ private readable;
1234
+ /** The file evidence an entry pins, read now: what its author saw and what the branch held. */
1235
+ private pinned;
1236
+ /** Why this may not be posted, or null. */
1237
+ private refusal;
1238
+ /** `work` reads where the author works: its files are pinned from there (R9). */
1239
+ post(entry: BoardPost, by: Omit<BoardAuthor, 'node'>, work?: WorkBlobs): Promise<McpToolReply>;
1240
+ /** Whether a pinned file has moved past what its entry was written on. */
1241
+ private stale;
1242
+ private marked;
1243
+ /** `read` of `board`, or of one entry by its reference. */
1244
+ read(written: string): Promise<McpToolReply>;
1245
+ /** The pins a new descendant of this node is told (R10). */
1246
+ pins(): string[];
1247
+ /**
1248
+ * A delegated task's `contract`s for what it provides, onto this board as it
1249
+ * lands (R6), which is how a sibling reads them: a node reads its ancestors'
1250
+ * boards and never another's. Each keeps its author, and is read again against
1251
+ * the branch it has landed on. Only what the plan declared rises, and only once.
1252
+ */
1253
+ raise(from: readonly BoardEntry[], provides: readonly string[]): Promise<void>;
1254
+ }
1255
+
1256
+ /** One node as the log leaves it. */
1257
+ interface TreeNode {
1258
+ path: NodePath;
1259
+ sessionId: string;
1260
+ taskId?: string;
1261
+ limit: number;
1262
+ /** What its open children hold. */
1263
+ committed: number;
1264
+ /** Its own, and what its closed children spent. */
1265
+ spent: number;
1266
+ /** The subplanner's share of `reserve`, held for its next turn. */
1267
+ kept: number;
1268
+ /** What raises gave it: a grant to its task, which outlives this node, so what a parent's turn takes back comes off `limit` and not off this (M5). */
1269
+ raised: number;
1270
+ open: boolean;
1271
+ /** Children it has ever opened: the next one's id. */
1272
+ opened: number;
1273
+ /** What was posted on its board, oldest first; absent on a node nothing was (ADR-0057 R8). */
1274
+ board?: BoardEntry[];
1275
+ }
1276
+ /** A node's four numbers (ADR-0056 M5). `spent + committed + reserve ≤ limit`, unless `spent` overshot, as nothing can recall a request. */
1277
+ interface BudgetNumbers {
1278
+ limit: number;
1279
+ committed: number;
1280
+ spent: number;
1281
+ reserve: number;
1282
+ }
1283
+ /** What a claim holds until its attempt ends. */
1284
+ interface BudgetHold {
1285
+ /** Idempotent. */
1286
+ release(): void;
1287
+ }
1288
+ type Carved = {
1289
+ ok: true;
1290
+ mandate: Mandate;
1291
+ budget: NodeBudget;
1292
+ } | {
1293
+ ok: false;
1294
+ refusal: MandateRefusal;
1295
+ };
1296
+ /** One node's side of the ledger, as a Session spends it. */
1297
+ interface NodeBudget {
1298
+ readonly path: NodePath;
1299
+ readonly sessionId: string;
1300
+ readonly tree: TreeLedger;
1301
+ /** Why a new session may not start here, or null. Changes nothing: a claim's refusal is also asked with no claim to follow. */
1302
+ refusal(): string | null;
1303
+ /** In the claim's synchronous step, so concurrent claims each see the last one's. */
1304
+ hold(): BudgetHold;
1305
+ spent(record: UsageRecord, on: {
1306
+ taskId: string;
1307
+ attempt: number;
1308
+ }): void;
1309
+ /** Why a subplanner's turn may not start, or null: that share, room and its children's room must come to one session. */
1310
+ turnRefusal(): string | null;
1311
+ turnSpent(record: UsageRecord): void;
1312
+ /** `child` asked this turn: it pays first for what the turn cannot (M5). */
1313
+ woken(child: NodePath): void;
1314
+ /** The turn ended: keep one session again, from room, then from the children's. */
1315
+ parked(): void;
1316
+ /** Nothing here can go on, for this reason. Said once, until a raise. */
1317
+ stop(why: string): void;
1318
+ /** Why it stopped, or null: what a parent reads to end it `blocked`. */
1319
+ stoppedFor(): string | null;
1320
+ /** Tell `listener` why, each time this node stops. Returns how to stop being told. */
1321
+ onStop(listener: (why: string) => void): () => void;
1322
+ /** Tell `listener` each time a raise takes: it only moves numbers, so what the node's Session starts again is its own. */
1323
+ onRaised(listener: () => void): void;
1324
+ }
1325
+ interface TreeLedgerOptions {
1326
+ /** {@link SESSION_RESERVE_TOKENS} unless a test works with smaller numbers. */
1327
+ sessionReserve?: number;
1328
+ /** The log refused a write. What it refused waits and goes with the next. */
1329
+ failed?: (err: unknown) => void;
1330
+ }
1331
+ /**
1332
+ * The budget ledger (ADR-0056 M5), the root's and shared down its tree. Every
1333
+ * read and decision is synchronous, as a claim is (ADR-0029); what is decided
1334
+ * goes to the log behind it, whole, in the order it was decided. Only budgets
1335
+ * are here: a child's mandate and what to spend it on are its Session's.
1336
+ *
1337
+ * What is live is held here and never logged: nothing is live after a restart.
1338
+ */
1339
+ declare class TreeLedger {
1340
+ private readonly store;
1341
+ private readonly tree;
1342
+ private readonly reserve;
1343
+ private readonly failed;
1344
+ /** Sessions holding an allowance, by node. */
1345
+ private readonly live;
1346
+ /** Who pays first for the turn under way, by node. */
1347
+ private readonly wakers;
1348
+ private readonly listeners;
1349
+ private readonly raiseListeners;
1350
+ /** Why each stopped node did, which a raise clears (M5). */
1351
+ private readonly stopped;
1352
+ private pending;
1353
+ private writing;
1354
+ private failureSaid;
1355
+ private constructor();
1356
+ /** Rebuild from the log, and from nothing else (ADR-0058 S3). A log with no root gets one: this is the first carve of a tree. */
1357
+ static open(store: EventStore, root: {
1358
+ sessionId: string;
1359
+ limit: number;
1360
+ }, options?: TreeLedgerOptions): Promise<TreeLedger>;
1361
+ private write;
1362
+ /** What one step decides lands as one batch: the first await lets the step finish. */
1363
+ private flush;
1364
+ /** Everything decided so far has been offered to the log. */
1365
+ flushed(): Promise<void>;
1366
+ /** The Session holding the tree is closing. */
1367
+ shutdown(): Promise<void>;
1368
+ private at;
1369
+ private liveReserve;
1370
+ /** What `limit` leaves once its node's spending and children are paid: its reserve and room. */
1371
+ private free;
1372
+ private room;
1373
+ /**
1374
+ * What a claim can take. A turn spends its subplanner's share down until it
1375
+ * parks, and a claim comes at that turn's end: read as it stands, the share
1376
+ * would pay for the claim, and the turn that follows the work would find
1377
+ * nothing and could not report it.
1378
+ */
1379
+ private claimRoom;
1380
+ numbers(path: NodePath): BudgetNumbers;
1381
+ /** What a node can still carve or claim: `limit` less the other three. What `narrow` is handed as the parent's tokens (M4). */
1382
+ left(path: NodePath): number;
1383
+ node(path: NodePath): NodeBudget;
1384
+ /** Every node ever opened under `path`, closed ones too, nearest first: a reference outlives the child it names (ADR-0057 R1). */
1385
+ below(path: NodePath): TreeNode[];
1386
+ /**
1387
+ * A node's board, every opening of one child's together: a Continue opens a
1388
+ * new node for the same child and its one plan (ADR-0055 D17).
1389
+ */
1390
+ board(path: NodePath): readonly BoardEntry[];
1391
+ /** One child's board, closed or not: a fresh retry is a new child with a board of its own (ADR-0055 D15). */
1392
+ boardOf(sessionId: string): readonly BoardEntry[];
1393
+ /** Put an entry on `path`'s board, or null where that board is full. */
1394
+ post(path: NodePath, draft: BoardDraft): BoardEntry | null;
1395
+ /** What `path` and everything under it spent: a closed child is in its parent's `spent`, an open one still its own (M5). */
1396
+ subtreeSpent(path: NodePath): number;
1397
+ /** What a node's task has cost so far: this opening's subtree and every opening before it, since a report of the last alone reads as the whole (ADR-0056 M10). */
1398
+ taskSpent(path: NodePath): number;
1399
+ /** The open node `sessionId` is, if any: what a reopen finds. */
1400
+ nodeOf(sessionId: string): NodeBudget | undefined;
1401
+ /** What every child `parent` opened for `taskId` has spent, closed ones included: a retry's budget is what they left (ADR-0056 M10). */
1402
+ spentOn(parent: NodePath, taskId: string): number;
1403
+ /** What raises gave every child `parent` opened for `taskId`: the tree granted it to the task, on top of its stated budget (ADR-0056 M5, M10). */
1404
+ raisedOn(parent: NodePath, taskId: string): number;
1405
+ private childrenFor;
1406
+ /** Close the children of `parent` that `gone` picks by their task: no session is live in them, so what they hold would stay committed for good. */
1407
+ closeChildren(parent: NodePath, gone: (taskId: string | undefined) => boolean): void;
1408
+ private hold;
1409
+ private openChildren;
1410
+ /** What a turn can reach: what its node has beside its live sessions, and its children's room. */
1411
+ private turnCover;
1412
+ /**
1413
+ * A child's mandate, carved from what `parent` has left (ADR-0056 M4, M5). One
1414
+ * synchronous step, so no second carve can read the same room.
1415
+ */
1416
+ carve(parent: NodePath, mandate: Mandate, request: MandateRequest, child: {
1417
+ sessionId: string;
1418
+ taskId: string;
1419
+ }): Carved;
1420
+ /**
1421
+ * A raise, for the parent's answer to a checkpoint to apply (M5): from what
1422
+ * the parent has left, the child's limit and the parent's `committed` rising
1423
+ * together. Null when it took; otherwise why not.
1424
+ */
1425
+ raise(path: NodePath, tokens: number): string | null;
1426
+ /** Where the user keeps the tree's budget (ADR-0056 M5), read as a carve or a raise is asked for. */
1427
+ setRootLimit(limit: number): void;
1428
+ /**
1429
+ * A child ended, on confirmed close: what it did not spend returns to its
1430
+ * parent and goes no further (M5), so a sibling gets it only by the parent's
1431
+ * next carve. What is still open under it closes first, in the same step.
1432
+ */
1433
+ close(path: NodePath): void;
1434
+ private closeUnder;
1435
+ private nearestOpen;
1436
+ private spend;
1437
+ /**
1438
+ * A call's usage arrives with the next already sent, and nothing recalls a
1439
+ * request, so a node that stopped at its limit would always pass it, by a call
1440
+ * or more. It stops while what it can reach still pays for what is on its way:
1441
+ * the next call of each live session and the one after, each the size of the
1442
+ * one just reported, which a long context only grows from. An estimate, never
1443
+ * more than the reserve that is held for it. No live session means a runner
1444
+ * winding down, with no call to follow.
1445
+ */
1446
+ private stopShortOfLimit;
1447
+ /** Past what it can pay, a node's spending takes its children's room; with none left it is out. */
1448
+ private settle;
1449
+ /** Take up to `short` of the children's room, the child that woke the turn first and then the oldest. Returns what it took. */
1450
+ private takeFromChildren;
1451
+ private park;
1452
+ private spentOut;
1453
+ private stopNode;
1454
+ }
1455
+
1518
1456
  /** Where a start stands against the user's Stop (ADR-0029): no longer current once one comes. */
1519
1457
  type Admitted = Pick<StartAdmission, 'current'>;
1520
1458
  type AttemptPhase = 'starting' | 'running' | 'integrating';
@@ -1608,6 +1546,11 @@ type StartWait =
1608
1546
  | {
1609
1547
  kind: 'start-hold';
1610
1548
  }
1549
+ /** A delegated task's check is running: its fresh attempt takes that check back, so it waits for it to end (ADR-0055 D8). */
1550
+ | {
1551
+ kind: 'check-running';
1552
+ check: string;
1553
+ }
1611
1554
  /** Its merge gate (ADR-0020): the ancestors whose landed work is not in the user's branch yet. */
1612
1555
  | {
1613
1556
  kind: 'merge-gate';
@@ -1864,6 +1807,8 @@ interface EscalationRun {
1864
1807
  mergesAtClose: boolean;
1865
1808
  /** What Merge all would merge now. */
1866
1809
  merge: RunMergeView | null;
1810
+ /** The checks that have not passed work Merge all would merge (ADR-0055 D9): the user's is not held, and its confirmation names them. */
1811
+ mergeHold: readonly string[];
1867
1812
  /** The isolated run's id. */
1868
1813
  id: string | null;
1869
1814
  /** Which of the session's starts a dirty checkout turned away, when one waits on it; null when none does. */
@@ -1890,7 +1835,7 @@ interface EscalationInput {
1890
1835
  holdReason: (taskId: string) => string | null | undefined;
1891
1836
  /** Something will run this check between rounds without being asked (ADR-0045 L5). */
1892
1837
  loopRunsOn: (taskId: string) => boolean;
1893
- /** A loop carried on with no scheduler starts this task, so the run is open for it alone (ADR-0045 L5). */
1838
+ /** A loop or a delegated task's check carried on with no scheduler starts this task, so the run is open for it alone (ADR-0045 L5, ADR-0055 D8). */
1894
1839
  carried: (taskId: string) => boolean;
1895
1840
  run: EscalationRun;
1896
1841
  }
@@ -1979,6 +1924,8 @@ type SerializedTaskStatus = {
1979
1924
  loop?: TaskLoop;
1980
1925
  /** The round a loop's check is in, past its first (ADR-0045 L5); absent on round 1 and on any other task. */
1981
1926
  loopRound?: number;
1927
+ /** A delegated task's row: its child's tasks, what is left of its budget, a Stop still winding down (ADR-0055 D20); absent on any other task and when it has nothing to say. */
1928
+ delegation?: DelegatedRow;
1982
1929
  };
1983
1930
  /**
1984
1931
  * Landed work not yet in the user's branch. `paused` means the running plan
@@ -2033,6 +1980,8 @@ type SerializedTask = {
2033
1980
  loop?: TaskLoop;
2034
1981
  /** As on {@link SerializedTaskStatus}: only on a loop's check past its first round. */
2035
1982
  loopRound?: number;
1983
+ /** What a delegated task's child plan must achieve (ADR-0055 D1); absent on any other task. */
1984
+ goal?: string;
2036
1985
  };
2037
1986
  /** Skill loads go out as notices: their bodies are for the planner, and stay in the session. */
2038
1987
  type SerializedConversationMessage = Omit<ConversationMessage, 'skill'> & {
@@ -2390,7 +2339,7 @@ type PlanSnapshot = {
2390
2339
  plan: SurfacePlan | SurfacePlanState;
2391
2340
  };
2392
2341
  /** What only the status stream says about a task; a plan refresh keeps these as they were. */
2393
- type RunTaskStatus = Partial<Pick<SerializedTaskStatus, 'verdict' | 'idleSince' | 'completionReceived' | 'attaching' | 'isolation' | 'checkpoint' | 'queued' | 'continuable' | 'awaitingApproval' | 'mergeGate' | 'forcedPastGate' | 'waitsForApproval' | 'holdReason' | 'loopStranded' | 'handoff' | 'attempted'>>;
2342
+ type RunTaskStatus = Partial<Pick<SerializedTaskStatus, 'verdict' | 'idleSince' | 'completionReceived' | 'attaching' | 'isolation' | 'checkpoint' | 'queued' | 'continuable' | 'awaitingApproval' | 'mergeGate' | 'forcedPastGate' | 'waitsForApproval' | 'holdReason' | 'loopStranded' | 'handoff' | 'attempted' | 'delegation'>>;
2394
2343
  /** A task in the run view: the plan's fields, then the status stream's. */
2395
2344
  type RunTask = Omit<SerializedTask, 'subtasks'> & RunTaskStatus & {
2396
2345
  subtasks: RunTask[];
@@ -2454,7 +2403,7 @@ type SessionNotice = {
2454
2403
  };
2455
2404
  type SessionBroadcaster = (msg: SessionMessage) => void;
2456
2405
  declare function serializeTask(t: SurfaceTask): SerializedTask;
2457
- declare function serializeTaskStatus(t: Task, idleSince?: string | null, isolation?: TaskIsolation | null, queued?: readonly QueuedTaskMessage[], awaitingApproval?: number, mergeGate?: readonly string[], checkpoint?: string, waitsForApproval?: boolean, holdReason?: string | null, completionReceived?: Verdict['reported'] | null, attaching?: 1 | 2 | null, loopStranded?: boolean): SerializedTaskStatus;
2406
+ declare function serializeTaskStatus(t: Task, idleSince?: string | null, isolation?: TaskIsolation | null, queued?: readonly QueuedTaskMessage[], awaitingApproval?: number, mergeGate?: readonly string[], checkpoint?: string, waitsForApproval?: boolean, holdReason?: string | null, completionReceived?: Verdict['reported'] | null, attaching?: 1 | 2 | null, loopStranded?: boolean, delegation?: DelegatedRow | undefined): SerializedTaskStatus;
2458
2407
  /** The one place a plan's skill bodies come off before it leaves the session: every wire path that carries plan state goes through here. */
2459
2408
  declare function surfacePlan({ queuedMessages: _carriedOverAtLoad, ...plan }: LegacyPlanState): SurfacePlan;
2460
2409
  declare function surfacePlanState(state: PlanState): SurfacePlanState;
@@ -2642,6 +2591,115 @@ declare class HostedLease implements SessionLease {
2642
2591
  */
2643
2592
  declare function processLeaseHost(): LeaseHost;
2644
2593
 
2594
+ /** The changes to a run in flight, as a closing Session waits them out. */
2595
+ interface RunChanges {
2596
+ /** One is under way. */
2597
+ readonly changing: boolean;
2598
+ /** Every one under way has ended and none follows: nothing of this run changes git past here, so its lease can go. */
2599
+ closeChanges(): Promise<void>;
2600
+ }
2601
+
2602
+ /** What a Session hears from the pool of its tree. */
2603
+ interface PoolNode {
2604
+ /** A slot freed in another Session: tick, as a Session's own events do. */
2605
+ tick(): Promise<void>;
2606
+ /** A slot is free for the planner turn this Session was refused: ask again now, synchronously. */
2607
+ openTurn(): void;
2608
+ /** Whether this Session's attempt on `taskId` waits on its planner: a question it asked, or a turn that ended without a report. */
2609
+ waitsOnPlanner(taskId: string): boolean;
2610
+ /** Another node found `runner`'s usage limit, which resets at `at`. */
2611
+ limitFound(runner: string, at: Date): void;
2612
+ }
2613
+ /** What a node asks of the pool: only a role tells it what a claim takes. */
2614
+ interface SlotRequest {
2615
+ role: 'planner' | 'task' | 'script' | 'child-plan' | 'merge';
2616
+ taskId?: string;
2617
+ }
2618
+ /** What {@link SlotPool.take} granted. */
2619
+ interface HeldSlot {
2620
+ /** False for a role that reserves no slot. */
2621
+ readonly holdsSlot: boolean;
2622
+ /** Idempotent: a slot is freed once, and offered on at once. */
2623
+ free(): void;
2624
+ }
2625
+ /**
2626
+ * One pool of slots across a tree (ADR-0055 D18, ADR-0058 S4), the root's and
2627
+ * shared by every Session under it. A flat run has a pool of its own, with one
2628
+ * node: there it is the Parallel limit over attempts alone, and it ticks nobody.
2629
+ *
2630
+ * A claim is synchronous and so is everything here that grants one (ADR-0029);
2631
+ * a Session waiting on a slot in another Session is ticked from here, because a
2632
+ * scheduler ticks only on its own Session's events.
2633
+ */
2634
+ declare class SlotPool {
2635
+ private readonly limit;
2636
+ private readonly nodes;
2637
+ private root;
2638
+ private readonly holders;
2639
+ /** Nodes whose subplanner turn was refused, oldest first. */
2640
+ private readonly keptTurns;
2641
+ /** Sessions a start was refused, oldest first. */
2642
+ private readonly keptStarts;
2643
+ /** The one turn past the limit: see {@link turnPasses}. */
2644
+ private passing;
2645
+ /** The turn being offered: out of {@link keptTurns}, so it would otherwise queue behind the turns it was chosen ahead of. */
2646
+ private offered;
2647
+ private offering;
2648
+ /** Wakes the offer in flight once a slot goes: a tick's own spawn can take far longer than its claim. */
2649
+ private taken;
2650
+ /** When the tree was held on a usage limit, oldest first and never overlapping. */
2651
+ private readonly holds;
2652
+ private readonly limits;
2653
+ constructor(limit: () => number);
2654
+ /** Add a Session to the tree; the first is its root. Returns how it leaves. */
2655
+ join(node: PoolNode): () => void;
2656
+ /** The tree has a child: there is more than one Session to count across. */
2657
+ get shared(): boolean;
2658
+ private get counted();
2659
+ private get full();
2660
+ /** Why `node` may not take this now, or null. Changes nothing: a claim's refusal is also asked with no claim to follow. */
2661
+ refusal(node: PoolNode, request: SlotRequest): string | null;
2662
+ /**
2663
+ * Take a slot, or be refused and kept (ADR-0059 L1): what a refusal changes
2664
+ * is that the node is told when one frees. {@link refusal} keeps nobody, so
2665
+ * a wait for a slot asks here even when it knows the answer.
2666
+ */
2667
+ take(node: PoolNode, request: SlotRequest): HeldSlot | {
2668
+ refused: string;
2669
+ };
2670
+ /** An attempt's wait on its planner began or ended: the test {@link turnPasses} reads has changed. */
2671
+ waitsChanged(): void;
2672
+ /**
2673
+ * `node` found `runner`'s limit (D18). Returns when it resets, the later of
2674
+ * this and any limit a sibling found: a runner is usable only once every
2675
+ * limit it hit has reset, and the account is the tree's. A flat run's waits
2676
+ * stay its own.
2677
+ */
2678
+ limitFound(node: PoolNode, runner: string, at: Date): Date;
2679
+ /**
2680
+ * How long the tree has been held on a usage limit since `since`, up to each
2681
+ * limit's reset: what a child's wall clock does not count (ADR-0056 M3). It
2682
+ * ends at the reset whatever happens first, so what waits on a slot past it
2683
+ * is on the clock, and limits that overlap count once.
2684
+ */
2685
+ heldSince(since: number): number;
2686
+ private held;
2687
+ /** The runners a usage limit holds across the tree. */
2688
+ limited(): ReadonlySet<string>;
2689
+ /**
2690
+ * Everything that holds a slot waits on a planner, and `node`'s turn is the
2691
+ * oldest one of them waits on: a limit that stopped the answer would stop
2692
+ * the tree. One at a time: a turn waits on nothing (ADR-0056 M8), so when it
2693
+ * ends this is read again, and nothing holds a slot for ever on a turn that
2694
+ * waits for that slot.
2695
+ */
2696
+ private turnPasses;
2697
+ private waitedTurn;
2698
+ private nextTurn;
2699
+ private offer;
2700
+ private offerStarts;
2701
+ }
2702
+
2645
2703
  /** What ran an automatic Merge all: a task reaching its merge gate, or the run settling with landed work unmerged. */
2646
2704
  type AutoMergeTrigger = {
2647
2705
  kind: 'gate';
@@ -2781,6 +2839,8 @@ interface TaskOrchestratorDeps {
2781
2839
  runner: IRunner;
2782
2840
  /** Runs script tasks' bodies (ADR-0035), in place of a registered runner. */
2783
2841
  scriptRunner: IRunner;
2842
+ /** Runs delegated tasks' child plans (ADR-0055 D3). */
2843
+ childPlanRunner: IRunner;
2784
2844
  store: PlanStore;
2785
2845
  output: TaskOutputSource;
2786
2846
  /** The plan's isolation run and the open run's lifecycle (ADR-0013). */
@@ -2816,8 +2876,12 @@ interface TaskOrchestratorDeps {
2816
2876
  isApproved: (taskId: string) => boolean;
2817
2877
  /** Read beside the run's setting at each automatic merge, which passes no command (ADR-0056 M6). */
2818
2878
  mayMergeUnasked: () => boolean;
2879
+ /** The run's changes to git in flight; absent where no session's lease stands behind them. */
2880
+ runChanges?: RunChanges;
2819
2881
  /** A read-only attempt's shell (ADR-0046); absent, its runner is given no `run_command`. */
2820
2882
  taskShell?: (site: TaskShellSite) => TaskShell | undefined;
2883
+ /** Why a script that would land its work may not run unsealed now; absent at a root, whose scripts ADR-0035 answers for. */
2884
+ unsealedRefusal?: (task: Readonly<Task>) => string | null;
2821
2885
  /** The session's web fetcher for one read-only attempt (ADR-0046); absent, its `fetch` and `web_search` are not available. */
2822
2886
  taskWebFetcher?: (task: string) => IWebFetcher | undefined;
2823
2887
  /** Keep a read-only attempt's saved patch beside its log (ADR-0050); absent, `save_patch` is not available. */
@@ -2827,8 +2891,47 @@ interface TaskOrchestratorDeps {
2827
2891
  * ran (ADR-0047). A task that ran no session, a user task, gets a log of its own.
2828
2892
  */
2829
2893
  recordHandoff: (sessionId: string | undefined, taskId: string, handoff: StoredHandoff) => void;
2830
- /** The session's saved task logs, which a task's `read_task` reads handoffs from (ADR-0047). */
2894
+ /** The session's saved task logs, which a task's `read` reads handoffs from (ADR-0047). */
2831
2895
  taskLog: TaskLogReader;
2896
+ /** The plans below this one (ADR-0057 R1); absent or null with nothing delegated. */
2897
+ subtree?: () => Promise<Subtree | null>;
2898
+ /** This plan's node in its tree, what a reference is pinned under. Absent, the root's. */
2899
+ planNode?: () => string;
2900
+ /** This plan's board; null for a flat plan, which has none (ADR-0057 R8). */
2901
+ board?: () => Promise<NodeBoard | null>;
2902
+ /** What a landing owes the tasks beside it (ADR-0057 R6); undefined where it owes nothing, as on a flat plan. */
2903
+ landed?: (task: Readonly<Task>) => Promise<void> | undefined;
2904
+ /** Where sessions run (ADR-0059). */
2905
+ leases: ExecutionBackend;
2906
+ /** The tree's slots and usage limits (ADR-0055 D18), which the backend reserves from. */
2907
+ pool: SlotPool;
2908
+ /** The Session's id, as it saves under it. */
2909
+ sessionId: () => string;
2910
+ /** Tell the tree this Session found a runner's usage limit; the reset as the tree has it. */
2911
+ limitFound: (runner: string, at: Date) => Date;
2912
+ /** This Session is done with the tree. */
2913
+ leaveTree: () => void;
2914
+ /** This node's side of the tree's ledger; absent at the root, which is charged nothing, and on a flat run, which has no ledger (ADR-0056 M5). */
2915
+ budget?: NodeBudget;
2916
+ /** What is ready cannot start, and nothing live will free room for it, so nothing ticks this again. */
2917
+ startRefused: (why: string) => void;
2918
+ /** This node's autonomy, as its mandate reads it (ADR-0056 M6). */
2919
+ autonomous: () => boolean;
2920
+ /** Nothing is deciding where the run goes any more; see {@link TaskOrchestrator.movesUnasked}. */
2921
+ decided?: () => void;
2922
+ /** Why nothing may start at this node (ADR-0055 D16, ADR-0056 M10); absent at the root, whose own Stop is its generation. */
2923
+ nodeRefusal?: () => string | null;
2924
+ /** The child `task` holds goes (ADR-0055 D15). Absent where no Session hosts one. */
2925
+ discardChild?: (task: Readonly<Task>) => Promise<void>;
2926
+ }
2927
+ /** What a backend hears of the Session it serves, past the local lease it wraps. */
2928
+ interface ExecutionContext {
2929
+ sessionId: () => string;
2930
+ /** The root of this Session's tree: what a clean-up at the root finds a child's leavings by. */
2931
+ treeId: () => string;
2932
+ tell: (level: 'info' | 'warn' | 'error', text: string) => void;
2933
+ /** What a worktree's bootstrap runs on this host, in what was last landed. */
2934
+ setupCommand: () => string | undefined;
2832
2935
  }
2833
2936
  /**
2834
2937
  * What a caller supplies to {@link TaskOrchestrator.compose}; every optional
@@ -2836,17 +2939,28 @@ interface TaskOrchestratorDeps {
2836
2939
  * resolved wiring the orchestrator itself runs on.
2837
2940
  */
2838
2941
  interface TaskOrchestratorOptions {
2942
+ /** The plans below this one (ADR-0057 R1); absent or null with nothing delegated. */
2943
+ subtree?: () => Promise<Subtree | null>;
2944
+ /** This plan's node in its tree, what a reference is pinned under. Absent, the root's. */
2945
+ planNode?: () => string;
2946
+ /** This plan's board; null for a flat plan, which has none (ADR-0057 R8). */
2947
+ board?: () => Promise<NodeBoard | null>;
2948
+ /** What a landing owes the tasks beside it (ADR-0057 R6); undefined where it owes nothing, as on a flat plan. */
2949
+ landed?: (task: Readonly<Task>) => Promise<void> | undefined;
2839
2950
  /** What starting a research task owes the planner (ADR-0037 R3). */
2840
2951
  research?: Pick<ResearchTasks, 'started'>;
2841
2952
  config: IConfig;
2842
2953
  notifications: INotification;
2843
2954
  runner: IRunner;
2844
2955
  scriptRunner?: IRunner;
2956
+ childPlanRunner?: IRunner;
2845
2957
  store?: PlanStore;
2846
2958
  output?: TaskOutputSource;
2847
2959
  isolation?: IWorktreeIsolation;
2848
2960
  registry?: RunnerRegistry | null;
2849
2961
  workspaceRoot?: () => string;
2962
+ /** A child session's state root (ADR-0055 D11); absent on a flat run, whose workspace is both roots. */
2963
+ stateRoot?: string;
2850
2964
  workspaceEnv?: (cwd: string) => Promise<WorkspaceEnv>;
2851
2965
  skillsAt?: (roots: readonly string[]) => SkillLookup;
2852
2966
  previousAttemptFromLog?: (taskId: string) => string | null;
@@ -2860,6 +2974,8 @@ interface TaskOrchestratorOptions {
2860
2974
  /** Whether this node's mandate lets an automatic merge run; true unless set. */
2861
2975
  mayMergeUnasked?: () => boolean;
2862
2976
  taskShell?: (site: TaskShellSite) => TaskShell | undefined;
2977
+ /** See {@link TaskOrchestratorDeps.unsealedRefusal}. */
2978
+ unsealedRefusal?: (task: Readonly<Task>) => string | null;
2863
2979
  taskWebFetcher?: (task: string) => IWebFetcher | undefined;
2864
2980
  keepPatch?: (taskId: string, attempt: number, patch: Buffer) => string;
2865
2981
  recordHandoff?: (sessionId: string | undefined, taskId: string, handoff: StoredHandoff) => void;
@@ -2869,6 +2985,30 @@ interface TaskOrchestratorOptions {
2869
2985
  * to the run (ADR-0049); the run is changed only while it answers null.
2870
2986
  */
2871
2987
  sessionHeld?: () => SessionHeld | null;
2988
+ /** Where sessions run (ADR-0059), given the local worktrees: those, unless the `containers` setting or a test supplies another. */
2989
+ execution?: (local: ExecutionBackend, context: ExecutionContext) => ExecutionBackend;
2990
+ /** The root's pool, handed down to a child (ADR-0055 D18); absent, this is a root, with a pool of its own. */
2991
+ pool?: SlotPool;
2992
+ /** The Session's id, as it saves under it. */
2993
+ sessionId?: () => string;
2994
+ /** Its tree's root's, when it is a child; absent, it is a root. */
2995
+ treeId?: () => string;
2996
+ /** The pool has a slot for the planner turn it refused this Session. */
2997
+ openTurn?: () => void;
2998
+ /** A child's side of the tree's ledger (ADR-0056 M5). */
2999
+ budget?: NodeBudget;
3000
+ /** What follows a start its budget refuses with nothing live; the node stops, unless a subplanner can ask. */
3001
+ startRefused?: (why: string) => void;
3002
+ /** This node's autonomy, as its mandate reads it (ADR-0056 M6); the config's, at a root. */
3003
+ autonomous?: () => boolean;
3004
+ /** Nothing is deciding where the run goes any more. */
3005
+ decided?: () => void;
3006
+ /** What this node's ancestors protect (ADR-0056 M4). */
3007
+ inheritedProtectedPaths?: () => readonly string[];
3008
+ /** See {@link TaskOrchestratorDeps.nodeRefusal}. */
3009
+ nodeRefusal?: () => string | null;
3010
+ /** See {@link TaskOrchestratorDeps.discardChild}. */
3011
+ discardChild?: (task: Readonly<Task>) => Promise<void>;
2872
3012
  }
2873
3013
  /**
2874
3014
  * The pure scheduler. Owns execution state (the {@link RunState},
@@ -2883,6 +3023,7 @@ interface TaskOrchestratorOptions {
2883
3023
  * and does not unblock dependent work.
2884
3024
  */
2885
3025
  declare class TaskOrchestrator {
3026
+ private readonly deps;
2886
3027
  private config;
2887
3028
  private notifications;
2888
3029
  private store;
@@ -2902,6 +3043,10 @@ declare class TaskOrchestrator {
2902
3043
  private admissions;
2903
3044
  private reviewApproved;
2904
3045
  private autoMergeBlocked;
3046
+ /** Ticks and attempt ends under way; see {@link deciding}. */
3047
+ private decisions;
3048
+ /** The run closed with its merge held, which it would otherwise have made itself. */
3049
+ private mergeOwedAtClose;
2905
3050
  private mergesInFlight;
2906
3051
  private retryCounts;
2907
3052
  /** A Merge all is under way: no ops task starts until it ends (ADR-0020). */
@@ -2940,6 +3085,8 @@ declare class TaskOrchestrator {
2940
3085
  * by its body stays here, so a retry of that task resumes it (L7).
2941
3086
  */
2942
3087
  private readonly carriedLoops;
3088
+ /** The checks of a delegated task that passed with no scheduler to start them (ADR-0055 D8): carried as a loop is, never queued, since one that stops being ready simply stops being owed. */
3089
+ private readonly carriedChecks;
2943
3090
  /** What was last said of a carried loop's approvals, so it is said once. */
2944
3091
  private loopNotice;
2945
3092
  /**
@@ -2953,6 +3100,13 @@ declare class TaskOrchestrator {
2953
3100
  private releasing;
2954
3101
  /** Tasks paused on a usage limit, the run's hold on them, and each runner's wait for its reset (ADR-0018, U1). */
2955
3102
  private readonly limitPauses;
3103
+ /**
3104
+ * Resumes whose reset came with every slot of the tree taken: past their
3105
+ * time, with no timer to fire again, so a tick offers them (ADR-0055 D18).
3106
+ */
3107
+ private readonly keptResumes;
3108
+ /** What a close waits out past the runners: ticks, attempt ends, and work nothing awaits, a Stop's release of a worktree chiefly. */
3109
+ private readonly loose;
2956
3110
  /**
2957
3111
  * The plan's isolation run and the open run's lifecycle (ADR-0013); see
2958
3112
  * {@link IsolationRunController}. Read and acted on there directly, except
@@ -2960,6 +3114,11 @@ declare class TaskOrchestrator {
2960
3114
  * ops tasks and starts the tasks its merge lets through.
2961
3115
  */
2962
3116
  readonly runs: IsolationRunController;
3117
+ /** Where a session runs (ADR-0059): every start reserves here in its claim, and opens here after it. */
3118
+ private readonly leases;
3119
+ /** The tree's slots and usage limits (ADR-0055 D18); a flat run's own. */
3120
+ private readonly pool;
3121
+ private readonly sessionId;
2963
3122
  /** A passed attempt's work onto the integration branch, and the conflict repair a landing may need. */
2964
3123
  private landing;
2965
3124
  private observers;
@@ -2985,12 +3144,14 @@ declare class TaskOrchestrator {
2985
3144
  /** Recent clean output of a task's latest attempt, running or ended; null if it never ran. */
2986
3145
  getLiveOutput(taskId: string, opts: LiveTailOptions): LiveTail | null;
2987
3146
  get storeInstance(): PlanStore;
2988
- approveCheckpoint(taskId: string): void;
3147
+ approveCheckpoint(taskId: string, note?: string): void;
2989
3148
  rejectCheckpoint(taskId: string, reason?: string): void;
2990
3149
  /** What the task's waiting checkpoint asks, whole; undefined once it settles or when none waits. */
2991
3150
  getCheckpointQuestion(taskId: string): string | undefined;
2992
3151
  /** Whether the task waits at a checkpoint its live attempt can still answer. */
2993
3152
  awaitsCheckpoint(taskId: string): boolean;
3153
+ /** A line from Ordewell to a running task (ADR-0057 R6); false where nothing is running. */
3154
+ addressTask(taskId: string, line: string): boolean;
2994
3155
  /**
2995
3156
  * Send a structured task a user message (ADR-0018, M1). Mid-turn it queues
2996
3157
  * behind the turn; a task waiting for input takes it at once and is back in
@@ -3070,8 +3231,9 @@ declare class TaskOrchestrator {
3070
3231
  * admitted and not yet claimed, a force start queued behind its
3071
3232
  * dependencies, a conflict repair owed a slot or the end of a start hold,
3072
3233
  * a task that resumes on its own at its usage limit's reset, a start a
3073
- * dirty checkout parked for Stash and continue to replay, or a loop carried
3074
- * on with no scheduler. A Stop is owed to it, whether or not anything runs.
3234
+ * dirty checkout parked for Stash and continue to replay, or a loop or a
3235
+ * delegated task's check carried on with no scheduler. A Stop is owed to it,
3236
+ * whether or not anything runs.
3075
3237
  * A carried loop a body task stopped starts nothing, so it is not here: see
3076
3238
  * {@link loopCarried}.
3077
3239
  */
@@ -3121,6 +3283,8 @@ declare class TaskOrchestrator {
3121
3283
  * over as before.
3122
3284
  */
3123
3285
  private closeRun;
3286
+ /** A hold is no merge that stopped (ADR-0055 D9): the tick after its check's pass or Skip merges, the run closed or not. */
3287
+ private mergeOwedAfterHold;
3124
3288
  /**
3125
3289
  * Whether closing the run merges what landed, with nobody asked. The run
3126
3290
  * says it has ended before it closes, and a list read in between must not
@@ -3159,6 +3323,13 @@ declare class TaskOrchestrator {
3159
3323
  */
3160
3324
  judgingOnce<T>(read: () => T): T;
3161
3325
  private startRule;
3326
+ /** What tells a check, which reads the runners' modes as an attempt's kind does (ADR-0031). */
3327
+ private get checkRules();
3328
+ private liveCheck;
3329
+ /** A retry takes the task's checks back, and a live one cannot be (ADR-0055 D8). */
3330
+ private refuseBesideLiveCheck;
3331
+ /** What holds every merge but the user's (ADR-0055 D9), named as a refusal names it. */
3332
+ get mergeHold(): readonly Task[];
3162
3333
  /**
3163
3334
  * Why no resolver may be added to this task's conflict now, or null: a
3164
3335
  * conflict has one worker (ADR-0015), so a repair or a resolver that has it
@@ -3338,6 +3509,8 @@ declare class TaskOrchestrator {
3338
3509
  * came from, which a repair follows on from.
3339
3510
  */
3340
3511
  private applyLanding;
3512
+ /** Before anything the landing lets start: what a dependent reads of it should be there (ADR-0057 R6). A flat plan owes nothing, and waits on nothing. */
3513
+ private owedByLanding;
3341
3514
  /**
3342
3515
  * Work that did not land waits on the user — it never fails the task or
3343
3516
  * blocks its dependents — unless its conflict is owed a repair, which takes
@@ -3360,6 +3533,8 @@ declare class TaskOrchestrator {
3360
3533
  private retireResolver;
3361
3534
  /** A planner's remove reaches no {@link releaseTask}: what it took out of the plan since `before` ends its conflicts here. */
3362
3535
  private retireResolversLeft;
3536
+ /** A delegated task the edit took out leaves nothing naming its child's run, so that goes with it, once a child still stopping has (ADR-0055 D15). */
3537
+ private retireChildrenLeft;
3363
3538
  /** A control that would end a conflict waits for the resolver at work on it: a conflict has one worker (ADR-0015). */
3364
3539
  private refuseBesideResolver;
3365
3540
  private landResolved;
@@ -3615,7 +3790,49 @@ declare class TaskOrchestrator {
3615
3790
  * still under way read as `dependencies` rather than `queued`.
3616
3791
  */
3617
3792
  private holdsQueued;
3793
+ /**
3794
+ * Whether a start nobody asked for has to wait for a slot, wherever in the
3795
+ * tree they are held. Asked with no claim to follow, so it reserves to be
3796
+ * refused: the refusal is what has a slot freed elsewhere tick this Session
3797
+ * (ADR-0059 L1).
3798
+ */
3618
3799
  private slotsFull;
3800
+ /** The kept resumes the tree has room for now. A Stop or a retry since has cancelled the rest. */
3801
+ private offerKeptResumes;
3802
+ /** A pool's tick: it has nobody to tell of a failure, so it is said. */
3803
+ offered(): Promise<void>;
3804
+ /** The task's live attempt holds a slot: every one does but a delegated task's (ADR-0055 D18). */
3805
+ holdsSlot(taskId: string): boolean;
3806
+ /** The task's live attempt waits on this Session's planner: what lets a turn pass the limit (ADR-0055 D18). */
3807
+ waitsOnPlanner(taskId: string): boolean;
3808
+ /** A subplanner's turn found its runner's limit, the account's and so the tree's (ADR-0055 D18). */
3809
+ plannerLimited(runner: string, at: Date): void;
3810
+ limitFoundElsewhere(runner: string, at: Date): void;
3811
+ /** A planner turn's lease (ADR-0059 L1): a subplanner's waits its turn for a slot, and a root's is never held (ADR-0055 D18). */
3812
+ reserveTurn(): {
3813
+ lease: ReservedLease;
3814
+ } | {
3815
+ refused: string;
3816
+ };
3817
+ /** The tree's pool, which a child joins (ADR-0055 D18). */
3818
+ get slotPool(): SlotPool;
3819
+ /** A runner let go for the task has not gone yet. */
3820
+ windingDown(taskId: string): boolean;
3821
+ /** Every runner this Session let go has gone. */
3822
+ woundDown(): Promise<void>;
3823
+ /** Nothing the run started is still going, so a close has nothing to wait for. */
3824
+ get leftNothing(): boolean;
3825
+ /** Every runner let go has gone, with what a Stop left to end after it and whatever a backend had running past one, and the run changes git no more. What a next attempt comes back to stays. */
3826
+ allGone(): Promise<void>;
3827
+ /** The Session is closing: the tree stops counting on it. */
3828
+ leaveTree(): void;
3829
+ /** What the run left past its worktrees, as a Clean up gives it up (ADR-0059 L6). */
3830
+ reclaimLeases(): Promise<void>;
3831
+ private leaseRequest;
3832
+ /** What a start of `task` reserves: a delegated task takes a node and no slot, or a parent at its limit deadlocks on the children that need them (ADR-0055 D18). */
3833
+ /** Whether a start of `task` is its child plan's: a delegated task's repair is a plain runner's, which holds a slot and spends tokens (ADR-0055 D14). */
3834
+ private startsChildPlan;
3835
+ private attemptRequest;
3619
3836
  private sayQueuedAtGate;
3620
3837
  /** A start asked for under the whole rule that the rule holds back, in words the user can act on; a turn's hold ends by itself. */
3621
3838
  private sayQueuedHeld;
@@ -3633,6 +3850,27 @@ declare class TaskOrchestrator {
3633
3850
  isAnyFailed(): boolean;
3634
3851
  approveReview(origin?: RunOrigin): Promise<void>;
3635
3852
  tick(): Promise<void>;
3853
+ /**
3854
+ * A tick or an attempt's end is under way, so the run may yet go on or end,
3855
+ * and tell its supervisor which. A child read in that gap has neither, and
3856
+ * would be taken for one that sits on nothing (ADR-0056 M8). Only a child is
3857
+ * read that way, so a flat run's ticks take the turns they always took. A
3858
+ * close waits one out, at any level: its next step may be a release.
3859
+ */
3860
+ private deciding;
3861
+ /** `work`, unchanged, as a close waits for it. */
3862
+ private underWay;
3863
+ private ticked;
3864
+ /** Why what is ready cannot start, with nothing live to free room for it: no event will tick this again (ADR-0056 M5). Null otherwise. */
3865
+ get refusedStart(): string | null;
3866
+ /**
3867
+ * The run moves with nobody asked: something is at work, a reset resumes it,
3868
+ * or a slot elsewhere in the tree starts what is ready (ADR-0055 D18). What
3869
+ * tells a child that sits on nothing from one that waits (ADR-0056 M8).
3870
+ */
3871
+ get movesUnasked(): boolean;
3872
+ /** What is ready now, for a reader that asks far more often than a tick does, so it logs nothing. */
3873
+ private readyNow;
3636
3874
  /** One of a tick's starters: takes the next of `waiting` as each start it made has attached. */
3637
3875
  private startReady;
3638
3876
  /**
@@ -3646,6 +3884,13 @@ declare class TaskOrchestrator {
3646
3884
  * as a scheduled one is held, on whatever the start rule waits for.
3647
3885
  */
3648
3886
  private settleRun;
3887
+ /**
3888
+ * What holds this run back is another Session's, not the user's: a slot, or a
3889
+ * usage limit a sibling found. Nothing here will tick for it, and what frees
3890
+ * it does, so no child parks waiting on a user (ADR-0055 D18). A flat run has
3891
+ * no such wait: an idle Session of one has its slots free.
3892
+ */
3893
+ private waitsOnTree;
3649
3894
  private waitOnUser;
3650
3895
  /**
3651
3896
  * A loop's check failed (ADR-0045). With `failed` evidence and rounds left,
@@ -3683,8 +3928,10 @@ declare class TaskOrchestrator {
3683
3928
  private ownsNextRound;
3684
3929
  /** A check between rounds that nothing will run: it waits on a person, its failure no longer one its loop answers (ADR-0045 L5, ADR-0053). */
3685
3930
  loopStranded(taskId: string): boolean;
3686
- /** A tick starts something: the scheduler the plan, or the loops carried on, their tasks alone. */
3931
+ /** A tick starts something: the scheduler the plan, or what is carried on, those tasks alone. */
3687
3932
  private get startsOnTick();
3933
+ /** What is owed with no scheduler: the loops carried on, and the checks of a delegated task that ended. */
3934
+ private carriedOn;
3688
3935
  /**
3689
3936
  * The carried loops with work owed now: a check between rounds whose loop
3690
3937
  * goes on, or a round waiting to start. None under a scheduler, which owes
@@ -3692,17 +3939,29 @@ declare class TaskOrchestrator {
3692
3939
  * One a body task stopped is not here until that task passes (L7).
3693
3940
  */
3694
3941
  private loopsCarriedOn;
3942
+ /**
3943
+ * The checks of a delegated task that ended, ready to start. Whatever start
3944
+ * ended it, a run with no scheduler would otherwise close under them with
3945
+ * their merge still held (ADR-0055 D8, D9). None under a scheduler, which
3946
+ * owes them itself, or while a usage limit holds the run. One behind a
3947
+ * failure or other unfinished work is not here: nothing is moving towards it.
3948
+ */
3949
+ private checksCarriedOn;
3950
+ /** A delegated task passed, and nothing scheduled follows it. A Stop since the start that landed it leaves its checks where they are: a resolver's landing outlives one, with this task waiting on the user and no attempt to stop. */
3951
+ private carryChecks;
3695
3952
  /**
3696
3953
  * The run that carried them is over, by a Stop or by what gives the run up
3697
3954
  * or clears it up: a retry of a body task that passes runs that task alone.
3698
3955
  */
3699
3956
  private endCarriedLoops;
3700
- /** What a tick may start with no scheduler: the loops carried on, and nothing that depends on them. */
3957
+ /** What a tick may start with no scheduler: what is carried on, and nothing that depends on it. */
3701
3958
  carriedTasks(): Set<string>;
3702
3959
  /**
3703
- * A task of a carried loop outside the user's approval record waits as any
3704
- * does (ADR-0045, Consent). Said here, once, as well as listed: a run they
3705
- * started one task of says nothing else of why the loop's next step does not come.
3960
+ * A carried task outside the user's approval record waits as any does
3961
+ * (ADR-0045, Consent): starting a delegated task consents to that task alone,
3962
+ * and a script check's consent is to its body (ADR-0035). Said here, once, as
3963
+ * well as listed: a run they started one task of says nothing else of why the
3964
+ * next step does not come.
3706
3965
  */
3707
3966
  private sayUnapprovedInLoop;
3708
3967
  /** Skipped work is not completed work, so a run with any says so rather than that everything completed. */
@@ -3719,16 +3978,23 @@ declare class TaskOrchestrator {
3719
3978
  /**
3720
3979
  * Why a start may not take its claim now, or null when it may: a Stop came
3721
3980
  * since it was admitted (ADR-0029), the task left the plan or is being
3722
- * marked complete, a resolver has its conflict, so a start of it would be a
3981
+ * marked complete, this node may start nothing (ADR-0055 D16, ADR-0056 M10), a resolver has its conflict, so a start of it would be a
3723
3982
  * second worker on it (ADR-0015), a revert is taking back work it depends on
3724
3983
  * or skipped it since (ADR-0054), it
3725
- * is outside the user's approval record (ADR-0035), or every slot is taken.
3726
- * Read in the same synchronous step as the claim it
3727
- * guards, so nothing can change in between. `revives` is a start asked of a
3728
- * task already skipped, as a force start or a continue may be: only that runs
3729
- * one again. A retry has made it pending by the time it claims.
3984
+ * is outside the user's approval record (ADR-0035), its node's budget has no
3985
+ * room for it (ADR-0056 M5), or its lease cannot be
3986
+ * reserved, every slot being taken (ADR-0059 L1). Read in the same
3987
+ * synchronous step as the claim it guards, so nothing can change in between.
3730
3988
  */
3731
3989
  private claimRefusal;
3990
+ /**
3991
+ * All of {@link claimRefusal} short of the lease: what a refusal keeps nobody
3992
+ * for. Whatever else a start spends is checked here, since every attempt
3993
+ * passes it. `revives` is a start asked of a task already skipped, as a force
3994
+ * start or a continue may be: only that runs one again. A retry has made it
3995
+ * pending by the time it claims.
3996
+ */
3997
+ private startRefusal;
3732
3998
  /** An explicit start's refusal, before it changes anything: its caller's answer, as a {@link TaskControlError}. */
3733
3999
  private refuseClaim;
3734
4000
  /** An explicit start's claim, once {@link refuseClaim} let it through: its spawn, or the refusal as a rejection. */
@@ -3736,8 +4002,10 @@ declare class TaskOrchestrator {
3736
4002
  /**
3737
4003
  * The one claim every attempt start passes — the scheduler's, a run or force
3738
4004
  * start, a retry's, a continue, a resume, a repair. Checked and taken in one
3739
- * synchronous step ({@link claimRefusal}, then {@link Attempts.start}), so
3740
- * no other start can take the same task or slot.
4005
+ * synchronous step ({@link startRefusal}, the lease's `reserve`, then
4006
+ * {@link Attempts.start}), so no other start can take the same task or slot.
4007
+ * `reserve` rather than `refusal` answers for the slot, since being turned
4008
+ * down is what keeps this Session for the slot that frees (ADR-0059 L1).
3741
4009
  * The task is read here, as it stands: a start that awaited first — its run
3742
4010
  * opening, the user's answer — runs what the plan holds now, not what it
3743
4011
  * held when it was asked for. A task already claimed is not claimed again:
@@ -3755,191 +4023,6 @@ declare class TaskOrchestrator {
3755
4023
  continueBlockedRun(how: 'stash' | 'shared' | 'reread'): Promise<void>;
3756
4024
  }
3757
4025
 
3758
- /**
3759
- * The effect allowlist (ADR-0035): the user's standing approval for named
3760
- * effects, beside the planner allowlist, which names reads. It pre-approves a
3761
- * script task whose `commands` are all covered; it never covers a `script`
3762
- * body, whose shell text no rule can be matched against.
3763
- *
3764
- * A rule is matched against the whole command, every word in order and flags
3765
- * included, because an effect's flags change what it does: `git push origin
3766
- * feature/*` covers `git push origin feature/login`, never the same push with
3767
- * `--force`. `*` stands for one word, or inside a word for any run of
3768
- * characters, as in the planner allowlist, except that it never stands for
3769
- * what changes the effect: a flag (`--force`, and `/force` on Windows), a
3770
- * refspec's force or delete (`+main`, `:main`), or a `:` outside a URL
3771
- * (`feature/x:main`, which pushes to `main`). The rule writes any of those
3772
- * itself. A rule spells out its operation before any `*`, so
3773
- * `az webapp config * set` is no rule at all.
3774
- *
3775
- * Exclusions are the planner allowlist's `!` entries, with any `!` entry
3776
- * written here; an exclusion wins. A command exclusion is matched as the
3777
- * planner allowlist matches it; a path or URL exclusion holds for an argument
3778
- * that names an absolute path or URL it covers, whole or after a `=`, `@`,
3779
- * `:` or `,` or a short flag (`--url=…`, `-d @…`). A relative path is not
3780
- * resolved, since the body does not say where it runs. A program named by its
3781
- * path is a file the repository ships (ADR-0026), so it is never covered.
3782
- * There are no built-in rules: a default would pre-approve an effect the user
3783
- * never chose.
3784
- */
3785
- interface EffectRule {
3786
- text: string;
3787
- binary: string;
3788
- words: RuleWord[];
3789
- }
3790
- interface EffectRules {
3791
- rules: EffectRule[];
3792
- exclusions: PlannerAllowlist;
3793
- platform: NodeJS.Platform;
3794
- /** The entries that are no rule, each with the warning that says why it is ignored. */
3795
- ignored: {
3796
- rule: string;
3797
- warning: string;
3798
- }[];
3799
- }
3800
- /**
3801
- * `entries` are the effect allowlist's; `plannerEntries` the planner allowlist
3802
- * in force, whose `!` entries hold here too; `platform` the one the commands
3803
- * run on, which decides how a flag is spelled and how a path resolves.
3804
- */
3805
- declare function parseEffectAllowlist(entries: readonly string[], plannerEntries?: readonly string[], platform?: NodeJS.Platform): EffectRules;
3806
- /**
3807
- * The rules, by name, that cover every command of a `commands` body, or null
3808
- * when one command is not covered, an exclusion holds for one, or the body is
3809
- * a `script`.
3810
- */
3811
- declare function effectRuleFor(list: EffectRules, body: ScriptBody): string | null;
3812
-
3813
- /**
3814
- * The mandate (ADR-0056): what a node may do without a person, and within what
3815
- * limits. The root's is derived on each read from today's settings and never
3816
- * saved; a child's is one pure function of its parent's and its request, which
3817
- * can only narrow what the parent may hand on. Authority only: capability stays
3818
- * at the boundary a session runs in, and a prompt enforces nothing.
3819
- */
3820
- declare const GRANTS: readonly ["observe", "steer", "control", "replan", "approve", "delegate", "effect", "research", "merge"];
3821
- /** Each is a set of things a node may do unasked (M2). */
3822
- type Grant = (typeof GRANTS)[number];
3823
- /** What a mandate could not finish without: a child holds these unasked, and `narrow` refuses one that would lack any (M2). */
3824
- declare const FLOOR_GRANTS: readonly Grant[];
3825
- /** The shape of a tree, by ADR-0055 D19: refused past the hard cap, never dropped. */
3826
- declare const SHAPE_LIMITS: {
3827
- readonly depth: {
3828
- readonly default: 2;
3829
- readonly hard: 4;
3830
- };
3831
- readonly delegatedTasks: {
3832
- readonly default: 8;
3833
- readonly hard: 30;
3834
- };
3835
- readonly tasksPerPlan: {
3836
- readonly default: 30;
3837
- readonly hard: 100;
3838
- };
3839
- };
3840
- interface MandateLimits {
3841
- /** The node's token budget; the root's is the tree's, from the delegation setting (M3, M5). */
3842
- tokens: number;
3843
- /** Null: no limit, which only the root has. */
3844
- wallClockMs: number | null;
3845
- /** How many levels may still open below this node. */
3846
- depth: number;
3847
- delegatedTasks: number;
3848
- tasksPerPlan: number;
3849
- runners: readonly RunnerId[];
3850
- /** The models a runner is held to; a runner not named may use any it offers, as the user's allowlist reads. */
3851
- models: Readonly<Record<RunnerId, readonly string[]>>;
3852
- }
3853
- interface MandateExclusions {
3854
- protectedPaths: readonly string[];
3855
- }
3856
- interface Mandate {
3857
- /** What this node may do. */
3858
- grants: ReadonlySet<Grant>;
3859
- /** What it may hand a child. */
3860
- grantable: ReadonlySet<Grant>;
3861
- /** Read in place of the global level. */
3862
- autonomy: 'full' | 'guarded';
3863
- limits: MandateLimits;
3864
- /** These only accumulate. */
3865
- exclusions: MandateExclusions;
3866
- /** One per level, each with its own `!` rules: a command must match at every level (M4). */
3867
- effectRules: readonly EffectRules[];
3868
- }
3869
- /** What the root's mandate is read from, each where it lives today (M1). */
3870
- interface RootSettings {
3871
- /** The run's own copy: where a guard or a failure lowers it, and the user's change lands. */
3872
- level: SupervisorLevel;
3873
- autorun: boolean;
3874
- autonomous: boolean;
3875
- /** The tree's budget; 0 is delegation off (ADR-0055 D21). */
3876
- delegationTokens: number;
3877
- /** The effect allowlist, absent where nothing was read. */
3878
- effects?: EffectRules;
3879
- runners: readonly RunnerId[];
3880
- models: Readonly<Record<RunnerId, readonly string[]>>;
3881
- }
3882
- /** What a delegated task asks of the mandate it is given. `grants` names what it asks for past the floor; absent asks for nothing. */
3883
- interface MandateRequest {
3884
- /** The level of the node narrowing, the root being 0: the one a refusal names. */
3885
- level: number;
3886
- grants?: readonly Grant[];
3887
- autonomy?: Mandate['autonomy'];
3888
- /** A delegated task must carry one (M5). */
3889
- tokens?: number;
3890
- wallClockMs?: number;
3891
- depth?: number;
3892
- delegatedTasks?: number;
3893
- tasksPerPlan?: number;
3894
- runners?: readonly RunnerId[];
3895
- models?: Readonly<Record<RunnerId, readonly string[]>>;
3896
- protectedPaths?: readonly string[];
3897
- effectRules?: EffectRules;
3898
- }
3899
- interface MandateRefusal {
3900
- level: number;
3901
- field: string;
3902
- asked: string;
3903
- there: string;
3904
- }
3905
- type Narrowed = {
3906
- ok: true;
3907
- mandate: Mandate;
3908
- } | {
3909
- ok: false;
3910
- refusal: MandateRefusal;
3911
- };
3912
- declare function mandateRefusalText({ level, field, asked, there }: MandateRefusal): string;
3913
- /**
3914
- * The root's mandate, as it stands now (M1): built afresh on each read, so an
3915
- * Act that a guard lowered reads Watch at the next check, and nothing saved
3916
- * could disagree with the settings.
3917
- */
3918
- declare function rootMandate(settings: RootSettings): Mandate;
3919
- /**
3920
- * What holds at a node now (M1): its saved mandate within every ancestor's
3921
- * `grantable` as it stands, so a change at the root reaches the subtree at the
3922
- * next check. This reading intersects and refuses nothing: the floor binds
3923
- * {@link narrow}, which writes a new child, not this.
3924
- */
3925
- declare function heldMandate(saved: Mandate, ancestors: readonly Mandate[]): Mandate;
3926
- /**
3927
- * A child's mandate (M4), or the refusal that names the level that refused,
3928
- * the field, what was asked and what is there. Pure, and written to be proved:
3929
- * no result is wider than its parent. `parent.limits.tokens` and `wallClockMs`
3930
- * are what the parent has left, which is the caller's to read.
3931
- */
3932
- declare function narrow(parent: Mandate, request: MandateRequest): Narrowed;
3933
- /** What a node reads of its own authority, at each check (M1). */
3934
- interface NodeAuthority {
3935
- /** How far below the root, which is 0 and where a person remains. */
3936
- readonly depth: number;
3937
- /** The mandate that holds now. `level` is what the root's is read at; a child's does not depend on it. */
3938
- mandate(level: SupervisorLevel): Mandate;
3939
- }
3940
- /** The root: its settings read as each check asks, the level included, which each caller names. */
3941
- declare function rootAuthority(settings: () => Omit<RootSettings, 'level'>): NodeAuthority;
3942
-
3943
4026
  /** One call at the command seam, as every route words it (ADR-0029). Task references are resolved here. */
3944
4027
  type ExecutionCommand = {
3945
4028
  command: 'run_plan';
@@ -3981,6 +4064,7 @@ type ExecutionCommand = {
3981
4064
  /** Merge all: the run's landed work into the user's branch (ADR-0013). */
3982
4065
  | {
3983
4066
  command: 'merge_all';
4067
+ pastChecks?: true;
3984
4068
  }
3985
4069
  /** End a task as skipped: nothing it did lands, and its dependents go ahead without it once the work it waited on is done. `reason` is required of anyone but the user. */
3986
4070
  | {
@@ -4057,6 +4141,7 @@ type ExecutionCommand = {
4057
4141
  task: string;
4058
4142
  answer: 'approve' | 'reject';
4059
4143
  reason?: string;
4144
+ raiseTokens?: number;
4060
4145
  }
4061
4146
  /** The user signs off the plan's review: every task in it is approved, as executing it approves them, and it runs. */
4062
4147
  | {
@@ -4103,6 +4188,11 @@ type CommandPayload =
4103
4188
  | {
4104
4189
  kind: 'revert';
4105
4190
  result: IsolationRevertResult;
4191
+ }
4192
+ /** The user's `merge_all` asked first: what it would merge past are these checks, named as the plan names them, and `pastChecks` is the yes (ADR-0055 D9). */
4193
+ | {
4194
+ kind: 'merge_hold';
4195
+ checks: string[];
4106
4196
  };
4107
4197
  /**
4108
4198
  * What a call came to: `message` is written for the caller, a model included,
@@ -4194,11 +4284,15 @@ interface ExecutionHost {
4194
4284
  admitStart(): StartAdmission;
4195
4285
  /** The task's attempt is live: starting, running or landing. */
4196
4286
  running(taskId: string): boolean;
4287
+ /** Its live attempt holds a slot, as a delegated task's does not (ADR-0055 D18). */
4288
+ holdsSlot(taskId: string): boolean;
4197
4289
  /** A conflict repair of the task will start by itself: there is a start to withdraw (ADR-0015). */
4198
4290
  repairOwed(taskId: string): boolean;
4199
4291
  maxParallel(): number;
4200
4292
  /** The run waits on the user to stash or skip isolation before anything starts. */
4201
4293
  runBlocked(): boolean;
4294
+ /** Why nothing may start here, as every surface says it: another host holds the session (ADR-0049). */
4295
+ heldRefusal(): string | null;
4202
4296
  /**
4203
4297
  * Every reason the task cannot start now, from the scheduler's one start
4204
4298
  * rule (ADR-0034). Takes the task, not its id: the one the seam located.
@@ -4236,6 +4330,8 @@ interface ExecutionHost {
4236
4330
  /** A start cleared its last check and is about to act; the user's ends their Stop (ADR-0029). */
4237
4331
  startAsked(actor: ExecutionActor): void;
4238
4332
  retryTask(taskId: string, actor: ExecutionActor): Promise<void>;
4333
+ /** Whether the task is a delegated task's check (ADR-0055 D8). */
4334
+ checksDelegatedTask(taskId: string): boolean;
4239
4335
  /** End the task as skipped, landing nothing of it; see {@link TaskOrchestrator.skipTask}. */
4240
4336
  skipTask(taskId: string, reason: string | undefined, actor: ExecutionActor): Promise<void>;
4241
4337
  continueTask(taskId: string, message: string, actor: ExecutionActor): Promise<void>;
@@ -4252,7 +4348,11 @@ interface ExecutionHost {
4252
4348
  cancelResume(taskId: string): Promise<boolean>;
4253
4349
  /** The task waits at a checkpoint, and a runner is left to hear the answer. */
4254
4350
  awaitsCheckpoint(taskId: string): boolean;
4255
- answerCheckpoint(taskId: string, approve: boolean, reason?: string): void;
4351
+ answerCheckpoint(taskId: string, approve: boolean, reason?: string, note?: string): void;
4352
+ /** The checks that hold every merge but the user's (ADR-0055 D9), each as its row names it. */
4353
+ mergeHold(): readonly string[];
4354
+ /** A raise for a delegated task's child, from what this node has left (ADR-0056 M5). Null when it took; otherwise why not. */
4355
+ raiseChild(taskId: string, tokens: number): Promise<string | null>;
4256
4356
  /** Sign off the review: the plan is the user's, and runs. */
4257
4357
  approveReview(): Promise<void>;
4258
4358
  /** Answer a runner's request, as its card does; false once it was answered or withdrawn. */
@@ -4354,7 +4454,7 @@ declare class ExecutionCommands {
4354
4454
  * it, and the supervisor keeps its own tools. `confirmed` is the user's yes to
4355
4455
  * an option's `confirm`, which a choice cannot imply.
4356
4456
  */
4357
- answerEscalation(id: string, option: string, note?: string, confirmed?: boolean): Promise<CommandOutcome>;
4457
+ answerEscalation(id: string, option: string, note?: string, confirmed?: boolean, raiseTokens?: number): Promise<CommandOutcome>;
4358
4458
  /** A valid, consented call, from the user's answer where one is needed to the act and its record. */
4359
4459
  private carryOut;
4360
4460
  /**
@@ -4377,8 +4477,9 @@ declare class ExecutionCommands {
4377
4477
  private startScript;
4378
4478
  /**
4379
4479
  * Why a script task cannot start as `run_script` would start it, but for its
4380
- * merge gate, at which it waits: a dependency that failed or has not
4381
- * finished, a Merge all under way, every slot taken. Null when it can.
4480
+ * merge gate, at which it waits: another host holding the session, a
4481
+ * dependency that failed or has not finished, a Merge all under way, every
4482
+ * slot taken. Null when it can.
4382
4483
  */
4383
4484
  private scriptUnready;
4384
4485
  /**
@@ -4731,6 +4832,18 @@ interface PlanChangeScope {
4731
4832
  catalog(runners: RunnerId[]): AssignmentCatalog & {
4732
4833
  runnerModes?: Record<RunnerId, RunnerModeInfo[]>;
4733
4834
  };
4835
+ /**
4836
+ * The node a change is made on, with what it holds now and how far below the
4837
+ * root it stands, read as each check runs (ADR-0056 M4). Absent or undefined,
4838
+ * nothing may delegate: a one-shot planner has no node, and a build that does
4839
+ * not offer delegation reads no mandate for it.
4840
+ */
4841
+ node?(): {
4842
+ mandate: Mandate;
4843
+ level: number;
4844
+ } | undefined;
4845
+ /** Whether a script can run sealed here (ADR-0036 S6): a change that brings a read-only one waits on this, as an attached skill waits on its lookup. */
4846
+ canSeal?(): Promise<boolean>;
4734
4847
  /** Where attached skill names are looked up; absent, none are. */
4735
4848
  skills?(): SkillLookup;
4736
4849
  /** The handles the open turn's committed edits gave, each with its task's id, for a later batch to name them by (ADR-0022 P2). */
@@ -4771,12 +4884,41 @@ declare class PlanChangeCheck {
4771
4884
  checkNow<P extends CheckPhase>(change: PlannerChange<P>, phase: P): CheckedChange;
4772
4885
  /** A plan's JSON read into tasks as it is submitted; nothing in it reads the plan. */
4773
4886
  private read;
4887
+ /** Whether this node may write a delegated task: read on every route, since a parse that took one anyway would only be refused later (ADR-0055 D21). */
4888
+ private delegation;
4889
+ /** Only such a change waits on the probe, so a flat plan's check takes the turns it always took. */
4890
+ private bringsSealedScript;
4774
4891
  private rules;
4775
4892
  /** What a child's mandate leaves out of this change: `replan` for any, and `research` for a research task it brings. */
4776
4893
  private ungranted;
4894
+ /**
4895
+ * What a change leaves past this node's limits (ADR-0055 D2, D19): each
4896
+ * delegated task it wrote narrowed as it is written (ADR-0056 M4), so a plan
4897
+ * never holds what its carve would refuse, and a child held to what a child
4898
+ * plan may be. Only what this change wrote: what stands in the plan is not
4899
+ * this edit's to answer for. A root plan has no task limit, so a flat run
4900
+ * does not change.
4901
+ */
4902
+ private pastMandate;
4777
4903
  private refused;
4778
4904
  }
4779
4905
 
4906
+ /** What a planner that may delegate is told of its limits (ADR-0055 D19). */
4907
+ interface PlannerDelegation {
4908
+ tokens: number;
4909
+ delegatedTasks: number;
4910
+ /** What a delegated task of this plan can ask for past the floor. */
4911
+ grantable: readonly string[];
4912
+ }
4913
+ /** What a subplanner is told of what it was given (ADR-0055 D4). */
4914
+ interface SubplannerBrief {
4915
+ /** Its own and its ancestors': a change to one fails its verdict (D7). */
4916
+ protectedPaths: readonly string[];
4917
+ readOnly: boolean;
4918
+ /** The runner it plans on. */
4919
+ runner: RunnerId;
4920
+ }
4921
+
4780
4922
  /**
4781
4923
  * How much of its model's window an API planner's next call may fill. The
4782
4924
  * check runs before every call, against the model configured at that moment,
@@ -4865,6 +5007,8 @@ interface CliAgentAiServiceDeps extends Partial<AgentProcessDeps> {
4865
5007
  projectServerApprovals?: ProjectServerApprovals;
4866
5008
  /** What the planner is shown of its context and budgets at the start of each turn. */
4867
5009
  turnBudgets?: TurnBudgets;
5010
+ /** Open the execution lease of the turn a process starts for (ADR-0059 L1); absent, it runs at `workspaceRoot`. */
5011
+ openLease?: (signal: AbortSignal) => Promise<OpenLease> | undefined;
4868
5012
  }
4869
5013
  /**
4870
5014
  * A coding agent driven as Ordewell's planner (ADR-0009).
@@ -4891,6 +5035,7 @@ declare class CliAgentAiService implements IAiService {
4891
5035
  private readonly mcpServer;
4892
5036
  private readonly turnBudgets;
4893
5037
  private readonly projectServerApprovals;
5038
+ private readonly openLease;
4894
5039
  private activeAbort;
4895
5040
  /** See {@link compactions}. */
4896
5041
  private compacted;
@@ -5087,6 +5232,14 @@ interface PlannerToolsOffer {
5087
5232
  /** The tools a child's grants leave out of its list, fixed as its session starts (ADR-0056 M6); the root's list is not filtered. */
5088
5233
  withheld?: readonly string[];
5089
5234
  handler: PlannerToolHandler;
5235
+ /** Its `submit_plan` takes a delegated task (ADR-0055 D1). */
5236
+ delegates?: boolean;
5237
+ /** A subplanner's: its token serves `task_complete` and `checkpoint` too, for the handler's `taskComplete` and `checkpoint` (D5). */
5238
+ subplanner?: boolean;
5239
+ /** Its harness's own subagents are withheld: nothing refuses what they spend, so a subplanner has them only with `research` (ADR-0056 M6). */
5240
+ noSubagents?: true;
5241
+ /** What a subplanner's process loads of the user's setup: its tasks' setting holds for it too (ADR-0057 R11). */
5242
+ runnerConfig?: TaskRunnerConfig;
5090
5243
  }
5091
5244
  /**
5092
5245
  * Everything the conversation loop needs to start planning (ADR-0002).
@@ -5127,6 +5280,10 @@ interface ConversationRequest {
5127
5280
  */
5128
5281
  initialMessage?: string;
5129
5282
  plannerTools?: PlannerToolsOffer;
5283
+ /** Teaches the planner delegation, only where its mandate holds `delegate` (ADR-0055 D21). */
5284
+ delegation?: PlannerDelegation;
5285
+ /** The prompt is a subplanner's, whose parent stands where the user does (ADR-0055 D4). */
5286
+ subplanner?: SubplannerBrief;
5130
5287
  /**
5131
5288
  * The session's notice channel, for a warning the planner's surface should
5132
5289
  * show once (ADR-0036 N4: the native-shell setting is on but the harness is
@@ -5539,6 +5696,8 @@ interface PlannerConversationHost {
5539
5696
  goal(): string;
5540
5697
  aiService(): IAiService;
5541
5698
  onProgress(progress: ResearchProgress): void;
5699
+ /** The planner is a subplanner: its prompt told it the catalog, and a plan that has settled leaves it only its report (ADR-0057 R11). */
5700
+ subplanner?(): boolean;
5542
5701
  /** Fresh discovery, allowlist-filtered the way the system prompt shows it. */
5543
5702
  opening(runners: RunnerId[]): Promise<ConversationOpening>;
5544
5703
  /** What the per-turn catalog block and every read draw from, as of now. */
@@ -5581,10 +5740,12 @@ interface PlannerConversationHost {
5581
5740
  capturePrd(text: string): void;
5582
5741
  /** The planner turn was stopped or abandoned: whatever it is waiting on (a parked approval) will never be answered. */
5583
5742
  turnAborted(): void;
5584
- /** A planner turn is about to take the conversation, which nothing holds now. */
5743
+ /** A planner turn is about to take the conversation, which nothing holds now. Throws why it may not. */
5585
5744
  turnClaiming?(): void;
5586
5745
  /** A planner turn let the conversation go, so the next one may take it. */
5587
5746
  turnReleased?(): void;
5747
+ /** A turn was let go without ending: abandoned with its plan, or hung. */
5748
+ turnDropped?(): void;
5588
5749
  /**
5589
5750
  * A turn ended on the planner's usage limit, which it reported as `said`:
5590
5751
  * what is sent again, and when. `answered`: a planner tool's change in the
@@ -5712,6 +5873,7 @@ declare class PlannerConversation {
5712
5873
  private readonly briefing;
5713
5874
  private readonly blocks;
5714
5875
  private live;
5876
+ private readonly idling;
5715
5877
  private openTurn;
5716
5878
  /** When the live context's newest turn opened; null with no live context. */
5717
5879
  private requestedAt;
@@ -5733,10 +5895,14 @@ declare class PlannerConversation {
5733
5895
  get lastRequestAt(): number | null;
5734
5896
  /** Whether a planner turn holds the conversation. */
5735
5897
  get isTurnInFlight(): boolean;
5898
+ /** Resolves once no turn holds the conversation: what a subtree's stop waits on (ADR-0055 D16). */
5899
+ idle(): Promise<void>;
5900
+ private settleIdle;
5736
5901
  /** Stop the planner turn in flight; it settles as stopped. False when none is. */
5737
5902
  stopTurn(by?: 'force_send'): boolean;
5738
5903
  /**
5739
- * Drop the planner turn in flight together with the plan it was answering:
5904
+ * Drop the planner turn in flight together with the plan it was answering,
5905
+ * or the session another host took from under it:
5740
5906
  * it is stopped, whatever it settles is discarded, and the conversation is
5741
5907
  * free at once rather than when the abandoned turn finishes unwinding.
5742
5908
  */
@@ -6184,21 +6350,6 @@ declare function resolveTaskRef<T extends RefTask<T>>(tasks: readonly T[], ref:
6184
6350
  */
6185
6351
  declare function resolveUserTaskRef<T extends RefTask<T>>(tasks: readonly T[], ref: string | number): T | undefined;
6186
6352
 
6187
- interface SessionMeta {
6188
- id: string;
6189
- goal: string;
6190
- runners: RunnerId[];
6191
- taskCount: number;
6192
- status: PlanStatus;
6193
- createdAt: string;
6194
- updatedAt: string;
6195
- }
6196
- interface SessionData {
6197
- meta: SessionMeta;
6198
- /** The full plan state — including conversationHistory, prdMarkdown, and queuedMessages. */
6199
- plan: LegacyPlanState;
6200
- }
6201
-
6202
6353
  /** One repo that kept "Merge all" from touching anything, as part of one line. */
6203
6354
  declare function blockNotice({ repo, reason, files, expected, actual, rebasing }: IsolationMergeBlock): string;
6204
6355
  /** Every repo's reason, in the order "Merge all" named them, so a surface can list them or join them. */
@@ -6260,9 +6411,9 @@ interface IsolationPreviewNotice {
6260
6411
  declare function describeIsolationPreview(preview: IsolationPreview): IsolationPreviewNotice;
6261
6412
 
6262
6413
  /**
6263
- * Pure, no-imports helper so a browser surface can format a conflict's files
6264
- * without pulling `isolationRecord` — and its `path` import — into a webview
6265
- * bundle. Kept out of `isolationRecord.ts` for exactly that reason.
6414
+ * Pure helper so a browser surface can format a conflict's files without
6415
+ * pulling `isolationRecord` — and its `path` import — into a webview bundle.
6416
+ * Kept out of `isolationRecord.ts` for exactly that reason.
6266
6417
  */
6267
6418
  declare function capConflictFiles(files: string[], max?: number): string;
6268
6419
 
@@ -6333,6 +6484,12 @@ declare function repoResultLines(handoff: {
6333
6484
  landed: readonly unknown[];
6334
6485
  }[];
6335
6486
  }): string[];
6487
+ /**
6488
+ * What a Merge all asks the user to confirm when a delegated task's check has
6489
+ * not passed (ADR-0055 D9), the checks named as the plan names them. Every
6490
+ * surface asks with these words, and the inbox's option carries them.
6491
+ */
6492
+ declare function mergePastChecksConfirmation(checks: readonly string[]): string;
6336
6493
 
6337
6494
  /** What the user last picked for one planner backend. */
6338
6495
  interface PlannerModelChoice {
@@ -6500,6 +6657,13 @@ interface UserSettings {
6500
6657
  * no surface offers it.
6501
6658
  */
6502
6659
  taskRunnerConfig?: TaskRunnerConfig;
6660
+ /**
6661
+ * Run tasks in containers (ADR-0059 L5), as `containerSettings` reads it:
6662
+ * absent means local worktrees. Kept as written, since one it cannot use must
6663
+ * refuse a task, not fall back to the host. Set in the file only: no surface
6664
+ * offers it.
6665
+ */
6666
+ containers?: unknown;
6503
6667
  }
6504
6668
  /**
6505
6669
  * The model allowlist, remembered planner models and enabled runners are shared
@@ -6700,7 +6864,9 @@ type DaemonErrorCode = 'session_not_found' | 'task_not_found' | 'no_plan' | 'alr
6700
6864
  /** The selected planner cannot plan yet (no key, no base URL, no model); `plannerNotReady` says which. */
6701
6865
  | 'planner_not_ready'
6702
6866
  /** The request asked for something the plan, task or conversation does not allow. */
6703
- | 'refused' | 'checkpoint_not_waiting' | 'approval_not_outstanding'
6867
+ | 'refused' | 'checkpoint_not_waiting'
6868
+ /** Merge all would merge work no check has passed; the message names the checks, and a surface asks before sending `pastChecks` (ADR-0055 D9). */
6869
+ | 'merge_past_check' | 'approval_not_outstanding'
6704
6870
  /** Another live Ordewell host runs this session (ADR-0049); `sessionHeld` names it. */
6705
6871
  | 'session_held';
6706
6872
  /** The body of every non-2xx answer a route gives. */
@@ -7862,6 +8028,48 @@ declare function confirmedOverride(command: ExecutionCommand, seen?: TaskStatus)
7862
8028
  /** Change versus ops can still be flipped (ADR-0020): an AI task that has not started. */
7863
8029
  declare function opsEditable(task: ActionSubject): boolean;
7864
8030
 
8031
+ /**
8032
+ * What a delegated task's row says (ADR-0055 D16, D20), in sentence case, each
8033
+ * part null where there is nothing to say. Surfaces pick the glyphs and the
8034
+ * layout, and derive none of the facts.
8035
+ */
8036
+ interface TaskRowDelegated {
8037
+ goal: string | null;
8038
+ /** "1.3M of 2M tokens left". */
8039
+ tokens: string | null;
8040
+ /** "2 of 4 done, 1 running". */
8041
+ counts: string | null;
8042
+ /** That a Stop is still winding the subtree down. */
8043
+ stopping: string | null;
8044
+ }
8045
+ declare const STOPPING_LABEL = "Stopping: its child plan is still winding down.";
8046
+ declare function delegatedRowView(task: {
8047
+ type: string;
8048
+ goal?: string;
8049
+ delegation?: DelegatedRow;
8050
+ handoff?: HandoffDigest;
8051
+ }): TaskRowDelegated | null;
8052
+ /** One finding of a delegated task's report, as a surface draws it. */
8053
+ interface SubtreeItemView {
8054
+ /** The task and finding that name it, as a reader says them; null for a subplanner's own, which names no task. */
8055
+ reference: string | null;
8056
+ /** "F1 · Major · title". */
8057
+ heading: string;
8058
+ detail?: string;
8059
+ }
8060
+ /** What a delegated task's report adds to a handoff (ADR-0057 R2), worded once for every surface. */
8061
+ interface SubtreeView {
8062
+ /** "3 done, 1 failed · 3 commits · 1.2M tokens · 1 major, 2 minor findings below". */
8063
+ summary: string;
8064
+ /** Changed outside its declared scope, or null (ADR-0056 M7). */
8065
+ outsideScope: string | null;
8066
+ /** What the parent must decide. */
8067
+ open: SubtreeItemView[];
8068
+ /** What the subplanner passed up. */
8069
+ promoted: SubtreeItemView[];
8070
+ }
8071
+ declare function subtreeView(subtree: SubtreeReport): SubtreeView;
8072
+
7865
8073
  /**
7866
8074
  * Where a loop's check stands, for the words a surface puts on its row
7867
8075
  * (ADR-0045): derived from the check's status, verdict and round, as a block
@@ -7954,6 +8162,9 @@ interface TaskRowSubject {
7954
8162
  loopStranded?: boolean;
7955
8163
  /** The latest attempt's handoff as the plan keeps it (ADR-0047); a row shows it without opening the task. */
7956
8164
  handoff?: HandoffDigest;
8165
+ /** A delegated task's goal, and its row's live facts (ADR-0055 D20). */
8166
+ goal?: string;
8167
+ delegation?: DelegatedRow;
7957
8168
  }
7958
8169
  interface TaskRowIsolation {
7959
8170
  state: TaskIsolationState;
@@ -8015,7 +8226,7 @@ interface TaskRowContext {
8015
8226
  * or a script task (ADR-0035): an ops task by nature, drawn with its body and
8016
8227
  * with no prompt, runner or model to edit.
8017
8228
  */
8018
- type TaskRowKind = 'ai' | 'user' | 'ops' | 'script';
8229
+ type TaskRowKind = 'ai' | 'user' | 'ops' | 'script' | 'delegated';
8019
8230
  /** The task's whole spend (see `taskUsageSummary`); `attempt` is the latest one, 0 before any reported. */
8020
8231
  interface TaskRowUsage extends Pick<TaskUsageSummary, 'totals' | 'contextFill'> {
8021
8232
  attempt: number;
@@ -8057,6 +8268,8 @@ type TaskRowLoop = {
8057
8268
  round: number | null;
8058
8269
  };
8059
8270
  interface TaskRowView {
8271
+ /** A delegated task's goal, budget left, child counts and Stop (ADR-0055 D20); null on any other task. */
8272
+ delegated: TaskRowDelegated | null;
8060
8273
  usage: TaskRowUsage | null;
8061
8274
  /** "2", or "2.1" for a subtask. */
8062
8275
  orderLabel: string;
@@ -8172,6 +8385,7 @@ declare function loopRowParts(loop: TaskRowLoop): string[];
8172
8385
  */
8173
8386
  interface TaskLogHeaderTask {
8174
8387
  status: string;
8388
+ type?: string;
8175
8389
  awaitingReason?: AwaitingReason;
8176
8390
  skipReason?: string;
8177
8391
  resumeAt?: string | null;
@@ -8218,7 +8432,7 @@ interface TaskLogHeaderView {
8218
8432
  /** The commands the attempt on screen loaded (#66): the last `runner_loaded` block of its log. */
8219
8433
  declare function taskLogCommands(view: Pick<TaskLogView, 'blocks'>): readonly RunnerCommand[];
8220
8434
  /** Whether an opened task's composer continues it rather than messaging its turn (ADR-0018, K1). */
8221
- declare function logContinues(task: Pick<TaskLogHeaderTask, 'status' | 'awaitingReason' | 'continuable'>): boolean;
8435
+ declare function logContinues(task: Pick<TaskLogHeaderTask, 'type' | 'status' | 'awaitingReason' | 'continuable'>): boolean;
8222
8436
  /** Whether the composer resumes it now, once the user says the limit has reset; a subset of `logContinues`. */
8223
8437
  declare function logResumes(task: Pick<TaskLogHeaderTask, 'status' | 'awaitingReason' | 'continuable'>): boolean;
8224
8438
  /**
@@ -8314,18 +8528,12 @@ declare const PLANNED_WITH_LABEL = "Planned-with line in commits Ordewell writes
8314
8528
  declare const trailerSummary: (run: GlobalRunSettings) => string;
8315
8529
  declare const autorunSummary: (run: GlobalRunSettings) => string;
8316
8530
  declare const delegationSummary: (run: GlobalRunSettings) => string;
8317
- /**
8318
- * Whether any surface offers the delegation setting. Off until a delegated task
8319
- * exists (ADR-0055 D21): until then a control for it promises something that
8320
- * does nothing and warns about tokens that cannot be spent. The setting, its
8321
- * validation and its stored value stay in core and stay inert; the delegated-task
8322
- * slice turns this on, and only here.
8323
- */
8324
- declare const DELEGATION_OFFERED = false;
8531
+ /** Whether any surface offers the delegation setting, and whether a planner may write a delegated task. */
8532
+ declare const DELEGATION_OFFERED: boolean;
8325
8533
  declare const ACT_RISK = "Tasks it adds run too, ops tasks included, which can act on real systems unattended.";
8326
8534
  declare const AUTORUN_RISK = "Plans start without review, ops tasks included, which can act on real systems unattended. In Full nobody is asked at all: the planner may start anything, script tasks included, from a plan no person read.";
8327
- /** What turning delegation on accepts below the root, as Act and autorun accept it at the root (ADR-0056 M9). */
8328
- declare const DELEGATION_RISK = "A subplanner approves and runs its own plan, and no person reads it. Its change tasks keep the network they have in a flat run, so an instruction injected into something a subplanner read can become a plan nobody read, and run with that network.";
8535
+ /** What turning delegation on accepts below the root, as Act and autorun accept it at the root (ADR-0056 M9). A container is not named as a remedy: only its own setting runs one, a child's tasks included (ADR-0059). */
8536
+ declare const DELEGATION_RISK = "A subplanner approves and runs its own plan, and no person reads it. Its change tasks keep the network they have in a flat run, so an instruction injected into something a subplanner read can become a plan nobody read, and run with that network. Delegation starts no container of its own: containers are opt-in, and only that setting runs a child's tasks in one.";
8329
8537
  /** `on`/`off` as a switch's value; null for anything else. */
8330
8538
  declare function parseSwitch(arg: string): boolean | null;
8331
8539
  /** A delegation setting as typed: `off`, which is 0, or the tree's budget in tokens; null for anything else. */
@@ -8333,4 +8541,4 @@ declare function parseDelegation(arg: string): number | null;
8333
8541
  /** A whole number at least `min`, as typed; null for anything else. */
8334
8542
  declare function parseCount(arg: string | undefined, min: number): number | null;
8335
8543
 
8336
- export { type DaemonErrorCode as $, ACT_RISK as A, type BlockingSubject as B, CHAT_COMMANDS as C, type CliAgentAiServiceDeps as D, type CommandBlock as E, type CommandDescriptor as F, type CommandOutcome as G, type CommandPayload as H, type CommandStatus as I, type CommandsResponse as J, type CompactionBlock as K, type CompactionStatus as L, type Consent as M, type ContextCondenseRequest as N, ConversationBusyError as O, type ConversationCompactResponse as P, type ConversationCompaction as Q, ConversationEditError as R, type ConversationForkResponse as S, type ConversationInput as T, type ConversationRequest as U, type ConversationRewindResponse as V, type ConversationTurn as W, type ConversationView as X, DEFAULT_WEB_READ_BUDGET as Y, DELEGATION_OFFERED as Z, DELEGATION_RISK as _, AUTORUN_RISK as a, type MergeGateEntry as a$, type DecideOptions as a0, type DiffRow as a1, type DiffRowKind as a2, type DiffStat as a3, type DisplayBlock as a4, EMPTY_CONVERSATION as a5, EMPTY_HOLD as a6, EMPTY_RUN as a7, EMPTY_TASK_LOG as a8, EMPTY_TASK_LOG_FOLLOW as a9, IDLE_ACTIVITY as aA, type INotification as aB, type IWebFetcher as aC, type IsolationDiffResponse as aD, type IsolationMergeResponse as aE, type IsolationPreviewNotice as aF, type IsolationPreviewResponse as aG, IsolationRunController as aH, LEASE_HEARTBEAT_MS as aI, LEASE_STALE_MS as aJ, LeaseHost as aK, type LeaseHostKind as aL, type LeaseHostOptions as aM, type LiveOutputLookup as aN, type LiveTail as aO, type LiveTailOptions as aP, type LocalEntry as aQ, type LoopCheckFacts as aR, type LoopCheckState as aS, type LoopGuard as aT, type Mandate as aU, type MandateExclusions as aV, type MandateLimits as aW, type MandateRefusal as aX, type MandateRequest as aY, type MappedTool as aZ, type MarkdownToken as a_, type EffectRules as aa, type ErrorBody as ab, type Escalation as ac, type EscalationInput as ad, type EscalationKind as ae, type EscalationOption as af, type EscalationOptionId as ag, type EscalationRun as ah, type EscalationsResponse as ai, type ExecuteCommandResponse as aj, type ExecuteResponse as ak, type ExecutionCommand as al, ExecutionCommands as am, type ExecutionHost as an, type ExecutionSummary as ao, FLOOR_GRANTS as ap, GRANTS as aq, type GatedConversation as ar, type GeneratePlanResponse as as, type GlobalRunSettings as at, type Grant as au, HEADLESS_CLI_COMMANDS as av, type HandoffBlock as aw, type HandoffForkRepo as ax, type HandoffPeekSubject as ay, type IAiService as az, type AccountLimitBucket as b, type RootSettings as b$, type MergeGateResponse as b0, type MergeWordingHandoff as b1, type MessageBlock as b2, type MessageRole as b3, type ModelsResponse as b4, NOT_STARTED as b5, NO_TURN as b6, type Narrowed as b7, type NewTaskFields as b8, type NodeAuthority as b9, type PlannerModelChoice as bA, PlannerModelMemory as bB, type PlannerModelRecall as bC, type PlannerModelStore as bD, type PlannerModes as bE, type PlannerNotReady as bF, PlannerNotReadyError as bG, type PlannerReadinessConfig as bH, type PlannerSubmission as bI, PlannerTurnDiscardedError as bJ, PlannerTurnStoppedError as bK, PlannerUsageLimitError as bL, type PrdResponse as bM, type PromptHold as bN, REPORT_PREVIEW_LINES as bO, RUN_SETTINGS_DEFAULTS as bP, type ReadRunnerAuthOptions as bQ, type RefTask as bR, ResearchTasks as bS, type ResolveAll as bT, type ResolveConflictResponse as bU, type RevertPreviewTask as bV, type RevertRefusal as bW, type RevertRefusalReason as bX, type RevertScope as bY, type RewindTarget as bZ, type RewindTargetsResponse as b_, type NotificationAction as ba, OUTPUT_LINES_DEFAULT as bb, OUTPUT_LINES_MAX as bc, type OkResponse as bd, type OpRef as be, type OpRefusal as bf, type OrchestratorObserver as bg, type OutputPreview as bh, type OverrideDirection as bi, PLANNED_WITH_LABEL as bj, type PendingApproval as bk, PendingApprovals as bl, type PendingApprovalsOptions as bm, type PlanBlock as bn, type PlanBlocks as bo, type PlanIntake as bp, type PlanMarkerStatus as bq, type PlanResponse as br, type PlanSnapshot as bs, PlanStore as bt, type PlannerCommandSyntax as bu, type PlannerContextDeps as bv, type PlannerContextStatus as bw, PlannerConversation as bx, type PlannerConversationHost as by, type PlannerModelCandidate as bz, type AccountLimitSnapshot as c, type TaskLogBatch as c$, type RunActivity as c0, type RunInput as c1, type RunMergeView as c2, RunSettingsError as c3, type RunSnapshotFrame as c4, type RunTask as c5, type RunTaskStatus as c6, type RunView as c7, type RunnerAuth as c8, type RunnerAuthExec as c9, type SessionResponse as cA, type SettingsResponse as cB, SettingsService as cC, type SettingsUpdateResponse as cD, type SkillInvocation as cE, type SkillLoadBlock as cF, type SkillToken as cG, type StartAdmission as cH, type StopResponse as cI, type StreamStamp as cJ, type SubagentBlock as cK, type SubagentChild as cL, type SubagentStatus as cM, type SurfacePlan as cN, type SurfacePlanState as cO, type SurfaceTask as cP, TASK_QUERY_ANSWER_MAX_CHARS as cQ, TASK_QUERY_ANSWER_OR_OPS as cR, TASK_QUERY_FIELDS as cS, TASK_QUERY_PROTOCOL as cT, TASK_QUERY_REMINDER as cU, TERMINAL_UI_COMMANDS as cV, type TakenPrompt as cW, type TaskAction as cX, type TaskActivity as cY, type TaskAttemptSnapshot as cZ, type TaskLogAttemptsResponse as c_, type RunnerAuthExecOptions as ca, type RunnerAuthExecResult as cb, type RunnerCatalog as cc, type RunnerLoadedBlock as cd, type RunnersResponse as ce, SHAPE_LIMITS as cf, STALLED_LABEL as cg, STOPPED_BEFORE_START as ch, SUPERVISOR_LEVEL_LABEL as ci, type SerializedConversationMessage as cj, type SerializedPendingTurn as ck, type SerializedPlan as cl, type SerializedTask as cm, type SerializedTaskStatus as cn, type ServerInfoResponse as co, type SessionBroadcaster as cp, type SessionData as cq, type SessionFrame as cr, type SessionHeld as cs, SessionHeldError as ct, type SessionLease as cu, type SessionLeaseRecord as cv, type SessionListResponse as cw, type SessionMessage as cx, type SessionMeta as cy, type SessionNotice as cz, type AccountLimitStatus as d, approvalStatusText as d$, type TaskLogEvent as d0, type TaskLogFollow as d1, type TaskLogFollowInput as d2, type TaskLogHeaderKind as d3, type TaskLogHeaderTask as d4, type TaskLogHeaderUsage as d5, type TaskLogHeaderView as d6, type TaskLogResponse as d7, type TaskLogView as d8, type TaskMessageResponse as d9, type ToolBlock as dA, type ToolHeadline as dB, type ToolStatus as dC, type TreeTask as dD, TurnBudgets as dE, type TurnGate as dF, type UsageBlock as dG, type UserSettings as dH, WAITS_FOR_APPROVAL_LABEL as dI, type WakeUpItem as dJ, type WebRead as dK, type WebReadDecision as dL, type WebReadOptions as dM, WebReadPolicy as dN, type Worded as dO, type WorkspacesResponse as dP, abbreviateHome as dQ, accountLimitPercentText as dR, accountLimitWindowLabel as dS, accountLimitsOf as dT, accountLimitsText as dU, accountLimitsTitle as dV, activityLabel as dW, aheadOfDraft as dX, applyTaskOps as dY, approvalKindText as dZ, approvalLabel as d_, type TaskOp as da, TaskOrchestrator as db, type TaskOrchestratorDeps as dc, type TaskOrchestratorOptions as dd, type TaskOutputSource as de, type TaskPlacement as df, type TaskQuery as dg, type TaskQueryCatalog as dh, type TaskQueryField as di, type TaskRef as dj, type TaskRevertPreview as dk, type TaskRevertPreviewResponse as dl, type TaskRowAction as dm, type TaskRowContext as dn, type TaskRowHandoff as dp, type TaskRowIsolation as dq, type TaskRowKind as dr, type TaskRowLoop as ds, type TaskRowPlacement as dt, type TaskRowSubject as du, type TaskRowUsage as dv, type TaskRowView as dw, type TaskRunFacts as dx, type TaskStatusKind as dy, type ThinkingDisplayBlock as dz, type AccountLimitWindow as e, logContinues as e$, assignedRunnerOf as e0, assignmentIssue as e1, assignmentIssueText as e2, attachingLabel as e3, automergeSummary as e4, autorunSummary as e5, awaitingLabel as e6, blockNotice as e7, blockNotices as e8, blockedByLabel as e9, digestTaskLog as eA, drainNext as eB, editableWhileRunning as eC, effectRuleFor as eD, elapsedSince as eE, escalations as eF, executionSummary as eG, followTaskLog as eH, followTurn as eI, formatElapsed as eJ, fromTranscript as eK, getSettingsPath as eL, handoffBaseLine as eM, handoffBranchName as eN, handoffFindingsText as eO, handoffPeekParts as eP, hasAccountLimits as eQ, hasHiddenDetail as eR, hasLongReport as eS, heldMandate as eT, holdLabel as eU, holdPrompt as eV, isFileEditTool as eW, isRepoGroup as eX, isTaskRunning as eY, lastServedModel as eZ, loadedSkillTokens as e_, blockingFailures as ea, canMergeTasks as eb, canSetDependencies as ec, canSplitTask as ed, capConflictFiles as ee, cardEditNeedsUnstarted as ef, cardEditable as eg, checkoutRule as eh, classifyOutcome as ei, closeTurn as ej, coalesceTaskLog as ek, compactionText as el, confirmedOverride as em, createAiService as en, delegationSummary as eo, dependencyCandidates as ep, dependentsNotice as eq, dependentsOf as er, describeHeld as es, describeHolder as et, describeIsolationPreview as eu, describeMergeResult as ev, describePlannerNotReady as ew, diffRows as ex, diffStat as ey, diffSummary as ez, type AccountLimitsFrame as f, resumeTime as f$, logResumes as f0, loopBodyLabel as f1, loopBodyRound as f2, loopCheckLabel as f3, loopCheckState as f4, loopLeaveNotice as f5, loopRowParts as f6, mandateRefusalText as f7, manualOverrideConfirmation as f8, manualOverrideStopFirst as f9, plannerNotReady as fA, plannerNotReadyFor as fB, plannerTranscript as fC, processLeaseHost as fD, ranSealed as fE, readRunnerAuth as fF, receiptLabel as fG, reconcileApprovals as fH, recordRunnerAuth as fI, recordedRunnerAuth as fJ, reduceConversation as fK, reduceRun as fL, reduceTaskLog as fM, renderTaskQueryAnswer as fN, repairWaitLabel as fO, repairedLanded as fP, repairedNotice as fQ, repeatingChecks as fR, replayTaskLog as fS, repoBaseLine as fT, repoResultLines as fU, reposWithWork as fV, resetRunnerAuth as fW, resolveSkillInvocation as fX, resolveTaskRef as fY, resolveUserTaskRef as fZ, resumeLabel as f_, mapAgentTool as fa, markAction as fb, markdownHtml as fc, markdownTokens as fd, mergeOutcome as fe, mergeTarget as ff, namedRepos as fg, narrow as fh, newTaskFields as fi, noExecutorRefusal as fj, normalizeAgentArgs as fk, opRefusalText as fl, opsEditable as fm, outputLines as fn, outputPreview as fo, overrideNeedsConfirmation as fp, parseCount as fq, parseDelegation as fr, parseEffectAllowlist as fs, parseSwitch as ft, parseTaskOpsJson as fu, parseTaskQueryJson as fv, pastGateConfirmation as fw, planBlocks as fx, planMarkerParts as fy, plannerMessage as fz, type AccountLimitsMessage as g, revertEffect as g0, revertListLine as g1, revertTaskRef as g2, rootAuthority as g3, rootMandate as g4, runHoldsEditors as g5, runSettingsOf as g6, runnerToolSubject as g7, scriptExitText as g8, scriptRunText as g9, taskStartedNotice as gA, taskStatusKind as gB, textHasTaskOps as gC, textHasTaskQuery as gD, titledRefs as gE, titledTaskRef as gF, titledTaskRefOf as gG, toTaskLogEvent as gH, toolHeadline as gI, trailerSummary as gJ, trimToolOutput as gK, truncateCheckpointSummary as gL, unsendAll as gM, unsendLatest as gN, wakeUpItems as gO, walkPlan as gP, withoutSealedMark as gQ, workspaceEnvOf as gR, secondsText as ga, serializePlan as gb, serializeTask as gc, serializeTaskStatus as gd, skillTokens as ge, skippedLabel as gf, stopTurn as gg, subtreeIds as gh, summarizeToolCall as gi, supervisorSummary as gj, surfacePlan as gk, surfacePlanState as gl, takeAt as gm, taskActionCommand as gn, taskActivity as go, taskChatAvailable as gp, taskLogCommands as gq, taskLogForSurface as gr, taskLogHeaderView as gs, taskOrderLabel as gt, taskPlacement as gu, taskQuerySignature as gv, taskRef as gw, taskRefOf as gx, taskRowActions as gy, taskRowView as gz, type AdoptSessionResponse as h, type AnswerEscalationRequest as i, type ApplyTaskOpsResult as j, type ApprovalAnswerResponse as k, type ApprovalBlock as l, type ApprovalRequestMessage as m, type ApprovalStatus as n, type ApprovalsResponse as o, type ApproveReviewResponse as p, type AskOptions as q, type AssignmentCatalog as r, type AssignmentIssue as s, type AssignmentScope as t, type AssignmentWriter as u, type AutoMergeTrigger as v, CHECKPOINT_TRUNCATE_LENGTH as w, type CancelPlanningResponse as x, type CancelResumeResponse as y, CliAgentAiService as z };
8544
+ export { DELEGATION_RISK as $, ACT_RISK as A, type BlockingSubject as B, CHAT_COMMANDS as C, CliAgentAiService as D, type CliAgentAiServiceDeps as E, type CommandBlock as F, type CommandDescriptor as G, type CommandOutcome as H, type CommandPayload as I, type CommandStatus as J, type CommandsResponse as K, type CompactionBlock as L, type CompactionStatus as M, type Consent as N, type ContextCondenseRequest as O, ConversationBusyError as P, type ConversationCompactResponse as Q, type ConversationCompaction as R, ConversationEditError as S, type ConversationForkResponse as T, type ConversationInput as U, type ConversationRequest as V, type ConversationRewindResponse as W, type ConversationTurn as X, type ConversationView as Y, DEFAULT_WEB_READ_BUDGET as Z, DELEGATION_OFFERED as _, AUTORUN_RISK as a, type NodeBudget as a$, type DaemonErrorCode as a0, type DecideOptions as a1, type DiffRow as a2, type DiffRowKind as a3, type DiffStat as a4, type DisplayBlock as a5, EMPTY_CONVERSATION as a6, EMPTY_HOLD as a7, EMPTY_RUN as a8, EMPTY_TASK_LOG as a9, type IsolationMergeResponse as aA, type IsolationPreviewNotice as aB, type IsolationPreviewResponse as aC, LEASE_HEARTBEAT_MS as aD, LEASE_STALE_MS as aE, LeaseHost as aF, type LeaseHostKind as aG, type LeaseHostOptions as aH, type LiveOutputLookup as aI, type LiveTail as aJ, type LiveTailOptions as aK, type LocalEntry as aL, type LoopCheckFacts as aM, type LoopCheckState as aN, type LoopGuard as aO, type MappedTool as aP, type MarkdownToken as aQ, type MergeGateEntry as aR, type MergeGateResponse as aS, type MergeWordingHandoff as aT, type MessageBlock as aU, type MessageRole as aV, type ModelsResponse as aW, NOT_STARTED as aX, NO_TURN as aY, type NewTaskFields as aZ, NodeBoard as a_, EMPTY_TASK_LOG_FOLLOW as aa, type ErrorBody as ab, type Escalation as ac, type EscalationInput as ad, type EscalationKind as ae, type EscalationOption as af, type EscalationOptionId as ag, type EscalationRun as ah, type EscalationsResponse as ai, type ExecuteCommandResponse as aj, type ExecuteResponse as ak, type ExecutionCommand as al, ExecutionCommands as am, type ExecutionHost as an, type ExecutionSummary as ao, type GatedConversation as ap, type GeneratePlanResponse as aq, type GlobalRunSettings as ar, HEADLESS_CLI_COMMANDS as as, type HandoffBlock as at, type HandoffForkRepo as au, type HandoffPeekSubject as av, type IAiService as aw, IDLE_ACTIVITY as ax, type IWebFetcher as ay, type IsolationDiffResponse as az, type AccountLimitBucket as b, type RunnerCatalog as b$, OUTPUT_LINES_DEFAULT as b0, OUTPUT_LINES_MAX as b1, type OkResponse as b2, type OpRef as b3, type OpRefusal as b4, type OrchestratorObserver as b5, type OutputPreview as b6, type OverrideDirection as b7, PLANNED_WITH_LABEL as b8, type PendingApproval as b9, type PrdResponse as bA, type PromptHold as bB, REPORT_PREVIEW_LINES as bC, RUN_SETTINGS_DEFAULTS as bD, type ReadRunnerAuthOptions as bE, type RefTask as bF, ResearchTasks as bG, type ResolveAll as bH, type ResolveConflictResponse as bI, type RevertPreviewTask as bJ, type RevertRefusal as bK, type RevertRefusalReason as bL, type RevertScope as bM, type RewindTarget as bN, type RewindTargetsResponse as bO, type RunActivity as bP, type RunInput as bQ, type RunMergeView as bR, RunSettingsError as bS, type RunSnapshotFrame as bT, type RunTask as bU, type RunTaskStatus as bV, type RunView as bW, type RunnerAuth as bX, type RunnerAuthExec as bY, type RunnerAuthExecOptions as bZ, type RunnerAuthExecResult as b_, PendingApprovals as ba, type PendingApprovalsOptions as bb, type PlanBlock as bc, type PlanBlocks as bd, type PlanIntake as be, type PlanMarkerStatus as bf, type PlanResponse as bg, type PlanSnapshot as bh, PlanStore as bi, type PlannerCommandSyntax as bj, type PlannerContextDeps as bk, type PlannerContextStatus as bl, PlannerConversation as bm, type PlannerConversationHost as bn, type PlannerModelCandidate as bo, type PlannerModelChoice as bp, PlannerModelMemory as bq, type PlannerModelRecall as br, type PlannerModelStore as bs, type PlannerNotReady as bt, PlannerNotReadyError as bu, type PlannerReadinessConfig as bv, type PlannerSubmission as bw, PlannerTurnDiscardedError as bx, PlannerTurnStoppedError as by, PlannerUsageLimitError as bz, type AccountLimitSnapshot as c, type TaskMessageResponse as c$, type RunnerLoadedBlock as c0, type RunnersResponse as c1, STALLED_LABEL as c2, STOPPED_BEFORE_START as c3, STOPPING_LABEL as c4, SUPERVISOR_LEVEL_LABEL as c5, type SerializedConversationMessage as c6, type SerializedPendingTurn as c7, type SerializedPlan as c8, type SerializedTask as c9, Subtree as cA, type SubtreeItemView as cB, type SubtreeView as cC, type SurfacePlan as cD, type SurfacePlanState as cE, type SurfaceTask as cF, TASK_QUERY_ANSWER_MAX_CHARS as cG, TASK_QUERY_ANSWER_OR_OPS as cH, TASK_QUERY_FIELDS as cI, TASK_QUERY_PROTOCOL as cJ, TASK_QUERY_REMINDER as cK, TERMINAL_UI_COMMANDS as cL, type TakenPrompt as cM, type TaskAction as cN, type TaskActivity as cO, type TaskAttemptSnapshot as cP, type TaskLogAttemptsResponse as cQ, type TaskLogBatch as cR, type TaskLogEvent as cS, type TaskLogFollow as cT, type TaskLogFollowInput as cU, type TaskLogHeaderKind as cV, type TaskLogHeaderTask as cW, type TaskLogHeaderUsage as cX, type TaskLogHeaderView as cY, type TaskLogResponse as cZ, type TaskLogView as c_, type SerializedTaskStatus as ca, type ServerInfoResponse as cb, type SessionBroadcaster as cc, type SessionFrame as cd, type SessionHeld as ce, SessionHeldError as cf, type SessionLease as cg, type SessionLeaseRecord as ch, type SessionListResponse as ci, type SessionMessage as cj, type SessionNotice as ck, type SessionResponse as cl, type SettingsResponse as cm, SettingsService as cn, type SettingsUpdateResponse as co, type SkillInvocation as cp, type SkillLoadBlock as cq, type SkillToken as cr, SlotPool as cs, type StartAdmission as ct, type StopResponse as cu, type StreamStamp as cv, type SubagentBlock as cw, type SubagentChild as cx, type SubagentStatus as cy, type SubplannerBrief as cz, type AccountLimitStatus as d, blockNotice as d$, type TaskOp as d0, TaskOrchestrator as d1, type TaskOrchestratorDeps as d2, type TaskOrchestratorOptions as d3, type TaskOutputSource as d4, type TaskPlacement as d5, type TaskQuery as d6, type TaskQueryCatalog as d7, type TaskQueryField as d8, type TaskRef as d9, WAITS_FOR_APPROVAL_LABEL as dA, type WakeUpItem as dB, type WebRead as dC, type WebReadDecision as dD, type WebReadOptions as dE, WebReadPolicy as dF, type Worded as dG, type WorkspacesResponse as dH, abbreviateHome as dI, accountLimitPercentText as dJ, accountLimitWindowLabel as dK, accountLimitsOf as dL, accountLimitsText as dM, accountLimitsTitle as dN, activityLabel as dO, aheadOfDraft as dP, applyTaskOps as dQ, approvalKindText as dR, approvalLabel as dS, approvalStatusText as dT, assignedRunnerOf as dU, assignmentIssue as dV, assignmentIssueText as dW, attachingLabel as dX, automergeSummary as dY, autorunSummary as dZ, awaitingLabel as d_, type TaskRevertPreview as da, type TaskRevertPreviewResponse as db, type TaskRowAction as dc, type TaskRowContext as dd, type TaskRowDelegated as de, type TaskRowHandoff as df, type TaskRowIsolation as dg, type TaskRowKind as dh, type TaskRowLoop as di, type TaskRowPlacement as dj, type TaskRowSubject as dk, type TaskRowUsage as dl, type TaskRowView as dm, type TaskRunFacts as dn, type TaskStatusKind as dp, type ThinkingDisplayBlock as dq, type ToolBlock as dr, type ToolHeadline as ds, type ToolStatus as dt, TreeLedger as du, type TreeTask as dv, TurnBudgets as dw, type TurnGate as dx, type UsageBlock as dy, type UserSettings as dz, type AccountLimitWindow as e, manualOverrideStopFirst as e$, blockNotices as e0, blockedByLabel as e1, blockingFailures as e2, canMergeTasks as e3, canSetDependencies as e4, canSplitTask as e5, capConflictFiles as e6, cardEditNeedsUnstarted as e7, cardEditable as e8, checkoutRule as e9, followTurn as eA, formatElapsed as eB, fromTranscript as eC, getSettingsPath as eD, handoffBaseLine as eE, handoffBranchName as eF, handoffFindingsText as eG, handoffPeekParts as eH, hasAccountLimits as eI, hasHiddenDetail as eJ, hasLongReport as eK, holdLabel as eL, holdPrompt as eM, isFileEditTool as eN, isRepoGroup as eO, isTaskRunning as eP, lastServedModel as eQ, loadedSkillTokens as eR, logContinues as eS, logResumes as eT, loopBodyLabel as eU, loopBodyRound as eV, loopCheckLabel as eW, loopCheckState as eX, loopLeaveNotice as eY, loopRowParts as eZ, manualOverrideConfirmation as e_, classifyOutcome as ea, closeTurn as eb, coalesceTaskLog as ec, compactionText as ed, confirmedOverride as ee, createAiService as ef, delegatedRowView as eg, delegationSummary as eh, dependencyCandidates as ei, dependentsNotice as ej, dependentsOf as ek, describeHeld as el, describeHolder as em, describeIsolationPreview as en, describeMergeResult as eo, describePlannerNotReady as ep, diffRows as eq, diffStat as er, diffSummary as es, digestTaskLog as et, drainNext as eu, editableWhileRunning as ev, elapsedSince as ew, escalations as ex, executionSummary as ey, followTaskLog as ez, type AccountLimitsFrame as f, serializeTask as f$, mapAgentTool as f0, markAction as f1, markdownHtml as f2, markdownTokens as f3, mergeOutcome as f4, mergePastChecksConfirmation as f5, mergeTarget as f6, namedRepos as f7, newTaskFields as f8, noExecutorRefusal as f9, reduceRun as fA, reduceTaskLog as fB, renderTaskQueryAnswer as fC, repairWaitLabel as fD, repairedLanded as fE, repairedNotice as fF, repeatingChecks as fG, replayTaskLog as fH, repoBaseLine as fI, repoResultLines as fJ, reposWithWork as fK, resetRunnerAuth as fL, resolveSkillInvocation as fM, resolveTaskRef as fN, resolveUserTaskRef as fO, resumeLabel as fP, resumeTime as fQ, revertEffect as fR, revertListLine as fS, revertTaskRef as fT, runHoldsEditors as fU, runSettingsOf as fV, runnerToolSubject as fW, scriptExitText as fX, scriptRunText as fY, secondsText as fZ, serializePlan as f_, normalizeAgentArgs as fa, opRefusalText as fb, opsEditable as fc, outputLines as fd, outputPreview as fe, overrideNeedsConfirmation as ff, parseCount as fg, parseDelegation as fh, parseSwitch as fi, parseTaskOpsJson as fj, parseTaskQueryJson as fk, pastGateConfirmation as fl, planBlocks as fm, planMarkerParts as fn, plannerMessage as fo, plannerNotReady as fp, plannerNotReadyFor as fq, plannerTranscript as fr, processLeaseHost as fs, ranSealed as ft, readRunnerAuth as fu, receiptLabel as fv, reconcileApprovals as fw, recordRunnerAuth as fx, recordedRunnerAuth as fy, reduceConversation as fz, type AccountLimitsMessage as g, serializeTaskStatus as g0, skillTokens as g1, skippedLabel as g2, stopTurn as g3, subtreeIds as g4, subtreeView as g5, summarizeToolCall as g6, supervisorSummary as g7, surfacePlan as g8, surfacePlanState as g9, unsendAll as gA, unsendLatest as gB, wakeUpItems as gC, walkPlan as gD, withoutSealedMark as gE, workspaceEnvOf as gF, takeAt as ga, taskActionCommand as gb, taskActivity as gc, taskChatAvailable as gd, taskLogCommands as ge, taskLogForSurface as gf, taskLogHeaderView as gg, taskOrderLabel as gh, taskPlacement as gi, taskQuerySignature as gj, taskRef as gk, taskRefOf as gl, taskRowActions as gm, taskRowView as gn, taskStartedNotice as go, taskStatusKind as gp, textHasTaskOps as gq, textHasTaskQuery as gr, titledRefs as gs, titledTaskRef as gt, titledTaskRefOf as gu, toTaskLogEvent as gv, toolHeadline as gw, trailerSummary as gx, trimToolOutput as gy, truncateCheckpointSummary as gz, type AdoptSessionResponse as h, type AnswerEscalationRequest as i, type ApplyTaskOpsResult as j, type ApprovalAnswerResponse as k, type ApprovalBlock as l, type ApprovalRequestMessage as m, type ApprovalStatus as n, type ApprovalsResponse as o, type ApproveReviewResponse as p, type AskOptions as q, type AssignmentCatalog as r, type AssignmentIssue as s, type AssignmentScope as t, type AssignmentWriter as u, type AutoMergeTrigger as v, CHECKPOINT_TRUNCATE_LENGTH as w, type CancelPlanningResponse as x, type CancelResumeResponse as y, type Carved as z };