@ordewell/core 0.11.3 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/AUTHORS.md +3 -1
  2. package/dist/{ModeResolver-DyXeZ9ib.d.ts → ModeResolver-B64aUZXJ.d.ts} +1 -1
  3. package/dist/{ModeResolver-BDAgouuE.d.mts → ModeResolver-CA2fUU9h.d.mts} +1 -1
  4. package/dist/{Task-BL4Idm9r.d.mts → Task-CxGgN6I0.d.mts} +801 -39
  5. package/dist/{Task-BL4Idm9r.d.ts → Task-CxGgN6I0.d.ts} +801 -39
  6. package/dist/{chunk-YAFNYNFX.mjs → chunk-2IFKPYPO.mjs} +453 -17
  7. package/dist/chunk-2IFKPYPO.mjs.map +1 -0
  8. package/dist/{chunk-MYXMIVWA.mjs → chunk-FX33UKAD.mjs} +8 -6
  9. package/dist/chunk-FX33UKAD.mjs.map +1 -0
  10. package/dist/{chunk-QTZ5XTBY.mjs → chunk-PKJTN2PD.mjs} +458 -13
  11. package/dist/chunk-PKJTN2PD.mjs.map +1 -0
  12. package/dist/{chunk-7WNLN63J.mjs → chunk-RZ23BMVR.mjs} +599 -124
  13. package/dist/chunk-RZ23BMVR.mjs.map +1 -0
  14. package/dist/chunk-UDUATSCJ.mjs +48 -0
  15. package/dist/chunk-UDUATSCJ.mjs.map +1 -0
  16. package/dist/{IRunner-DOMtDTVt.d.ts → eventStore-CTCSKS9E.d.ts} +976 -145
  17. package/dist/{IRunner-ihalYfTo.d.mts → eventStore-CcB8yELZ.d.mts} +976 -145
  18. package/dist/index.d.mts +905 -164
  19. package/dist/index.d.ts +905 -164
  20. package/dist/index.js +12496 -3308
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +10830 -2919
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/parsing.d.mts +2 -2
  25. package/dist/parsing.d.ts +2 -2
  26. package/dist/parsing.js +277 -21
  27. package/dist/parsing.js.map +1 -1
  28. package/dist/parsing.mjs +3 -3
  29. package/dist/plan-utils.d.mts +8 -5
  30. package/dist/plan-utils.d.ts +8 -5
  31. package/dist/plan-utils.js +262 -33
  32. package/dist/plan-utils.js.map +1 -1
  33. package/dist/plan-utils.mjs +39 -3
  34. package/dist/{runSettingsText-DVOd7vKC.d.ts → runSettingsText-DFVpe6aU.d.mts} +1996 -895
  35. package/dist/{runSettingsText-BuLVBlXS.d.mts → runSettingsText-lKLDsyli.d.ts} +1996 -895
  36. package/dist/testing.d.mts +99 -4
  37. package/dist/testing.d.ts +99 -4
  38. package/dist/testing.js +213 -8
  39. package/dist/testing.js.map +1 -1
  40. package/dist/testing.mjs +193 -8
  41. package/dist/testing.mjs.map +1 -1
  42. package/docker/task.Dockerfile +22 -0
  43. package/package.json +3 -1
  44. package/skills/diagnose/SKILL.md +2 -2
  45. package/skills/explore/SKILL.md +1 -1
  46. package/dist/chunk-7WNLN63J.mjs.map +0 -1
  47. package/dist/chunk-MYXMIVWA.mjs.map +0 -1
  48. package/dist/chunk-QTZ5XTBY.mjs.map +0 -1
  49. package/dist/chunk-YAFNYNFX.mjs.map +0 -1
@@ -1,357 +1,27 @@
1
- import { aP as StoredHandoff, aS as Task, az as RunnerId, b0 as TaskSnapshot, b as AwaitingReason, b6 as UsageRecord, aA as RunnerSessionCost, i as DiscoveredModel, aO as SkillsService, aJ as SkillInfo, aq as RepoGroupLayout, x as IWorktreeIsolation, X as IsolationRun, K as IsolationHandoff, a8 as PlanIsolation, Y as IsolationTaskRecord, aV as TaskIsolation, a0 as IsolationView, ah as PrepareIntent, al as PreviousWork, q as HandoffCommit, S as IsolationOutcome, ao as RepairEvidence, b4 as TreeSnapshot, aT as TaskDiffRead, aU as TaskDiffResult, R as IsolationMergeResult, b8 as Verdict, a1 as LegacyPlanState, aZ as TaskReportTo, a$ as TaskSkillSnapshot, a_ as TaskSkillNotice, aQ as SubagentOutcome, d as ConversationMessage, aL as SkillLoadNotice, a7 as PendingPlanTurn, r as HandoffDigest, a as AttemptUsage, aX as TaskLoop, ay as RunSettings, ab as PlanUsage, ap as ReplyRun, aE as SessionBilling, a4 as ModelUsage, b7 as UsageTotals, c as ContextFill, ac as PlannerCompaction, k as ExecutionRecord, av as ResearchStep, a9 as PlanState, b1 as TaskStatus, E as ExecutionActor, l as GuardTrip, aR as SupervisorLevel, j as ExecutionCommandName, G as GuardTarget, au as ResearchProgress, at as ResearchLogEntry, aK as SkillLoad, ae as PlannerContextPause, aa as PlanStatus, P as IsolationMergeBlock, N as IsolationLandedTask, T as IsolationPreview, ax as ResearchToolType, a2 as LoopSubject, aw as ResearchStepOutcome, ad as PlannerCompactionOutcome, ag as PlannerUsage, F as FindingSeverity, b2 as TaskType, a6 as PatchView, aW as TaskIsolationState, b3 as TaskUsageSummary } from './Task-BL4Idm9r.js';
2
- import { q as ApprovalRequest, r as ApprovalSource, N as IApproval, br as ToolOutcome, R as IRunnerSession, O as IConfig, bs 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, ar as QueuedTaskMessage, l as ApprovalKind, Q as IRunner, aO as RunnerRegistry, bm as TaskShellSite, bl as TaskShell, aW as ScriptBody, d as AgentProcessDeps, a as AgentAdapter, a9 as OrdewellMcpServer, P as IFileSystem, am as PlannerToolHandler, j as ApprovalAnswer, aJ as RunnerManifest, f as AiProvider, ag as PlannerBackendId, a8 as OrchestratorOption } from './IRunner-DOMtDTVt.js';
3
- import { R as RunnerModeInfo } from './ModeResolver-DyXeZ9ib.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.mjs';
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-CcB8yELZ.mjs';
3
+ import { R as RunnerModeInfo } from './ModeResolver-CA2fUU9h.mjs';
4
4
 
5
- type NotificationAction = {
6
- label: string;
7
- value: string;
8
- };
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>;
14
- }
15
-
16
- /**
17
- * The SSRF guard for anything that leaves the machine on the planner's behalf.
18
- * An unguarded `http://169.254.169.254/` would hand a prompt-injected planner
19
- * the cloud metadata credentials, so every outbound host is vetted here: IP
20
- * literals by range, names by every address DNS answers with.
21
- */
22
- type ResolveAll = (host: string) => Promise<string[]>;
23
-
24
- /**
25
- * The one decision every planner web read goes through, whichever runner
26
- * plans and whichever path carries the bytes (ADR-0027). A fetch names the
27
- * URL it reads; a search names its query and the provider that receives it.
28
- */
29
- type WebRead = {
30
- kind: 'fetch';
31
- url: string;
32
- }
33
- /** `provider` keys the grant; `providerLabel`, when given, names it to the person asked. */
34
- | {
35
- kind: 'search';
36
- query: string;
37
- provider: string;
38
- providerLabel?: string;
39
- note?: string;
40
- };
41
- /** A refusal's `reason` is written for the planner: it says what to do next. */
42
- type WebReadDecision = {
43
- allowed: true;
44
- } | {
45
- allowed: false;
46
- reason: string;
47
- };
48
- interface DecideOptions {
49
- /**
50
- * Research subagents run in the background where nothing can prompt
51
- * (ADR-0008, T6). When false, a read that would ask is refused with the gap
52
- * to report, rather than raising a prompt nobody can answer.
53
- */
54
- prompting?: boolean;
55
- /**
56
- * Where this read's decisions go, the first URL's and any redirect hop's,
57
- * in place of the session's approval record. A read-only task's are its
58
- * attempt's, never the planner's (ADR-0046), so its policy records nothing
59
- * without one.
60
- */
61
- record?: DecisionRecord;
62
- }
63
- type DecisionRecord = (req: ApprovalRequest, granted: boolean, source: ApprovalSource) => void;
64
- interface WebReadPolicyOptions {
65
- approval: IApproval;
66
- /** The secrets Ordewell holds — provider keys, MCP tokens — read per request. */
67
- secrets?: () => Iterable<string>;
68
- /** Defaults to `process.env`; its credential-named values are secrets too. */
69
- env?: () => Record<string, string | undefined>;
70
- /** How many web reads one planner turn may make; read per request, so a settings change applies at once. */
71
- budget?: () => number;
72
- /** The planner turn in progress; a new one starts a new budget. */
73
- turn?: () => string | undefined;
74
- /** Override DNS resolution (every answer for a name) in tests. */
75
- resolveHost?: ResolveAll;
76
- /**
77
- * The read-only task whose attempt reads (ADR-0046), by title, set by
78
- * {@link WebReadPolicy.forTask}: named in approvals and refusals, and its
79
- * budget is the policy's own for its whole life, whatever `turn` says.
80
- */
81
- task?: string;
82
- }
83
- declare const DEFAULT_WEB_READ_BUDGET = 40;
84
- declare class WebReadPolicy {
85
- private readonly options;
86
- private readonly reader;
87
- private readonly approval;
88
- private readonly resolveHost;
89
- private readonly secrets;
90
- private readonly env;
91
- private readonly budget;
92
- private readonly turn;
93
- private spent;
94
- constructor(opts: WebReadPolicyOptions);
95
- /**
96
- * The same policy for one read-only task attempt (ADR-0046): the same
97
- * approval, allowlist, live secrets and budget setting, with a count of its
98
- * own that no planner turn resets and no other attempt spends. One per
99
- * attempt, so a retry or a continue starts a fresh count.
100
- */
101
- forTask(task: string): WebReadPolicy;
102
- /**
103
- * Every check that needs no network runs first, then the budget and the
104
- * approval, and only then is a fetch's host resolved: a lookup is itself a
105
- * request, so `<data>.attacker.example` would leave through DNS even for a
106
- * read that is then excluded, over budget or declined.
107
- */
108
- decide(read: WebRead, opts?: DecideOptions): Promise<WebReadDecision>;
109
- private subagentRefusal;
110
- /**
111
- * Whether a search through `provider` would run without asking anyone, for
112
- * a runner whose own search raises no request and is set on or off when
113
- * its session starts (ADR-0027). A zero budget disables it; no query is
114
- * known yet, so secrets and individual reads cannot be checked or counted.
115
- */
116
- searchesUnasked(provider: string): boolean;
117
- /** The last check, the one that leaves the machine: every DNS answer for the host must be public. */
118
- private resolved;
119
- private refuse;
120
- /**
121
- * A planner's decisions reach the session's record, the conversation's
122
- * approval cards; a task attempt's go only where its read says, since they
123
- * are not the planner's (ADR-0046).
124
- */
125
- private recordFor;
126
- private vetFetch;
127
- private vetSearch;
128
- /** Reads the current planner turn, or the task attempt, may still make, for the reader to see. */
129
- remaining(): number;
130
- private spend;
131
- private refund;
132
- private carriesSecret;
133
- }
134
-
135
- /** What a caller that can go away hands a read: once `signal` aborts, nothing more leaves for it. */
136
- interface WebReadOptions {
137
- signal?: AbortSignal;
138
- /** Where the read's decisions go instead of the session's record; see `DecideOptions.record`. */
139
- record?: DecisionRecord;
140
- }
141
- interface IWebFetcher {
142
- /**
143
- * The web-read policy's decision (ADR-0027), for a read this fetcher will
144
- * carry or one a runner carries natively, such as Claude's `WebFetch`.
145
- * `fetch` and `search` decide for themselves; never call both.
146
- */
147
- decide(read: WebRead): Promise<WebReadDecision>;
148
- /** Whether a runner's own search through `provider` would run unasked; see `WebReadPolicy.searchesUnasked`. */
149
- searchesUnasked?(provider: string): boolean;
150
- fetch(url: string, opts?: WebReadOptions): Promise<ToolOutcome>;
151
- /**
152
- * Optional web search. A fetcher without it makes the `web_search` tool
153
- * report itself unavailable rather than failing the turn — the same
154
- * degradation `fetch` already uses when no fetcher is wired at all.
155
- */
156
- search?(query: string, opts?: WebReadOptions): Promise<ToolOutcome>;
157
- /**
158
- * A variant of this fetcher for a research subagent: same policy, cache,
159
- * transport and budget, but a read that would ask a person is refused
160
- * instead (ADR-0027, ADR-0008 T6). Absent means subagents get no web tools
161
- * and the `fetch`/`web_search` tools report themselves unavailable.
162
- */
163
- forSubagent?(): IWebFetcher;
164
- /**
165
- * A variant for one read-only task attempt, `task` naming it (ADR-0046):
166
- * a subagent's in never prompting, with a web-read budget of its own that
167
- * no planner turn and no other attempt spends or resets. Absent, or
168
- * undefined, means read-only tasks get no web reads, and their `fetch` and
169
- * `web_search` report themselves unavailable.
170
- */
171
- forTask?(task: string): IWebFetcher | undefined;
172
- /** Reads the current budget still allows, where this fetcher's policy counts them. */
173
- remaining?(): number | undefined;
174
- }
175
-
176
- interface LiveTailOptions {
177
- maxLines: number;
178
- /** A previous {@link LiveTail.nextOffset}: only output after it is rendered. */
179
- sinceOffset?: number;
180
- }
181
- interface LiveTail {
182
- /** Plain text, escapes and control characters removed. */
183
- text: string;
184
- /** Total output received so far; pass it back as `sinceOffset` to read only what follows. */
185
- nextOffset: number;
186
- running: boolean;
187
- }
188
- /**
189
- * The one owner of a task attempt's output: what it printed while running
190
- * and what it answered once it ended.
191
- */
192
- interface TaskOutputSource {
193
- /** Start capturing a session; replaces any earlier capture of the task. */
194
- attach(taskId: string, session: IRunnerSession): void;
195
- /** Stop capturing the task's session; what was captured stays readable. */
196
- detach(taskId: string): void;
197
- /** Drop every capture (a new plan reuses task ids). */
198
- reset(): void;
199
- /** The task's final answer: the summary its runner reported through `task_complete`, else what its last turn said. */
200
- finalText(taskId: string): string;
201
- /** The summary the runner reported through `task_complete` in its latest turn, if it made that call. */
202
- report(taskId: string): string | undefined;
203
- /** The handoff of that call, findings numbered (ADR-0047); undefined when the turn made none, or it handed nothing over. */
204
- handoff(taskId: string): StoredHandoff | undefined;
205
- /** Recent output of the task's latest capture, or null if it never had one. */
206
- liveTail(taskId: string, opts: LiveTailOptions): LiveTail | null;
207
- }
208
-
209
- /**
210
- * The deep module owning all plan-shaped state. Holds `planTasks` (the ordered
211
- * tree the user edits), the flattened `allTasks` view and the `taskMap` index.
212
- * The orchestrator calls `markCompleted`/`markFailed`/`markInProgress`/`retry`
213
- * to update task status — it never mutates task state directly.
214
- *
215
- * Completion and failure are read from `task.status` and nothing else, so
216
- * `isCompleted`, `completedCount`, `isAllComplete` and the scheduler's
217
- * dependency checks cannot disagree. The store owns its task objects: `load`
218
- * copies what it is given, the getters hand out readonly views, and
219
- * `snapshot` is the copy a caller may keep or write to disk.
220
- *
221
- * `rebuild` is the internal seam that keeps the flat views in sync with the
222
- * tree.
223
- *
224
- * `planRunners` lives here because it's part of the plan's identity (the runner
225
- * set, carried on the plan). `assertAssignedRunners` is pure store logic.
226
- */
227
- declare class PlanStore {
228
- private _planTasks;
229
- private _allTasks;
230
- private _taskMap;
231
- /** Task id → the top-level task it hangs under, itself for a top-level task. */
232
- private _rootMap;
233
- private _planRunners;
234
- private _onMutate;
235
- private _onTree;
236
- private _executionLog;
237
- /** Hook called after every structural mutation (add/remove/update/resetForRun). */
238
- set onMutate(cb: (() => void) | null);
239
- /**
240
- * Called with every task id, subtasks included, each time the tree is
241
- * rebuilt — on every route a task enters or leaves the plan, loads and
242
- * reconciled edits included, which `onMutate` does not hear.
243
- */
244
- set onTree(cb: ((ids: ReadonlySet<string>) => void) | null);
245
- get planTasks(): ReadonlyArray<Readonly<Task>>;
246
- get allTasks(): ReadonlyArray<Readonly<Task>>;
247
- get planRunners(): RunnerId[];
248
- get completedCount(): number;
249
- get failedCount(): number;
250
- isAllComplete(): boolean;
251
- isAnyFailed(): boolean;
252
- isCompleted(id: string): boolean;
253
- isSkipped(id: string): boolean;
254
- /** Nothing is left to do: every task completed or was skipped. */
255
- isFinished(): boolean;
256
- isFailed(id: string): boolean;
257
- get(taskId: string): Readonly<Task> | undefined;
258
- /**
259
- * Whether a task runs as an ops task (ADR-0020): a top-level AI task marked
260
- * so, or a script task, which is one by nature (ADR-0035). A subtask runs
261
- * with the top-level task it hangs under.
262
- */
263
- isOps(taskId: string): boolean;
264
- /** A copy of the task tree, detached from the store: later status changes do not reach it. */
265
- snapshot(): Task[];
266
- getExecutionLog(): ReadonlyArray<TaskSnapshot>;
267
- appendToLog(snapshot: TaskSnapshot): void;
268
- /**
269
- * Drop a task's archived snapshot. Un-marking a completion has to erase the
270
- * "finished" record too — dependent tasks are prompted from the log, so a
271
- * left-behind snapshot would keep feeding them a result that no longer exists.
272
- */
273
- removeFromLog(taskId: string): void;
274
- clearLog(): void;
275
- private notifyMutate;
276
- private countStatus;
277
- /**
278
- * `keepFailed` adopts failed tasks as failed: an edit reconciled into a run
279
- * is not a retry, and the tasks it blocks wait for one (ADR-0034).
280
- */
281
- load(tasks: ReadonlyArray<Readonly<Task>>, runners: readonly RunnerId[], opts?: {
282
- keepFailed?: boolean;
283
- }): void;
284
- add(partial: Partial<Task>): Task;
285
- remove(taskId: string): void;
286
- update(taskId: string, changes: Partial<Task>): Task | undefined;
287
- /**
288
- * Run preparation as one named op: flip every AI and script task to 'approved'. By
289
- * default completed tasks keep their status so a reloaded half-finished plan
290
- * resumes the remainder instead of re-running work that already succeeded,
291
- * and skipped ones stay skipped: a new run is not a retry of them.
292
- * `preserveCompleted: false` is for a freshly committed plan, where
293
- * everything starts over. `keepScheduledResumes` leaves a task waiting on
294
- * its runner's usage-limit reset waiting: an edit adopted between runs is
295
- * not the user starting over.
296
- */
297
- resetForRun(opts?: {
298
- preserveCompleted?: boolean;
299
- keepScheduledResumes?: boolean;
300
- }): void;
301
- markCompleted(id: string): void;
302
- markFailed(id: string): void;
303
- markSkipped(id: string, reason?: string): void;
304
- markInProgress(id: string): void;
305
- /** Its first attempt is starting: from now on its chat exists, whatever becomes of the attempt. */
306
- markAttempted(id: string): void;
307
- markAwaitingUser(id: string, reason?: AwaitingReason): void;
308
- /** When a task paused on a usage limit resumes on its own; undefined leaves it to a manual retry. */
309
- setTaskResumeAt(id: string, resumeAt: string | undefined): void;
310
- markPending(id: string): void;
311
- /**
312
- * A new attempt of the task, from nothing it earned. A loop's check starts
313
- * its rounds over (ADR-0045 L7), unless `sameRound`: a resume after a usage
314
- * limit goes on with the attempt the limit cut short.
315
- */
316
- retry(id: string, opts?: {
317
- sameRound?: boolean;
318
- }): void;
319
- /** A loop's check counts its rounds afresh from its next run (ADR-0045 L7). */
320
- startRoundsOver(id: string): void;
321
- /**
322
- * The loop runs its check again, as its next round (ADR-0045 L5): a new
323
- * attempt, the round counted on, and nothing of the round before kept on
324
- * the task — its report went to the body, and stays in its attempt's log.
325
- */
326
- repeat(id: string): void;
327
- /** Kept on the task: it was force-started past its merge gate, before these ancestors' work was merged (ADR-0020). */
328
- setForcedPastGate(id: string, dependencies: string[]): void;
329
- /** A reason outlives nothing: any status but `awaiting_user` drops it, and the resume time with it; any status drops a skip's reason. */
330
- private setStatus;
331
- setTaskVerdict(id: string, verdict: Task['verdict']): void;
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;
332
11
  /**
333
- * How the task's latest attempt ended, and the handoff it left (ADR-0047),
334
- * kept as its digest. An attempt that left no handoff clears the one before
335
- * it: the plan holds the latest attempt's, as it does the summary.
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).
336
15
  */
337
- setTaskOutputSummary(id: string, summary: Task['outputSummary'], handoff?: StoredHandoff): void;
338
- /** The owner is saved with the session it belongs to, and dropped with it. */
339
- setTaskRunnerSessionId(id: string, runnerSessionId: string | undefined, owner?: RunnerId): void;
340
- setCarriedFrom(id: string, runner: RunnerId): void;
341
- clearCarriedFrom(id: string): void;
342
- startAttemptUsage(id: string, attempt: number, runner: string, startedAt: string): void;
343
- recordAttemptUsage(id: string, attempt: number, record: UsageRecord, now: string): void;
344
- endAttemptUsage(id: string, attempt: number, now: string, runnerSessionCost?: RunnerSessionCost, sessionId?: string): void;
345
- setTaskAttemptSkills(id: string, skills: Task['attemptSkills']): void;
16
+ models?: AssignmentCatalog;
346
17
  /**
347
- * Widen the plan's runner set. Retargeting a task onto a runner the plan has
348
- * not used before has to land here as well as on the plan state, or
349
- * {@link resolveTaskRunner} reads the new runner as foreign and spawns the
350
- * plan's first one instead.
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.
351
21
  */
352
- admitRunner(runner: RunnerId): void;
353
- resolveTaskRunner(task: Readonly<Task>): RunnerId;
354
- private rebuild;
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;
355
25
  }
356
26
 
357
27
  /**
@@ -385,6 +55,10 @@ interface AssignmentCatalog {
385
55
  runnerModes?: Partial<Record<RunnerId, readonly RunnerModeInfo[]>>;
386
56
  /** Raw (unfiltered) allowlist, keyed by runner; read through {@link effectiveAllowlist}. */
387
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'];
388
62
  /** Full (true) or Guarded: the mode a derived task lands on unless it is read-only (ADR-0001). Full when absent. */
389
63
  autonomousDefault?: boolean;
