cascade-ai 0.67.0 → 0.69.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/dist/index.d.cts CHANGED
@@ -5,6 +5,137 @@ import { OAuthClientInformationMixed, OAuthTokens, OAuthClientMetadata, OAuthCli
5
5
  export { OAuthClientInformationMixed, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
6
6
  import Database from 'better-sqlite3';
7
7
 
8
+ /**
9
+ * Durable resume checkpoints.
10
+ *
11
+ * Before this, a stopped run was recoverable only through an in-memory field on
12
+ * the Cascade instance, set on exactly one path (the budget cap). So the two
13
+ * interruptions people actually hit — a crash, and Ctrl-C — lost every finished
14
+ * section, and even a budget stop was lost the moment the process exited. The
15
+ * work was on disk; the knowledge of what had been done was not.
16
+ *
17
+ * A checkpoint records the finished nodes of a run so a resume can restore them
18
+ * instead of paying to produce them a second time. Design constraints, in the
19
+ * order that shaped the code:
20
+ *
21
+ * - **A crash is one of the triggers.** Writes are atomic (temp file + rename),
22
+ * because the process dying mid-write is a case this store must survive, not
23
+ * an edge case. A half-written checkpoint that parses as valid JSON would be
24
+ * worse than none at all.
25
+ * - **A corrupt or foreign checkpoint must never break the caller.** Listing
26
+ * skips anything unreadable, unparseable, or written by a schema version this
27
+ * build doesn't know. Resume is a convenience; it may degrade, never throw.
28
+ * - **Checkpoints hold the user's prompt and partial output.** They are pruned
29
+ * by both count and age so that content does not accumulate in the workspace
30
+ * indefinitely.
31
+ */
32
+ /** Bump when the on-disk shape changes incompatibly. Older files are ignored. */
33
+ declare const RESUME_SCHEMA_VERSION = 1;
34
+ type ResumeReason = 'budget' | 'cancelled' | 'error' | 'breaker';
35
+ /** A node whose work is done and must not be re-run on resume. */
36
+ interface CompletedNode {
37
+ /** Stable id from the compiled task graph. */
38
+ id: string;
39
+ title: string;
40
+ status: 'COMPLETED' | 'PARTIAL';
41
+ output: string;
42
+ }
43
+ interface ResumeCheckpoint {
44
+ version: number;
45
+ taskId: string;
46
+ prompt: string;
47
+ /** ISO-8601. */
48
+ createdAt: string;
49
+ reason: ResumeReason;
50
+ /** Why the run stopped, in the words the user would see. */
51
+ detail?: string;
52
+ partialOutput: string;
53
+ completed: CompletedNode[];
54
+ }
55
+ interface ResumeStoreOptions {
56
+ /** Directory checkpoints live in (created on demand). */
57
+ dir: string;
58
+ /** Keep at most this many, newest first. Default 5. */
59
+ maxCheckpoints?: number;
60
+ /** Discard checkpoints older than this. Default 7 days. */
61
+ maxAgeMs?: number;
62
+ /** Injectable clock, for tests. */
63
+ now?: () => number;
64
+ /** How long a claim may be held before it is considered abandoned. */
65
+ claimTimeoutMs?: number;
66
+ }
67
+ declare class ResumeStore {
68
+ private readonly dir;
69
+ private readonly maxCheckpoints;
70
+ private readonly maxAgeMs;
71
+ private readonly now;
72
+ private readonly claimTimeoutMs;
73
+ constructor(options: ResumeStoreOptions);
74
+ /**
75
+ * Persist a checkpoint and prune.
76
+ *
77
+ * Never throws — failing to save a resume file must not turn a recoverable
78
+ * stop into a crash — but DOES report whether the atomic rename landed.
79
+ * Callers decide whether an original claim is safe to discard based on that
80
+ * answer, so "I swallowed the error" and "the checkpoint is on disk" must not
81
+ * look alike to them.
82
+ *
83
+ * @returns true only when the checkpoint is durably on disk.
84
+ */
85
+ save(checkpoint: Omit<ResumeCheckpoint, 'version' | 'createdAt'> & {
86
+ createdAt?: string;
87
+ }): Promise<boolean>;
88
+ /** Valid, unexpired checkpoints, newest first. */
89
+ list(): Promise<ResumeCheckpoint[]>;
90
+ /** The most recent resumable run, or null. */
91
+ latest(): Promise<ResumeCheckpoint | null>;
92
+ /**
93
+ * Claim a checkpoint for a resume attempt.
94
+ *
95
+ * This used to DELETE the file and return it, which opened a window with no
96
+ * recovery record at all: between preparing the continuation and the resumed
97
+ * run writing its own checkpoint, a crash lost the only copy — and a crash is
98
+ * precisely what this store exists to survive. Deleting on read optimised for
99
+ * "never resume twice" at the cost of "never lose the work", which is the
100
+ * wrong trade for a recovery mechanism.
101
+ *
102
+ * Instead the file is RENAMED to `.claimed`, which still hides it from
103
+ * `list()` (so a concurrent resume cannot take the same work) but keeps it on
104
+ * disk. The caller then either `release`s it — restoring it for another
105
+ * attempt — or `settle`s it once the resumed run is safely under way. A claim
106
+ * abandoned by a dead process is reclaimed after `claimTimeoutMs`.
107
+ */
108
+ claim(taskId?: string): Promise<{
109
+ checkpoint: ResumeCheckpoint;
110
+ claimId: string;
111
+ } | null>;
112
+ /** Give a claim back, so the work stays recoverable after a failed start. */
113
+ release(claimId: string): Promise<void>;
114
+ /** Discard a claim for good — the resumed run has taken ownership. */
115
+ settle(claimId: string): Promise<void>;
116
+ /**
117
+ * Claim-and-discard in one step. Kept for callers that genuinely want the old
118
+ * semantics; prefer claim/settle so a crash mid-resume stays recoverable.
119
+ */
120
+ consume(taskId?: string): Promise<ResumeCheckpoint | null>;
121
+ /**
122
+ * Return claims abandoned by a process that died mid-resume. Without this a
123
+ * crash between claim and settle would strand the checkpoint invisibly —
124
+ * trading one loss window for another.
125
+ */
126
+ reclaimStale(): Promise<number>;
127
+ /** Remove every checkpoint for a task id. */
128
+ delete(taskId: string): Promise<void>;
129
+ /** Drop expired checkpoints, then any beyond the newest `maxCheckpoints`. */
130
+ prune(): Promise<void>;
131
+ }
132
+ /**
133
+ * Render restored work for a correction planner: enough for the model to know
134
+ * what exists and not redo it, without pasting entire artifacts back into the
135
+ * prompt (which is what the token budget was being spent on in the first place).
136
+ */
137
+ declare function summarizeCompleted(completed: readonly CompletedNode[], perNodeChars?: number): string;
138
+
8
139
  type ProviderType = 'anthropic' | 'openai' | 'gemini' | 'azure' | 'openai-compatible' | 'ollama' | 'github-models';
9
140
  interface ModelInfo {
10
141
  id: string;
@@ -244,7 +375,17 @@ interface ToolExecuteOptions {
244
375
  }>;
245
376
  }
246
377
  type TierRole = 'T1' | 'T2' | 'T3';
247
- type TierStatus = 'IDLE' | 'ACTIVE' | 'COMPLETED' | 'FAILED' | 'ESCALATED';
378
+ /**
379
+ * A tier's lifecycle state.
380
+ *
381
+ * BLOCKED is terminal like COMPLETED and FAILED — the tier will never run,
382
+ * because work it depended on failed first. It is NOT the same word as
383
+ * `MessageStatus.BLOCKED` below, which means "running but stuck": one describes
384
+ * a tier that never started, the other a tier that started and cannot finish.
385
+ * Anything rendering a status label should treat BLOCKED as "skipped", not as
386
+ * an error and not as in-progress.
387
+ */
388
+ type TierStatus = 'IDLE' | 'ACTIVE' | 'COMPLETED' | 'FAILED' | 'ESCALATED' | 'BLOCKED';
248
389
  type TaskComplexity = 'Simple' | 'Moderate' | 'Complex' | 'Highly Complex';
249
390
  interface TierConfig {
250
391
  role: TierRole;
@@ -317,10 +458,27 @@ interface StatusUpdate {
317
458
  interface T2Result {
318
459
  sectionId: string;
319
460
  sectionTitle: string;
320
- status: 'COMPLETED' | 'PARTIAL' | 'FAILED' | 'ESCALATED';
461
+ /**
462
+ * BLOCKED is not a kind of FAILED. A failed section was attempted and did not
463
+ * work; a blocked one was never attempted, because something it depended on
464
+ * failed first — so it spent no tokens and says nothing about whether it
465
+ * would have worked. Reporting it as FAILED (which is what happened until
466
+ * now) tells the user their work broke when in fact the scheduler protected
467
+ * them from paying for work that could not succeed yet.
468
+ */
469
+ status: 'COMPLETED' | 'PARTIAL' | 'FAILED' | 'ESCALATED' | 'BLOCKED';
321
470
  t3Results: T3Result[];
322
471
  sectionSummary: string;
323
472
  issues: string[];
473
+ /**
474
+ * Set only on BLOCKED sections: which upstream work stopped this one, by
475
+ * title and by id. Structured rather than flattened into `issues`, so a UI
476
+ * can link a skipped section back to its cause instead of parsing prose.
477
+ */
478
+ blockedBy?: {
479
+ ids: string[];
480
+ titles: string[];
481
+ };
324
482
  /**
325
483
  * Set when this section reached PARTIAL because the user was asked about an
326
484
  * escalation and chose "skip" — not because a T2/T3 pipeline shortfall left
@@ -1087,7 +1245,24 @@ declare class TaskAnalyzer {
1087
1245
  private feedback?;
1088
1246
  private bias;
1089
1247
  private lastProfile;
1090
- private lastSelectedModels;
1248
+ /** Models chosen by the run currently in flight. Cleared when it completes. */
1249
+ private currentRunSelections;
1250
+ /**
1251
+ * Immutable snapshot of the last COMPLETED run: its selections AND the task
1252
+ * type they were chosen for.
1253
+ *
1254
+ * The task type has to travel WITH the selections. `lastProfile` is overwritten
1255
+ * by the next analyze(), so if run B started before the user rated run A, A's
1256
+ * models were credited under B's task type — teaching the router that a coding
1257
+ * model is good at creative writing, from a rating that never said so.
1258
+ *
1259
+ * Explicit ratings arrive after the run finishes — rateLastRun() is by nature
1260
+ * a reaction to a finished answer — but the run finalizer calls
1261
+ * recordRunOutcome() first, and that used to clear the only map there was. So
1262
+ * every explicit rating iterated an empty map, recorded nothing, and returned
1263
+ * false: the 3x-weighted user signal never reached the tracker at all.
1264
+ */
1265
+ private lastCompletedRun?;
1091
1266
  constructor(tracker?: ModelPerformanceTracker, bias?: AutoBias);
1092
1267
  /**
1093
1268
  * Supply per-model rating counts. Optional by design: a host that collects no
@@ -1119,9 +1294,17 @@ declare class TaskAnalyzer {
1119
1294
  */
1120
1295
  recordRunOutcome(outcome: 'success' | 'failure', costByTier: Record<string, number>, contextTokens?: number): void;
1121
1296
  /**
1122
- * Record an explicit user rating (good/bad) for the last run's selected models.
1297
+ * Record an explicit user rating (good/bad) for the last COMPLETED run's models.
1123
1298
  * Explicit ratings carry 3× the weight of auto-detected outcomes.
1124
- * Does NOT clear lastSelectedModels — the auto record already did that.
1299
+ *
1300
+ * Reads the completed-run snapshot, not the in-flight map: by the time a user
1301
+ * can rate an answer, the finalizer has already run and the in-flight map is
1302
+ * empty.
1303
+ *
1304
+ * The snapshot is CONSUMED. recordExplicit() weights a rating 3x by recording
1305
+ * three samples, so a double submit would inject six — a stutter on the
1306
+ * button would count as two opinions. Consuming makes a second call a no-op
1307
+ * that reports false, which is what "rate this run" should mean.
1125
1308
  */
1126
1309
  recordExplicitRating(rating: 'good' | 'bad'): boolean;
1127
1310
  private scoreModel;
@@ -2058,9 +2241,49 @@ declare abstract class BaseTier extends EventEmitter {
2058
2241
  * which node (Cockpit node panel / Why panel).
2059
2242
  */
2060
2243
  protected servingModel?: string;
2244
+ /**
2245
+ * This tier's identity IN THE TASK GRAPH — the section or subtask id the
2246
+ * planner assigned — plus where it sat in the dependency order.
2247
+ *
2248
+ * `id` and `graphNodeId` are deliberately different id spaces, and both are
2249
+ * needed. `id` identifies this tier INSTANCE ("T2_a1b2c3d4", minted per
2250
+ * construction); `graphNodeId` identifies the WORK ("s1"), which is what
2251
+ * dependency edges point at. Without the distinction a graph view cannot be
2252
+ * drawn at all: `dependsOn` lists section ids, while every running tier
2253
+ * reports a generated tier id, so every edge would name a node that never
2254
+ * appears in the stream.
2255
+ */
2256
+ protected graphNodeId?: string;
2257
+ protected graphDependsOn?: readonly string[];
2258
+ protected graphWave?: number;
2061
2259
  constructor(role: TierRole, id?: string, parentId?: string);
2062
2260
  /** Mark this tier as the run's presenter (root tier). */
2063
2261
  setPresenter(on?: boolean): void;
2262
+ /**
2263
+ * Record where this tier sits in the compiled task graph.
2264
+ *
2265
+ * Called by whichever tier scheduled this one: the id and edges are known at
2266
+ * construction, the wave only once the scheduler reaches it, so the fields
2267
+ * are set independently rather than all at once.
2268
+ */
2269
+ setGraphPosition(position: {
2270
+ nodeId?: string;
2271
+ dependsOn?: readonly string[];
2272
+ wave?: number;
2273
+ }): void;
2274
+ /**
2275
+ * The graph fields, for spreading into a status payload.
2276
+ *
2277
+ * One helper rather than two literals because `tier:status` is emitted from
2278
+ * two different call sites with two different payload shapes; adding a field
2279
+ * to only one of them makes it arrive intermittently, which is worse for a
2280
+ * consumer than never arriving at all.
2281
+ */
2282
+ protected graphFields(): {
2283
+ nodeId?: string;
2284
+ dependsOn?: string[];
2285
+ waveId?: number;
2286
+ };
2064
2287
  getStatus(): TierStatus;
2065
2288
  protected setStatus(status: TierStatus, output?: string): void;
2066
2289
  /** Record the model serving this tier; future status events carry it. */
@@ -2344,7 +2567,20 @@ declare class T1Administrator extends BaseTier {
2344
2567
  private validatePlan;
2345
2568
  private dispatchT2Managers;
2346
2569
  /**
2347
- * Runs T2 managers respecting dependsOn declarations using Kahn's algorithm.
2570
+ * Runs T2 managers respecting dependsOn declarations.
2571
+ *
2572
+ * Graph construction, validation and cycle repair now live in
2573
+ * compileSectionGraph (orchestration/adapters.ts), and wave execution in
2574
+ * DependencyScheduler — the same pair T2 uses for its subtasks, so a plan is
2575
+ * ordered by one implementation rather than two that drifted.
2576
+ *
2577
+ * The hand-rolled Kahn pass this replaces treated every node it could not
2578
+ * reach as part of a cycle. That set also contains everything DOWNSTREAM of a
2579
+ * cycle, whose in-degree can never fall to zero while its dependency is stuck,
2580
+ * so breaking a cycle here also cut the dependencies of innocent later
2581
+ * sections and started them immediately — before the sections they consume.
2582
+ * compileTaskGraph uses strongly connected components, so only real cycle
2583
+ * members are touched.
2348
2584
  */
2349
2585
  private runT2sWithDependencies;
2350
2586
  private compileFinalOutput;
@@ -2419,8 +2655,35 @@ declare class Cascade extends EventEmitter {
2419
2655
  /** Orchestration decisions for the CURRENT run — cleared on each run(). */
2420
2656
  private decisionLog;
2421
2657
  private initialized;
2422
- /** Last task that stopped at the budget cap — powers /continue (resumeRun). */
2658
+ /**
2659
+ * Last interrupted task — powers /continue (resumeRun). Kept in memory as a
2660
+ * fast path for the same-process case; the durable copy lives in `resumeStore`
2661
+ * so a crash or an exit no longer throws away everything already finished.
2662
+ */
2423
2663
  private lastInterruptedRun?;
2664
+ /** Durable resume checkpoints. See core/orchestration/resume-store.ts. */
2665
+ private resumeStore?;
2666
+ /** Claim held by an in-flight resume, settled once the run owns the work. */
2667
+ private claimedResumeId?;
2668
+ /**
2669
+ * Facts inherited from the checkpoint this run is resuming, so its own
2670
+ * checkpoint is cumulative rather than only covering this attempt.
2671
+ */
2672
+ private resumedFrom?;
2673
+ /** Whether THIS run persisted a checkpoint of its own. Drives settle-vs-release. */
2674
+ private wroteCheckpointThisRun;
2675
+ /**
2676
+ * Whether the run reached the end of its normal path.
2677
+ *
2678
+ * NOT the same as `runError == null`: the cancellation and budget handlers
2679
+ * both null `runError` deliberately (an intentional stop is not a telemetry
2680
+ * error) and the breaker path never sets it at all. Treating that as success
2681
+ * meant an immediate cancel — no output, no replacement checkpoint — deleted
2682
+ * the original claim and the finished sections with it.
2683
+ */
2684
+ private completedNormally;
2685
+ /** Section results of the CURRENT run, so an interruption can checkpoint them. */
2686
+ private currentRunCompleted;
2424
2687
  private initPromise?;
2425
2688
  private store?;
2426
2689
  private audit?;
@@ -2583,8 +2846,24 @@ declare class Cascade extends EventEmitter {
2583
2846
  * see core/router/feedback-prior.ts.
2584
2847
  */
2585
2848
  setFeedbackSource(source: FeedbackSource): void;
2586
- /** True when a task stopped at the budget cap and can be resumed via /continue. */
2849
+ /**
2850
+ * Write a durable checkpoint for an interrupted run.
2851
+ *
2852
+ * Best-effort throughout: a run is already ending badly on every path that
2853
+ * calls this, and failing to record a resume file must not make that ending
2854
+ * worse. Nothing here is allowed to throw.
2855
+ */
2856
+ private checkpointRun;
2857
+ /** True when a task can be resumed via /continue (this process or a previous one). */
2587
2858
  hasResumableRun(): boolean;
2859
+ /**
2860
+ * True when a checkpoint from ANY process is resumable. Async because the
2861
+ * durable check touches disk; `hasResumableRun` remains the sync in-memory
2862
+ * answer for callers already on a hot path.
2863
+ */
2864
+ hasDurableResume(): Promise<boolean>;
2865
+ /** Interrupted runs available to resume, newest first. */
2866
+ listResumableRuns(): Promise<ResumeCheckpoint[]>;
2588
2867
  /**
2589
2868
  * Raise the per-run token budget for a resume and return the continuation
2590
2869
  * prompt (or null when nothing is resumable). Consumes the interrupted-run
@@ -2594,6 +2873,39 @@ declare class Cascade extends EventEmitter {
2594
2873
  prepareResume(opts?: {
2595
2874
  maxTokens?: number;
2596
2875
  }): string | null;
2876
+ /**
2877
+ * Durable counterpart to prepareResume: recovers a run interrupted in ANY
2878
+ * process, including one that crashed before it could return. Consumes the
2879
+ * checkpoint, so the same finished work is never restored into two runs.
2880
+ */
2881
+ prepareDurableResume(opts?: {
2882
+ maxTokens?: number;
2883
+ taskId?: string;
2884
+ }): Promise<string | null>;
2885
+ /**
2886
+ * Discard the claimed checkpoint once the resumed run has taken ownership of
2887
+ * the work (it has produced its own checkpoint, or finished). Called by the
2888
+ * run loop; safe to call when nothing is claimed.
2889
+ */
2890
+ private settleClaimedResume;
2891
+ /**
2892
+ * Hand back a claim taken by prepareDurableResume() when the caller never
2893
+ * gets as far as run(). The REPL prepares the continuation and submits it
2894
+ * separately, so a failure in between would otherwise strand the checkpoint
2895
+ * for the whole lease timeout before reclaimStale() could recover it.
2896
+ */
2897
+ releaseClaimedResume(): Promise<void>;
2898
+ /** A resume gets a bigger budget — the previous attempt exhausted the old one. */
2899
+ private raiseBudgetForResume;
2900
+ /**
2901
+ * The resume prompt. Completed sections are named explicitly rather than
2902
+ * gestured at: "do not recreate the files" is an instruction a planner can
2903
+ * only guess at, whereas a list of what is already done, and what it produced,
2904
+ * is something it can act on. That difference is the entire reason node
2905
+ * results are kept — a re-plan that cannot see the finished work will plan the
2906
+ * finished work again, and the user pays for it a second time.
2907
+ */
2908
+ private buildResumePrompt;
2597
2909
  /**
2598
2910
  * Resume the last budget-capped task with a raised budget (SDK/headless).
2599
2911
  * Returns null when there is nothing to resume.
@@ -2899,14 +3211,6 @@ declare class T2Manager extends BaseTier {
2899
3211
  * then reduce in-degrees of their dependents and repeat.
2900
3212
  */
2901
3213
  private runWithDependencies;
2902
- /**
2903
- * Detects cyclic dependencies using Kahn's algorithm and breaks them
2904
- * by removing back-edges. Returns a sanitized copy of assignments.
2905
- *
2906
- * A cycle like t1→t2→t3→t1 is broken at the last edge (t3→t1),
2907
- * meaning t3 will start without waiting for t1, preventing deadlock.
2908
- */
2909
- private breakCycles;
2910
3214
  private retryT3;
2911
3215
  private publishSectionOutput;
2912
3216
  private aggregateResults;
@@ -2997,6 +3301,11 @@ declare class T3Worker extends BaseTier {
2997
3301
  * needed. Best-effort: any parse/error just keeps the current output.
2998
3302
  */
2999
3303
  private reflectAndImprove;
3304
+ /**
3305
+ * Run the acceptance criteria that can be settled by looking at the workspace.
3306
+ * Reads go through the same workspace-relative resolution verifyArtifacts uses.
3307
+ */
3308
+ private checkAcceptance;
3000
3309
  private selfTest;
3001
3310
  /**
3002
3311
  * world-state v2: distill a completed subtask's output into durable
@@ -3018,6 +3327,193 @@ declare class T3Worker extends BaseTier {
3018
3327
  private isFileOperation;
3019
3328
  }
3020
3329
 
3330
+ type TaskGraphNodeKind = 'section' | 'subtask';
3331
+ type TaskGraphValidationMode = 'strict' | 'repair';
3332
+ interface TaskGraphInputNode<TPayload> {
3333
+ id: string;
3334
+ title: string;
3335
+ kind: TaskGraphNodeKind;
3336
+ dependsOn?: readonly string[];
3337
+ payload: TPayload;
3338
+ }
3339
+ interface TaskGraphNode<TPayload> {
3340
+ id: string;
3341
+ title: string;
3342
+ kind: TaskGraphNodeKind;
3343
+ dependsOn: readonly string[];
3344
+ payload: TPayload;
3345
+ /** Stable source order used to keep execution deterministic. */
3346
+ ordinal: number;
3347
+ }
3348
+ interface TaskGraph<TPayload> {
3349
+ nodes: readonly TaskGraphNode<TPayload>[];
3350
+ }
3351
+ type TaskGraphIssueCode = 'INVALID_NODE' | 'DUPLICATE_NODE_ID' | 'SELF_DEPENDENCY' | 'UNKNOWN_DEPENDENCY' | 'CYCLE_EDGE_REMOVED';
3352
+ interface TaskGraphIssue {
3353
+ code: TaskGraphIssueCode;
3354
+ message: string;
3355
+ nodeId?: string;
3356
+ dependencyId?: string;
3357
+ }
3358
+ interface CompiledTaskGraph<TPayload> {
3359
+ graph: TaskGraph<TPayload>;
3360
+ issues: readonly TaskGraphIssue[];
3361
+ }
3362
+ interface CompileTaskGraphOptions {
3363
+ /**
3364
+ * strict: reject malformed LLM plans.
3365
+ * repair: preserve today's graceful-degradation behaviour by dropping only
3366
+ * invalid dependency edges and duplicate/invalid nodes.
3367
+ */
3368
+ mode?: TaskGraphValidationMode;
3369
+ }
3370
+ declare class TaskGraphValidationError extends Error {
3371
+ readonly issues: readonly TaskGraphIssue[];
3372
+ constructor(issues: readonly TaskGraphIssue[]);
3373
+ }
3374
+ /**
3375
+ * Compile untrusted planner output into a deterministic, schedulable graph.
3376
+ * The compiler is the trust boundary between LLM-authored JSON and execution.
3377
+ */
3378
+ declare function compileTaskGraph<TPayload>(inputNodes: readonly TaskGraphInputNode<TPayload>[], options?: CompileTaskGraphOptions): CompiledTaskGraph<TPayload>;
3379
+
3380
+ type SchedulerExecutionMode = 'parallel' | 'sequential';
3381
+ /**
3382
+ * How a finished node turned out.
3383
+ *
3384
+ * The scheduler used to know only "the promise resolved", which is not the same
3385
+ * question. T1 catches worker errors into an ordinary `T2Result { status:
3386
+ * 'FAILED' }` so one dead section cannot crash the run — a resolved value. The
3387
+ * scheduler saw success and released the dependents anyway, so "run the
3388
+ * integration tests" would start after "implement the API" had failed outright,
3389
+ * and then fail too, and bill for the privilege.
3390
+ *
3391
+ * `partial` deliberately does NOT block: partial output is usable, and treating
3392
+ * it as failure would cancel work that a degraded-but-real result can support.
3393
+ */
3394
+ type NodeOutcome = 'succeeded' | 'partial' | 'failed';
3395
+ /** What to do with the dependents of a failed node. */
3396
+ type UpstreamFailurePolicy =
3397
+ /** Run them anyway — the historical behaviour, kept as the default. */
3398
+ 'continue'
3399
+ /** Skip them, transitively, and report why. */
3400
+ | 'block';
3401
+ /** Why a node never ran. */
3402
+ interface BlockedNode {
3403
+ /**
3404
+ * Ids of the failed ancestors responsible, nearest first. Stable and
3405
+ * machine-readable — use these to link a blocked node back to its cause.
3406
+ */
3407
+ blockedBy: string[];
3408
+ /**
3409
+ * The same ancestors by title, in the same order. Node ids are internal
3410
+ * ("s1", "s2"); anything a person reads should use these instead.
3411
+ */
3412
+ blockedByTitles: string[];
3413
+ /** A sentence naming the cause, written with titles rather than ids. */
3414
+ reason: string;
3415
+ }
3416
+ interface SchedulerHooks<TPayload, TResult> {
3417
+ execute(node: TaskGraphNode<TPayload>): Promise<TResult>;
3418
+ onWaveStart?(nodes: readonly TaskGraphNode<TPayload>[], wave: number): void | Promise<void>;
3419
+ onNodeComplete?(node: TaskGraphNode<TPayload>, result: TResult): void | Promise<void>;
3420
+ /**
3421
+ * Decide whether a result counts as success. Omit it and every result counts
3422
+ * as `succeeded`, which is exactly the previous behaviour — so adding failure
3423
+ * awareness to a caller is opt-in and cannot silently change an existing one.
3424
+ */
3425
+ classify?(node: TaskGraphNode<TPayload>, result: TResult): NodeOutcome;
3426
+ /**
3427
+ * A node that will never run because an ancestor failed. Callers use this to
3428
+ * synthesize a result for it, so downstream reporting still sees every node.
3429
+ */
3430
+ onNodeBlocked?(node: TaskGraphNode<TPayload>, blocked: BlockedNode): void | Promise<void>;
3431
+ }
3432
+ interface SchedulerOptions {
3433
+ mode?: SchedulerExecutionMode;
3434
+ /** Defaults to 'continue' so existing callers behave exactly as before. */
3435
+ onUpstreamFailure?: UpstreamFailurePolicy;
3436
+ }
3437
+ interface SchedulerResult<TResult> {
3438
+ results: ReadonlyMap<string, TResult>;
3439
+ waves: readonly (readonly string[])[];
3440
+ /** Nodes skipped because an ancestor failed. Empty under 'continue'. */
3441
+ blocked: ReadonlyMap<string, BlockedNode>;
3442
+ }
3443
+ /**
3444
+ * Shared deterministic dependency scheduler for T1 sections and T2 subtasks.
3445
+ * Graph validation/repair belongs in compileTaskGraph; this class only executes
3446
+ * a valid DAG in stable source order.
3447
+ */
3448
+ declare class DependencyScheduler<TPayload, TResult> {
3449
+ private readonly graph;
3450
+ private readonly hooks;
3451
+ private readonly options;
3452
+ constructor(graph: TaskGraph<TPayload>, hooks: SchedulerHooks<TPayload, TResult>, options?: SchedulerOptions);
3453
+ run(): Promise<SchedulerResult<TResult>>;
3454
+ }
3455
+
3456
+ declare function compileSectionGraph(sections: readonly T1ToT2Assignment[]): CompiledTaskGraph<T1ToT2Assignment>;
3457
+ /**
3458
+ * The subtask fields the graph actually needs. Generic over the whole record so
3459
+ * both the planner's `T3SubtaskSpec` and T2's richer `T2ToT3Assignment` compile
3460
+ * without a cast — they carry the same identity and dependency fields, and the
3461
+ * payload comes back out at its original type.
3462
+ */
3463
+ interface SubtaskGraphInput {
3464
+ subtaskId: string;
3465
+ subtaskTitle: string;
3466
+ dependsOn?: string[];
3467
+ }
3468
+ declare function compileSubtaskGraph<TSubtask extends SubtaskGraphInput>(subtasks: readonly TSubtask[]): CompiledTaskGraph<TSubtask>;
3469
+
3470
+ /**
3471
+ * The deterministic rung of the verification ladder.
3472
+ *
3473
+ * T1 asks the planner for acceptance criteria that are "1-3 checks a reviewer
3474
+ * could verify mechanically (file exists / contains X / command exits 0)", and
3475
+ * the planner obliges — but nothing ever checked them mechanically. Every
3476
+ * criterion went to selfTest(), an LLM call, which is both slower and a worse
3477
+ * judge of "does this file exist" than `stat` is. A grader that hallucinates a
3478
+ * pass on a file that was never written is exactly the failure the criteria
3479
+ * were introduced to prevent.
3480
+ *
3481
+ * So: decide here what can be decided by looking, and hand the rest to the
3482
+ * model unchanged. The governing rule is that **deferring is always safe and
3483
+ * deciding wrongly is not**, so every pattern below is deliberately narrow.
3484
+ * A criterion this module cannot confidently parse comes back `undecidable`
3485
+ * and the LLM grader sees it exactly as it does today.
3486
+ *
3487
+ * Shell criteria ("command exits 0") are deliberately NOT executed. Acceptance
3488
+ * text is LLM-authored and reaches this code as free prose; running it would
3489
+ * turn a plan into an arbitrary command execution channel. Those stay
3490
+ * undecidable.
3491
+ */
3492
+ type AcceptanceVerdict = 'passed' | 'failed' | 'undecidable';
3493
+ interface AcceptanceResult {
3494
+ criterion: string;
3495
+ verdict: AcceptanceVerdict;
3496
+ /** Human-readable reason, present for passed/failed. */
3497
+ detail?: string;
3498
+ }
3499
+ interface AcceptanceProbe {
3500
+ /** null when the path does not exist or is not a regular file. */
3501
+ stat(path: string): Promise<{
3502
+ size: number;
3503
+ } | null>;
3504
+ /** utf-8 contents, or null when unreadable (missing, binary, too large). */
3505
+ read(path: string): Promise<string | null>;
3506
+ }
3507
+ /**
3508
+ * Evaluate acceptance criteria, deciding only the mechanically checkable ones.
3509
+ * Order and length of the result always match `criteria`.
3510
+ */
3511
+ declare function evaluateAcceptance(criteria: readonly string[], ownedFiles: readonly string[], probe: AcceptanceProbe): Promise<AcceptanceResult[]>;
3512
+ /** The criteria that still need a model's judgement. */
3513
+ declare function undecided(results: readonly AcceptanceResult[]): string[];
3514
+ /** Failure messages, phrased so correctOutput can act on them directly. */
3515
+ declare function failures(results: readonly AcceptanceResult[]): string[];
3516
+
3021
3517
  /**
3022
3518
  * High-level SDK entry point — loads config from the workspace and runs a task.
3023
3519
  *
@@ -3224,7 +3720,13 @@ declare class DashboardSocket {
3224
3720
  broadcastToRoom(room: string, event: string, data: unknown): void;
3225
3721
  broadcast(event: string, data: unknown): void;
3226
3722
  emitCascadeEvent(ev: CascadeEvent): void;
3227
- emitTierStatus(tierId: string, role: string, status: string, sessionId: string, action?: string): void;
3723
+ /**
3724
+ * `extra` exists because this signature is positional: every field the tier
3725
+ * emits that is not named here is silently dropped on this path, while the
3726
+ * other two forwarders spread the whole event through. That asymmetry is how
3727
+ * a field can reach the web dashboard from one code path and not another.
3728
+ */
3729
+ emitTierStatus(tierId: string, role: string, status: string, sessionId: string, action?: string, extra?: Record<string, unknown>): void;
3228
3730
  emitStreamToken(tierId: string, text: string, sessionId: string): void;
3229
3731
  emitPeerMessage(event: PeerMessageEvent): void;
3230
3732
  emitApprovalRequest(request: PermissionRequest): void;
@@ -4812,4 +5314,4 @@ declare function gatherSyncBundle(config: CascadeConfig): SyncBundle;
4812
5314
  /** Merge a pulled bundle into a config, returning the updated config to persist. */
4813
5315
  declare function applySyncBundle(bundle: SyncBundle, config: CascadeConfig): CascadeConfig;
4814
5316
 
4815
- export { AZURE_BASE_MODELS, AZURE_BASE_URL_TEMPLATE, type ApprovalRequest, type ApprovalResponse, type AuditEntry, AuditLogger, type BenchmarksConfig, type Block, type BudgetConfig, CASCADE_AUDIT_FILE, CASCADE_CONFIG_DIR, CASCADE_CONFIG_FILE, CASCADE_DASHBOARD_SECRET_FILE, CASCADE_DB_FILE, CASCADE_IGNORE_FILE, CASCADE_KEYSTORE_FILE, CASCADE_MD_FILE, CASCADE_VERSION, CHARS_PER_TOKEN, COMPLEXITY_T2_COUNT, Cascade, CascadeCancelledError, type CascadeConfig, type CascadeEvent, type CascadeEventType, CascadeIgnore, type CascadeMessage, CascadeRouter, type CascadeRunOptions, type CascadeRunResult, type CascadeThemeName, CascadeToolError, type ChartSeries, type ChartSpec, type Chunk, type ChunkOptions, CloudClient, type CloudConversation, type CloudMessage, type CloudSession, type CloudSessionStore, type CloudTurnInput, type CloudUser, type CodeChunkOptions, type CodeChunker, CodeSearchTool, type CompleteFn, ConfigManager, type ConversationMessage, type CurrentPageProvider, type CurrentPageSnapshot, CurrentPageTool, DEFAULT_API_PORT, DEFAULT_APPROVAL_REQUIRED, DEFAULT_AUTO_SUMMARIZE_AT, DEFAULT_CLOUD_URL, DEFAULT_CONTEXT_LIMIT, DEFAULT_DASHBOARD_PORT, DEFAULT_MAX_SESSION_MESSAGES, DEFAULT_RETENTION_DAYS, DEFAULT_THEME, DOCUMENT_MIME, type DashboardConfig, DashboardServer, type DeviceStart, type DiscoveredMcpTool, type DistilledFact, type ChartKind as DocumentChartKind, type DocumentFormat, type Embedder, type EncryptedBlob, type EscalationDecision, type EscalationPayload, type ExtendedContextConfig, type FileManifest, FileMcpOAuthStore, GITHUB_MODELS_API_VERSION, GITHUB_MODELS_CATALOG_URL, GITHUB_MODELS_INFERENCE_URL, GLOBAL_CONFIG_DIR, GLOBAL_CREDENTIALS_FILE, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, GenerateDocumentTool, type GenerateOptions, type GenerateResult, type GraphFactSource, GraphRetriever, type GraphSearchOptions, GraphSearchTool, type HookDefinition, type HooksConfig, HooksRunner, type Identity, type ImageAttachment, type ImageByteLoader, type ImageInfo, type ImageRef, type InlineRun, Keystore, LLMReranker, LM_STUDIO_BASE_URL, type LegacyThemeName, type LoadedImage, MAX_PAGE_TEXT_CHARS, MCP_TOOL_PREFIX, MODELS, type ManifestDiff, McpClient, type McpDiscoveryResult, McpOAuthProvider, type McpOAuthState, type McpOAuthStore, type McpServerConfig$1 as McpServerConfig, type McpServerRename, type MemoryConfig, MemoryStore, type Message, type MessageContent, type MessagePayload, type MessageStatus, type MessageType, type ModelInfo, type ModelOverrides, type NativeProvider, OLLAMA_BASE_URL, OpenAICompatibleEmbedder, type OpenAIEmbedderOptions, PROVIDER_DISPLAY_NAMES, type PeerMessage, type PeerMessageEvent, type PeerSyncPayload, type PeerSyncType, type PermissionDecision, type PermissionDecisionPayload, type PermissionRequest, type PermissionTrailEntry, type PlanReviewConfig, type ProviderConfig, type ProviderType, type RankedFact, type RefreshResult, type RenderOptions, type Reranker, type RetrievalContext, type RetrievalMode, type RetrievalPlan, Retriever, type RetrieverSearchOptions, type RuntimeNode, type RuntimeNodeLog, type RuntimeRefreshPayload, type RuntimeScope, type RuntimeSession, type RuntimeSnapshotPayload, type ScheduledTask, type ScoredChunk, type SearchOptions, type Session, type SessionCheckpoint, type SessionMetadata, type SessionSubscriptionPayload, type Slide, SqliteVectorStore, type StatusUpdate, type StoredMessage, type StreamChunk, type SyncBundle, T1Administrator, type T1ToT2Assignment, T1_MODEL_PRIORITY, T2Manager, type T2Result, type T2ToT3Assignment, T2_MODEL_PRIORITY, type T3Result, type T3ResultPayload, type T3SubtaskSpec, T3Worker, T3_MODEL_PRIORITY, THEME_NAMES, TOOL_NAMES, type TaskComplexity, TaskScheduler, Telemetry, type TelemetryConfig, type Theme, type ThemeColors, type ThemeName, type TierConfig, type TierLimits, type TierRole, type TierStatus, type TokenUsage, type ToolCall, type ToolDefinition, type ToolExecuteOptions, ToolRegistry, type ToolResult, type ToolsConfig, VISION_MODEL_PRIORITY, type VectorStore, type WebSearchConfig, type WebhookConfig, type WorkspaceConfig, type WorkspaceFileReader, WorkspaceIndex, type WorkspaceIndexOptions, applySyncBundle, azureModelForDeployment, beginMcpOAuth, buildDocumentTools, buildManifest, buildSessionTranscript, bytesToBase64, cagCharBudget, chartKind, chartToTableRows, chatCompleterFromProviders, chunkCode, chunkText, completeMcpOAuth, connectMcpWithLoopbackOAuth, createCascade, decryptJSON as decryptSyncBlob, diffManifest, disambiguateMcpServerNames, discoverMcpAuthServer, discoverMcpTools, distillSessionFacts, embedderFromProviders, encryptJSON as encryptSyncBlob, extractCharts, fileExt, fileOAuthProvider, gatherSyncBundle, hashContent, heuristicCodeChunker, inferAzureBaseModel, inlineRuns, isDocumentFormat, isMcpToolName, matchImageLine, mcpServerPrefix, mcpToolName, nodeHttpFetch, parseBlocks, parseChartSpec, parseDelimited, parseRankOrder, parseSlide, planRetrieval, preferIpv4Host, reciprocalRankFusion, refreshMcpToken, removeMcpServerDenials, renderDocument, renderDocx, renderPptx, renderXlsx, runCascade, sessionWorthRemembering, sniffImage, splitSlides, streamCascade, stripInline, uniqueMcpServerName };
5317
+ export { AZURE_BASE_MODELS, AZURE_BASE_URL_TEMPLATE, type AcceptanceProbe, type AcceptanceResult, type AcceptanceVerdict, type ApprovalRequest, type ApprovalResponse, type AuditEntry, AuditLogger, type BenchmarksConfig, type Block, type BudgetConfig, CASCADE_AUDIT_FILE, CASCADE_CONFIG_DIR, CASCADE_CONFIG_FILE, CASCADE_DASHBOARD_SECRET_FILE, CASCADE_DB_FILE, CASCADE_IGNORE_FILE, CASCADE_KEYSTORE_FILE, CASCADE_MD_FILE, CASCADE_VERSION, CHARS_PER_TOKEN, COMPLEXITY_T2_COUNT, Cascade, CascadeCancelledError, type CascadeConfig, type CascadeEvent, type CascadeEventType, CascadeIgnore, type CascadeMessage, CascadeRouter, type CascadeRunOptions, type CascadeRunResult, type CascadeThemeName, CascadeToolError, type ChartSeries, type ChartSpec, type Chunk, type ChunkOptions, CloudClient, type CloudConversation, type CloudMessage, type CloudSession, type CloudSessionStore, type CloudTurnInput, type CloudUser, type CodeChunkOptions, type CodeChunker, CodeSearchTool, type CompileTaskGraphOptions, type CompiledTaskGraph, type CompleteFn, type CompletedNode, ConfigManager, type ConversationMessage, type CurrentPageProvider, type CurrentPageSnapshot, CurrentPageTool, DEFAULT_API_PORT, DEFAULT_APPROVAL_REQUIRED, DEFAULT_AUTO_SUMMARIZE_AT, DEFAULT_CLOUD_URL, DEFAULT_CONTEXT_LIMIT, DEFAULT_DASHBOARD_PORT, DEFAULT_MAX_SESSION_MESSAGES, DEFAULT_RETENTION_DAYS, DEFAULT_THEME, DOCUMENT_MIME, type DashboardConfig, DashboardServer, DependencyScheduler, type DeviceStart, type DiscoveredMcpTool, type DistilledFact, type ChartKind as DocumentChartKind, type DocumentFormat, type Embedder, type EncryptedBlob, type EscalationDecision, type EscalationPayload, type ExtendedContextConfig, type FileManifest, FileMcpOAuthStore, GITHUB_MODELS_API_VERSION, GITHUB_MODELS_CATALOG_URL, GITHUB_MODELS_INFERENCE_URL, GLOBAL_CONFIG_DIR, GLOBAL_CREDENTIALS_FILE, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, GenerateDocumentTool, type GenerateOptions, type GenerateResult, type GraphFactSource, GraphRetriever, type GraphSearchOptions, GraphSearchTool, type HookDefinition, type HooksConfig, HooksRunner, type Identity, type ImageAttachment, type ImageByteLoader, type ImageInfo, type ImageRef, type InlineRun, Keystore, LLMReranker, LM_STUDIO_BASE_URL, type LegacyThemeName, type LoadedImage, MAX_PAGE_TEXT_CHARS, MCP_TOOL_PREFIX, MODELS, type ManifestDiff, McpClient, type McpDiscoveryResult, McpOAuthProvider, type McpOAuthState, type McpOAuthStore, type McpServerConfig$1 as McpServerConfig, type McpServerRename, type MemoryConfig, MemoryStore, type Message, type MessageContent, type MessagePayload, type MessageStatus, type MessageType, type ModelInfo, type ModelOverrides, type NativeProvider, OLLAMA_BASE_URL, OpenAICompatibleEmbedder, type OpenAIEmbedderOptions, PROVIDER_DISPLAY_NAMES, type PeerMessage, type PeerMessageEvent, type PeerSyncPayload, type PeerSyncType, type PermissionDecision, type PermissionDecisionPayload, type PermissionRequest, type PermissionTrailEntry, type PlanReviewConfig, type ProviderConfig, type ProviderType, RESUME_SCHEMA_VERSION, type RankedFact, type RefreshResult, type RenderOptions, type Reranker, type ResumeCheckpoint, type ResumeReason, ResumeStore, type ResumeStoreOptions, type RetrievalContext, type RetrievalMode, type RetrievalPlan, Retriever, type RetrieverSearchOptions, type RuntimeNode, type RuntimeNodeLog, type RuntimeRefreshPayload, type RuntimeScope, type RuntimeSession, type RuntimeSnapshotPayload, type ScheduledTask, type SchedulerExecutionMode, type SchedulerHooks, type SchedulerOptions, type SchedulerResult, type ScoredChunk, type SearchOptions, type Session, type SessionCheckpoint, type SessionMetadata, type SessionSubscriptionPayload, type Slide, SqliteVectorStore, type StatusUpdate, type StoredMessage, type StreamChunk, type SyncBundle, T1Administrator, type T1ToT2Assignment, T1_MODEL_PRIORITY, T2Manager, type T2Result, type T2ToT3Assignment, T2_MODEL_PRIORITY, type T3Result, type T3ResultPayload, type T3SubtaskSpec, T3Worker, T3_MODEL_PRIORITY, THEME_NAMES, TOOL_NAMES, type TaskComplexity, type TaskGraph, type TaskGraphInputNode, type TaskGraphIssue, type TaskGraphIssueCode, type TaskGraphNode, type TaskGraphNodeKind, TaskGraphValidationError, type TaskGraphValidationMode, TaskScheduler, Telemetry, type TelemetryConfig, type Theme, type ThemeColors, type ThemeName, type TierConfig, type TierLimits, type TierRole, type TierStatus, type TokenUsage, type ToolCall, type ToolDefinition, type ToolExecuteOptions, ToolRegistry, type ToolResult, type ToolsConfig, VISION_MODEL_PRIORITY, type VectorStore, type WebSearchConfig, type WebhookConfig, type WorkspaceConfig, type WorkspaceFileReader, WorkspaceIndex, type WorkspaceIndexOptions, applySyncBundle, azureModelForDeployment, beginMcpOAuth, buildDocumentTools, buildManifest, buildSessionTranscript, bytesToBase64, cagCharBudget, chartKind, chartToTableRows, chatCompleterFromProviders, chunkCode, chunkText, compileSectionGraph, compileSubtaskGraph, compileTaskGraph, completeMcpOAuth, connectMcpWithLoopbackOAuth, createCascade, decryptJSON as decryptSyncBlob, diffManifest, disambiguateMcpServerNames, discoverMcpAuthServer, discoverMcpTools, distillSessionFacts, embedderFromProviders, encryptJSON as encryptSyncBlob, evaluateAcceptance, extractCharts, failures, fileExt, fileOAuthProvider, gatherSyncBundle, hashContent, heuristicCodeChunker, inferAzureBaseModel, inlineRuns, isDocumentFormat, isMcpToolName, matchImageLine, mcpServerPrefix, mcpToolName, nodeHttpFetch, parseBlocks, parseChartSpec, parseDelimited, parseRankOrder, parseSlide, planRetrieval, preferIpv4Host, reciprocalRankFusion, refreshMcpToken, removeMcpServerDenials, renderDocument, renderDocx, renderPptx, renderXlsx, runCascade, sessionWorthRemembering, sniffImage, splitSlides, streamCascade, stripInline, summarizeCompleted, undecided, uniqueMcpServerName };