@ordewell/core 0.5.3 → 0.5.5

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 (44) hide show
  1. package/dist/{ITerminalRunner-Bd-vAJnw.d.ts → ITerminalRunner-BV9Rd2o9.d.ts} +3 -1
  2. package/dist/{ITerminalRunner-ByeoLF57.d.mts → ITerminalRunner-C77ZNZS9.d.mts} +3 -1
  3. package/dist/{ModeResolver-DVJ7HV3k.d.mts → ModeResolver-D-SUFRNF.d.mts} +1 -1
  4. package/dist/{ModeResolver-Dkig8ghQ.d.ts → ModeResolver-D3XO0fT9.d.ts} +1 -1
  5. package/dist/{Task-BxQkPlXO.d.mts → Task-Vl5Zq_D-.d.mts} +168 -7
  6. package/dist/{Task-BxQkPlXO.d.ts → Task-Vl5Zq_D-.d.ts} +168 -7
  7. package/dist/{chunk-JVMDEHRQ.mjs → chunk-HD2FWPRV.mjs} +72 -37
  8. package/dist/chunk-HD2FWPRV.mjs.map +1 -0
  9. package/dist/{chunk-GWPIYDQW.mjs → chunk-KLN7ELXO.mjs} +897 -18
  10. package/dist/chunk-KLN7ELXO.mjs.map +1 -0
  11. package/dist/{chunk-XWOUIA6A.mjs → chunk-UUBGVCGJ.mjs} +2 -2
  12. package/dist/index.d.mts +320 -60
  13. package/dist/index.d.ts +320 -60
  14. package/dist/index.js +2497 -672
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +1605 -659
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/order-labels.d.mts +1 -1
  19. package/dist/order-labels.d.ts +1 -1
  20. package/dist/{parsing-CF_grC29.d.ts → parsing-BTP4bwkk.d.ts} +11 -3
  21. package/dist/{parsing-DRp4dPC0.d.mts → parsing-CDtRSxBY.d.mts} +11 -3
  22. package/dist/parsing.d.mts +3 -3
  23. package/dist/parsing.d.ts +3 -3
  24. package/dist/parsing.js.map +1 -1
  25. package/dist/parsing.mjs +2 -2
  26. package/dist/plan-utils-BFaPo-IT.d.ts +708 -0
  27. package/dist/plan-utils-pE4TBwxl.d.mts +708 -0
  28. package/dist/plan-utils.d.mts +3 -3
  29. package/dist/plan-utils.d.ts +3 -3
  30. package/dist/plan-utils.js +668 -7
  31. package/dist/plan-utils.js.map +1 -1
  32. package/dist/plan-utils.mjs +38 -4
  33. package/dist/testing.d.mts +5 -3
  34. package/dist/testing.d.ts +5 -3
  35. package/dist/testing.js +3 -0
  36. package/dist/testing.js.map +1 -1
  37. package/dist/testing.mjs +3 -0
  38. package/dist/testing.mjs.map +1 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-GWPIYDQW.mjs.map +0 -1
  41. package/dist/chunk-JVMDEHRQ.mjs.map +0 -1
  42. package/dist/plan-utils-CkNbqAmS.d.ts +0 -329
  43. package/dist/plan-utils-CtB3_Ovf.d.mts +0 -329
  44. /package/dist/{chunk-XWOUIA6A.mjs.map → chunk-UUBGVCGJ.mjs.map} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { I as IApproval, a as ApprovalMode } from './ApprovalPolicy-BVhGdECT.js';
2
- import { F as RunnerId } from './Task-BxQkPlXO.js';
2
+ import { H as RunnerId } from './Task-Vl5Zq_D-.js';
3
3
 
