@ordewell/core 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/{IFileSystem-B5asBRDa.d.mts → IFileSystem-D96mT-bm.d.ts} +1 -1
- package/dist/{IFileSystem-CbCmUDBn.d.ts → IFileSystem-DXJCRw5x.d.mts} +1 -1
- package/dist/IRunner-CGCihlGQ.d.ts +1165 -0
- package/dist/IRunner-XVUddYVX.d.mts +1165 -0
- package/dist/{ModeResolver-DtfhWeVL.d.ts → ModeResolver-DE-o2lRF.d.mts} +2 -2
- package/dist/{ModeResolver-DcqAho4y.d.mts → ModeResolver-EWlpmVAy.d.ts} +2 -2
- package/dist/Task-1Q19y7sZ.d.mts +1122 -0
- package/dist/Task-1Q19y7sZ.d.ts +1122 -0
- package/dist/{chunk-OUIQWNS3.mjs → chunk-2M4X3VB7.mjs} +15 -5
- package/dist/chunk-2M4X3VB7.mjs.map +1 -0
- package/dist/{chunk-AJ5KDO6V.mjs → chunk-BLLIBCCS.mjs} +117 -198
- package/dist/chunk-BLLIBCCS.mjs.map +1 -0
- package/dist/{chunk-MZYZS7B3.mjs → chunk-UUU46PVQ.mjs} +68 -8
- package/dist/chunk-UUU46PVQ.mjs.map +1 -0
- package/dist/index.d.mts +516 -713
- package/dist/index.d.ts +516 -713
- package/dist/index.js +5012 -6370
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3275 -4589
- package/dist/index.mjs.map +1 -1
- package/dist/order-labels.d.mts +1 -4
- package/dist/order-labels.d.ts +1 -4
- package/dist/{parsing-COFGUEWx.d.ts → parsing-CZdp2p_n.d.ts} +5 -3
- package/dist/{parsing-D0uo-Icd.d.mts → parsing-GgUJoYKs.d.mts} +5 -3
- package/dist/parsing.d.mts +3 -6
- package/dist/parsing.d.ts +3 -6
- package/dist/parsing.js +25 -4
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +2 -2
- package/dist/{plan-utils-CdW3mxt5.d.mts → plan-utils-BMayhGBj.d.mts} +89 -12
- package/dist/{plan-utils-D4Z8UyLL.d.ts → plan-utils-Cv48fe7r.d.ts} +89 -12
- package/dist/plan-utils.d.mts +4 -4
- package/dist/plan-utils.d.ts +4 -4
- package/dist/plan-utils.js +120 -65
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +7 -3
- package/dist/testing.d.mts +12 -24
- package/dist/testing.d.ts +12 -24
- package/dist/testing.js +24 -28
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +17 -27
- package/dist/testing.mjs.map +1 -1
- package/package.json +1 -1
- package/skills/tdd/SKILL.md +19 -0
- package/dist/Task-BpzIbunq.d.mts +0 -2599
- package/dist/Task-BpzIbunq.d.ts +0 -2599
- package/dist/chunk-AJ5KDO6V.mjs.map +0 -1
- package/dist/chunk-MZYZS7B3.mjs.map +0 -1
- package/dist/chunk-OUIQWNS3.mjs.map +0 -1
package/dist/Task-BpzIbunq.d.ts
DELETED
|
@@ -1,2599 +0,0 @@
|
|
|
1
|
-
import { ChildProcess } from 'child_process';
|
|
2
|
-
import { EventEmitter } from 'events';
|
|
3
|
-
import { z } from 'zod/v4';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Why isolated execution is unavailable for a workspace. The orchestrator needs
|
|
7
|
-
* the reason, not just a boolean: `dirty` is offered a stash or an explicit
|
|
8
|
-
* "run without isolation", while the others fall back to the shared
|
|
9
|
-
* workspace root with a one-line notice.
|
|
10
|
-
*/
|
|
11
|
-
type IsolationInactiveReason = 'disabled' | 'git-missing' | 'not-git' | 'no-commits' | 'dirty';
|
|
12
|
-
/**
|
|
13
|
-
* `repos` names, relative to the workspace, the repositories behind the answer:
|
|
14
|
-
* when active, the ones that will isolate, with `shared` the paths every task
|
|
15
|
-
* will share live — loose entries of the workspace root, or, for a lone
|
|
16
|
-
* repository, the repositories nested inside it; otherwise the dirty ones of a
|
|
17
|
-
* `dirty` group, or the commitless ones of a `no-commits` group. A group of one
|
|
18
|
-
* names none.
|
|
19
|
-
*/
|
|
20
|
-
type IsolationAvailability = {
|
|
21
|
-
active: true;
|
|
22
|
-
repos?: string[];
|
|
23
|
-
shared?: string[];
|
|
24
|
-
} | {
|
|
25
|
-
active: false;
|
|
26
|
-
reason: IsolationInactiveReason;
|
|
27
|
-
repos?: string[];
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Where a run's tasks work, as the planner is told it: the repos of the group
|
|
31
|
-
* and the paths shared live between tasks. A lone repository is `['.']` with
|
|
32
|
-
* nothing shared, unless it holds nested repositories, which are shared live
|
|
33
|
-
* and named in `shared`.
|
|
34
|
-
*/
|
|
35
|
-
interface RepoGroupLayout {
|
|
36
|
-
repos: string[];
|
|
37
|
-
shared: string[];
|
|
38
|
-
}
|
|
39
|
-
type IsolationOutcome = 'merged' | 'conflict' | 'failed';
|
|
40
|
-
/**
|
|
41
|
-
* `active` — worktree exists, a runner may be writing to it.
|
|
42
|
-
* `kept` — released with its worktree and branch preserved for inspection.
|
|
43
|
-
* `conflict` — integration stopped on a merge conflict; worktree and refs kept.
|
|
44
|
-
* `repairing` — a conflict repair (ADR-0015) is working in the kept worktree;
|
|
45
|
-
* one that ends without landing leaves the task `conflict` again.
|
|
46
|
-
* `failed` — integration hit a git error other than a conflict; refs kept.
|
|
47
|
-
* `merged` — landed on the integration branch; worktree and task branch removed.
|
|
48
|
-
*/
|
|
49
|
-
type IsolationTaskStatus = 'active' | 'kept' | 'conflict' | 'repairing' | 'failed' | 'merged';
|
|
50
|
-
/** One repo's share of a task: its worktree inside the task workspace. */
|
|
51
|
-
interface IsolationTaskRepo {
|
|
52
|
-
/** Absolute path of this repo's worktree: the task workspace joined with the repo's path. */
|
|
53
|
-
worktree: string;
|
|
54
|
-
/**
|
|
55
|
-
* Paths linked from the real workspace and kept out of the task's commit:
|
|
56
|
-
* the artifacts bootstrapped from the real repo, and a lone repository's
|
|
57
|
-
* nested repositories (`run.shared`). Recorded because a symlink is not
|
|
58
|
-
* matched by an ignore rule such as `node_modules/` and would otherwise be
|
|
59
|
-
* committed.
|
|
60
|
-
*/
|
|
61
|
-
linked: string[];
|
|
62
|
-
/** Whether the task brought commits to this repo; unknown until it first integrates. */
|
|
63
|
-
changed?: boolean;
|
|
64
|
-
/**
|
|
65
|
-
* The integration tip right after the task landed here: what the user's
|
|
66
|
-
* HEAD must contain for the task's work to count as merged (ADR-0020).
|
|
67
|
-
*/
|
|
68
|
-
landedTip?: string;
|
|
69
|
-
}
|
|
70
|
-
interface IsolationTaskRecord {
|
|
71
|
-
taskId: string;
|
|
72
|
-
order: number;
|
|
73
|
-
title: string;
|
|
74
|
-
/** One branch name, the same in every repo, so a task is one name to look up across the group. */
|
|
75
|
-
branch: string;
|
|
76
|
-
/**
|
|
77
|
-
* Absolute path of the task workspace, holding one worktree per repo at the
|
|
78
|
-
* repo's path. The Runner's cwd is inside it when the workspace is a repo
|
|
79
|
-
* subdirectory; for a group of one it is the worktree.
|
|
80
|
-
*/
|
|
81
|
-
workspace: string;
|
|
82
|
-
/** For the task as a whole: landing is atomic across the repos it changed. */
|
|
83
|
-
status: IsolationTaskStatus;
|
|
84
|
-
/** Keyed by repo path. */
|
|
85
|
-
repos: Record<string, IsolationTaskRepo>;
|
|
86
|
-
/** The repo whose merge stopped the task from landing, while `status` is `conflict` or `failed`. */
|
|
87
|
-
conflictRepo?: string;
|
|
88
|
-
/**
|
|
89
|
-
* What a `failed` landing stopped on, in words a surface can repeat — a
|
|
90
|
-
* missing worktree is the common one. Cleared by the next landing attempt,
|
|
91
|
-
* so a stale reason cannot outlive the failure it explains.
|
|
92
|
-
*/
|
|
93
|
-
landingError?: string;
|
|
94
|
-
/** Repo-relative paths, in `conflictRepo`, that conflicted; set only while `status` is `conflict` or `repairing`. */
|
|
95
|
-
conflictFiles?: string[];
|
|
96
|
-
/**
|
|
97
|
-
* Conflict repairs started for this task (ADR-0015). Counted when one starts,
|
|
98
|
-
* so a crash cannot hand the spent attempt back; absent reads as none.
|
|
99
|
-
*/
|
|
100
|
-
repairs?: number;
|
|
101
|
-
/**
|
|
102
|
-
* Keyed by repo path: each changed repo's integration tip when the repair in
|
|
103
|
-
* flight started — what the task branch must contain before it may land.
|
|
104
|
-
* Set only while `status` is `repairing`.
|
|
105
|
-
*/
|
|
106
|
-
repairBase?: Record<string, string>;
|
|
107
|
-
/**
|
|
108
|
-
* Every file a repair was started for, across all of them: as `conflictFiles`
|
|
109
|
-
* names it in a group of one, prefixed with its repo's path in a group.
|
|
110
|
-
*/
|
|
111
|
-
repairedFiles?: string[];
|
|
112
|
-
/**
|
|
113
|
-
* Set once the task's landed work is in the checked-out HEAD of every repo
|
|
114
|
-
* it changed — merged into the user's branch, by Merge all or by hand. What
|
|
115
|
-
* a merge gate waits for (ADR-0020).
|
|
116
|
-
*/
|
|
117
|
-
inHead?: true;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* A task's landing in flight: each changed repo's integration tip from before
|
|
121
|
-
* the task's merge. On the run rather than the task record because it must
|
|
122
|
-
* outlive that record — a retry drops and recreates it — until every repo is
|
|
123
|
-
* back at its tip or the task has landed.
|
|
124
|
-
*/
|
|
125
|
-
interface IsolationLanding {
|
|
126
|
-
taskId: string;
|
|
127
|
-
/** Keyed by repo path. */
|
|
128
|
-
tips: Record<string, string>;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* One repository of the group (ADR-0014). Every git operation on it runs in
|
|
132
|
-
* `root`, never in the workspace root.
|
|
133
|
-
*/
|
|
134
|
-
interface IsolationRepo {
|
|
135
|
-
/** Relative to the workspace root; `.` when the workspace is itself the repository. */
|
|
136
|
-
path: string;
|
|
137
|
-
/**
|
|
138
|
-
* Absolute: the workspace root joined with `path`. For a group of one that is
|
|
139
|
-
* the workspace root, which may be a subdirectory of the repository.
|
|
140
|
-
*/
|
|
141
|
-
root: string;
|
|
142
|
-
/** The commit checked out at run start. Switching branches mid-run does not retarget it. */
|
|
143
|
-
baseRef: string;
|
|
144
|
-
/** Branch name checked out at run start; absent on a detached HEAD. */
|
|
145
|
-
baseBranch?: string;
|
|
146
|
-
integrationBranch: string;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* One Execute-Plan click or one manual task run over the workspace's repo
|
|
150
|
-
* group. Plain JSON on purpose: the orchestrator persists it with the plan
|
|
151
|
-
* state so a resumed session can find its integration branches again. The
|
|
152
|
-
* module mutates `tasks` in place.
|
|
153
|
-
*/
|
|
154
|
-
interface IsolationRun {
|
|
155
|
-
id: string;
|
|
156
|
-
workspaceRoot: string;
|
|
157
|
-
repos: IsolationRepo[];
|
|
158
|
-
/**
|
|
159
|
-
* Workspace paths outside every isolated repo, linked live into each task
|
|
160
|
-
* workspace: loose entries of the workspace root, the entries beside a deeper
|
|
161
|
-
* repo, and `sharedRepos`. For a lone repository, the repositories nested
|
|
162
|
-
* inside it, which cannot be isolated with it and so are linked live instead.
|
|
163
|
-
* Empty when a group of one holds no nested repositories.
|
|
164
|
-
*/
|
|
165
|
-
shared: string[];
|
|
166
|
-
/** Repos of the group that could not be isolated — no commits, or git refused a worktree — and are among `shared`. */
|
|
167
|
-
sharedRepos: string[];
|
|
168
|
-
/** Keyed by task id — ids are unique within one plan and a run belongs to one plan. */
|
|
169
|
-
tasks: Record<string, IsolationTaskRecord>;
|
|
170
|
-
/**
|
|
171
|
-
* Set before a task's first merge and cleared once it has landed or been
|
|
172
|
-
* rolled back. One found set — after a crash, or a rollback git refused —
|
|
173
|
-
* names exactly what to return each repo's integration branch to.
|
|
174
|
-
*/
|
|
175
|
-
landing?: IsolationLanding;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* What a plan persists of isolated execution (`LegacyPlanState.isolation`): its
|
|
179
|
-
* run, and which added tasks resolve which conflicts. Belongs to that plan and
|
|
180
|
-
* its branches alone, so a copy of the plan (a fork) must not carry it.
|
|
181
|
-
*/
|
|
182
|
-
interface PlanIsolation {
|
|
183
|
-
run: IsolationRun;
|
|
184
|
-
/** Resolver task id → the conflicted task whose branch it merges. */
|
|
185
|
-
resolvers: Record<string, string>;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* A task's isolation as a surface shows it. `kept` covers every record whose
|
|
189
|
-
* worktree stays for inspection — a failed verdict, an interrupted attempt, an
|
|
190
|
-
* integration git refused — because to the user they are one thing: work that
|
|
191
|
-
* did not land and can be looked at. `none` is a task with no worktree in a plan
|
|
192
|
-
* that has an isolation run.
|
|
193
|
-
*/
|
|
194
|
-
type TaskIsolationState = 'none' | 'active' | 'integrated' | 'conflict' | 'repairing' | 'kept';
|
|
195
|
-
type TaskIsolation = {
|
|
196
|
-
state: 'none';
|
|
197
|
-
} | {
|
|
198
|
-
state: Exclude<TaskIsolationState, 'none'>;
|
|
199
|
-
branch: string;
|
|
200
|
-
/** The task workspace; for a group of one, the task's worktree. */
|
|
201
|
-
worktree: string;
|
|
202
|
-
/** Paths of the repos the task changed. */
|
|
203
|
-
repos: string[];
|
|
204
|
-
conflictRepo?: string;
|
|
205
|
-
/** Repo-relative paths, in `conflictRepo`, that conflicted. */
|
|
206
|
-
conflictFiles?: string[];
|
|
207
|
-
/** The conflict repair running or last run, of the most a task may have; absent before its first. */
|
|
208
|
-
repair?: {
|
|
209
|
-
attempt: number;
|
|
210
|
-
limit: number;
|
|
211
|
-
};
|
|
212
|
-
/** What {@link IsolationTaskRecord.repairedFiles} says. */
|
|
213
|
-
repairedFiles?: string[];
|
|
214
|
-
};
|
|
215
|
-
interface IsolationLandedTask {
|
|
216
|
-
taskId: string;
|
|
217
|
-
order: number;
|
|
218
|
-
title: string;
|
|
219
|
-
/** Set when the task landed only after a conflict repair (ADR-0015): the files it was started for. */
|
|
220
|
-
repairedFiles?: string[];
|
|
221
|
-
}
|
|
222
|
-
interface IsolationHandoffRepo {
|
|
223
|
-
path: string;
|
|
224
|
-
integrationBranch: string;
|
|
225
|
-
baseRef: string;
|
|
226
|
-
/** Tasks whose work landed in this repo, in plan order. */
|
|
227
|
-
landed: IsolationLandedTask[];
|
|
228
|
-
}
|
|
229
|
-
interface IsolationHandoff {
|
|
230
|
-
repos: IsolationHandoffRepo[];
|
|
231
|
-
/** Tasks that landed on the integration branches, in plan order. */
|
|
232
|
-
landed: IsolationLandedTask[];
|
|
233
|
-
}
|
|
234
|
-
/** A plan's isolation as a surface shows it: a mark for each task the run touched, and its handoff. */
|
|
235
|
-
interface IsolationView {
|
|
236
|
-
tasks: Record<string, TaskIsolation>;
|
|
237
|
-
handoff: IsolationHandoff;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Why "Merge all" would not touch a repo. `partial-landing`: a task's landing
|
|
241
|
-
* was interrupted and could not be rolled back there, so its integration
|
|
242
|
-
* branch holds part of a task.
|
|
243
|
-
*/
|
|
244
|
-
type IsolationMergeBlockReason = 'merge-in-progress' | 'conflict' | 'uncommitted-changes' | 'partial-landing' | 'git-error';
|
|
245
|
-
interface IsolationMergeBlock {
|
|
246
|
-
repo: string;
|
|
247
|
-
reason: IsolationMergeBlockReason;
|
|
248
|
-
/** The files that would conflict, or the user's uncommitted ones the merge also changes; empty for the other reasons. */
|
|
249
|
-
files: string[];
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* How "Merge all" went.
|
|
253
|
-
* - `merged`: every repo with work on its integration branch took it.
|
|
254
|
-
* - `blocked`: the preflight found repos that could not, so nothing was
|
|
255
|
-
* touched anywhere; `blocked` says which and why.
|
|
256
|
-
* - `conflict` / `failed`: a merge stopped in `repo` — on git older than 2.38,
|
|
257
|
-
* which cannot preflight, or for a reason no preflight could foresee. That
|
|
258
|
-
* merge was aborted, leaving `repo` as it was; `landed` names the repos
|
|
259
|
-
* merged before it, which stay merged, and is absent when there are none.
|
|
260
|
-
*
|
|
261
|
-
* A group of one is blocked only by a partial landing; otherwise its one merge
|
|
262
|
-
* lands or is aborted whole, so it reports as it always has.
|
|
263
|
-
*/
|
|
264
|
-
type IsolationMergeResult = {
|
|
265
|
-
outcome: 'merged';
|
|
266
|
-
} | {
|
|
267
|
-
outcome: 'blocked';
|
|
268
|
-
blocked: IsolationMergeBlock[];
|
|
269
|
-
} | {
|
|
270
|
-
outcome: 'conflict' | 'failed';
|
|
271
|
-
repo: string;
|
|
272
|
-
files?: string[];
|
|
273
|
-
landed?: string[];
|
|
274
|
-
};
|
|
275
|
-
/**
|
|
276
|
-
* What `discard` does with each repo's integration branch: `keep` it for review
|
|
277
|
-
* or merge, `delete` it, or delete it only in the repos whose checked-out HEAD
|
|
278
|
-
* already contains it (`delete-merged`) — the one way that can never give up
|
|
279
|
-
* landed work the user has not merged.
|
|
280
|
-
*/
|
|
281
|
-
type IntegrationDisposal = 'keep' | 'delete' | 'delete-merged';
|
|
282
|
-
/**
|
|
283
|
-
* Whether a conflict repair's work may land. `not-merged`: the task branch in
|
|
284
|
-
* `repo` does not contain the tip the repair started from. `conflict-markers`:
|
|
285
|
-
* it adds leftover conflict markers to `files`. `failed`: git could not tell.
|
|
286
|
-
*/
|
|
287
|
-
type RepairEvidence = {
|
|
288
|
-
ok: true;
|
|
289
|
-
} | {
|
|
290
|
-
ok: false;
|
|
291
|
-
reason: 'not-merged' | 'conflict-markers' | 'failed';
|
|
292
|
-
repo: string;
|
|
293
|
-
files?: string[];
|
|
294
|
-
};
|
|
295
|
-
interface PreparedTask {
|
|
296
|
-
cwd: string;
|
|
297
|
-
branch: string;
|
|
298
|
-
/**
|
|
299
|
-
* Paths, relative to the task workspace, that are copies rather than links
|
|
300
|
-
* because a hard link was impossible (Windows, another volume). Edits to them
|
|
301
|
-
* stay in the task, so the user is told.
|
|
302
|
-
*/
|
|
303
|
-
copied: string[];
|
|
304
|
-
/** What the attempt this one replaces left unlanded, kept on a branch before its worktree went; absent when nothing was. */
|
|
305
|
-
preserved?: PreservedWork[];
|
|
306
|
-
}
|
|
307
|
-
/** A task as a removal names it. */
|
|
308
|
-
type IsolationTaskRef = Pick<IsolationTaskRecord, 'taskId' | 'order' | 'title'>;
|
|
309
|
-
/**
|
|
310
|
-
* Work a removal found that had not landed — edits never committed, or
|
|
311
|
-
* commits only the task's branch carried — committed and kept on `branch`,
|
|
312
|
-
* a branch of its own under `ordewell-preserved/`, before the worktree went.
|
|
313
|
-
*/
|
|
314
|
-
interface PreservedWork {
|
|
315
|
-
/** Absent for a leftover directory no task record owned. */
|
|
316
|
-
task?: IsolationTaskRef;
|
|
317
|
-
repo: string;
|
|
318
|
-
branch: string;
|
|
319
|
-
/** The commit `branch` points at. */
|
|
320
|
-
commit: string;
|
|
321
|
-
}
|
|
322
|
-
/** A worktree a removal left in place, because the work in it could not be kept any other way. */
|
|
323
|
-
interface RefusedRemoval {
|
|
324
|
-
/** Absent for a leftover directory no task record owned. */
|
|
325
|
-
task?: IsolationTaskRef;
|
|
326
|
-
worktree: string;
|
|
327
|
-
/** What stopped git keeping it, in words a surface can repeat. */
|
|
328
|
-
reason: string;
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* What removing task worktrees did in place of deleting work. A removal never
|
|
332
|
-
* deletes work that has not landed: it keeps it on a branch first
|
|
333
|
-
* (`preserved`), and where it cannot, it leaves that worktree alone
|
|
334
|
-
* (`refused`) — so the record of a refused task stays too.
|
|
335
|
-
*/
|
|
336
|
-
interface IsolationRemoval {
|
|
337
|
-
preserved: PreservedWork[];
|
|
338
|
-
refused: RefusedRemoval[];
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* What a crash-recovery prune found and left alone: task records that were
|
|
342
|
-
* `active` yet still held unlanded work, so the prune kept them as `kept`
|
|
343
|
-
* rather than deleting work no one else has.
|
|
344
|
-
*/
|
|
345
|
-
interface IsolationPruneResult extends IsolationRemoval {
|
|
346
|
-
kept: IsolationTaskRef[];
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* The tracked changes of a workspace's repos at one moment, against each
|
|
350
|
-
* repo's HEAD: repo path → file path → the content it held then. What an ops
|
|
351
|
-
* task's tree check compares against (ADR-0020).
|
|
352
|
-
*/
|
|
353
|
-
type TreeSnapshot = Record<string, Record<string, string>>;
|
|
354
|
-
interface IWorktreeIsolation {
|
|
355
|
-
/**
|
|
356
|
-
* A repo group with at least one repo to isolate, a clean tracked tree in
|
|
357
|
-
* each, and the config enabled; otherwise the reason it is not.
|
|
358
|
-
*/
|
|
359
|
-
isActive(workspaceRoot: string): Promise<IsolationAvailability>;
|
|
360
|
-
/**
|
|
361
|
-
* Put the tracked changes of every dirty repo of the group on its git stash,
|
|
362
|
-
* the user's way out of a `dirty` refusal. Untracked files stay: they never
|
|
363
|
-
* block isolation.
|
|
364
|
-
*/
|
|
365
|
-
stash(workspaceRoot: string): Promise<void>;
|
|
366
|
-
/**
|
|
367
|
-
* Mint a run: resolve each repo's base ref to a commit now, and share the
|
|
368
|
-
* repos that cannot be isolated. Only meaningful after `isActive` said yes;
|
|
369
|
-
* throws when no repo of the group can be isolated after all.
|
|
370
|
-
*/
|
|
371
|
-
startRun(workspaceRoot: string): Promise<IsolationRun>;
|
|
372
|
-
/**
|
|
373
|
-
* Create the task workspace — one worktree per isolated repo from its
|
|
374
|
-
* integration tip, the shared paths linked in — and return the cwd to spawn
|
|
375
|
-
* the Runner into. A second `prepare` for the same task is a retry: the old
|
|
376
|
-
* attempt is discarded and the workspace recreated from the tips, so the
|
|
377
|
-
* task sees everything its predecessors have integrated.
|
|
378
|
-
*/
|
|
379
|
-
prepare(task: Task, run: IsolationRun): Promise<PreparedTask>;
|
|
380
|
-
/**
|
|
381
|
-
* Hand a conflicted task's kept workspace to a conflict repair (ADR-0015)
|
|
382
|
-
* as it is — nothing is re-cut — and return the same cwd. Records each
|
|
383
|
-
* changed repo's integration tip as `repairBase`, counts the repair, and
|
|
384
|
-
* moves the task to `repairing`. Throws for a task that is not `conflict`.
|
|
385
|
-
*/
|
|
386
|
-
reopen(task: Task, run: IsolationRun): Promise<PreparedTask>;
|
|
387
|
-
/**
|
|
388
|
-
* The evidence a repair must show before it lands: its work committed, and
|
|
389
|
-
* in each repo of `repairBase` the task branch containing that tip
|
|
390
|
-
* (`git merge-base --is-ancestor`) and adding no leftover conflict markers
|
|
391
|
-
* (`git diff --check`; whitespace warnings do not count). Changes nothing
|
|
392
|
-
* else: a task that fails stays `repairing` until released.
|
|
393
|
-
*/
|
|
394
|
-
verifyRepair(task: Task, run: IsolationRun): Promise<RepairEvidence>;
|
|
395
|
-
/**
|
|
396
|
-
* Land the task atomically across the repos it changed: commit each
|
|
397
|
-
* worktree, then `git merge --no-ff` the task branch into each changed
|
|
398
|
-
* repo's integration branch. If any merge conflicts or fails, it is aborted
|
|
399
|
-
* and the merges already made for the task are reset away, so `merged`
|
|
400
|
-
* always means the whole task landed. Serialized inside the module; among
|
|
401
|
-
* tasks waiting at once the lowest plan order goes first. On anything but
|
|
402
|
-
* `merged` the worktrees and refs stay, and nothing is resolved here: a
|
|
403
|
-
* conflict is repaired, if at all, by a new attempt of the task in its own
|
|
404
|
-
* worktree (ADR-0015), never inside this queue.
|
|
405
|
-
*
|
|
406
|
-
* `persist` is called once `run.landing` is set and before the first
|
|
407
|
-
* merge; the caller saves the run there, synchronously, which is what
|
|
408
|
-
* lets `pruneOrphans` finish a landing a crash interrupted.
|
|
409
|
-
*/
|
|
410
|
-
integrate(task: Task, run: IsolationRun, persist?: () => void): Promise<IsolationOutcome>;
|
|
411
|
-
/**
|
|
412
|
-
* `keep: false` removes the task's worktree, branch and record (retry, task
|
|
413
|
-
* removal), keeping whatever had not landed on a branch first; a record
|
|
414
|
-
* whose work cannot be kept stays, off `active`. `keep: true` leaves the
|
|
415
|
-
* worktree and branch exactly as they are
|
|
416
|
-
* for inspection — a failed verdict, a stop, a cancel — and only moves the task off `active`,
|
|
417
|
-
* so a crash-recovery prune does not sweep it away; a repair it ends leaves
|
|
418
|
-
* the task `conflict`, as it was before the repair. Takes the run rather than
|
|
419
|
-
* a bare task id: ids are only unique within one plan, and one daemon serves
|
|
420
|
-
* many (ADR-0007).
|
|
421
|
-
*/
|
|
422
|
-
release(run: IsolationRun, taskId: string, opts: {
|
|
423
|
-
keep: boolean;
|
|
424
|
-
}): Promise<IsolationRemoval>;
|
|
425
|
-
/** End of run: park the integration branch for review and report what landed. */
|
|
426
|
-
handoff(run: IsolationRun): Promise<IsolationHandoff>;
|
|
427
|
-
/**
|
|
428
|
-
* Drop what a crash left behind: a landing it interrupted is rolled back in
|
|
429
|
-
* every repo, a repair it interrupted leaves its task `conflict`, then stale
|
|
430
|
-
* active worktrees and directories no record owns go. An `active` record
|
|
431
|
-
* that still holds unlanded work — commits its branch alone carries, or
|
|
432
|
-
* edits in its worktree — is not a crash orphan: it may belong to a runner
|
|
433
|
-
* another host is still driving, so it is kept as `kept` and named in the
|
|
434
|
-
* result. A leftover directory holding work is kept on a branch before it
|
|
435
|
-
* goes, as {@link release} does.
|
|
436
|
-
*/
|
|
437
|
-
pruneOrphans(run: IsolationRun): Promise<IsolationPruneResult>;
|
|
438
|
-
/** Unified diff of each repo's integration branch against its base ref. */
|
|
439
|
-
reviewDiff(run: IsolationRun): Promise<string>;
|
|
440
|
-
/**
|
|
441
|
-
* Mark the landed tasks whose work the checked-out HEAD now contains in
|
|
442
|
-
* every repo they changed (`git merge-base --is-ancestor`), and return their
|
|
443
|
-
* ids. Only adds marks: a task found merged stays merged.
|
|
444
|
-
*/
|
|
445
|
-
findInHead(run: IsolationRun): Promise<string[]>;
|
|
446
|
-
/**
|
|
447
|
-
* The tracked changes of each committed repo of the workspace's group,
|
|
448
|
-
* except those in `exclude`; null when there is no repo to snapshot.
|
|
449
|
-
* Untracked and ignored files never count.
|
|
450
|
-
*/
|
|
451
|
-
snapshotTree(workspaceRoot: string, exclude: readonly string[]): Promise<TreeSnapshot | null>;
|
|
452
|
-
/**
|
|
453
|
-
* Tracked files whose content differs from what `snapshot` recorded: new
|
|
454
|
-
* changes, and further changes to files already changed then. Paths are
|
|
455
|
-
* from the workspace root. A repo git cannot read now is left out.
|
|
456
|
-
*/
|
|
457
|
-
changedSince(workspaceRoot: string, snapshot: TreeSnapshot): Promise<string[]>;
|
|
458
|
-
/**
|
|
459
|
-
* "Merge all": merge each repo's integration branch into whatever the user
|
|
460
|
-
* has checked out there. The one irreversible step, so it only ever happens
|
|
461
|
-
* when a caller asks for it. Every repo with work is preflighted first — no
|
|
462
|
-
* merge of the user's in progress, no conflict against their HEAD, no
|
|
463
|
-
* uncommitted edit to a file the merge changes — and unless all pass,
|
|
464
|
-
* nothing is merged anywhere. Only a merge Ordewell itself just started is
|
|
465
|
-
* ever aborted; nothing of the user's is reset. Waits for a landing in
|
|
466
|
-
* flight and holds the next back, so a merge during a run takes the
|
|
467
|
-
* integration branches between two landings, never part of one.
|
|
468
|
-
*/
|
|
469
|
-
mergeIntoCheckedOut(run: IsolationRun): Promise<IsolationMergeResult>;
|
|
470
|
-
/**
|
|
471
|
-
* Remove every worktree and task branch of the run, and settle each repo's
|
|
472
|
-
* integration branch as `integration` says. Anything but `keep` also clears
|
|
473
|
-
* the run's task records, except those of worktrees it refused to remove.
|
|
474
|
-
* Work that had not landed is kept on a branch first, as {@link release} does.
|
|
475
|
-
*/
|
|
476
|
-
discard(run: IsolationRun, opts: {
|
|
477
|
-
integration: IntegrationDisposal;
|
|
478
|
-
}): Promise<IsolationRemoval>;
|
|
479
|
-
/**
|
|
480
|
-
* Clear what other runs left in each repo of `run`'s group: every
|
|
481
|
-
* `ordewell/<run-id>/…` branch the repo's checked-out HEAD already contains.
|
|
482
|
-
* Never a branch of `run` itself, one a worktree has checked out, or any
|
|
483
|
-
* branch of a run that still has a worktree — that run may be live in
|
|
484
|
-
* another plan. Tries every repo, then throws naming those where git failed.
|
|
485
|
-
*/
|
|
486
|
-
sweep(run: IsolationRun): Promise<void>;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/** Reads a runner's `--version` line, or undefined when it will not say. */
|
|
490
|
-
type RunnerVersionProbe = (command: string, PATH: string) => Promise<string | undefined>;
|
|
491
|
-
|
|
492
|
-
/** One source for the spawn options, so a runner built on this base cannot drift from the interface. */
|
|
493
|
-
type RunnerSpawnOptions = Parameters<ITerminalRunner['spawn']>[0];
|
|
494
|
-
declare abstract class AbstractTerminalSession implements ITerminalSession {
|
|
495
|
-
id: string;
|
|
496
|
-
taskId: string;
|
|
497
|
-
protected exited: boolean;
|
|
498
|
-
protected outputEmitter: EventEmitter<any>;
|
|
499
|
-
protected exitEmitter: EventEmitter<any>;
|
|
500
|
-
constructor(id: string, taskId: string);
|
|
501
|
-
protected baseHandleExit(code: number): void;
|
|
502
|
-
onOutput(callback: (text: string) => void): void;
|
|
503
|
-
onExit(callback: (code: number) => void): void;
|
|
504
|
-
abstract kill(): void;
|
|
505
|
-
abstract getOutput(): string;
|
|
506
|
-
abstract write(text: string): void;
|
|
507
|
-
}
|
|
508
|
-
declare abstract class AbstractRunner<S extends ITerminalSession> implements ITerminalRunner {
|
|
509
|
-
protected sessions: Map<string, S>;
|
|
510
|
-
get activeCount(): number;
|
|
511
|
-
stop(sessionId: string): void;
|
|
512
|
-
stopAll(): void;
|
|
513
|
-
protected registerSession(id: string, session: S): void;
|
|
514
|
-
abstract spawn(opts: RunnerSpawnOptions): Promise<ITerminalSession>;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
declare function stripAnsi(text: string): string;
|
|
518
|
-
declare function posixShellQuote(s: string): string;
|
|
519
|
-
/**
|
|
520
|
-
* Wrap a command for execution inside a POSIX login shell, which is what
|
|
521
|
-
* resolves runner binaries managed by nvm/volta/asdf.
|
|
522
|
-
*
|
|
523
|
-
* Deliberately POSIX-only. This used to take a `platform` and emit
|
|
524
|
-
* `powershell.exe -Command "'claude' '-p' '…'"` for Windows, which PowerShell
|
|
525
|
-
* cannot run at all: a quoted string in leading position is parsed in
|
|
526
|
-
* expression mode, so the invocation died with a parse error before the runner
|
|
527
|
-
* started — a task that failed instantly, every time, on that platform. Windows
|
|
528
|
-
* has no login-shell equivalent to emulate (its PATH comes from the registry
|
|
529
|
-
* and is already inherited), so {@link planShellLaunch} starts the runner
|
|
530
|
-
* directly there instead of routing it through a shell. Platform choice belongs
|
|
531
|
-
* to that function; this one only knows how to phrase the POSIX half.
|
|
532
|
-
*/
|
|
533
|
-
declare function buildShellInvocation(command: string, args: string[]): {
|
|
534
|
-
shellPath: string;
|
|
535
|
-
shellArgs: string[];
|
|
536
|
-
};
|
|
537
|
-
/** A terminal size in cells. */
|
|
538
|
-
interface PtySize {
|
|
539
|
-
cols: number;
|
|
540
|
-
rows: number;
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
|
-
* Options for {@link wrapWithPty}. `size` sets the PTY's window size before the
|
|
544
|
-
* wrapped command starts. `controlChannel` makes the wrapper listen on its fd 3
|
|
545
|
-
* for `"<cols> <rows>"` lines and resize the PTY live — the caller spawns the
|
|
546
|
-
* child with an extra pipe there and writes resize requests into it.
|
|
547
|
-
*/
|
|
548
|
-
interface PtyWrapOptions {
|
|
549
|
-
size?: PtySize;
|
|
550
|
-
controlChannel?: boolean;
|
|
551
|
-
/** Decides which `script` is on the host; defaults to this process's platform. */
|
|
552
|
-
platform?: NodeJS.Platform;
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Wrap a command in `script` to allocate the PTY some runners require when
|
|
556
|
-
* headless; `-e` propagates the child's exit code so verification still works.
|
|
557
|
-
*
|
|
558
|
-
* POSIX-only by nature — there is no `script` on Windows, which
|
|
559
|
-
* `HeadlessRunner`'s `hasScriptCmd` probe already discovers, so this is never
|
|
560
|
-
* reached there.
|
|
561
|
-
*
|
|
562
|
-
* `script` sizes its PTY off the terminal it is attached to; spawned off a pipe
|
|
563
|
-
* (every transport here) it allocates 0x0, which a runner TUI renders as
|
|
564
|
-
* garbage. `stty` fixes the size on the PTY slave before the command starts, so
|
|
565
|
-
* the TUI reads its true dimensions via ioctl.
|
|
566
|
-
*
|
|
567
|
-
* The control channel can only live on a *separate* fd from the agent's stdin,
|
|
568
|
-
* so the wrapper saves the PTY on fd 4 first: POSIX sends an asynchronous
|
|
569
|
-
* command's stdin to `/dev/null`, so the watcher's own stdin cannot be the PTY.
|
|
570
|
-
* A background job also inherits an fd 0 that is not the terminal; `stty` names
|
|
571
|
-
* fd 4 explicitly for that reason.
|
|
572
|
-
*/
|
|
573
|
-
declare function wrapWithPty(command: string, args: string[], opts?: PtyWrapOptions): {
|
|
574
|
-
command: string;
|
|
575
|
-
args: string[];
|
|
576
|
-
};
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* cmd.exe's command-line buffer. A longer line is truncated rather than
|
|
580
|
-
* rejected, which would corrupt a planner's system prompt or a task's prompt
|
|
581
|
-
* mid-sentence and produce a confident answer to half a question — so the
|
|
582
|
-
* batch route refuses instead. See {@link CommandLineTooLongError}.
|
|
583
|
-
*/
|
|
584
|
-
declare const CMD_EXE_MAX_COMMAND_LINE = 8191;
|
|
585
|
-
/**
|
|
586
|
-
* CreateProcess's own ceiling, which the native and PowerShell routes are
|
|
587
|
-
* bounded by instead. Windows truncates here too, so the same refusal applies —
|
|
588
|
-
* it is simply four times further away.
|
|
589
|
-
*/
|
|
590
|
-
declare const WINDOWS_MAX_COMMAND_LINE = 32767;
|
|
591
|
-
interface LaunchPlan {
|
|
592
|
-
/** The executable handed to `spawn()` or `vscode.window.createTerminal`. */
|
|
593
|
-
file: string;
|
|
594
|
-
/** Arguments for `file`. Pass verbatim when {@link verbatim} is set. */
|
|
595
|
-
args: string[];
|
|
596
|
-
/**
|
|
597
|
-
* Windows batch route only: `args` is already a quoted command line and must
|
|
598
|
-
* not be re-quoted. Maps to `windowsVerbatimArguments` for `spawn`, and to
|
|
599
|
-
* the string form of `shellArgs` for a VS Code terminal.
|
|
600
|
-
*/
|
|
601
|
-
verbatim?: boolean;
|
|
602
|
-
}
|
|
603
|
-
/** Test seam: every OS touchpoint is injectable, and production uses the defaults. */
|
|
604
|
-
interface LaunchDeps {
|
|
605
|
-
platform?: NodeJS.Platform;
|
|
606
|
-
/** The PATH executables are looked up on. Defaults to the augmented PATH. */
|
|
607
|
-
resolvePath?: () => Promise<string>;
|
|
608
|
-
/** True when `candidate` names an existing file. */
|
|
609
|
-
exists?: (candidate: string) => boolean;
|
|
610
|
-
/** Absolute path to the Windows command interpreter. */
|
|
611
|
-
comSpec?: () => string;
|
|
612
|
-
/** Absolute path to Windows PowerShell. */
|
|
613
|
-
powerShell?: () => string;
|
|
614
|
-
/** PATHEXT, as the environment reports it. */
|
|
615
|
-
pathExt?: () => string;
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* Thrown when a command's arguments do not fit the buffer of the only
|
|
619
|
-
* interpreter that can start it. Windows truncates rather than rejecting, and a
|
|
620
|
-
* system prompt cut off mid-sentence makes the planner answer half a question
|
|
621
|
-
* confidently — the silent success this repo refuses — so this is raised
|
|
622
|
-
* instead. `TaskOrchestrator.startTask` catches it and holds the task, so the
|
|
623
|
-
* message is what the user reads: it names the fix, because they cannot infer
|
|
624
|
-
* it from a truncated prompt.
|
|
625
|
-
*/
|
|
626
|
-
declare class CommandLineTooLongError extends Error {
|
|
627
|
-
readonly command: string;
|
|
628
|
-
readonly length: number;
|
|
629
|
-
readonly limit: number;
|
|
630
|
-
constructor(command: string, length: number, limit?: number);
|
|
631
|
-
}
|
|
632
|
-
/**
|
|
633
|
-
* Thrown when only a batch shim resolved for a multi-line argument. cmd.exe
|
|
634
|
-
* reads up to the first CR/LF and discards the rest with no error and exit code
|
|
635
|
-
* 0 — quoting does not help — so the agent would get the first paragraph of its
|
|
636
|
-
* prompt without the completion marker instruction, then exit looking successful.
|
|
637
|
-
*/
|
|
638
|
-
declare class EmbeddedNewlineError extends Error {
|
|
639
|
-
readonly command: string;
|
|
640
|
-
constructor(command: string);
|
|
641
|
-
}
|
|
642
|
-
/**
|
|
643
|
-
* Thrown when `command` cannot be resolved to a real file. Kept distinguishable
|
|
644
|
-
* from `WorkspaceNotFoundError` (utils/workspace) even though both a missing
|
|
645
|
-
* cwd and a missing binary surface as the same `spawn` ENOENT to Node — the two
|
|
646
|
-
* are checked, and named, separately so the failure names the actual cause.
|
|
647
|
-
*/
|
|
648
|
-
declare class ExecutableNotFoundError extends Error {
|
|
649
|
-
readonly command: string;
|
|
650
|
-
readonly searchedPath: string;
|
|
651
|
-
constructor(command: string, searchedPath: string);
|
|
652
|
-
}
|
|
653
|
-
/**
|
|
654
|
-
* Whether `command` actually resolves to a file, given the plan
|
|
655
|
-
* {@link planDirectLaunch} produced for it and the PATH it was resolved
|
|
656
|
-
* against.
|
|
657
|
-
*
|
|
658
|
-
* POSIX is deliberately identity in `planDirectLaunch` (execvp does its own
|
|
659
|
-
* PATH search), so the search is repeated here instead. Windows already did
|
|
660
|
-
* the search inside `planDirectLaunch` — signalled by the returned file
|
|
661
|
-
* differing from the bare command name it was given; an unresolved command
|
|
662
|
-
* comes back unchanged.
|
|
663
|
-
*/
|
|
664
|
-
declare function isExecutableResolved(command: string, plan: LaunchPlan, PATH: string, deps?: LaunchDeps): boolean;
|
|
665
|
-
/** The verbatim command line cmd.exe receives after `/d /s /c`, before wrapping. */
|
|
666
|
-
declare function windowsCommandLine(file: string, args: string[]): string;
|
|
667
|
-
/**
|
|
668
|
-
* How to start `command` with `args` through `spawn()`, with no shell.
|
|
669
|
-
*
|
|
670
|
-
* POSIX returns its input unchanged — execvp already searches PATH, and adding
|
|
671
|
-
* a resolution step there would be a new way for a working setup to break.
|
|
672
|
-
*
|
|
673
|
-
* Windows resolves the command against PATH × PATHEXT, preferring a native
|
|
674
|
-
* executable (spawned directly) over a batch shim (through cmd.exe) over a
|
|
675
|
-
* PowerShell script shim (through `powershell.exe -File`). A command that
|
|
676
|
-
* resolves to nothing is returned unchanged, so the caller's existing ENOENT —
|
|
677
|
-
* which names what the user typed — is what surfaces rather than a second,
|
|
678
|
-
* vaguer error from here.
|
|
679
|
-
*
|
|
680
|
-
* The tiers are tried in preference order and the first that *fits* wins, with
|
|
681
|
-
* one deliberate exception: an overflowing batch shim does not fall through to
|
|
682
|
-
* PowerShell. Overflow means a very large prompt, which is precisely where
|
|
683
|
-
* `-File` argument fidelity is least worth betting on, and where a clear held
|
|
684
|
-
* task beats a plausibly-mangled one. So capacity does not reorder the tiers —
|
|
685
|
-
* a `.ps1` beside a too-long `.cmd` still raises.
|
|
686
|
-
*
|
|
687
|
-
* A line break does reorder them: cmd.exe cannot carry one at any length, so a
|
|
688
|
-
* `.ps1` beside a `.cmd` wins, and a lone `.cmd` raises.
|
|
689
|
-
*
|
|
690
|
-
* @throws {CommandLineTooLongError} when the selected route's buffer cannot
|
|
691
|
-
* carry the arguments.
|
|
692
|
-
* @throws {EmbeddedNewlineError} when the arguments span lines and only the
|
|
693
|
-
* batch route resolved.
|
|
694
|
-
*/
|
|
695
|
-
declare function planDirectLaunch(command: string, args: string[], deps?: LaunchDeps): Promise<LaunchPlan>;
|
|
696
|
-
/**
|
|
697
|
-
* How to start `command` for a surface that hands an executable and arguments
|
|
698
|
-
* to a terminal — the VS Code runner today, a Windows TUI later.
|
|
699
|
-
*
|
|
700
|
-
* On POSIX this is the login shell, unchanged: `bash -lc` runs the user's
|
|
701
|
-
* profile, which is how nvm/volta/asdf-managed runner binaries resolve at all.
|
|
702
|
-
* Windows has no login-shell equivalent (its PATH comes from the registry and
|
|
703
|
-
* is already inherited), so it takes the direct route instead. That is not just
|
|
704
|
-
* a simplification: it means the runner's own exit code is the terminal's exit
|
|
705
|
-
* code, rather than a `$LASTEXITCODE` that PowerShell propagates unreliably —
|
|
706
|
-
* and the exit code is half of what {@link VerdictEngine} judges a task on.
|
|
707
|
-
*/
|
|
708
|
-
declare function planShellLaunch(command: string, args: string[], deps?: LaunchDeps): Promise<LaunchPlan>;
|
|
709
|
-
|
|
710
|
-
type SpawnFn = (command: string, args: string[], options: {
|
|
711
|
-
env: NodeJS.ProcessEnv;
|
|
712
|
-
stdio: Array<'pipe' | 'ignore'>;
|
|
713
|
-
cwd: string;
|
|
714
|
-
/** Set by the Windows batch route, where `args` is already a quoted command line. */
|
|
715
|
-
windowsVerbatimArguments?: boolean;
|
|
716
|
-
/** Set on POSIX so the runner leads its own process group (see `spawnInOwnGroup`). */
|
|
717
|
-
detached?: boolean;
|
|
718
|
-
}) => ChildProcess;
|
|
719
|
-
/** Test seam: every OS touchpoint is injectable; production uses the defaults. */
|
|
720
|
-
interface HeadlessRunnerDeps {
|
|
721
|
-
spawnImpl?: SpawnFn;
|
|
722
|
-
hasScriptCmd?: () => boolean;
|
|
723
|
-
resolvePath?: () => Promise<string>;
|
|
724
|
-
/**
|
|
725
|
-
* Overrides for executable resolution ({@link planDirectLaunch}). Only the
|
|
726
|
-
* Windows branch consults them, so a POSIX test never needs to pass anything.
|
|
727
|
-
*/
|
|
728
|
-
launchDeps?: LaunchDeps;
|
|
729
|
-
/** Reads the runner's version, for a manifest whose command line differs by major version. */
|
|
730
|
-
runnerVersion?: RunnerVersionProbe;
|
|
731
|
-
}
|
|
732
|
-
declare class HeadlessSession extends AbstractTerminalSession {
|
|
733
|
-
private spawnImpl;
|
|
734
|
-
readonly interactive: boolean;
|
|
735
|
-
private platform;
|
|
736
|
-
private process;
|
|
737
|
-
private outputBuffer;
|
|
738
|
-
private controlStream;
|
|
739
|
-
constructor(id: string, taskId: string, spawnImpl: SpawnFn, interactive?: boolean, platform?: NodeJS.Platform);
|
|
740
|
-
get isStarted(): boolean;
|
|
741
|
-
start(launch: LaunchPlan, cwd: string, resolvedPath: string, env?: Record<string, string>, options?: {
|
|
742
|
-
controlChannel?: boolean;
|
|
743
|
-
}): void;
|
|
744
|
-
kill(): void;
|
|
745
|
-
getOutput(): string;
|
|
746
|
-
write(text: string): void;
|
|
747
|
-
/** Closes the runner's stdin: what it has read so far is all it will get. */
|
|
748
|
-
endInput(): void;
|
|
749
|
-
/** PTY resize requests from a surface that owns the terminal rendering the wrapper. */
|
|
750
|
-
writeControl(text: string): void;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/** Everything needed to start one runner process, resolved before any child exists. */
|
|
754
|
-
interface PreparedLaunch {
|
|
755
|
-
launch: LaunchPlan;
|
|
756
|
-
resolvedPath: string;
|
|
757
|
-
env: Record<string, string>;
|
|
758
|
-
/** True when the invocation was wrapped in `script` to allocate a PTY. */
|
|
759
|
-
pty: boolean;
|
|
760
|
-
/** True when the started process needs an explicit Enter sent to submit its pre-filled prompt. */
|
|
761
|
-
submitPromptKey: boolean;
|
|
762
|
-
/**
|
|
763
|
-
* True when nothing can be typed into the run: no TTY, no interactive shape,
|
|
764
|
-
* and the prompt already in its arguments. Its stdin is then closed, because
|
|
765
|
-
* a CLI such as `codex exec` reads a piped stdin to its end before starting.
|
|
766
|
-
*/
|
|
767
|
-
closeStdin: boolean;
|
|
768
|
-
}
|
|
769
|
-
declare class HeadlessRunner extends AbstractRunner<HeadlessSession> {
|
|
770
|
-
private spawnImpl;
|
|
771
|
-
private hasScriptCmd;
|
|
772
|
-
private resolvePath;
|
|
773
|
-
private launchDeps;
|
|
774
|
-
private runnerVersion;
|
|
775
|
-
private spawnCount;
|
|
776
|
-
/**
|
|
777
|
-
* Session shape: a piped subprocess is not a terminal, so runners get their
|
|
778
|
-
* non-interactive subcommand. The VS Code runner owns a pseudoterminal and
|
|
779
|
-
* overrides this to true. Autonomy is a separate axis (see `ResolveContext`)
|
|
780
|
-
* and stays on either way — no surface has a human answering permission
|
|
781
|
-
* prompts on the orchestrator's behalf.
|
|
782
|
-
*/
|
|
783
|
-
protected readonly defaultInteractive: boolean;
|
|
784
|
-
constructor(deps?: HeadlessRunnerDeps);
|
|
785
|
-
/**
|
|
786
|
-
* Unique per spawn, not per task: a retry reuses its task id and ids often
|
|
787
|
-
* share a prefix, and a shared registry key let the old attempt's exit
|
|
788
|
-
* unregister the new one. Unlike TmuxRunner, nothing outside this process
|
|
789
|
-
* keys on the id, so a counter is enough to scope it to the attempt.
|
|
790
|
-
*/
|
|
791
|
-
protected nextSessionId(taskId: string): string;
|
|
792
|
-
protected createSession(id: string, taskId: string): HeadlessSession;
|
|
793
|
-
/** Everything up to, but not including, spawning — so a surface that owns its own child reaches the same decisions. */
|
|
794
|
-
protected prepareLaunch(opts: RunnerSpawnOptions, ptyOptions?: PtyWrapOptions): Promise<PreparedLaunch>;
|
|
795
|
-
spawn(opts: RunnerSpawnOptions): Promise<ITerminalSession>;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
/**
|
|
799
|
-
* What one model call consumed, as its provider or runner reported it. Shared
|
|
800
|
-
* by the planner's usage line (#49) and per-attempt task usage (#26): a record
|
|
801
|
-
* says nothing about who asked for the call, so either can hold a list of them.
|
|
802
|
-
*
|
|
803
|
-
* Every measure is optional because backends report different subsets. An
|
|
804
|
-
* absent field means "not reported" — never zero — so a total can tell the two
|
|
805
|
-
* apart.
|
|
806
|
-
*/
|
|
807
|
-
interface UsageRecord {
|
|
808
|
-
/** The provider or runner id that reported the call — `openai`, `claude-code`, … */
|
|
809
|
-
source: string;
|
|
810
|
-
model?: string;
|
|
811
|
-
inputTokens?: number;
|
|
812
|
-
outputTokens?: number;
|
|
813
|
-
/** The share of `inputTokens` served from the provider's prompt cache. */
|
|
814
|
-
cachedInputTokens?: number;
|
|
815
|
-
/**
|
|
816
|
-
* Filled only from a provider's or runner's own report, never from a price
|
|
817
|
-
* table or an estimate: prices go stale, and a subscription runner has no
|
|
818
|
-
* per-token price at all. No report means no cost, not a guessed one.
|
|
819
|
-
*/
|
|
820
|
-
reportedCost?: {
|
|
821
|
-
amount: number;
|
|
822
|
-
currency: string;
|
|
823
|
-
};
|
|
824
|
-
/** The model's context window, when the runner itself reports it. */
|
|
825
|
-
contextWindow?: number;
|
|
826
|
-
/** Set when a subagent made the call; its usage still counts toward the total. */
|
|
827
|
-
subagentId?: string;
|
|
828
|
-
}
|
|
829
|
-
/**
|
|
830
|
-
* A running sum of {@link UsageRecord}s. A measure stays absent until some
|
|
831
|
-
* record reports it. Cost is kept per currency: two runners may bill in
|
|
832
|
-
* different ones, and there is no honest exchange rate to fold them together.
|
|
833
|
-
*/
|
|
834
|
-
interface UsageTotals {
|
|
835
|
-
inputTokens?: number;
|
|
836
|
-
outputTokens?: number;
|
|
837
|
-
cachedInputTokens?: number;
|
|
838
|
-
reportedCost?: Record<string, number>;
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* The input side of a call whose provider reports its prompt in parts — the
|
|
842
|
-
* uncached tail, with cache reads and cache writes beside it rather than inside
|
|
843
|
-
* it (Anthropic, and OpenCode after it). The prompt the model saw is all three;
|
|
844
|
-
* only the reads were served from cache, since a write is billed as fresh
|
|
845
|
-
* input. A part left unreported adds nothing, and with no part reported there
|
|
846
|
-
* is no measure at all.
|
|
847
|
-
*/
|
|
848
|
-
declare function partedPromptUsage(parts: {
|
|
849
|
-
uncached?: number;
|
|
850
|
-
cacheRead?: number;
|
|
851
|
-
cacheWrite?: number;
|
|
852
|
-
}): Pick<UsageRecord, 'inputTokens' | 'cachedInputTokens'>;
|
|
853
|
-
declare function addUsage(totals: UsageTotals, record: UsageRecord): UsageTotals;
|
|
854
|
-
/**
|
|
855
|
-
* What the planner has consumed over a session (#49). `totals` includes every
|
|
856
|
-
* `bySubagent` entry. `lastPromptTokens` and `contextWindow` track the
|
|
857
|
-
* planner's own calls only — a subagent runs its own model, whose window says
|
|
858
|
-
* nothing about the planner's.
|
|
859
|
-
*/
|
|
860
|
-
interface PlannerUsage {
|
|
861
|
-
totals: UsageTotals;
|
|
862
|
-
bySubagent?: Record<string, UsageTotals>;
|
|
863
|
-
lastPromptTokens?: number;
|
|
864
|
-
contextWindow?: number;
|
|
865
|
-
}
|
|
866
|
-
declare function addPlannerUsage(usage: PlannerUsage, record: UsageRecord): PlannerUsage;
|
|
867
|
-
/** Whether any measure was reported: a token line of nothing but blanks says nothing. */
|
|
868
|
-
declare function isMeasured(totals: UsageTotals): boolean;
|
|
869
|
-
/** What the token line shows of a ledger — live from its broadcast, or reloaded from the saved one. */
|
|
870
|
-
interface UsageLine {
|
|
871
|
-
totals: UsageTotals;
|
|
872
|
-
bySubagent?: Record<string, UsageTotals>;
|
|
873
|
-
contextFill?: {
|
|
874
|
-
usedTokens: number;
|
|
875
|
-
windowTokens: number;
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
declare function usageLine(usage: PlannerUsage): UsageLine;
|
|
879
|
-
/**
|
|
880
|
-
* The last planner prompt against its window, or undefined while either is
|
|
881
|
-
* unknown. `usedTokens` is the prompt total as reported, cached tokens
|
|
882
|
-
* included: a cached token still occupies the window, so subtracting the
|
|
883
|
-
* cached share would understate how full the context is. A window of 0 is
|
|
884
|
-
* treated as unknown — never guessed.
|
|
885
|
-
*/
|
|
886
|
-
declare function plannerContextFill(usage: PlannerUsage): {
|
|
887
|
-
usedTokens: number;
|
|
888
|
-
windowTokens: number;
|
|
889
|
-
} | undefined;
|
|
890
|
-
|
|
891
|
-
/**
|
|
892
|
-
* The planner's single approval seam. Every capability that reaches beyond the
|
|
893
|
-
* default read-only, in-workspace envelope — a path outside the workspace root,
|
|
894
|
-
* a shell command outside the auto-allowed set, a URL fetch — routes through
|
|
895
|
-
* one `request()` so the policy has exactly one owner (the same "one repair
|
|
896
|
-
* owner" shape as PlanRepair).
|
|
897
|
-
*
|
|
898
|
-
* Surfaces supply the human channel. VS Code answers with a modal; the web
|
|
899
|
-
* server currently has no prompt UI, so it denies unless the scope was
|
|
900
|
-
* pre-approved through config. Denial is always a visible, actionable tool
|
|
901
|
-
* result — never a silent success.
|
|
902
|
-
*/
|
|
903
|
-
/**
|
|
904
|
-
* `runner_tool` is a task runner's own tool request (ADR-0018, A1): it waits
|
|
905
|
-
* for an answer as long as it takes, and never passes through the planner's
|
|
906
|
-
* policy — the runner's mode already decided it needed asking.
|
|
907
|
-
*/
|
|
908
|
-
type ApprovalKind = 'external_path' | 'shell_command' | 'url_fetch' | 'runner_tool';
|
|
909
|
-
interface ApprovalRequest {
|
|
910
|
-
kind: ApprovalKind;
|
|
911
|
-
/** The concrete thing being asked about: an absolute path, a command line, a URL. */
|
|
912
|
-
subject: string;
|
|
913
|
-
/**
|
|
914
|
-
* What a grant covers. Approving remembers this, not `subject`, so reading a
|
|
915
|
-
* second file from an already-approved directory does not prompt again.
|
|
916
|
-
*/
|
|
917
|
-
scope: string;
|
|
918
|
-
/**
|
|
919
|
-
* Every scope one answer settles, when a command needs more than one — its
|
|
920
|
-
* own and each outside directory it touches — so it prompts once rather than
|
|
921
|
-
* once per scope. `scope` is the first. Absent means `scope` alone.
|
|
922
|
-
*/
|
|
923
|
-
scopes?: string[];
|
|
924
|
-
/** One-line context for the prompt. */
|
|
925
|
-
detail?: string;
|
|
926
|
-
/** The task whose runner asked; absent for the planner's own requests. */
|
|
927
|
-
taskId?: string;
|
|
928
|
-
/** "Allow for this task" can be offered: the runner proposed its own session-scoped grant. */
|
|
929
|
-
allowForTask?: boolean;
|
|
930
|
-
}
|
|
931
|
-
/**
|
|
932
|
-
* An answer, from whoever gives it — a person on any surface, or later the
|
|
933
|
-
* supervisor (#28). `allowForTask` is Allow plus the runner's own grant for
|
|
934
|
-
* the rest of the task; `note` goes back to the agent with a denial.
|
|
935
|
-
*/
|
|
936
|
-
type ApprovalDecision = {
|
|
937
|
-
decision: 'allow';
|
|
938
|
-
} | {
|
|
939
|
-
decision: 'allowForTask';
|
|
940
|
-
} | {
|
|
941
|
-
decision: 'deny';
|
|
942
|
-
note?: string;
|
|
943
|
-
};
|
|
944
|
-
/** What an answer may be given as: a planner prompt's yes/no still is one. */
|
|
945
|
-
type ApprovalAnswer = boolean | ApprovalDecision;
|
|
946
|
-
declare function toApprovalDecision(answer: ApprovalAnswer): ApprovalDecision;
|
|
947
|
-
declare function isGranted(decision: ApprovalDecision): boolean;
|
|
948
|
-
declare function approvalScopes(request: ApprovalRequest): string[];
|
|
949
|
-
declare function isRunnerApproval(request: ApprovalRequest): boolean;
|
|
950
|
-
interface IApproval {
|
|
951
|
-
request(req: ApprovalRequest): Promise<boolean>;
|
|
952
|
-
}
|
|
953
|
-
/** Denies everything. The safe default when a surface wires no approval channel. */
|
|
954
|
-
declare const DENY_ALL: IApproval;
|
|
955
|
-
|
|
956
|
-
/**
|
|
957
|
-
* The tools of the Ordewell MCP server, per token role (ADR-0022), and the
|
|
958
|
-
* handler interfaces the session side implements to answer them.
|
|
959
|
-
*
|
|
960
|
-
* Every handler method is optional so each tool can be wired on its own; one
|
|
961
|
-
* left unset answers its call with a "not available" error rather than
|
|
962
|
-
* disappearing from `tools/list`, so the list a runner sees never depends on
|
|
963
|
-
* how far the wiring has got.
|
|
964
|
-
*/
|
|
965
|
-
declare const taskCompleteInput: z.ZodObject<{
|
|
966
|
-
status: z.ZodEnum<{
|
|
967
|
-
failed: "failed";
|
|
968
|
-
blocked: "blocked";
|
|
969
|
-
done: "done";
|
|
970
|
-
}>;
|
|
971
|
-
summary: z.ZodString;
|
|
972
|
-
reason: z.ZodOptional<z.ZodString>;
|
|
973
|
-
}, z.core.$strip>;
|
|
974
|
-
declare const checkpointInput: z.ZodObject<{
|
|
975
|
-
question: z.ZodString;
|
|
976
|
-
}, z.core.$strip>;
|
|
977
|
-
declare const listRunnersInput: z.ZodObject<{}, z.core.$strip>;
|
|
978
|
-
declare const listModelsInput: z.ZodObject<{
|
|
979
|
-
runner: z.ZodString;
|
|
980
|
-
}, z.core.$strip>;
|
|
981
|
-
declare const submitPlanInput: z.ZodObject<{
|
|
982
|
-
tasks: z.ZodArray<z.ZodObject<{
|
|
983
|
-
id: z.ZodOptional<z.ZodString>;
|
|
984
|
-
order: z.ZodOptional<z.ZodNumber>;
|
|
985
|
-
title: z.ZodString;
|
|
986
|
-
description: z.ZodOptional<z.ZodString>;
|
|
987
|
-
type: z.ZodOptional<z.ZodEnum<{
|
|
988
|
-
user: "user";
|
|
989
|
-
ai: "ai";
|
|
990
|
-
}>>;
|
|
991
|
-
dependencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
992
|
-
prompt: z.ZodOptional<z.ZodString>;
|
|
993
|
-
userSteps: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
994
|
-
order: z.ZodOptional<z.ZodNumber>;
|
|
995
|
-
instruction: z.ZodString;
|
|
996
|
-
}, z.core.$loose>>>;
|
|
997
|
-
assignedRunner: z.ZodOptional<z.ZodString>;
|
|
998
|
-
assignedModel: z.ZodOptional<z.ZodObject<{
|
|
999
|
-
modelId: z.ZodString;
|
|
1000
|
-
modelLabel: z.ZodOptional<z.ZodString>;
|
|
1001
|
-
thinkingEffort: z.ZodOptional<z.ZodString>;
|
|
1002
|
-
}, z.core.$loose>>;
|
|
1003
|
-
taskMode: z.ZodOptional<z.ZodString>;
|
|
1004
|
-
autonomy: z.ZodOptional<z.ZodEnum<{
|
|
1005
|
-
AFK: "AFK";
|
|
1006
|
-
HITL: "HITL";
|
|
1007
|
-
}>>;
|
|
1008
|
-
sliceType: z.ZodOptional<z.ZodEnum<{
|
|
1009
|
-
AFK: "AFK";
|
|
1010
|
-
HITL: "HITL";
|
|
1011
|
-
}>>;
|
|
1012
|
-
ops: z.ZodOptional<z.ZodBoolean>;
|
|
1013
|
-
userStoriesCovered: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1014
|
-
subtasks: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
1015
|
-
}, z.core.$loose>>;
|
|
1016
|
-
}, z.core.$strip>;
|
|
1017
|
-
declare const editPlanInput: z.ZodObject<{
|
|
1018
|
-
ops: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1019
|
-
op: z.ZodLiteral<"update">;
|
|
1020
|
-
taskId: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
1021
|
-
changes: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
1022
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
1023
|
-
op: z.ZodLiteral<"add">;
|
|
1024
|
-
task: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
1025
|
-
handle: z.ZodOptional<z.ZodString>;
|
|
1026
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
1027
|
-
op: z.ZodLiteral<"remove">;
|
|
1028
|
-
taskId: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
1029
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
1030
|
-
op: z.ZodLiteral<"reorder">;
|
|
1031
|
-
taskIds: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
1032
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
1033
|
-
op: z.ZodLiteral<"merge">;
|
|
1034
|
-
taskIds: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
1035
|
-
merged: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
1036
|
-
handle: z.ZodOptional<z.ZodString>;
|
|
1037
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
1038
|
-
op: z.ZodLiteral<"split">;
|
|
1039
|
-
taskId: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
1040
|
-
parts: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
1041
|
-
handle: z.ZodOptional<z.ZodString>;
|
|
1042
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
1043
|
-
op: z.ZodLiteral<"rearm">;
|
|
1044
|
-
taskId: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
1045
|
-
changes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
1046
|
-
}, z.core.$strip>]>>;
|
|
1047
|
-
}, z.core.$strip>;
|
|
1048
|
-
declare const taskQueryInput: z.ZodObject<{
|
|
1049
|
-
tasks: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
|
|
1050
|
-
fields: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
1051
|
-
description: "description";
|
|
1052
|
-
prompt: "prompt";
|
|
1053
|
-
userSteps: "userSteps";
|
|
1054
|
-
verdict: "verdict";
|
|
1055
|
-
outputSummary: "outputSummary";
|
|
1056
|
-
userStoriesCovered: "userStoriesCovered";
|
|
1057
|
-
}>>>;
|
|
1058
|
-
catalog: z.ZodOptional<z.ZodBoolean>;
|
|
1059
|
-
}, z.core.$strip>;
|
|
1060
|
-
declare const taskOutputInput: z.ZodObject<{
|
|
1061
|
-
task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
1062
|
-
lines: z.ZodOptional<z.ZodNumber>;
|
|
1063
|
-
since: z.ZodOptional<z.ZodNumber>;
|
|
1064
|
-
}, z.core.$strip>;
|
|
1065
|
-
type TaskCompleteArgs = z.infer<typeof taskCompleteInput>;
|
|
1066
|
-
type CheckpointArgs = z.infer<typeof checkpointInput>;
|
|
1067
|
-
type ListRunnersArgs = z.infer<typeof listRunnersInput>;
|
|
1068
|
-
type ListModelsArgs = z.infer<typeof listModelsInput>;
|
|
1069
|
-
type SubmitPlanArgs = z.infer<typeof submitPlanInput>;
|
|
1070
|
-
type EditPlanArgs = z.infer<typeof editPlanInput>;
|
|
1071
|
-
type TaskQueryArgs = z.infer<typeof taskQueryInput>;
|
|
1072
|
-
type TaskOutputArgs = z.infer<typeof taskOutputInput>;
|
|
1073
|
-
/** What a handler answers with. `isError` marks a refusal the caller should act on. */
|
|
1074
|
-
interface McpToolReply {
|
|
1075
|
-
text: string;
|
|
1076
|
-
isError?: boolean;
|
|
1077
|
-
}
|
|
1078
|
-
/**
|
|
1079
|
-
* How a checkpoint call ended. `withdrawn` is the attempt going away under it
|
|
1080
|
-
* (verdict, cancel, retry, stop) or the call being refused, not a verdict on
|
|
1081
|
-
* the question.
|
|
1082
|
-
*/
|
|
1083
|
-
type CheckpointAnswer = {
|
|
1084
|
-
kind: 'continue';
|
|
1085
|
-
} | {
|
|
1086
|
-
kind: 'rejected';
|
|
1087
|
-
reason: string;
|
|
1088
|
-
} | {
|
|
1089
|
-
kind: 'withdrawn';
|
|
1090
|
-
why: string;
|
|
1091
|
-
};
|
|
1092
|
-
interface McpToolContext {
|
|
1093
|
-
/** Aborts when the token is revoked or the caller cancels — a waiting handler must stop. */
|
|
1094
|
-
signal: AbortSignal;
|
|
1095
|
-
}
|
|
1096
|
-
type Run<A> = (args: A, context: McpToolContext) => Promise<McpToolReply>;
|
|
1097
|
-
interface TaskToolHandler {
|
|
1098
|
-
taskComplete?: Run<TaskCompleteArgs>;
|
|
1099
|
-
checkpoint?: Run<CheckpointArgs>;
|
|
1100
|
-
}
|
|
1101
|
-
interface PlannerToolHandler {
|
|
1102
|
-
listRunners?: Run<ListRunnersArgs>;
|
|
1103
|
-
listModels?: Run<ListModelsArgs>;
|
|
1104
|
-
submitPlan?: Run<SubmitPlanArgs>;
|
|
1105
|
-
editPlan?: Run<EditPlanArgs>;
|
|
1106
|
-
taskQuery?: Run<TaskQueryArgs>;
|
|
1107
|
-
taskOutput?: Run<TaskOutputArgs>;
|
|
1108
|
-
}
|
|
1109
|
-
interface McpTool<H> {
|
|
1110
|
-
name: string;
|
|
1111
|
-
description: string;
|
|
1112
|
-
inputSchema: {
|
|
1113
|
-
type: 'object';
|
|
1114
|
-
[key: string]: unknown;
|
|
1115
|
-
};
|
|
1116
|
-
annotations?: {
|
|
1117
|
-
readOnlyHint: boolean;
|
|
1118
|
-
};
|
|
1119
|
-
/** Validate `args` and hand them to the handler's method for this tool. */
|
|
1120
|
-
call(handler: H, args: unknown, context: McpToolContext): Promise<McpToolReply>;
|
|
1121
|
-
}
|
|
1122
|
-
/** The task tool whose call settles the attempt (ADR-0022, V1). */
|
|
1123
|
-
declare const TASK_COMPLETE_TOOL = "task_complete";
|
|
1124
|
-
declare const TASK_TOOLS: readonly McpTool<TaskToolHandler>[];
|
|
1125
|
-
declare const PLANNER_TOOLS: readonly McpTool<PlannerToolHandler>[];
|
|
1126
|
-
|
|
1127
|
-
/**
|
|
1128
|
-
* The Ordewell MCP server (ADR-0022): one loopback listener per process,
|
|
1129
|
-
* shared by every session in it, where the caller's token alone decides which
|
|
1130
|
-
* tools it sees and which session and task its calls reach.
|
|
1131
|
-
*/
|
|
1132
|
-
declare const ORDEWELL_MCP_PATH = "/mcp";
|
|
1133
|
-
interface OrdewellMcpServerOptions {
|
|
1134
|
-
/** How often a call that is still waiting tells its caller so. */
|
|
1135
|
-
heartbeatMs?: number;
|
|
1136
|
-
}
|
|
1137
|
-
interface TaskTokenScope {
|
|
1138
|
-
sessionId: string;
|
|
1139
|
-
taskId: string;
|
|
1140
|
-
/** The attempt generation the token is good for; a later attempt gets a new token. */
|
|
1141
|
-
attempt: number;
|
|
1142
|
-
}
|
|
1143
|
-
interface PlannerTokenScope {
|
|
1144
|
-
sessionId: string;
|
|
1145
|
-
}
|
|
1146
|
-
/** Where a runner reaches the server and what it presents there. Never put either half in a prompt. */
|
|
1147
|
-
interface McpCredential {
|
|
1148
|
-
url: string;
|
|
1149
|
-
token: string;
|
|
1150
|
-
}
|
|
1151
|
-
declare class OrdewellMcpServer {
|
|
1152
|
-
private readonly grants;
|
|
1153
|
-
private http;
|
|
1154
|
-
private listening;
|
|
1155
|
-
private boundUrl;
|
|
1156
|
-
private boundHost;
|
|
1157
|
-
private readonly heartbeatMs;
|
|
1158
|
-
constructor(options?: OrdewellMcpServerOptions);
|
|
1159
|
-
/** The listener's URL, once the first token has started it. */
|
|
1160
|
-
get url(): string | undefined;
|
|
1161
|
-
issueTaskToken(scope: TaskTokenScope, handler?: TaskToolHandler): Promise<McpCredential>;
|
|
1162
|
-
issuePlannerToken(scope: PlannerTokenScope, handler?: PlannerToolHandler): Promise<McpCredential>;
|
|
1163
|
-
/** Refuse every later request carrying `token`, and abort the calls it has in flight. */
|
|
1164
|
-
revoke(token: string): void;
|
|
1165
|
-
dispose(): Promise<void>;
|
|
1166
|
-
private issue;
|
|
1167
|
-
private start;
|
|
1168
|
-
private handle;
|
|
1169
|
-
}
|
|
1170
|
-
/** The process's one server, which the daemon and the extension host hand to every session they build. */
|
|
1171
|
-
declare function sharedMcpServer(): OrdewellMcpServer;
|
|
1172
|
-
|
|
1173
|
-
/** The server's name in every runner's MCP configuration, and so in its tool names (`mcp__ordewell__*`). */
|
|
1174
|
-
declare const ORDEWELL_MCP_SERVER_NAME = "ordewell";
|
|
1175
|
-
interface McpClientConfig {
|
|
1176
|
-
name: typeof ORDEWELL_MCP_SERVER_NAME;
|
|
1177
|
-
url: string;
|
|
1178
|
-
headers: Record<string, string>;
|
|
1179
|
-
}
|
|
1180
|
-
/**
|
|
1181
|
-
* The one shape every runner adapter renders its own MCP configuration from.
|
|
1182
|
-
* The token travels only in `headers`: the adapter writes it to an owner-only
|
|
1183
|
-
* file or an environment variable, never a prompt or a process argument
|
|
1184
|
-
* (ADR-0022, A4/A5).
|
|
1185
|
-
*/
|
|
1186
|
-
declare function mcpClientConfig(credential: McpCredential): McpClientConfig;
|
|
1187
|
-
/** A runner's MCP configuration on disk, and the way to take it back off. */
|
|
1188
|
-
interface OwnerOnlyFile {
|
|
1189
|
-
path: string;
|
|
1190
|
-
/** Delete the file. Idempotent. */
|
|
1191
|
-
remove(): void;
|
|
1192
|
-
}
|
|
1193
|
-
/**
|
|
1194
|
-
* Write a configuration that carries a token where only the user can read it,
|
|
1195
|
-
* for a runner that takes its MCP servers from a file (ADR-0022, A5). The
|
|
1196
|
-
* directory is private too, so the name cannot be raced before the mode lands.
|
|
1197
|
-
*/
|
|
1198
|
-
declare function ownerOnlyConfigFile(fileName: string, contents: string): OwnerOnlyFile;
|
|
1199
|
-
|
|
1200
|
-
/**
|
|
1201
|
-
* The harness-planner transport contract (ADR-0009).
|
|
1202
|
-
*
|
|
1203
|
-
* One adapter per coding agent, each speaking that agent's own programmatic
|
|
1204
|
-
* protocol and normalizing it to the event union below. Everything above this
|
|
1205
|
-
* line — reply classification, the repair loop, plan validation, the four
|
|
1206
|
-
* surfaces — is already provider-agnostic, so an adapter is the entire cost of
|
|
1207
|
-
* teaching Ordewell to plan with another agent.
|
|
1208
|
-
*/
|
|
1209
|
-
/**
|
|
1210
|
-
* One normalized event from a running agent turn. Deliberately smaller than
|
|
1211
|
-
* any single agent's native protocol: this is the intersection Ordewell can act
|
|
1212
|
-
* on, not a lossless re-encoding. Event fidelity differs by agent — `thinking`
|
|
1213
|
-
* is rich on Claude Code and absent elsewhere — so consumers must tolerate a
|
|
1214
|
-
* turn that emits nothing but `assistant_text` and `turn_end`.
|
|
1215
|
-
*/
|
|
1216
|
-
type AgentEvent =
|
|
1217
|
-
/**
|
|
1218
|
-
* A complete run of the assistant's reply. Concatenated in order to form the
|
|
1219
|
-
* turn's text. When the same run already streamed as `assistant_text_delta`,
|
|
1220
|
-
* this is the authoritative copy of it — it replaces the deltas, it is not
|
|
1221
|
-
* appended after them.
|
|
1222
|
-
*/
|
|
1223
|
-
{
|
|
1224
|
-
type: 'assistant_text';
|
|
1225
|
-
text: string;
|
|
1226
|
-
}
|
|
1227
|
-
/**
|
|
1228
|
-
* An incremental piece of the assistant's reply, for agents that stream
|
|
1229
|
-
* partial messages. The planner's own text only: a subagent's words never
|
|
1230
|
-
* arrive here, so they can never become the reply.
|
|
1231
|
-
*/
|
|
1232
|
-
| {
|
|
1233
|
-
type: 'assistant_text_delta';
|
|
1234
|
-
text: string;
|
|
1235
|
-
}
|
|
1236
|
-
/**
|
|
1237
|
-
* Reasoning the agent chose to expose. Never contributes to the reply text.
|
|
1238
|
-
* Like `assistant_text`, it supersedes deltas already streamed for it.
|
|
1239
|
-
*/
|
|
1240
|
-
| {
|
|
1241
|
-
type: 'thinking';
|
|
1242
|
-
text: string;
|
|
1243
|
-
subagentId?: string;
|
|
1244
|
-
} | {
|
|
1245
|
-
type: 'thinking_delta';
|
|
1246
|
-
text: string;
|
|
1247
|
-
subagentId?: string;
|
|
1248
|
-
}
|
|
1249
|
-
/** `subagentId` marks a call made inside a subagent rather than by the planner itself. */
|
|
1250
|
-
| {
|
|
1251
|
-
type: 'tool_call';
|
|
1252
|
-
id: string;
|
|
1253
|
-
name: string;
|
|
1254
|
-
args: Record<string, unknown>;
|
|
1255
|
-
subagentId?: string;
|
|
1256
|
-
} | {
|
|
1257
|
-
type: 'tool_result';
|
|
1258
|
-
id: string;
|
|
1259
|
-
name: string;
|
|
1260
|
-
output: string;
|
|
1261
|
-
success: boolean;
|
|
1262
|
-
subagentId?: string;
|
|
1263
|
-
}
|
|
1264
|
-
/** One model call's usage, as the agent reported it — never estimated. */
|
|
1265
|
-
| {
|
|
1266
|
-
type: 'usage';
|
|
1267
|
-
record: UsageRecord;
|
|
1268
|
-
}
|
|
1269
|
-
/** The agent delegated `brief` to a subagent, whose events carry `subagentId` until it finishes. */
|
|
1270
|
-
| {
|
|
1271
|
-
type: 'subagent_started';
|
|
1272
|
-
subagentId: string;
|
|
1273
|
-
brief: string;
|
|
1274
|
-
model?: string;
|
|
1275
|
-
} | {
|
|
1276
|
-
type: 'subagent_finished';
|
|
1277
|
-
subagentId: string;
|
|
1278
|
-
outcome: SubagentOutcome;
|
|
1279
|
-
digest: string;
|
|
1280
|
-
}
|
|
1281
|
-
/**
|
|
1282
|
-
* The agent asked to do something its mode does not cover. A planner always
|
|
1283
|
-
* auto-denies it (T1) — a planner that can mutate is not a planner — and the
|
|
1284
|
-
* adapter answers so the turn does not hang. A task's request stays open
|
|
1285
|
-
* until {@link TaskModeAgentAdapter.answerPermission} (ADR-0018, A1).
|
|
1286
|
-
*
|
|
1287
|
-
* `input` and `suggestions` are the raw request; `suggestions` are the
|
|
1288
|
-
* agent's own session-scoped grants, what "Allow for this task" answers with.
|
|
1289
|
-
* `decided` marks a request the task's mode already answered — the adapter
|
|
1290
|
-
* replied as the manifest says that mode does — so it is shown, never asked.
|
|
1291
|
-
*/
|
|
1292
|
-
| {
|
|
1293
|
-
type: 'permission_request';
|
|
1294
|
-
id: string;
|
|
1295
|
-
name: string;
|
|
1296
|
-
detail: string;
|
|
1297
|
-
input?: Record<string, unknown>;
|
|
1298
|
-
suggestions?: unknown[];
|
|
1299
|
-
toolUseId?: string;
|
|
1300
|
-
decided?: ApprovalDecision;
|
|
1301
|
-
}
|
|
1302
|
-
/** The agent withdrew an open request — an interrupt cancels the call it was for. It takes no answer now. */
|
|
1303
|
-
| {
|
|
1304
|
-
type: 'permission_cancelled';
|
|
1305
|
-
id: string;
|
|
1306
|
-
}
|
|
1307
|
-
/**
|
|
1308
|
-
* The agent delegated work to a subagent it left running in the background,
|
|
1309
|
-
* and may end its turn before that work reports. Ordewell's conversation is
|
|
1310
|
-
* request/response: a turn that ends hands control back to the user, and
|
|
1311
|
-
* anything the agent says afterwards arrives with no turn open and is lost.
|
|
1312
|
-
* Naming the launch is what lets the service ask for the results in time.
|
|
1313
|
-
*/
|
|
1314
|
-
| {
|
|
1315
|
-
type: 'background_agent';
|
|
1316
|
-
id: string;
|
|
1317
|
-
}
|
|
1318
|
-
/**
|
|
1319
|
-
* The agent finished its turn and is waiting for the next user message.
|
|
1320
|
-
* `interrupted` marks a turn cut short by {@link TaskModeAgentAdapter.interrupt}
|
|
1321
|
-
* rather than one the agent chose to end.
|
|
1322
|
-
*/
|
|
1323
|
-
| {
|
|
1324
|
-
type: 'turn_end';
|
|
1325
|
-
interrupted?: boolean;
|
|
1326
|
-
}
|
|
1327
|
-
/**
|
|
1328
|
-
* The model has a message handed over by {@link TaskModeAgentAdapter.steer},
|
|
1329
|
-
* by the runner's own account rather than because the write succeeded
|
|
1330
|
-
* (ADR-0023, D3). With no turn open, the runner opened one for it.
|
|
1331
|
-
*/
|
|
1332
|
-
| {
|
|
1333
|
-
type: 'message_delivered';
|
|
1334
|
-
id: string;
|
|
1335
|
-
}
|
|
1336
|
-
/**
|
|
1337
|
-
* The runner let go of a message it accepted from `steer` without showing it
|
|
1338
|
-
* to the model — Codex does, for one its turn ended before consuming. It is
|
|
1339
|
-
* Ordewell's to send again (ADR-0023, D4).
|
|
1340
|
-
*/
|
|
1341
|
-
| {
|
|
1342
|
-
type: 'message_dropped';
|
|
1343
|
-
id: string;
|
|
1344
|
-
}
|
|
1345
|
-
/** The turn failed. Carries the agent's own words — never a Ordewell paraphrase. */
|
|
1346
|
-
| {
|
|
1347
|
-
type: 'error';
|
|
1348
|
-
message: string;
|
|
1349
|
-
};
|
|
1350
|
-
interface AgentStartCommon {
|
|
1351
|
-
/** Workspace root. The agent works from here and, in read-only mode, cannot leave it. */
|
|
1352
|
-
cwd: string;
|
|
1353
|
-
/** Model id from the runner's own discovery catalog. Omitted means the agent's default. */
|
|
1354
|
-
model?: string;
|
|
1355
|
-
/**
|
|
1356
|
-
* The agent's own session id from a previous run. For the planner a hint
|
|
1357
|
-
* only: Ordewell's transcript is the source of truth (T4), so a failed resume
|
|
1358
|
-
* degrades to a fresh session seeded from the stored history, not an error.
|
|
1359
|
-
*/
|
|
1360
|
-
resumeSessionId?: string;
|
|
1361
|
-
}
|
|
1362
|
-
/** The read-only planner (ADR-0008/0009). The only start `CliAgentAiService` can express. */
|
|
1363
|
-
interface PlannerStartOptions extends AgentStartCommon {
|
|
1364
|
-
kind: 'planner';
|
|
1365
|
-
/** The planner system prompt, in its harness variant. */
|
|
1366
|
-
systemPrompt: string;
|
|
1367
|
-
/** Variant / reasoning effort id from that model's `variants` list. */
|
|
1368
|
-
effort?: string;
|
|
1369
|
-
/** The Ordewell MCP server to inject, pre-authorized (ADR-0022). Only for an adapter with {@link AgentAdapter.mcpAttached}. */
|
|
1370
|
-
mcp?: McpClientConfig;
|
|
1371
|
-
}
|
|
1372
|
-
/**
|
|
1373
|
-
* What a runner manifest says the task's mode and effort mean (ADR-0001),
|
|
1374
|
-
* resolved by the same code terminal tasks use — see `resolveTaskRunnerFlags`.
|
|
1375
|
-
* The adapter adds only its protocol flags around these.
|
|
1376
|
-
*/
|
|
1377
|
-
interface TaskRunnerFlags {
|
|
1378
|
-
/** The runner's own permission-mode value for the task's mode. */
|
|
1379
|
-
permissionMode: string;
|
|
1380
|
-
/** The task's raw effort id, present only alongside a model. Each adapter maps it to its own protocol. */
|
|
1381
|
-
effort?: string;
|
|
1382
|
-
/** The manifest's further settings for the task's mode, by setting name — see `PluginFeatures.modeSettings`. */
|
|
1383
|
-
modeSettings: Record<string, string>;
|
|
1384
|
-
}
|
|
1385
|
-
/** A plan task driven over the runner's programmatic protocol (ADR-0018, C1). */
|
|
1386
|
-
interface TaskStartOptions extends AgentStartCommon {
|
|
1387
|
-
kind: 'task';
|
|
1388
|
-
/** The task's runner mode id, as the plan names it. */
|
|
1389
|
-
mode: string;
|
|
1390
|
-
flags: TaskRunnerFlags;
|
|
1391
|
-
/**
|
|
1392
|
-
* The Ordewell MCP server and this attempt's token (ADR-0022). An adapter
|
|
1393
|
-
* that can inject it does, with its tools pre-approved; one that cannot
|
|
1394
|
-
* ignores it, and the task completes by its marker.
|
|
1395
|
-
*/
|
|
1396
|
-
mcp?: McpClientConfig;
|
|
1397
|
-
}
|
|
1398
|
-
/**
|
|
1399
|
-
* The explicit start switch. Discriminated so that a read-only planner and a
|
|
1400
|
-
* mutating task can never be confused by a missing field: every caller names
|
|
1401
|
-
* which one it is starting.
|
|
1402
|
-
*/
|
|
1403
|
-
type AgentStartOptions = PlannerStartOptions | TaskStartOptions;
|
|
1404
|
-
/** A runner asked to start in task mode that has no task-mode connector yet. */
|
|
1405
|
-
declare class TaskModeUnsupportedError extends Error {
|
|
1406
|
-
readonly runner: string;
|
|
1407
|
-
constructor(runner: string);
|
|
1408
|
-
}
|
|
1409
|
-
interface AgentAdapter {
|
|
1410
|
-
/** The runner id this adapter drives — `claude-code`, `codex`, `opencode`. */
|
|
1411
|
-
readonly agentId: string;
|
|
1412
|
-
/** Spawn the agent — read-only for a planner, in the task's mode for a task — ready to receive messages. */
|
|
1413
|
-
start(opts: AgentStartOptions): Promise<void>;
|
|
1414
|
-
/**
|
|
1415
|
-
* Send one user message and stream the turn's events until it ends. Resolves
|
|
1416
|
-
* when the agent yields the floor; rejects only when the transport itself
|
|
1417
|
-
* failed in a way no `error` event could describe.
|
|
1418
|
-
*
|
|
1419
|
-
* `onActivity`, when given, fires on raw transport traffic — every stdio
|
|
1420
|
-
* line or stream chunk the process produces — independent of whether that
|
|
1421
|
-
* traffic becomes an `AgentEvent`. An adapter may legitimately emit nothing
|
|
1422
|
-
* for long stretches (a subagent's filtered output, most often); a caller
|
|
1423
|
-
* using presence-of-events as a liveness signal would read that silence as
|
|
1424
|
-
* a hang. `onActivity` is the seam that keeps liveness detection from being
|
|
1425
|
-
* coupled to what each adapter chooses to surface.
|
|
1426
|
-
*/
|
|
1427
|
-
send(message: string, onEvent: (event: AgentEvent) => void, signal?: AbortSignal, onActivity?: () => void): Promise<void>;
|
|
1428
|
-
/** The agent's native session id once it has announced one. Resumption hint only. */
|
|
1429
|
-
nativeSessionId(): string | null;
|
|
1430
|
-
/** Kill the process and release its resources. Idempotent. */
|
|
1431
|
-
dispose(): void;
|
|
1432
|
-
/**
|
|
1433
|
-
* Whether the Ordewell MCP server passed in {@link PlannerStartOptions.mcp}
|
|
1434
|
-
* is connected, as the runner itself reports it (ADR-0022, S4). Absent on an
|
|
1435
|
-
* adapter that cannot inject the server at all.
|
|
1436
|
-
*/
|
|
1437
|
-
mcpAttached?(): Promise<boolean>;
|
|
1438
|
-
}
|
|
1439
|
-
/** What an adapter adds to run a task rather than a planner (ADR-0018). */
|
|
1440
|
-
interface TaskModeAgentAdapter extends AgentAdapter {
|
|
1441
|
-
/**
|
|
1442
|
-
* Ask the running turn to stop, keeping the process and its session. Resolves
|
|
1443
|
-
* true once the agent acknowledged it; the turn then ends with
|
|
1444
|
-
* `turn_end { interrupted: true }`. False means the agent did not answer
|
|
1445
|
-
* within `timeoutMs`, and the caller must fall back to killing it.
|
|
1446
|
-
*/
|
|
1447
|
-
interrupt(timeoutMs: number): Promise<boolean>;
|
|
1448
|
-
/**
|
|
1449
|
-
* Registers the listener for what the agent does after a turn has ended and
|
|
1450
|
-
* before the next message — a turn it opens itself when background work
|
|
1451
|
-
* finishes, most often. Without one that output is dropped, which is right
|
|
1452
|
-
* for a planner and wrong for a task, whose marker may be said there.
|
|
1453
|
-
*/
|
|
1454
|
-
onOutOfTurn?(listener: (event: AgentEvent) => void): void;
|
|
1455
|
-
/** Registers a listener for the process ending, for any reason. Fires at most once. */
|
|
1456
|
-
onProcessExit(listener: (code: number) => void): void;
|
|
1457
|
-
/**
|
|
1458
|
-
* Hand a message to the running turn, for the runner to show the model at
|
|
1459
|
-
* its next step boundary (ADR-0023, D1–D2). Resolves true once the runner
|
|
1460
|
-
* accepted it: handed over, not yet delivered — the turn's events later
|
|
1461
|
-
* carry `message_delivered` or `message_dropped` for `id`. False when the
|
|
1462
|
-
* runner refused it or no turn is running; the caller keeps it for the
|
|
1463
|
-
* turn's end. Absent on an adapter whose runner cannot take a message
|
|
1464
|
-
* mid-turn.
|
|
1465
|
-
*/
|
|
1466
|
-
steer?(id: string, text: string): Promise<boolean>;
|
|
1467
|
-
/**
|
|
1468
|
-
* Answer an open `permission_request`. False when the id is not open — it
|
|
1469
|
-
* was answered, cancelled, or never asked.
|
|
1470
|
-
*/
|
|
1471
|
-
answerPermission(id: string, decision: ApprovalDecision): boolean;
|
|
1472
|
-
}
|
|
1473
|
-
/**
|
|
1474
|
-
* The single injected boundary between Ordewell and the operating system —
|
|
1475
|
-
* the same pattern `HeadlessRunnerDeps` uses for task execution. Tests feed
|
|
1476
|
-
* recorded agent output through `spawn` (and, for HTTP-transport agents,
|
|
1477
|
-
* `fetch`) so one test exercises adapter parsing, event mapping, reply
|
|
1478
|
-
* classification and the repair loop as a single observable behavior.
|
|
1479
|
-
*/
|
|
1480
|
-
interface AgentProcessDeps {
|
|
1481
|
-
spawn: SpawnFn;
|
|
1482
|
-
fetch: typeof globalThis.fetch;
|
|
1483
|
-
/** Resolves the PATH agents are spawned under. Defaults to the augmented PATH. */
|
|
1484
|
-
resolvePath?: () => Promise<string>;
|
|
1485
|
-
/** Host platform. Defaults to the real one; injected so OS-specific behavior is testable anywhere. */
|
|
1486
|
-
platform?: NodeJS.Platform;
|
|
1487
|
-
/** True when `workspace` names an existing directory. Defaults to a real filesystem check. */
|
|
1488
|
-
isDirectory?: (workspace: string) => boolean;
|
|
1489
|
-
/** True when `candidate` names an existing, spawnable file. Defaults to a real filesystem check. */
|
|
1490
|
-
exists?: (candidate: string) => boolean;
|
|
1491
|
-
/** The workspace's own variables for a cwd (ADR-0016). Defaults to {@link resolveWorkspaceEnv}. */
|
|
1492
|
-
workspaceEnv?: (cwd: string) => Promise<Record<string, string>>;
|
|
1493
|
-
}
|
|
1494
|
-
/** Builds the adapter for one runner id, or null when that runner cannot plan. */
|
|
1495
|
-
type AgentAdapterFactory = (runner: string, deps: AgentProcessDeps) => AgentAdapter | null;
|
|
1496
|
-
/**
|
|
1497
|
-
* Split a stream of chunks into complete lines. Every agent transport here is
|
|
1498
|
-
* newline-delimited JSON of some shape, and a chunk boundary lands mid-object
|
|
1499
|
-
* often enough that parsing per-chunk silently drops events.
|
|
1500
|
-
*/
|
|
1501
|
-
declare class LineBuffer {
|
|
1502
|
-
private buffer;
|
|
1503
|
-
push(chunk: string, onLine: (line: string) => void): void;
|
|
1504
|
-
/** Anything left unterminated when the stream closed. */
|
|
1505
|
-
flush(): string;
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
interface RunnerPluginManifest {
|
|
1509
|
-
name: string;
|
|
1510
|
-
displayName: string;
|
|
1511
|
-
description: string;
|
|
1512
|
-
version: string;
|
|
1513
|
-
author?: string;
|
|
1514
|
-
homepage?: string;
|
|
1515
|
-
runner: PluginRunnerDef;
|
|
1516
|
-
features: PluginFeatures;
|
|
1517
|
-
modelDiscovery: PluginModelDiscovery;
|
|
1518
|
-
contextFile?: string;
|
|
1519
|
-
contextFileAltPath?: string;
|
|
1520
|
-
modes?: PluginMode[];
|
|
1521
|
-
}
|
|
1522
|
-
interface PluginRunnerDef {
|
|
1523
|
-
command: string;
|
|
1524
|
-
argsTemplate: string[];
|
|
1525
|
-
promptInArgs: boolean;
|
|
1526
|
-
env?: Record<string, string>;
|
|
1527
|
-
/** When true, the runner requires a PTY. HeadlessRunner wraps with `script` to allocate one. */
|
|
1528
|
-
requiresTty?: boolean;
|
|
1529
|
-
/**
|
|
1530
|
-
* True when this runner's interactive prompt flag (e.g. opencode's
|
|
1531
|
-
* `--prompt`) only pre-fills its TUI's composer instead of running it, so a
|
|
1532
|
-
* surface driving that TUI unattended must send an explicit Enter after
|
|
1533
|
-
* launch. Only takes effect while the resolved invocation is interactive —
|
|
1534
|
-
* see `RunnerInvocation.submitPromptKey`.
|
|
1535
|
-
*/
|
|
1536
|
-
submitPromptKey?: boolean;
|
|
1537
|
-
/**
|
|
1538
|
-
* Screens the agent can stop on before starting the task, waiting for a
|
|
1539
|
-
* human — a folder-trust or permission-mode confirmation. Ordewell never
|
|
1540
|
-
* answers one; seeing it, the task's user is told where to.
|
|
1541
|
-
*/
|
|
1542
|
-
blockingPrompts?: BlockingPrompt[];
|
|
1543
|
-
/**
|
|
1544
|
-
* The command lines a later major version of the runner takes instead of
|
|
1545
|
-
* `argsTemplate`/`env`, for a runner whose CLI changed shape between majors.
|
|
1546
|
-
* The one with the highest `minMajor` the installed version reaches wins; an
|
|
1547
|
-
* install whose version cannot be read keeps the base template.
|
|
1548
|
-
*/
|
|
1549
|
-
versioned?: VersionedInvocation[];
|
|
1550
|
-
}
|
|
1551
|
-
interface VersionedInvocation {
|
|
1552
|
-
minMajor: number;
|
|
1553
|
-
argsTemplate: string[];
|
|
1554
|
-
env?: Record<string, string>;
|
|
1555
|
-
}
|
|
1556
|
-
interface BlockingPrompt {
|
|
1557
|
-
/** Text the prompt shows; matched ignoring case and whitespace. */
|
|
1558
|
-
phrase: string;
|
|
1559
|
-
/** Completes "<runner> is asking …", e.g. "whether to trust this folder". */
|
|
1560
|
-
asks: string;
|
|
1561
|
-
}
|
|
1562
|
-
interface PluginFeatures {
|
|
1563
|
-
modelSelection: boolean;
|
|
1564
|
-
thinkingEffort: boolean;
|
|
1565
|
-
planMode: boolean;
|
|
1566
|
-
planModeFlag: string;
|
|
1567
|
-
buildModeFlag?: string;
|
|
1568
|
-
/** Flag appended when headless mode is on, so the agent never prompts for permission (e.g. Claude's --dangerously-skip-permissions). */
|
|
1569
|
-
headlessFlag?: string;
|
|
1570
|
-
thinkingFlag?: string;
|
|
1571
|
-
thinkingValueEnabled?: string;
|
|
1572
|
-
thinkingValueDisabled?: string;
|
|
1573
|
-
thinkingValueAdaptive?: string;
|
|
1574
|
-
/** Maps mode IDs to the CLI --permission-mode value. Used by {{feature:permissionModeVal}}. */
|
|
1575
|
-
permissionModeValues?: Record<string, string>;
|
|
1576
|
-
/**
|
|
1577
|
-
* Further per-mode settings for runners whose permission story has more than
|
|
1578
|
-
* one axis (Codex: sandbox, approval policy, approvals reviewer). Keyed by
|
|
1579
|
-
* setting name, then by mode id; a mode a map does not name leaves that
|
|
1580
|
-
* setting to the runner's own default. Codex declares `approvalPolicy`
|
|
1581
|
-
* (`-a` / `approval_policy`) and `approvalsReviewer` (`approvals_reviewer`);
|
|
1582
|
-
* the terminal template reads them too, through `{{feature:approvalPolicyVal}}`
|
|
1583
|
-
* and the `*Config` tokens.
|
|
1584
|
-
*/
|
|
1585
|
-
modeSettings?: Record<string, Record<string, string>>;
|
|
1586
|
-
}
|
|
1587
|
-
type PluginParser = 'claude-help' | 'opencode-models' | 'opencode-models-verbose' | 'anthropic-models' | 'line-by-line' | 'json' | 'json-table';
|
|
1588
|
-
interface DiscoveryCommand {
|
|
1589
|
-
command: string;
|
|
1590
|
-
args: string[];
|
|
1591
|
-
parser?: PluginParser;
|
|
1592
|
-
}
|
|
1593
|
-
type ApiAuthMethod = {
|
|
1594
|
-
type: 'env';
|
|
1595
|
-
varName: string;
|
|
1596
|
-
header: string;
|
|
1597
|
-
prefix?: string;
|
|
1598
|
-
} | {
|
|
1599
|
-
type: 'file';
|
|
1600
|
-
path: string;
|
|
1601
|
-
jsonPath: string;
|
|
1602
|
-
header: string;
|
|
1603
|
-
prefix?: string;
|
|
1604
|
-
};
|
|
1605
|
-
interface ApiDiscoveryConfig {
|
|
1606
|
-
url: string;
|
|
1607
|
-
headers?: Record<string, string>;
|
|
1608
|
-
auth: ApiAuthMethod[];
|
|
1609
|
-
parser: PluginParser;
|
|
1610
|
-
}
|
|
1611
|
-
interface PluginModelDiscovery {
|
|
1612
|
-
method: 'command' | 'hardcoded';
|
|
1613
|
-
command?: string;
|
|
1614
|
-
args?: string[];
|
|
1615
|
-
parser?: PluginParser;
|
|
1616
|
-
jsonPath?: string;
|
|
1617
|
-
/**
|
|
1618
|
-
* Stdio JSON-RPC discovery (Codex `app-server`): spawn the command, send
|
|
1619
|
-
* `initialize` then `model/list`, and read the catalog from the response.
|
|
1620
|
-
* Tried BEFORE apiDiscovery and command discovery. When the call fails,
|
|
1621
|
-
* `cacheFile` (the runner's own on-disk catalog cache, `~` expanded) is
|
|
1622
|
-
* read before falling through to the remaining discovery methods.
|
|
1623
|
-
*/
|
|
1624
|
-
appServer?: {
|
|
1625
|
-
command: string;
|
|
1626
|
-
args: string[];
|
|
1627
|
-
cacheFile?: string;
|
|
1628
|
-
};
|
|
1629
|
-
/**
|
|
1630
|
-
* Optional last-resort list for user plugins whose CLI cannot enumerate
|
|
1631
|
-
* models. Used only when command discovery fails entirely or the CLI is
|
|
1632
|
-
* unavailable. Built-in manifests must NOT use this: anything listed here is
|
|
1633
|
-
* shown to the user as available even when it isn't.
|
|
1634
|
-
*/
|
|
1635
|
-
fallbackModels?: {
|
|
1636
|
-
modelId: string;
|
|
1637
|
-
modelLabel: string;
|
|
1638
|
-
}[];
|
|
1639
|
-
/**
|
|
1640
|
-
* Stable `--model` aliases that the runner's CLI always accepts but its help
|
|
1641
|
-
* text may omit (e.g. Claude's 'haiku'). Merged into successful discovery
|
|
1642
|
-
* results to fill gaps — discovered models take precedence, missing aliases
|
|
1643
|
-
* are appended — and used as the last resort when discovery fails entirely.
|
|
1644
|
-
* Unlike `fallbackModels`, entries must be stable CLI-accepted aliases
|
|
1645
|
-
* (contracts that always resolve), not arbitrary model IDs.
|
|
1646
|
-
*/
|
|
1647
|
-
canonicalAliases?: {
|
|
1648
|
-
modelId: string;
|
|
1649
|
-
modelLabel: string;
|
|
1650
|
-
}[];
|
|
1651
|
-
/**
|
|
1652
|
-
* HTTP API discovery — tried BEFORE command discovery. When the runner's CLI
|
|
1653
|
-
* has no model-listing subcommand (Claude Code), an API endpoint can serve as
|
|
1654
|
-
* the authoritative source. Auth methods are tried in order; the first that
|
|
1655
|
-
* yields a token is used. If no auth method yields a token or the request
|
|
1656
|
-
* fails, discovery falls through to `discoveryCommands` + `canonicalAliases`.
|
|
1657
|
-
*/
|
|
1658
|
-
apiDiscovery?: ApiDiscoveryConfig;
|
|
1659
|
-
preferredPatterns?: {
|
|
1660
|
-
id: string;
|
|
1661
|
-
label: string;
|
|
1662
|
-
}[];
|
|
1663
|
-
variants?: {
|
|
1664
|
-
id: string;
|
|
1665
|
-
label: string;
|
|
1666
|
-
}[];
|
|
1667
|
-
discoveryCommands?: DiscoveryCommand[];
|
|
1668
|
-
}
|
|
1669
|
-
interface PluginMode {
|
|
1670
|
-
id: string;
|
|
1671
|
-
label: string;
|
|
1672
|
-
description: string;
|
|
1673
|
-
/** CLI value passed to the runner's permission/mode flag. Defaults to id if omitted. */
|
|
1674
|
-
cliValue?: string;
|
|
1675
|
-
/** Marks this mode as the runner's most permissive mode — the resolved default when autonomous mode is ON. */
|
|
1676
|
-
autonomous?: boolean;
|
|
1677
|
-
/** Marks this mode as the runner's conservative build mode — the resolved default when autonomous mode is OFF. */
|
|
1678
|
-
safe?: boolean;
|
|
1679
|
-
}
|
|
1680
|
-
interface PluginEntry {
|
|
1681
|
-
manifest: RunnerPluginManifest;
|
|
1682
|
-
source: 'builtin' | 'user';
|
|
1683
|
-
installPath?: string;
|
|
1684
|
-
}
|
|
1685
|
-
interface ResolveContext {
|
|
1686
|
-
prompt: string;
|
|
1687
|
-
model?: string;
|
|
1688
|
-
thinkingEffort?: string;
|
|
1689
|
-
/** All variant ids the assigned model offers — lets {{opencodeVariantConfig}} disable the non-chosen ones. */
|
|
1690
|
-
modelVariants?: string[];
|
|
1691
|
-
mode: string;
|
|
1692
|
-
/**
|
|
1693
|
-
* Autonomy axis: when true, resolve {{if headless}} blocks and the
|
|
1694
|
-
* {{feature:headless}} token so the agent never stops to ask for permission.
|
|
1695
|
-
* True for every orchestrated task run — nobody is watching the terminal on
|
|
1696
|
-
* Ordewell's behalf — independently of the session *shape* below.
|
|
1697
|
-
*/
|
|
1698
|
-
headless?: boolean;
|
|
1699
|
-
/**
|
|
1700
|
-
* Session-shape axis: true when the runner is launched onto a real TTY the
|
|
1701
|
-
* user can attach to (a tmux window, a VS Code pseudoterminal), so the
|
|
1702
|
-
* runner's own TUI should come up rather than its non-interactive
|
|
1703
|
-
* subcommand. Defaults to `!headless` for callers that predate the split.
|
|
1704
|
-
*/
|
|
1705
|
-
interactive?: boolean;
|
|
1706
|
-
/** The task's working directory — needed by runners whose autonomy flags name a path. */
|
|
1707
|
-
cwd?: string;
|
|
1708
|
-
/** The installed runner's `--version`, read only for a manifest with `versioned` invocations. */
|
|
1709
|
-
runnerVersion?: string;
|
|
1710
|
-
}
|
|
1711
|
-
interface RunnerInvocation {
|
|
1712
|
-
command: string;
|
|
1713
|
-
args: string[];
|
|
1714
|
-
env: Record<string, string>;
|
|
1715
|
-
promptInArgs: boolean;
|
|
1716
|
-
/** True when a surface running this invocation on a real TTY must send an explicit Enter once the process starts. */
|
|
1717
|
-
submitPromptKey: boolean;
|
|
1718
|
-
}
|
|
1719
|
-
/**
|
|
1720
|
-
* Persistent storage seam for plugin manifests. The RunnerRegistry delegates
|
|
1721
|
-
* all filesystem operations to this interface so the plugin lifecycle is
|
|
1722
|
-
* testable without real I/O.
|
|
1723
|
-
*/
|
|
1724
|
-
interface IPluginStore {
|
|
1725
|
-
/** Path to the user plugins directory (~/.ordewell/plugins/). */
|
|
1726
|
-
getUserPluginsDir(): string;
|
|
1727
|
-
/** List subdirectory names inside the user plugins directory. */
|
|
1728
|
-
listUserPluginDirs(): string[];
|
|
1729
|
-
/** List entry names directly inside a directory. Returns [] when unreadable. */
|
|
1730
|
-
listDir(dir: string): string[];
|
|
1731
|
-
/** Read and parse a manifest.json from pluginDir. Returns null on failure. */
|
|
1732
|
-
loadManifest(pluginDir: string): RunnerPluginManifest | null;
|
|
1733
|
-
/** Recursively copy sourceDir to destDir. */
|
|
1734
|
-
copyDir(sourceDir: string, destDir: string): void;
|
|
1735
|
-
/** Recursively remove a directory. */
|
|
1736
|
-
removeDir(dir: string): void;
|
|
1737
|
-
/** Ensure a directory exists (mkdir -p). */
|
|
1738
|
-
ensureDir(dir: string): void;
|
|
1739
|
-
/** Write a text file. */
|
|
1740
|
-
writeFile(filePath: string, content: string): void;
|
|
1741
|
-
/** Read a UTF-8 text file. Returns null on ENOENT or read error. */
|
|
1742
|
-
readFile(filePath: string): string | null;
|
|
1743
|
-
/** True if path exists and is a directory. */
|
|
1744
|
-
dirExists(path: string): boolean;
|
|
1745
|
-
/** True if path exists. */
|
|
1746
|
-
exists(path: string): boolean;
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
/**
|
|
1750
|
-
* Decides whether one out-of-envelope capability may run, and remembers the
|
|
1751
|
-
* answer for the rest of the session.
|
|
1752
|
-
*
|
|
1753
|
-
* Grants are keyed on {@link ApprovalRequest.scope}, never on the concrete
|
|
1754
|
-
* subject: approving a read of `/tmp/foo/a.log` grants `/tmp/foo/*`, and
|
|
1755
|
-
* approving `az group list` grants `az group`. Without that, a planner doing
|
|
1756
|
-
* real research would prompt on every single call and the feature would be
|
|
1757
|
-
* unusable.
|
|
1758
|
-
*
|
|
1759
|
-
* `mode` is the policy floor:
|
|
1760
|
-
* ask consult the human channel; no channel means deny (headless, web)
|
|
1761
|
-
* allow grant everything the tier system did not already refuse
|
|
1762
|
-
* deny grant nothing beyond `preApproved`
|
|
1763
|
-
*
|
|
1764
|
-
* Note the ordering: `preApproved` is honored under every mode including
|
|
1765
|
-
* `deny`, because it is an explicit operator decision rather than a default.
|
|
1766
|
-
*/
|
|
1767
|
-
type ApprovalMode = 'ask' | 'allow' | 'deny';
|
|
1768
|
-
interface ApprovalPolicyOptions {
|
|
1769
|
-
mode?: ApprovalMode;
|
|
1770
|
-
/** Scopes granted up front from config. A trailing `*` matches by prefix. */
|
|
1771
|
-
preApproved?: string[];
|
|
1772
|
-
/** The human channel. Absent means there is nobody to ask. */
|
|
1773
|
-
ask?: (req: ApprovalRequest) => Promise<boolean>;
|
|
1774
|
-
/** Called whenever a decision is reached, for surfacing in a UI or a log. */
|
|
1775
|
-
onDecision?: (req: ApprovalRequest, granted: boolean, source: ApprovalSource) => void;
|
|
1776
|
-
}
|
|
1777
|
-
type ApprovalSource = 'pre-approved' | 'remembered' | 'mode' | 'asked' | 'no-channel';
|
|
1778
|
-
declare class ApprovalPolicy implements IApproval {
|
|
1779
|
-
private readonly mode;
|
|
1780
|
-
private readonly preApproved;
|
|
1781
|
-
private readonly asker?;
|
|
1782
|
-
private readonly onDecision?;
|
|
1783
|
-
private readonly granted;
|
|
1784
|
-
private readonly refused;
|
|
1785
|
-
/** One in-flight ask per scope: a parallel tool round must not prompt twice for the same thing. */
|
|
1786
|
-
private readonly inFlight;
|
|
1787
|
-
private generation;
|
|
1788
|
-
constructor(opts?: ApprovalPolicyOptions);
|
|
1789
|
-
request(req: ApprovalRequest): Promise<boolean>;
|
|
1790
|
-
/** Scopes the user has granted this session — for display and for persistence. */
|
|
1791
|
-
grantedScopes(): string[];
|
|
1792
|
-
/** Drop every session-scoped decision. Called on session reset. */
|
|
1793
|
-
reset(): void;
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
interface CatalogModel {
|
|
1797
|
-
id: string;
|
|
1798
|
-
name: string;
|
|
1799
|
-
description: string;
|
|
1800
|
-
pricing: {
|
|
1801
|
-
prompt: string;
|
|
1802
|
-
completion: string;
|
|
1803
|
-
};
|
|
1804
|
-
contextLength: number;
|
|
1805
|
-
}
|
|
1806
|
-
declare class ModelCatalog {
|
|
1807
|
-
static fetchModels(apiKey: string, baseUrl?: string, fetchImpl?: typeof fetch): Promise<CatalogModel[]>;
|
|
1808
|
-
/**
|
|
1809
|
-
* Delete the on-disk catalog cache for a base URL so the next fetch re-runs.
|
|
1810
|
-
* Used by the resolver's `invalidate()` in production (no injected fetch);
|
|
1811
|
-
* best-effort — a missing file or unreadable dir is a no-op.
|
|
1812
|
-
*/
|
|
1813
|
-
static clearCache(baseUrl?: string): void;
|
|
1814
|
-
private static readCache;
|
|
1815
|
-
private static writeCache;
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
interface ModelShortcut {
|
|
1819
|
-
label: string;
|
|
1820
|
-
id: string;
|
|
1821
|
-
provider: string;
|
|
1822
|
-
description: string;
|
|
1823
|
-
pricing?: string;
|
|
1824
|
-
}
|
|
1825
|
-
declare const ORCHESTRATOR_SHORTCUTS: ModelShortcut[];
|
|
1826
|
-
declare function resolveModelShortcut(input: string, shortcuts: ModelShortcut[]): string | null;
|
|
1827
|
-
/**
|
|
1828
|
-
* Resolves a value to a model id ONLY when it is in the supplied catalog
|
|
1829
|
-
* option list — either directly or via a shortcut label. Free-typed, unknown,
|
|
1830
|
-
* or not-currently-available values return null, so callers never store an id
|
|
1831
|
-
* the user couldn't have selected from the list. Keeps model-setting list-only.
|
|
1832
|
-
*/
|
|
1833
|
-
declare function knownModelId(value: string, optionIds: string[], shortcuts: ModelShortcut[]): string | null;
|
|
1834
|
-
|
|
1835
|
-
type ProviderModelLists = Record<string, string[]>;
|
|
1836
|
-
interface OrchestratorOption {
|
|
1837
|
-
id: string;
|
|
1838
|
-
label: string;
|
|
1839
|
-
provider: string;
|
|
1840
|
-
apiProvider: AiProvider;
|
|
1841
|
-
description?: string;
|
|
1842
|
-
pricing?: string;
|
|
1843
|
-
/** The model's context window when the catalog reports one (#49). */
|
|
1844
|
-
contextWindow?: number;
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
interface FetchAllProviderModelsOptions {
|
|
1848
|
-
apiKeys: Record<string, string>;
|
|
1849
|
-
baseUrls: Record<string, string>;
|
|
1850
|
-
fetchImpl?: typeof fetch;
|
|
1851
|
-
}
|
|
1852
|
-
type AllProviderModels = Record<string, CatalogModel[]>;
|
|
1853
|
-
/**
|
|
1854
|
-
* Result of a picker-catalog fetch fan-out: the per-provider model lists plus
|
|
1855
|
-
* the per-provider failures. `errors` is keyed by provider id and holds the
|
|
1856
|
-
* failure message for any provider whose catalog fetch rejected — the seam the
|
|
1857
|
-
* surfaces (CLI, VS Code) use to flag "this configured provider didn't work"
|
|
1858
|
-
* rather than silently showing a short list.
|
|
1859
|
-
*/
|
|
1860
|
-
interface ProviderModelsResult {
|
|
1861
|
-
models: AllProviderModels;
|
|
1862
|
-
errors: Record<string, string>;
|
|
1863
|
-
}
|
|
1864
|
-
/** The provider config fields the discovery fan-out needs. */
|
|
1865
|
-
interface ProviderCredentialSource {
|
|
1866
|
-
getProviderApiKey(provider: AiProvider): string;
|
|
1867
|
-
getProviderBaseUrl(provider: AiProvider): string;
|
|
1868
|
-
openaiCompatibleBaseUrl: string;
|
|
1869
|
-
}
|
|
1870
|
-
/**
|
|
1871
|
-
* Collect the API keys and base URLs the picker fan-out should probe, applying
|
|
1872
|
-
* the single "configured" policy shared by every surface (CLI + VS Code):
|
|
1873
|
-
* - a preset is discovered only when it has an API key (OpenRouter included —
|
|
1874
|
-
* its public catalog is not probed keyless, matching the product rule that
|
|
1875
|
-
* only key-configured providers appear);
|
|
1876
|
-
* - `openai_compatible` is discovered only when an explicit endpoint is set
|
|
1877
|
-
* (keyless local servers like ollama / LM Studio).
|
|
1878
|
-
* Base URLs are handed through only for providers that pass the gate, so the
|
|
1879
|
-
* fan-out never touches an unconfigured third-party endpoint.
|
|
1880
|
-
*/
|
|
1881
|
-
declare function collectProviderCredentials(config: ProviderCredentialSource): {
|
|
1882
|
-
apiKeys: Record<string, string>;
|
|
1883
|
-
baseUrls: Record<string, string>;
|
|
1884
|
-
};
|
|
1885
|
-
declare function fetchAllProviderModels(opts: FetchAllProviderModelsOptions): Promise<ProviderModelsResult>;
|
|
1886
|
-
declare function resolveProvider(modelId: string, providerModelLists: Record<string, string[]>): AiProvider | null;
|
|
1887
|
-
declare function toOrchestratorOptions(providerModels: AllProviderModels, shortcuts: ModelShortcut[]): OrchestratorOption[];
|
|
1888
|
-
|
|
1889
|
-
/**
|
|
1890
|
-
* Who plans. Mostly LLM vendors reached over HTTP; the last three are *harness
|
|
1891
|
-
* planners* (ADR-0009) — a coding agent CLI already installed on the machine,
|
|
1892
|
-
* driven as the planner over its own programmatic transport and authenticated
|
|
1893
|
-
* by the subscription the user already holds. They are runners in the provider
|
|
1894
|
-
* axis, deliberately: "what plans for me?" is one question, and it belongs in
|
|
1895
|
-
* one setting. `isCliProvider` is the single guard that tells the two kinds
|
|
1896
|
-
* apart.
|
|
1897
|
-
*/
|
|
1898
|
-
type AiProvider = 'google' | 'openrouter' | 'openai_compatible' | 'openai' | 'xai' | 'groq' | 'deepseek' | 'together' | 'mistral' | 'anthropic' | 'fireworks' | 'perplexity' | 'zhipu' | 'kimi' | 'cerebras' | 'deepinfra' | 'doubao' | 'qwen' | 'hunyuan' | 'baichuan' | 'minimax' | 'yi' | 'stepfun' | 'siliconflow' | 'cohere' | 'novita' | 'claude-code' | 'codex' | 'opencode';
|
|
1899
|
-
interface IConfig {
|
|
1900
|
-
aiProvider: AiProvider;
|
|
1901
|
-
apiKey: string;
|
|
1902
|
-
planningModel: string;
|
|
1903
|
-
enabledRunners: string[];
|
|
1904
|
-
maxParallelSessions: number;
|
|
1905
|
-
researchEnabled: boolean;
|
|
1906
|
-
researchMaxSteps: number;
|
|
1907
|
-
researchMaxFileSize: number;
|
|
1908
|
-
openAiBaseUrl: string;
|
|
1909
|
-
openAiApiKey: string;
|
|
1910
|
-
/** Provider keys + base URL the ModelResolver uses to fetch the picker catalogs. */
|
|
1911
|
-
openrouterKey: string;
|
|
1912
|
-
geminiKey: string;
|
|
1913
|
-
geminiBaseUrl?: string;
|
|
1914
|
-
/** Base URL + API key for a user-provided OpenAI-compatible endpoint (ollama, vLLM, LM Studio, etc.). */
|
|
1915
|
-
openaiCompatibleBaseUrl: string;
|
|
1916
|
-
openaiCompatibleApiKey: string;
|
|
1917
|
-
orchestratorModel: string;
|
|
1918
|
-
geminiModel: string;
|
|
1919
|
-
/** Model for research subagents (issue #34); defaults to the (cheap) planner model. */
|
|
1920
|
-
researchSubagentModel: string;
|
|
1921
|
-
/**
|
|
1922
|
-
* Thinking effort / model variant for a harness planner (ADR-0009), chosen
|
|
1923
|
-
* from the agent's own discovered variants. Separate from the per-task
|
|
1924
|
-
* efforts the plan carries — this one is the planner's own dial.
|
|
1925
|
-
*/
|
|
1926
|
-
plannerThinkingEffort?: string;
|
|
1927
|
-
planMapEnabled: boolean;
|
|
1928
|
-
autonomousMode: boolean;
|
|
1929
|
-
/**
|
|
1930
|
-
* Run each AI task in its own git worktree and integrate the results on a
|
|
1931
|
-
* per-run branch (ADR-0013). Non-git workspaces fall back to the shared
|
|
1932
|
-
* workspace root regardless of this flag.
|
|
1933
|
-
*/
|
|
1934
|
-
worktreeIsolation: boolean;
|
|
1935
|
-
/**
|
|
1936
|
-
* Shell command run in a fresh task worktree instead of symlinking ignored
|
|
1937
|
-
* artifacts from the main worktree — for repos where sharing `node_modules`
|
|
1938
|
-
* or a virtualenv is wrong.
|
|
1939
|
-
*/
|
|
1940
|
-
worktreeSetupCommand?: string;
|
|
1941
|
-
/**
|
|
1942
|
-
* Repositories of the repo group listed by hand, relative to the workspace
|
|
1943
|
-
* (ADR-0014). When non-empty the group is exactly these, replacing the
|
|
1944
|
-
* repositories auto-detected directly inside the folder; needed for ones
|
|
1945
|
-
* deeper than that, which are never auto-detected.
|
|
1946
|
-
*/
|
|
1947
|
-
workspaceRepos: string[];
|
|
1948
|
-
/**
|
|
1949
|
-
* Extra paths or globs, relative to each repo root, linked from the real repo
|
|
1950
|
-
* into its task worktree where they exist — gitignored local state such as
|
|
1951
|
-
* `*.tfstate` that a task must use, not a copy of.
|
|
1952
|
-
*/
|
|
1953
|
-
worktreeLinks: string[];
|
|
1954
|
-
/**
|
|
1955
|
-
* How many conflict repairs one task may go through before its conflict is
|
|
1956
|
-
* left for a person (ADR-0015); 0 turns repair off.
|
|
1957
|
-
*/
|
|
1958
|
-
conflictRepairAttempts: number;
|
|
1959
|
-
/**
|
|
1960
|
-
* What to do when planner research reaches outside its default envelope — an
|
|
1961
|
-
* out-of-workspace path, or a shell command beyond the auto-allowed read-only
|
|
1962
|
-
* set. `ask` prompts the user (and denies where no surface can prompt, such as
|
|
1963
|
-
* headless runs); `allow` and `deny` skip the prompt entirely.
|
|
1964
|
-
*/
|
|
1965
|
-
approvalMode: ApprovalMode;
|
|
1966
|
-
/** Scopes granted up front, so CI and power users never see a prompt. Trailing `*` matches by prefix. */
|
|
1967
|
-
approvalPreApproved: string[];
|
|
1968
|
-
/** Get the base URL for an OpenAI-compatible provider. */
|
|
1969
|
-
getProviderBaseUrl(provider: AiProvider): string;
|
|
1970
|
-
/** Get the API key for a provider. */
|
|
1971
|
-
getProviderApiKey(provider: AiProvider): string;
|
|
1972
|
-
/**
|
|
1973
|
-
* Push the canonical provider routing lists in (sole producer: ModelResolver).
|
|
1974
|
-
* Config consumes them when resolving a chosen model id to its serving API.
|
|
1975
|
-
*/
|
|
1976
|
-
setProviderModelLists(lists: ProviderModelLists): void;
|
|
1977
|
-
}
|
|
1978
|
-
declare function enabledRunners(cfg: IConfig): RunnerId[];
|
|
1979
|
-
|
|
1980
|
-
/** True when a name belongs to a built-in runner and is therefore not installable. */
|
|
1981
|
-
declare function isReservedRunnerName(name: string): boolean;
|
|
1982
|
-
/** Clone seam: takes a validated URL and a destination, or throws. */
|
|
1983
|
-
type PluginCloneFn = (url: string, destDir: string) => void;
|
|
1984
|
-
declare class RunnerRegistry {
|
|
1985
|
-
private plugins;
|
|
1986
|
-
private store;
|
|
1987
|
-
private clone;
|
|
1988
|
-
constructor(store?: IPluginStore, clone?: PluginCloneFn);
|
|
1989
|
-
private loadBuiltins;
|
|
1990
|
-
loadUserPlugins(): void;
|
|
1991
|
-
get(id: string): PluginEntry | undefined;
|
|
1992
|
-
getManifest(id: string): RunnerPluginManifest | undefined;
|
|
1993
|
-
list(): PluginEntry[];
|
|
1994
|
-
listEnabled(config: IConfig): PluginEntry[];
|
|
1995
|
-
listEnabledIds(config: IConfig): string[];
|
|
1996
|
-
isBuiltIn(id: string): boolean;
|
|
1997
|
-
installFromPath(sourcePath: string): RunnerPluginManifest;
|
|
1998
|
-
installFromGit(url: string): RunnerPluginManifest;
|
|
1999
|
-
/**
|
|
2000
|
-
* The single destination-building step both install routes share: the name is
|
|
2001
|
-
* constrained to a plain segment and the resolved destination is asserted to
|
|
2002
|
-
* be inside the plugins directory before anything is copied.
|
|
2003
|
-
*/
|
|
2004
|
-
private registerInstalled;
|
|
2005
|
-
remove(name: string): void;
|
|
2006
|
-
createSkeleton(name: string, outputDir: string): string;
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
interface ITerminalSession {
|
|
2010
|
-
id: string;
|
|
2011
|
-
taskId: string;
|
|
2012
|
-
onOutput(callback: (text: string) => void): void;
|
|
2013
|
-
onExit(callback: (code: number) => void): void;
|
|
2014
|
-
kill(): void;
|
|
2015
|
-
getOutput(): string;
|
|
2016
|
-
write(text: string): void;
|
|
2017
|
-
/**
|
|
2018
|
-
* True when the session runs the agent as a raw-mode TUI (a real PTY for the
|
|
2019
|
-
* VS Code terminal, a tmux window). Such a surface submits an input line on
|
|
2020
|
-
* the Enter keystroke (`\r`), so a synchronized resume token terminated with
|
|
2021
|
-
* `\n` only types the line and never sends it. A line-oriented piped session
|
|
2022
|
-
* (`defaultInteractive = false`) leaves this false and accepts `\n`.
|
|
2023
|
-
*/
|
|
2024
|
-
readonly interactive?: boolean;
|
|
2025
|
-
/**
|
|
2026
|
-
* Optional transport-level control channel: PTY resize requests for a session
|
|
2027
|
-
* whose runner renders a TUI. Absent on transports without a resizable PTY
|
|
2028
|
-
* (a plain piped subprocess); surfaces must feature-detect before calling.
|
|
2029
|
-
*/
|
|
2030
|
-
writeControl?(text: string): void;
|
|
2031
|
-
}
|
|
2032
|
-
/**
|
|
2033
|
-
* How Ordewell drives a task's runner (ADR-0018): through its screen and
|
|
2034
|
-
* keyboard, or through its programmatic protocol.
|
|
2035
|
-
*/
|
|
2036
|
-
type RunnerTransport = 'terminal' | 'structured';
|
|
2037
|
-
declare function isRunnerTransport(value: unknown): value is RunnerTransport;
|
|
2038
|
-
/** How a structured turn ended. `failed` carries the agent's own words in the preceding `error` event. */
|
|
2039
|
-
type StructuredTurnEnd = 'completed' | 'interrupted' | 'failed';
|
|
2040
|
-
/**
|
|
2041
|
-
* One normalized event from a structured task (ADR-0018, O1b): the adapter's
|
|
2042
|
-
* events, with the turn made explicit at both ends and the message queue (M1)
|
|
2043
|
-
* alongside. Subagent work carries its `subagentId`. The source for the
|
|
2044
|
-
* full-fidelity task log, never for verdicts.
|
|
2045
|
-
*/
|
|
2046
|
-
type StructuredEvent = Exclude<AgentEvent, {
|
|
2047
|
-
type: 'turn_end' | 'permission_cancelled' | 'message_delivered' | 'message_dropped';
|
|
2048
|
-
}>
|
|
2049
|
-
/**
|
|
2050
|
-
* `text` is the user message the turn answers; `messageId` is set when it had
|
|
2051
|
-
* waited in the queue, `forced` when it was force sent (ADR-0023, Q2).
|
|
2052
|
-
*/
|
|
2053
|
-
| {
|
|
2054
|
-
type: 'turn_start';
|
|
2055
|
-
text: string;
|
|
2056
|
-
messageId?: string;
|
|
2057
|
-
forced?: boolean;
|
|
2058
|
-
} | {
|
|
2059
|
-
type: 'turn_end';
|
|
2060
|
-
reason: StructuredTurnEnd;
|
|
2061
|
-
}
|
|
2062
|
-
/**
|
|
2063
|
-
* Waiting in Ordewell's queue — again, for a message the runner was handed
|
|
2064
|
-
* and let go of (ADR-0023, D4). `forced` puts it ahead of the rest, behind
|
|
2065
|
-
* earlier forced ones (F2); a message already listed moves there.
|
|
2066
|
-
*/
|
|
2067
|
-
| {
|
|
2068
|
-
type: 'message_queued';
|
|
2069
|
-
messageId: string;
|
|
2070
|
-
text: string;
|
|
2071
|
-
forced?: boolean;
|
|
2072
|
-
} | {
|
|
2073
|
-
type: 'message_removed';
|
|
2074
|
-
messageId: string;
|
|
2075
|
-
} | {
|
|
2076
|
-
type: 'message_undelivered';
|
|
2077
|
-
messageId: string;
|
|
2078
|
-
text: string;
|
|
2079
|
-
}
|
|
2080
|
-
/** The runner accepted the message into its running turn; it can no longer be taken back (ADR-0023, D3). */
|
|
2081
|
-
| {
|
|
2082
|
-
type: 'message_handed_over';
|
|
2083
|
-
messageId: string;
|
|
2084
|
-
}
|
|
2085
|
-
/** The model read the message inside the running turn, at this point of it (ADR-0023, Q2). */
|
|
2086
|
-
| {
|
|
2087
|
-
type: 'message_delivered';
|
|
2088
|
-
messageId: string;
|
|
2089
|
-
text: string;
|
|
2090
|
-
}
|
|
2091
|
-
/** An open `permission_request` was answered, by whoever answered it (ADR-0018, A1). */
|
|
2092
|
-
| {
|
|
2093
|
-
type: 'permission_decided';
|
|
2094
|
-
id: string;
|
|
2095
|
-
decision: ApprovalDecision;
|
|
2096
|
-
}
|
|
2097
|
-
/** An open `permission_request` can no longer be answered: the runner withdrew it, or its process is gone. */
|
|
2098
|
-
| {
|
|
2099
|
-
type: 'permission_withdrawn';
|
|
2100
|
-
id: string;
|
|
2101
|
-
};
|
|
2102
|
-
interface QueuedTaskMessage {
|
|
2103
|
-
id: string;
|
|
2104
|
-
text: string;
|
|
2105
|
-
/** The runner has it and owes a delivery, so it cannot be taken back (ADR-0023, Q1). */
|
|
2106
|
-
handedOver?: boolean;
|
|
2107
|
-
/** Force sent: it goes out as soon as the running turn is interrupted (ADR-0023, F1). */
|
|
2108
|
-
forced?: boolean;
|
|
2109
|
-
}
|
|
2110
|
-
/**
|
|
2111
|
-
* What a session driven over its runner's protocol can do that a terminal
|
|
2112
|
-
* cannot (ADR-0018, S2). Optional: callers feature-detect it with
|
|
2113
|
-
* {@link isStructuredSession}, and code that does not look behaves as it did.
|
|
2114
|
-
*/
|
|
2115
|
-
interface StructuredSessionCapability {
|
|
2116
|
-
readonly transport: 'structured';
|
|
2117
|
-
/** `working` while a turn runs; `idle` between turns, waiting for a message. */
|
|
2118
|
-
turnState(): 'working' | 'idle';
|
|
2119
|
-
onTurnEnd(listener: (reason: StructuredTurnEnd) => void): void;
|
|
2120
|
-
onEvent(listener: (event: StructuredEvent) => void): void;
|
|
2121
|
-
/**
|
|
2122
|
-
* Send a user message: at once when idle; while a turn runs, handed to the
|
|
2123
|
-
* runner for its next step where it can take one, otherwise queued until
|
|
2124
|
-
* the turn ends (ADR-0023). Returns its id, for {@link removeQueued}.
|
|
2125
|
-
*/
|
|
2126
|
-
sendMessage(text: string): string;
|
|
2127
|
-
/**
|
|
2128
|
-
* Force send (ADR-0023, F1–F3): interrupt the running turn, as
|
|
2129
|
-
* {@link interrupt} does, and deliver this message as the turn that
|
|
2130
|
-
* replaces it, ahead of anything still queued. The task does not wait for
|
|
2131
|
-
* input in between. With no turn running it is {@link sendMessage}.
|
|
2132
|
-
*/
|
|
2133
|
-
forceSend(text: string): string;
|
|
2134
|
-
/** Force send a message still queued, by id. False once it was handed over or delivered. */
|
|
2135
|
-
forceSendQueued(id: string): boolean;
|
|
2136
|
-
/** Take a message back before the runner has it. False once it was handed over or delivered. */
|
|
2137
|
-
removeQueued(id: string): boolean;
|
|
2138
|
-
/** Messages not yet delivered, oldest first: queued ones, and ones handed over that the runner owes. */
|
|
2139
|
-
queued(): QueuedTaskMessage[];
|
|
2140
|
-
/**
|
|
2141
|
-
* Stop the running turn, keeping the session: a soft interrupt first, then —
|
|
2142
|
-
* if the runner does not answer in time — kill and resume. Either way the
|
|
2143
|
-
* turn ends `interrupted`. Resolves once it has.
|
|
2144
|
-
*/
|
|
2145
|
-
interrupt(): Promise<void>;
|
|
2146
|
-
/** The runner's own session id once announced — what a continue resumes (ADR-0018, K1). */
|
|
2147
|
-
nativeSessionId(): string | null;
|
|
2148
|
-
/**
|
|
2149
|
-
* Answer a `permission_request` this session emitted, by its event id. False
|
|
2150
|
-
* when it is no longer open — answered, withdrawn, or never asked.
|
|
2151
|
-
*/
|
|
2152
|
-
answerPermission(id: string, decision: ApprovalDecision): boolean;
|
|
2153
|
-
/**
|
|
2154
|
-
* The runner's `task_complete` calls on this attempt's token (ADR-0022):
|
|
2155
|
-
* completion evidence beside the marker, for `VerdictEngine` to weigh.
|
|
2156
|
-
*/
|
|
2157
|
-
onTaskComplete(listener: (report: TaskCompleteArgs) => void): void;
|
|
2158
|
-
/**
|
|
2159
|
-
* Answers the runner's `checkpoint` calls (ADR-0022, V5): the call stays
|
|
2160
|
-
* open until the returned promise settles, and its result is the answer.
|
|
2161
|
-
* `signal` aborts when the caller goes away.
|
|
2162
|
-
*/
|
|
2163
|
-
onToolCheckpoint(handler: (question: string, signal: AbortSignal) => Promise<CheckpointAnswer>): void;
|
|
2164
|
-
}
|
|
2165
|
-
declare function isStructuredSession(session: ITerminalSession): session is ITerminalSession & StructuredSessionCapability;
|
|
2166
|
-
|
|
2167
|
-
interface ITerminalRunner {
|
|
2168
|
-
spawn(opts: {
|
|
2169
|
-
taskId: string;
|
|
2170
|
-
runner: string;
|
|
2171
|
-
prompt: string;
|
|
2172
|
-
modelId?: string;
|
|
2173
|
-
thinkingEffort?: string;
|
|
2174
|
-
modelVariants?: string[];
|
|
2175
|
-
mode?: string;
|
|
2176
|
-
headless?: boolean;
|
|
2177
|
-
cwd: string;
|
|
2178
|
-
registry?: RunnerRegistry;
|
|
2179
|
-
/** Task order and title — surfaces use these to label task_started/output events. */
|
|
2180
|
-
order?: number;
|
|
2181
|
-
title?: string;
|
|
2182
|
-
/**
|
|
2183
|
-
* The owning plan session. Task ids are only unique within one plan, so
|
|
2184
|
-
* transports that key OS resources by task (tmux windows, log files) need
|
|
2185
|
-
* this to keep two plans' identically named tasks apart.
|
|
2186
|
-
*/
|
|
2187
|
-
planSessionId?: string;
|
|
2188
|
-
/**
|
|
2189
|
-
* The workspace's own variables (ADR-0016), under the runner's: a
|
|
2190
|
-
* manifest's env still wins over them.
|
|
2191
|
-
*/
|
|
2192
|
-
env?: Record<string, string>;
|
|
2193
|
-
/**
|
|
2194
|
-
* The transport the plan asks for (ADR-0018, S1). A router decides per
|
|
2195
|
-
* task whether the runner can honour it; any other runner ignores it.
|
|
2196
|
-
*/
|
|
2197
|
-
transport?: RunnerTransport;
|
|
2198
|
-
/**
|
|
2199
|
-
* The runner's own session to continue in (ADR-0018, K1). Only the
|
|
2200
|
-
* structured transport can honour it; a terminal session comes back fresh,
|
|
2201
|
-
* which is why a continue refuses one.
|
|
2202
|
-
*/
|
|
2203
|
-
resumeSessionId?: string;
|
|
2204
|
-
/**
|
|
2205
|
-
* Which run of the task this is, from 1. A structured runner binds the
|
|
2206
|
-
* attempt's MCP token to it (ADR-0022, A2).
|
|
2207
|
-
*/
|
|
2208
|
-
attempt?: number;
|
|
2209
|
-
}): Promise<ITerminalSession>;
|
|
2210
|
-
stop(sessionId: string): void;
|
|
2211
|
-
stopAll(): void;
|
|
2212
|
-
activeCount: number;
|
|
2213
|
-
}
|
|
2214
|
-
|
|
2215
|
-
interface UserStep {
|
|
2216
|
-
order: number;
|
|
2217
|
-
instruction: string;
|
|
2218
|
-
completed: boolean;
|
|
2219
|
-
}
|
|
2220
|
-
/** One deterministic signal gathered while verifying a completed task. */
|
|
2221
|
-
interface VerificationCheck {
|
|
2222
|
-
name: 'exit_code' | 'completion_marker' | 'task_complete' | 'manual';
|
|
2223
|
-
passed: boolean;
|
|
2224
|
-
/** A check that did not apply. Skipped checks don't affect the verdict. */
|
|
2225
|
-
skipped: boolean;
|
|
2226
|
-
detail: string;
|
|
2227
|
-
}
|
|
2228
|
-
/** Evidence-based verdict for a completed task. Single end-to-end outcome produced by verification. */
|
|
2229
|
-
interface Verdict {
|
|
2230
|
-
outcome: 'pass' | 'fail';
|
|
2231
|
-
reason: string;
|
|
2232
|
-
checks: VerificationCheck[];
|
|
2233
|
-
decidedAt: string;
|
|
2234
|
-
}
|
|
2235
|
-
interface TaskOutputSummary {
|
|
2236
|
-
reviewReason: string;
|
|
2237
|
-
logTail: string;
|
|
2238
|
-
capturedAt: string;
|
|
2239
|
-
}
|
|
2240
|
-
type TaskType = 'ai' | 'user';
|
|
2241
|
-
type TaskStatus = 'pending' | 'approved' | 'in_progress' | 'completed' | 'failed' | 'blocked' | 'awaiting_user';
|
|
2242
|
-
type TaskMode = string;
|
|
2243
|
-
/**
|
|
2244
|
-
* Why an `awaiting_user` task waits (ADR-0018, W1): a structured turn that
|
|
2245
|
-
* ended without the done marker, a checkpoint question, work that did not
|
|
2246
|
-
* land, or an ops task that changed tracked files (ADR-0020). Saved, so no
|
|
2247
|
-
* surface has to guess it from whether an attempt is live.
|
|
2248
|
-
*/
|
|
2249
|
-
type AwaitingReason = 'input' | 'checkpoint' | 'conflict' | 'files-changed';
|
|
2250
|
-
declare function isAwaitingReason(value: unknown): value is AwaitingReason;
|
|
2251
|
-
interface TaskModelAssignment {
|
|
2252
|
-
modelId: string;
|
|
2253
|
-
modelLabel: string;
|
|
2254
|
-
thinkingEffort?: string;
|
|
2255
|
-
/**
|
|
2256
|
-
* All variant ids the model offered when this assignment was made. Carried
|
|
2257
|
-
* on the assignment because runners need it at spawn time (opencode's TUI
|
|
2258
|
-
* only honors an assigned variant when the others are config-disabled) and
|
|
2259
|
-
* the discovery catalog isn't available there.
|
|
2260
|
-
*/
|
|
2261
|
-
availableVariants?: string[];
|
|
2262
|
-
}
|
|
2263
|
-
type RunnerId = string;
|
|
2264
|
-
/**
|
|
2265
|
-
* How a task's latest attempt was driven (ADR-0018). Recorded only when its
|
|
2266
|
-
* plan asked for the structured transport, so a terminal plan's tasks carry
|
|
2267
|
-
* nothing new.
|
|
2268
|
-
*/
|
|
2269
|
-
interface TaskTransport {
|
|
2270
|
-
kind: RunnerTransport;
|
|
2271
|
-
/** Why a plan that asked for structured ran this task on the terminal — never a silent downgrade (S3). */
|
|
2272
|
-
fallback?: string;
|
|
2273
|
-
/** The runner's own session id of a structured attempt, once it ends: what a continue resumes (K1). */
|
|
2274
|
-
nativeSessionId?: string;
|
|
2275
|
-
}
|
|
2276
|
-
interface Task {
|
|
2277
|
-
id: string;
|
|
2278
|
-
order: number;
|
|
2279
|
-
title: string;
|
|
2280
|
-
description: string;
|
|
2281
|
-
type: TaskType;
|
|
2282
|
-
status: TaskStatus;
|
|
2283
|
-
dependencies: string[];
|
|
2284
|
-
prompt?: string;
|
|
2285
|
-
userSteps?: UserStep[];
|
|
2286
|
-
subtasks: Task[];
|
|
2287
|
-
verdict?: Verdict;
|
|
2288
|
-
outputSummary?: TaskOutputSummary;
|
|
2289
|
-
assignedModel?: TaskModelAssignment;
|
|
2290
|
-
assignedRunner: RunnerId;
|
|
2291
|
-
thinkingEffort?: string;
|
|
2292
|
-
taskMode?: TaskMode;
|
|
2293
|
-
completionMarker: string;
|
|
2294
|
-
autonomy?: 'AFK' | 'HITL';
|
|
2295
|
-
sliceType?: 'HITL' | 'AFK';
|
|
2296
|
-
userStoriesCovered?: string[];
|
|
2297
|
-
transport?: TaskTransport;
|
|
2298
|
-
/** Set only while `status` is `awaiting_user`, and not always then — a usage-limit pause has none. */
|
|
2299
|
-
awaitingReason?: AwaitingReason;
|
|
2300
|
-
/**
|
|
2301
|
-
* An ops task (ADR-0020): it changes no repository files, runs at the
|
|
2302
|
-
* workspace root rather than in a worktree, and waits for the change tasks
|
|
2303
|
-
* it depends on to be merged into the user's branch. Top-level AI tasks
|
|
2304
|
-
* only; absent means a change task.
|
|
2305
|
-
*/
|
|
2306
|
-
ops?: boolean;
|
|
2307
|
-
/**
|
|
2308
|
-
* The dependencies, by title, whose work was not merged into the user's
|
|
2309
|
-
* branch when the user force-started this task past its merge gate
|
|
2310
|
-
* (ADR-0020). Cleared when the task is retried.
|
|
2311
|
-
*/
|
|
2312
|
-
forcedPastGate?: string[];
|
|
2313
|
-
}
|
|
2314
|
-
interface DiscoveredMode {
|
|
2315
|
-
id: string;
|
|
2316
|
-
label: string;
|
|
2317
|
-
description: string;
|
|
2318
|
-
}
|
|
2319
|
-
type ResearchToolType = 'read_file' | 'read_files' | 'glob' | 'grep' | 'find_symbol' | 'list_dir' | 'bash' | 'fetch' | 'web_search' | 'spawn_research_agent'
|
|
2320
|
-
/**
|
|
2321
|
-
* A tool belonging to a harness planner's own toolbox (ADR-0009) that has no
|
|
2322
|
-
* Ordewell equivalent — Edit, WebFetch, TodoWrite, whatever a coding agent
|
|
2323
|
-
* ships next. The real name travels in `toolLabel` rather than being
|
|
2324
|
-
* relabelled as a tool it is not; the union stays closed so the
|
|
2325
|
-
* exhaustiveness checks in every surface's icon/label switch survive.
|
|
2326
|
-
*/
|
|
2327
|
-
| 'agent_tool';
|
|
2328
|
-
/**
|
|
2329
|
-
* What happened when a research tool call ran, for honest per-surface
|
|
2330
|
-
* rendering. The broadcast seam carries this on every `research_step_done` so
|
|
2331
|
-
* surfaces do not have to pattern-match refusal text to tell a refused `rm`
|
|
2332
|
-
* from a successful `rm` — the old render path flipped a `✓` for both.
|
|
2333
|
-
*/
|
|
2334
|
-
type ResearchStepOutcome = 'success' | 'failure' | 'refused' | 'denied' | 'not_executed';
|
|
2335
|
-
interface ResearchStep {
|
|
2336
|
-
id: string;
|
|
2337
|
-
tool: ResearchToolType;
|
|
2338
|
-
/** The tool's own name when it came from a harness planner — always set for `agent_tool`. */
|
|
2339
|
-
toolLabel?: string;
|
|
2340
|
-
args: string;
|
|
2341
|
-
result: string;
|
|
2342
|
-
success: boolean;
|
|
2343
|
-
outcome: ResearchStepOutcome;
|
|
2344
|
-
/** The model's tool_call id, so a surface can match `tool_result` to the
|
|
2345
|
-
* pending `tool_call` it announced — robust under parallel same-tool rounds. */
|
|
2346
|
-
toolCallId?: string;
|
|
2347
|
-
/** The research subagent that ran the call, so a reload regroups it under that subagent. */
|
|
2348
|
-
subagentId?: string;
|
|
2349
|
-
timestamp: string;
|
|
2350
|
-
thinkingText?: string;
|
|
2351
|
-
}
|
|
2352
|
-
interface UserPromptEntry {
|
|
2353
|
-
id: string;
|
|
2354
|
-
type: 'user_prompt' | 'system';
|
|
2355
|
-
content: string;
|
|
2356
|
-
timestamp: string;
|
|
2357
|
-
}
|
|
2358
|
-
type SubagentOutcome = 'done' | 'failed' | 'stopped';
|
|
2359
|
-
/**
|
|
2360
|
-
* One subagent's whole run, logged when it finishes: what it was asked, how it
|
|
2361
|
-
* ended and what it reported. Its steps stay separate entries carrying the same
|
|
2362
|
-
* `subagentId`, so an older reader that knows only steps still shows them.
|
|
2363
|
-
*/
|
|
2364
|
-
interface SubagentLogEntry {
|
|
2365
|
-
id: string;
|
|
2366
|
-
type: 'subagent';
|
|
2367
|
-
subagentId: string;
|
|
2368
|
-
brief: string;
|
|
2369
|
-
model?: string;
|
|
2370
|
-
outcome: SubagentOutcome;
|
|
2371
|
-
digest: string;
|
|
2372
|
-
usage?: UsageTotals;
|
|
2373
|
-
timestamp: string;
|
|
2374
|
-
}
|
|
2375
|
-
type ResearchLogEntry = ResearchStep | UserPromptEntry | SubagentLogEntry;
|
|
2376
|
-
interface ResearchProgress {
|
|
2377
|
-
type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted' | 'liveness' | 'text_delta' | 'text_retracted' | 'usage' | 'subagent_started' | 'subagent_finished';
|
|
2378
|
-
/** Minted by whoever runs the turn and passed through untouched; absent outside a turn. */
|
|
2379
|
-
turnId?: string;
|
|
2380
|
-
/** One continuous run of model text — text before a tool call is its own segment. */
|
|
2381
|
-
segmentId?: string;
|
|
2382
|
-
text?: string;
|
|
2383
|
-
tool?: string;
|
|
2384
|
-
/** Harness planners (ADR-0009): the agent's own name for a tool Ordewell has no member for. */
|
|
2385
|
-
toolLabel?: string;
|
|
2386
|
-
toolArgs?: string;
|
|
2387
|
-
toolResult?: string;
|
|
2388
|
-
planToken?: string;
|
|
2389
|
-
step?: ResearchStep;
|
|
2390
|
-
/** The model's tool_call id, threaded on tool_call and tool_result so a
|
|
2391
|
-
* surface can match the result to its pending call — robust under parallel
|
|
2392
|
-
* same-tool rounds where LIFO-by-name matching mislabels summaries. */
|
|
2393
|
-
toolCallId?: string;
|
|
2394
|
-
/** Present when this event originates from (or reports on) one spawned research subagent (issue #34). */
|
|
2395
|
-
subagentId?: string;
|
|
2396
|
-
record?: UsageRecord;
|
|
2397
|
-
brief?: string;
|
|
2398
|
-
model?: string;
|
|
2399
|
-
outcome?: SubagentOutcome;
|
|
2400
|
-
digest?: string;
|
|
2401
|
-
usage?: UsageTotals;
|
|
2402
|
-
}
|
|
2403
|
-
interface ThinkingBlock {
|
|
2404
|
-
id: string;
|
|
2405
|
-
text: string;
|
|
2406
|
-
}
|
|
2407
|
-
interface StreamThinkingEvent {
|
|
2408
|
-
type: 'thinking';
|
|
2409
|
-
block: ThinkingBlock;
|
|
2410
|
-
}
|
|
2411
|
-
interface StreamStepEvent {
|
|
2412
|
-
type: 'step';
|
|
2413
|
-
step: ResearchStep;
|
|
2414
|
-
}
|
|
2415
|
-
type StreamEvent = StreamThinkingEvent | StreamStepEvent;
|
|
2416
|
-
interface DiscoveredModel {
|
|
2417
|
-
modelId: string;
|
|
2418
|
-
modelLabel: string;
|
|
2419
|
-
runnerProvider?: string;
|
|
2420
|
-
/**
|
|
2421
|
-
* Human-facing provider name as the runner itself reports it (e.g.
|
|
2422
|
-
* "OpenCode Zen" for `runnerProvider: 'opencode'`). Populated from the
|
|
2423
|
-
* runner's own provider catalog when available; when absent the UI derives a
|
|
2424
|
-
* label from `runnerProvider` by title-casing.
|
|
2425
|
-
*/
|
|
2426
|
-
runnerProviderLabel?: string;
|
|
2427
|
-
/**
|
|
2428
|
-
* The runner whose catalog listed this model. Stamped once, at the single
|
|
2429
|
-
* `ModelDiscovery.discover` choke point, so a flat cross-runner list can
|
|
2430
|
-
* still say where each entry came from — `runnerProvider` alone cannot:
|
|
2431
|
-
* OpenCode reports most of its catalog as `openrouter`, which names the
|
|
2432
|
-
* serving backend, not the agent Ordewell would spawn.
|
|
2433
|
-
*/
|
|
2434
|
-
runnerId?: string;
|
|
2435
|
-
/** The runner's display name (`OpenCode`), from its manifest. */
|
|
2436
|
-
runnerLabel?: string;
|
|
2437
|
-
variants: {
|
|
2438
|
-
id: string;
|
|
2439
|
-
label: string;
|
|
2440
|
-
}[];
|
|
2441
|
-
/**
|
|
2442
|
-
* The model's context window when the runner or catalog reports it (#49).
|
|
2443
|
-
* Read by the planner-model lookup so context fill can be shown; absent when
|
|
2444
|
-
* unknown rather than defaulted to zero.
|
|
2445
|
-
*/
|
|
2446
|
-
contextWindow?: number;
|
|
2447
|
-
}
|
|
2448
|
-
type PlanStatus = 'draft' | 'approved' | 'rejected' | 'running' | 'completed';
|
|
2449
|
-
/**
|
|
2450
|
-
* One entry of the planner's persisted dialogue (ADR-0002). The single source
|
|
2451
|
-
* of truth for both UI redisplay and conversational context. Tool-call results
|
|
2452
|
-
* are NOT stored here — they live in the AI service's tool-use history;
|
|
2453
|
-
* `researchLog` remains the persisted tool trace for the UI.
|
|
2454
|
-
*/
|
|
2455
|
-
interface ConversationMessage {
|
|
2456
|
-
role: 'user' | 'assistant';
|
|
2457
|
-
content: string;
|
|
2458
|
-
timestamp: string;
|
|
2459
|
-
/**
|
|
2460
|
-
* Timeline marker: 'plan_generated' records the point in the dialogue where
|
|
2461
|
-
* the plan was committed (the UI anchors the plan card there on restore);
|
|
2462
|
-
* 'system' is a host-injected notice; 'compaction' is the summary a
|
|
2463
|
-
* user-triggered compaction left in place of the earlier messages — always
|
|
2464
|
-
* the transcript's first entry. Absent for ordinary chat turns, so
|
|
2465
|
-
* sessions saved before markers existed degrade gracefully.
|
|
2466
|
-
*/
|
|
2467
|
-
kind?: 'plan_generated' | 'system' | 'compaction';
|
|
2468
|
-
}
|
|
2469
|
-
interface QueuedMessage {
|
|
2470
|
-
id: string;
|
|
2471
|
-
text: string;
|
|
2472
|
-
timestamp: string;
|
|
2473
|
-
}
|
|
2474
|
-
interface PlanModificationWarnings {
|
|
2475
|
-
deletedCompleted: string[];
|
|
2476
|
-
changedCompleted: string[];
|
|
2477
|
-
deletedInProgress: string[];
|
|
2478
|
-
modifiedInProgress: string[];
|
|
2479
|
-
brokenDependencies: string[];
|
|
2480
|
-
}
|
|
2481
|
-
declare function emptyWarnings(): PlanModificationWarnings;
|
|
2482
|
-
interface LegacyPlanState {
|
|
2483
|
-
tasks: Task[];
|
|
2484
|
-
generatedAt: string;
|
|
2485
|
-
status: PlanStatus;
|
|
2486
|
-
runners: RunnerId[];
|
|
2487
|
-
lastUpdated: string;
|
|
2488
|
-
researchLog?: ResearchLogEntry[];
|
|
2489
|
-
/** The planner dialogue — user messages and assistant messages, in order (ADR-0002). */
|
|
2490
|
-
conversationHistory?: ConversationMessage[];
|
|
2491
|
-
/** Full markdown PRD once written by the planner (PRD mode), also saved to .scratch/<slug>/PRD.md. */
|
|
2492
|
-
prdMarkdown?: string;
|
|
2493
|
-
/** Follow-ups queued while tasks execute — applied as plan modifications between batches. */
|
|
2494
|
-
queuedMessages?: QueuedMessage[];
|
|
2495
|
-
/**
|
|
2496
|
-
* The plan's isolation run (ADR-0013), written from the orchestrator at
|
|
2497
|
-
* persist time and read back only when a saved plan is adopted. It names
|
|
2498
|
-
* branches and worktrees that belong to this plan alone: a fork of the plan
|
|
2499
|
-
* must leave it behind rather than share it.
|
|
2500
|
-
*/
|
|
2501
|
-
isolation?: PlanIsolation;
|
|
2502
|
-
/**
|
|
2503
|
-
* The `runnerTransport` setting as the plan's latest run copied it when it
|
|
2504
|
-
* started (ADR-0018, S1): the plan, not the live setting, says what runs.
|
|
2505
|
-
*/
|
|
2506
|
-
runnerTransport?: RunnerTransport;
|
|
2507
|
-
/** Kept so a reopened session shows the same token line (#49). */
|
|
2508
|
-
plannerUsage?: PlannerUsage;
|
|
2509
|
-
}
|
|
2510
|
-
interface Message {
|
|
2511
|
-
id: string;
|
|
2512
|
-
role: 'user' | 'planner' | 'system';
|
|
2513
|
-
content: string;
|
|
2514
|
-
timestamp: number;
|
|
2515
|
-
}
|
|
2516
|
-
interface TaskSnapshot extends Task {
|
|
2517
|
-
completedAt: number;
|
|
2518
|
-
verdict?: Verdict;
|
|
2519
|
-
retryCount: number;
|
|
2520
|
-
finalized: boolean;
|
|
2521
|
-
}
|
|
2522
|
-
type PlanState = {
|
|
2523
|
-
phase: 'planning';
|
|
2524
|
-
history: Message[];
|
|
2525
|
-
message: string;
|
|
2526
|
-
pendingTasks: Task[];
|
|
2527
|
-
} | {
|
|
2528
|
-
phase: 'executing';
|
|
2529
|
-
history: Message[];
|
|
2530
|
-
message: string;
|
|
2531
|
-
executionLog: TaskSnapshot[];
|
|
2532
|
-
pendingTasks: Task[];
|
|
2533
|
-
goal: string;
|
|
2534
|
-
runners: string[];
|
|
2535
|
-
status: PlanStatus;
|
|
2536
|
-
};
|
|
2537
|
-
declare function migratePlanState(raw: unknown): PlanState;
|
|
2538
|
-
declare function migrateLegacyPlan(legacy: LegacyPlanState): PlanState;
|
|
2539
|
-
|
|
2540
|
-
declare function createTask(overrides?: Partial<Task>): Task;
|
|
2541
|
-
/**
|
|
2542
|
-
* The `ops` flag as a task stores it (ADR-0020), however it arrived. Only a
|
|
2543
|
-
* literal `true` on an AI task marks an ops task: the planner's JSON is
|
|
2544
|
-
* untyped, and a quoted "true" read as ops would move a task out of its
|
|
2545
|
-
* worktree. Anything else is a change task, stored absent so saved plans need
|
|
2546
|
-
* no migration. Whether the task may be ops at all — a subtask may not — is
|
|
2547
|
-
* for whoever knows where it sits: parsing drops the flag, an edit refuses it.
|
|
2548
|
-
*/
|
|
2549
|
-
declare function opsFlag(value: unknown, type?: Task['type']): true | undefined;
|
|
2550
|
-
/**
|
|
2551
|
-
* The flag of a task a merge or a split makes from others: what the edit
|
|
2552
|
-
* chose, else ops only when every task it came from was. A merge that takes
|
|
2553
|
-
* in a change edits files, so it is a change; a split part runs where its
|
|
2554
|
-
* task did.
|
|
2555
|
-
*/
|
|
2556
|
-
declare function inheritedOps(from: readonly Pick<Task, 'ops'>[], chosen?: boolean): boolean;
|
|
2557
|
-
/** The runner set a plan has until one is chosen: the one runner every host ships. */
|
|
2558
|
-
declare const DEFAULT_RUNNERS: readonly RunnerId[];
|
|
2559
|
-
declare function createEmptyPlan(): LegacyPlanState;
|
|
2560
|
-
declare function flattenTasks(tasks: readonly Task[]): Task[];
|
|
2561
|
-
/** A flattened task with the parent it hangs under, null for a top-level task. */
|
|
2562
|
-
interface TaskWithParent {
|
|
2563
|
-
task: Task;
|
|
2564
|
-
parent: Task | null;
|
|
2565
|
-
}
|
|
2566
|
-
declare function flattenTasksWithParents(tasks: readonly Task[]): TaskWithParent[];
|
|
2567
|
-
declare function migrateTask(task: Record<string, unknown>): Task;
|
|
2568
|
-
declare function addTaskToPlan(tasks: readonly Task[], partial: Partial<Task>): Task[];
|
|
2569
|
-
declare function removeTaskFromPlan(tasks: readonly Task[], taskId: string): Task[];
|
|
2570
|
-
declare function updateTaskInPlan(tasks: readonly Task[], taskId: string, changes: Partial<Task>): Task[];
|
|
2571
|
-
declare function renumberTasks(tasks: readonly Task[]): Task[];
|
|
2572
|
-
/**
|
|
2573
|
-
* Lay a planner-written task list over the plan it rewrites without letting it
|
|
2574
|
-
* change execution state. A planner restates tasks; it never witnessed one run,
|
|
2575
|
-
* so the status it writes is not evidence. A settled task is kept exactly as
|
|
2576
|
-
* it stands wherever the rewrite names it, and put back beside its old
|
|
2577
|
-
* neighbour where the rewrite leaves it out. Every other task keeps the status
|
|
2578
|
-
* it had; a task the rewrite adds starts pending.
|
|
2579
|
-
*/
|
|
2580
|
-
declare function keepExecutionState(current: readonly Task[], rewrite: Task[]): Task[];
|
|
2581
|
-
declare function validateModifiedPlan(original: Task[], modified: Task[]): PlanModificationWarnings;
|
|
2582
|
-
interface ActiveTaskSession {
|
|
2583
|
-
id: string;
|
|
2584
|
-
taskId: string;
|
|
2585
|
-
}
|
|
2586
|
-
interface ValidationResult {
|
|
2587
|
-
valid: boolean;
|
|
2588
|
-
errors: string[];
|
|
2589
|
-
}
|
|
2590
|
-
interface ValidationContext {
|
|
2591
|
-
executionLog: TaskSnapshot[];
|
|
2592
|
-
oldPending: Task[];
|
|
2593
|
-
newPending: Task[];
|
|
2594
|
-
activeSessions: Map<string, ActiveTaskSession>;
|
|
2595
|
-
}
|
|
2596
|
-
type ValidationCheck = (ctx: ValidationContext) => ValidationResult;
|
|
2597
|
-
declare function warningsText(w: PlanModificationWarnings): string | null;
|
|
2598
|
-
|
|
2599
|
-
export { type IsolationMergeResult as $, AbstractRunner as A, type BlockingPrompt as B, CMD_EXE_MAX_COMMAND_LINE as C, DEFAULT_RUNNERS as D, type DiscoveryCommand as E, type EditPlanArgs as F, EmbeddedNewlineError as G, ExecutableNotFoundError as H, type FetchAllProviderModelsOptions as I, HeadlessRunner as J, type HeadlessRunnerDeps as K, HeadlessSession as L, type IApproval as M, type IConfig as N, type IPluginStore as O, type ITerminalRunner as P, type ITerminalSession as Q, type IWorktreeIsolation as R, type IntegrationDisposal as S, type IsolationAvailability as T, type IsolationHandoff as U, type IsolationHandoffRepo as V, type IsolationInactiveReason as W, type IsolationLandedTask as X, type IsolationLanding as Y, type IsolationMergeBlock as Z, type IsolationMergeBlockReason as _, AbstractTerminalSession as a, type ResolveContext as a$, type IsolationOutcome as a0, type IsolationPruneResult as a1, type IsolationRemoval as a2, type IsolationRepo as a3, type IsolationRun as a4, type IsolationTaskRecord as a5, type IsolationTaskRef as a6, type IsolationTaskRepo as a7, type IsolationTaskStatus as a8, type IsolationView as a9, type PlannerTokenScope as aA, type PlannerToolHandler as aB, type PlannerUsage as aC, type PluginCloneFn as aD, type PluginEntry as aE, type PluginFeatures as aF, type PluginMode as aG, type PluginModelDiscovery as aH, type PluginRunnerDef as aI, type PreparedLaunch as aJ, type PreparedTask as aK, type PreservedWork as aL, type ProviderCredentialSource as aM, type ProviderModelLists as aN, type ProviderModelsResult as aO, type PtySize as aP, type PtyWrapOptions as aQ, type QueuedMessage as aR, type QueuedTaskMessage as aS, type RefusedRemoval as aT, type RepairEvidence as aU, type RepoGroupLayout as aV, type ResearchLogEntry as aW, type ResearchProgress as aX, type ResearchStep as aY, type ResearchStepOutcome as aZ, type ResearchToolType as a_, type LaunchDeps as aa, type LaunchPlan as ab, type LegacyPlanState as ac, LineBuffer as ad, type ListModelsArgs as ae, type ListRunnersArgs as af, type McpClientConfig as ag, type McpCredential as ah, type McpTool as ai, type McpToolContext as aj, type McpToolReply as ak, type Message as al, ModelCatalog as am, type ModelShortcut as an, ORCHESTRATOR_SHORTCUTS as ao, ORDEWELL_MCP_PATH as ap, ORDEWELL_MCP_SERVER_NAME as aq, type OrchestratorOption as ar, OrdewellMcpServer as as, type OwnerOnlyFile as at, PLANNER_TOOLS as au, type PlanIsolation as av, type PlanModificationWarnings as aw, type PlanState as ax, type PlanStatus as ay, type PlannerStartOptions as az, type ActiveTaskSession as b, flattenTasksWithParents as b$, type RunnerId as b0, type RunnerInvocation as b1, type RunnerPluginManifest as b2, RunnerRegistry as b3, type RunnerSpawnOptions as b4, type RunnerTransport as b5, type RunnerVersionProbe as b6, type StreamEvent as b7, type StreamStepEvent as b8, type StreamThinkingEvent as b9, type TaskType as bA, type TaskWithParent as bB, type ThinkingBlock as bC, type TreeSnapshot as bD, type UsageLine as bE, type UsageRecord as bF, type UsageTotals as bG, type UserPromptEntry as bH, type UserStep as bI, type ValidationCheck as bJ, type ValidationContext as bK, type ValidationResult as bL, type Verdict as bM, type VerificationCheck as bN, WINDOWS_MAX_COMMAND_LINE as bO, addPlannerUsage as bP, addTaskToPlan as bQ, addUsage as bR, approvalScopes as bS, buildShellInvocation as bT, collectProviderCredentials as bU, createEmptyPlan as bV, createTask as bW, emptyWarnings as bX, enabledRunners as bY, fetchAllProviderModels as bZ, flattenTasks as b_, type StructuredEvent as ba, type StructuredSessionCapability as bb, type StructuredTurnEnd as bc, type SubagentLogEntry as bd, type SubagentOutcome as be, type SubmitPlanArgs as bf, TASK_COMPLETE_TOOL as bg, TASK_TOOLS as bh, type Task as bi, type TaskCompleteArgs as bj, type TaskIsolation as bk, type TaskIsolationState as bl, type TaskMode as bm, type TaskModeAgentAdapter as bn, TaskModeUnsupportedError as bo, type TaskModelAssignment as bp, type TaskOutputArgs as bq, type TaskOutputSummary as br, type TaskQueryArgs as bs, type TaskRunnerFlags as bt, type TaskSnapshot as bu, type TaskStartOptions as bv, type TaskStatus as bw, type TaskTokenScope as bx, type TaskToolHandler as by, type TaskTransport as bz, type AgentAdapter as c, inheritedOps as c0, isAwaitingReason as c1, isExecutableResolved as c2, isGranted as c3, isMeasured as c4, isReservedRunnerName as c5, isRunnerApproval as c6, isRunnerTransport as c7, isStructuredSession as c8, keepExecutionState as c9, knownModelId as ca, mcpClientConfig as cb, migrateLegacyPlan as cc, migratePlanState as cd, migrateTask as ce, opsFlag as cf, ownerOnlyConfigFile as cg, partedPromptUsage as ch, planDirectLaunch as ci, planShellLaunch as cj, plannerContextFill as ck, posixShellQuote as cl, removeTaskFromPlan as cm, renumberTasks as cn, resolveModelShortcut as co, resolveProvider as cp, sharedMcpServer as cq, stripAnsi as cr, toApprovalDecision as cs, toOrchestratorOptions as ct, updateTaskInPlan as cu, usageLine as cv, validateModifiedPlan as cw, warningsText as cx, windowsCommandLine as cy, wrapWithPty as cz, type AgentAdapterFactory as d, type AgentEvent as e, type AgentProcessDeps as f, type AgentStartOptions as g, type AiProvider as h, type AllProviderModels as i, type ApprovalAnswer as j, type ApprovalDecision as k, type ApprovalKind as l, type ApprovalMode as m, ApprovalPolicy as n, type ApprovalPolicyOptions as o, type ApprovalRequest as p, type ApprovalSource as q, type AwaitingReason as r, type CatalogModel as s, type CheckpointAnswer as t, type CheckpointArgs as u, CommandLineTooLongError as v, type ConversationMessage as w, DENY_ALL as x, type DiscoveredMode as y, type DiscoveredModel as z };
|