@ordewell/core 0.5.5 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/IFileSystem-BkPX7mLD.d.mts +76 -0
  2. package/dist/IFileSystem-C0l-4MGT.d.ts +76 -0
  3. package/dist/{ModeResolver-D3XO0fT9.d.ts → ModeResolver--16lh7dS.d.mts} +1 -1
  4. package/dist/{ModeResolver-D-SUFRNF.d.mts → ModeResolver-CjpG5Wli.d.ts} +1 -1
  5. package/dist/Task-Dyxp67s2.d.mts +2086 -0
  6. package/dist/Task-Dyxp67s2.d.ts +2086 -0
  7. package/dist/{chunk-T2S5O36I.mjs → chunk-C44UWIAD.mjs} +6 -6
  8. package/dist/chunk-C44UWIAD.mjs.map +1 -0
  9. package/dist/{chunk-HD2FWPRV.mjs → chunk-EDGUFCIR.mjs} +6 -1
  10. package/dist/chunk-EDGUFCIR.mjs.map +1 -0
  11. package/dist/{chunk-UUBGVCGJ.mjs → chunk-JBEFAJ2W.mjs} +2 -2
  12. package/dist/{chunk-KLN7ELXO.mjs → chunk-ROVYWEBI.mjs} +1560 -1269
  13. package/dist/chunk-ROVYWEBI.mjs.map +1 -0
  14. package/dist/index.d.mts +1414 -785
  15. package/dist/index.d.ts +1414 -785
  16. package/dist/index.js +7008 -4389
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +6075 -3767
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/order-labels.d.mts +3 -1
  21. package/dist/order-labels.d.ts +3 -1
  22. package/dist/{parsing-CDtRSxBY.d.mts → parsing-DPEpAszP.d.mts} +2 -2
  23. package/dist/{parsing-BTP4bwkk.d.ts → parsing-EcmCsF1y.d.ts} +2 -2
  24. package/dist/parsing.d.mts +5 -3
  25. package/dist/parsing.d.ts +5 -3
  26. package/dist/parsing.js.map +1 -1
  27. package/dist/parsing.mjs +2 -2
  28. package/dist/{plan-utils-pE4TBwxl.d.mts → plan-utils-BAvW3hvl.d.mts} +212 -43
  29. package/dist/{plan-utils-BFaPo-IT.d.ts → plan-utils-BMyEiDKv.d.ts} +212 -43
  30. package/dist/plan-utils.d.mts +5 -4
  31. package/dist/plan-utils.d.ts +5 -4
  32. package/dist/plan-utils.js +351 -64
  33. package/dist/plan-utils.js.map +1 -1
  34. package/dist/plan-utils.mjs +11 -5
  35. package/dist/testing.d.mts +54 -4
  36. package/dist/testing.d.ts +54 -4
  37. package/dist/testing.js +93 -2
  38. package/dist/testing.js.map +1 -1
  39. package/dist/testing.mjs +91 -2
  40. package/dist/testing.mjs.map +1 -1
  41. package/package.json +2 -1
  42. package/skills/grilling/SKILL.md +6 -16
  43. package/skills/improve-codebase-architecture/SKILL.md +1 -1
  44. package/dist/ApprovalPolicy-BVhGdECT.d.mts +0 -79
  45. package/dist/ApprovalPolicy-BVhGdECT.d.ts +0 -79
  46. package/dist/ITerminalRunner-BV9Rd2o9.d.ts +0 -563
  47. package/dist/ITerminalRunner-C77ZNZS9.d.mts +0 -563
  48. package/dist/Task-Vl5Zq_D-.d.mts +0 -825
  49. package/dist/Task-Vl5Zq_D-.d.ts +0 -825
  50. package/dist/chunk-HD2FWPRV.mjs.map +0 -1
  51. package/dist/chunk-KLN7ELXO.mjs.map +0 -1
  52. package/dist/chunk-T2S5O36I.mjs.map +0 -1
  53. /package/dist/{chunk-UUBGVCGJ.mjs.map → chunk-JBEFAJ2W.mjs.map} +0 -0