390
64
  /**
@@ -556,377 +230,371 @@ declare function canSplitTask(tasks: readonly Task[], taskId: string): {
556
230
  declare function applyTaskOps(currentTasks: readonly Task[], ops: TaskOp[], runners: RunnerId[], catalog?: AssignmentCatalog, earlierHandles?: ReadonlyMap<string, string>): ApplyTaskOpsResult;
557
231
 
558
232
  /**
559
- * Task skills end to end (ADR-0024): a skill reaches a runner only by being
560
- * attached to its task in the plan, and Ordewell puts its body in the task's
561
- * prompt — so it works the same on every runner, and nothing is written to a
562
- * harness's own skills directory.
233
+ * The SSRF guard for anything that leaves the machine on the planner's behalf.
234
+ * An unguarded `http://169.254.169.254/` would hand a prompt-injected planner
235
+ * the cloud metadata credentials, so every outbound host is vetted here: IP
236
+ * literals by range, names by every address DNS answers with.
237
+ */
238
+ type ResolveAll = (host: string) => Promise<string[]>;
239
+
240
+ /**
241
+ * The one decision every planner web read goes through, whichever runner
242
+ * plans and whichever path carries the bytes (ADR-0027). A fetch names the
243
+ * URL it reads; a search names its query and the provider that receives it.
563
244
  */