4
4
  interface ToolOutcome {
5
5
  success: boolean;
@@ -121,6 +121,8 @@ interface OrchestratorOption {
121
121
  apiProvider: AiProvider;
122
122
  description?: string;
123
123
  pricing?: string;
124
+ /** The model's context window when the catalog reports one (#49). */
125
+ contextWindow?: number;
124
126
  }
125
127
 
126
128
  interface FetchAllProviderModelsOptions {
@@ -1,5 +1,5 @@
1
1
  import { I as IApproval, a as ApprovalMode } from './ApprovalPolicy-BVhGdECT.mjs';
2
- import { F as RunnerId } from './Task-BxQkPlXO.mjs';
2
+ import { H as RunnerId } from './Task-Vl5Zq_D-.mjs';
3
3
 
4
4
  interface ToolOutcome {
5
5
  success: boolean;
@@ -121,6 +121,8 @@ interface OrchestratorOption {
121
121
  apiProvider: AiProvider;
122
122
  description?: string;
123
123
  pricing?: string;
124
+ /** The model's context window when the catalog reports one (#49). */
125
+ contextWindow?: number;
124
126
  }
125
127
 
126
128
  interface FetchAllProviderModelsOptions {
@@ -1,4 +1,4 @@
1
- import { F as RunnerId } from './Task-BxQkPlXO.mjs';
1
+ import { H as RunnerId } from './Task-Vl5Zq_D-.mjs';
2
2
 
3
3
  interface RunnerModeInfo {
4
4
  id: string;
@@ -1,4 +1,4 @@
1
- import { F as RunnerId } from './Task-BxQkPlXO.js';
1
+ import { H as RunnerId } from './Task-Vl5Zq_D-.js';
2
2
 
3
3
  interface RunnerModeInfo {
4
4
  id: string;
@@ -72,6 +72,12 @@ interface IsolationTaskRecord {
72
72
  repos: Record<string, IsolationTaskRepo>;
73
73
  /** The repo whose merge stopped the task from landing, while `status` is `conflict` or `failed`. */
74
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;
75
81
  /** Repo-relative paths, in `conflictRepo`, that conflicted; set only while `status` is `conflict` or `repairing`. */
76
82
  conflictFiles?: string[];
77
83
  /**
@@ -275,6 +281,18 @@ interface PreparedTask {
275
281
  */
276
282
  copied: string[];
277
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
+ }
278
296
  interface IWorktreeIsolation {
279
297
  /**
280
298
  * A repo group with at least one repo to isolate, a clean tracked tree in
@@ -333,9 +351,9 @@ interface IWorktreeIsolation {
333
351
  */
334
352
  integrate(task: Task, run: IsolationRun, persist?: () => void): Promise<IsolationOutcome>;
335
353
  /**
336
- * `keep: false` removes the task's worktree, branch and record (cancel, task
354
+ * `keep: false` removes the task's worktree, branch and record (retry, task
337
355
  * removal). `keep: true` leaves the worktree and branch exactly as they are
338
- * for inspection — a failed verdict — and only moves the task off `active`,
356
+ * for inspection — a failed verdict, a stop, a cancel — and only moves the task off `active`,
339
357
  * so a crash-recovery prune does not sweep it away; a repair it ends leaves
340
358
  * the task `conflict`, as it was before the repair. Takes the run rather than
341
359
  * a bare task id: ids are only unique within one plan, and one daemon serves
@@ -349,9 +367,13 @@ interface IWorktreeIsolation {
349
367
  /**
350
368
  * Drop what a crash left behind: a landing it interrupted is rolled back in
351
369
  * every repo, a repair it interrupted leaves its task `conflict`, then stale
352
- * active worktrees and directories no record owns go.
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.
353
375
  */
354
- pruneOrphans(run: IsolationRun): Promise<void>;
376
+ pruneOrphans(run: IsolationRun): Promise<IsolationPruneResult>;
355
377
  /** Unified diff of each repo's integration branch against its base ref. */
356
378
  reviewDiff(run: IsolationRun): Promise<string>;
357
379
  /**
@@ -382,6 +404,99 @@ interface IWorktreeIsolation {
382
404
  sweep(run: IsolationRun): Promise<void>;
383
405
  }
384
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
+
385
500
  interface UserStep {
386
501
  order: number;
387
502
  instruction: string;
@@ -478,6 +593,8 @@ interface ResearchStep {
478
593
  /** The model's tool_call id, so a surface can match `tool_result` to the
479
594
  * pending `tool_call` it announced — robust under parallel same-tool rounds. */
480
595
  toolCallId?: string;
596
+ /** The research subagent that ran the call, so a reload regroups it under that subagent. */
597
+ subagentId?: string;
481
598
  timestamp: string;
482
599
  thinkingText?: string;
483
600
  }
@@ -487,9 +604,30 @@ interface UserPromptEntry {
487
604
  content: string;
488
605
  timestamp: string;
489
606
  }
490
- type ResearchLogEntry = ResearchStep | UserPromptEntry;
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;
491
625
  interface ResearchProgress {
492
- type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted' | 'liveness';
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;
493
631
  text?: string;
494
632
  tool?: string;
495
633
  /** Harness planners (ADR-0009): the agent's own name for a tool Ordewell has no member for. */
@@ -504,6 +642,12 @@ interface ResearchProgress {
504
642
  toolCallId?: string;
505
643
  /** Present when this event originates from (or reports on) one spawned research subagent (issue #34). */
506
644
  subagentId?: string;
645
+ record?: UsageRecord;
646
+ brief?: string;
647
+ model?: string;
648
+ outcome?: SubagentOutcome;
649
+ digest?: string;
650
+ usage?: UsageTotals;
507
651
  }
508
652
  interface ThinkingBlock {
509
653
  id: string;
@@ -543,6 +687,12 @@ interface DiscoveredModel {
543
687
  id: string;
544
688
  label: string;
545
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;
546
696
  }
547
697
  type PlanStatus = 'draft' | 'approved' | 'rejected' | 'running' | 'completed';
548
698
  /**
@@ -598,6 +748,8 @@ interface LegacyPlanState {
598
748
  * must leave it behind rather than share it.
599
749
  */
600
750
  isolation?: PlanIsolation;
751
+ /** Kept so a reopened session shows the same token line (#49). */
752
+ plannerUsage?: PlannerUsage;
601
753
  }
602
754
  interface Message {
603
755
  id: string;
@@ -643,6 +795,15 @@ declare function addTaskToPlan(tasks: Task[], partial: Partial<Task>): Task[];
643
795
  declare function removeTaskFromPlan(tasks: Task[], taskId: string): Task[];
644
796
  declare function updateTaskInPlan(tasks: Task[], taskId: string, changes: Partial<Task>): Task[];
645
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[];
646
807
  declare function validateModifiedPlan(original: Task[], modified: Task[]): PlanModificationWarnings;
647
808
  interface ActiveTaskSession {
648
809
  id: string;
@@ -661,4 +822,4 @@ interface ValidationContext {
661
822
  type ValidationCheck = (ctx: ValidationContext) => ValidationResult;
662
823
  declare function warningsText(w: PlanModificationWarnings): string | null;
663
824
 
664
- export { type UserStep as $, type ActiveTaskSession as A, type ResearchStepOutcome as B, type ConversationMessage as C, type DiscoveredMode as D, type ResearchToolType as E, type RunnerId as F, type StreamStepEvent as G, type StreamThinkingEvent as H, type IWorktreeIsolation as I, type TaskIsolation as J, type TaskIsolationState as K, type LegacyPlanState as L, type Message as M, type TaskMode as N, type TaskModelAssignment as O, type PlanIsolation as P, type QueuedMessage as Q, type RepairEvidence as R, type StreamEvent as S, type Task as T, type TaskOutputSummary as U, type TaskSnapshot as V, type TaskStatus as W, type TaskType as X, type TaskWithParent as Y, type ThinkingBlock as Z, type UserPromptEntry as _, type DiscoveredModel as a, type ValidationCheck as a0, type ValidationContext as a1, type ValidationResult as a2, type Verdict as a3, type VerificationCheck as a4, addTaskToPlan as a5, createEmptyPlan as a6, createTask as a7, emptyWarnings as a8, flattenTasks as a9, flattenTasksWithParents as aa, migrateLegacyPlan as ab, migratePlanState as ac, migrateTask as ad, removeTaskFromPlan as ae, renumberTasks as af, updateTaskInPlan as ag, validateModifiedPlan as ah, warningsText as ai, 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 IsolationRepo as m, type IsolationRun as n, type IsolationTaskRecord as o, type IsolationTaskRepo as p, type IsolationTaskStatus as q, type IsolationView as r, type PlanModificationWarnings as s, type PlanState as t, type PlanStatus as u, type PreparedTask as v, type RepoGroupLayout as w, type ResearchLogEntry as x, type ResearchProgress as y, type ResearchStep as z };
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 };
@@ -72,6 +72,12 @@ interface IsolationTaskRecord {
72
72
  repos: Record<string, IsolationTaskRepo>;
73
73
  /** The repo whose merge stopped the task from landing, while `status` is `conflict` or `failed`. */
74
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;
75
81
  /** Repo-relative paths, in `conflictRepo`, that conflicted; set only while `status` is `conflict` or `repairing`. */
76
82
  conflictFiles?: string[];
77
83
  /**
@@ -275,6 +281,18 @@ interface PreparedTask {
275
281
  */
276
282
  copied: string[];
277
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
+ }
278
296
  interface IWorktreeIsolation {
279
297
  /**
280
298
  * A repo group with at least one repo to isolate, a clean tracked tree in
@@ -333,9 +351,9 @@ interface IWorktreeIsolation {
333
351
  */
334
352
  integrate(task: Task, run: IsolationRun, persist?: () => void): Promise<IsolationOutcome>;
335
353
  /**
336
- * `keep: false` removes the task's worktree, branch and record (cancel, task
354
+ * `keep: false` removes the task's worktree, branch and record (retry, task
337
355
  * removal). `keep: true` leaves the worktree and branch exactly as they are
338
- * for inspection — a failed verdict — and only moves the task off `active`,
356
+ * for inspection — a failed verdict, a stop, a cancel — and only moves the task off `active`,
339
357
  * so a crash-recovery prune does not sweep it away; a repair it ends leaves
340
358
  * the task `conflict`, as it was before the repair. Takes the run rather than
341
359
  * a bare task id: ids are only unique within one plan, and one daemon serves
@@ -349,9 +367,13 @@ interface IWorktreeIsolation {
349
367
  /**
350
368
  * Drop what a crash left behind: a landing it interrupted is rolled back in
351
369
  * every repo, a repair it interrupted leaves its task `conflict`, then stale
352
- * active worktrees and directories no record owns go.
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.
353
375
  */
354
- pruneOrphans(run: IsolationRun): Promise<void>;
376
+ pruneOrphans(run: IsolationRun): Promise<IsolationPruneResult>;
355
377
  /** Unified diff of each repo's integration branch against its base ref. */
356
378
  reviewDiff(run: IsolationRun): Promise<string>;
357
379
  /**
@@ -382,6 +404,99 @@ interface IWorktreeIsolation {
382
404
  sweep(run: IsolationRun): Promise<void>;
383
405
  }
384
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
+
385
500
  interface UserStep {
386
501
  order: number;
387
502
  instruction: string;
@@ -478,6 +593,8 @@ interface ResearchStep {
478
593
  /** The model's tool_call id, so a surface can match `tool_result` to the
479
594
  * pending `tool_call` it announced — robust under parallel same-tool rounds. */
480
595
  toolCallId?: string;
596
+ /** The research subagent that ran the call, so a reload regroups it under that subagent. */
597
+ subagentId?: string;
481
598
  timestamp: string;
482
599
  thinkingText?: string;
483
600
  }
@@ -487,9 +604,30 @@ interface UserPromptEntry {
487
604
  content: string;
488
605
  timestamp: string;
489
606
  }
490
- type ResearchLogEntry = ResearchStep | UserPromptEntry;
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;
491
625
  interface ResearchProgress {
492
- type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted' | 'liveness';
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;
493
631
  text?: string;
494
632
  tool?: string;
495
633
  /** Harness planners (ADR-0009): the agent's own name for a tool Ordewell has no member for. */
@@ -504,6 +642,12 @@ interface ResearchProgress {
504
642
  toolCallId?: string;
505
643
  /** Present when this event originates from (or reports on) one spawned research subagent (issue #34). */
506
644
  subagentId?: string;
645
+ record?: UsageRecord;
646
+ brief?: string;
647
+ model?: string;
648
+ outcome?: SubagentOutcome;
649
+ digest?: string;
650
+ usage?: UsageTotals;
507
651
  }
508
652
  interface ThinkingBlock {
509
653
  id: string;
@@ -543,6 +687,12 @@ interface DiscoveredModel {
543
687
  id: string;
544
688
  label: string;
545
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;
546
696
  }
547
697
  type PlanStatus = 'draft' | 'approved' | 'rejected' | 'running' | 'completed';
548
698
  /**
@@ -598,6 +748,8 @@ interface LegacyPlanState {
598
748
  * must leave it behind rather than share it.
599
749
  */
600
750
  isolation?: PlanIsolation;
751
+ /** Kept so a reopened session shows the same token line (#49). */
752
+ plannerUsage?: PlannerUsage;
601
753
  }
602
754
  interface Message {
603
755
  id: string;
@@ -643,6 +795,15 @@ declare function addTaskToPlan(tasks: Task[], partial: Partial<Task>): Task[];
643
795
  declare function removeTaskFromPlan(tasks: Task[], taskId: string): Task[];
644
796
  declare function updateTaskInPlan(tasks: Task[], taskId: string, changes: Partial<Task>): Task[];
645
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[];
646
807
  declare function validateModifiedPlan(original: Task[], modified: Task[]): PlanModificationWarnings;
647
808
  interface ActiveTaskSession {
648
809
  id: string;
@@ -661,4 +822,4 @@ interface ValidationContext {
661
822
  type ValidationCheck = (ctx: ValidationContext) => ValidationResult;
662
823
  declare function warningsText(w: PlanModificationWarnings): string | null;
663
824
 
664
- export { type UserStep as $, type ActiveTaskSession as A, type ResearchStepOutcome as B, type ConversationMessage as C, type DiscoveredMode as D, type ResearchToolType as E, type RunnerId as F, type StreamStepEvent as G, type StreamThinkingEvent as H, type IWorktreeIsolation as I, type TaskIsolation as J, type TaskIsolationState as K, type LegacyPlanState as L, type Message as M, type TaskMode as N, type TaskModelAssignment as O, type PlanIsolation as P, type QueuedMessage as Q, type RepairEvidence as R, type StreamEvent as S, type Task as T, type TaskOutputSummary as U, type TaskSnapshot as V, type TaskStatus as W, type TaskType as X, type TaskWithParent as Y, type ThinkingBlock as Z, type UserPromptEntry as _, type DiscoveredModel as a, type ValidationCheck as a0, type ValidationContext as a1, type ValidationResult as a2, type Verdict as a3, type VerificationCheck as a4, addTaskToPlan as a5, createEmptyPlan as a6, createTask as a7, emptyWarnings as a8, flattenTasks as a9, flattenTasksWithParents as aa, migrateLegacyPlan as ab, migratePlanState as ac, migrateTask as ad, removeTaskFromPlan as ae, renumberTasks as af, updateTaskInPlan as ag, validateModifiedPlan as ah, warningsText as ai, 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 IsolationRepo as m, type IsolationRun as n, type IsolationTaskRecord as o, type IsolationTaskRepo as p, type IsolationTaskStatus as q, type IsolationView as r, type PlanModificationWarnings as s, type PlanState as t, type PlanStatus as u, type PreparedTask as v, type RepoGroupLayout as w, type ResearchLogEntry as x, type ResearchProgress as y, type ResearchStep as z };
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 };