@@ -1,825 +0,0 @@
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' | 'nested-repos';
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; otherwise the nested ones `nested-repos` refuses, the dirty
12
- * ones of a `dirty` group, or the commitless ones of a `no-commits` group. A
13
- * group of one names none.
14
- */
15
- type IsolationAvailability = {
16
- active: true;
17
- repos?: string[];
18
- shared?: string[];
19
- } | {
20
- active: false;
21
- reason: IsolationInactiveReason;
22
- repos?: string[];
23
- };
24
- /**
25
- * Where a run's tasks work, as the planner is told it: the repos of the group
26
- * and the paths shared live between tasks. A lone repository is `['.']` with
27
- * nothing shared.
28
- */
29
- interface RepoGroupLayout {
30
- repos: string[];
31
- shared: string[];
32
- }
33
- type IsolationOutcome = 'merged' | 'conflict' | 'failed';
34
- /**
35
- * `active` — worktree exists, a runner may be writing to it.
36
- * `kept` — released with its worktree and branch preserved for inspection.
37
- * `conflict` — integration stopped on a merge conflict; worktree and refs kept.
38
- * `repairing` — a conflict repair (ADR-0015) is working in the kept worktree;
39
- * one that ends without landing leaves the task `conflict` again.
40
- * `failed` — integration hit a git error other than a conflict; refs kept.
41
- * `merged` — landed on the integration branch; worktree and task branch removed.
42
- */
43
- type IsolationTaskStatus = 'active' | 'kept' | 'conflict' | 'repairing' | 'failed' | 'merged';
44
- /** One repo's share of a task: its worktree inside the task workspace. */
45
- interface IsolationTaskRepo {
46
- /** Absolute path of this repo's worktree: the task workspace joined with the repo's path. */
47
- worktree: string;
48
- /**
49
- * Paths bootstrapped from the real repo (symlinks, junctions, copies).
50
- * Recorded so the commit step can leave them out — a symlink is not matched
51
- * by a `node_modules/` ignore rule and would otherwise be committed.
52
- */
53
- linked: string[];
54
- /** Whether the task brought commits to this repo; unknown until it first integrates. */
55
- changed?: boolean;
56
- }
57
- interface IsolationTaskRecord {
58
- taskId: string;
59
- order: number;
60
- title: string;
61
- /** One branch name, the same in every repo, so a task is one name to look up across the group. */
62
- branch: string;
63
- /**
64
- * Absolute path of the task workspace, holding one worktree per repo at the
65
- * repo's path. The Runner's cwd is inside it when the workspace is a repo
66
- * subdirectory; for a group of one it is the worktree.
67
- */
68
- workspace: string;
69
- /** For the task as a whole: landing is atomic across the repos it changed. */
70
- status: IsolationTaskStatus;
71
- /** Keyed by repo path. */
72
- repos: Record<string, IsolationTaskRepo>;
73
- /** The repo whose merge stopped the task from landing, while `status` is `conflict` or `failed`. */
74
- conflictRepo?: string;
75
- /**
76
- * What a `failed` landing stopped on, in words a surface can repeat — a
77
- * missing worktree is the common one. Cleared by the next landing attempt,
78
- * so a stale reason cannot outlive the failure it explains.
79
- */
80
- landingError?: string;
81
- /** Repo-relative paths, in `conflictRepo`, that conflicted; set only while `status` is `conflict` or `repairing`. */
82
- conflictFiles?: string[];
83
- /**
84
- * Conflict repairs started for this task (ADR-0015). Counted when one starts,
85
- * so a crash cannot hand the spent attempt back; absent reads as none.
86
- */
87
- repairs?: number;
88
- /**
89
- * Keyed by repo path: each changed repo's integration tip when the repair in
90
- * flight started — what the task branch must contain before it may land.
91
- * Set only while `status` is `repairing`.
92
- */
93
- repairBase?: Record<string, string>;
94
- /**
95
- * Every file a repair was started for, across all of them: as `conflictFiles`
96
- * names it in a group of one, prefixed with its repo's path in a group.
97
- */
98
- repairedFiles?: string[];
99
- }
100
- /**
101
- * A task's landing in flight: each changed repo's integration tip from before
102
- * the task's merge. On the run rather than the task record because it must
103
- * outlive that record — a retry drops and recreates it — until every repo is
104
- * back at its tip or the task has landed.
105
- */
106
- interface IsolationLanding {
107
- taskId: string;
108
- /** Keyed by repo path. */
109
- tips: Record<string, string>;
110
- }
111
- /**
112
- * One repository of the group (ADR-0014). Every git operation on it runs in
113
- * `root`, never in the workspace root.
114
- */
115
- interface IsolationRepo {
116
- /** Relative to the workspace root; `.` when the workspace is itself the repository. */
117
- path: string;
118
- /**
119
- * Absolute: the workspace root joined with `path`. For a group of one that is
120
- * the workspace root, which may be a subdirectory of the repository.
121
- */
122
- root: string;
123
- /** The commit checked out at run start. Switching branches mid-run does not retarget it. */
124
- baseRef: string;
125
- /** Branch name checked out at run start; absent on a detached HEAD. */
126
- baseBranch?: string;
127
- integrationBranch: string;
128
- }
129
- /**
130
- * One Execute-Plan click or one manual task run over the workspace's repo
131
- * group. Plain JSON on purpose: the orchestrator persists it with the plan
132
- * state so a resumed session can find its integration branches again. The
133
- * module mutates `tasks` in place.
134
- */
135
- interface IsolationRun {
136
- id: string;
137
- workspaceRoot: string;
138
- repos: IsolationRepo[];
139
- /**
140
- * Workspace paths outside every isolated repo, linked live into each task
141
- * workspace: loose entries of the workspace root, the entries beside a deeper
142
- * repo, and `sharedRepos`. Empty for a group of one.
143
- */
144
- shared: string[];
145
- /** Repos of the group that could not be isolated — no commits, or git refused a worktree — and are among `shared`. */
146
- sharedRepos: string[];
147
- /** Keyed by task id — ids are unique within one plan and a run belongs to one plan. */
148
- tasks: Record<string, IsolationTaskRecord>;
149
- /**
150
- * Set before a task's first merge and cleared once it has landed or been
151
- * rolled back. One found set — after a crash, or a rollback git refused —
152
- * names exactly what to return each repo's integration branch to.
153
- */
154
- landing?: IsolationLanding;
155
- }
156
- /**
157
- * What a plan persists of isolated execution (`LegacyPlanState.isolation`): its
158
- * run, and which added tasks resolve which conflicts. Belongs to that plan and
159
- * its branches alone, so a copy of the plan (a fork) must not carry it.
160
- */
161
- interface PlanIsolation {
162
- run: IsolationRun;
163
- /** Resolver task id → the conflicted task whose branch it merges. */
164
- resolvers: Record<string, string>;
165
- }
166
- /**
167
- * A task's isolation as a surface shows it. `kept` covers every record whose
168
- * worktree stays for inspection — a failed verdict, an interrupted attempt, an
169
- * integration git refused — because to the user they are one thing: work that
170
- * did not land and can be looked at. `none` is a task with no worktree in a plan
171
- * that has an isolation run.
172
- */
173
- type TaskIsolationState = 'none' | 'active' | 'integrated' | 'conflict' | 'repairing' | 'kept';
174
- type TaskIsolation = {
175
- state: 'none';
176
- } | {
177
- state: Exclude<TaskIsolationState, 'none'>;
178
- branch: string;
179
- /** The task workspace; for a group of one, the task's worktree. */
180
- worktree: string;
181
- /** Paths of the repos the task changed. */
182
- repos: string[];
183
- conflictRepo?: string;
184
- /** Repo-relative paths, in `conflictRepo`, that conflicted. */
185
- conflictFiles?: string[];
186
- /** The conflict repair running or last run, of the most a task may have; absent before its first. */
187
- repair?: {
188
- attempt: number;
189
- limit: number;
190
- };
191
- /** What {@link IsolationTaskRecord.repairedFiles} says. */
192
- repairedFiles?: string[];
193
- };
194
- interface IsolationLandedTask {
195
- taskId: string;
196
- order: number;
197
- title: string;
198
- /** Set when the task landed only after a conflict repair (ADR-0015): the files it was started for. */
199
- repairedFiles?: string[];
200
- }
201
- interface IsolationHandoffRepo {
202
- path: string;
203
- integrationBranch: string;
204
- baseRef: string;
205
- /** Tasks whose work landed in this repo, in plan order. */
206
- landed: IsolationLandedTask[];
207
- }
208
- interface IsolationHandoff {
209
- repos: IsolationHandoffRepo[];
210
- /** Tasks that landed on the integration branches, in plan order. */
211
- landed: IsolationLandedTask[];
212
- }
213
- /** A plan's isolation as a surface shows it: a mark for each task the run touched, and its handoff. */
214
- interface IsolationView {
215
- tasks: Record<string, TaskIsolation>;
216
- handoff: IsolationHandoff;
217
- }
218
- /**
219
- * Why "Merge all" would not touch a repo. `partial-landing`: a task's landing
220
- * was interrupted and could not be rolled back there, so its integration
221
- * branch holds part of a task.
222
- */
223
- type IsolationMergeBlockReason = 'merge-in-progress' | 'conflict' | 'uncommitted-changes' | 'partial-landing' | 'git-error';
224
- interface IsolationMergeBlock {
225
- repo: string;
226
- reason: IsolationMergeBlockReason;
227
- /** The files that would conflict, or the user's uncommitted ones the merge also changes; empty for the other reasons. */
228
- files: string[];
229
- }
230
- /**
231
- * How "Merge all" went.
232
- * - `merged`: every repo with work on its integration branch took it.
233
- * - `blocked`: the preflight found repos that could not, so nothing was
234
- * touched anywhere; `blocked` says which and why.
235
- * - `conflict` / `failed`: a merge stopped in `repo` — on git older than 2.38,
236
- * which cannot preflight, or for a reason no preflight could foresee. That
237
- * merge was aborted, leaving `repo` as it was; `landed` names the repos
238
- * merged before it, which stay merged, and is absent when there are none.
239
- *
240
- * A group of one is blocked only by a partial landing; otherwise its one merge
241
- * lands or is aborted whole, so it reports as it always has.
242
- */
243
- type IsolationMergeResult = {
244
- outcome: 'merged';
245
- } | {
246
- outcome: 'blocked';
247
- blocked: IsolationMergeBlock[];
248
- } | {
249
- outcome: 'conflict' | 'failed';
250
- repo: string;
251
- files?: string[];
252
- landed?: string[];
253
- };
254
- /**
255
- * What `discard` does with each repo's integration branch: `keep` it for review
256
- * or merge, `delete` it, or delete it only in the repos whose checked-out HEAD
257
- * already contains it (`delete-merged`) — the one way that can never give up
258
- * landed work the user has not merged.
259
- */
260
- type IntegrationDisposal = 'keep' | 'delete' | 'delete-merged';
261
- /**
262
- * Whether a conflict repair's work may land. `not-merged`: the task branch in
263
- * `repo` does not contain the tip the repair started from. `conflict-markers`:
264
- * it adds leftover conflict markers to `files`. `failed`: git could not tell.
265
- */
266
- type RepairEvidence = {
267
- ok: true;
268
- } | {
269
- ok: false;
270
- reason: 'not-merged' | 'conflict-markers' | 'failed';
271
- repo: string;
272
- files?: string[];
273
- };
274
- interface PreparedTask {
275
- cwd: string;
276
- branch: string;
277
- /**
278
- * Paths, relative to the task workspace, that are copies rather than links
279
- * because a hard link was impossible (Windows, another volume). Edits to them
280
- * stay in the task, so the user is told.
281
- */
282
- copied: string[];
283
- }
284
- /**
285
- * What a crash-recovery prune found and left alone: task records that were
286
- * `active` yet still held unlanded work, so the prune kept them as `kept`
287
- * rather than deleting work no one else has.
288
- */
289
- interface IsolationPruneResult {
290
- kept: Array<{
291
- taskId: string;
292
- order: number;
293
- title: string;
294
- }>;
295
- }
296
- interface IWorktreeIsolation {
297
- /**
298
- * A repo group with at least one repo to isolate, a clean tracked tree in
299
- * each, and the config enabled; otherwise the reason it is not.
300
- */
301
- isActive(workspaceRoot: string): Promise<IsolationAvailability>;
302
- /**
303
- * Put the tracked changes of every dirty repo of the group on its git stash,
304
- * the user's way out of a `dirty` refusal. Untracked files stay: they never
305
- * block isolation.
306
- */
307
- stash(workspaceRoot: string): Promise<void>;
308
- /**
309
- * Mint a run: resolve each repo's base ref to a commit now, and share the
310
- * repos that cannot be isolated. Only meaningful after `isActive` said yes;
311
- * throws when no repo of the group can be isolated after all.
312
- */
313
- startRun(workspaceRoot: string): Promise<IsolationRun>;
314
- /**
315
- * Create the task workspace — one worktree per isolated repo from its
316
- * integration tip, the shared paths linked in — and return the cwd to spawn
317
- * the Runner into. A second `prepare` for the same task is a retry: the old
318
- * attempt is discarded and the workspace recreated from the tips, so the
319
- * task sees everything its predecessors have integrated.
320
- */
321
- prepare(task: Task, run: IsolationRun): Promise<PreparedTask>;
322
- /**
323
- * Hand a conflicted task's kept workspace to a conflict repair (ADR-0015)
324
- * as it is — nothing is re-cut — and return the same cwd. Records each
325
- * changed repo's integration tip as `repairBase`, counts the repair, and
326
- * moves the task to `repairing`. Throws for a task that is not `conflict`.
327
- */
328
- reopen(task: Task, run: IsolationRun): Promise<PreparedTask>;
329
- /**
330
- * The evidence a repair must show before it lands: its work committed, and
331
- * in each repo of `repairBase` the task branch containing that tip
332
- * (`git merge-base --is-ancestor`) and adding no leftover conflict markers
333
- * (`git diff --check`; whitespace warnings do not count). Changes nothing
334
- * else: a task that fails stays `repairing` until released.
335
- */
336
- verifyRepair(task: Task, run: IsolationRun): Promise<RepairEvidence>;
337
- /**
338
- * Land the task atomically across the repos it changed: commit each
339
- * worktree, then `git merge --no-ff` the task branch into each changed
340
- * repo's integration branch. If any merge conflicts or fails, it is aborted
341
- * and the merges already made for the task are reset away, so `merged`
342
- * always means the whole task landed. Serialized inside the module; among
343
- * tasks waiting at once the lowest plan order goes first. On anything but
344
- * `merged` the worktrees and refs stay, and nothing is resolved here: a
345
- * conflict is repaired, if at all, by a new attempt of the task in its own
346
- * worktree (ADR-0015), never inside this queue.
347
- *
348
- * `persist` is called once `run.landing` is set and before the first
349
- * merge; the caller saves the run there, synchronously, which is what
350
- * lets `pruneOrphans` finish a landing a crash interrupted.
351
- */
352
- integrate(task: Task, run: IsolationRun, persist?: () => void): Promise<IsolationOutcome>;
353
- /**
354
- * `keep: false` removes the task's worktree, branch and record (retry, task
355
- * removal). `keep: true` leaves the worktree and branch exactly as they are
356
- * for inspection — a failed verdict, a stop, a cancel — and only moves the task off `active`,
357
- * so a crash-recovery prune does not sweep it away; a repair it ends leaves
358
- * the task `conflict`, as it was before the repair. Takes the run rather than
359
- * a bare task id: ids are only unique within one plan, and one daemon serves
360
- * many (ADR-0007).
361
- */
362
- release(run: IsolationRun, taskId: string, opts: {
363
- keep: boolean;
364
- }): Promise<void>;
365
- /** End of run: park the integration branch for review and report what landed. */
366
- handoff(run: IsolationRun): Promise<IsolationHandoff>;
367
- /**
368
- * Drop what a crash left behind: a landing it interrupted is rolled back in
369
- * every repo, a repair it interrupted leaves its task `conflict`, then stale
370
- * active worktrees and directories no record owns go. An `active` record
371
- * that still holds unlanded work — commits its branch alone carries, or
372
- * edits in its worktree — is not a crash orphan: it may belong to a runner
373
- * another host is still driving, so it is kept as `kept` and named in the
374
- * result.
375
- */
376
- pruneOrphans(run: IsolationRun): Promise<IsolationPruneResult>;
377
- /** Unified diff of each repo's integration branch against its base ref. */
378
- reviewDiff(run: IsolationRun): Promise<string>;
379
- /**
380
- * "Merge all": merge each repo's integration branch into whatever the user
381
- * has checked out there. The one irreversible step, so it only ever happens
382
- * when a caller asks for it. Every repo with work is preflighted first — no
383
- * merge of the user's in progress, no conflict against their HEAD, no
384
- * uncommitted edit to a file the merge changes — and unless all pass,
385
- * nothing is merged anywhere. Only a merge Ordewell itself just started is
386
- * ever aborted; nothing of the user's is reset.
387
- */
388
- mergeIntoCheckedOut(run: IsolationRun): Promise<IsolationMergeResult>;
389
- /**
390
- * Remove every worktree and task branch of the run, and settle each repo's
391
- * integration branch as `integration` says. Anything but `keep` also clears
392
- * the run's task records.
393
- */
394
- discard(run: IsolationRun, opts: {
395
- integration: IntegrationDisposal;
396
- }): Promise<void>;
397
- /**
398
- * Clear what other runs left in each repo of `run`'s group: every
399
- * `ordewell/<run-id>/…` branch the repo's checked-out HEAD already contains.
400
- * Never a branch of `run` itself, one a worktree has checked out, or any
401
- * branch of a run that still has a worktree — that run may be live in
402
- * another plan. Tries every repo, then throws naming those where git failed.
403
- */
404
- sweep(run: IsolationRun): Promise<void>;
405
- }
406
-
407
- /**
408
- * What one model call consumed, as its provider or runner reported it. Shared
409
- * by the planner's usage line (#49) and per-attempt task usage (#26): a record
410
- * says nothing about who asked for the call, so either can hold a list of them.
411
- *
412
- * Every measure is optional because backends report different subsets. An
413
- * absent field means "not reported" — never zero — so a total can tell the two
414
- * apart.
415
- */
416
- interface UsageRecord {
417
- /** The provider or runner id that reported the call — `openai`, `claude-code`, … */
418
- source: string;
419
- model?: string;
420
- inputTokens?: number;
421
- outputTokens?: number;
422
- /** The share of `inputTokens` served from the provider's prompt cache. */
423
- cachedInputTokens?: number;
424
- /**
425
- * Filled only from a provider's or runner's own report, never from a price
426
- * table or an estimate: prices go stale, and a subscription runner has no
427
- * per-token price at all. No report means no cost, not a guessed one.
428
- */
429
- reportedCost?: {
430
- amount: number;
431
- currency: string;
432
- };
433
- /** The model's context window, when the runner itself reports it. */
434
- contextWindow?: number;
435
- /** Set when a subagent made the call; its usage still counts toward the total. */
436
- subagentId?: string;
437
- }
438
- /**
439
- * A running sum of {@link UsageRecord}s. A measure stays absent until some
440
- * record reports it. Cost is kept per currency: two runners may bill in
441
- * different ones, and there is no honest exchange rate to fold them together.
442
- */
443
- interface UsageTotals {
444
- inputTokens?: number;
445
- outputTokens?: number;
446
- cachedInputTokens?: number;
447
- reportedCost?: Record<string, number>;
448
- }
449
- /**
450
- * The input side of a call whose provider reports its prompt in parts — the
451
- * uncached tail, with cache reads and cache writes beside it rather than inside
452
- * it (Anthropic, and OpenCode after it). The prompt the model saw is all three;
453
- * only the reads were served from cache, since a write is billed as fresh
454
- * input. A part left unreported adds nothing, and with no part reported there
455
- * is no measure at all.
456
- */
457
- declare function partedPromptUsage(parts: {
458
- uncached?: number;
459
- cacheRead?: number;
460
- cacheWrite?: number;
461
- }): Pick<UsageRecord, 'inputTokens' | 'cachedInputTokens'>;
462
- declare function addUsage(totals: UsageTotals, record: UsageRecord): UsageTotals;
463
- /**
464
- * What the planner has consumed over a session (#49). `totals` includes every
465
- * `bySubagent` entry. `lastPromptTokens` and `contextWindow` track the
466
- * planner's own calls only — a subagent runs its own model, whose window says
467
- * nothing about the planner's.
468
- */
469
- interface PlannerUsage {
470
- totals: UsageTotals;
471
- bySubagent?: Record<string, UsageTotals>;
472
- lastPromptTokens?: number;
473
- contextWindow?: number;
474
- }
475
- declare function addPlannerUsage(usage: PlannerUsage, record: UsageRecord): PlannerUsage;
476
- /** Whether any measure was reported: a token line of nothing but blanks says nothing. */
477
- declare function isMeasured(totals: UsageTotals): boolean;
478
- /** What the token line shows of a ledger — live from its broadcast, or reloaded from the saved one. */
479
- interface UsageLine {
480
- totals: UsageTotals;
481
- bySubagent?: Record<string, UsageTotals>;
482
- contextFill?: {
483
- usedTokens: number;
484
- windowTokens: number;
485
- };
486
- }
487
- declare function usageLine(usage: PlannerUsage): UsageLine;
488
- /**
489
- * The last planner prompt against its window, or undefined while either is
490
- * unknown. `usedTokens` is the prompt total as reported, cached tokens
491
- * included: a cached token still occupies the window, so subtracting the
492
- * cached share would understate how full the context is. A window of 0 is
493
- * treated as unknown — never guessed.
494
- */
495
- declare function plannerContextFill(usage: PlannerUsage): {
496
- usedTokens: number;
497
- windowTokens: number;
498
- } | undefined;
499
-
500
- interface UserStep {
501
- order: number;
502
- instruction: string;
503
- completed: boolean;
504
- }
505
- /** One deterministic signal gathered while verifying a completed task. */
506
- interface VerificationCheck {
507
- name: 'exit_code' | 'completion_marker' | 'manual';
508
- passed: boolean;
509
- /** A check that did not apply. Skipped checks don't affect the verdict. */
510
- skipped: boolean;
511
- detail: string;
512
- }
513
- /** Evidence-based verdict for a completed task. Single end-to-end outcome produced by verification. */
514
- interface Verdict {
515
- outcome: 'pass' | 'fail';
516
- reason: string;
517
- checks: VerificationCheck[];
518
- decidedAt: string;
519
- }
520
- interface TaskOutputSummary {
521
- reviewReason: string;
522
- logTail: string;
523
- capturedAt: string;
524
- }
525
- type TaskType = 'ai' | 'user';
526
- type TaskStatus = 'pending' | 'approved' | 'in_progress' | 'completed' | 'failed' | 'blocked' | 'awaiting_user';
527
- type TaskMode = string;
528
- interface TaskModelAssignment {
529
- modelId: string;
530
- modelLabel: string;
531
- thinkingEffort?: string;
532
- /**
533
- * All variant ids the model offered when this assignment was made. Carried
534
- * on the assignment because runners need it at spawn time (opencode's TUI
535
- * only honors an assigned variant when the others are config-disabled) and
536
- * the discovery catalog isn't available there.
537
- */
538
- availableVariants?: string[];
539
- }
540
- type RunnerId = string;
541
- interface Task {
542
- id: string;
543
- order: number;
544
- title: string;
545
- description: string;
546
- type: TaskType;
547
- status: TaskStatus;
548
- dependencies: string[];
549
- prompt?: string;
550
- userSteps?: UserStep[];
551
- subtasks: Task[];
552
- verdict?: Verdict;
553
- outputSummary?: TaskOutputSummary;
554
- assignedModel?: TaskModelAssignment;
555
- assignedRunner: RunnerId;
556
- thinkingEffort?: string;
557
- taskMode?: TaskMode;
558
- completionMarker: string;
559
- autonomy?: 'AFK' | 'HITL';
560
- sliceType?: 'HITL' | 'AFK';
561
- userStoriesCovered?: string[];
562
- }
563
- interface DiscoveredMode {
564
- id: string;
565
- label: string;
566
- description: string;
567
- }
568
- type ResearchToolType = 'read_file' | 'read_files' | 'glob' | 'grep' | 'find_symbol' | 'list_dir' | 'bash' | 'fetch' | 'web_search' | 'spawn_research_agent'
569
- /**
570
- * A tool belonging to a harness planner's own toolbox (ADR-0009) that has no
571
- * Ordewell equivalent — Edit, WebFetch, TodoWrite, whatever a coding agent
572
- * ships next. The real name travels in `toolLabel` rather than being
573
- * relabelled as a tool it is not; the union stays closed so the
574
- * exhaustiveness checks in every surface's icon/label switch survive.
575
- */
576
- | 'agent_tool';
577
- /**
578
- * What happened when a research tool call ran, for honest per-surface
579
- * rendering. The broadcast seam carries this on every `research_step_done` so
580
- * surfaces do not have to pattern-match refusal text to tell a refused `rm`
581
- * from a successful `rm` — the old render path flipped a `✓` for both.
582
- */
583
- type ResearchStepOutcome = 'success' | 'failure' | 'refused' | 'denied' | 'not_executed';
584
- interface ResearchStep {
585
- id: string;
586
- tool: ResearchToolType;
587
- /** The tool's own name when it came from a harness planner — always set for `agent_tool`. */
588
- toolLabel?: string;
589
- args: string;
590
- result: string;
591
- success: boolean;
592
- outcome: ResearchStepOutcome;
593
- /** The model's tool_call id, so a surface can match `tool_result` to the
594
- * pending `tool_call` it announced — robust under parallel same-tool rounds. */
595
- toolCallId?: string;
596
- /** The research subagent that ran the call, so a reload regroups it under that subagent. */
597
- subagentId?: string;
598
- timestamp: string;
599
- thinkingText?: string;
600
- }
601
- interface UserPromptEntry {
602
- id: string;
603
- type: 'user_prompt' | 'system';
604
- content: string;
605
- timestamp: string;
606
- }
607
- type SubagentOutcome = 'done' | 'failed' | 'stopped';
608
- /**
609
- * One subagent's whole run, logged when it finishes: what it was asked, how it
610
- * ended and what it reported. Its steps stay separate entries carrying the same
611
- * `subagentId`, so an older reader that knows only steps still shows them.
612
- */
613
- interface SubagentLogEntry {
614
- id: string;
615
- type: 'subagent';
616
- subagentId: string;
617
- brief: string;
618
- model?: string;
619
- outcome: SubagentOutcome;
620
- digest: string;
621
- usage?: UsageTotals;
622
- timestamp: string;
623
- }
624
- type ResearchLogEntry = ResearchStep | UserPromptEntry | SubagentLogEntry;
625
- interface ResearchProgress {
626
- type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted' | 'liveness' | 'text_delta' | 'text_retracted' | 'usage' | 'subagent_started' | 'subagent_finished';
627
- /** Minted by whoever runs the turn and passed through untouched; absent outside a turn. */
628
- turnId?: string;
629
- /** One continuous run of model text — text before a tool call is its own segment. */
630
- segmentId?: string;
631
- text?: string;
632
- tool?: string;
633
- /** Harness planners (ADR-0009): the agent's own name for a tool Ordewell has no member for. */
634
- toolLabel?: string;
635
- toolArgs?: string;
636
- toolResult?: string;
637
- planToken?: string;
638
- step?: ResearchStep;
639
- /** The model's tool_call id, threaded on tool_call and tool_result so a
640
- * surface can match the result to its pending call — robust under parallel
641
- * same-tool rounds where LIFO-by-name matching mislabels summaries. */
642
- toolCallId?: string;
643
- /** Present when this event originates from (or reports on) one spawned research subagent (issue #34). */
644
- subagentId?: string;
645
- record?: UsageRecord;
646
- brief?: string;
647
- model?: string;
648
- outcome?: SubagentOutcome;
649
- digest?: string;
650
- usage?: UsageTotals;
651
- }
652
- interface ThinkingBlock {
653
- id: string;
654
- text: string;
655
- }
656
- interface StreamThinkingEvent {
657
- type: 'thinking';
658
- block: ThinkingBlock;
659
- }
660
- interface StreamStepEvent {
661
- type: 'step';
662
- step: ResearchStep;
663
- }
664
- type StreamEvent = StreamThinkingEvent | StreamStepEvent;
665
- interface DiscoveredModel {
666
- modelId: string;
667
- modelLabel: string;
668
- runnerProvider?: string;
669
- /**
670
- * Human-facing provider name as the runner itself reports it (e.g.
671
- * "OpenCode Zen" for `runnerProvider: 'opencode'`). Populated from the
672
- * runner's own provider catalog when available; when absent the UI derives a
673
- * label from `runnerProvider` by title-casing.
674
- */
675
- runnerProviderLabel?: string;
676
- /**
677
- * The runner whose catalog listed this model. Stamped once, at the single
678
- * `ModelDiscovery.discover` choke point, so a flat cross-runner list can
679
- * still say where each entry came from — `runnerProvider` alone cannot:
680
- * OpenCode reports most of its catalog as `openrouter`, which names the
681
- * serving backend, not the agent Ordewell would spawn.
682
- */
683
- runnerId?: string;
684
- /** The runner's display name (`OpenCode`), from its manifest. */
685
- runnerLabel?: string;
686
- variants: {
687
- id: string;
688
- label: string;
689
- }[];
690
- /**
691
- * The model's context window when the runner or catalog reports it (#49).
692
- * Read by the planner-model lookup so context fill can be shown; absent when
693
- * unknown rather than defaulted to zero.
694
- */
695
- contextWindow?: number;
696
- }
697
- type PlanStatus = 'draft' | 'approved' | 'rejected' | 'running' | 'completed';
698
- /**
699
- * One entry of the planner's persisted dialogue (ADR-0002). The single source
700
- * of truth for both UI redisplay and conversational context. Tool-call results
701
- * are NOT stored here — they live in the AI service's tool-use history;
702
- * `researchLog` remains the persisted tool trace for the UI.
703
- */
704
- interface ConversationMessage {
705
- role: 'user' | 'assistant';
706
- content: string;
707
- timestamp: string;
708
- /**
709
- * Timeline marker: 'plan_generated' records the point in the dialogue where
710
- * the plan was committed (the UI anchors the plan card there on restore);
711
- * 'system' is a host-injected notice; 'compaction' is the summary a
712
- * user-triggered compaction left in place of the earlier messages — always
713
- * the transcript's first entry. Absent for ordinary chat turns, so
714
- * sessions saved before markers existed degrade gracefully.
715
- */
716
- kind?: 'plan_generated' | 'system' | 'compaction';
717
- }
718
- interface QueuedMessage {
719
- id: string;
720
- text: string;
721
- timestamp: string;
722
- }
723
- interface PlanModificationWarnings {
724
- deletedCompleted: string[];
725
- changedCompleted: string[];
726
- deletedInProgress: string[];
727
- modifiedInProgress: string[];
728
- brokenDependencies: string[];
729
- }
730
- declare function emptyWarnings(): PlanModificationWarnings;
731
- interface LegacyPlanState {
732
- tasks: Task[];
733
- generatedAt: string;
734
- status: PlanStatus;
735
- runners: RunnerId[];
736
- lastUpdated: string;
737
- researchLog?: ResearchLogEntry[];
738
- /** The planner dialogue — user messages and assistant messages, in order (ADR-0002). */
739
- conversationHistory?: ConversationMessage[];
740
- /** Full markdown PRD once written by the planner (PRD mode), also saved to .scratch/<slug>/PRD.md. */
741
- prdMarkdown?: string;
742
- /** Follow-ups queued while tasks execute — applied as plan modifications between batches. */
743
- queuedMessages?: QueuedMessage[];
744
- /**
745
- * The plan's isolation run (ADR-0013), written from the orchestrator at
746
- * persist time and read back only when a saved plan is adopted. It names
747
- * branches and worktrees that belong to this plan alone: a fork of the plan
748
- * must leave it behind rather than share it.
749
- */
750
- isolation?: PlanIsolation;
751
- /** Kept so a reopened session shows the same token line (#49). */
752
- plannerUsage?: PlannerUsage;
753
- }
754
- interface Message {
755
- id: string;
756
- role: 'user' | 'planner' | 'system';
757
- content: string;
758
- timestamp: number;
759
- }
760
- interface TaskSnapshot extends Task {
761
- completedAt: number;
762
- verdict?: Verdict;
763
- retryCount: number;
764
- finalized: boolean;
765
- }
766
- type PlanState = {
767
- phase: 'planning';
768
- history: Message[];
769
- message: string;
770
- pendingTasks: Task[];
771
- } | {
772
- phase: 'executing';
773
- history: Message[];
774
- message: string;
775
- executionLog: TaskSnapshot[];
776
- pendingTasks: Task[];
777
- goal: string;
778
- runners: string[];
779
- status: PlanStatus;
780
- };
781
- declare function migratePlanState(raw: unknown): PlanState;
782
- declare function migrateLegacyPlan(legacy: LegacyPlanState): PlanState;
783
-
784
- declare function createTask(overrides?: Partial<Task>): Task;
785
- declare function createEmptyPlan(): LegacyPlanState;
786
- declare function flattenTasks(tasks: Task[]): Task[];
787
- /** A flattened task with the parent it hangs under, null for a top-level task. */
788
- interface TaskWithParent {
789
- task: Task;
790
- parent: Task | null;
791
- }
792
- declare function flattenTasksWithParents(tasks: Task[]): TaskWithParent[];
793
- declare function migrateTask(task: Record<string, unknown>): Task;
794
- declare function addTaskToPlan(tasks: Task[], partial: Partial<Task>): Task[];
795
- declare function removeTaskFromPlan(tasks: Task[], taskId: string): Task[];
796
- declare function updateTaskInPlan(tasks: Task[], taskId: string, changes: Partial<Task>): Task[];
797
- declare function renumberTasks(tasks: Task[]): Task[];
798
- /**
799
- * Lay a planner-written task list over the plan it rewrites without letting it
800
- * change execution state. A planner restates tasks; it never witnessed one run,
801
- * so the status it writes is not evidence. A settled task is kept exactly as
802
- * it stands wherever the rewrite names it, and put back beside its old
803
- * neighbour where the rewrite leaves it out. Every other task keeps the status
804
- * it had; a task the rewrite adds starts pending.
805
- */
806
- declare function keepExecutionState(current: Task[], rewrite: Task[]): Task[];
807
- declare function validateModifiedPlan(original: Task[], modified: Task[]): PlanModificationWarnings;
808
- interface ActiveTaskSession {
809
- id: string;
810
- taskId: string;
811
- }
812
- interface ValidationResult {
813
- valid: boolean;
814
- errors: string[];
815
- }
816
- interface ValidationContext {
817
- executionLog: TaskSnapshot[];
818
- oldPending: Task[];
819
- newPending: Task[];
820
- activeSessions: Map<string, ActiveTaskSession>;
821
- }
822
- type ValidationCheck = (ctx: ValidationContext) => ValidationResult;
823
- declare function warningsText(w: PlanModificationWarnings): string | null;
824
-
825
- export { type TaskType as $, type ActiveTaskSession as A, type ResearchProgress as B, type ConversationMessage as C, type DiscoveredMode as D, type ResearchStep as E, type ResearchStepOutcome as F, type ResearchToolType as G, type RunnerId as H, type IWorktreeIsolation as I, type StreamStepEvent as J, type StreamThinkingEvent as K, type LegacyPlanState as L, type Message as M, type SubagentLogEntry as N, type SubagentOutcome as O, type PlanIsolation as P, type QueuedMessage as Q, type RepairEvidence as R, type StreamEvent as S, type Task as T, type TaskIsolation as U, type TaskIsolationState as V, type TaskMode as W, type TaskModelAssignment as X, type TaskOutputSummary as Y, type TaskSnapshot as Z, type TaskStatus as _, type DiscoveredModel as a, type TaskWithParent as a0, type ThinkingBlock as a1, type UsageLine as a2, type UsageRecord as a3, type UsageTotals as a4, type UserPromptEntry as a5, type UserStep as a6, type ValidationCheck as a7, type ValidationContext as a8, type ValidationResult as a9, type Verdict as aa, type VerificationCheck as ab, addPlannerUsage as ac, addTaskToPlan as ad, addUsage as ae, createEmptyPlan as af, createTask as ag, emptyWarnings as ah, flattenTasks as ai, flattenTasksWithParents as aj, isMeasured as ak, keepExecutionState as al, migrateLegacyPlan as am, migratePlanState as an, migrateTask as ao, partedPromptUsage as ap, plannerContextFill as aq, removeTaskFromPlan as ar, renumberTasks as as, updateTaskInPlan as at, usageLine as au, validateModifiedPlan as av, warningsText as aw, type IntegrationDisposal as b, type IsolationAvailability as c, type IsolationHandoff as d, type IsolationHandoffRepo as e, type IsolationInactiveReason as f, type IsolationLandedTask as g, type IsolationLanding as h, type IsolationMergeBlock as i, type IsolationMergeBlockReason as j, type IsolationMergeResult as k, type IsolationOutcome as l, type IsolationPruneResult as m, type IsolationRepo as n, type IsolationRun as o, type IsolationTaskRecord as p, type IsolationTaskRepo as q, type IsolationTaskStatus as r, type IsolationView as s, type PlanModificationWarnings as t, type PlanState as u, type PlanStatus as v, type PlannerUsage as w, type PreparedTask as x, type RepoGroupLayout as y, type ResearchLogEntry as z };