@ordewell/core 0.7.1 → 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-BnLh-_SK.d.mts → IFileSystem-D96mT-bm.d.ts} +1 -1
- package/dist/{IFileSystem-DofiRcCo.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-DkMYf4uS.d.ts → ModeResolver-DE-o2lRF.d.mts} +2 -2
- package/dist/{ModeResolver-DZzJSdIU.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-ZV5LDNCO.mjs → chunk-2M4X3VB7.mjs} +18 -6
- package/dist/chunk-2M4X3VB7.mjs.map +1 -0
- package/dist/{chunk-Y36NIASW.mjs → chunk-BLLIBCCS.mjs} +153 -264
- package/dist/chunk-BLLIBCCS.mjs.map +1 -0
- package/dist/chunk-H5O74L77.mjs +185 -0
- package/dist/chunk-H5O74L77.mjs.map +1 -0
- package/dist/{chunk-LGQNXPBX.mjs → chunk-UUU46PVQ.mjs} +72 -192
- package/dist/chunk-UUU46PVQ.mjs.map +1 -0
- package/dist/index.d.mts +1119 -930
- package/dist/index.d.ts +1119 -930
- package/dist/index.js +8361 -8849
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8197 -8614
- 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-ddZXZFP4.d.ts → parsing-CZdp2p_n.d.ts} +5 -3
- package/dist/{parsing-B6ctfF80.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 +3 -2
- package/dist/{plan-utils-CKgNtDwQ.d.mts → plan-utils-BMayhGBj.d.mts} +89 -14
- package/dist/{plan-utils--kQRzSE-.d.ts → plan-utils-Cv48fe7r.d.ts} +89 -14
- package/dist/plan-utils.d.mts +4 -4
- package/dist/plan-utils.d.ts +4 -4
- package/dist/plan-utils.js +128 -65
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +10 -2
- package/dist/testing.d.mts +12 -24
- package/dist/testing.d.ts +12 -24
- package/dist/testing.js +29 -29
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +21 -27
- package/dist/testing.mjs.map +1 -1
- package/package.json +1 -1
- package/skills/tdd/SKILL.md +19 -0
- package/dist/Task-BnLxkVQF.d.mts +0 -2594
- package/dist/Task-BnLxkVQF.d.ts +0 -2594
- package/dist/chunk-LGQNXPBX.mjs.map +0 -1
- package/dist/chunk-Y36NIASW.mjs.map +0 -1
- package/dist/chunk-ZV5LDNCO.mjs.map +0 -1
|
@@ -0,0 +1,1122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why isolated execution is unavailable for a workspace. The orchestrator needs
|
|
3
|
+
* the reason, not just a boolean: `dirty` is offered a stash or an explicit
|
|
4
|
+
* "run without isolation", while the others fall back to the shared
|
|
5
|
+
* workspace root with a one-line notice.
|
|
6
|
+
*/
|
|
7
|
+
type IsolationInactiveReason = 'disabled' | 'git-missing' | 'not-git' | 'no-commits' | 'dirty';
|
|
8
|
+
/**
|
|
9
|
+
* `repos` names, relative to the workspace, the repositories behind the answer:
|
|
10
|
+
* when active, the ones that will isolate, with `shared` the paths every task
|
|
11
|
+
* will share live — loose entries of the workspace root, or, for a lone
|
|
12
|
+
* repository, the repositories nested inside it; otherwise the dirty ones of a
|
|
13
|
+
* `dirty` group, or the commitless ones of a `no-commits` group. A group of one
|
|
14
|
+
* names none.
|
|
15
|
+
*/
|
|
16
|
+
type IsolationAvailability = {
|
|
17
|
+
active: true;
|
|
18
|
+
repos?: string[];
|
|
19
|
+
shared?: string[];
|
|
20
|
+
} | {
|
|
21
|
+
active: false;
|
|
22
|
+
reason: IsolationInactiveReason;
|
|
23
|
+
repos?: string[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Where a run's tasks work, as the planner is told it: the repos of the group
|
|
27
|
+
* and the paths shared live between tasks. A lone repository is `['.']` with
|
|
28
|
+
* nothing shared, unless it holds nested repositories, which are shared live
|
|
29
|
+
* and named in `shared`.
|
|
30
|
+
*/
|
|
31
|
+
interface RepoGroupLayout {
|
|
32
|
+
repos: string[];
|
|
33
|
+
shared: string[];
|
|
34
|
+
}
|
|
35
|
+
type IsolationOutcome = 'merged' | 'conflict' | 'failed';
|
|
36
|
+
/**
|
|
37
|
+
* `active` — worktree exists, a runner may be writing to it.
|
|
38
|
+
* `kept` — released with its worktree and branch preserved for inspection.
|
|
39
|
+
* `conflict` — integration stopped on a merge conflict; worktree and refs kept.
|
|
40
|
+
* `repairing` — a conflict repair (ADR-0015) is working in the kept worktree;
|
|
41
|
+
* one that ends without landing leaves the task `conflict` again.
|
|
42
|
+
* `failed` — integration hit a git error other than a conflict; refs kept.
|
|
43
|
+
* `merged` — landed on the integration branch; worktree and task branch removed.
|
|
44
|
+
*/
|
|
45
|
+
type IsolationTaskStatus = 'active' | 'kept' | 'conflict' | 'repairing' | 'failed' | 'merged';
|
|
46
|
+
/** One repo's share of a task: its worktree inside the task workspace. */
|
|
47
|
+
interface IsolationTaskRepo {
|
|
48
|
+
/** Absolute path of this repo's worktree: the task workspace joined with the repo's path. */
|
|
49
|
+
worktree: string;
|
|
50
|
+
/**
|
|
51
|
+
* Paths linked from the real workspace and kept out of the task's commit:
|
|
52
|
+
* the artifacts bootstrapped from the real repo, and a lone repository's
|
|
53
|
+
* nested repositories (`run.shared`). Recorded because a symlink is not
|
|
54
|
+
* matched by an ignore rule such as `node_modules/` and would otherwise be
|
|
55
|
+
* committed.
|
|
56
|
+
*/
|
|
57
|
+
linked: string[];
|
|
58
|
+
/** Whether the task brought commits to this repo; unknown until it first integrates. */
|
|
59
|
+
changed?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The integration tip right after the task landed here: what the user's
|
|
62
|
+
* HEAD must contain for the task's work to count as merged (ADR-0020).
|
|
63
|
+
*/
|
|
64
|
+
landedTip?: string;
|
|
65
|
+
}
|
|
66
|
+
interface IsolationTaskRecord {
|
|
67
|
+
taskId: string;
|
|
68
|
+
order: number;
|
|
69
|
+
title: string;
|
|
70
|
+
/** One branch name, the same in every repo, so a task is one name to look up across the group. */
|
|
71
|
+
branch: string;
|
|
72
|
+
/**
|
|
73
|
+
* Absolute path of the task workspace, holding one worktree per repo at the
|
|
74
|
+
* repo's path. The Runner's cwd is inside it when the workspace is a repo
|
|
75
|
+
* subdirectory; for a group of one it is the worktree.
|
|
76
|
+
*/
|
|
77
|
+
workspace: string;
|
|
78
|
+
/** For the task as a whole: landing is atomic across the repos it changed. */
|
|
79
|
+
status: IsolationTaskStatus;
|
|
80
|
+
/** Keyed by repo path. */
|
|
81
|
+
repos: Record<string, IsolationTaskRepo>;
|
|
82
|
+
/** The repo whose merge stopped the task from landing, while `status` is `conflict` or `failed`. */
|
|
83
|
+
conflictRepo?: string;
|
|
84
|
+
/**
|
|
85
|
+
* What a `failed` landing stopped on, in words a surface can repeat — a
|
|
86
|
+
* missing worktree is the common one. Cleared by the next landing attempt,
|
|
87
|
+
* so a stale reason cannot outlive the failure it explains.
|
|
88
|
+
*/
|
|
89
|
+
landingError?: string;
|
|
90
|
+
/** Repo-relative paths, in `conflictRepo`, that conflicted; set only while `status` is `conflict` or `repairing`. */
|
|
91
|
+
conflictFiles?: string[];
|
|
92
|
+
/**
|
|
93
|
+
* Conflict repairs started for this task (ADR-0015). Counted when one starts,
|
|
94
|
+
* so a crash cannot hand the spent attempt back; absent reads as none.
|
|
95
|
+
*/
|
|
96
|
+
repairs?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Keyed by repo path: each changed repo's integration tip when the repair in
|
|
99
|
+
* flight started — what the task branch must contain before it may land.
|
|
100
|
+
* Set only while `status` is `repairing`.
|
|
101
|
+
*/
|
|
102
|
+
repairBase?: Record<string, string>;
|
|
103
|
+
/**
|
|
104
|
+
* Every file a repair was started for, across all of them: as `conflictFiles`
|
|
105
|
+
* names it in a group of one, prefixed with its repo's path in a group.
|
|
106
|
+
*/
|
|
107
|
+
repairedFiles?: string[];
|
|
108
|
+
/**
|
|
109
|
+
* Set once the task's landed work is in the checked-out HEAD of every repo
|
|
110
|
+
* it changed — merged into the user's branch, by Merge all or by hand. What
|
|
111
|
+
* a merge gate waits for (ADR-0020).
|
|
112
|
+
*/
|
|
113
|
+
inHead?: true;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A task's landing in flight: each changed repo's integration tip from before
|
|
117
|
+
* the task's merge. On the run rather than the task record because it must
|
|
118
|
+
* outlive that record — a retry drops and recreates it — until every repo is
|
|
119
|
+
* back at its tip or the task has landed.
|
|
120
|
+
*/
|
|
121
|
+
interface IsolationLanding {
|
|
122
|
+
taskId: string;
|
|
123
|
+
/** Keyed by repo path. */
|
|
124
|
+
tips: Record<string, string>;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* One repository of the group (ADR-0014). Every git operation on it runs in
|
|
128
|
+
* `root`, never in the workspace root.
|
|
129
|
+
*/
|
|
130
|
+
interface IsolationRepo {
|
|
131
|
+
/** Relative to the workspace root; `.` when the workspace is itself the repository. */
|
|
132
|
+
path: string;
|
|
133
|
+
/**
|
|
134
|
+
* Absolute: the workspace root joined with `path`. For a group of one that is
|
|
135
|
+
* the workspace root, which may be a subdirectory of the repository.
|
|
136
|
+
*/
|
|
137
|
+
root: string;
|
|
138
|
+
/** The commit checked out at run start. Switching branches mid-run does not retarget it. */
|
|
139
|
+
baseRef: string;
|
|
140
|
+
/** Branch name checked out at run start; absent on a detached HEAD. */
|
|
141
|
+
baseBranch?: string;
|
|
142
|
+
integrationBranch: string;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* One Execute-Plan click or one manual task run over the workspace's repo
|
|
146
|
+
* group. Plain JSON on purpose: the orchestrator persists it with the plan
|
|
147
|
+
* state so a resumed session can find its integration branches again. The
|
|
148
|
+
* module mutates `tasks` in place.
|
|
149
|
+
*/
|
|
150
|
+
interface IsolationRun {
|
|
151
|
+
id: string;
|
|
152
|
+
workspaceRoot: string;
|
|
153
|
+
repos: IsolationRepo[];
|
|
154
|
+
/**
|
|
155
|
+
* Workspace paths outside every isolated repo, linked live into each task
|
|
156
|
+
* workspace: loose entries of the workspace root, the entries beside a deeper
|
|
157
|
+
* repo, and `sharedRepos`. For a lone repository, the repositories nested
|
|
158
|
+
* inside it, which cannot be isolated with it and so are linked live instead.
|
|
159
|
+
* Empty when a group of one holds no nested repositories.
|
|
160
|
+
*/
|
|
161
|
+
shared: string[];
|
|
162
|
+
/** Repos of the group that could not be isolated — no commits, or git refused a worktree — and are among `shared`. */
|
|
163
|
+
sharedRepos: string[];
|
|
164
|
+
/** Keyed by task id — ids are unique within one plan and a run belongs to one plan. */
|
|
165
|
+
tasks: Record<string, IsolationTaskRecord>;
|
|
166
|
+
/**
|
|
167
|
+
* Set before a task's first merge and cleared once it has landed or been
|
|
168
|
+
* rolled back. One found set — after a crash, or a rollback git refused —
|
|
169
|
+
* names exactly what to return each repo's integration branch to.
|
|
170
|
+
*/
|
|
171
|
+
landing?: IsolationLanding;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* What a plan persists of isolated execution (`LegacyPlanState.isolation`): its
|
|
175
|
+
* run, and which added tasks resolve which conflicts. Belongs to that plan and
|
|
176
|
+
* its branches alone, so a copy of the plan (a fork) must not carry it.
|
|
177
|
+
*/
|
|
178
|
+
interface PlanIsolation {
|
|
179
|
+
run: IsolationRun;
|
|
180
|
+
/** Resolver task id → the conflicted task whose branch it merges. */
|
|
181
|
+
resolvers: Record<string, string>;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* A task's isolation as a surface shows it. `kept` covers every record whose
|
|
185
|
+
* worktree stays for inspection — a failed verdict, an interrupted attempt, an
|
|
186
|
+
* integration git refused — because to the user they are one thing: work that
|
|
187
|
+
* did not land and can be looked at. `none` is a task with no worktree in a plan
|
|
188
|
+
* that has an isolation run.
|
|
189
|
+
*/
|
|
190
|
+
type TaskIsolationState = 'none' | 'active' | 'integrated' | 'conflict' | 'repairing' | 'kept';
|
|
191
|
+
type TaskIsolation = {
|
|
192
|
+
state: 'none';
|
|
193
|
+
} | {
|
|
194
|
+
state: Exclude<TaskIsolationState, 'none'>;
|
|
195
|
+
branch: string;
|
|
196
|
+
/** The task workspace; for a group of one, the task's worktree. */
|
|
197
|
+
worktree: string;
|
|
198
|
+
/** Paths of the repos the task changed. */
|
|
199
|
+
repos: string[];
|
|
200
|
+
conflictRepo?: string;
|
|
201
|
+
/** Repo-relative paths, in `conflictRepo`, that conflicted. */
|
|
202
|
+
conflictFiles?: string[];
|
|
203
|
+
/** The conflict repair running or last run, of the most a task may have; absent before its first. */
|
|
204
|
+
repair?: {
|
|
205
|
+
attempt: number;
|
|
206
|
+
limit: number;
|
|
207
|
+
};
|
|
208
|
+
/** What {@link IsolationTaskRecord.repairedFiles} says. */
|
|
209
|
+
repairedFiles?: string[];
|
|
210
|
+
};
|
|
211
|
+
interface IsolationLandedTask {
|
|
212
|
+
taskId: string;
|
|
213
|
+
order: number;
|
|
214
|
+
title: string;
|
|
215
|
+
/** Set when the task landed only after a conflict repair (ADR-0015): the files it was started for. */
|
|
216
|
+
repairedFiles?: string[];
|
|
217
|
+
}
|
|
218
|
+
interface IsolationHandoffRepo {
|
|
219
|
+
path: string;
|
|
220
|
+
integrationBranch: string;
|
|
221
|
+
baseRef: string;
|
|
222
|
+
/** Tasks whose work landed in this repo, in plan order. */
|
|
223
|
+
landed: IsolationLandedTask[];
|
|
224
|
+
}
|
|
225
|
+
interface IsolationHandoff {
|
|
226
|
+
repos: IsolationHandoffRepo[];
|
|
227
|
+
/** Tasks that landed on the integration branches, in plan order. */
|
|
228
|
+
landed: IsolationLandedTask[];
|
|
229
|
+
}
|
|
230
|
+
/** A plan's isolation as a surface shows it: a mark for each task the run touched, and its handoff. */
|
|
231
|
+
interface IsolationView {
|
|
232
|
+
tasks: Record<string, TaskIsolation>;
|
|
233
|
+
handoff: IsolationHandoff;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Why "Merge all" would not touch a repo. `partial-landing`: a task's landing
|
|
237
|
+
* was interrupted and could not be rolled back there, so its integration
|
|
238
|
+
* branch holds part of a task.
|
|
239
|
+
*/
|
|
240
|
+
type IsolationMergeBlockReason = 'merge-in-progress' | 'conflict' | 'uncommitted-changes' | 'partial-landing' | 'git-error';
|
|
241
|
+
interface IsolationMergeBlock {
|
|
242
|
+
repo: string;
|
|
243
|
+
reason: IsolationMergeBlockReason;
|
|
244
|
+
/** The files that would conflict, or the user's uncommitted ones the merge also changes; empty for the other reasons. */
|
|
245
|
+
files: string[];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* How "Merge all" went.
|
|
249
|
+
* - `merged`: every repo with work on its integration branch took it.
|
|
250
|
+
* - `blocked`: the preflight found repos that could not, so nothing was
|
|
251
|
+
* touched anywhere; `blocked` says which and why.
|
|
252
|
+
* - `conflict` / `failed`: a merge stopped in `repo` — on git older than 2.38,
|
|
253
|
+
* which cannot preflight, or for a reason no preflight could foresee. That
|
|
254
|
+
* merge was aborted, leaving `repo` as it was; `landed` names the repos
|
|
255
|
+
* merged before it, which stay merged, and is absent when there are none.
|
|
256
|
+
*
|
|
257
|
+
* A group of one is blocked only by a partial landing; otherwise its one merge
|
|
258
|
+
* lands or is aborted whole, so it reports as it always has.
|
|
259
|
+
*/
|
|
260
|
+
type IsolationMergeResult = {
|
|
261
|
+
outcome: 'merged';
|
|
262
|
+
} | {
|
|
263
|
+
outcome: 'blocked';
|
|
264
|
+
blocked: IsolationMergeBlock[];
|
|
265
|
+
} | {
|
|
266
|
+
outcome: 'conflict' | 'failed';
|
|
267
|
+
repo: string;
|
|
268
|
+
files?: string[];
|
|
269
|
+
landed?: string[];
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* What `discard` does with each repo's integration branch: `keep` it for review
|
|
273
|
+
* or merge, `delete` it, or delete it only in the repos whose checked-out HEAD
|
|
274
|
+
* already contains it (`delete-merged`) — the one way that can never give up
|
|
275
|
+
* landed work the user has not merged.
|
|
276
|
+
*/
|
|
277
|
+
type IntegrationDisposal = 'keep' | 'delete' | 'delete-merged';
|
|
278
|
+
/**
|
|
279
|
+
* Whether a conflict repair's work may land. `not-merged`: the task branch in
|
|
280
|
+
* `repo` does not contain the tip the repair started from. `conflict-markers`:
|
|
281
|
+
* it adds leftover conflict markers to `files`. `failed`: git could not tell.
|
|
282
|
+
*/
|
|
283
|
+
type RepairEvidence = {
|
|
284
|
+
ok: true;
|
|
285
|
+
} | {
|
|
286
|
+
ok: false;
|
|
287
|
+
reason: 'not-merged' | 'conflict-markers' | 'failed';
|
|
288
|
+
repo: string;
|
|
289
|
+
files?: string[];
|
|
290
|
+
};
|
|
291
|
+
interface PreparedTask {
|
|
292
|
+
cwd: string;
|
|
293
|
+
branch: string;
|
|
294
|
+
/**
|
|
295
|
+
* Paths, relative to the task workspace, that are copies rather than links
|
|
296
|
+
* because a hard link was impossible (Windows, another volume). Edits to them
|
|
297
|
+
* stay in the task, so the user is told.
|
|
298
|
+
*/
|
|
299
|
+
copied: string[];
|
|
300
|
+
/** What the attempt this one replaces left unlanded, kept on a branch before its worktree went; absent when nothing was. */
|
|
301
|
+
preserved?: PreservedWork[];
|
|
302
|
+
}
|
|
303
|
+
/** A task as a removal names it. */
|
|
304
|
+
type IsolationTaskRef = Pick<IsolationTaskRecord, 'taskId' | 'order' | 'title'>;
|
|
305
|
+
/**
|
|
306
|
+
* Work a removal found that had not landed — edits never committed, or
|
|
307
|
+
* commits only the task's branch carried — committed and kept on `branch`,
|
|
308
|
+
* a branch of its own under `ordewell-preserved/`, before the worktree went.
|
|
309
|
+
*/
|
|
310
|
+
interface PreservedWork {
|
|
311
|
+
/** Absent for a leftover directory no task record owned. */
|
|
312
|
+
task?: IsolationTaskRef;
|
|
313
|
+
repo: string;
|
|
314
|
+
branch: string;
|
|
315
|
+
/** The commit `branch` points at. */
|
|
316
|
+
commit: string;
|
|
317
|
+
}
|
|
318
|
+
/** A worktree a removal left in place, because the work in it could not be kept any other way. */
|
|
319
|
+
interface RefusedRemoval {
|
|
320
|
+
/** Absent for a leftover directory no task record owned. */
|
|
321
|
+
task?: IsolationTaskRef;
|
|
322
|
+
worktree: string;
|
|
323
|
+
/** What stopped git keeping it, in words a surface can repeat. */
|
|
324
|
+
reason: string;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* What removing task worktrees did in place of deleting work. A removal never
|
|
328
|
+
* deletes work that has not landed: it keeps it on a branch first
|
|
329
|
+
* (`preserved`), and where it cannot, it leaves that worktree alone
|
|
330
|
+
* (`refused`) — so the record of a refused task stays too.
|
|
331
|
+
*/
|
|
332
|
+
interface IsolationRemoval {
|
|
333
|
+
preserved: PreservedWork[];
|
|
334
|
+
refused: RefusedRemoval[];
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* What a crash-recovery prune found and left alone: task records that were
|
|
338
|
+
* `active` yet still held unlanded work, so the prune kept them as `kept`
|
|
339
|
+
* rather than deleting work no one else has.
|
|
340
|
+
*/
|
|
341
|
+
interface IsolationPruneResult extends IsolationRemoval {
|
|
342
|
+
kept: IsolationTaskRef[];
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* The tracked changes of a workspace's repos at one moment, against each
|
|
346
|
+
* repo's HEAD: repo path → file path → the content it held then. What an ops
|
|
347
|
+
* task's tree check compares against (ADR-0020).
|
|
348
|
+
*/
|
|
349
|
+
type TreeSnapshot = Record<string, Record<string, string>>;
|
|
350
|
+
interface IWorktreeIsolation {
|
|
351
|
+
/**
|
|
352
|
+
* A repo group with at least one repo to isolate, a clean tracked tree in
|
|
353
|
+
* each, and the config enabled; otherwise the reason it is not.
|
|
354
|
+
*/
|
|
355
|
+
isActive(workspaceRoot: string): Promise<IsolationAvailability>;
|
|
356
|
+
/**
|
|
357
|
+
* Put the tracked changes of every dirty repo of the group on its git stash,
|
|
358
|
+
* the user's way out of a `dirty` refusal. Untracked files stay: they never
|
|
359
|
+
* block isolation.
|
|
360
|
+
*/
|
|
361
|
+
stash(workspaceRoot: string): Promise<void>;
|
|
362
|
+
/**
|
|
363
|
+
* Mint a run: resolve each repo's base ref to a commit now, and share the
|
|
364
|
+
* repos that cannot be isolated. Only meaningful after `isActive` said yes;
|
|
365
|
+
* throws when no repo of the group can be isolated after all.
|
|
366
|
+
*/
|
|
367
|
+
startRun(workspaceRoot: string): Promise<IsolationRun>;
|
|
368
|
+
/**
|
|
369
|
+
* Create the task workspace — one worktree per isolated repo from its
|
|
370
|
+
* integration tip, the shared paths linked in — and return the cwd to spawn
|
|
371
|
+
* the Runner into. A second `prepare` for the same task is a retry: the old
|
|
372
|
+
* attempt is discarded and the workspace recreated from the tips, so the
|
|
373
|
+
* task sees everything its predecessors have integrated.
|
|
374
|
+
*/
|
|
375
|
+
prepare(task: Task, run: IsolationRun): Promise<PreparedTask>;
|
|
376
|
+
/**
|
|
377
|
+
* Hand a conflicted task's kept workspace to a conflict repair (ADR-0015)
|
|
378
|
+
* as it is — nothing is re-cut — and return the same cwd. Records each
|
|
379
|
+
* changed repo's integration tip as `repairBase`, counts the repair, and
|
|
380
|
+
* moves the task to `repairing`. Throws for a task that is not `conflict`.
|
|
381
|
+
*/
|
|
382
|
+
reopen(task: Task, run: IsolationRun): Promise<PreparedTask>;
|
|
383
|
+
/**
|
|
384
|
+
* The evidence a repair must show before it lands: its work committed, and
|
|
385
|
+
* in each repo of `repairBase` the task branch containing that tip
|
|
386
|
+
* (`git merge-base --is-ancestor`) and adding no leftover conflict markers
|
|
387
|
+
* (`git diff --check`; whitespace warnings do not count). Changes nothing
|
|
388
|
+
* else: a task that fails stays `repairing` until released.
|
|
389
|
+
*/
|
|
390
|
+
verifyRepair(task: Task, run: IsolationRun): Promise<RepairEvidence>;
|
|
391
|
+
/**
|
|
392
|
+
* Land the task atomically across the repos it changed: commit each
|
|
393
|
+
* worktree, then `git merge --no-ff` the task branch into each changed
|
|
394
|
+
* repo's integration branch. If any merge conflicts or fails, it is aborted
|
|
395
|
+
* and the merges already made for the task are reset away, so `merged`
|
|
396
|
+
* always means the whole task landed. Serialized inside the module; among
|
|
397
|
+
* tasks waiting at once the lowest plan order goes first. On anything but
|
|
398
|
+
* `merged` the worktrees and refs stay, and nothing is resolved here: a
|
|
399
|
+
* conflict is repaired, if at all, by a new attempt of the task in its own
|
|
400
|
+
* worktree (ADR-0015), never inside this queue.
|
|
401
|
+
*
|
|
402
|
+
* `persist` is called once `run.landing` is set and before the first
|
|
403
|
+
* merge; the caller saves the run there, synchronously, which is what
|
|
404
|
+
* lets `pruneOrphans` finish a landing a crash interrupted.
|
|
405
|
+
*/
|
|
406
|
+
integrate(task: Task, run: IsolationRun, persist?: () => void): Promise<IsolationOutcome>;
|
|
407
|
+
/**
|
|
408
|
+
* `keep: false` removes the task's worktree, branch and record (retry, task
|
|
409
|
+
* removal), keeping whatever had not landed on a branch first; a record
|
|
410
|
+
* whose work cannot be kept stays, off `active`. `keep: true` leaves the
|
|
411
|
+
* worktree and branch exactly as they are
|
|
412
|
+
* for inspection — a failed verdict, a stop, a cancel — and only moves the task off `active`,
|
|
413
|
+
* so a crash-recovery prune does not sweep it away; a repair it ends leaves
|
|
414
|
+
* the task `conflict`, as it was before the repair. Takes the run rather than
|
|
415
|
+
* a bare task id: ids are only unique within one plan, and one daemon serves
|
|
416
|
+
* many (ADR-0007).
|
|
417
|
+
*/
|
|
418
|
+
release(run: IsolationRun, taskId: string, opts: {
|
|
419
|
+
keep: boolean;
|
|
420
|
+
}): Promise<IsolationRemoval>;
|
|
421
|
+
/** End of run: park the integration branch for review and report what landed. */
|
|
422
|
+
handoff(run: IsolationRun): Promise<IsolationHandoff>;
|
|
423
|
+
/**
|
|
424
|
+
* Drop what a crash left behind: a landing it interrupted is rolled back in
|
|
425
|
+
* every repo, a repair it interrupted leaves its task `conflict`, then stale
|
|
426
|
+
* active worktrees and directories no record owns go. An `active` record
|
|
427
|
+
* that still holds unlanded work — commits its branch alone carries, or
|
|
428
|
+
* edits in its worktree — is not a crash orphan: it may belong to a runner
|
|
429
|
+
* another host is still driving, so it is kept as `kept` and named in the
|
|
430
|
+
* result. A leftover directory holding work is kept on a branch before it
|
|
431
|
+
* goes, as {@link release} does.
|
|
432
|
+
*/
|
|
433
|
+
pruneOrphans(run: IsolationRun): Promise<IsolationPruneResult>;
|
|
434
|
+
/** Unified diff of each repo's integration branch against its base ref. */
|
|
435
|
+
reviewDiff(run: IsolationRun): Promise<string>;
|
|
436
|
+
/**
|
|
437
|
+
* Mark the landed tasks whose work the checked-out HEAD now contains in
|
|
438
|
+
* every repo they changed (`git merge-base --is-ancestor`), and return their
|
|
439
|
+
* ids. Only adds marks: a task found merged stays merged.
|
|
440
|
+
*/
|
|
441
|
+
findInHead(run: IsolationRun): Promise<string[]>;
|
|
442
|
+
/**
|
|
443
|
+
* The tracked changes of each committed repo of the workspace's group,
|
|
444
|
+
* except those in `exclude`; null when there is no repo to snapshot.
|
|
445
|
+
* Untracked and ignored files never count.
|
|
446
|
+
*/
|
|
447
|
+
snapshotTree(workspaceRoot: string, exclude: readonly string[]): Promise<TreeSnapshot | null>;
|
|
448
|
+
/**
|
|
449
|
+
* Tracked files whose content differs from what `snapshot` recorded: new
|
|
450
|
+
* changes, and further changes to files already changed then. Paths are
|
|
451
|
+
* from the workspace root. A repo git cannot read now is left out.
|
|
452
|
+
*/
|
|
453
|
+
changedSince(workspaceRoot: string, snapshot: TreeSnapshot): Promise<string[]>;
|
|
454
|
+
/**
|
|
455
|
+
* "Merge all": merge each repo's integration branch into whatever the user
|
|
456
|
+
* has checked out there. The one irreversible step, so it only ever happens
|
|
457
|
+
* when a caller asks for it. Every repo with work is preflighted first — no
|
|
458
|
+
* merge of the user's in progress, no conflict against their HEAD, no
|
|
459
|
+
* uncommitted edit to a file the merge changes — and unless all pass,
|
|
460
|
+
* nothing is merged anywhere. Only a merge Ordewell itself just started is
|
|
461
|
+
* ever aborted; nothing of the user's is reset. Waits for a landing in
|
|
462
|
+
* flight and holds the next back, so a merge during a run takes the
|
|
463
|
+
* integration branches between two landings, never part of one.
|
|
464
|
+
*/
|
|
465
|
+
mergeIntoCheckedOut(run: IsolationRun): Promise<IsolationMergeResult>;
|
|
466
|
+
/**
|
|
467
|
+
* Remove every worktree and task branch of the run, and settle each repo's
|
|
468
|
+
* integration branch as `integration` says. Anything but `keep` also clears
|
|
469
|
+
* the run's task records, except those of worktrees it refused to remove.
|
|
470
|
+
* Work that had not landed is kept on a branch first, as {@link release} does.
|
|
471
|
+
*/
|
|
472
|
+
discard(run: IsolationRun, opts: {
|
|
473
|
+
integration: IntegrationDisposal;
|
|
474
|
+
}): Promise<IsolationRemoval>;
|
|
475
|
+
/**
|
|
476
|
+
* Clear what other runs left in each repo of `run`'s group: every
|
|
477
|
+
* `ordewell/<run-id>/…` branch the repo's checked-out HEAD already contains.
|
|
478
|
+
* Never a branch of `run` itself, one a worktree has checked out, or any
|
|
479
|
+
* branch of a run that still has a worktree — that run may be live in
|
|
480
|
+
* another plan. Tries every repo, then throws naming those where git failed.
|
|
481
|
+
*/
|
|
482
|
+
sweep(run: IsolationRun): Promise<void>;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* What one model call consumed, as its provider or runner reported it. Shared
|
|
487
|
+
* by the planner's usage line (#49) and per-attempt task usage (#26): a record
|
|
488
|
+
* says nothing about who asked for the call, so either can hold a list of them.
|
|
489
|
+
*
|
|
490
|
+
* Every measure is optional because backends report different subsets. An
|
|
491
|
+
* absent field means "not reported" — never zero — so a total can tell the two
|
|
492
|
+
* apart.
|
|
493
|
+
*/
|
|
494
|
+
interface UsageRecord {
|
|
495
|
+
/** The provider or runner id that reported the call — `openai`, `claude-code`, … */
|
|
496
|
+
source: string;
|
|
497
|
+
model?: string;
|
|
498
|
+
inputTokens?: number;
|
|
499
|
+
outputTokens?: number;
|
|
500
|
+
/** The share of `inputTokens` served from the provider's prompt cache. */
|
|
501
|
+
cachedInputTokens?: number;
|
|
502
|
+
/**
|
|
503
|
+
* Filled only from a provider's or runner's own report, never from a price
|
|
504
|
+
* table or an estimate: prices go stale, and a subscription runner has no
|
|
505
|
+
* per-token price at all. No report means no cost, not a guessed one.
|
|
506
|
+
*/
|
|
507
|
+
reportedCost?: {
|
|
508
|
+
amount: number;
|
|
509
|
+
currency: string;
|
|
510
|
+
};
|
|
511
|
+
/** The model's context window, when the runner itself reports it. */
|
|
512
|
+
contextWindow?: number;
|
|
513
|
+
/** Set when a subagent made the call; its usage still counts toward the total. */
|
|
514
|
+
subagentId?: string;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* A running sum of {@link UsageRecord}s. A measure stays absent until some
|
|
518
|
+
* record reports it. Cost is kept per currency: two runners may bill in
|
|
519
|
+
* different ones, and there is no honest exchange rate to fold them together.
|
|
520
|
+
*/
|
|
521
|
+
interface UsageTotals {
|
|
522
|
+
inputTokens?: number;
|
|
523
|
+
outputTokens?: number;
|
|
524
|
+
cachedInputTokens?: number;
|
|
525
|
+
reportedCost?: Record<string, number>;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* The input side of a call whose provider reports its prompt in parts — the
|
|
529
|
+
* uncached tail, with cache reads and cache writes beside it rather than inside
|
|
530
|
+
* it (Anthropic, and OpenCode after it). The prompt the model saw is all three;
|
|
531
|
+
* only the reads were served from cache, since a write is billed as fresh
|
|
532
|
+
* input. A part left unreported adds nothing, and with no part reported there
|
|
533
|
+
* is no measure at all.
|
|
534
|
+
*/
|
|
535
|
+
declare function partedPromptUsage(parts: {
|
|
536
|
+
uncached?: number;
|
|
537
|
+
cacheRead?: number;
|
|
538
|
+
cacheWrite?: number;
|
|
539
|
+
}): Pick<UsageRecord, 'inputTokens' | 'cachedInputTokens'>;
|
|
540
|
+
declare function addUsage(totals: UsageTotals, record: UsageRecord): UsageTotals;
|
|
541
|
+
/**
|
|
542
|
+
* What the planner has consumed over a session (#49). `totals` includes every
|
|
543
|
+
* `bySubagent` entry. `lastPromptTokens` and `contextWindow` track the
|
|
544
|
+
* planner's own calls only — a subagent runs its own model, whose window says
|
|
545
|
+
* nothing about the planner's.
|
|
546
|
+
*/
|
|
547
|
+
interface PlannerUsage {
|
|
548
|
+
totals: UsageTotals;
|
|
549
|
+
bySubagent?: Record<string, UsageTotals>;
|
|
550
|
+
lastPromptTokens?: number;
|
|
551
|
+
contextWindow?: number;
|
|
552
|
+
}
|
|
553
|
+
declare function addPlannerUsage(usage: PlannerUsage, record: UsageRecord): PlannerUsage;
|
|
554
|
+
/** Whether any measure was reported: a token line of nothing but blanks says nothing. */
|
|
555
|
+
declare function isMeasured(totals: UsageTotals): boolean;
|
|
556
|
+
/** What the token line shows of a ledger — live from its broadcast, or reloaded from the saved one. */
|
|
557
|
+
interface UsageLine {
|
|
558
|
+
totals: UsageTotals;
|
|
559
|
+
bySubagent?: Record<string, UsageTotals>;
|
|
560
|
+
contextFill?: {
|
|
561
|
+
usedTokens: number;
|
|
562
|
+
windowTokens: number;
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
declare function usageLine(usage: PlannerUsage): UsageLine;
|
|
566
|
+
/**
|
|
567
|
+
* The last planner prompt against its window, or undefined while either is
|
|
568
|
+
* unknown. `usedTokens` is the prompt total as reported, cached tokens
|
|
569
|
+
* included: a cached token still occupies the window, so subtracting the
|
|
570
|
+
* cached share would understate how full the context is. A window of 0 is
|
|
571
|
+
* treated as unknown — never guessed.
|
|
572
|
+
*/
|
|
573
|
+
declare function plannerContextFill(usage: PlannerUsage): {
|
|
574
|
+
usedTokens: number;
|
|
575
|
+
windowTokens: number;
|
|
576
|
+
} | undefined;
|
|
577
|
+
|
|
578
|
+
declare const BUILTIN_SKILL_NAMES: readonly ["grilling", "to-spec", "improve-codebase-architecture", "tdd"];
|
|
579
|
+
interface SkillMetadata {
|
|
580
|
+
name: string;
|
|
581
|
+
description: string;
|
|
582
|
+
disableModelInvocation?: boolean;
|
|
583
|
+
}
|
|
584
|
+
/** Who a skill is for: the planner's conversation, or a runner's task prompt. */
|
|
585
|
+
type SkillAppliesTo = 'planner' | 'task';
|
|
586
|
+
/** `global` is ~/.ordewell/skills/ (built-in seeds included); `workspace` is a `.ordewell/skills/` of the workspace's (see {@link workspaceSkillRoots}). */
|
|
587
|
+
type SkillSource = 'global' | 'workspace';
|
|
588
|
+
interface SkillInfo {
|
|
589
|
+
name: string;
|
|
590
|
+
description: string;
|
|
591
|
+
metadata: SkillMetadata;
|
|
592
|
+
/** Full content of SKILL.md with frontmatter stripped */
|
|
593
|
+
content: string;
|
|
594
|
+
/** Absolute path to the SKILL.md file */
|
|
595
|
+
path: string;
|
|
596
|
+
source: SkillSource;
|
|
597
|
+
/** Frontmatter `applies-to`; absent or unrecognised reads as `planner`. */
|
|
598
|
+
appliesTo: SkillAppliesTo;
|
|
599
|
+
/** False when frontmatter sets `disable-model-invocation: true`. */
|
|
600
|
+
modelInvocable: boolean;
|
|
601
|
+
/** Frontmatter `user-invocable`; false makes the skill model-only. */
|
|
602
|
+
userInvocable: boolean;
|
|
603
|
+
}
|
|
604
|
+
/** A workspace skill hidden because a global skill, or a workspace one read before it, has the same name. */
|
|
605
|
+
interface ShadowedSkill {
|
|
606
|
+
skill: SkillInfo;
|
|
607
|
+
shadowedBy: SkillInfo;
|
|
608
|
+
}
|
|
609
|
+
/** A skill folder no name can reach — `/name`, a plan and a spawn all use the folder name — so it is listed nowhere. */
|
|
610
|
+
interface InvalidSkill {
|
|
611
|
+
name: string;
|
|
612
|
+
/** Absolute path to the folder's SKILL.md. */
|
|
613
|
+
path: string;
|
|
614
|
+
source: SkillSource;
|
|
615
|
+
reason: string;
|
|
616
|
+
}
|
|
617
|
+
/** A skills folder's catalog: what wins, what a winner hides, and the folders skipped for their name. */
|
|
618
|
+
interface SkillCatalog {
|
|
619
|
+
skills: SkillInfo[];
|
|
620
|
+
shadowed: ShadowedSkill[];
|
|
621
|
+
invalid: InvalidSkill[];
|
|
622
|
+
}
|
|
623
|
+
declare class SkillsService {
|
|
624
|
+
private readonly workspaceRoots;
|
|
625
|
+
/** `workspaceRoots` in the order they win, as {@link workspaceSkillRoots} gives them. */
|
|
626
|
+
constructor(workspaceRoots?: string | readonly string[]);
|
|
627
|
+
/**
|
|
628
|
+
* The same global skills, with the workspace ones read from `roots` instead —
|
|
629
|
+
* a task's worktrees, whose `.ordewell/skills/` are their own committed copies.
|
|
630
|
+
*/
|
|
631
|
+
forRoot(roots: string | readonly string[]): SkillsService;
|
|
632
|
+
/** Where {@link findSkill} looks, in the order it looks — global first. */
|
|
633
|
+
searchedDirs(): string[];
|
|
634
|
+
private workspaceDirs;
|
|
635
|
+
private globalDir;
|
|
636
|
+
private readSeedManifest;
|
|
637
|
+
private recordSeed;
|
|
638
|
+
/** A seed the user never edited: safe to replace with a newer built-in. */
|
|
639
|
+
private isUntouchedSeed;
|
|
640
|
+
private seed;
|
|
641
|
+
seedBuiltinSkill(name: string): boolean;
|
|
642
|
+
/** Removes a retired built-in's seed from the global dir, but only if it's untouched by the user. */
|
|
643
|
+
private isUnmodifiedRetiredSeed;
|
|
644
|
+
private pruneRetired;
|
|
645
|
+
/** A name no skill could have resolves to nothing: it is joined into a path, so `../x` would read outside the skill dirs. */
|
|
646
|
+
findSkill(name: string): SkillInfo | undefined;
|
|
647
|
+
listSkills(): SkillInfo[];
|
|
648
|
+
/** Workspace skills skipped by `listSkills` and `findSkill` because one that wins has the same name. */
|
|
649
|
+
listShadowed(): ShadowedSkill[];
|
|
650
|
+
/**
|
|
651
|
+
* Global wins a name clash: a repository's committed skill must not be able
|
|
652
|
+
* to silently replace one the user installed, built-ins included. Among
|
|
653
|
+
* workspace folders the first read wins, the same way.
|
|
654
|
+
*/
|
|
655
|
+
private catalog;
|
|
656
|
+
/** Inspect installed skills without seeding, refreshing or pruning global files. */
|
|
657
|
+
readCatalog(): SkillCatalog;
|
|
658
|
+
getSkillContent(name: string): string | undefined;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* The skills a workspace sees outside any task — every listing, picker and
|
|
662
|
+
* catalog before a task worktree exists: global, then the folders a spawn
|
|
663
|
+
* reads (see {@link workspaceSkillRoots}), each repo's as checked out in the
|
|
664
|
+
* workspace. `workspaceRepos` is the setting that names a repo group.
|
|
665
|
+
*/
|
|
666
|
+
declare function createSkillsService(workspaceRoot?: string, workspaceRepos?: readonly string[]): SkillsService;
|
|
667
|
+
|
|
668
|
+
interface UserStep {
|
|
669
|
+
order: number;
|
|
670
|
+
instruction: string;
|
|
671
|
+
completed: boolean;
|
|
672
|
+
}
|
|
673
|
+
/** One deterministic signal gathered while verifying a completed task. `completion_marker` is only in verdicts saved before completion was reported through `task_complete` alone. */
|
|
674
|
+
interface VerificationCheck {
|
|
675
|
+
name: 'exit_code' | 'completion_marker' | 'task_complete' | 'manual';
|
|
676
|
+
passed: boolean;
|
|
677
|
+
/** A check that did not apply. Skipped checks don't affect the verdict. */
|
|
678
|
+
skipped: boolean;
|
|
679
|
+
detail: string;
|
|
680
|
+
}
|
|
681
|
+
/** Evidence-based verdict for a completed task. Single end-to-end outcome produced by verification. */
|
|
682
|
+
interface Verdict {
|
|
683
|
+
outcome: 'pass' | 'fail';
|
|
684
|
+
reason: string;
|
|
685
|
+
checks: VerificationCheck[];
|
|
686
|
+
decidedAt: string;
|
|
687
|
+
}
|
|
688
|
+
interface TaskOutputSummary {
|
|
689
|
+
reviewReason: string;
|
|
690
|
+
logTail: string;
|
|
691
|
+
capturedAt: string;
|
|
692
|
+
}
|
|
693
|
+
type TaskType = 'ai' | 'user';
|
|
694
|
+
type TaskStatus = 'pending' | 'approved' | 'in_progress' | 'completed' | 'failed' | 'blocked' | 'awaiting_user';
|
|
695
|
+
type TaskMode = string;
|
|
696
|
+
/**
|
|
697
|
+
* Why an `awaiting_user` task waits (ADR-0018, W1): a structured turn that
|
|
698
|
+
* ended without a `task_complete` call, a checkpoint question, work that did not
|
|
699
|
+
* land, or an ops task that changed tracked files (ADR-0020). Saved, so no
|
|
700
|
+
* surface has to guess it from whether an attempt is live.
|
|
701
|
+
*/
|
|
702
|
+
type AwaitingReason = 'input' | 'checkpoint' | 'conflict' | 'files-changed';
|
|
703
|
+
declare function isAwaitingReason(value: unknown): value is AwaitingReason;
|
|
704
|
+
interface TaskModelAssignment {
|
|
705
|
+
modelId: string;
|
|
706
|
+
modelLabel: string;
|
|
707
|
+
thinkingEffort?: string;
|
|
708
|
+
/**
|
|
709
|
+
* All variant ids the model offered when this assignment was made. Carried
|
|
710
|
+
* on the assignment because runners need it at spawn time (opencode's TUI
|
|
711
|
+
* only honors an assigned variant when the others are config-disabled) and
|
|
712
|
+
* the discovery catalog isn't available there.
|
|
713
|
+
*/
|
|
714
|
+
availableVariants?: string[];
|
|
715
|
+
}
|
|
716
|
+
type RunnerId = string;
|
|
717
|
+
/**
|
|
718
|
+
* Bring loaded tasks to the structured-only shape, in place. A task saved
|
|
719
|
+
* while the terminal transport existed may say it ran there, and why; neither
|
|
720
|
+
* means anything now, and such a task left no session to continue. Its
|
|
721
|
+
* completion marker id went with the text markers: completion is reported
|
|
722
|
+
* only through `task_complete`.
|
|
723
|
+
*/
|
|
724
|
+
declare function migrateLoadedTasks(tasks: readonly Task[]): void;
|
|
725
|
+
/**
|
|
726
|
+
* A task skill as one attempt received it: read from the task's own worktree
|
|
727
|
+
* (or global) at spawn, kept so the user can see what the runner was told
|
|
728
|
+
* even after the skill's file changes.
|
|
729
|
+
*/
|
|
730
|
+
interface TaskSkillSnapshot {
|
|
731
|
+
name: string;
|
|
732
|
+
source: SkillSource;
|
|
733
|
+
path: string;
|
|
734
|
+
content: string;
|
|
735
|
+
}
|
|
736
|
+
/** A task skill as a surface is told of it: the body stays in the task's log file and the session. */
|
|
737
|
+
type TaskSkillNotice = Omit<TaskSkillSnapshot, 'content'>;
|
|
738
|
+
interface Task {
|
|
739
|
+
id: string;
|
|
740
|
+
order: number;
|
|
741
|
+
title: string;
|
|
742
|
+
description: string;
|
|
743
|
+
type: TaskType;
|
|
744
|
+
status: TaskStatus;
|
|
745
|
+
dependencies: string[];
|
|
746
|
+
prompt?: string;
|
|
747
|
+
userSteps?: UserStep[];
|
|
748
|
+
subtasks: Task[];
|
|
749
|
+
verdict?: Verdict;
|
|
750
|
+
outputSummary?: TaskOutputSummary;
|
|
751
|
+
assignedModel?: TaskModelAssignment;
|
|
752
|
+
assignedRunner: RunnerId;
|
|
753
|
+
thinkingEffort?: string;
|
|
754
|
+
taskMode?: TaskMode;
|
|
755
|
+
autonomy?: 'AFK' | 'HITL';
|
|
756
|
+
sliceType?: 'HITL' | 'AFK';
|
|
757
|
+
userStoriesCovered?: string[];
|
|
758
|
+
/** The runner's saved session for a later continue; cleared when a fresh attempt starts. */
|
|
759
|
+
runnerSessionId?: string;
|
|
760
|
+
/** Set only while `status` is `awaiting_user`, and not always then — a usage-limit pause has none. */
|
|
761
|
+
awaitingReason?: AwaitingReason;
|
|
762
|
+
/**
|
|
763
|
+
* An ops task (ADR-0020): it changes no repository files, runs at the
|
|
764
|
+
* workspace root rather than in a worktree, and waits for the change tasks
|
|
765
|
+
* it depends on to be merged into the user's branch. Top-level AI tasks
|
|
766
|
+
* only; absent means a change task.
|
|
767
|
+
*/
|
|
768
|
+
ops?: boolean;
|
|
769
|
+
/**
|
|
770
|
+
* The dependencies, by title, whose work was not merged into the user's
|
|
771
|
+
* branch when the user force-started this task past its merge gate
|
|
772
|
+
* (ADR-0020). Cleared when the task is retried.
|
|
773
|
+
*/
|
|
774
|
+
forcedPastGate?: string[];
|
|
775
|
+
/**
|
|
776
|
+
* Task skills (`applies-to: task`) attached by name. Their bodies are put in
|
|
777
|
+
* the runner's prompt at spawn, resolved where the task runs — a subtask's
|
|
778
|
+
* are its own, never its parent's. Absent means none.
|
|
779
|
+
*/
|
|
780
|
+
skills?: string[];
|
|
781
|
+
/** The skills the latest attempt was given, as read when it spawned. */
|
|
782
|
+
attemptSkills?: TaskSkillSnapshot[];
|
|
783
|
+
}
|
|
784
|
+
interface DiscoveredMode {
|
|
785
|
+
id: string;
|
|
786
|
+
label: string;
|
|
787
|
+
description: string;
|
|
788
|
+
}
|
|
789
|
+
type ResearchToolType = 'read_file' | 'read_files' | 'glob' | 'grep' | 'find_symbol' | 'list_dir' | 'bash' | 'fetch' | 'web_search' | 'spawn_research_agent'
|
|
790
|
+
/**
|
|
791
|
+
* A tool belonging to a harness planner's own toolbox (ADR-0009) that has no
|
|
792
|
+
* Ordewell equivalent — Edit, WebFetch, TodoWrite, whatever a coding agent
|
|
793
|
+
* ships next. The real name travels in `toolLabel` rather than being
|
|
794
|
+
* relabelled as a tool it is not; the union stays closed so the
|
|
795
|
+
* exhaustiveness checks in every surface's icon/label switch survive.
|
|
796
|
+
*/
|
|
797
|
+
| 'agent_tool';
|
|
798
|
+
/**
|
|
799
|
+
* What happened when a research tool call ran, for honest per-surface
|
|
800
|
+
* rendering. The broadcast seam carries this on every `research_step_done` so
|
|
801
|
+
* surfaces do not have to pattern-match refusal text to tell a refused `rm`
|
|
802
|
+
* from a successful `rm` — the old render path flipped a `✓` for both.
|
|
803
|
+
*/
|
|
804
|
+
type ResearchStepOutcome = 'success' | 'failure' | 'refused' | 'denied' | 'not_executed';
|
|
805
|
+
interface ResearchStep {
|
|
806
|
+
id: string;
|
|
807
|
+
tool: ResearchToolType;
|
|
808
|
+
/** The tool's own name when it came from a harness planner — always set for `agent_tool`. */
|
|
809
|
+
toolLabel?: string;
|
|
810
|
+
args: string;
|
|
811
|
+
result: string;
|
|
812
|
+
success: boolean;
|
|
813
|
+
outcome: ResearchStepOutcome;
|
|
814
|
+
/** The model's tool_call id, so a surface can match `tool_result` to the
|
|
815
|
+
* pending `tool_call` it announced — robust under parallel same-tool rounds. */
|
|
816
|
+
toolCallId?: string;
|
|
817
|
+
/** The research subagent that ran the call, so a reload regroups it under that subagent. */
|
|
818
|
+
subagentId?: string;
|
|
819
|
+
timestamp: string;
|
|
820
|
+
thinkingText?: string;
|
|
821
|
+
}
|
|
822
|
+
interface UserPromptEntry {
|
|
823
|
+
id: string;
|
|
824
|
+
type: 'user_prompt' | 'system';
|
|
825
|
+
content: string;
|
|
826
|
+
timestamp: string;
|
|
827
|
+
}
|
|
828
|
+
type SubagentOutcome = 'done' | 'failed' | 'stopped';
|
|
829
|
+
/**
|
|
830
|
+
* One subagent's whole run, logged when it finishes: what it was asked, how it
|
|
831
|
+
* ended and what it reported. Its steps stay separate entries carrying the same
|
|
832
|
+
* `subagentId`, so an older reader that knows only steps still shows them.
|
|
833
|
+
*/
|
|
834
|
+
interface SubagentLogEntry {
|
|
835
|
+
id: string;
|
|
836
|
+
type: 'subagent';
|
|
837
|
+
subagentId: string;
|
|
838
|
+
brief: string;
|
|
839
|
+
model?: string;
|
|
840
|
+
outcome: SubagentOutcome;
|
|
841
|
+
digest: string;
|
|
842
|
+
usage?: UsageTotals;
|
|
843
|
+
timestamp: string;
|
|
844
|
+
}
|
|
845
|
+
type ResearchLogEntry = ResearchStep | UserPromptEntry | SubagentLogEntry;
|
|
846
|
+
interface ResearchProgress {
|
|
847
|
+
type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted' | 'liveness' | 'text_delta' | 'text_retracted' | 'usage' | 'subagent_started' | 'subagent_finished';
|
|
848
|
+
/** Minted by whoever runs the turn and passed through untouched; absent outside a turn. */
|
|
849
|
+
turnId?: string;
|
|
850
|
+
/** One continuous run of model text — text before a tool call is its own segment. */
|
|
851
|
+
segmentId?: string;
|
|
852
|
+
text?: string;
|
|
853
|
+
tool?: string;
|
|
854
|
+
/** Harness planners (ADR-0009): the agent's own name for a tool Ordewell has no member for. */
|
|
855
|
+
toolLabel?: string;
|
|
856
|
+
toolArgs?: string;
|
|
857
|
+
toolResult?: string;
|
|
858
|
+
planToken?: string;
|
|
859
|
+
step?: ResearchStep;
|
|
860
|
+
/** The model's tool_call id, threaded on tool_call and tool_result so a
|
|
861
|
+
* surface can match the result to its pending call — robust under parallel
|
|
862
|
+
* same-tool rounds where LIFO-by-name matching mislabels summaries. */
|
|
863
|
+
toolCallId?: string;
|
|
864
|
+
/** Present when this event originates from (or reports on) one spawned research subagent (issue #34). */
|
|
865
|
+
subagentId?: string;
|
|
866
|
+
record?: UsageRecord;
|
|
867
|
+
brief?: string;
|
|
868
|
+
model?: string;
|
|
869
|
+
outcome?: SubagentOutcome;
|
|
870
|
+
digest?: string;
|
|
871
|
+
usage?: UsageTotals;
|
|
872
|
+
}
|
|
873
|
+
interface ThinkingBlock {
|
|
874
|
+
id: string;
|
|
875
|
+
text: string;
|
|
876
|
+
}
|
|
877
|
+
interface StreamThinkingEvent {
|
|
878
|
+
type: 'thinking';
|
|
879
|
+
block: ThinkingBlock;
|
|
880
|
+
}
|
|
881
|
+
interface StreamStepEvent {
|
|
882
|
+
type: 'step';
|
|
883
|
+
step: ResearchStep;
|
|
884
|
+
}
|
|
885
|
+
type StreamEvent = StreamThinkingEvent | StreamStepEvent;
|
|
886
|
+
interface DiscoveredModel {
|
|
887
|
+
modelId: string;
|
|
888
|
+
modelLabel: string;
|
|
889
|
+
runnerProvider?: string;
|
|
890
|
+
/**
|
|
891
|
+
* Human-facing provider name as the runner itself reports it (e.g.
|
|
892
|
+
* "OpenCode Zen" for `runnerProvider: 'opencode'`). Populated from the
|
|
893
|
+
* runner's own provider catalog when available; when absent the UI derives a
|
|
894
|
+
* label from `runnerProvider` by title-casing.
|
|
895
|
+
*/
|
|
896
|
+
runnerProviderLabel?: string;
|
|
897
|
+
/**
|
|
898
|
+
* The runner whose catalog listed this model. Stamped once, at the single
|
|
899
|
+
* `ModelDiscovery.discover` choke point, so a flat cross-runner list can
|
|
900
|
+
* still say where each entry came from — `runnerProvider` alone cannot:
|
|
901
|
+
* OpenCode reports most of its catalog as `openrouter`, which names the
|
|
902
|
+
* serving backend, not the agent Ordewell would spawn.
|
|
903
|
+
*/
|
|
904
|
+
runnerId?: string;
|
|
905
|
+
/** The runner's display name (`OpenCode`), from its manifest. */
|
|
906
|
+
runnerLabel?: string;
|
|
907
|
+
variants: {
|
|
908
|
+
id: string;
|
|
909
|
+
label: string;
|
|
910
|
+
}[];
|
|
911
|
+
/**
|
|
912
|
+
* The model's context window when the runner or catalog reports it (#49).
|
|
913
|
+
* Read by the planner-model lookup so context fill can be shown; absent when
|
|
914
|
+
* unknown rather than defaulted to zero.
|
|
915
|
+
*/
|
|
916
|
+
contextWindow?: number;
|
|
917
|
+
}
|
|
918
|
+
type PlanStatus = 'draft' | 'approved' | 'rejected' | 'running' | 'completed';
|
|
919
|
+
/**
|
|
920
|
+
* One entry of the planner's persisted dialogue (ADR-0002). The single source
|
|
921
|
+
* of truth for both UI redisplay and conversational context. Tool-call results
|
|
922
|
+
* are NOT stored here — they live in the AI service's tool-use history;
|
|
923
|
+
* `researchLog` remains the persisted tool trace for the UI.
|
|
924
|
+
*/
|
|
925
|
+
interface ConversationMessage {
|
|
926
|
+
role: 'user' | 'assistant';
|
|
927
|
+
content: string;
|
|
928
|
+
timestamp: string;
|
|
929
|
+
/**
|
|
930
|
+
* Timeline marker: 'plan_generated' records the point in the dialogue where
|
|
931
|
+
* the plan was committed (the UI anchors the plan card there on restore);
|
|
932
|
+
* 'system' is a host-injected notice; 'compaction' is the summary a
|
|
933
|
+
* user-triggered compaction left in place of the earlier messages — always
|
|
934
|
+
* the transcript's first entry. 'skill_load' is a skill loaded into the
|
|
935
|
+
* conversation, its snapshot in `skill`; one the user invoked follows the
|
|
936
|
+
* message that named it. Absent for ordinary chat turns, so sessions saved
|
|
937
|
+
* before markers existed degrade gracefully.
|
|
938
|
+
*/
|
|
939
|
+
kind?: 'plan_generated' | 'system' | 'compaction' | 'skill_load';
|
|
940
|
+
/** Present exactly when `kind` is 'skill_load'. */
|
|
941
|
+
skill?: SkillLoad;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* A skill's SKILL.md as it was when loaded into a planner conversation. A
|
|
945
|
+
* snapshot rather than a reference: a resumed, forked or rewound conversation
|
|
946
|
+
* replays the body the planner originally saw, even after the file changed or
|
|
947
|
+
* was deleted.
|
|
948
|
+
*/
|
|
949
|
+
interface SkillLoad {
|
|
950
|
+
invokedBy: 'user' | 'planner';
|
|
951
|
+
name: string;
|
|
952
|
+
source: SkillSource;
|
|
953
|
+
/** The SKILL.md that won, home-abbreviated (`~/...`): which copy loaded is what a reader needs from it. */
|
|
954
|
+
path: string;
|
|
955
|
+
content: string;
|
|
956
|
+
/**
|
|
957
|
+
* Set when the user named a task skill: it is not loaded into the planner's
|
|
958
|
+
* context, which is only told to attach it to the tasks it fits. `content`
|
|
959
|
+
* is empty; the description is what the planner's directive quotes.
|
|
960
|
+
*/
|
|
961
|
+
attaches?: {
|
|
962
|
+
description: string;
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
/** A skill load as a surface announces it — the body stays with the transcript. */
|
|
966
|
+
type SkillLoadNotice = Omit<SkillLoad, 'content'>;
|
|
967
|
+
/** A message the user sent, as opposed to a skill load their message caused. */
|
|
968
|
+
declare function isUserMessage(entry: ConversationMessage): boolean;
|
|
969
|
+
interface QueuedMessage {
|
|
970
|
+
id: string;
|
|
971
|
+
text: string;
|
|
972
|
+
timestamp: string;
|
|
973
|
+
/** What its `/name` tokens loaded when it was sent: the drain composes them with the text, as a live send does. */
|
|
974
|
+
skills?: SkillLoad[];
|
|
975
|
+
}
|
|
976
|
+
interface PlanModificationWarnings {
|
|
977
|
+
deletedCompleted: string[];
|
|
978
|
+
changedCompleted: string[];
|
|
979
|
+
deletedInProgress: string[];
|
|
980
|
+
modifiedInProgress: string[];
|
|
981
|
+
brokenDependencies: string[];
|
|
982
|
+
}
|
|
983
|
+
declare function emptyWarnings(): PlanModificationWarnings;
|
|
984
|
+
interface LegacyPlanState {
|
|
985
|
+
tasks: Task[];
|
|
986
|
+
generatedAt: string;
|
|
987
|
+
status: PlanStatus;
|
|
988
|
+
runners: RunnerId[];
|
|
989
|
+
lastUpdated: string;
|
|
990
|
+
researchLog?: ResearchLogEntry[];
|
|
991
|
+
/** The planner dialogue — user messages and assistant messages, in order (ADR-0002). */
|
|
992
|
+
conversationHistory?: ConversationMessage[];
|
|
993
|
+
/** Full markdown PRD once written by the planner (PRD mode), also saved to .scratch/<slug>/PRD.md. */
|
|
994
|
+
prdMarkdown?: string;
|
|
995
|
+
/** Follow-ups queued while tasks execute — applied as plan modifications between batches. */
|
|
996
|
+
queuedMessages?: QueuedMessage[];
|
|
997
|
+
/**
|
|
998
|
+
* The plan's isolation run (ADR-0013), written from the orchestrator at
|
|
999
|
+
* persist time and read back only when a saved plan is adopted. It names
|
|
1000
|
+
* branches and worktrees that belong to this plan alone: a fork of the plan
|
|
1001
|
+
* must leave it behind rather than share it.
|
|
1002
|
+
*/
|
|
1003
|
+
isolation?: PlanIsolation;
|
|
1004
|
+
/** Kept so a reopened session shows the same token line (#49). */
|
|
1005
|
+
plannerUsage?: PlannerUsage;
|
|
1006
|
+
}
|
|
1007
|
+
interface Message {
|
|
1008
|
+
id: string;
|
|
1009
|
+
role: 'user' | 'planner' | 'system';
|
|
1010
|
+
content: string;
|
|
1011
|
+
timestamp: number;
|
|
1012
|
+
}
|
|
1013
|
+
interface TaskSnapshot extends Task {
|
|
1014
|
+
completedAt: number;
|
|
1015
|
+
verdict?: Verdict;
|
|
1016
|
+
retryCount: number;
|
|
1017
|
+
finalized: boolean;
|
|
1018
|
+
}
|
|
1019
|
+
type PlanState = {
|
|
1020
|
+
phase: 'planning';
|
|
1021
|
+
history: Message[];
|
|
1022
|
+
message: string;
|
|
1023
|
+
pendingTasks: Task[];
|
|
1024
|
+
} | {
|
|
1025
|
+
phase: 'executing';
|
|
1026
|
+
history: Message[];
|
|
1027
|
+
message: string;
|
|
1028
|
+
executionLog: TaskSnapshot[];
|
|
1029
|
+
pendingTasks: Task[];
|
|
1030
|
+
goal: string;
|
|
1031
|
+
runners: string[];
|
|
1032
|
+
status: PlanStatus;
|
|
1033
|
+
};
|
|
1034
|
+
declare function migratePlanState(raw: unknown): PlanState;
|
|
1035
|
+
declare function migrateLegacyPlan(legacy: LegacyPlanState): PlanState;
|
|
1036
|
+
|
|
1037
|
+
declare function createTask(overrides?: Partial<Task>): Task;
|
|
1038
|
+
declare function isSkillName(name: string): boolean;
|
|
1039
|
+
/**
|
|
1040
|
+
* The `skills` field as a task stores it, however it arrived: the planner's
|
|
1041
|
+
* JSON is untyped. Names are trimmed, lower-cased and deduplicated, and one
|
|
1042
|
+
* no skill could have is dropped; none at all is stored absent, so a plan
|
|
1043
|
+
* without skills reads exactly as it did before them.
|
|
1044
|
+
*/
|
|
1045
|
+
declare function skillNames(value: unknown): string[] | undefined;
|
|
1046
|
+
/**
|
|
1047
|
+
* The skills of a task a merge or a split makes from others: what the edit
|
|
1048
|
+
* chose when it named any, none when it chose an explicit `[]`, else what the
|
|
1049
|
+
* tasks it came from had — a malformed value is not a request to drop them.
|
|
1050
|
+
*/
|
|
1051
|
+
declare function inheritedSkills(chosen: unknown, inherited: readonly string[] | undefined): string[] | undefined;
|
|
1052
|
+
/**
|
|
1053
|
+
* The `ops` flag as a task stores it (ADR-0020), however it arrived. Only a
|
|
1054
|
+
* literal `true` on an AI task marks an ops task: the planner's JSON is
|
|
1055
|
+
* untyped, and a quoted "true" read as ops would move a task out of its
|
|
1056
|
+
* worktree. Anything else is a change task, stored absent so saved plans need
|
|
1057
|
+
* no migration. Whether the task may be ops at all — a subtask may not — is
|
|
1058
|
+
* for whoever knows where it sits: parsing drops the flag, an edit refuses it.
|
|
1059
|
+
*/
|
|
1060
|
+
declare function opsFlag(value: unknown, type?: Task['type']): true | undefined;
|
|
1061
|
+
/**
|
|
1062
|
+
* The flag of a task a merge or a split makes from others: what the edit
|
|
1063
|
+
* chose, else ops only when every task it came from was. A merge that takes
|
|
1064
|
+
* in a change edits files, so it is a change; a split part runs where its
|
|
1065
|
+
* task did.
|
|
1066
|
+
*/
|
|
1067
|
+
declare function inheritedOps(from: readonly Pick<Task, 'ops'>[], chosen?: boolean): boolean;
|
|
1068
|
+
/** The runner set a plan has until one is chosen: the one runner every host ships. */
|
|
1069
|
+
declare const DEFAULT_RUNNERS: readonly RunnerId[];
|
|
1070
|
+
declare function createEmptyPlan(): LegacyPlanState;
|
|
1071
|
+
declare function flattenTasks(tasks: readonly Task[]): Task[];
|
|
1072
|
+
/** A flattened task with the parent it hangs under, null for a top-level task. */
|
|
1073
|
+
interface TaskWithParent {
|
|
1074
|
+
task: Task;
|
|
1075
|
+
parent: Task | null;
|
|
1076
|
+
}
|
|
1077
|
+
declare function flattenTasksWithParents(tasks: readonly Task[]): TaskWithParent[];
|
|
1078
|
+
declare function migrateTask(task: Record<string, unknown>): Task;
|
|
1079
|
+
declare function addTaskToPlan(tasks: readonly Task[], partial: Partial<Task>): Task[];
|
|
1080
|
+
declare function removeTaskFromPlan(tasks: readonly Task[], taskId: string): Task[];
|
|
1081
|
+
declare function updateTaskInPlan(tasks: readonly Task[], taskId: string, changes: Partial<Task>): Task[];
|
|
1082
|
+
declare function renumberTasks(tasks: readonly Task[]): Task[];
|
|
1083
|
+
/**
|
|
1084
|
+
* Lay a planner-written task list over the plan it rewrites without letting it
|
|
1085
|
+
* change execution state. A planner restates tasks; it never witnessed one run,
|
|
1086
|
+
* so the status it writes is not evidence. A settled task is kept exactly as
|
|
1087
|
+
* it stands wherever the rewrite names it, and put back beside its old
|
|
1088
|
+
* neighbour where the rewrite leaves it out. Every other task keeps the status
|
|
1089
|
+
* it had; a task the rewrite adds starts pending.
|
|
1090
|
+
*/
|
|
1091
|
+
declare function keepExecutionState(current: readonly Task[], rewrite: Task[]): Task[];
|
|
1092
|
+
/** What only execution reads; {@link keepExecutionState} puts it back on whatever a planner returns. */
|
|
1093
|
+
declare const EXECUTION_ONLY: readonly ["attemptSkills", "runnerSessionId", "awaitingReason", "forcedPastGate", "outputSummary"];
|
|
1094
|
+
type PlannerTaskView = Omit<Task, typeof EXECUTION_ONLY[number] | 'subtasks'> & {
|
|
1095
|
+
subtasks: PlannerTaskView[];
|
|
1096
|
+
};
|
|
1097
|
+
/**
|
|
1098
|
+
* A task as a planner prompt carries it. An attempt's skill snapshots are
|
|
1099
|
+
* whole SKILL.md bodies and an output summary holds a log tail: sent on every
|
|
1100
|
+
* planner call, they cost tokens and invite the planner to edit state it does
|
|
1101
|
+
* not own.
|
|
1102
|
+
*/
|
|
1103
|
+
declare function plannerTaskView(task: Task): PlannerTaskView;
|
|
1104
|
+
declare function validateModifiedPlan(original: Task[], modified: Task[]): PlanModificationWarnings;
|
|
1105
|
+
interface ActiveTaskSession {
|
|
1106
|
+
id: string;
|
|
1107
|
+
taskId: string;
|
|
1108
|
+
}
|
|
1109
|
+
interface ValidationResult {
|
|
1110
|
+
valid: boolean;
|
|
1111
|
+
errors: string[];
|
|
1112
|
+
}
|
|
1113
|
+
interface ValidationContext {
|
|
1114
|
+
executionLog: TaskSnapshot[];
|
|
1115
|
+
oldPending: Task[];
|
|
1116
|
+
newPending: Task[];
|
|
1117
|
+
activeSessions: Map<string, ActiveTaskSession>;
|
|
1118
|
+
}
|
|
1119
|
+
type ValidationCheck = (ctx: ValidationContext) => ValidationResult;
|
|
1120
|
+
declare function warningsText(w: PlanModificationWarnings): string | null;
|
|
1121
|
+
|
|
1122
|
+
export { type SkillLoad as $, type ActiveTaskSession as A, BUILTIN_SKILL_NAMES as B, type ConversationMessage as C, DEFAULT_RUNNERS as D, type PlanStatus as E, type PlannerTaskView as F, type PlannerUsage as G, type PreparedTask as H, type IWorktreeIsolation as I, type PreservedWork as J, type RepairEvidence as K, type LegacyPlanState as L, type Message as M, type RepoGroupLayout as N, type ResearchLogEntry as O, type PlanIsolation as P, type QueuedMessage as Q, type RefusedRemoval as R, type ResearchProgress as S, type ResearchStep as T, type ResearchStepOutcome as U, type ResearchToolType as V, type RunnerId as W, type ShadowedSkill as X, type SkillAppliesTo as Y, type SkillCatalog as Z, type SkillInfo as _, type AwaitingReason as a, warningsText as a$, type SkillLoadNotice as a0, type SkillMetadata as a1, type SkillSource as a2, SkillsService as a3, type StreamEvent as a4, type StreamStepEvent as a5, type StreamThinkingEvent as a6, type SubagentLogEntry as a7, type SubagentOutcome as a8, type Task as a9, createEmptyPlan as aA, createSkillsService as aB, createTask as aC, emptyWarnings as aD, flattenTasks as aE, flattenTasksWithParents as aF, inheritedOps as aG, inheritedSkills as aH, isAwaitingReason as aI, isMeasured as aJ, isSkillName as aK, isUserMessage as aL, keepExecutionState as aM, migrateLegacyPlan as aN, migrateLoadedTasks as aO, migratePlanState as aP, migrateTask as aQ, opsFlag as aR, partedPromptUsage as aS, plannerContextFill as aT, plannerTaskView as aU, removeTaskFromPlan as aV, renumberTasks as aW, skillNames as aX, updateTaskInPlan as aY, usageLine as aZ, validateModifiedPlan as a_, type TaskIsolation as aa, type TaskIsolationState as ab, type TaskMode as ac, type TaskModelAssignment as ad, type TaskOutputSummary as ae, type TaskSkillNotice as af, type TaskSkillSnapshot as ag, type TaskSnapshot as ah, type TaskStatus as ai, type TaskType as aj, type TaskWithParent as ak, type ThinkingBlock as al, type TreeSnapshot as am, type UsageLine as an, type UsageRecord as ao, type UsageTotals as ap, type UserPromptEntry as aq, type UserStep as ar, type ValidationCheck as as, type ValidationContext as at, type ValidationResult as au, type Verdict as av, type VerificationCheck as aw, addPlannerUsage as ax, addTaskToPlan as ay, addUsage as az, type DiscoveredMode as b, type DiscoveredModel as c, type IntegrationDisposal as d, type InvalidSkill as e, type IsolationAvailability as f, type IsolationHandoff as g, type IsolationHandoffRepo as h, type IsolationInactiveReason as i, type IsolationLandedTask as j, type IsolationLanding as k, type IsolationMergeBlock as l, type IsolationMergeBlockReason as m, type IsolationMergeResult as n, type IsolationOutcome as o, type IsolationPruneResult as p, type IsolationRemoval as q, type IsolationRepo as r, type IsolationRun as s, type IsolationTaskRecord as t, type IsolationTaskRef as u, type IsolationTaskRepo as v, type IsolationTaskStatus as w, type IsolationView as x, type PlanModificationWarnings as y, type PlanState as z };
|