564
- /** Where a task's skill names are looked up: global first, then the workspace folders it reads. */
565
- interface SkillLookup extends Pick<SkillsService, 'findSkill' | 'searchedDirs'> {
245
+ type WebRead = {
246
+ kind: 'fetch';
247
+ url: string;
248
+ }
249
+ /** `provider` keys the grant; `providerLabel`, when given, names it to the person asked. */
250
+ | {
251
+ kind: 'search';
252
+ query: string;
253
+ provider: string;
254
+ providerLabel?: string;
255
+ note?: string;
256
+ };
257
+ /** A refusal's `reason` is written for the planner: it says what to do next. */
258
+ type WebReadDecision = {
259
+ allowed: true;
260
+ } | {
261
+ allowed: false;
262
+ reason: string;
263
+ };
264
+ interface DecideOptions {
265
+ /**
266
+ * Research subagents run in the background where nothing can prompt
267
+ * (ADR-0008, T6). When false, a read that would ask is refused with the gap
268
+ * to report, rather than raising a prompt nobody can answer.
269
+ */
270
+ prompting?: boolean;
566
271
  /**
567
- * Set when tasks get worktrees: of `skills`, the workspace ones git will not
568
- * carry into them, each SKILL.md path mapped to the folder to commit,
569
- * relative to the workspace root.
272
+ * Where this read's decisions go, the first URL's and any redirect hop's,
273
+ * in place of the session's approval record. A read-only task's are its
274
+ * attempt's, never the planner's (ADR-0046), so its policy records nothing
275
+ * without one.
570
276
  */
571
- uncommitted?(skills: readonly SkillInfo[]): Promise<Map<string, string>>;
277
+ record?: DecisionRecord;
572
278
  }
573
- /**
574
- * Where a workspace's skills are read, known without asking git: the repos
575
- * of its group in layout order (`['.']` for one repository), and whether
576
- * tasks will get worktrees, which only a committed skill reaches.
577
- */
578
- interface SkillLayout {
579
- repos: readonly string[];
580
- worktrees: boolean;
279
+ type DecisionRecord = (req: ApprovalRequest, granted: boolean, source: ApprovalSource) => void;
280
+ interface WebReadPolicyOptions {
281
+ approval: IApproval;
282
+ /** The secrets Ordewell holds — provider keys, MCP tokens — read per request. */
283
+ secrets?: () => Iterable<string>;
284
+ /** Defaults to `process.env`; its credential-named values are secrets too. */
285
+ env?: () => Record<string, string | undefined>;
286
+ /** How many web reads one planner turn may make; read per request, so a settings change applies at once. */
287
+ budget?: () => number;
288
+ /** The planner turn in progress; a new one starts a new budget. */
289
+ turn?: () => string | undefined;
290
+ /** Override DNS resolution (every answer for a name) in tests. */
291
+ resolveHost?: ResolveAll;
292
+ /**
293
+ * The read-only task whose attempt reads (ADR-0046), by title, set by
294
+ * {@link WebReadPolicy.forTask}: named in approvals and refusals, and its
295
+ * budget is the policy's own for its whole life, whatever `turn` says.
296
+ */
297
+ task?: string;
298
+ }
299
+ declare const DEFAULT_WEB_READ_BUDGET = 40;
300
+ declare class WebReadPolicy {
301
+ private readonly options;
302
+ private readonly reader;
303
+ private readonly approval;
304
+ private readonly resolveHost;
305
+ private readonly secrets;
306
+ private readonly env;
307
+ private readonly budget;
308
+ private readonly turn;
309
+ private spent;
310
+ constructor(opts: WebReadPolicyOptions);
311
+ /**
312
+ * The same policy for one read-only task attempt (ADR-0046): the same
313
+ * approval, allowlist, live secrets and budget setting, with a count of its
314
+ * own that no planner turn resets and no other attempt spends. One per
315
+ * attempt, so a retry or a continue starts a fresh count.
316
+ */
317
+ forTask(task: string): WebReadPolicy;
318
+ /**
319
+ * Every check that needs no network runs first, then the budget and the
320
+ * approval, and only then is a fetch's host resolved: a lookup is itself a
321
+ * request, so `<data>.attacker.example` would leave through DNS even for a
322
+ * read that is then excluded, over budget or declined.
323
+ */
324
+ decide(read: WebRead, opts?: DecideOptions): Promise<WebReadDecision>;
325
+ private subagentRefusal;
326
+ /**
327
+ * Whether a search through `provider` would run without asking anyone, for
328
+ * a runner whose own search raises no request and is set on or off when
329
+ * its session starts (ADR-0027). A zero budget disables it; no query is
330
+ * known yet, so secrets and individual reads cannot be checked or counted.
331
+ */
332
+ searchesUnasked(provider: string): boolean;
333
+ /** The last check, the one that leaves the machine: every DNS answer for the host must be public. */
334
+ private resolved;
335
+ private refuse;
336
+ /**
337
+ * A planner's decisions reach the session's record, the conversation's
338
+ * approval cards; a task attempt's go only where its read says, since they
339
+ * are not the planner's (ADR-0046).
340
+ */
341
+ private recordFor;
342
+ private vetFetch;
343
+ private vetSearch;
344
+ /** Reads the current planner turn, or the task attempt, may still make, for the reader to see. */
345
+ remaining(): number;
346
+ private spend;
347
+ private refund;
348
+ private carriesSecret;
581
349
  }
582
350
 
583
- /**
584
- * Where the run's tasks will work: `false` in the shared workspace root;
585
- * otherwise each in its own worktree of every repo of the group (ADR-0013,
586
- * ADR-0014), which is what decides if tasks on the same file must be ordered.
587
- */
588
- type IsolatedExecution = false | RepoGroupLayout;
589
- interface PlannerModes {
590
- autonomousDefault: boolean;
591
- /** Not a toggle: a fact about the run. */
592
- isolatedExecution: IsolatedExecution;
351
+ /** What a caller that can go away hands a read: once `signal` aborts, nothing more leaves for it. */
352
+ interface WebReadOptions {
353
+ signal?: AbortSignal;
354
+ /** Where the read's decisions go instead of the session's record; see `DecideOptions.record`. */
355
+ record?: DecisionRecord;
356
+ }
357
+ interface IWebFetcher {
358
+ /**
359
+ * The web-read policy's decision (ADR-0027), for a read this fetcher will
360
+ * carry or one a runner carries natively, such as Claude's `WebFetch`.
361
+ * `fetch` and `search` decide for themselves; never call both.
362
+ */
363
+ decide(read: WebRead): Promise<WebReadDecision>;
364
+ /** Whether a runner's own search through `provider` would run unasked; see `WebReadPolicy.searchesUnasked`. */
365
+ searchesUnasked?(provider: string): boolean;
366
+ fetch(url: string, opts?: WebReadOptions): Promise<ToolOutcome>;
367
+ /**
368
+ * Optional web search. A fetcher without it makes the `web_search` tool
369
+ * report itself unavailable rather than failing the turn — the same
370
+ * degradation `fetch` already uses when no fetcher is wired at all.
371
+ */
372
+ search?(query: string, opts?: WebReadOptions): Promise<ToolOutcome>;
593
373
  /**
594
- * Where the run's tasks will look their skills up (ADR-0024), also a fact
595
- * about the run. A plan attaching a planner skill is refused against it,
596
- * and a coding agent's submit_plan call is refused in its turn rather than
597
- * accepted and then failed. Absent: the plan is not checked.
374
+ * A variant of this fetcher for a research subagent: same policy, cache,
375
+ * transport and budget, but a read that would ask a person is refused
376
+ * instead (ADR-0027, ADR-0008 T6). Absent means subagents get no web tools
377
+ * and the `fetch`/`web_search` tools report themselves unavailable.
378
+ */
379
+ forSubagent?(): IWebFetcher;
380
+ /**
381
+ * A variant for one read-only task attempt, `task` naming it (ADR-0046):
382
+ * a subagent's in never prompting, with a web-read budget of its own that
383
+ * no planner turn and no other attempt spends or resets. Absent, or
384
+ * undefined, means read-only tasks get no web reads, and their `fetch` and
385
+ * `web_search` report themselves unavailable.
598
386
  */
599
- taskSkills?: SkillLookup;
387
+ forTask?(task: string): IWebFetcher | undefined;
388
+ /** Reads the current budget still allows, where this fetcher's policy counts them. */
389
+ remaining?(): number | undefined;
600
390
  }
601
391
 
602
- type IsolationNoticeLevel = 'info' | 'warn' | 'error';
603
- /**
604
- * How an attempt that may run in a worktree gets one: fresh, the kept one for
605
- * a repair, restoring the previous attempt's work, or fresh and thrown away
606
- * whatever it changes, for a read-only task (ADR-0046).
607
- */
608
- type AttemptIntent = 'fresh' | 'repair' | 'readOnly' | PrepareIntent;
392
+ interface LiveTailOptions {
393
+ maxLines: number;
394
+ /** A previous {@link LiveTail.nextOffset}: only output after it is rendered. */
395
+ sinceOffset?: number;
396
+ }
397
+ interface LiveTail {
398
+ /** Plain text, escapes and control characters removed. */
399
+ text: string;
400
+ /** Total output received so far; pass it back as `sinceOffset` to read only what follows. */
401
+ nextOffset: number;
402
+ running: boolean;
403
+ }
609
404
  /**
610
- * What adopting a persisted run found: nothing to change, landed work git
611
- * shows the user has merged since, or a settled run that had nothing left to
612
- * hand over and was cleared up.
405
+ * The one owner of a task attempt's output: what it printed while running
406
+ * and what it answered once it ended.
613
407
  */
614
- type AdoptOutcome = 'unchanged' | 'changed' | 'cleared';
615
- interface AttemptWorkspace {
616
- cwd: string;
617
- worktree: boolean;
618
- /** The task workspace holding the attempt's worktrees, which `cwd` is in; set with `worktree` when it was prepared. */
619
- workspace?: string;
620
- /** Set for a `restore` in a worktree; absent in the checkout, where the previous attempt's edits remain. */
621
- previousWork?: PreviousWork;
408
+ interface TaskOutputSource {
409
+ /** Start capturing a session; replaces any earlier capture of the task. */
410
+ attach(taskId: string, session: IRunnerSession): void;
411
+ /** Stop capturing the task's session; what was captured stays readable. */
412
+ detach(taskId: string): void;
413
+ /** Drop every capture (a new plan reuses task ids). */
414
+ reset(): void;
415
+ /** The task's final answer: the summary its runner reported through `task_complete`, else what its last turn said. */
416
+ finalText(taskId: string): string;
417
+ /** The summary the runner reported through `task_complete` in its latest turn, if it made that call. */
418
+ report(taskId: string): string | undefined;
419
+ /** The handoff of that call, findings numbered (ADR-0047); undefined when the turn made none, or it handed nothing over. */
420
+ handoff(taskId: string): StoredHandoff | undefined;
421
+ /** Recent output of the task's latest capture, or null if it never had one. */
422
+ liveTail(taskId: string, opts: LiveTailOptions): LiveTail | null;
622
423
  }
424
+
623
425
  /**
624
- * What the controller reports. It never schedules or emits on its own: the
625
- * orchestrator turns these into observer events and decides what runs next.
426
+ * The deep module owning all plan-shaped state. Holds `planTasks` (the ordered
427
+ * tree the user edits), the flattened `allTasks` view and the `taskMap` index.
428
+ * The orchestrator calls `markCompleted`/`markFailed`/`markInProgress`/`retry`
429
+ * to update task status — it never mutates task state directly.
430
+ *
431
+ * Completion and failure are read from `task.status` and nothing else, so
432
+ * `isCompleted`, `completedCount`, `isAllComplete` and the scheduler's
433
+ * dependency checks cannot disagree. The store owns its task objects: `load`
434
+ * copies what it is given, the getters hand out readonly views, and
435
+ * `snapshot` is the copy a caller may keep or write to disk.
436
+ *
437
+ * `rebuild` is the internal seam that keeps the flat views in sync with the
438
+ * tree.
439
+ *
440
+ * `planRunners` lives here because it's part of the plan's identity (the runner
441
+ * set, carried on the plan). `assertAssignedRunners` is pure store logic.
626
442
  */
627
- interface IsolationRunListener {
443
+ declare class PlanStore {
444
+ private _planTasks;
445
+ private _allTasks;
446
+ private _taskMap;
447
+ /** Task id → the top-level task it hangs under, itself for a top-level task. */
448
+ private _rootMap;
449
+ private _planRunners;
450
+ private _onMutate;
451
+ private _onTree;
452
+ private _executionLog;
453
+ /** Hook called after every structural mutation (add/remove/update/resetForRun). */
454
+ set onMutate(cb: (() => void) | null);
628
455
  /**
629
- * The run record changed and should be persisted with the plan. `detached`
630
- * is set instead when the record that changed is no longer the plan's own —
631
- * a release that outlived the plan load that replaced its run — and belongs
632
- * to the session that was left.
456
+ * Called with every task id, subtasks included, each time the tree is
457
+ * rebuilt — on every route a task enters or leaves the plan, loads and
458
+ * reconciled edits included, which `onMutate` does not hear.
633
459
  */
634
- changed(detached?: IsolationRun): void;
635
- /** A run did not start: `repos` of the group have tracked changes. */
636
- blocked(repos: string[]): void;
637
- /** An isolated run closed and handed its integration branches over. */
638
- handoff(handoff: IsolationHandoff): void;
639
- /** The run was cleared up and forgotten: nothing was handed over, or it was discarded. */
640
- closed(): void;
641
- /** Already said through the notifications; also for a surface that shows no toasts. */
642
- notice(level: IsolationNoticeLevel, message: string): void;
643
- }
644
- interface IsolationRunControllerDeps {
645
- isolation: IWorktreeIsolation;
646
- config: IConfig;
647
- notifications: INotification;
648
- workspaceRoot: () => string;
649
- listener: IsolationRunListener;
460
+ set onTree(cb: ((ids: ReadonlySet<string>) => void) | null);
461
+ get planTasks(): ReadonlyArray<Readonly<Task>>;
462
+ get allTasks(): ReadonlyArray<Readonly<Task>>;
463
+ get planRunners(): RunnerId[];
464
+ get completedCount(): number;
465
+ get failedCount(): number;
466
+ isAllComplete(): boolean;
467
+ isAnyFailed(): boolean;
468
+ isCompleted(id: string): boolean;
469
+ isSkipped(id: string): boolean;
470
+ /** Nothing is left to do: every task completed or was skipped. */
471
+ isFinished(): boolean;
472
+ isFailed(id: string): boolean;
473
+ get(taskId: string): Readonly<Task> | undefined;
650
474
  /**
651
- * The tasks no clean-up of the whole run may take a worktree from: an
652
- * attempt is running them, or the plan has them in progress or waiting on
653
- * the user mid-attempt.
475
+ * Whether a task runs as an ops task (ADR-0020): a top-level AI task marked
476
+ * so, or a script task, which is one by nature (ADR-0035). A subtask runs
477
+ * with the top-level task it hangs under.
654
478
  */
655
- liveTasks: () => ReadonlySet<string>;
656
- }
657
- /**
658
- * The lifecycle of a plan's isolation run (ADR-0013, ADR-0014, ADR-0020),
659
- * between the scheduler and the git layer: deciding, at a run's first change
660
- * task, whether it executes in worktrees, a blocked run and how it goes on,
661
- * each attempt's working directory, releasing worktrees, which landed work the
662
- * user has merged, the tree check of an ops task, the handoff when the run
663
- * closes, and what the user does with it afterwards. The record it keeps
664
- * outlives one run — a resumed plan continues it.
665
- */
666
- declare class IsolationRunController {
667
- private readonly isolation;
668
- private readonly config;
669
- private readonly notifications;
670
- private readonly workspaceRoot;
671
- private readonly listener;
672
- private readonly liveTasks;
673
- private run;
674
- /** See {@link mergedRun}. */
675
- private merged;
676
- /** An adoption stays scoped even while cleanup temporarily leaves no run record. */
677
- private adoptions;
678
- /** Copied paths already reported for the current run: every task gets the same copies. */
679
- private reportedCopies;
680
- private reportedSetupFailures;
681
- /**
682
- * How the open run executes; null while no run is open, `undecided` until
683
- * its first change task asks. A run is one Execute-Plan or one manual task
684
- * run, from its start until it settles or is stopped.
685
- */
686
- private mode;
687
- /** Resolver task id → the conflicted task it resolves; see {@link linkResolver}. */
688
- private resolvers;
689
- private opening;
690
- private opened;
691
- /** Bumped by an interrupt, so an activation in flight across it does not open the run it ended. */
692
- private activations;
693
- /** The start a dirty tree turned away, handed back once the user chooses how to go on. */
694
- private blockedStart;
695
- /** The dirty repos behind {@link blockedStart}, for the stash notice. */
696
- private blockedRepos;
697
- constructor(deps: IsolationRunControllerDeps);
698
- /** The plan's run record, open or not; null when the plan has not isolated. */
699
- get current(): IsolationRun | null;
700
- /**
701
- * The id of the plan's last isolated run once it closed with everything it
702
- * landed merged into the user's branch and was cleared up: what tells a
703
- * Merge all asked for after it, which has nothing to merge, from one in a
704
- * plan that never isolated (ADR-0029). Null while the plan holds a run
705
- * record, and once a later run decides how it executes. Saved with the plan.
706
- */
707
- get mergedRun(): string | null;
708
- get isOpen(): boolean;
709
- /** How many runs have opened: what tells one run's launches from the next one's. */
710
- get epoch(): number;
711
- /** The open run has decided whether it isolates. */
712
- get decided(): boolean;
713
- /** The open run executes in worktrees. */
714
- get isolating(): boolean;
715
- /** A run is waiting on the user to stash or to go on without isolation. */
716
- get blocked(): boolean;
717
- /** What the plan persists of isolated execution; null when no run ever isolated. */
718
- get planIsolation(): PlanIsolation | null;
719
- /** The one guard of every action on the run: a plan that never isolated is a request refused, not a fault. */
720
- requireRun(): IsolationRun;
721
- /** A task's record in the open run, only while that run isolates. */
722
- openRecord(taskId: string): IsolationTaskRecord | undefined;
723
- /** Where a task's isolated work stands; null when the plan has no isolation run to speak of. */
724
- taskIsolation(taskId: string): TaskIsolation | null;
725
- view(): IsolationView | null;
726
- /**
727
- * Take over a plan's persisted isolation, or none for a plan that has not
728
- * isolated yet. Whatever a crashed process left behind for the run — a
729
- * worktree still marked active, a directory no record owns — is pruned,
730
- * while kept, failed and conflicted worktrees stay for the user. Then git is
731
- * asked which landed work the user has merged since the record was saved:
732
- * a settled run whose landed work is all in HEAD has nothing left to hand
733
- * over, so it is cleared up as Merge all would have, unless it still holds
734
- * work that never landed (ADR-0013).
735
- *
736
- * Deliberately not reported to the listener: adopting is not a change of the
737
- * session's own, and a host that adopts without persisting (VS Code's
738
- * restore) would otherwise write a new session file on every reload. What
739
- * git changed is returned instead, for the caller to save where it can and
740
- * announce. A plan loaded meanwhile supersedes this adoption, which then
741
- * touches nothing and resolves `unchanged`. `mergedRun` is the plan's saved
742
- * {@link mergedRun}.
743
- */
744
- adopt(state: PlanIsolation | null, mergedRun?: string): Promise<AdoptOutcome>;
745
- /** Open a run if none is, leaving whether it isolates to its first change task. */
746
- open(): void;
747
- /**
748
- * Decide once per run, as its first change task starts, whether it executes
749
- * in worktrees (ADR-0020): an ops task needs none, so a run of only ops
750
- * tasks never asks. `resume` is what a dirty tree parks until the user
751
- * chooses how to go on. Resolves false while the run waits on that choice.
752
- */
753
- decide(resume: () => Promise<void>): Promise<boolean>;
754
- /**
755
- * Whether the run in force, or else the next one, gives each task its own
756
- * worktrees, and of which repo group — what the planner is told, since it
757
- * decides whether tasks on the same file have to be ordered and which shared
758
- * paths two tasks must not edit at once. A tree that would block counts as
759
- * not isolating: the user may yet run without isolation, and ordering is
760
- * the safe rule then.
761
- */
762
- plannerLayout(): Promise<IsolatedExecution>;
763
- /**
764
- * Where the workspace's skills are read, and whether tasks get worktrees.
765
- * Read synchronously inside a planner turn or a chip edit, so it comes from
766
- * the files, not git or the planner's last layout: a restored session is
767
- * right from its first edit. The run's repos while it isolates; otherwise
768
- * the group the workspace holds, in worktrees unless isolation is off or
769
- * this run chose to share the root.
770
- */
771
- skillLayout(): SkillLayout;
772
- /**
773
- * Go on with the start a dirty tree turned away. `stash` puts the user's
774
- * tracked changes on the git stash first, so the run isolates; `shared` runs
775
- * this one run in the workspace root, knowingly. Returns the parked start for
776
- * the caller to replay; null when nothing was parked.
777
- */
778
- continueBlocked(how: 'stash' | 'shared'): Promise<(() => Promise<void>) | null>;
779
- /**
780
- * The working directory of an attempt that may run in a worktree — which
781
- * attempts do is the attempt kind's to say (see `attemptKind.attemptCwd`):
782
- * the worktree prepared for it in an isolated run, the kept one for a
783
- * conflict repair, else the workspace root. `worktree` says which. Does not
784
- * itself report a worktree it creates as a change — the caller does once
785
- * the attempt is committed as running, so a spawn abandoned or failed after
786
- * this settles is not misreported as a change that stuck.
787
- */
788
- attemptCwd(task: Task, intent: AttemptIntent): Promise<AttemptWorkspace>;
789
- /** The workspace root, for an attempt that runs outside any worktree. */
790
- workspaceCwd(task: Task): Promise<{
791
- cwd: string;
792
- worktree: boolean;
793
- }>;
794
- private reopenCwd;
795
- private prepareCwd;
479
+ isOps(taskId: string): boolean;
480
+ /** Every task's status and what it waits for, to tell cheaply whether one has changed since this was read. */
481
+ statusMark(): string;
482
+ /** A copy of the task tree, detached from the store: later status changes do not reach it. */
483
+ snapshot(): Task[];
484
+ getExecutionLog(): ReadonlyArray<TaskSnapshot>;
485
+ appendToLog(snapshot: TaskSnapshot): void;
796
486
  /**
797
- * Land a task's work on the run's integration branches. The record is
798
- * reported changed once the landing is set and before the first merge, so
799
- * a crash mid-landing leaves the tips to roll back to, and again once it
800
- * settles. A git error is `failed`, never a throw.
487
+ * Drop a task's archived snapshot. Un-marking a completion has to erase the
488
+ * "finished" record too — dependent tasks are prompted from the log, so a
489
+ * left-behind snapshot would keep feeding them a result that no longer exists.
801
490
  */
802
- integrate(task: Task, commit?: HandoffCommit): Promise<IsolationOutcome>;
803
- /** What a conflict repair's work shows (ADR-0015); git that cannot tell counts against it. */
804
- verifyRepair(task: Task): Promise<RepairEvidence>;
491
+ removeFromLog(taskId: string): void;
492
+ clearLog(): void;
493
+ private notifyMutate;
494
+ private countStatus;
495
+ load(tasks: ReadonlyArray<Readonly<Task>>, runners: readonly RunnerId[]): void;
496
+ add(partial: Partial<Task>): Task;
497
+ remove(taskId: string): void;
498
+ update(taskId: string, changes: Partial<Task>): Task | undefined;
805
499
  /**
806
- * Let go of a task's worktree. `keep` leaves it and its branch for
807
- * inspection; otherwise both go. A landing still in flight settles first, so
808
- * the worktree is never torn down underneath its merge. Bound to the run
809
- * the call was made in: a plan loaded meanwhile adopts its own run, whose
810
- * records share these task ids and are not this release's to touch.
500
+ * Run preparation as one named op: flip every AI and script task to 'approved'. By
501
+ * default completed tasks keep their status so a reloaded half-finished plan
502
+ * resumes the remainder instead of re-running work that already succeeded,
503
+ * and skipped ones stay skipped: a new run is not a retry of them. A failed
504
+ * task gets its fresh chance here, not at a load (ADR-0053).
505
+ * `preserveCompleted: false` is for a freshly committed plan, where
506
+ * everything starts over. `edited` is an edit adopted between runs, which is
507
+ * not the user starting over: a failed task stays failed, and a task waiting
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.
811
510
  */
812
- release(taskId: string, opts: {
813
- keep: boolean;
814
- }, landing?: Promise<unknown> | null): Promise<void>;
511
+ resetForRun(opts?: {
512
+ preserveCompleted?: boolean;
513
+ edited?: boolean;
514
+ keepFailed?: ReadonlySet<string>;
515
+ }): void;
815
516
  /**
816
- * Close the open run. An isolated one hands its integration branch over for
817
- * review — unless everything it landed is merged already, at merge gates or
818
- * by the run's automatic Merge all, which leaves nothing to hand over, so it
819
- * is cleared up as a Merge all would have.
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.
820
520
  */
821
- close(): Promise<void>;
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;
822
541
  /**
823
- * Whether a task's landed work still waits to be merged into the user's
824
- * branch — what holds an ops or user task at its merge gate (ADR-0020). A
825
- * task with no record never landed anything to wait for.
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.
826
545
  */
827
- awaitsMerge(taskId: string): boolean;
546
+ retry(id: string, opts?: {
547
+ sameRound?: boolean;
548
+ }): void;
828
549
  /**
829
- * Look again for landed work the user has merged — by Merge all, or by hand
830
- * with git. Resolves true when some was found.
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.
831
554
  */
832
- refreshInHead(): Promise<boolean>;
833
- /** Mark the run's landed work git finds in HEAD; true when some was found. */
834
- private findLanded;
835
- /** The run landed something and every landing is in the user's HEAD. */
836
- private allMerged;
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;
837
560
  /**
838
- * The tracked state of the workspace as an attempt whose tree is checked
839
- * starts (see `attemptKind.checksTree`), for {@link filesChangedSince}
840
- * (ADR-0020). Null where the check does not run: a run that shares the
841
- * workspace root, where every task's edits land in it, or a workspace git
842
- * cannot isolate at all.
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.
843
564
  */
844
- snapshotWorkspace(): Promise<TreeSnapshot | null>;
845
- /** Tracked files changed since `snapshot`; none when the run has since gone on in the workspace root. */
846
- filesChangedSince(snapshot: TreeSnapshot | null): Promise<string[]>;
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;
847
575
  /**
848
- * A stop or a plan load: the open run ends without a handoff and a parked
849
- * start is dropped. `keepOpen` spares a run the scheduler is still driving.
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.
850
579
  */
851
- interrupt(opts?: {
852
- keepOpen?: boolean;
853
- }): void;
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;
854
589
  /**
855
- * Mark which added task resolves which conflict. The resolver merges the
856
- * conflicted task's branch by hand in its own worktree; once that lands, the
857
- * conflicted task can land in turn — through the same merge, so a resolver
858
- * that did not really bring it along conflicts again instead of being taken
859
- * at its word.
860
- */
861
- linkResolver(resolverId: string, conflictedId: string): void;
862
- /** The conflicted task a landed resolver was added for, forgotten as it is returned. */
863
- takeResolver(resolverId: string): string | undefined;
864
- taskDiff(taskId: string, options: TaskDiffRead): Promise<TaskDiffResult>;
865
- reviewDiff(): Promise<string>;
866
- /**
867
- * "Merge all": the run's integration branches into whatever the user has
868
- * checked out, in every repo or none. During a run it merges what has
869
- * landed so far and the run goes on (ADR-0020): its branches stay for the
870
- * tasks still to land. Once a settled run merged everything, it has nothing
871
- * left to hand over, so it is cleared up and forgotten; a branch the user's
872
- * HEAD somehow does not contain stays for the next run's sweep.
873
- */
874
- merge(): Promise<IsolationMergeResult>;
875
- /**
876
- * Worktrees and task branches go; the integration branch and the record stay
877
- * for review and merge. Refused while a task of the run is still live.
878
- */
879
- cleanup(): Promise<void>;
880
- /**
881
- * The run and everything it made go, and the plan forgets it; the next run
882
- * starts afresh. Refused while a task of the run is still live.
883
- */
884
- discard(): Promise<void>;
885
- /** The records of tasks still live, whose worktrees no clean-up of the whole run may take. */
886
- private liveRecords;
887
- private refuseWhileLive;
888
- /**
889
- * Clear a settled run up once everything it landed is merged — and only
890
- * when nothing else is left in it: a live task, or a worktree holding work
891
- * that never landed, keeps the whole run, worktrees and branches, rather
892
- * than leaving that work to a deletion.
893
- */
894
- private clearMerged;
895
- /** The records that keep a settled run from being cleared up: live, or holding work that never landed. */
896
- private holdouts;
897
- /** The run's worktrees and merged branches go; false, said, when git would not let them. */
898
- private discardMerged;
899
- /** Say what a removal kept back instead of deleting: where the work went, or which worktree stayed. */
900
- private report;
901
- /** The run goes from the plan; `merged` names it as {@link mergedRun} when everything it landed was merged. */
902
- private forget;
903
- private assess;
904
- private activate;
905
- private begin;
906
- /** What earlier runs left merged in the group goes; a failure here is worth a word, never a stopped run. */
907
- private sweep;
908
- /**
909
- * The plan's run carries on while it still has any record: anything landed
910
- * means a resumed plan's dependents must start from a tip that holds their
911
- * predecessors' work, and anything held — a kept attempt, a conflict, a
912
- * repair — is work the user may still want, which a fresh run's mint would
913
- * delete. Only a run with no records at all is superseded.
914
- */
915
- private continuableRun;
916
- /**
917
- * A run with no records at all holds only superseded attempts, so it goes —
918
- * but its integration branch only where the user's HEAD contains it, as does
919
- * that of a run that cannot be continued for another reason (it ran from a
920
- * different workspace path): only the user gives landed work up.
921
- *
922
- * False when `interrupted` came while git was minting: a newer activation
923
- * may be minting its own run, so this one is never installed over it, and
924
- * is discarded rather than left as branches no record names.
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.
925
594
  */
926
- private mint;
927
- private reportCopies;
928
- private reportSetupFailures;
929
- private tell;
595
+ admitRunner(runner: RunnerId): void;
596
+ resolveTaskRunner(task: Readonly<Task>): RunnerId;
597
+ private rebuild;
930
598
  }
931
599
 
932
600
  /** A continue the user asked for: their message, and the runner session it resumes (ADR-0018, K1). */
@@ -953,6 +621,9 @@ interface Continuation {
953
621
  * - `carryOver`: a change task paused on `from`'s usage limit and moved to
954
622
  * another runner (ADR-0018, U1). A fresh session — none can cross runners —
955
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.
956
627
  */
957
628
  type AttemptKind = {
958
629
  readonly kind: 'change';
@@ -969,13 +640,17 @@ type AttemptKind = {
969
640
  } & Readonly<Continuation>) | {
970
641
  readonly kind: 'carryOver';
971
642
  readonly from: RunnerId;
643
+ } | {
644
+ readonly kind: 'delegated';
645
+ readonly readOnly: boolean;
972
646
  };
973
647
  /**
974
648
  * What a task's work is, whichever attempt does it: a change that lands, an
975
649
  * ops task's effects from the checkout (ADR-0020), or a read-only task's,
976
- * 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.
977
652
  */
978
- type TaskWork = 'change' | 'ops' | 'readOnly';
653
+ type TaskWork = 'change' | 'ops' | 'readOnly' | 'delegated' | 'delegatedReadOnly';
979
654
 
980
655
  /** Which repair of a task an attempt is, of the most `conflictRepairAttempts` allows. */
981
656
  interface RepairAttempt {
@@ -1027,6 +702,8 @@ interface LandingDeps {
1027
702
  tasks: Pick<PlanStore, 'get' | 'planTasks'>;
1028
703
  /** Whether a repair merge's message ends with the Planned-with trailer; read as the prompt is built. */
1029
704
  plannedWith?: () => boolean;
705
+ /** What this node's ancestors protect, which accumulates down a tree (ADR-0056 M4). */
706
+ inheritedProtectedPaths?: () => readonly string[];
1030
707
  }
1031
708
  /**
1032
709
  * Landing and Conflict repair (ADR-0013, ADR-0014, ADR-0015): a passed
@@ -1043,6 +720,7 @@ declare class Landing {
1043
720
  private readonly config;
1044
721
  private readonly tasks;
1045
722
  private readonly plannedWith;
723
+ private readonly inheritedProtectedPaths;
1046
724
  constructor(deps: LandingDeps);
1047
725
  /**
1048
726
  * Land a passed attempt's work. One whose worktree has no unlanded record
@@ -1067,6 +745,9 @@ declare class Landing {
1067
745
  * must not bring it in anyway.
1068
746
  */
1069
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;
1070
751
  /** A repair that ended without landing leaves the task as its conflict did: waiting on the user, worktree and refs kept. */
1071
752
  unrepaired(task: Task, why: string): Promise<Extract<LandingOutcome, {
1072
753
  kind: 'awaiting_user';
@@ -1085,8 +766,18 @@ declare class Landing {
1085
766
  outcome: Promise<LandingOutcome>;
1086
767
  } | null;
1087
768
  private landConflicted;
1088
- /** The repair a conflicted task is owed next; null when repair is off, used up, or there is no isolated run to repair it in. */
769
+ /**
770
+ * The repair a conflicted task is owed next; null when repair is off, used
771
+ * up, or there is no isolated run to repair it in. A resolver takes the
772
+ * conflict instead: a conflict has one worker (ADR-0015).
773
+ */
1089
774
  nextRepair(taskId: string): RepairAttempt | null;
775
+ /**
776
+ * The tasks in the plan still out to resolve this task's conflict. A skipped
777
+ * one has been given up, and a link to one that left the plan points at
778
+ * nothing, so either leaves the conflict free for another worker.
779
+ */
780
+ liveResolvers(taskId: string): Readonly<Task>[];
1090
781
  /** What a repair is asked to do; read once `reopen` has recorded the tips it must bring in. */
1091
782
  repairPrompt(task: Task): string;
1092
783
  private hasUnlandedWork;
@@ -1188,7 +879,9 @@ type TaskLogEvent =
1188
879
  /**
1189
880
  * A turn began by delivering `message`; `messageId` names it when it had been
1190
881
  * queued, `forced` when it was force sent. `from` on this and the other
1191
- * message events marks one Ordewell sent rather than the user.
882
+ * message events marks one Ordewell sent rather than the user. `startedAt`
883
+ * (ISO) is when it began, which a reload needs to date its open turn (#81);
884
+ * a log saved before it was kept has none.
1192
885
  */
1193
886
  | {
1194
887
  type: 'turn_start';
@@ -1196,6 +889,7 @@ type TaskLogEvent =
1196
889
  messageId?: string;
1197
890
  forced?: boolean;
1198
891
  from?: TaskMessageFrom;
892
+ startedAt?: string;
1199
893
  }
1200
894
  /**
1201
895
  * A turn began by running the runner's own command `/name args` (#66): the
@@ -1206,6 +900,7 @@ type TaskLogEvent =
1206
900
  name: string;
1207
901
  args?: string;
1208
902
  messageId?: string;
903
+ startedAt?: string;
1209
904
  }
1210
905
  /** What the running command printed, for one that runs without the model. */
1211
906
  | {
@@ -1444,7 +1139,56 @@ declare function scriptExitText(exit: Omit<Extract<TaskLogEvent, {
1444
1139
  */
1445
1140
  declare function digestTaskLog(events: readonly TaskLogEvent[], maxCalls: number): string;
1446
1141
 
1447
- /** 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). */
1448
1192
  interface TaskLogReader {
1449
1193
  /** The attempts of a task that have a saved log, oldest first. */
1450
1194
  attempts(taskId: string): number[];
@@ -1453,6 +1197,264 @@ interface TaskLogReader {
1453
1197
  patch?(taskId: string, attempt: number): Buffer | undefined;
1454
1198
  }
1455
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
+
1456
+ /** Where a start stands against the user's Stop (ADR-0029): no longer current once one comes. */
1457
+ type Admitted = Pick<StartAdmission, 'current'>;
1456
1458
  type AttemptPhase = 'starting' | 'running' | 'integrating';
1457
1459
  /** Read-only view of a task's live attempt. */
1458
1460
  interface TaskAttemptSnapshot {
@@ -1478,9 +1480,10 @@ type StartWait =
1478
1480
  kind: 'started';
1479
1481
  }
1480
1482
  /**
1481
- * A loop's check between rounds (ADR-0045 L3): the loop runs it again, as
1482
- * round `round` of `maxRounds`, once its body has passed and landed;
1483
- * `waiting` is the body that has not yet.
1483
+ * A loop's check between rounds (ADR-0045 L3): it runs again, as round
1484
+ * `round` of `maxRounds`, once its body has passed and landed; `waiting` is
1485
+ * the body that has not yet. Whether anything will run it then is no part
1486
+ * of the rule: a Stop or a reload leaves that to a start of it (L5).
1484
1487
  */
1485
1488
  | {
1486
1489
  kind: 'repeating';
@@ -1492,6 +1495,11 @@ type StartWait =
1492
1495
  | {
1493
1496
  kind: 'completing';
1494
1497
  }
1498
+ /** A task added to resolve its conflict has it: a conflict has one worker (ADR-0015). */
1499
+ | {
1500
+ kind: 'resolver';
1501
+ resolver: string;
1502
+ }
1495
1503
  /** An AI task with no prompt yet, or a script task with no body to run: there is nothing to run. */
1496
1504
  | {
1497
1505
  kind: 'no-prompt';
@@ -1538,6 +1546,11 @@ type StartWait =
1538
1546
  | {
1539
1547
  kind: 'start-hold';
1540
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
+ }
1541
1554
  /** Its merge gate (ADR-0020): the ancestors whose landed work is not in the user's branch yet. */
1542
1555
  | {
1543
1556
  kind: 'merge-gate';
@@ -1554,15 +1567,44 @@ type StartWait =
1554
1567
  * and goes past the whole gate and a planner turn's start hold. Queued behind
1555
1568
  * its dependencies, it starts later with nobody asking, so it waits for a
1556
1569
  * start hold as the scheduler's starts do, and goes past only the gate it was
1557
- * confirmed past: `pastGate`.
1570
+ * confirmed past: `pastGate`. `asScheduled` is one the start rule has already
1571
+ * passed whole, carried back to the queue if it has to wait again.
1558
1572
  */
1559
1573
  type ForcedStart = {
1560
1574
  queued: false;
1561
1575
  } | {
1562
1576
  queued: true;
1563
1577
  pastGate: readonly string[];
1578
+ asScheduled?: true;
1564
1579
  };
1565
1580
 
1581
+ type RevertRefusalReason = 'no-landing' | 'no-own-merge' | 'checked-out' | 'in-a-loop' | 'live-dependent' | 'in-branch' | 'git';
1582
+ interface RevertRefusal {
1583
+ reason: RevertRefusalReason;
1584
+ message: string;
1585
+ /** `live-dependent`: the tasks in the way. */
1586
+ tasks?: IsolationTaskRef[];
1587
+ /** `in-branch`: what undoes the work there, newest first, for the user to run. */
1588
+ commands?: RevertCommand[];
1589
+ }
1590
+ /** The tasks a revert would take, the one asked for first; `refusal` says why it may not go ahead, and with no landing there is nothing to take. */
1591
+ interface RevertScope {
1592
+ taskIds: string[];
1593
+ refusal?: RevertRefusal;
1594
+ }
1595
+ /** A task a preview lists; `dependent` is one that goes with the task the user chose, and `ref` how the plan calls it, a subtask's full path. */
1596
+ interface RevertPreviewTask extends IsolationRevertTask {
1597
+ dependent: boolean;
1598
+ ref?: string;
1599
+ }
1600
+ /** What a revert would do now, for a surface to confirm with the user before anything changes. */
1601
+ type TaskRevertPreview = {
1602
+ revertible: true;
1603
+ tasks: RevertPreviewTask[];
1604
+ } | ({
1605
+ revertible: false;
1606
+ } & RevertRefusal);
1607
+
1566
1608
  /** One reported window: what share of it is used, how long it is, and when it resets. */
1567
1609
  interface AccountLimitWindow {
1568
1610
  /** Which of a bucket's windows this is, in the order reported; only a label when the length is unknown. */
@@ -1621,16 +1663,190 @@ declare function accountLimitWindowLabel(window: AccountLimitWindow): string;
1621
1663
  /** `24% used`, `<1% used` for a sliver, `0% used` only when the runner said 0. */
1622
1664
  declare function accountLimitPercentText(percent: number): string;
1623
1665
  /**
1624
- * The compact row: `Codex 5h 24% used · Weekly 61% used`, with any other
1625
- * bucket after its own name, and `(stale)` when the read is not current.
1626
- * Null when there is nothing known to draw.
1627
- */
1628
- declare function accountLimitsText(snapshot: AccountLimitSnapshot): string | null;
1666
+ * The compact row: `Codex 5h 24% used · Weekly 61% used`, with any other
1667
+ * bucket after its own name, and `(stale)` when the read is not current.
1668
+ * Null when there is nothing known to draw.
1669
+ */
1670
+ declare function accountLimitsText(snapshot: AccountLimitSnapshot): string | null;
1671
+ /**
1672
+ * The row's hover: each window's exact share and reset, the plan, and how
1673
+ * current the read is. `time` words an instant in the reader's clock.
1674
+ */
1675
+ declare function accountLimitsTitle(snapshot: AccountLimitSnapshot, time: (at: Date) => string): string;
1676
+
1677
+ /**
1678
+ * The bridge between "core needs an answer" and "a human somewhere is looking
1679
+ * at a UI". Core cannot prompt: the human may be at a browser, a TUI, a CLI
1680
+ * stream, or a VS Code webview, and on the web server they are on the far end
1681
+ * of a socket. So the Session parks a promise here, announces the request
1682
+ * through the normal broadcast seam, and every surface answers through the
1683
+ * same `resolve(id, granted)` call.
1684
+ *
1685
+ * Timeouts are load-bearing rather than defensive: a planner turn that blocks
1686
+ * forever on an unanswered prompt would hang the whole research loop with no
1687
+ * visible cause. On expiry the request resolves to denied and the model gets a
1688
+ * normal, actionable tool result. A task runner's request is the exception
1689
+ * (ADR-0018, A1): the runner is parked on it rather than a research loop, and
1690
+ * it waits for a person — or the supervisor — however long that takes.
1691
+ */
1692
+ interface PendingApproval {
1693
+ id: string;
1694
+ request: ApprovalRequest;
1695
+ createdAt: string;
1696
+ }
1697
+ interface PendingApprovalsOptions {
1698
+ /** Denies and resolves after this long with no answer. Default 5 minutes. */
1699
+ timeoutMs?: number;
1700
+ /** Announce a new request to the surfaces. */
1701
+ onRequest?: (pending: PendingApproval) => void;
1702
+ /** Announce that a request is no longer actionable (answered or expired). */
1703
+ onSettled?: (id: string, granted: boolean, settled: {
1704
+ request: ApprovalRequest;
1705
+ decision: ApprovalDecision;
1706
+ }) => void;
1707
+ }
1708
+ interface AskOptions {
1709
+ /**
1710
+ * An id the caller already announced the request under. One still in use is
1711
+ * refused — denied at once — rather than overwriting the request that has it.
1712
+ */
1713
+ id?: string;
1714
+ /** Wait for an answer however long it takes. */
1715
+ noTimeout?: boolean;
1716
+ /**
1717
+ * Given the answer as the request settles, before anything awaiting the
1718
+ * promise runs: a caller about to tear down what asked (a runner being
1719
+ * stopped) must deliver the answer before it goes.
1720
+ */
1721
+ onDecision?: (decision: ApprovalDecision) => void;
1722
+ }
1723
+ declare class PendingApprovals {
1724
+ private readonly opts;
1725
+ private readonly entries;
1726
+ private readonly listeners;
1727
+ constructor(opts?: PendingApprovalsOptions);
1728
+ /** Hear each new request once it is announced — the supervisor wakes on a runner's (ADR-0029). */
1729
+ listen(listener: (pending: PendingApproval) => void): () => void;
1730
+ /** Park a request and return the promise the approval policy awaits. */
1731
+ ask(request: ApprovalRequest, options?: AskOptions): Promise<boolean>;
1732
+ /** Park a request whose answer is more than yes or no. */
1733
+ decide(request: ApprovalRequest, options?: AskOptions): Promise<ApprovalDecision>;
1734
+ /**
1735
+ * Answer one request. Returns false when the id is unknown or already
1736
+ * settled. "Allow for this task" on a request that did not offer it is a
1737
+ * plain allow: no grant is made that the requester never proposed.
1738
+ */
1739
+ resolve(id: string, answer: ApprovalAnswer): boolean;
1740
+ /** Everything still awaiting an answer — replayed to a surface that connects late. */
1741
+ outstanding(): PendingApproval[];
1742
+ /**
1743
+ * Deny everything in flight, or only the requests `which` picks. Called on
1744
+ * abort and on session reset.
1745
+ */
1746
+ clear(which?: (request: ApprovalRequest) => boolean, note?: string): void;
1747
+ }
1748
+
1749
+ /**
1750
+ * What an escalation waits on (ADR-0053). `approval`, `checkpoint`, `input`,
1751
+ * `conflict`, `files-changed` and `usage-limit` are a task's wait; `failed` and
1752
+ * `out-of-rounds` its end; `merge-gate`, `unapproved`, `on-hold`, `no-prompt`
1753
+ * and `by-hand` what alone keeps a task from starting; `merge-all` and
1754
+ * `blocked-run` are the run's own.
1755
+ */
1756
+ type EscalationKind = 'approval' | 'checkpoint' | 'input' | 'conflict' | 'files-changed' | 'usage-limit' | 'failed' | 'out-of-rounds' | 'merge-gate' | 'unapproved' | 'on-hold' | 'no-prompt' | 'by-hand' | 'merge-all' | 'blocked-run';
1757
+ type EscalationOptionId = 'allow' | 'allow-for-task' | 'deny' | 'approve' | 'reject' | 'reply' | 'resume' | 'continue' | 'retry' | 'run' | 'force-start' | 'skip' | 'complete' | 'merge-all' | 'resolve-conflict' | 'start-resolver' | 'stash' | 'run-without-isolation';
1758
+ /** One answer the user may give: what the wait's own card or command offers. */
1759
+ interface EscalationOption {
1760
+ id: EscalationOptionId;
1761
+ label: string;
1762
+ /** What this answer does with a note; absent when it takes none. */
1763
+ note?: 'optional' | 'required';
1764
+ /** The question a surface asks first, as it does on the card; the answer then carries the user's yes. */
1765
+ confirm?: string;
1766
+ }
1629
1767
  /**
1630
- * The row's hover: each window's exact share and reset, the plan, and how
1631
- * current the read is. `time` words an instant in the reader's clock.
1768
+ * One thing the run waits on a person for, derived and never stored
1769
+ * (ADR-0053). No deadline and no default answer: answering for the user on a
1770
+ * timer is a consent decision, which belongs to the mandate (#23).
1632
1771
  */
1633
- declare function accountLimitsTitle(snapshot: AccountLimitSnapshot, time: (at: Date) => string): string;
1772
+ interface Escalation {
1773
+ /**
1774
+ * Kind, task and attempt, then the approval, what a merge gate waits to have
1775
+ * merged, and how many of the attempt's waits ended before it; for Merge all,
1776
+ * the run and the attempts it would merge. A later wait is another
1777
+ * escalation, so a stale answer is told from a live one.
1778
+ */
1779
+ id: string;
1780
+ kind: EscalationKind;
1781
+ /** The task it belongs to, the link to its evidence; absent on the run's own. */
1782
+ task?: {
1783
+ id: string;
1784
+ ref: string;
1785
+ title: string;
1786
+ };
1787
+ /** The attempt it was raised in; absent before any ran. */
1788
+ attempt?: number;
1789
+ /** The runner request it answers, for `approval`. */
1790
+ approval?: string;
1791
+ /** The task out to resolve it, for a `conflict` nothing is starting that task for. */
1792
+ resolver?: string;
1793
+ reason: string;
1794
+ options: EscalationOption[];
1795
+ }
1796
+ /** What the list reads of the run. */
1797
+ interface EscalationRun {
1798
+ /** A full-plan run is scheduling, held, or ended on a failure a retry continues: only then, or in a carried loop, does a task that has not started wait on anyone, a reverted one apart. */
1799
+ open: boolean;
1800
+ /** Something could move a task on unasked, so the seam refuses a Mark complete (ADR-0029). */
1801
+ advancing: boolean;
1802
+ /** A Merge all is under way. */
1803
+ merging: boolean;
1804
+ /** Ordewell merges at gates by itself: the setting is on, and no merge has stopped it. */
1805
+ autoMerges: boolean;
1806
+ /** The run's close merges what landed by itself, so nobody has to; a Stop or a merge that stopped ends this, and the wait is real. */
1807
+ mergesAtClose: boolean;
1808
+ /** What Merge all would merge now. */
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[];
1812
+ /** The isolated run's id. */
1813
+ id: string | null;
1814
+ /** Which of the session's starts a dirty checkout turned away, when one waits on it; null when none does. */
1815
+ blocked: number | null;
1816
+ }
1817
+ /** What the list is derived from: nothing here is kept for it. */
1818
+ interface EscalationInput {
1819
+ /** The plan tree. */
1820
+ tasks: readonly Task[];
1821
+ /** Every task's start waits; see `planStartWaits`. */
1822
+ waits: ReadonlyMap<string, readonly StartWait[]>;
1823
+ approvals: readonly PendingApproval[];
1824
+ /** The task's latest attempt, 0 before any ran. */
1825
+ attempt: (task: Readonly<Task>) => number;
1826
+ /** An attempt of the task is live, so a runner is there to hear an answer. */
1827
+ live: (taskId: string) => boolean;
1828
+ /** What the task's waiting checkpoint asks, whole. */
1829
+ checkpoint: (taskId: string) => string | undefined;
1830
+ /** Added since the user approved the plan, and left for them (ADR-0035). */
1831
+ waitsForApproval: (taskId: string) => boolean;
1832
+ /** Queued to start with no scheduler, once what holds it clears. */
1833
+ startQueued: (taskId: string) => boolean;
1834
+ /** Why the task is on hold. */
1835
+ holdReason: (taskId: string) => string | null | undefined;
1836
+ /** Something will run this check between rounds without being asked (ADR-0045 L5). */
1837
+ loopRunsOn: (taskId: string) => boolean;
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). */
1839
+ carried: (taskId: string) => boolean;
1840
+ run: EscalationRun;
1841
+ }
1842
+ /**
1843
+ * Everything that will not move without a person (ADR-0053), in the order of
1844
+ * what stops the most first: a run that cannot start, then the plan's tasks in
1845
+ * order, then the work waiting to be merged. Pure and derived, as `planBlocks`
1846
+ * and `startWaits` are: nothing is stored, so nothing has to be cleared, and it
1847
+ * survives a reload as the waits it reads do.
1848
+ */
1849
+ declare function escalations(input: EscalationInput): Escalation[];
1634
1850
 
1635
1851
  type SerializedTaskStatus = {
1636
1852
  id: string;
@@ -1695,6 +1911,8 @@ type SerializedTaskStatus = {
1695
1911
  waitsForApproval?: true;
1696
1912
  /** Why a task is held out of auto-scheduling, in the user's words; absent when it is not held. */
1697
1913
  holdReason?: string;
1914
+ /** A loop's check between rounds that nothing will run, after a Stop or a reload (ADR-0045 L5). Absent for any other task. */
1915
+ loopStranded?: true;
1698
1916
  /** Set only on a research task, so a stream of statuses alone can say which task is one (ADR-0037). */
1699
1917
  research?: true;
1700
1918
  /** The latest attempt's handoff, as the plan keeps it (ADR-0047); absent when the attempt left none. */
@@ -1706,6 +1924,8 @@ type SerializedTaskStatus = {
1706
1924
  loop?: TaskLoop;
1707
1925
  /** The round a loop's check is in, past its first (ADR-0045 L5); absent on round 1 and on any other task. */
1708
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;
1709
1929
  };
1710
1930
  /**
1711
1931
  * Landed work not yet in the user's branch. `paused` means the running plan
@@ -1760,6 +1980,8 @@ type SerializedTask = {
1760
1980
  loop?: TaskLoop;
1761
1981
  /** As on {@link SerializedTaskStatus}: only on a loop's check past its first round. */
1762
1982
  loopRound?: number;
1983
+ /** What a delegated task's child plan must achieve (ADR-0055 D1); absent on any other task. */
1984
+ goal?: string;
1763
1985
  };
1764
1986
  /** Skill loads go out as notices: their bodies are for the planner, and stay in the session. */
1765
1987
  type SerializedConversationMessage = Omit<ConversationMessage, 'skill'> & {
@@ -1862,6 +2084,8 @@ type SessionMessage =
1862
2084
  * digests the turn delivers, shown ahead of the prompt; `supervisor` marks a
1863
2085
  * wake-up turn, which no user message opened (ADR-0029). `forced`: the
1864
2086
  * prompt was force sent, and this turn replaces the one it stopped.
2087
+ * `startedAt` (ISO) is when it began, which a surface counts its elapsed
2088
+ * time from (#81); an older daemon sends none.
1865
2089
  */
1866
2090
  | {
1867
2091
  type: 'planner_turn_started';
@@ -1871,6 +2095,7 @@ type SessionMessage =
1871
2095
  wakeUps?: string[];
1872
2096
  supervisor?: true;
1873
2097
  forced?: true;
2098
+ startedAt?: string;
1874
2099
  } | {
1875
2100
  type: 'planner_skill_loaded';
1876
2101
  turnId: string;
@@ -1981,12 +2206,26 @@ type SessionMessage =
1981
2206
  type: 'planner_compaction';
1982
2207
  compaction: PlannerCompaction;
1983
2208
  turnId?: string;
1984
- } | {
2209
+ }
2210
+ /**
2211
+ * A planner turn waits for the probe of installed coding agents (ADR-0009),
2212
+ * or nothing does any longer. While `waiting`, no planner has heard of that
2213
+ * turn, so a surface says so rather than that it is planning. It ends with
2214
+ * the probe, whatever became of the turn: one stopped at the gate leaves it
2215
+ * `true` for the next to wait under.
2216
+ */
2217
+ | {
2218
+ type: 'planner_detection';
2219
+ waiting: boolean;
2220
+ }
2221
+ /** `escalations`: what waits on a person now (ADR-0053), whole; absent from an older daemon. */
2222
+ | {
1985
2223
  type: 'status_update';
1986
2224
  tasks: SerializedTaskStatus[];
1987
2225
  runMerge?: RunMergeView;
1988
2226
  usage?: PlanUsage;
1989
2227
  activity?: RunActivity;
2228
+ escalations?: Escalation[];
1990
2229
  } | {
1991
2230
  type: 'review_needed';
1992
2231
  tasks: SerializedTask[];
@@ -2100,7 +2339,7 @@ type PlanSnapshot = {
2100
2339
  plan: SurfacePlan | SurfacePlanState;
2101
2340
  };
2102
2341
  /** What only the status stream says about a task; a plan refresh keeps these as they were. */
2103
- type RunTaskStatus = Partial<Pick<SerializedTaskStatus, 'verdict' | 'idleSince' | 'completionReceived' | 'attaching' | 'isolation' | 'checkpoint' | 'queued' | 'continuable' | 'awaitingApproval' | 'mergeGate' | 'forcedPastGate' | 'waitsForApproval' | 'holdReason' | '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'>>;
2104
2343
  /** A task in the run view: the plan's fields, then the status stream's. */
2105
2344
  type RunTask = Omit<SerializedTask, 'subtasks'> & RunTaskStatus & {
2106
2345
  subtasks: RunTask[];
@@ -2147,6 +2386,8 @@ interface RunView {
2147
2386
  mergeResult: IsolationMergeResult | null;
2148
2387
  /** What the scheduler is doing with the plan, as {@link RunActivity}. */
2149
2388
  activity?: RunActivity;
2389
+ /** What will not move without a person (ADR-0053), as the newest status update said; absent from an older daemon's snapshot. */
2390
+ escalations?: Escalation[];
2150
2391
  }
2151
2392
  type RunInput = SessionMessage | PlanSnapshot;
2152
2393
  /**
@@ -2162,7 +2403,7 @@ type SessionNotice = {
2162
2403
  };
2163
2404
  type SessionBroadcaster = (msg: SessionMessage) => void;
2164
2405
  declare function serializeTask(t: SurfaceTask): SerializedTask;
2165
- 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): 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;
2166
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. */
2167
2408
  declare function surfacePlan({ queuedMessages: _carriedOverAtLoad, ...plan }: LegacyPlanState): SurfacePlan;
2168
2409
  declare function surfacePlanState(state: PlanState): SurfacePlanState;
@@ -2252,9 +2493,11 @@ declare class SessionHeldError extends Error {
2252
2493
  interface SessionLease {
2253
2494
  /**
2254
2495
  * Hold `sessionId`'s lease in `workspace`, letting go of the one held before
2255
- * only once this one is held. Throws {@link SessionHeldError}.
2496
+ * only once this one is held. Throws {@link SessionHeldError}. `commit` runs
2497
+ * holding both: when it throws, the one held before is still held, and a
2498
+ * lease this claim took is let go.
2256
2499
  */
2257
- claim(sessionId: string, workspace: string): void;
2500
+ claim(sessionId: string, workspace: string, commit?: () => void): void;
2258
2501
  /**
2259
2502
  * Null while this Session holds `sessionId`'s lease, claiming it when no
2260
2503
  * live host holds it; otherwise who holds it. A lease another host took from
@@ -2335,7 +2578,7 @@ declare class HostedLease implements SessionLease {
2335
2578
  workspace: string;
2336
2579
  private lostLease;
2337
2580
  constructor(host: LeaseHost, onLost: (held: SessionHeld) => void);
2338
- claim(sessionId: string, workspace: string): void;
2581
+ claim(sessionId: string, workspace: string, commit?: () => void): void;
2339
2582
  hold(sessionId: string, workspace: string): SessionHeld | null;
2340
2583
  release(): void;
2341
2584
  /** @internal Another host took `file`'s lease from this one. */
@@ -2348,6 +2591,115 @@ declare class HostedLease implements SessionLease {
2348
2591
  */
2349
2592
  declare function processLeaseHost(): LeaseHost;
2350
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
+
2351
2703
  /** What ran an automatic Merge all: a task reaching its merge gate, or the run settling with landed work unmerged. */
2352
2704
  type AutoMergeTrigger = {
2353
2705
  kind: 'gate';
@@ -2357,10 +2709,12 @@ type AutoMergeTrigger = {
2357
2709
  };
2358
2710
  /**
2359
2711
  * Who opened a run (ADR-0029): a call the user made, or one made for them; a
2360
- * call of the supervisor's own; or a usage-limit reset resuming what it paused.
2361
- * Only the user's starts a run's supervision afresh.
2712
+ * call of the supervisor's own, or a subplanner's, which has no user to make
2713
+ * one for (ADR-0056 M6); a usage-limit reset resuming what it paused; or a loop
2714
+ * carried on with no scheduler (ADR-0045 L5). Only the user's starts a run's
2715
+ * supervision afresh.
2362
2716
  */
2363
- type RunOrigin = 'user' | 'supervisor' | 'limit-reset';
2717
+ type RunOrigin = 'user' | 'supervisor' | 'limit-reset' | 'loop';
2364
2718
  /** What the scheduler reads of the open run's settings, at each decision it makes from them. */
2365
2719
  type SchedulerRunSettings = Pick<RunSettings, 'mergeAtGatesAutomatically'>;
2366
2720
  /**
@@ -2485,6 +2839,8 @@ interface TaskOrchestratorDeps {
2485
2839
  runner: IRunner;
2486
2840
  /** Runs script tasks' bodies (ADR-0035), in place of a registered runner. */
2487
2841
  scriptRunner: IRunner;
2842
+ /** Runs delegated tasks' child plans (ADR-0055 D3). */
2843
+ childPlanRunner: IRunner;
2488
2844
  store: PlanStore;
2489
2845
  output: TaskOutputSource;
2490
2846
  /** The plan's isolation run and the open run's lifecycle (ADR-0013). */
@@ -2518,8 +2874,14 @@ interface TaskOrchestratorDeps {
2518
2874
  * owns it, and the scheduler starts nothing outside it.
2519
2875
  */
2520
2876
  isApproved: (taskId: string) => boolean;
2877
+ /** Read beside the run's setting at each automatic merge, which passes no command (ADR-0056 M6). */
2878
+ mayMergeUnasked: () => boolean;
2879
+ /** The run's changes to git in flight; absent where no session's lease stands behind them. */
2880
+ runChanges?: RunChanges;
2521
2881
  /** A read-only attempt's shell (ADR-0046); absent, its runner is given no `run_command`. */
2522
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;
2523
2885
  /** The session's web fetcher for one read-only attempt (ADR-0046); absent, its `fetch` and `web_search` are not available. */
2524
2886
  taskWebFetcher?: (task: string) => IWebFetcher | undefined;
2525
2887
  /** Keep a read-only attempt's saved patch beside its log (ADR-0050); absent, `save_patch` is not available. */
@@ -2529,8 +2891,47 @@ interface TaskOrchestratorDeps {
2529
2891
  * ran (ADR-0047). A task that ran no session, a user task, gets a log of its own.
2530
2892
  */
2531
2893
  recordHandoff: (sessionId: string | undefined, taskId: string, handoff: StoredHandoff) => void;
2532
- /** 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). */
2533
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;
2534
2935
  }
2535
2936
  /**
2536
2937
  * What a caller supplies to {@link TaskOrchestrator.compose}; every optional
@@ -2538,17 +2939,28 @@ interface TaskOrchestratorDeps {
2538
2939
  * resolved wiring the orchestrator itself runs on.
2539
2940
  */
2540
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;
2541
2950
  /** What starting a research task owes the planner (ADR-0037 R3). */
2542
2951
  research?: Pick<ResearchTasks, 'started'>;
2543
2952
  config: IConfig;
2544
2953
  notifications: INotification;
2545
2954
  runner: IRunner;
2546
2955
  scriptRunner?: IRunner;
2956
+ childPlanRunner?: IRunner;
2547
2957
  store?: PlanStore;
2548
2958
  output?: TaskOutputSource;
2549
2959
  isolation?: IWorktreeIsolation;
2550
2960
  registry?: RunnerRegistry | null;
2551
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;
2552
2964
  workspaceEnv?: (cwd: string) => Promise<WorkspaceEnv>;
2553
2965
  skillsAt?: (roots: readonly string[]) => SkillLookup;
2554
2966
  previousAttemptFromLog?: (taskId: string) => string | null;
@@ -2559,7 +2971,11 @@ interface TaskOrchestratorOptions {
2559
2971
  plannedWith?: () => boolean;
2560
2972
  /** Every task is approved unless a Session supplies its record: an orchestrator on its own has no user to ask. */
2561
2973
  isApproved?: (taskId: string) => boolean;
2974
+ /** Whether this node's mandate lets an automatic merge run; true unless set. */
2975
+ mayMergeUnasked?: () => boolean;
2562
2976
  taskShell?: (site: TaskShellSite) => TaskShell | undefined;
2977
+ /** See {@link TaskOrchestratorDeps.unsealedRefusal}. */
2978
+ unsealedRefusal?: (task: Readonly<Task>) => string | null;
2563
2979
  taskWebFetcher?: (task: string) => IWebFetcher | undefined;
2564
2980
  keepPatch?: (taskId: string, attempt: number, patch: Buffer) => string;
2565
2981
  recordHandoff?: (sessionId: string | undefined, taskId: string, handoff: StoredHandoff) => void;
@@ -2569,6 +2985,30 @@ interface TaskOrchestratorOptions {
2569
2985
  * to the run (ADR-0049); the run is changed only while it answers null.
2570
2986
  */
2571
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>;
2572
3012
  }
2573
3013
  /**
2574
3014
  * The pure scheduler. Owns execution state (the {@link RunState},
@@ -2583,6 +3023,7 @@ interface TaskOrchestratorOptions {
2583
3023
  * and does not unblock dependent work.
2584
3024
  */
2585
3025
  declare class TaskOrchestrator {
3026
+ private readonly deps;
2586
3027
  private config;
2587
3028
  private notifications;
2588
3029
  private store;
@@ -2592,12 +3033,20 @@ declare class TaskOrchestrator {
2592
3033
  private runState;
2593
3034
  /** Planner turns in flight that new starts wait for (ADR-0029); running attempts carry on. */
2594
3035
  private startHolds;
3036
+ /** The judgement a {@link judgingOnce} read shares, while one runs. */
3037
+ private judgement;
3038
+ /** The {@link revertReach} of each revert under way: unlike a hold, it stops a start admitted before the revert too. */
3039
+ private readonly reverting;
2595
3040
  /** Bumped by the user's Stop: a start admitted under an older generation never takes its claim. */
2596
3041
  private stopGeneration;
2597
3042
  /** Starts admitted and not yet claimed or let go; see {@link StartAdmission}. */
2598
3043
  private admissions;
2599
3044
  private reviewApproved;
2600
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;
2601
3050
  private mergesInFlight;
2602
3051
  private retryCounts;
2603
3052
  /** A Merge all is under way: no ops task starts until it ends (ADR-0020). */
@@ -2628,6 +3077,18 @@ declare class TaskOrchestrator {
2628
3077
  private queuedStarts;
2629
3078
  /** Conflict repairs owed with every slot taken and no scheduler to claim them: a freed slot starts them. */
2630
3079
  private owedRepairs;
3080
+ /**
3081
+ * The checks whose loop goes on with no scheduler: each failed a round in a
3082
+ * run the scheduler was not driving, so nothing else would start what it
3083
+ * owes (ADR-0045 L5). Held here and not on the plan, because a Stop ends it
3084
+ * and a reload does not bring it back; an edit does neither. A loop stopped
3085
+ * by its body stays here, so a retry of that task resumes it (L7).
3086
+ */
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;
3090
+ /** What was last said of a carried loop's approvals, so it is said once. */
3091
+ private loopNotice;
2631
3092
  /**
2632
3093
  * Tasks Mark complete is landing after it ended their attempt, or that
2633
3094
  * their conflict resolver is landing, and that landing: in progress with
@@ -2639,6 +3100,13 @@ declare class TaskOrchestrator {
2639
3100
  private releasing;
2640
3101
  /** Tasks paused on a usage limit, the run's hold on them, and each runner's wait for its reset (ADR-0018, U1). */
2641
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;
2642
3110
  /**
2643
3111
  * The plan's isolation run and the open run's lifecycle (ADR-0013); see
2644
3112
  * {@link IsolationRunController}. Read and acted on there directly, except
@@ -2646,12 +3114,18 @@ declare class TaskOrchestrator {
2646
3114
  * ops tasks and starts the tasks its merge lets through.
2647
3115
  */
2648
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;
2649
3122
  /** A passed attempt's work onto the integration branch, and the conflict repair a landing may need. */
2650
3123
  private landing;
2651
3124
  private observers;
2652
3125
  private previousRounds;
2653
3126
  private runSettings;
2654
3127
  private isApproved;
3128
+ private mayMergeUnasked;
2655
3129
  constructor(deps: TaskOrchestratorDeps);
2656
3130
  /**
2657
3131
  * The composition root: resolves every optional collaborator and wires the
@@ -2670,12 +3144,14 @@ declare class TaskOrchestrator {
2670
3144
  /** Recent clean output of a task's latest attempt, running or ended; null if it never ran. */
2671
3145
  getLiveOutput(taskId: string, opts: LiveTailOptions): LiveTail | null;
2672
3146
  get storeInstance(): PlanStore;
2673
- approveCheckpoint(taskId: string): void;
3147
+ approveCheckpoint(taskId: string, note?: string): void;
2674
3148
  rejectCheckpoint(taskId: string, reason?: string): void;
2675
3149
  /** What the task's waiting checkpoint asks, whole; undefined once it settles or when none waits. */
2676
3150
  getCheckpointQuestion(taskId: string): string | undefined;
2677
3151
  /** Whether the task waits at a checkpoint its live attempt can still answer. */
2678
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;
2679
3155
  /**
2680
3156
  * Send a structured task a user message (ADR-0018, M1). Mid-turn it queues
2681
3157
  * behind the turn; a task waiting for input takes it at once and is back in
@@ -2719,6 +3195,10 @@ declare class TaskOrchestrator {
2719
3195
  get isReviewApproved(): boolean;
2720
3196
  /** The scheduler is driving the plan: ready tasks start on their own as slots free. Narrower than {@link isRunning}. */
2721
3197
  get isScheduling(): boolean;
3198
+ /** A scheduled run, driving the plan or held by a usage limit that gives it back: a hold never closes one. */
3199
+ get scheduledRun(): boolean;
3200
+ /** A usage limit holds the run, whatever it goes back to at the reset. */
3201
+ get runHeld(): boolean;
2722
3202
  /**
2723
3203
  * A full-plan run with nothing starting that something still resumes: a
2724
3204
  * usage limit's reset, or a retry of a failed task its blocked tasks wait on.
@@ -2729,6 +3209,8 @@ declare class TaskOrchestrator {
2729
3209
  /** Task id → session id of every attempt whose runner is up; a spawn in flight has no session yet. */
2730
3210
  get activeSessionMap(): Map<string, string>;
2731
3211
  getAttempt(taskId: string): TaskAttemptSnapshot | undefined;
3212
+ /** The number of the task's latest attempt, live or ended; 0 before any ran. */
3213
+ latestAttempt(task: Readonly<Task>): number;
2732
3214
  /** A planner turn holds new starts: what is ready starts when it releases them. */
2733
3215
  get startsHeld(): boolean;
2734
3216
  /**
@@ -2748,11 +3230,20 @@ declare class TaskOrchestrator {
2748
3230
  * Something can still start that nobody has to ask for again: a start
2749
3231
  * admitted and not yet claimed, a force start queued behind its
2750
3232
  * dependencies, a conflict repair owed a slot or the end of a start hold,
2751
- * a task that resumes on its own at its usage limit's reset, or a start a
2752
- * dirty checkout parked for Stash and continue to replay. A Stop is owed to
2753
- * it, whether or not anything runs.
3233
+ * a task that resumes on its own at its usage limit's reset, a start a
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.
3237
+ * A carried loop a body task stopped starts nothing, so it is not here: see
3238
+ * {@link loopCarried}.
2754
3239
  */
2755
3240
  get startsPending(): boolean;
3241
+ /**
3242
+ * A loop is carried on, one a body task stopped included. Nothing of that
3243
+ * one starts by itself, so nothing here moves a task on, but a retry that
3244
+ * passes would resume it: a Stop is owed to it too (ADR-0045 L7).
3245
+ */
3246
+ get loopCarried(): boolean;
2756
3247
  /**
2757
3248
  * The plan's activity as one word for every surface: a start on its way,
2758
3249
  * work running, a scheduler driving the plan, or a run that resumes by
@@ -2772,7 +3263,9 @@ declare class TaskOrchestrator {
2772
3263
  */
2773
3264
  get advancesOnItsOwn(): boolean;
2774
3265
  /** A start admitted now that nothing has to count as live work: one that claims before it awaits, or follows on from one that did. */
2775
- private admittedNow;
3266
+ admittedNow(): Admitted;
3267
+ /** The user's Stop found nothing to stop: it still withdraws every start admitted before it, one no live work counts included (ADR-0029). */
3268
+ stopAsked(): void;
2776
3269
  /** Run `work`, which awaits before it claims, under an admission it holds throughout and claims under. */
2777
3270
  private whileAdmitted;
2778
3271
  /**
@@ -2790,6 +3283,15 @@ declare class TaskOrchestrator {
2790
3283
  * over as before.
2791
3284
  */
2792
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;
3288
+ /**
3289
+ * Whether closing the run merges what landed, with nobody asked. The run
3290
+ * says it has ended before it closes, and a list read in between must not
3291
+ * name a Merge all it is about to do itself (ADR-0053). A Stop takes the run
3292
+ * out of isolating and a merge that stopped blocks this, so neither wait is hidden.
3293
+ */
3294
+ get mergesAtClose(): boolean;
2793
3295
  /**
2794
3296
  * The run as a surface offers Merge all mid-run (ADR-0020): what has landed
2795
3297
  * and is not merged, and whether the run is paused at its gates with nothing
@@ -2812,9 +3314,48 @@ declare class TaskOrchestrator {
2812
3314
  * Takes the task, not its id: the one the command seam located.
2813
3315
  */
2814
3316
  startWaits(task: Readonly<Task>, forced?: ForcedStart): StartWait[];
3317
+ /** Every task's {@link startWaits}, the plan judged once. */
3318
+ planStartWaits(): Map<string, StartWait[]>;
3319
+ /**
3320
+ * Run `read`, a synchronous read of the plan, on one judgement of it: what a
3321
+ * read asks task by task would otherwise judge the whole plan per task.
3322
+ * Nothing can change the plan inside it, so no answer can be stale.
3323
+ */
3324
+ judgingOnce<T>(read: () => T): T;
2815
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[];
3333
+ /**
3334
+ * Why no resolver may be added to this task's conflict now, or null: a
3335
+ * conflict has one worker (ADR-0015), so a repair or a resolver that has it
3336
+ * keeps it. The words are for whoever asked, since a surface may still offer
3337
+ * the action from a status update it has not yet replaced.
3338
+ */
3339
+ resolverRefusal(taskId: string): string | null;
3340
+ /** What the conflict repair this task is owed waits for; null when no repair will start by itself. */
3341
+ repairWait(taskId: string): RepairWait | null;
3342
+ /** A usage limit keeps this task from starting by itself: the hold on the run, or its own runner's limit with no reset to wait for. */
3343
+ private onUsageLimit;
3344
+ /** Where a task's isolated work stands, and who has its conflict: the record says the first, the scheduler the second. */
3345
+ taskIsolation(taskId: string): TaskIsolation | null;
3346
+ /** Each task's isolation mark and the run's handoff, as {@link taskIsolation} reads them. */
3347
+ isolationView(): IsolationView | null;
3348
+ private withConflictWorker;
3349
+ /**
3350
+ * A repair will start by itself: the task is not started, it has one left,
3351
+ * and something will claim it, the scheduler or {@link owedRepairs}. Nothing
3352
+ * saves this: a reload or a Stop leaves the conflict waiting on the user.
3353
+ */
3354
+ private owesRepair;
2816
3355
  /** The failed tasks a task not started yet waits on, through its dependencies (ADR-0034); empty when it is not blocked. */
2817
3356
  getBlockedBy(taskId: string): string[];
3357
+ /** Throws what {@link loadPlan} would, for a caller with work of its own to do before it loads. */
3358
+ assertLoadable(tasks: readonly Task[], planRunners?: RunnerId[]): void;
2818
3359
  /**
2819
3360
  * `edited`: the plan is the last one edited, not a new one. Only a Stop
2820
3361
  * withdraws a queued start (ADR-0035), so one of a task the edit keeps
@@ -2828,8 +3369,9 @@ declare class TaskOrchestrator {
2828
3369
  * reload would clear the holds and the review approval, so a task the user
2829
3370
  * cancelled would be re-armed by the tick that follows, and a retry after
2830
3371
  * the edit would run the failed task alone, its failure lost. Otherwise it
2831
- * is the plan the next run starts from, with what waits on a usage-limit
2832
- * reset still waiting: an edit between runs is not the user starting over.
3372
+ * is the plan the next run starts from, with a failed task still failed and
3373
+ * what waits on the user still waiting: an edit between runs is not the user
3374
+ * starting over.
2833
3375
  */
2834
3376
  adoptEdit(tasks: readonly Task[], planRunners: RunnerId[]): void;
2835
3377
  /**
@@ -2844,37 +3386,51 @@ declare class TaskOrchestrator {
2844
3386
  * input it waits at — adopting the snapshot's "pending" would offer the
2845
3387
  * scheduler work a runner is already doing.
2846
3388
  *
2847
- * A failed task stays failed, unlike a plan load's fresh chance: an edit is
2848
- * not a retry, and the tasks it blocks wait for that retry (ADR-0034).
3389
+ * A failed task stays failed: an edit is not a retry, and the tasks it blocks
3390
+ * wait for that retry (ADR-0034). One the edit made manual has no retry to wait for.
2849
3391
  */
2850
3392
  reconcilePlan(newTasks: readonly Task[], planRunners?: RunnerId[]): void;
2851
3393
  /**
2852
3394
  * An edit landed on the plan — a planner's task ops through
2853
3395
  * {@link reconcilePlan}, or the user's direct edit through the PlanEditor.
2854
- * One that leaves a loop's check out of rounds ends the loop (ADR-0045 L6):
2855
- * what of its body has not run will not run, and, while the run is under
2856
- * way, the check settles once more so the supervisor hears the end. Nothing
2857
- * else notices such an edit: the check settled when it failed a round, and
2858
- * no round will run it again.
3396
+ * A task it made manual is the manual task it now is (ADR-0053), a runner
3397
+ * still at a question apart. One that leaves a loop's check out of rounds
3398
+ * ends the loop (ADR-0045 L6): what of its body has not run will not run,
3399
+ * and, while the run is under way, the check settles once more so the
3400
+ * supervisor hears the end. Nothing else notices such an edit: the check
3401
+ * settled when it failed a round, and no round will run it again.
2859
3402
  */
2860
3403
  planEdited(before: readonly Task[]): void;
2861
3404
  /**
2862
- * Begin the full-plan run afresh, as Execute does with nothing at work.
2863
- * Whatever an earlier run left goes: its state, even held or armed with
2864
- * nothing live, which {@link start} would take as a run already under way;
2865
- * its scheduled resumes and execution log; and, as the plan is loaded over
3405
+ * Begin the full-plan run afresh, as Execute does with no open run to join.
3406
+ * Whatever an earlier run left goes: its state, which {@link start} would
3407
+ * take as a run already under way; its scheduled resumes and execution log;
3408
+ * and, as the plan is loaded over
2866
3409
  * itself, its retry counts, holds, loop rounds and review approval. Every
2867
3410
  * task not done is ready to run again, and the scheduler starts it; a plan
2868
- * with nothing left to run ends at once, and the scheduler says so.
3411
+ * with nothing left to run ends at once, and the scheduler says so, but for
3412
+ * `keepFailed`: failures nobody asked this run to try again (ADR-0030).
2869
3413
  */
2870
- beginRun(origin: RunOrigin): Promise<void>;
3414
+ beginRun(origin: RunOrigin, keepFailed?: ReadonlySet<string>): Promise<void>;
2871
3415
  /**
2872
3416
  * Take the plan into the run under way (#95), whose tasks were approved
2873
3417
  * first: a scheduled run starts them as their dependencies finish, and one
2874
3418
  * the scheduler was not driving — tasks started on their own — becomes the
2875
- * plan's run, as Execute would make it.
3419
+ * plan's run, as Execute would make it. A hold starts nothing either way:
3420
+ * the plan's run is what its reset gives back (ADR-0018 U1). A start a dirty
3421
+ * checkout parked in a scheduled run is replayed, which reads the checkout
3422
+ * again; a new run would take back the run's failures (ADR-0053).
2876
3423
  */
2877
3424
  joinRun(origin: RunOrigin): Promise<void>;
3425
+ private joinHold;
3426
+ /**
3427
+ * The user's Execute takes in work the scheduler was not driving: it becomes
3428
+ * the plan's run, so what failed gets the fresh chance Execute gives
3429
+ * (ADR-0053). A scheduled run keeps its failures, which a retry continues
3430
+ * (ADR-0034), and Execute only approves what waits in it: a hold that gives
3431
+ * one back is that run still.
3432
+ */
3433
+ readyFailed(): void;
2878
3434
  /** Start the full-plan run, or continue one that is held or ended on a failure. */
2879
3435
  start(origin?: RunOrigin): Promise<void>;
2880
3436
  /**
@@ -2884,6 +3440,18 @@ declare class TaskOrchestrator {
2884
3440
  * for meanwhile.
2885
3441
  */
2886
3442
  stop(reason?: 'user' | 'restart'): void;
3443
+ /**
3444
+ * Nothing claims a repair once the run has stopped, so what was owed one is
3445
+ * a conflict waiting on the user, listed with its options, not a task that
3446
+ * reads as never started.
3447
+ */
3448
+ private withdrawRepairs;
3449
+ /**
3450
+ * A force start behind a dependency lifted a reverted task's hold for that
3451
+ * start alone, in the queue or left to the scheduler. With the start
3452
+ * withdrawn, the task stays what the user took back (ADR-0054).
3453
+ */
3454
+ private withdrawQueuedStarts;
2887
3455
  private chosenHoldsNow;
2888
3456
  private restoreHolds;
2889
3457
  private releaseUnchosenHolds;
@@ -2941,17 +3509,34 @@ declare class TaskOrchestrator {
2941
3509
  * came from, which a repair follows on from.
2942
3510
  */
2943
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;
2944
3514
  /**
2945
3515
  * Work that did not land waits on the user — it never fails the task or
2946
3516
  * blocks its dependents — unless its conflict is owed a repair, which takes
2947
3517
  * the slot the ending attempt freed and never one more than the run allows.
2948
- * A planner turn's start hold holds it as it holds any new spawn (ADR-0029).
2949
- * The repair follows on from the start whose work it repairs: a Stop since
2950
- * that was admitted withdraws it.
3518
+ * A planner turn's start hold holds it as it holds any new spawn (ADR-0029),
3519
+ * and a usage limit does too: `reopen` spends a repair, so one the runner
3520
+ * would refuse is not tried. The repair follows on from the start whose work
3521
+ * it repairs: a Stop since that was admitted withdraws it.
2951
3522
  */
2952
3523
  private settleUnlanded;
2953
3524
  /** Start the repairs {@link settleUnlanded} left owed, as slots and any start hold allow. */
2954
3525
  private startOwedRepairs;
3526
+ /**
3527
+ * A conflict ended some other way than through its resolver: the task
3528
+ * landed, was retried or skipped, or left the plan. The merge the resolver
3529
+ * was added for is no longer needed, so it goes with its link: skipped,
3530
+ * with why, and never started (ADR-0015).
3531
+ */
3532
+ private retireResolvers;
3533
+ private retireResolver;
3534
+ /** A planner's remove reaches no {@link releaseTask}: what it took out of the plan since `before` ends its conflicts here. */
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;
3538
+ /** A control that would end a conflict waits for the resolver at work on it: a conflict has one worker (ADR-0015). */
3539
+ private refuseBesideResolver;
2955
3540
  private landResolved;
2956
3541
  /**
2957
3542
  * Hold a task for as long as `landing` is merging its worktree. Its end lets
@@ -2962,6 +3547,10 @@ declare class TaskOrchestrator {
2962
3547
  getReadyTasks(): Task[];
2963
3548
  private readiness;
2964
3549
  private mergesAutomatically;
3550
+ /** A Merge all is under way. */
3551
+ get isMerging(): boolean;
3552
+ /** A gate reached now is merged with nobody asked: the setting is on, and no merge has stopped it. */
3553
+ get mergesAtGates(): boolean;
2965
3554
  /**
2966
3555
  * The user changed the run settings under an open run (ADR-0030). The
2967
3556
  * scheduler reads them at each decision it makes from them, so a merge gate
@@ -2996,6 +3585,7 @@ declare class TaskOrchestrator {
2996
3585
  * Cancel a running (or scheduled) task: kill its session and return it to
2997
3586
  * 'pending' — "not executed". The task is put on hold so the scheduler
2998
3587
  * doesn't immediately restart it; Retry / Force Start release the hold.
3588
+ * A repair, running or owed a start, leaves its conflict waiting instead.
2999
3589
  *
3000
3590
  * The attempt's worktree is kept, as a stopped or failed one is: a runner is
3001
3591
  * often cancelled because it looked stuck after doing the work, and Mark
@@ -3031,6 +3621,33 @@ declare class TaskOrchestrator {
3031
3621
  * to waiting on their own, because the scheduler gates on `isCompleted`.
3032
3622
  */
3033
3623
  markTaskIncomplete(taskId: string): Promise<void>;
3624
+ /**
3625
+ * What reverting a task would take, and what stands in its way, as far as
3626
+ * the plan can say (ADR-0054); {@link revertPreview} and the revert itself
3627
+ * ask git the rest.
3628
+ */
3629
+ revertScope(taskId: string): RevertScope;
3630
+ /** What a revert of the task would do now: the tasks, newest landing first, or why it would not go ahead. */
3631
+ revertPreview(taskId: string): Promise<TaskRevertPreview>;
3632
+ /**
3633
+ * Revert the task's landed work, with its landed dependents, on the
3634
+ * integration branches (ADR-0054). No new start while it waits for its
3635
+ * turn and works: one that took its claim now would be cut from a tip about
3636
+ * to lose what it depends on, so the claim of anything depending on the work
3637
+ * is refused too, a start admitted before the revert with it. What it finds
3638
+ * in the way there is judged again: a Mark complete landing a dependent is
3639
+ * live like any other. Tasks not depending on it keep running.
3640
+ */
3641
+ revertTask(taskId: string): Promise<IsolationRevertResult>;
3642
+ private revertRefusalNow;
3643
+ /**
3644
+ * Each reverted task goes back to not started, held like a stopped one so
3645
+ * the scheduler does not run it again, and starts over when retried: from
3646
+ * the current tip, which no longer holds its work. Its completion goes as
3647
+ * Mark not done takes one back; the logs of its attempts stay. A conflict it
3648
+ * held is over, so a resolver out to it goes too (ADR-0015).
3649
+ */
3650
+ private takeBack;
3034
3651
  /**
3035
3652
  * Skip: end a task as `skipped`, with no verdict and nothing of its work
3036
3653
  * landed. Its worktree is kept, as a failed attempt's is, so the user can
@@ -3047,8 +3664,10 @@ declare class TaskOrchestrator {
3047
3664
  * the task ended in rather than a fresh one given its prompt again. The
3048
3665
  * worktree keeps the same path, where the runner keeps its sessions, and
3049
3666
  * restores the previous work so that session's memory matches its files.
3667
+ * `revives` is {@link claimRefusal}'s, read as the start is asked for: a dirty
3668
+ * tree's replay carries the first answer, not one read after a skip.
3050
3669
  */
3051
- continueTask(taskId: string, message: string, origin?: RunOrigin): Promise<void>;
3670
+ continueTask(taskId: string, message: string, origin?: RunOrigin, revives?: boolean): Promise<void>;
3052
3671
  /**
3053
3672
  * Keep a task paused on its runner's usage limit from resuming on its own:
3054
3673
  * it waits for a manual retry, as one whose reset time could not be read
@@ -3111,9 +3730,9 @@ declare class TaskOrchestrator {
3111
3730
  * Reuses the scheduler's own claim so a force-started task gets the same
3112
3731
  * augmented prompt, session tracking, and exit handling — callers must not
3113
3732
  * re-spawn the runner themselves. No-op if the task is unknown, a user
3114
- * task, or already running.
3733
+ * task, or already running. `revives` is as on {@link continueTask}.
3115
3734
  */
3116
- forceStartTask(taskId: string, origin?: RunOrigin): Promise<string | undefined>;
3735
+ forceStartTask(taskId: string, origin?: RunOrigin, revives?: boolean): Promise<string | undefined>;
3117
3736
  /**
3118
3737
  * Start an approved task once its merge gate opens, never past it: a script
3119
3738
  * task `run_script` added behind work not merged into the user's branch yet
@@ -3122,6 +3741,17 @@ declare class TaskOrchestrator {
3122
3741
  * so it starts once Merge all has merged that work, and a Stop withdraws it.
3123
3742
  */
3124
3743
  startAtGate(taskId: string): void;
3744
+ /** A start of this task waits in the queue, and starts with nobody asked once what holds it clears. */
3745
+ startQueuedFor(taskId: string): boolean;
3746
+ /**
3747
+ * Start a task the user's own act asked for, under the one start rule and
3748
+ * past none of it (ADR-0015). A scheduler driving the run claims it as it
3749
+ * would any task; otherwise it waits with the queued starts for a slot, a
3750
+ * start hold or a usage limit, says what holds it, and a Stop withdraws it.
3751
+ * A conflict repair is started the same way, and this is what a resolver
3752
+ * added outside a run has in place of one.
3753
+ */
3754
+ startWhenAllowed(taskId: string): void;
3125
3755
  /**
3126
3756
  * A forced start, from its run opening to its claim. It is judged again once
3127
3757
  * the run has opened, against the task as it stands then, which is what
@@ -3160,8 +3790,53 @@ declare class TaskOrchestrator {
3160
3790
  * still under way read as `dependencies` rather than `queued`.
3161
3791
  */
3162
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
+ */
3163
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;
3164
3836
  private sayQueuedAtGate;
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. */
3838
+ private sayQueuedHeld;
3839
+ private sayOnce;
3165
3840
  private refuseOpsDuringMerge;
3166
3841
  /**
3167
3842
  * A force start passes a merge gate (ADR-0020), and the surface confirmed it
@@ -3175,6 +3850,29 @@ declare class TaskOrchestrator {
3175
3850
  isAnyFailed(): boolean;
3176
3851
  approveReview(origin?: RunOrigin): Promise<void>;
3177
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;
3874
+ /** One of a tick's starters: takes the next of `waiting` as each start it made has attached. */
3875
+ private startReady;
3178
3876
  /**
3179
3877
  * The one step that decides, with nothing at work and nothing the scheduler
3180
3878
  * can start, whether the run goes on, is held, waits on the user or is over
@@ -3182,9 +3880,18 @@ declare class TaskOrchestrator {
3182
3880
  * only an end emits it. A run the scheduler is not driving — tasks started
3183
3881
  * on their own — ends on a verdict; a cancel, Mark complete or failed spawn
3184
3882
  * just closes it, so the next run decides its own mode rather than inherit
3185
- * this one's.
3883
+ * this one's. A loop carried on outside a scheduled run holds that run open
3884
+ * as a scheduled one is held, on whatever the start rule waits for.
3186
3885
  */
3187
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;
3894
+ private waitOnUser;
3188
3895
  /**
3189
3896
  * A loop's check failed (ADR-0045). With `failed` evidence and rounds left,
3190
3897
  * a round starts: its body runs again as new attempts, each from the
@@ -3192,7 +3899,9 @@ declare class TaskOrchestrator {
3192
3899
  * the failure is the round's, not the task's. Approval is not spent by a
3193
3900
  * run, so the plan's approval of the body covers every round. Out of
3194
3901
  * rounds, the body still to run will not run. A body task the user skipped
3195
- * has stopped the loop (L7): no round starts, and the skip stands.
3902
+ * has stopped the loop (L7): no round starts, and the skip stands. One they
3903
+ * reverted keeps its hold, which a round takes as any start does (L5, ADR-0054):
3904
+ * the rest of the body runs, and the loop waits for them to say.
3196
3905
  */
3197
3906
  private startRound;
3198
3907
  /**
@@ -3208,6 +3917,53 @@ declare class TaskOrchestrator {
3208
3917
  * complete, a reload in between — resumes the loop alike.
3209
3918
  */
3210
3919
  private repeatChecks;
3920
+ private repeatCheck;
3921
+ /**
3922
+ * Something will run this check between rounds without being asked: a
3923
+ * scheduler, the one a reset gives back, or its loop carried on (ADR-0045
3924
+ * L5). What the seam reads before it says so to anyone.
3925
+ */
3926
+ loopRunsOn(checkId: string): boolean;
3927
+ /** A start of this task would be its loop's next round: between rounds, its body landed, and nothing else will run it. */
3928
+ private ownsNextRound;
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). */
3930
+ loopStranded(taskId: string): boolean;
3931
+ /** A tick starts something: the scheduler the plan, or what is carried on, those tasks alone. */
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;
3935
+ /**
3936
+ * The carried loops with work owed now: a check between rounds whose loop
3937
+ * goes on, or a round waiting to start. None under a scheduler, which owes
3938
+ * them itself, or while a usage limit holds the run, which starts nothing.
3939
+ * One a body task stopped is not here until that task passes (L7).
3940
+ */
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;
3952
+ /**
3953
+ * The run that carried them is over, by a Stop or by what gives the run up
3954
+ * or clears it up: a retry of a body task that passes runs that task alone.
3955
+ */
3956
+ private endCarriedLoops;
3957
+ /** What a tick may start with no scheduler: what is carried on, and nothing that depends on it. */
3958
+ carriedTasks(): Set<string>;
3959
+ /**
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.
3965
+ */
3966
+ private sayUnapprovedInLoop;
3211
3967
  /** Skipped work is not completed work, so a run with any says so rather than that everything completed. */
3212
3968
  private sayFinished;
3213
3969
  /** How a run that ended on failures ends: what it finished, what failed, and what that blocks. */
@@ -3222,11 +3978,23 @@ declare class TaskOrchestrator {
3222
3978
  /**
3223
3979
  * Why a start may not take its claim now, or null when it may: a Stop came
3224
3980
  * since it was admitted (ADR-0029), the task left the plan or is being
3225
- * marked complete, it is outside the user's approval record (ADR-0035), or
3226
- * every slot is taken. Read in the same synchronous step as the claim it
3227
- * guards, so nothing can change in between.
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
3982
+ * second worker on it (ADR-0015), a revert is taking back work it depends on
3983
+ * or skipped it since (ADR-0054), it
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.
3228
3988
  */
3229
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;
3230
3998
  /** An explicit start's refusal, before it changes anything: its caller's answer, as a {@link TaskControlError}. */
3231
3999
  private refuseClaim;
3232
4000
  /** An explicit start's claim, once {@link refuseClaim} let it through: its spawn, or the refusal as a rejection. */
@@ -3234,8 +4002,10 @@ declare class TaskOrchestrator {
3234
4002
  /**
3235
4003
  * The one claim every attempt start passes — the scheduler's, a run or force
3236
4004
  * start, a retry's, a continue, a resume, a repair. Checked and taken in one
3237
- * synchronous step ({@link claimRefusal}, then {@link Attempts.start}), so
3238
- * 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).
3239
4009
  * The task is read here, as it stands: a start that awaited first — its run
3240
4010
  * opening, the user's answer — runs what the plan holds now, not what it
3241
4011
  * held when it was asked for. A task already claimed is not claimed again:
@@ -3247,9 +4017,10 @@ declare class TaskOrchestrator {
3247
4017
  /**
3248
4018
  * Replay the start a dirty tree turned away. `stash` puts the user's tracked
3249
4019
  * changes on the git stash first, so the run isolates; `shared` runs this one
3250
- * run in the workspace root, knowingly.
4020
+ * run in the workspace root, knowingly; `reread` reads the tree again, which
4021
+ * may turn the start away again.
3251
4022
  */
3252
- continueBlockedRun(how: 'stash' | 'shared'): Promise<void>;
4023
+ continueBlockedRun(how: 'stash' | 'shared' | 'reread'): Promise<void>;
3253
4024
  }
3254
4025
 
3255
4026
  /** One call at the command seam, as every route words it (ADR-0029). Task references are resolved here. */
@@ -3293,6 +4064,7 @@ type ExecutionCommand = {
3293
4064
  /** Merge all: the run's landed work into the user's branch (ADR-0013). */
3294
4065
  | {
3295
4066
  command: 'merge_all';
4067
+ pastChecks?: true;
3296
4068
  }
3297
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. */
3298
4070
  | {
@@ -3325,6 +4097,18 @@ type ExecutionCommand = {
3325
4097
  confirmed?: boolean;
3326
4098
  seen?: TaskStatus;
3327
4099
  }
4100
+ /**
4101
+ * The user takes a task's landed work off the integration branch, before
4102
+ * Merge all, with its landed dependents (ADR-0054). Needs `confirmed`, their
4103
+ * yes to what the preview listed; `seen` is the task ids it listed, and a
4104
+ * set that changed since is refused.
4105
+ */
4106
+ | {
4107
+ command: 'revert_task';
4108
+ task: string;
4109
+ confirmed?: boolean;
4110
+ seen?: string[];
4111
+ }
3328
4112
  /** Stop a structured task's running turn, keeping its session: it then waits for the user's message. */
3329
4113
  | {
3330
4114
  command: 'interrupt_task';
@@ -3357,6 +4141,7 @@ type ExecutionCommand = {
3357
4141
  task: string;
3358
4142
  answer: 'approve' | 'reject';
3359
4143
  reason?: string;
4144
+ raiseTokens?: number;
3360
4145
  }
3361
4146
  /** The user signs off the plan's review: every task in it is approved, as executing it approves them, and it runs. */
3362
4147
  | {
@@ -3398,6 +4183,16 @@ type CommandPayload =
3398
4183
  | {
3399
4184
  kind: 'merge';
3400
4185
  result: IsolationMergeResult;
4186
+ }
4187
+ /** The user's `revert_task`, reverted or stopped: which tasks, or the task and files, or the commands that do it by hand. */
4188
+ | {
4189
+ kind: 'revert';
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[];
3401
4196
  };
3402
4197
  /**
3403
4198
  * What a call came to: `message` is written for the caller, a model included,
@@ -3429,19 +4224,6 @@ type Consent = {
3429
4224
  * body is never covered.
3430
4225
  */
3431
4226
  type EffectAllowlist = (body: ScriptBody) => string | null;
3432
- /**
3433
- * What a supervisor at `level` may do. The mandate (#23) replaces this check;
3434
- * nothing above it moves.
3435
- */
3436
- type SupervisorPolicy = (level: SupervisorLevel, command: ExecutionCommandName) => Consent;
3437
- /**
3438
- * The supervisor levels (ADR-0029). Off: it does not act. Watch: it reads and
3439
- * suggests, and every execution command it calls is refused. Act: every
3440
- * command runs without asking — choosing Act is the consent — inside the loop
3441
- * guards; what a command may never do, like allowing an approval, is still
3442
- * refused by the command itself.
3443
- */
3444
- declare const SUPERVISOR_LEVELS: SupervisorPolicy;
3445
4227
  /**
3446
4228
  * A check that stops an actor spinning (ADR-0029's loop guards): the trip —
3447
4229
  * the note naming the guard and what would be accepted, and what the user is
@@ -3470,6 +4252,8 @@ interface ExecutionHost {
3470
4252
  waitsForApproval(taskId: string): boolean;
3471
4253
  /** A start the user consented to approves these tasks, before it acts. */
3472
4254
  approve(taskIds: readonly string[]): void;
4255
+ /** A start under Full with autorun makes the plan approved, as autorun's own start does, so nothing later in the session is refused for it (ADR-0030). */
4256
+ approvePlan(): void;
3473
4257
  /** Full autonomy: starts follow plan approval without a question. */
3474
4258
  autonomous(): boolean;
3475
4259
  /** The user's autorun setting, as it is now (ADR-0030). */
@@ -3480,17 +4264,35 @@ interface ExecutionHost {
3480
4264
  startsHeld(): boolean;
3481
4265
  /** Work is running right now. */
3482
4266
  hasLiveWork(): boolean;
4267
+ /** A usage limit holds the run: what it paused resumes at the reset. */
4268
+ runHeld(): boolean;
4269
+ /** A scheduled run is open, driving the plan or held by a usage limit that gives it back, whether or not anything is live. */
4270
+ scheduledRun(): boolean;
3483
4271
  /** Something will start without being asked again: a start admitted and not yet claimed, or a scheduled resume. */
3484
4272
  startsPending(): boolean;
4273
+ /** A plan the turn in flight submitted, whose start autorun makes as the turn ends (ADR-0030): a start on its way that no run holds yet. */
4274
+ submittedStartWaits(): boolean;
4275
+ /** A loop is carried on, one a body task stopped included; see {@link TaskOrchestrator.loopCarried}. */
4276
+ loopCarried(): boolean;
4277
+ /** A run with nothing starting that something still resumes: a usage limit's reset, or a retry of a failure; see {@link TaskOrchestrator.awaitsResume}. */
4278
+ awaitsResume(): boolean;
4279
+ /** Something will run this loop's check between rounds without being asked; see {@link TaskOrchestrator.loopRunsOn}. */
4280
+ loopRunsOn(taskId: string): boolean;
3485
4281
  /** Something could move a task on without being asked: a start, an attempt in any phase, the scheduler or a usage-limit hold. */
3486
4282
  advancesOnItsOwn(): boolean;
3487
4283
  /** Admit a start that waits before it claims; see {@link StartAdmission}. */
3488
4284
  admitStart(): StartAdmission;
3489
4285
  /** The task's attempt is live: starting, running or landing. */
3490
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;
4289
+ /** A conflict repair of the task will start by itself: there is a start to withdraw (ADR-0015). */
4290
+ repairOwed(taskId: string): boolean;
3491
4291
  maxParallel(): number;
3492
4292
  /** The run waits on the user to stash or skip isolation before anything starts. */
3493
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;
3494
4296
  /**
3495
4297
  * Every reason the task cannot start now, from the scheduler's one start
3496
4298
  * rule (ADR-0034). Takes the task, not its id: the one the seam located.
@@ -3507,15 +4309,29 @@ interface ExecutionHost {
3507
4309
  * `left` still wait. Answers the line that says so.
3508
4310
  */
3509
4311
  joinRun(approved: number, left: number): Promise<string>;
4312
+ /** The fresh chance the user's Execute gives what failed, for work it takes in (ADR-0053); none while the scheduler drives the run. */
4313
+ readyFailed(): void;
3510
4314
  startTask(taskId: string, actor: ExecutionActor): Promise<void>;
3511
4315
  /** Force start the task; answers the line saying it waits for its dependencies, when it was queued behind them rather than started. */
3512
4316
  forceStart(taskId: string): Promise<string | undefined>;
3513
4317
  markComplete(taskId: string): Promise<void>;
3514
4318
  markNotDone(taskId: string): Promise<void>;
4319
+ /** What reverting the task would take, as far as the plan can say; see {@link TaskOrchestrator.revertScope}. */
4320
+ revertScope(taskId: string): RevertScope;
4321
+ /** Revert the task's landed work, through the queue it shares with landings. */
4322
+ revertTask(taskId: string): Promise<IsolationRevertResult>;
3515
4323
  interrupt(taskId: string): Promise<void>;
3516
- stopTask(taskId: string): Promise<void>;
3517
- stopRun(): void;
4324
+ /** `actor` is who stopped it: the user's stop holds the supervisor back from it (ADR-0029). */
4325
+ stopTask(taskId: string, actor: ExecutionActor): Promise<void>;
4326
+ /** `actor` is who stopped it: the user's Stop stands the supervisor down (ADR-0029). */
4327
+ stopRun(actor: ExecutionActor): void;
4328
+ /** The user's Stop found nothing to stop: what was admitted before it is withdrawn all the same. */
4329
+ stopAsked(): void;
4330
+ /** A start cleared its last check and is about to act; the user's ends their Stop (ADR-0029). */
4331
+ startAsked(actor: ExecutionActor): void;
3518
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;
3519
4335
  /** End the task as skipped, landing nothing of it; see {@link TaskOrchestrator.skipTask}. */
3520
4336
  skipTask(taskId: string, reason: string | undefined, actor: ExecutionActor): Promise<void>;
3521
4337
  continueTask(taskId: string, message: string, actor: ExecutionActor): Promise<void>;
@@ -3532,7 +4348,11 @@ interface ExecutionHost {
3532
4348
  cancelResume(taskId: string): Promise<boolean>;
3533
4349
  /** The task waits at a checkpoint, and a runner is left to hear the answer. */
3534
4350
  awaitsCheckpoint(taskId: string): boolean;
3535
- 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>;
3536
4356
  /** Sign off the review: the plan is the user's, and runs. */
3537
4357
  approveReview(): Promise<void>;
3538
4358
  /** Answer a runner's request, as its card does; false once it was answered or withdrawn. */
@@ -3568,6 +4388,12 @@ interface ExecutionHost {
3568
4388
  awaitEnd(taskId: string, ms: number, signal?: AbortSignal): Promise<void>;
3569
4389
  /** The task's result is owed to the planner, in a turn acting for whoever started it (ADR-0037, R2, R3). */
3570
4390
  owe(taskId: string, to: TaskReportTo): void;
4391
+ /** What waits on a person now, derived afresh (ADR-0053). */
4392
+ escalations(): readonly Escalation[];
4393
+ /** Add the task that merges a conflicted task's branch by hand; false when it has no conflict left. */
4394
+ resolveConflict(taskId: string): Promise<boolean>;
4395
+ /** Replay the run a dirty checkout turned away, stashing first or for this run without isolation. */
4396
+ continueRun(how: 'stash' | 'shared'): Promise<void>;
3571
4397
  /** The end of a task's output, as a planner turn is told it; see {@link taskReport}. */
3572
4398
  reportTail(taskId: string): string | undefined;
3573
4399
  }
@@ -3575,7 +4401,8 @@ interface ExecutionCommandsDeps {
3575
4401
  host: ExecutionHost;
3576
4402
  /** The approval card channel a Guarded start asks through. */
3577
4403
  consent: Pick<IApproval, 'request'>;
3578
- supervisor?: SupervisorPolicy;
4404
+ /** What this node may do unasked, read at each check (ADR-0056 M1); a root with nothing past what the host says unless set. */
4405
+ authority?: NodeAuthority;
3579
4406
  guards?: readonly LoopGuard[];
3580
4407
  /** What pre-approves a script's effect (ADR-0035); nothing does unless set. */
3581
4408
  effects?: EffectAllowlist;
@@ -3595,7 +4422,7 @@ interface ExecutionCommandsDeps {
3595
4422
  declare class ExecutionCommands {
3596
4423
  private readonly deps;
3597
4424
  private readonly host;
3598
- private readonly supervisor;
4425
+ private readonly authority;
3599
4426
  private readonly guards;
3600
4427
  private readonly effects;
3601
4428
  private readonly now;
@@ -3619,6 +4446,15 @@ declare class ExecutionCommands {
3619
4446
  * committed, its own edits in the turn included (ADR-0022 P2).
3620
4447
  */
3621
4448
  run(asked: ExecutionCommand, actor: ExecutionActor, signal?: AbortSignal): Promise<CommandOutcome>;
4449
+ /**
4450
+ * The user's answer to an escalation (ADR-0053): the list derived afresh,
4451
+ * and the option chosen dispatched to the existing command for that wait, as
4452
+ * the user, so every check it has still applies and this adds no authority.
4453
+ * It takes no actor because it is the user's alone: no planner tool reaches
4454
+ * it, and the supervisor keeps its own tools. `confirmed` is the user's yes to
4455
+ * an option's `confirm`, which a choice cannot imply.
4456
+ */
4457
+ answerEscalation(id: string, option: string, note?: string, confirmed?: boolean, raiseTokens?: number): Promise<CommandOutcome>;
3622
4458
  /** A valid, consented call, from the user's answer where one is needed to the act and its record. */
3623
4459
  private carryOut;
3624
4460
  /**
@@ -3641,28 +4477,32 @@ declare class ExecutionCommands {
3641
4477
  private startScript;
3642
4478
  /**
3643
4479
  * Why a script task cannot start as `run_script` would start it, but for its
3644
- * merge gate, at which it waits: a dependency that failed or has not
3645
- * 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.
3646
4483
  */
3647
4484
  private scriptUnready;
3648
4485
  /**
3649
- * Who may run a script without the mandate (#23), as ADR-0035's table says.
3650
- * An effect the user's allowlist covers runs in every row but one, and is
3651
- * recorded by its rule. In the user's turn: otherwise autorun in Full runs
3652
- * it (ADR-0030), and failing that they are asked on the card. The
3653
- * supervisor's level decides for it first: at Watch it is refused, to
3654
- * suggest it instead, covered or not; at Act it runs, the task it adds
3655
- * being approved (ADR-0029).
4486
+ * Who may run a script, as ADR-0035's table says. An effect the user's
4487
+ * allowlist covers runs in every row but one, and is recorded by its rule.
4488
+ * In the user's turn: otherwise autorun in Full runs it, on a first plan as
4489
+ * on an approved one (ADR-0030), and failing that they are asked on the
4490
+ * card. The supervisor's level decides for it first: at Watch it is refused,
4491
+ * to suggest it instead, covered or not; at Act it runs, the task it adds
4492
+ * being approved (ADR-0029). A subplanner's is its grants alone (ADR-0056 M6).
3656
4493
  */
3657
4494
  private scriptConsent;
3658
4495
  /**
3659
- * Of the tasks a planner edit in the user's turn wrote, the ones autorun
3660
- * approves as the edit commits: all of them, but for a script task, which
3661
- * runs unasked only where `run_script` would run it unasked (ADR-0035), so
3662
- * under Guarded it waits for the user unless their effect allowlist covers
3663
- * it. Its row shows the body verbatim, and starting it is their consent.
4496
+ * Of the tasks a planner edit wrote, the ones autorun approves as the edit
4497
+ * commits. Under Full every one of them, a rewritten script's new body
4498
+ * included, for the user's turn and a supervisor at Act alike. Otherwise only
4499
+ * in the user's turn, and all but a script task, which runs unasked only
4500
+ * where `run_script` would run it unasked (ADR-0035), so under Guarded it
4501
+ * waits for the user unless their effect allowlist covers it. Its row shows
4502
+ * the body verbatim, and starting it is their consent. Below the root this
4503
+ * is the `approve` grant (ADR-0056 M6).
3664
4504
  */
3665
- autorunApproves(written: readonly Readonly<Task>[]): string[];
4505
+ autorunApproves(actor: ExecutionActor, written: readonly Readonly<Task>[]): string[];
3666
4506
  /**
3667
4507
  * A task `run_script` answered for before it ended has ended: its result
3668
4508
  * joins the timeline beside its start, with who asked and how it was
@@ -3674,16 +4514,22 @@ declare class ExecutionCommands {
3674
4514
  * The tasks a start approves, read from the plan as it stands at the
3675
4515
  * command's last check. Autorun starts a plan as the user's standing
3676
4516
  * instruction, so its start approves what it runs as the user's does; so
3677
- * does a start under its exception for added tasks. A yes on the card
3678
- * approves the tasks the card named, and no others.
4517
+ * does a start under Full with autorun. Under Guarded it leaves a script task
4518
+ * the commit left unapproved: that waits for the user (ADR-0035). A yes on
4519
+ * the card approves the tasks the card named, and no others.
3679
4520
  */
3680
4521
  private consented;
3681
4522
  /**
3682
- * With autorun on and Full, a planner turn may start a task added after the
3683
- * user approved the plan (ADR-0030). Read at each start, so switching either
3684
- * off stops it at once; never on a plan the user has not approved.
4523
+ * With autorun on and Full, nobody is asked: a planner turn, or a supervisor
4524
+ * its level lets act, may start anything, on a first plan as on an approved
4525
+ * one (ADR-0030). Read at each start, so switching either off stops it at
4526
+ * once. Below Act the level has refused the call before this is read. A
4527
+ * subplanner has nobody to ask, so `approve` alone says it: its `autonomy`
4528
+ * says what its runners may do, not whether a person is asked (ADR-0056 M7).
3685
4529
  */
3686
- private addedMayStart;
4530
+ private startsUnattended;
4531
+ /** The mandate that answers for `actor` now: read at each check, so a change reaches the next one (ADR-0056 M1). */
4532
+ private mandateOf;
3687
4533
  /**
3688
4534
  * Guarded means "ask the user first": a planner start in the user's own
3689
4535
  * turn asks, so tasks they have not approved go into the question rather
@@ -3692,12 +4538,23 @@ declare class ExecutionCommands {
3692
4538
  */
3693
4539
  private asksToApprove;
3694
4540
  /**
3695
- * Who may do what without the mandate (#23): the user's own call needs no
3696
- * consent, and their own controls are theirs alone; a planner turn may
3697
- * always end or steer work, and starts it only as the autonomy level says;
3698
- * the supervisor's level decides for it.
4541
+ * Who may do what, in the one place it is decided (ADR-0056 M6): the user's
4542
+ * own call needs no consent, and their own controls are theirs alone; a turn
4543
+ * the user opened keeps ADR-0029's table, since a person needs no mandate: it
4544
+ * may always end or steer work, and starts it only as the autonomy level says.
4545
+ * The supervisor's level decides for it through its mandate, and a subplanner
4546
+ * has its grants alone, allow or refuse: nobody is there to ask.
4547
+ */
4548
+ consent(actor: ExecutionActor, command: ExecutionCommandName): Consent;
4549
+ private supervisorConsent;
4550
+ private grantConsent;
4551
+ /**
4552
+ * Autorun is the user's standing instruction, so at the root it is not held
4553
+ * to a level: Watch has no `control`, and autorun still starts its plan. Below
4554
+ * the root there is no user behind it, and `approve` and `control` narrow it
4555
+ * (ADR-0056 M6).
3699
4556
  */
3700
- private consent;
4557
+ private autorunConsent;
3701
4558
  /**
3702
4559
  * A checkpoint asks a person for judgement on the work, so the user answers
3703
4560
  * it from the task's card; the supervisor may only at Act, in a wake-up turn
@@ -3712,8 +4569,16 @@ declare class ExecutionCommands {
3712
4569
  */
3713
4570
  private prepare;
3714
4571
  private prepareRunPlan;
4572
+ /**
4573
+ * What a planner is told of its `run_plan`: it leaves a failure as it is
4574
+ * (ADR-0053), so the line says so, and where nothing was left to start the
4575
+ * run has ended again and the line does not say it runs.
4576
+ */
4577
+ private runningLine;
3715
4578
  /** A start of one task: `run_task`, or a fresh `retry_task`, which also takes a task that failed or waits on the user. */
3716
4579
  private prepareStart;
4580
+ /** Why the work this task builds on keeps a start of it, a fresh one or a continue, from running now; null when it does not. */
4581
+ private heldBehind;
3717
4582
  /**
3718
4583
  * The user's force start goes past what the scheduler waits for, so only
3719
4584
  * what no start could get past is refused here; the start rule decides the
@@ -3745,6 +4610,13 @@ declare class ExecutionCommands {
3745
4610
  private overriddenBy;
3746
4611
  private prepareMarkComplete;
3747
4612
  private prepareMarkNotDone;
4613
+ /**
4614
+ * A revert asks nothing of the run, so unlike an override it waits for no
4615
+ * task that does not depend on the work (ADR-0054). What only git can say —
4616
+ * that it is in the user's branch, that it conflicts — it says as it runs,
4617
+ * in its turn on the queue.
4618
+ */
4619
+ private prepareRevert;
3748
4620
  private prepareInterrupt;
3749
4621
  private prepareCancelResume;
3750
4622
  private prepareCheckpoint;
@@ -3756,6 +4628,8 @@ declare class ExecutionCommands {
3756
4628
  */
3757
4629
  private prepareResume;
3758
4630
  private prepareStop;
4631
+ /** What a refused Stop of a conflicted task can do instead: nothing runs it, so there is something else to press. */
4632
+ private conflictNext;
3759
4633
  private prepareStopRun;
3760
4634
  private prepareMessage;
3761
4635
  /** A message queued behind the running turn, sent now or taken back; one delivered meanwhile is past either. */
@@ -3774,9 +4648,11 @@ declare class ExecutionCommands {
3774
4648
  */
3775
4649
  private skippedRefusal;
3776
4650
  /**
3777
- * Why a loop's check between rounds is not started from the seam: its loop
3778
- * runs it again itself, once its body has landed (ADR-0045 L5). Null for
3779
- * any other task.
4651
+ * Why a loop's check between rounds is not started from the seam: something
4652
+ * will run it, once its body has landed, or nothing will and its body has
4653
+ * yet to land (ADR-0045 L5). Each says only what is true of it. Null for any
4654
+ * other task, and for a check whose fix has landed that nothing else will
4655
+ * run, which a start runs as its next round rather than leave it stranded.
3780
4656
  */
3781
4657
  private betweenRounds;
3782
4658
  /**
@@ -3956,12 +4832,26 @@ interface PlanChangeScope {
3956
4832
  catalog(runners: RunnerId[]): AssignmentCatalog & {
3957
4833
  runnerModes?: Record<RunnerId, RunnerModeInfo[]>;
3958
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>;
3959
4847
  /** Where attached skill names are looked up; absent, none are. */
3960
4848
  skills?(): SkillLookup;
3961
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). */
3962
4850
  handles?(): ReadonlyMap<string, string>;
3963
4851
  /** A further refusal of what the change leaves: the supervisor's oscillation guard (ADR-0029). */
3964
4852
  guard?(before: readonly Task[], after: readonly Task[]): string | undefined;
4853
+ /** The node, below the root: a child's change is held to its mandate, where a commit would otherwise approve with no command (ADR-0056 M6). Absent, or undefined, at the root. */
4854
+ child?(): NodeAuthority | undefined;
3965
4855
  }
3966
4856
  /** What a whole plan's JSON is held to as it is submitted. */
3967
4857
  interface PlanIntake {
@@ -3994,10 +4884,41 @@ declare class PlanChangeCheck {
3994
4884
  checkNow<P extends CheckPhase>(change: PlannerChange<P>, phase: P): CheckedChange;
3995
4885
  /** A plan's JSON read into tasks as it is submitted; nothing in it reads the plan. */
3996
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;
3997
4891
  private rules;
4892
+ /** What a child's mandate leaves out of this change: `replan` for any, and `research` for a research task it brings. */
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;
3998
4903
  private refused;
3999
4904
  }
4000
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
+
4001
4922
  /**
4002
4923
  * How much of its model's window an API planner's next call may fill. The
4003
4924
  * check runs before every call, against the model configured at that moment,
@@ -4086,6 +5007,8 @@ interface CliAgentAiServiceDeps extends Partial<AgentProcessDeps> {
4086
5007
  projectServerApprovals?: ProjectServerApprovals;
4087
5008
  /** What the planner is shown of its context and budgets at the start of each turn. */
4088
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;
4089
5012
  }
4090
5013
  /**
4091
5014
  * A coding agent driven as Ordewell's planner (ADR-0009).
@@ -4112,6 +5035,7 @@ declare class CliAgentAiService implements IAiService {
4112
5035
  private readonly mcpServer;
4113
5036
  private readonly turnBudgets;
4114
5037
  private readonly projectServerApprovals;
5038
+ private readonly openLease;
4115
5039
  private activeAbort;
4116
5040
  /** See {@link compactions}. */
4117
5041
  private compacted;
@@ -4132,6 +5056,7 @@ declare class CliAgentAiService implements IAiService {
4132
5056
  nativeSessionId(): string | null;
4133
5057
  /** The compactions the agents reported of their own context, never a subagent's or another thread's: their adapters drop those (ADR-0012 T10). */
4134
5058
  compactions(): number;
5059
+ promptCacheTtlMs(): number | undefined;
4135
5060
  heldSecrets(): readonly string[];
4136
5061
  restart(): void;
4137
5062
  reset(): void;
@@ -4304,7 +5229,17 @@ interface LandedHistory {
4304
5229
  */
4305
5230
  interface PlannerToolsOffer {
4306
5231
  sessionId: string;
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. */
5233
+ withheld?: readonly string[];
4307
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;
4308
5243
  }
4309
5244
  /**
4310
5245
  * Everything the conversation loop needs to start planning (ADR-0002).
@@ -4345,6 +5280,10 @@ interface ConversationRequest {
4345
5280
  */
4346
5281
  initialMessage?: string;
4347
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;
4348
5287
  /**
4349
5288
  * The session's notice channel, for a warning the planner's surface should
4350
5289
  * show once (ADR-0036 N4: the native-shell setting is on but the harness is
@@ -4501,6 +5440,8 @@ interface IAiService {
4501
5440
  * T10). Absent where nothing compacts a context in place.
4502
5441
  */
4503
5442
  compactions?(): number;
5443
+ /** How long this planner's prompt cache lasts, as its connector states it; absent when it states none (ADR-0057 R4). */
5444
+ promptCacheTtlMs?(): number | undefined;
4504
5445
  /**
4505
5446
  * Optional: what automatic compaction does for the planner's model now
4506
5447
  * (ADR-0002) — on, off in the settings file, or off for want of a known
@@ -4684,6 +5625,14 @@ declare class PlannerTurn {
4684
5625
  }
4685
5626
  /** What a planner turn may change: the whole plan, or tasks through edits. */
4686
5627
  type PlanChange = 'plan' | 'edit';
5628
+ /** A wake-up turn's brief, as `PlannerBriefing` sends it: whole to a context that does not hold it, else a line on a change (ADR-0057 R5). */
5629
+ interface WakeBrief {
5630
+ /** Which brief this is: a level's, or a report turn's. */
5631
+ key: string;
5632
+ whole: string;
5633
+ /** All a context holding {@link whole} needs told, and all that may differ between two wake-ups of one brief. */
5634
+ line: string;
5635
+ }
4687
5636
  /**
4688
5637
  * A wake-up turn (ADR-0029): a supervisor's turn on wake-up digests alone, no
4689
5638
  * user message in it. What it may do is its level's, decided by the host.
@@ -4698,7 +5647,7 @@ interface WakeUpTurn {
4698
5647
  */
4699
5648
  refused(change: PlanChange): string | undefined;
4700
5649
  /** Told to the model after the digests and never saved, like the plan block: who it is in this turn, and what it may do. */
4701
- brief?: string;
5650
+ brief?: WakeBrief;
4702
5651
  /**
4703
5652
  * Who the turn acts for at the command seam, read at each call; absent, the
4704
5653
  * supervisor at the run's level. A turn on a report owed to the user's turn
@@ -4747,6 +5696,8 @@ interface PlannerConversationHost {
4747
5696
  goal(): string;
4748
5697
  aiService(): IAiService;
4749
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;
4750
5701
  /** Fresh discovery, allowlist-filtered the way the system prompt shows it. */
4751
5702
  opening(runners: RunnerId[]): Promise<ConversationOpening>;
4752
5703
  /** What the per-turn catalog block and every read draw from, as of now. */
@@ -4789,10 +5740,12 @@ interface PlannerConversationHost {
4789
5740
  capturePrd(text: string): void;
4790
5741
  /** The planner turn was stopped or abandoned: whatever it is waiting on (a parked approval) will never be answered. */
4791
5742
  turnAborted(): void;
4792
- /** 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. */
4793
5744
  turnClaiming?(): void;
4794
5745
  /** A planner turn let the conversation go, so the next one may take it. */
4795
5746
  turnReleased?(): void;
5747
+ /** A turn was let go without ending: abandoned with its plan, or hung. */
5748
+ turnDropped?(): void;
4796
5749
  /**
4797
5750
  * A turn ended on the planner's usage limit, which it reported as `said`:
4798
5751
  * what is sent again, and when. `answered`: a planner tool's change in the
@@ -4920,7 +5873,10 @@ declare class PlannerConversation {
4920
5873
  private readonly briefing;
4921
5874
  private readonly blocks;
4922
5875
  private live;
5876
+ private readonly idling;
4923
5877
  private openTurn;
5878
+ /** When the live context's newest turn opened; null with no live context. */
5879
+ private requestedAt;
4924
5880
  /**
4925
5881
  * What an API planner's chat condenses its context through during a turn
4926
5882
  * (ADR-0002): the open turn, the history that landed before it, and the
@@ -4929,12 +5885,24 @@ declare class PlannerConversation {
4929
5885
  private readonly compaction;
4930
5886
  constructor(host: PlannerConversationHost);
4931
5887
  get transcript(): readonly ConversationMessage[];
5888
+ /**
5889
+ * When the planner's prefix was last sent, as its provider counts a prompt
5890
+ * cache's life: from the start of a request (ADR-0057 R4). The turn's opening
5891
+ * stands for it: a turn's later calls keep the prefix warm for longer, so
5892
+ * this can only say a wake is due earlier than it had to be. Null with no live
5893
+ * context, whose next turn is a replay.
5894
+ */
5895
+ get lastRequestAt(): number | null;
4932
5896
  /** Whether a planner turn holds the conversation. */
4933
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;
4934
5901
  /** Stop the planner turn in flight; it settles as stopped. False when none is. */
4935
5902
  stopTurn(by?: 'force_send'): boolean;
4936
5903
  /**
4937
- * 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:
4938
5906
  * it is stopped, whatever it settles is discarded, and the conversation is
4939
5907
  * free at once rather than when the abandoned turn finishes unwinding.
4940
5908
  */
@@ -5314,7 +6282,12 @@ declare class PlannerConversation {
5314
6282
  * whole block or what changed (ADR-0012 T10).
5315
6283
  */
5316
6284
  private planView;
5317
- /** One line per task, subtasks indented under their parent, with stable references — null until the plan has tasks. */
6285
+ /**
6286
+ * One line per task, subtasks indented under their parent, with stable
6287
+ * references — null until the plan has tasks. Past a size only the tasks a
6288
+ * planner acts on get a line (`planWindow`); what a line waits for costs a
6289
+ * start-rule walk, so the rest are not walked.
6290
+ */
5318
6291
  private planRows;
5319
6292
  }
5320
6293
 
@@ -5377,93 +6350,6 @@ declare function resolveTaskRef<T extends RefTask<T>>(tasks: readonly T[], ref:
5377
6350
  */
5378
6351
  declare function resolveUserTaskRef<T extends RefTask<T>>(tasks: readonly T[], ref: string | number): T | undefined;
5379
6352
 
5380
- interface SessionMeta {
5381
- id: string;
5382
- goal: string;
5383
- runners: RunnerId[];
5384
- taskCount: number;
5385
- status: PlanStatus;
5386
- createdAt: string;
5387
- updatedAt: string;
5388
- }
5389
- interface SessionData {
5390
- meta: SessionMeta;
5391
- /** The full plan state — including conversationHistory, prdMarkdown, and queuedMessages. */
5392
- plan: LegacyPlanState;
5393
- }
5394
-
5395
- /**
5396
- * The bridge between "core needs an answer" and "a human somewhere is looking
5397
- * at a UI". Core cannot prompt: the human may be at a browser, a TUI, a CLI
5398
- * stream, or a VS Code webview, and on the web server they are on the far end
5399
- * of a socket. So the Session parks a promise here, announces the request
5400
- * through the normal broadcast seam, and every surface answers through the
5401
- * same `resolve(id, granted)` call.
5402
- *
5403
- * Timeouts are load-bearing rather than defensive: a planner turn that blocks
5404
- * forever on an unanswered prompt would hang the whole research loop with no
5405
- * visible cause. On expiry the request resolves to denied and the model gets a
5406
- * normal, actionable tool result. A task runner's request is the exception
5407
- * (ADR-0018, A1): the runner is parked on it rather than a research loop, and
5408
- * it waits for a person — or the supervisor — however long that takes.
5409
- */
5410
- interface PendingApproval {
5411
- id: string;
5412
- request: ApprovalRequest;
5413
- createdAt: string;
5414
- }
5415
- interface PendingApprovalsOptions {
5416
- /** Denies and resolves after this long with no answer. Default 5 minutes. */
5417
- timeoutMs?: number;
5418
- /** Announce a new request to the surfaces. */
5419
- onRequest?: (pending: PendingApproval) => void;
5420
- /** Announce that a request is no longer actionable (answered or expired). */
5421
- onSettled?: (id: string, granted: boolean, settled: {
5422
- request: ApprovalRequest;
5423
- decision: ApprovalDecision;
5424
- }) => void;
5425
- }
5426
- interface AskOptions {
5427
- /**
5428
- * An id the caller already announced the request under. One still in use is
5429
- * refused — denied at once — rather than overwriting the request that has it.
5430
- */
5431
- id?: string;
5432
- /** Wait for an answer however long it takes. */
5433
- noTimeout?: boolean;
5434
- /**
5435
- * Given the answer as the request settles, before anything awaiting the
5436
- * promise runs: a caller about to tear down what asked (a runner being
5437
- * stopped) must deliver the answer before it goes.
5438
- */
5439
- onDecision?: (decision: ApprovalDecision) => void;
5440
- }
5441
- declare class PendingApprovals {
5442
- private readonly opts;
5443
- private readonly entries;
5444
- private readonly listeners;
5445
- constructor(opts?: PendingApprovalsOptions);
5446
- /** Hear each new request once it is announced — the supervisor wakes on a runner's (ADR-0029). */
5447
- listen(listener: (pending: PendingApproval) => void): () => void;
5448
- /** Park a request and return the promise the approval policy awaits. */
5449
- ask(request: ApprovalRequest, options?: AskOptions): Promise<boolean>;
5450
- /** Park a request whose answer is more than yes or no. */
5451
- decide(request: ApprovalRequest, options?: AskOptions): Promise<ApprovalDecision>;
5452
- /**
5453
- * Answer one request. Returns false when the id is unknown or already
5454
- * settled. "Allow for this task" on a request that did not offer it is a
5455
- * plain allow: no grant is made that the requester never proposed.
5456
- */
5457
- resolve(id: string, answer: ApprovalAnswer): boolean;
5458
- /** Everything still awaiting an answer — replayed to a surface that connects late. */
5459
- outstanding(): PendingApproval[];
5460
- /**
5461
- * Deny everything in flight, or only the requests `which` picks. Called on
5462
- * abort and on session reset.
5463
- */
5464
- clear(which?: (request: ApprovalRequest) => boolean, note?: string): void;
5465
- }
5466
-
5467
6353
  /** One repo that kept "Merge all" from touching anything, as part of one line. */
5468
6354
  declare function blockNotice({ repo, reason, files, expected, actual, rebasing }: IsolationMergeBlock): string;
5469
6355
  /** Every repo's reason, in the order "Merge all" named them, so a surface can list them or join them. */
@@ -5525,9 +6411,9 @@ interface IsolationPreviewNotice {
5525
6411
  declare function describeIsolationPreview(preview: IsolationPreview): IsolationPreviewNotice;
5526
6412
 
5527
6413
  /**
5528
- * Pure, no-imports helper so a browser surface can format a conflict's files
5529
- * without pulling `isolationRecord` — and its `path` import — into a webview
5530
- * 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.
5531
6417
  */
5532
6418
  declare function capConflictFiles(files: string[], max?: number): string;
5533
6419
 
@@ -5598,6 +6484,12 @@ declare function repoResultLines(handoff: {
5598
6484
  landed: readonly unknown[];
5599
6485
  }[];
5600
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;
5601
6493
 
5602
6494
  /** What the user last picked for one planner backend. */
5603
6495
  interface PlannerModelChoice {
@@ -5722,6 +6614,11 @@ interface GlobalRunSettings {
5722
6614
  supervisorRetryLimit: number;
5723
6615
  /** Reads a wake-up turn may make, in place of a user message's caps. */
5724
6616
  wakeUpReadBudget: number;
6617
+ /**
6618
+ * The token budget of a delegation tree (ADR-0055 D21, ADR-0056 M5); 0 is
6619
+ * delegation off. One value, so delegation is never on with no budget.
6620
+ */
6621
+ delegationTokens: number;
5725
6622
  }
5726
6623
  declare const RUN_SETTINGS_DEFAULTS: Readonly<GlobalRunSettings>;
5727
6624
  declare class RunSettingsError extends Error {
@@ -5747,12 +6644,26 @@ interface UserSettings {
5747
6644
  autorun?: boolean;
5748
6645
  supervisorRetryLimit?: number;
5749
6646
  wakeUpReadBudget?: number;
6647
+ delegationTokens?: number;
5750
6648
  /**
5751
6649
  * Whether an API planner prunes its own context ahead of a call that would
5752
6650
  * pass 90% of its model's window. Absent means on. Set in the file only:
5753
6651
  * no surface offers it.
5754
6652
  */
5755
6653
  plannerAutoCompaction?: boolean;
6654
+ /**
6655
+ * What a task runner loads of the user's own setup: all of it (`user`) or
6656
+ * what a task needs (`minimal`). Absent means `user`. Set in the file only:
6657
+ * no surface offers it.
6658
+ */
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;
5756
6667
  }
5757
6668
  /**
5758
6669
  * The model allowlist, remembered planner models and enabled runners are shared
@@ -5953,7 +6864,9 @@ type DaemonErrorCode = 'session_not_found' | 'task_not_found' | 'no_plan' | 'alr
5953
6864
  /** The selected planner cannot plan yet (no key, no base URL, no model); `plannerNotReady` says which. */
5954
6865
  | 'planner_not_ready'
5955
6866
  /** The request asked for something the plan, task or conversation does not allow. */
5956
- | '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'
5957
6870
  /** Another live Ordewell host runs this session (ADR-0049); `sessionHeld` names it. */
5958
6871
  | 'session_held';
5959
6872
  /** The body of every non-2xx answer a route gives. */
@@ -5997,6 +6910,24 @@ interface ExecuteCommandResponse {
5997
6910
  message: string;
5998
6911
  payload?: CommandPayload;
5999
6912
  }
6913
+ /**
6914
+ * `GET /api/plans/:id/escalations`: what waits on a person now (ADR-0053). A
6915
+ * client holding a socket has the same list on every status update.
6916
+ */
6917
+ interface EscalationsResponse {
6918
+ escalations: Escalation[];
6919
+ }
6920
+ /**
6921
+ * The body of `POST /api/plans/:id/escalations/answer`, run as the user, and
6922
+ * answered as `execute-command` answers. `confirmed` is the user's yes to the
6923
+ * option's `confirm`.
6924
+ */
6925
+ interface AnswerEscalationRequest {
6926
+ id: string;
6927
+ option: string;
6928
+ note?: string;
6929
+ confirmed?: boolean;
6930
+ }
6000
6931
  /**
6001
6932
  * A plan as the planner routes return it: the session's plan state, not the
6002
6933
  * `SerializedPlan` a websocket `plan_generated` carries. The two differ —
@@ -6057,6 +6988,8 @@ interface MergeGateEntry {
6057
6988
  interface MergeGateResponse {
6058
6989
  mergeGate: MergeGateEntry[];
6059
6990
  }
6991
+ /** A revert that would not go ahead is an answer, not a failure, so it comes back 200 with why (ADR-0054). */
6992
+ type TaskRevertPreviewResponse = TaskRevertPreview;
6060
6993
  interface IsolationDiffResponse {
6061
6994
  diff: string;
6062
6995
  }
@@ -6582,6 +7515,8 @@ interface TaskLogView extends BlockList {
6582
7515
  readonly queued: readonly QueuedTaskMessage[];
6583
7516
  /** A turn is running. */
6584
7517
  readonly working: boolean;
7518
+ /** When the open turn began (ISO), for a surface to count from; absent between turns and for a log saved before it was kept (#81). */
7519
+ readonly turnStartedAt?: string;
6585
7520
  /** How the last turn ended; absent before the first one does. */
6586
7521
  readonly lastTurnEnd?: StructuredTurnEnd;
6587
7522
  /** What the runner reported over the attempt, subagents included. */
@@ -6608,6 +7543,70 @@ declare function reduceTaskLog(view: TaskLogView, event: TaskLogEvent): TaskLogV
6608
7543
  /** A whole attempt's events, folded from the start — what a reload shows. */
6609
7544
  declare function replayTaskLog(events: readonly TaskLogEvent[], from?: TaskLogView): TaskLogView;
6610
7545
 
7546
+ /** What the label reads: a `TaskLogView`'s blocks, which is all a webview holds of one. */
7547
+ type LoggedBlocks = {
7548
+ readonly blocks: readonly DisplayBlock[];
7549
+ readonly turnStartedAt?: string;
7550
+ };
7551
+ /** What {@link taskActivity} reads of a log: the view's turn flag, and whether the log is an attempt the runner is no longer in. */
7552
+ type LoggedTurn = LoggedBlocks & {
7553
+ /** A turn is open in the log, as its `turn_start` said; a log that streams without one has open blocks but this unset. Absent where a surface holds no such flag, which reads as open. */
7554
+ readonly working?: boolean;
7555
+ /** How the last turn ended, as the view says; set means the log has seen a turn end, so what it left open is not a turn. */
7556
+ readonly lastTurnEnd?: StructuredTurnEnd;
7557
+ /** The log is an earlier attempt, browsed: whatever it left open ended with it. */
7558
+ readonly archived?: boolean;
7559
+ };
7560
+ /**
7561
+ * What a task's runner is busy with, from its log: the latest block still
7562
+ * open decides, since that is what the runner is doing now. Several running
7563
+ * subagents are counted rather than named, as no one of them is the task's
7564
+ * latest work. The label holds runner output — a surface sanitizes it.
7565
+ */
7566
+ declare function activityLabel(view: LoggedBlocks): string;
7567
+ /** The line above a composer: `busy` animates, and a quiet task's does not. */
7568
+ interface TaskActivity {
7569
+ label: string;
7570
+ busy: boolean;
7571
+ /** When the open turn began (ISO): a surface counts from it on its own ticks (#81). Absent for a log saved before turns were dated. */
7572
+ since?: string;
7573
+ }
7574
+ /**
7575
+ * The activity line of an open task log, or null when it has none to show: a
7576
+ * task not running, and one waiting on a runner request, whose card in the log
7577
+ * already asks the user. A running task whose runner has gone quiet is named
7578
+ * as the plan pane names it and is never `busy`, so a runner that is not
7579
+ * producing anything cannot look as if it were. Nor can a runner between
7580
+ * turns: `busy` needs an open turn, a running subagent that outlived it, or,
7581
+ * in a log that has seen no turn end, a block still open (a stream whose
7582
+ * `turn_start` this view never saw).
7583
+ * A browsed earlier attempt has no line, whatever its log was left open on.
7584
+ * A script task has no exception here, on any surface: it runs no model, but
7585
+ * its log is what it printed, so a step still open is one running.
7586
+ */
7587
+ declare function taskActivity(view: LoggedTurn, task: {
7588
+ status: string;
7589
+ idleSince?: string | null;
7590
+ awaitingApproval?: number;
7591
+ }): TaskActivity | null;
7592
+
7593
+ /**
7594
+ * How long something has run, for an activity line: whole seconds (`42s`),
7595
+ * then minutes and zero-padded seconds (`3m 05s`), then from an hour hours and
7596
+ * zero-padded minutes (`1h 05m`), since seconds mean nothing by then. A
7597
+ * fraction of a unit is dropped, not rounded up, so a time never reads ahead
7598
+ * of the clock. Returns '' for a negative or non-finite input: a start time
7599
+ * ahead of the reader's clock (skew between hosts) is not a duration to show.
7600
+ */
7601
+ declare function formatElapsed(ms: number): string;
7602
+ /**
7603
+ * The time from `startedAt` (ISO) to `now` (epoch ms), formatted. The clock is
7604
+ * the caller's, so a surface counts on a tick it already draws and a test
7605
+ * fixes it. '' when there is nothing to show: no start (a log saved before
7606
+ * turns were dated) or one that cannot be read.
7607
+ */
7608
+ declare function elapsedSince(startedAt: string | undefined, now: number): string;
7609
+
6611
7610
  /**
6612
7611
  * The one-line head of a command row, shared by every surface: the tool's name
6613
7612
  * and the argument that says what the call is about.
@@ -6717,6 +7716,8 @@ declare function aheadOfDraft(text: string, draft: string): string;
6717
7716
  interface TurnGate {
6718
7717
  readonly open: string | null;
6719
7718
  readonly stopped: string | null;
7719
+ /** When the open turn began (ISO), for its elapsed time (#81); absent when none is open or its daemon did not say. */
7720
+ readonly openedAt?: string;
6720
7721
  }
6721
7722
  declare const NO_TURN: TurnGate;
6722
7723
  interface GatedConversation {
@@ -6735,6 +7736,13 @@ declare function followTurn(view: ConversationView, gate: TurnGate, input: Conve
6735
7736
  * turn's late output never lands. Nothing changes when no turn is open.
6736
7737
  */
6737
7738
  declare function stopTurn(view: ConversationView, gate: TurnGate): GatedConversation;
7739
+ /**
7740
+ * The turn is over without its `planner_turn_ended` having been heard: its
7741
+ * call failed or returned the plan. The open turn and its start go, so the
7742
+ * next turn does not count from this one's; a stop still standing stays, as
7743
+ * the stopped turn's stragglers have not stopped.
7744
+ */
7745
+ declare function closeTurn(gate: TurnGate): TurnGate;
6738
7746
 
6739
7747
  /**
6740
7748
  * Whether the conversation holds anything the collapsed view hides — thinking,
@@ -6859,11 +7867,15 @@ declare function taskStatusKind(task: {
6859
7867
  status: string;
6860
7868
  idleSince?: string | null;
6861
7869
  }): TaskStatusKind;
7870
+ /** The word for a running task whose runner has printed nothing for a while (`quiet`). */
7871
+ declare const STALLED_LABEL = "Stalled";
6862
7872
  /** What an awaiting_user task waits on (ADR-0018, W1); null when no reason was saved. */
6863
7873
  declare function awaitingLabel(task: {
6864
7874
  status: string;
6865
7875
  awaitingReason?: AwaitingReason;
6866
7876
  }): string | null;
7877
+ /** A conflict repair that will start by itself (ADR-0015): progress, in words that ask nothing of the user. */
7878
+ declare function repairWaitLabel(wait: RepairWait): string;
6867
7879
  /** "Blocked by #1, #2.1": the failed tasks a blocked task waits on, by reference (ADR-0034). */
6868
7880
  declare function blockedByLabel(refs: readonly string[]): string;
6869
7881
  /** "On hold: could not connect to Ordewell's tools; start it again": why a task is out of the scheduler. */
@@ -6974,6 +7986,8 @@ interface ActionSubject {
6974
7986
  resumeAt?: string | null;
6975
7987
  /** Its last attempt left a runner session to continue (ADR-0018, K1). */
6976
7988
  continuable?: boolean;
7989
+ /** A conflict repair of it will start by itself, which Stop withdraws (ADR-0015). */
7990
+ repairWaits?: unknown;
6977
7991
  }
6978
7992
  /** The actions a task's row offers while a plan runs, in the order they are laid out. */
6979
7993
  declare function taskRowActions(task: ActionSubject, placement?: TaskRowPlacement): TaskRowAction[];
@@ -7014,6 +8028,48 @@ declare function confirmedOverride(command: ExecutionCommand, seen?: TaskStatus)
7014
8028
  /** Change versus ops can still be flipped (ADR-0020): an AI task that has not started. */
7015
8029
  declare function opsEditable(task: ActionSubject): boolean;
7016
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
+
7017
8073
  /**
7018
8074
  * Where a loop's check stands, for the words a surface puts on its row
7019
8075
  * (ADR-0045): derived from the check's status, verdict and round, as a block
@@ -7102,8 +8158,13 @@ interface TaskRowSubject {
7102
8158
  outcome: string;
7103
8159
  reported?: string;
7104
8160
  } | null;
8161
+ /** Between rounds with nothing to run it: only the run knows that, so it is told (ADR-0045 L5). */
8162
+ loopStranded?: boolean;
7105
8163
  /** The latest attempt's handoff as the plan keeps it (ADR-0047); a row shows it without opening the task. */
7106
8164
  handoff?: HandoffDigest;
8165
+ /** A delegated task's goal, and its row's live facts (ADR-0055 D20). */
8166
+ goal?: string;
8167
+ delegation?: DelegatedRow;
7107
8168
  }
7108
8169
  interface TaskRowIsolation {
7109
8170
  state: TaskIsolationState;
@@ -7115,6 +8176,11 @@ interface TaskRowIsolation {
7115
8176
  limit: number;
7116
8177
  };
7117
8178
  repairedFiles?: string[];
8179
+ /** Core's word that its landing can be reverted (ADR-0054); a surface offers the action only then. */
8180
+ revertible?: true;
8181
+ repairWaits?: RepairWait;
8182
+ /** The task added to resolve its conflict, by id. */
8183
+ resolver?: string;
7118
8184
  }
7119
8185
  /**
7120
8186
  * The plan a row labels its references against: the tree, so a subtask's
@@ -7160,7 +8226,7 @@ interface TaskRowContext {
7160
8226
  * or a script task (ADR-0035): an ops task by nature, drawn with its body and
7161
8227
  * with no prompt, runner or model to edit.
7162
8228
  */
7163
- type TaskRowKind = 'ai' | 'user' | 'ops' | 'script';
8229
+ type TaskRowKind = 'ai' | 'user' | 'ops' | 'script' | 'delegated';
7164
8230
  /** The task's whole spend (see `taskUsageSummary`); `attempt` is the latest one, 0 before any reported. */
7165
8231
  interface TaskRowUsage extends Pick<TaskUsageSummary, 'totals' | 'contextFill'> {
7166
8232
  attempt: number;
@@ -7202,6 +8268,8 @@ type TaskRowLoop = {
7202
8268
  round: number | null;
7203
8269
  };
7204
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;
7205
8273
  usage: TaskRowUsage | null;
7206
8274
  /** "2", or "2.1" for a subtask. */
7207
8275
  orderLabel: string;
@@ -7237,10 +8305,16 @@ interface TaskRowView {
7237
8305
  autonomous: boolean;
7238
8306
  /** The repos its isolated work changed, by path; empty for a group of one. */
7239
8307
  repos: string[];
7240
- /** Integration stopped on a conflict: the one isolation state that needs the user. */
8308
+ /** Integration stopped on a conflict: the one isolation state that needs the user. `resolver` is the task, by reference, that has it; then a second is not offered. */
7241
8309
  conflict: {
7242
8310
  repo: string | null;
7243
8311
  files: string | null;
8312
+ resolver: string | null;
8313
+ } | null;
8314
+ /** A conflict repair that starts by itself once what it waits for clears (ADR-0015): `label` says what, in sentence case. Progress, so `conflict` is null. */
8315
+ repairWaiting: {
8316
+ label: string;
8317
+ files: string | null;
7244
8318
  } | null;
7245
8319
  /** A conflict repair in flight (ADR-0015) — progress, not a blocker. */
7246
8320
  repairing: {
@@ -7273,6 +8347,8 @@ interface TaskRowView {
7273
8347
  dependencies: string[];
7274
8348
  /** What the row offers while a plan runs. */
7275
8349
  actions: TaskRowAction[];
8350
+ /** Its landed work can be reverted before Merge all (ADR-0054): a surface offers Revert only then, and core's preview says the rest. */
8351
+ revertible: boolean;
7276
8352
  /** The one mark its toggle sets. */
7277
8353
  mark: 'complete' | 'uncomplete';
7278
8354
  opsEditable: boolean;
@@ -7309,6 +8385,7 @@ declare function loopRowParts(loop: TaskRowLoop): string[];
7309
8385
  */
7310
8386
  interface TaskLogHeaderTask {
7311
8387
  status: string;
8388
+ type?: string;
7312
8389
  awaitingReason?: AwaitingReason;
7313
8390
  skipReason?: string;
7314
8391
  resumeAt?: string | null;
@@ -7316,6 +8393,8 @@ interface TaskLogHeaderTask {
7316
8393
  idleSince?: string | null;
7317
8394
  /** Its last attempt left a runner session the task's composer can continue (ADR-0018, K1). */
7318
8395
  continuable?: boolean;
8396
+ /** A conflict repair of it starts by itself once this clears (ADR-0015). */
8397
+ repairWaits?: RepairWait;
7319
8398
  attemptUsage?: readonly AttemptUsage[];
7320
8399
  }
7321
8400
  /**
@@ -7353,7 +8432,7 @@ interface TaskLogHeaderView {
7353
8432
  /** The commands the attempt on screen loaded (#66): the last `runner_loaded` block of its log. */
7354
8433
  declare function taskLogCommands(view: Pick<TaskLogView, 'blocks'>): readonly RunnerCommand[];
7355
8434
  /** Whether an opened task's composer continues it rather than messaging its turn (ADR-0018, K1). */
7356
- declare function logContinues(task: Pick<TaskLogHeaderTask, 'status' | 'awaitingReason' | 'continuable'>): boolean;
8435
+ declare function logContinues(task: Pick<TaskLogHeaderTask, 'type' | 'status' | 'awaitingReason' | 'continuable'>): boolean;
7357
8436
  /** Whether the composer resumes it now, once the user says the limit has reset; a subset of `logContinues`. */
7358
8437
  declare function logResumes(task: Pick<TaskLogHeaderTask, 'status' | 'awaitingReason' | 'continuable'>): boolean;
7359
8438
  /**
@@ -7404,6 +8483,21 @@ type OverrideDirection = 'complete' | 'uncomplete';
7404
8483
  * completion, never the work or what already ran on it.
7405
8484
  */
7406
8485
  declare function manualOverrideConfirmation(direction: OverrideDirection, label: string): string;
8486
+ /** What a revert does (ADR-0054), told with the list of tasks it takes: the same words on every surface. */
8487
+ declare const revertEffect = "Its landed work comes off the integration branch, newest first, and each task listed is held as not started until you retry it. Nothing in your own branch changes.";
8488
+ /** How a revert's confirmation calls a task: a subtask's order alone would name a top-level task. */
8489
+ declare function revertTaskRef(task: {
8490
+ order: number;
8491
+ ref?: string;
8492
+ }): string;
8493
+ /** One task of a revert's confirmation: `dependent` is one that goes with the task the user chose. */
8494
+ declare function revertListLine(task: {
8495
+ order: number;
8496
+ ref?: string;
8497
+ title: string;
8498
+ repos: readonly string[];
8499
+ dependent: boolean;
8500
+ }): string;
7407
8501
  /** Why a manual override waits for the run to stop (ADR-0029); `label` names the task. */
7408
8502
  declare function manualOverrideStopFirst(direction: OverrideDirection, label: string): string;
7409
8503
  interface NewTaskFields {
@@ -7433,11 +8527,18 @@ declare const automergeSummary: (run: GlobalRunSettings) => string;
7433
8527
  declare const PLANNED_WITH_LABEL = "Planned-with line in commits Ordewell writes";
7434
8528
  declare const trailerSummary: (run: GlobalRunSettings) => string;
7435
8529
  declare const autorunSummary: (run: GlobalRunSettings) => string;
8530
+ declare const delegationSummary: (run: GlobalRunSettings) => string;
8531
+ /** Whether any surface offers the delegation setting, and whether a planner may write a delegated task. */
8532
+ declare const DELEGATION_OFFERED: boolean;
7436
8533
  declare const ACT_RISK = "Tasks it adds run too, ops tasks included, which can act on real systems unattended.";
7437
- declare const AUTORUN_RISK = "Plans start without review, ops tasks included, which can act on real systems unattended. In Full, the planner may also start tasks it adds later.";
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.";
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.";
7438
8537
  /** `on`/`off` as a switch's value; null for anything else. */
7439
8538
  declare function parseSwitch(arg: string): boolean | null;
8539
+ /** A delegation setting as typed: `off`, which is 0, or the tree's budget in tokens; null for anything else. */
8540
+ declare function parseDelegation(arg: string): number | null;
7440
8541
  /** A whole number at least `min`, as typed; null for anything else. */
7441
8542
  declare function parseCount(arg: string | undefined, min: number): number | null;
7442
8543
 
7443
- export { type DiffRowKind as $, ACT_RISK as A, type BlockingSubject as B, CHAT_COMMANDS as C, type CommandBlock as D, type CommandDescriptor as E, type CommandOutcome as F, type CommandPayload as G, type CommandStatus as H, type CommandsResponse as I, type CompactionBlock as J, type CompactionStatus as K, type Consent as L, type ContextCondenseRequest as M, ConversationBusyError as N, type ConversationCompactResponse as O, type ConversationCompaction as P, ConversationEditError as Q, type ConversationForkResponse as R, type ConversationInput as S, type ConversationRequest as T, type ConversationRewindResponse as U, type ConversationTurn as V, type ConversationView as W, DEFAULT_WEB_READ_BUDGET as X, type DaemonErrorCode as Y, type DecideOptions as Z, type DiffRow as _, AUTORUN_RISK as a, type PendingApproval as a$, type DiffStat as a0, type DisplayBlock as a1, EMPTY_CONVERSATION as a2, EMPTY_HOLD as a3, EMPTY_RUN as a4, EMPTY_TASK_LOG as a5, EMPTY_TASK_LOG_FOLLOW as a6, type ErrorBody as a7, type ExecuteCommandResponse as a8, type ExecuteResponse as a9, type LiveTail as aA, type LiveTailOptions as aB, type LocalEntry as aC, type LoopCheckFacts as aD, type LoopCheckState as aE, type LoopGuard as aF, type MappedTool as aG, type MarkdownToken as aH, type MergeGateEntry as aI, type MergeGateResponse as aJ, type MergeWordingHandoff as aK, type MessageBlock as aL, type MessageRole as aM, type ModelsResponse as aN, NOT_STARTED as aO, NO_TURN as aP, type NewTaskFields as aQ, type NotificationAction as aR, OUTPUT_LINES_DEFAULT as aS, OUTPUT_LINES_MAX as aT, type OkResponse as aU, type OpRef as aV, type OpRefusal as aW, type OrchestratorObserver as aX, type OutputPreview as aY, type OverrideDirection as aZ, PLANNED_WITH_LABEL as a_, type ExecutionCommand as aa, ExecutionCommands as ab, type ExecutionHost as ac, type ExecutionSummary as ad, type GatedConversation as ae, type GeneratePlanResponse as af, type GlobalRunSettings as ag, HEADLESS_CLI_COMMANDS as ah, type HandoffBlock as ai, type HandoffForkRepo as aj, type HandoffPeekSubject as ak, type IAiService as al, IDLE_ACTIVITY as am, type INotification as an, type IWebFetcher as ao, type IsolationDiffResponse as ap, type IsolationMergeResponse as aq, type IsolationPreviewNotice as ar, type IsolationPreviewResponse as as, IsolationRunController as at, LEASE_HEARTBEAT_MS as au, LEASE_STALE_MS as av, LeaseHost as aw, type LeaseHostKind as ax, type LeaseHostOptions as ay, type LiveOutputLookup as az, type AccountLimitBucket as b, type SessionData as b$, PendingApprovals as b0, type PendingApprovalsOptions as b1, type PlanBlock as b2, type PlanBlocks as b3, type PlanIntake as b4, type PlanMarkerStatus as b5, type PlanResponse as b6, type PlanSnapshot as b7, PlanStore as b8, type PlannerCommandSyntax as b9, type RewindTarget as bA, type RewindTargetsResponse as bB, type RunActivity as bC, type RunInput as bD, type RunMergeView as bE, RunSettingsError as bF, type RunSnapshotFrame as bG, type RunTask as bH, type RunTaskStatus as bI, type RunView as bJ, type RunnerAuth as bK, type RunnerAuthExec as bL, type RunnerAuthExecOptions as bM, type RunnerAuthExecResult as bN, type RunnerCatalog as bO, type RunnerLoadedBlock as bP, type RunnersResponse as bQ, STOPPED_BEFORE_START as bR, SUPERVISOR_LEVELS as bS, SUPERVISOR_LEVEL_LABEL as bT, type SerializedConversationMessage as bU, type SerializedPendingTurn as bV, type SerializedPlan as bW, type SerializedTask as bX, type SerializedTaskStatus as bY, type ServerInfoResponse as bZ, type SessionBroadcaster as b_, type PlannerContextDeps as ba, type PlannerContextStatus as bb, PlannerConversation as bc, type PlannerConversationHost as bd, type PlannerModelCandidate as be, type PlannerModelChoice as bf, PlannerModelMemory as bg, type PlannerModelRecall as bh, type PlannerModelStore as bi, type PlannerModes as bj, type PlannerNotReady as bk, PlannerNotReadyError as bl, type PlannerReadinessConfig as bm, type PlannerSubmission as bn, PlannerTurnDiscardedError as bo, PlannerTurnStoppedError as bp, PlannerUsageLimitError as bq, type PrdResponse as br, type PromptHold as bs, REPORT_PREVIEW_LINES as bt, RUN_SETTINGS_DEFAULTS as bu, type ReadRunnerAuthOptions as bv, type RefTask as bw, ResearchTasks as bx, type ResolveAll as by, type ResolveConflictResponse as bz, type AccountLimitSnapshot as c, type TaskRowPlacement as c$, type SessionFrame as c0, type SessionHeld as c1, SessionHeldError as c2, type SessionLease as c3, type SessionLeaseRecord as c4, type SessionListResponse as c5, type SessionMessage as c6, type SessionMeta as c7, type SessionNotice as c8, type SessionResponse as c9, type TaskLogBatch as cA, type TaskLogEvent as cB, type TaskLogFollow as cC, type TaskLogFollowInput as cD, type TaskLogHeaderKind as cE, type TaskLogHeaderTask as cF, type TaskLogHeaderUsage as cG, type TaskLogHeaderView as cH, type TaskLogResponse as cI, type TaskLogView as cJ, type TaskMessageResponse as cK, type TaskOp as cL, TaskOrchestrator as cM, type TaskOrchestratorDeps as cN, type TaskOrchestratorOptions as cO, type TaskOutputSource as cP, type TaskPlacement as cQ, type TaskQuery as cR, type TaskQueryCatalog as cS, type TaskQueryField as cT, type TaskRef as cU, type TaskRowAction as cV, type TaskRowContext as cW, type TaskRowHandoff as cX, type TaskRowIsolation as cY, type TaskRowKind as cZ, type TaskRowLoop as c_, type SettingsResponse as ca, SettingsService as cb, type SettingsUpdateResponse as cc, type SkillInvocation as cd, type SkillLoadBlock as ce, type SkillToken as cf, type StartAdmission as cg, type StopResponse as ch, type StreamStamp as ci, type SubagentBlock as cj, type SubagentChild as ck, type SubagentStatus as cl, type SupervisorPolicy 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 TaskAttemptSnapshot as cy, type TaskLogAttemptsResponse as cz, type AccountLimitStatus as d, describeMergeResult as d$, type TaskRowSubject as d0, type TaskRowUsage as d1, type TaskRowView as d2, type TaskRunFacts as d3, type TaskStatusKind as d4, type ThinkingDisplayBlock as d5, type ToolBlock as d6, type ToolHeadline as d7, type ToolStatus as d8, type TreeTask as d9, assignmentIssueText as dA, attachingLabel as dB, automergeSummary as dC, autorunSummary as dD, awaitingLabel as dE, blockNotice as dF, blockNotices as dG, blockedByLabel as dH, blockingFailures as dI, canMergeTasks as dJ, canSetDependencies as dK, canSplitTask as dL, capConflictFiles as dM, cardEditNeedsUnstarted as dN, cardEditable as dO, checkoutRule as dP, classifyOutcome as dQ, coalesceTaskLog as dR, compactionText as dS, confirmedOverride as dT, createAiService as dU, dependencyCandidates as dV, dependentsNotice as dW, dependentsOf as dX, describeHeld as dY, describeHolder as dZ, describeIsolationPreview as d_, TurnBudgets as da, type TurnGate as db, type UsageBlock as dc, type UserSettings as dd, WAITS_FOR_APPROVAL_LABEL as de, type WakeUpItem as df, type WebRead as dg, type WebReadDecision as dh, type WebReadOptions as di, WebReadPolicy as dj, type Worded as dk, type WorkspacesResponse as dl, abbreviateHome as dm, accountLimitPercentText as dn, accountLimitWindowLabel as dp, accountLimitsOf as dq, accountLimitsText as dr, accountLimitsTitle as ds, aheadOfDraft as dt, applyTaskOps as du, approvalKindText as dv, approvalLabel as dw, approvalStatusText as dx, assignedRunnerOf as dy, assignmentIssue as dz, type AccountLimitWindow as e, ranSealed as e$, describePlannerNotReady as e0, diffRows as e1, diffStat as e2, diffSummary as e3, digestTaskLog as e4, drainNext as e5, editableWhileRunning as e6, executionSummary as e7, followTaskLog as e8, followTurn as e9, mapAgentTool as eA, markAction as eB, markdownHtml as eC, markdownTokens as eD, mergeOutcome as eE, mergeTarget as eF, namedRepos as eG, newTaskFields as eH, noExecutorRefusal as eI, normalizeAgentArgs as eJ, opRefusalText as eK, opsEditable as eL, outputLines as eM, outputPreview as eN, overrideNeedsConfirmation as eO, parseCount as eP, parseSwitch as eQ, parseTaskOpsJson as eR, parseTaskQueryJson as eS, pastGateConfirmation as eT, planBlocks as eU, planMarkerParts as eV, plannerMessage as eW, plannerNotReady as eX, plannerNotReadyFor as eY, plannerTranscript as eZ, processLeaseHost as e_, fromTranscript as ea, getSettingsPath as eb, handoffBaseLine as ec, handoffBranchName as ed, handoffFindingsText as ee, handoffPeekParts as ef, hasAccountLimits as eg, hasHiddenDetail as eh, hasLongReport as ei, holdLabel as ej, holdPrompt as ek, isFileEditTool as el, isRepoGroup as em, isTaskRunning as en, lastServedModel as eo, loadedSkillTokens as ep, logContinues as eq, logResumes as er, loopBodyLabel as es, loopBodyRound as et, loopCheckLabel as eu, loopCheckState as ev, loopLeaveNotice as ew, loopRowParts as ex, manualOverrideConfirmation as ey, manualOverrideStopFirst as ez, type AccountLimitsFrame as f, truncateCheckpointSummary as f$, readRunnerAuth as f0, receiptLabel as f1, reconcileApprovals as f2, recordRunnerAuth as f3, recordedRunnerAuth as f4, reduceConversation as f5, reduceRun as f6, reduceTaskLog as f7, renderTaskQueryAnswer as f8, repairedLanded as f9, supervisorSummary as fA, surfacePlan as fB, surfacePlanState as fC, takeAt as fD, taskActionCommand as fE, taskChatAvailable as fF, taskLogCommands as fG, taskLogForSurface as fH, taskLogHeaderView as fI, taskOrderLabel as fJ, taskPlacement as fK, taskQuerySignature as fL, taskRef as fM, taskRefOf as fN, taskRowActions as fO, taskRowView as fP, taskStartedNotice as fQ, taskStatusKind as fR, textHasTaskOps as fS, textHasTaskQuery as fT, titledRefs as fU, titledTaskRef as fV, titledTaskRefOf as fW, toTaskLogEvent as fX, toolHeadline as fY, trailerSummary as fZ, trimToolOutput as f_, repairedNotice as fa, repeatingChecks as fb, replayTaskLog as fc, repoBaseLine as fd, repoResultLines as fe, reposWithWork as ff, resetRunnerAuth as fg, resolveSkillInvocation as fh, resolveTaskRef as fi, resolveUserTaskRef as fj, resumeLabel as fk, resumeTime as fl, runHoldsEditors as fm, runSettingsOf as fn, runnerToolSubject as fo, scriptExitText as fp, scriptRunText as fq, secondsText as fr, serializePlan as fs, serializeTask as ft, serializeTaskStatus as fu, skillTokens as fv, skippedLabel as fw, stopTurn as fx, subtreeIds as fy, summarizeToolCall as fz, type AccountLimitsMessage as g, unsendAll as g0, unsendLatest as g1, wakeUpItems as g2, walkPlan as g3, withoutSealedMark as g4, workspaceEnvOf as g5, type AdoptSessionResponse as h, type ApplyTaskOpsResult as i, type ApprovalAnswerResponse as j, type ApprovalBlock as k, type ApprovalRequestMessage as l, type ApprovalStatus as m, type ApprovalsResponse as n, type ApproveReviewResponse as o, type AskOptions as p, type AssignmentCatalog as q, type AssignmentIssue as r, type AssignmentScope as s, type AssignmentWriter as t, type AutoMergeTrigger as u, CHECKPOINT_TRUNCATE_LENGTH as v, type CancelPlanningResponse as w, type CancelResumeResponse as x, CliAgentAiService as y, type CliAgentAiServiceDeps 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 };