@smithers-orchestrator/driver 0.24.0 → 0.25.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/package.json +6 -6
- package/src/ResolveOutputRow.ts +26 -0
- package/src/RunOptions.ts +5 -0
- package/src/RunStatus.ts +1 -0
- package/src/SmithersCtx.js +72 -10
- package/src/WorkflowDriver.js +28 -19
- package/src/index.d.ts +173 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smithers-orchestrator/driver",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Workflow execution driver — runs tasks, acts on EngineDecisions, reports results",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -63,11 +63,11 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"effect": "^3.21.1",
|
|
65
65
|
"zod": "^4.3.6",
|
|
66
|
-
"@smithers-orchestrator/db": "0.
|
|
67
|
-
"@smithers-orchestrator/
|
|
68
|
-
"@smithers-orchestrator/
|
|
69
|
-
"@smithers-orchestrator/
|
|
70
|
-
"@smithers-orchestrator/scheduler": "0.
|
|
66
|
+
"@smithers-orchestrator/db": "0.25.0",
|
|
67
|
+
"@smithers-orchestrator/graph": "0.25.0",
|
|
68
|
+
"@smithers-orchestrator/observability": "0.25.0",
|
|
69
|
+
"@smithers-orchestrator/errors": "0.25.0",
|
|
70
|
+
"@smithers-orchestrator/scheduler": "0.25.0"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@types/bun": "latest",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the row type a `ctx.output`/`ctx.outputMaybe`/`ctx.latest` call returns
|
|
5
|
+
* from the `table` argument it was given:
|
|
6
|
+
*
|
|
7
|
+
* - a string table name (a key of the workflow Schema) → the inferred row for
|
|
8
|
+
* that registered schema (`outputs.X` keyed by name);
|
|
9
|
+
* - a Zod schema object (e.g. `outputs.research`) → `z.infer` of that schema;
|
|
10
|
+
* - a Drizzle table (carries `$inferSelect`) → its select row;
|
|
11
|
+
* - anything else (a widened `string`, `unknown`) → an untyped output row.
|
|
12
|
+
*
|
|
13
|
+
* This is what makes `ctx.outputMaybe(outputs.research, ...)` carry the research
|
|
14
|
+
* fields instead of an untyped `Record<string, unknown>`.
|
|
15
|
+
*/
|
|
16
|
+
export type ResolveOutputRow<Schema, T> = T extends keyof Schema
|
|
17
|
+
? Schema[T] extends z.ZodTypeAny
|
|
18
|
+
? z.infer<Schema[T]>
|
|
19
|
+
: Schema[T] extends { $inferSelect: infer R }
|
|
20
|
+
? R
|
|
21
|
+
: Record<string, unknown>
|
|
22
|
+
: T extends z.ZodTypeAny
|
|
23
|
+
? z.infer<T>
|
|
24
|
+
: T extends { $inferSelect: infer R }
|
|
25
|
+
? R
|
|
26
|
+
: Record<string, unknown>;
|
package/src/RunOptions.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RunAuthContext } from "./RunAuthContext.ts";
|
|
2
|
+
import type { OutputSnapshot } from "./OutputSnapshot.ts";
|
|
2
3
|
import type { SmithersEvent } from "@smithers-orchestrator/observability/SmithersEvent";
|
|
3
4
|
|
|
4
5
|
export type HotReloadOptions = {
|
|
@@ -19,6 +20,7 @@ export type RunOptions = {
|
|
|
19
20
|
parentRunId?: string | null;
|
|
20
21
|
input: Record<string, unknown>;
|
|
21
22
|
maxConcurrency?: number;
|
|
23
|
+
requireRerenderOnOutputChange?: boolean;
|
|
22
24
|
onProgress?: (e: SmithersEvent) => void;
|
|
23
25
|
signal?: AbortSignal;
|
|
24
26
|
resume?: boolean;
|
|
@@ -34,6 +36,9 @@ export type RunOptions = {
|
|
|
34
36
|
auth?: RunAuthContext | null;
|
|
35
37
|
config?: Record<string, unknown>;
|
|
36
38
|
cliAgentToolsDefault?: "all" | "explicit-only";
|
|
39
|
+
initialOutputs?: OutputSnapshot;
|
|
40
|
+
initialIteration?: number;
|
|
41
|
+
initialIterations?: Record<string, number> | ReadonlyMap<string, number>;
|
|
37
42
|
resumeClaim?: {
|
|
38
43
|
claimOwnerId: string;
|
|
39
44
|
claimHeartbeatAtMs: number;
|
package/src/RunStatus.ts
CHANGED
package/src/SmithersCtx.js
CHANGED
|
@@ -12,6 +12,11 @@ import { resolveWorktreePath } from "@smithers-orchestrator/graph";
|
|
|
12
12
|
/** @typedef {import("./SmithersRuntimeConfig.ts").SmithersRuntimeConfig} SmithersRuntimeConfig */
|
|
13
13
|
/** @typedef {unknown} TableRef */
|
|
14
14
|
/** @typedef {Record<string, unknown>} OutputRow User-visible output row — harness metadata fields (runId, nodeId, iteration) are stripped. */
|
|
15
|
+
/**
|
|
16
|
+
* @template Schema
|
|
17
|
+
* @template T
|
|
18
|
+
* @typedef {import("./ResolveOutputRow.ts").ResolveOutputRow<Schema, T>} ResolveOutputRow
|
|
19
|
+
*/
|
|
15
20
|
/**
|
|
16
21
|
* @template Schema
|
|
17
22
|
* @typedef {import("./OutputAccessor.ts").OutputAccessor<Schema>} OutputAccessor
|
|
@@ -119,36 +124,93 @@ export class SmithersCtx {
|
|
|
119
124
|
});
|
|
120
125
|
}
|
|
121
126
|
/**
|
|
122
|
-
* @
|
|
127
|
+
* @template {keyof Schema & string} K
|
|
128
|
+
* @overload
|
|
129
|
+
* @param {K} table
|
|
130
|
+
* @param {OutputKey} key
|
|
131
|
+
* @returns {ResolveOutputRow<Schema, K>}
|
|
132
|
+
*/
|
|
133
|
+
/**
|
|
134
|
+
* @template {TableRef} T
|
|
135
|
+
* @overload
|
|
136
|
+
* @param {T} table
|
|
123
137
|
* @param {OutputKey} key
|
|
124
|
-
* @returns {
|
|
138
|
+
* @returns {ResolveOutputRow<Schema, T>}
|
|
139
|
+
*/
|
|
140
|
+
/**
|
|
141
|
+
* @template {TableRef} T
|
|
142
|
+
* @param {T} table
|
|
143
|
+
* @param {OutputKey} key
|
|
144
|
+
* @returns {ResolveOutputRow<Schema, T>}
|
|
125
145
|
*/
|
|
126
146
|
output(table, key) {
|
|
127
147
|
const row = this.resolveRow(table, key);
|
|
128
148
|
if (!row) {
|
|
129
149
|
throw new SmithersError("MISSING_OUTPUT", `Missing output for nodeId=${key.nodeId} iteration=${key.iteration ?? 0}`, { nodeId: key.nodeId, iteration: key.iteration ?? 0 });
|
|
130
150
|
}
|
|
131
|
-
return /** @type {
|
|
151
|
+
return /** @type {ResolveOutputRow<Schema, T>} */ (/** @type {unknown} */ (stripAutoColumns(row)));
|
|
132
152
|
}
|
|
133
153
|
/**
|
|
134
|
-
*
|
|
154
|
+
* Resolve a single output row. Without an explicit `key.iteration` this
|
|
155
|
+
* resolves the CURRENT render iteration — which equals the loop iteration
|
|
156
|
+
* only for a single, non-nested loop, and is 0 when several loops coexist.
|
|
157
|
+
* For a `<Loop>` exit condition use {@link latest} (the most recent
|
|
158
|
+
* iteration), not `outputMaybe`, or an `until` built on it never advances.
|
|
159
|
+
*
|
|
160
|
+
* @template {keyof Schema & string} K
|
|
161
|
+
* @overload
|
|
162
|
+
* @param {K} table
|
|
135
163
|
* @param {OutputKey} key
|
|
136
|
-
* @returns {
|
|
164
|
+
* @returns {ResolveOutputRow<Schema, K> | undefined}
|
|
165
|
+
*/
|
|
166
|
+
/**
|
|
167
|
+
* @template {TableRef} T
|
|
168
|
+
* @overload
|
|
169
|
+
* @param {T} table
|
|
170
|
+
* @param {OutputKey} key
|
|
171
|
+
* @returns {ResolveOutputRow<Schema, T> | undefined}
|
|
172
|
+
*/
|
|
173
|
+
/**
|
|
174
|
+
* @template {TableRef} T
|
|
175
|
+
* @param {T} table
|
|
176
|
+
* @param {OutputKey} key
|
|
177
|
+
* @returns {ResolveOutputRow<Schema, T> | undefined}
|
|
137
178
|
*/
|
|
138
179
|
outputMaybe(table, key) {
|
|
139
180
|
const row = this.resolveRow(table, key);
|
|
140
|
-
return row !== undefined ? /** @type {
|
|
181
|
+
return row !== undefined ? /** @type {ResolveOutputRow<Schema, T>} */ (/** @type {unknown} */ (stripAutoColumns(row))) : undefined;
|
|
141
182
|
}
|
|
142
183
|
/**
|
|
143
|
-
*
|
|
184
|
+
* Resolve the most recent iteration's output row for `nodeId` (highest
|
|
185
|
+
* iteration across all matching rows). This is the correct reader for a
|
|
186
|
+
* `<Loop>`/`<Ralph>` `until` exit condition; {@link outputMaybe} resolves
|
|
187
|
+
* the current render iteration and can read stale/iteration-0 data inside a
|
|
188
|
+
* loop.
|
|
189
|
+
*
|
|
190
|
+
* @template {keyof Schema & string} K
|
|
191
|
+
* @overload
|
|
192
|
+
* @param {K} table
|
|
144
193
|
* @param {string} nodeId
|
|
145
|
-
* @returns {
|
|
194
|
+
* @returns {ResolveOutputRow<Schema, K> | undefined}
|
|
195
|
+
*/
|
|
196
|
+
/**
|
|
197
|
+
* @template {TableRef} T
|
|
198
|
+
* @overload
|
|
199
|
+
* @param {T} table
|
|
200
|
+
* @param {string} nodeId
|
|
201
|
+
* @returns {ResolveOutputRow<Schema, T> | undefined}
|
|
202
|
+
*/
|
|
203
|
+
/**
|
|
204
|
+
* @template {TableRef} T
|
|
205
|
+
* @param {T} table
|
|
206
|
+
* @param {string} nodeId
|
|
207
|
+
* @returns {ResolveOutputRow<Schema, T> | undefined}
|
|
146
208
|
*/
|
|
147
209
|
latest(table, nodeId) {
|
|
148
210
|
const tableName = this.resolveTableName(table);
|
|
149
211
|
const rows = this._outputs[tableName] ?? [];
|
|
150
212
|
const matching = filterRowsByNodeId(rows, nodeId, this._currentScopes);
|
|
151
|
-
/** @type {
|
|
213
|
+
/** @type {ResolveOutputRow<Schema, T> | undefined} */
|
|
152
214
|
let best = undefined;
|
|
153
215
|
let bestIteration = -Infinity;
|
|
154
216
|
for (const row of matching) {
|
|
@@ -156,7 +218,7 @@ export class SmithersCtx {
|
|
|
156
218
|
? Number(row.iteration)
|
|
157
219
|
: 0;
|
|
158
220
|
if (!best || iter >= bestIteration) {
|
|
159
|
-
best = row;
|
|
221
|
+
best = /** @type {ResolveOutputRow<Schema, T>} */ (/** @type {unknown} */ (row));
|
|
160
222
|
bestIteration = iter;
|
|
161
223
|
}
|
|
162
224
|
}
|
package/src/WorkflowDriver.js
CHANGED
|
@@ -20,7 +20,6 @@ import { withAbort } from "./withAbort.js";
|
|
|
20
20
|
/** @typedef {import("@smithers-orchestrator/graph/types").TaskDescriptor} TaskDescriptor */
|
|
21
21
|
|
|
22
22
|
const SCHEDULER_SPECIFIER = "@smithers-orchestrator/scheduler";
|
|
23
|
-
const LOCAL_SCHEDULER_SPECIFIER = "../../scheduler/src/index.js";
|
|
24
23
|
function createRunId() {
|
|
25
24
|
return `run_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
26
25
|
}
|
|
@@ -155,19 +154,17 @@ function mergeOutputSnapshots(base, live) {
|
|
|
155
154
|
* @returns {Promise<CreateWorkflowSession | null>}
|
|
156
155
|
*/
|
|
157
156
|
async function loadCreateSession() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return mod.makeWorkflowSession;
|
|
170
|
-
}
|
|
157
|
+
let mod;
|
|
158
|
+
try {
|
|
159
|
+
// The scheduler is a workspace dependency, so the package specifier always
|
|
160
|
+
// resolves (no relative-path fallback needed).
|
|
161
|
+
mod = (await import(SCHEDULER_SPECIFIER));
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
if (typeof mod.makeWorkflowSession === "function") {
|
|
167
|
+
return mod.makeWorkflowSession;
|
|
171
168
|
}
|
|
172
169
|
return null;
|
|
173
170
|
}
|
|
@@ -276,6 +273,8 @@ export class WorkflowDriver {
|
|
|
276
273
|
baseOutputs = {};
|
|
277
274
|
/** @type {Map<string, Promise<{ key: string; task: TaskDescriptor; kind: "completed" | "failed" | "cancelled"; output?: unknown; error?: unknown }>>} */
|
|
278
275
|
inflightTasks = new Map();
|
|
276
|
+
/** @type {Map<string, TaskDescriptor>} */
|
|
277
|
+
inflightTaskDescriptors = new Map();
|
|
279
278
|
/** @type {Array<{ key: string; task: TaskDescriptor; kind: "completed" | "failed" | "cancelled"; output?: unknown; error?: unknown }>} */
|
|
280
279
|
settledTasks = [];
|
|
281
280
|
/**
|
|
@@ -428,6 +427,7 @@ export class WorkflowDriver {
|
|
|
428
427
|
defaultIteration: iteration,
|
|
429
428
|
baseRootDir,
|
|
430
429
|
workflowPath,
|
|
430
|
+
trigger: context.trigger,
|
|
431
431
|
});
|
|
432
432
|
// Capture tasks that deferred on unresolved deps this render so the run
|
|
433
433
|
// loop can fail loudly if any survive to a Finished decision instead of
|
|
@@ -490,10 +490,12 @@ export class WorkflowDriver {
|
|
|
490
490
|
}
|
|
491
491
|
})().then((settled) => {
|
|
492
492
|
this.inflightTasks.delete(key);
|
|
493
|
+
this.inflightTaskDescriptors.delete(key);
|
|
493
494
|
this.settledTasks.push(settled);
|
|
494
495
|
return settled;
|
|
495
496
|
});
|
|
496
497
|
this.inflightTasks.set(key, promise);
|
|
498
|
+
this.inflightTaskDescriptors.set(key, task);
|
|
497
499
|
}
|
|
498
500
|
/**
|
|
499
501
|
* Wait for the next settled task (or an optional deadline) and report it to
|
|
@@ -506,9 +508,12 @@ export class WorkflowDriver {
|
|
|
506
508
|
if (!this.session) {
|
|
507
509
|
throw new Error("WorkflowSession is not initialized.");
|
|
508
510
|
}
|
|
509
|
-
|
|
511
|
+
let waitedTasks = [];
|
|
512
|
+
let waitStart = 0;
|
|
510
513
|
try {
|
|
511
514
|
if (this.settledTasks.length === 0 && this.inflightTasks.size > 0) {
|
|
515
|
+
waitedTasks = [...this.inflightTaskDescriptors.values()];
|
|
516
|
+
waitStart = performance.now();
|
|
512
517
|
const racers = [...this.inflightTasks.values()];
|
|
513
518
|
if (deadlineMs != null) {
|
|
514
519
|
racers.push(sleepWithAbort(deadlineMs, this.activeOptions?.signal).then(() => null));
|
|
@@ -517,10 +522,12 @@ export class WorkflowDriver {
|
|
|
517
522
|
}
|
|
518
523
|
}
|
|
519
524
|
finally {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
525
|
+
if (waitedTasks.length > 0) {
|
|
526
|
+
await this.onSchedulerWait?.(performance.now() - waitStart, {
|
|
527
|
+
runId: this.activeRunId,
|
|
528
|
+
tasks: waitedTasks,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
524
531
|
}
|
|
525
532
|
if (this.activeOptions?.signal?.aborted) {
|
|
526
533
|
return this.cancelRun();
|
|
@@ -603,6 +610,8 @@ export class WorkflowDriver {
|
|
|
603
610
|
return { runId: this.activeRunId, status: "waiting-event" };
|
|
604
611
|
case "Timer":
|
|
605
612
|
return { runId: this.activeRunId, status: "waiting-timer" };
|
|
613
|
+
case "Quota":
|
|
614
|
+
return { runId: this.activeRunId, status: "waiting-quota" };
|
|
606
615
|
case "RetryBackoff": {
|
|
607
616
|
await sleepWithAbort(reason.waitMs, this.activeOptions?.signal);
|
|
608
617
|
if (this.activeOptions?.signal?.aborted) {
|
package/src/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as _smithers_orchestrator_graph_types from '@smithers-orchestrator/graph/types';
|
|
2
2
|
import { WorkflowGraph as WorkflowGraph$1, TaskDescriptor as TaskDescriptor$1 } from '@smithers-orchestrator/graph/types';
|
|
3
3
|
import { SmithersEvent } from '@smithers-orchestrator/observability/SmithersEvent';
|
|
4
|
-
import * as
|
|
4
|
+
import * as _smithers_orchestrator_scheduler from '@smithers-orchestrator/scheduler';
|
|
5
5
|
import { WaitReason as WaitReason$1, EngineDecision as EngineDecision$1 } from '@smithers-orchestrator/scheduler';
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import { SmithersWorkflowOptions } from '@smithers-orchestrator/scheduler/SmithersWorkflowOptions';
|
|
8
8
|
import { SchemaRegistryEntry } from '@smithers-orchestrator/db/SchemaRegistryEntry';
|
|
9
|
-
import * as
|
|
9
|
+
import * as _smithers_orchestrator_graph from '@smithers-orchestrator/graph';
|
|
10
10
|
import { ExtractOptions, WorkflowGraph } from '@smithers-orchestrator/graph';
|
|
11
11
|
|
|
12
12
|
type TaskCompletedEvent = {
|
|
@@ -40,6 +40,10 @@ type RunAuthContext$2 = {
|
|
|
40
40
|
createdAt: string;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
type OutputSnapshot$2<TFallback = unknown> = {
|
|
44
|
+
[tableName: string]: Array<TFallback>;
|
|
45
|
+
};
|
|
46
|
+
|
|
43
47
|
type HotReloadOptions$1 = {
|
|
44
48
|
/** Root directory to watch for changes (default: auto-detect from workflow entry) */
|
|
45
49
|
rootDir?: string;
|
|
@@ -57,6 +61,7 @@ type RunOptions$2 = {
|
|
|
57
61
|
parentRunId?: string | null;
|
|
58
62
|
input: Record<string, unknown>;
|
|
59
63
|
maxConcurrency?: number;
|
|
64
|
+
requireRerenderOnOutputChange?: boolean;
|
|
60
65
|
onProgress?: (e: SmithersEvent) => void;
|
|
61
66
|
signal?: AbortSignal;
|
|
62
67
|
resume?: boolean;
|
|
@@ -72,6 +77,9 @@ type RunOptions$2 = {
|
|
|
72
77
|
auth?: RunAuthContext$2 | null;
|
|
73
78
|
config?: Record<string, unknown>;
|
|
74
79
|
cliAgentToolsDefault?: "all" | "explicit-only";
|
|
80
|
+
initialOutputs?: OutputSnapshot$2;
|
|
81
|
+
initialIteration?: number;
|
|
82
|
+
initialIterations?: Record<string, number> | ReadonlyMap<string, number>;
|
|
75
83
|
resumeClaim?: {
|
|
76
84
|
claimOwnerId: string;
|
|
77
85
|
claimHeartbeatAtMs: number;
|
|
@@ -80,7 +88,7 @@ type RunOptions$2 = {
|
|
|
80
88
|
};
|
|
81
89
|
};
|
|
82
90
|
|
|
83
|
-
type RunStatus$1 = "running" | "waiting-approval" | "waiting-event" | "waiting-timer" | "finished" | "continued" | "failed" | "cancelled";
|
|
91
|
+
type RunStatus$1 = "running" | "waiting-approval" | "waiting-event" | "waiting-timer" | "waiting-quota" | "finished" | "continued" | "failed" | "cancelled";
|
|
84
92
|
|
|
85
93
|
type RunResult$2 = {
|
|
86
94
|
readonly runId: string;
|
|
@@ -136,10 +144,25 @@ type OutputAccessor$2<Schema, TRow = unknown> = {
|
|
|
136
144
|
} & {
|
|
137
145
|
[K in keyof Schema & string]: Array<InferOutputEntry$1<Schema[K]>>;
|
|
138
146
|
};
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Resolve the row type a `ctx.output`/`ctx.outputMaybe`/`ctx.latest` call returns
|
|
150
|
+
* from the `table` argument it was given:
|
|
151
|
+
*
|
|
152
|
+
* - a string table name (a key of the workflow Schema) → the inferred row for
|
|
153
|
+
* that registered schema (`outputs.X` keyed by name);
|
|
154
|
+
* - a Zod schema object (e.g. `outputs.research`) → `z.infer` of that schema;
|
|
155
|
+
* - a Drizzle table (carries `$inferSelect`) → its select row;
|
|
156
|
+
* - anything else (a widened `string`, `unknown`) → an untyped output row.
|
|
157
|
+
*
|
|
158
|
+
* This is what makes `ctx.outputMaybe(outputs.research, ...)` carry the research
|
|
159
|
+
* fields instead of an untyped `Record<string, unknown>`.
|
|
160
|
+
*/
|
|
161
|
+
type ResolveOutputRow$1<Schema, T> = T extends keyof Schema ? Schema[T] extends z.ZodTypeAny ? z.infer<Schema[T]> : Schema[T] extends {
|
|
162
|
+
$inferSelect: infer R;
|
|
163
|
+
} ? R : Record<string, unknown> : T extends z.ZodTypeAny ? z.infer<T> : T extends {
|
|
164
|
+
$inferSelect: infer R;
|
|
165
|
+
} ? R : Record<string, unknown>;
|
|
143
166
|
|
|
144
167
|
type SmithersRuntimeConfig$1 = {
|
|
145
168
|
cliAgentToolsDefault?: "all" | "explicit-only";
|
|
@@ -148,10 +171,6 @@ type SmithersRuntimeConfig$1 = {
|
|
|
148
171
|
worktreePaths?: Record<string, string>;
|
|
149
172
|
};
|
|
150
173
|
|
|
151
|
-
type OutputSnapshot$2<TFallback = unknown> = {
|
|
152
|
-
[tableName: string]: Array<TFallback>;
|
|
153
|
-
};
|
|
154
|
-
|
|
155
174
|
type SmithersCtxOptions$2 = {
|
|
156
175
|
runId: string;
|
|
157
176
|
iteration: number;
|
|
@@ -210,6 +229,19 @@ declare class SmithersCtx<Schema extends unknown = unknown> {
|
|
|
210
229
|
_zodToKeyName: Map<unknown, string> | undefined;
|
|
211
230
|
/** @type {Set<string>} */
|
|
212
231
|
_currentScopes: Set<string>;
|
|
232
|
+
/**
|
|
233
|
+
* Tasks that declared `deps` but could not resolve them this render, so
|
|
234
|
+
* they deferred (returned null) instead of mounting. The engine reads this
|
|
235
|
+
* after each render: a deferral is normal while an upstream is still
|
|
236
|
+
* producing, but one that survives to quiescence means the dependency can
|
|
237
|
+
* never resolve (e.g. a deps/needs key that maps to a node id no task
|
|
238
|
+
* produces) and the run would otherwise finish silently without it.
|
|
239
|
+
* @type {{ nodeId: string; waitingOn: string[] }[]}
|
|
240
|
+
*/
|
|
241
|
+
_deferredDeps: {
|
|
242
|
+
nodeId: string;
|
|
243
|
+
waitingOn: string[];
|
|
244
|
+
}[];
|
|
213
245
|
/**
|
|
214
246
|
* Return the resolved absolute path for a rendered worktree or task id.
|
|
215
247
|
* The lookup is populated from task descriptors, so task node ids and
|
|
@@ -228,23 +260,65 @@ declare class SmithersCtx<Schema extends unknown = unknown> {
|
|
|
228
260
|
*/
|
|
229
261
|
resolveWorktreePath(path: string): string;
|
|
230
262
|
/**
|
|
231
|
-
* @
|
|
263
|
+
* @template {keyof Schema & string} K
|
|
264
|
+
* @overload
|
|
265
|
+
* @param {K} table
|
|
232
266
|
* @param {OutputKey} key
|
|
233
|
-
* @returns {
|
|
267
|
+
* @returns {ResolveOutputRow<Schema, K>}
|
|
234
268
|
*/
|
|
235
|
-
output<
|
|
269
|
+
output<K extends keyof Schema & string>(table: K, key: OutputKey$1): ResolveOutputRow<Schema, K>;
|
|
236
270
|
/**
|
|
237
|
-
* @
|
|
271
|
+
* @template {TableRef} T
|
|
272
|
+
* @overload
|
|
273
|
+
* @param {T} table
|
|
238
274
|
* @param {OutputKey} key
|
|
239
|
-
* @returns {
|
|
275
|
+
* @returns {ResolveOutputRow<Schema, T>}
|
|
240
276
|
*/
|
|
241
|
-
|
|
277
|
+
output<T extends TableRef>(table: T, key: OutputKey$1): ResolveOutputRow<Schema, T>;
|
|
242
278
|
/**
|
|
243
|
-
*
|
|
279
|
+
* Resolve a single output row. Without an explicit `key.iteration` this
|
|
280
|
+
* resolves the CURRENT render iteration — which equals the loop iteration
|
|
281
|
+
* only for a single, non-nested loop, and is 0 when several loops coexist.
|
|
282
|
+
* For a `<Loop>` exit condition use {@link latest} (the most recent
|
|
283
|
+
* iteration), not `outputMaybe`, or an `until` built on it never advances.
|
|
284
|
+
*
|
|
285
|
+
* @template {keyof Schema & string} K
|
|
286
|
+
* @overload
|
|
287
|
+
* @param {K} table
|
|
288
|
+
* @param {OutputKey} key
|
|
289
|
+
* @returns {ResolveOutputRow<Schema, K> | undefined}
|
|
290
|
+
*/
|
|
291
|
+
outputMaybe<K extends keyof Schema & string>(table: K, key: OutputKey$1): ResolveOutputRow<Schema, K> | undefined;
|
|
292
|
+
/**
|
|
293
|
+
* @template {TableRef} T
|
|
294
|
+
* @overload
|
|
295
|
+
* @param {T} table
|
|
296
|
+
* @param {OutputKey} key
|
|
297
|
+
* @returns {ResolveOutputRow<Schema, T> | undefined}
|
|
298
|
+
*/
|
|
299
|
+
outputMaybe<T extends TableRef>(table: T, key: OutputKey$1): ResolveOutputRow<Schema, T> | undefined;
|
|
300
|
+
/**
|
|
301
|
+
* Resolve the most recent iteration's output row for `nodeId` (highest
|
|
302
|
+
* iteration across all matching rows). This is the correct reader for a
|
|
303
|
+
* `<Loop>`/`<Ralph>` `until` exit condition; {@link outputMaybe} resolves
|
|
304
|
+
* the current render iteration and can read stale/iteration-0 data inside a
|
|
305
|
+
* loop.
|
|
306
|
+
*
|
|
307
|
+
* @template {keyof Schema & string} K
|
|
308
|
+
* @overload
|
|
309
|
+
* @param {K} table
|
|
244
310
|
* @param {string} nodeId
|
|
245
|
-
* @returns {
|
|
311
|
+
* @returns {ResolveOutputRow<Schema, K> | undefined}
|
|
312
|
+
*/
|
|
313
|
+
latest<K extends keyof Schema & string>(table: K, nodeId: string): ResolveOutputRow<Schema, K> | undefined;
|
|
314
|
+
/**
|
|
315
|
+
* @template {TableRef} T
|
|
316
|
+
* @overload
|
|
317
|
+
* @param {T} table
|
|
318
|
+
* @param {string} nodeId
|
|
319
|
+
* @returns {ResolveOutputRow<Schema, T> | undefined}
|
|
246
320
|
*/
|
|
247
|
-
latest<
|
|
321
|
+
latest<T extends TableRef>(table: T, nodeId: string): ResolveOutputRow<Schema, T> | undefined;
|
|
248
322
|
/**
|
|
249
323
|
* @param {unknown} value
|
|
250
324
|
* @param {SafeParser} schema
|
|
@@ -262,6 +336,17 @@ declare class SmithersCtx<Schema extends unknown = unknown> {
|
|
|
262
336
|
* @returns {string}
|
|
263
337
|
*/
|
|
264
338
|
resolveTableName(table: TableRef): string;
|
|
339
|
+
/**
|
|
340
|
+
* Record that a task with `deps` deferred this render because its
|
|
341
|
+
* dependencies were not resolvable. Called by the Task component before it
|
|
342
|
+
* returns null. The engine inspects these at quiescence to turn a permanent
|
|
343
|
+
* deferral (a never-satisfiable dependency) into a loud error instead of a
|
|
344
|
+
* silent skip.
|
|
345
|
+
* @param {string} nodeId
|
|
346
|
+
* @param {string[]} waitingOn
|
|
347
|
+
* @returns {void}
|
|
348
|
+
*/
|
|
349
|
+
recordDeferredDep(nodeId: string, waitingOn: string[]): void;
|
|
265
350
|
/**
|
|
266
351
|
* @param {TableRef} table
|
|
267
352
|
* @param {OutputKey} key
|
|
@@ -275,8 +360,11 @@ type SmithersCtxOptions$1 = SmithersCtxOptions$2;
|
|
|
275
360
|
type RunAuthContext$1 = RunAuthContext$2;
|
|
276
361
|
type SmithersRuntimeConfig = SmithersRuntimeConfig$1;
|
|
277
362
|
type TableRef = unknown;
|
|
278
|
-
/**
|
|
363
|
+
/**
|
|
364
|
+
* User-visible output row — harness metadata fields (runId, nodeId, iteration) are stripped.
|
|
365
|
+
*/
|
|
279
366
|
type OutputRow = Record<string, unknown>;
|
|
367
|
+
type ResolveOutputRow<Schema, T> = ResolveOutputRow$1<Schema, T>;
|
|
280
368
|
type OutputAccessor$1<Schema> = OutputAccessor$2<Schema>;
|
|
281
369
|
|
|
282
370
|
type WorkflowElement = {
|
|
@@ -355,11 +443,36 @@ declare class WorkflowDriver<Schema extends unknown = unknown> {
|
|
|
355
443
|
/** @type {RunOptions | undefined} */
|
|
356
444
|
activeOptions: RunOptions$1 | undefined;
|
|
357
445
|
/** @type {import("@smithers-orchestrator/graph").WorkflowGraph | undefined} */
|
|
358
|
-
lastGraph:
|
|
446
|
+
lastGraph: _smithers_orchestrator_graph.WorkflowGraph | undefined;
|
|
447
|
+
/** @type {{ nodeId: string; waitingOn: string[] }[]} Tasks that deferred on unresolved deps in the latest render. */
|
|
448
|
+
lastDeferredDeps: {
|
|
449
|
+
nodeId: string;
|
|
450
|
+
waitingOn: string[];
|
|
451
|
+
}[];
|
|
452
|
+
/** @type {Record<string, string>} */
|
|
453
|
+
worktreePathsById: Record<string, string>;
|
|
359
454
|
/** @type {Map<string, string>} */
|
|
360
455
|
outputTablesByNodeId: Map<string, string>;
|
|
361
456
|
/** @type {OutputSnapshot} */
|
|
362
457
|
baseOutputs: OutputSnapshot$1;
|
|
458
|
+
/** @type {Map<string, Promise<{ key: string; task: TaskDescriptor; kind: "completed" | "failed" | "cancelled"; output?: unknown; error?: unknown }>>} */
|
|
459
|
+
inflightTasks: Map<string, Promise<{
|
|
460
|
+
key: string;
|
|
461
|
+
task: TaskDescriptor;
|
|
462
|
+
kind: "completed" | "failed" | "cancelled";
|
|
463
|
+
output?: unknown;
|
|
464
|
+
error?: unknown;
|
|
465
|
+
}>>;
|
|
466
|
+
/** @type {Map<string, TaskDescriptor>} */
|
|
467
|
+
inflightTaskDescriptors: Map<string, TaskDescriptor>;
|
|
468
|
+
/** @type {Array<{ key: string; task: TaskDescriptor; kind: "completed" | "failed" | "cancelled"; output?: unknown; error?: unknown }>} */
|
|
469
|
+
settledTasks: Array<{
|
|
470
|
+
key: string;
|
|
471
|
+
task: TaskDescriptor;
|
|
472
|
+
kind: "completed" | "failed" | "cancelled";
|
|
473
|
+
output?: unknown;
|
|
474
|
+
error?: unknown;
|
|
475
|
+
}>;
|
|
363
476
|
/**
|
|
364
477
|
* @param {RunOptions} options
|
|
365
478
|
* @returns {Promise<RunResult>}
|
|
@@ -382,6 +495,38 @@ declare class WorkflowDriver<Schema extends unknown = unknown> {
|
|
|
382
495
|
*/
|
|
383
496
|
executeTasks(tasks: readonly TaskDescriptor[]): Promise<EngineDecision | RunResult$1>;
|
|
384
497
|
/**
|
|
498
|
+
* Start a task without blocking the driver loop on its completion. Settled
|
|
499
|
+
* tasks queue in `settledTasks` and are reported to the session one at a
|
|
500
|
+
* time from `nextCompletionDecision`, so each decision is computed against
|
|
501
|
+
* fresh session state and a slow task never blocks scheduling work that
|
|
502
|
+
* became ready elsewhere in the graph (#267).
|
|
503
|
+
* @param {TaskDescriptor} task
|
|
504
|
+
* @param {{ runId: string; options: RunOptions; signal?: AbortSignal }} context
|
|
505
|
+
*/
|
|
506
|
+
startInflightTask(task: TaskDescriptor, context: {
|
|
507
|
+
runId: string;
|
|
508
|
+
options: RunOptions$1;
|
|
509
|
+
signal?: AbortSignal;
|
|
510
|
+
}): void;
|
|
511
|
+
/**
|
|
512
|
+
* Wait for the next settled task (or an optional deadline) and report it to
|
|
513
|
+
* the session for a fresh decision. Completions that landed while a previous
|
|
514
|
+
* one was being processed drain from `settledTasks` first.
|
|
515
|
+
* @param {number | null} [deadlineMs]
|
|
516
|
+
* @returns {Promise<EngineDecision | RunResult>}
|
|
517
|
+
*/
|
|
518
|
+
nextCompletionDecision(deadlineMs?: number | null): Promise<EngineDecision | RunResult$1>;
|
|
519
|
+
/**
|
|
520
|
+
* Await every in-flight task without reporting further decisions. Used
|
|
521
|
+
* before run-level exits (failure, continue-as-new) so task executors are
|
|
522
|
+
* not abandoned mid-write. This matches the pre-#267 barrier semantics:
|
|
523
|
+
* failure reporting waits for in-flight siblings (bounded by their
|
|
524
|
+
* timeouts), trading latency for the invariant that no executor writes
|
|
525
|
+
* after the run is terminal. Fail-fast would need a per-run abort threaded
|
|
526
|
+
* through executors.
|
|
527
|
+
*/
|
|
528
|
+
drainInflight(): Promise<void>;
|
|
529
|
+
/**
|
|
385
530
|
* @param {WaitReason} reason
|
|
386
531
|
* @returns {Promise<EngineDecision | RunResult>}
|
|
387
532
|
*/
|
|
@@ -412,11 +557,11 @@ type SchedulerWaitHandler = SchedulerWaitHandler$1;
|
|
|
412
557
|
type WaitHandler = WaitHandler$1;
|
|
413
558
|
type ContinueAsNewHandler = ContinueAsNewHandler$1;
|
|
414
559
|
type RunOptions$1 = RunOptions$2;
|
|
415
|
-
type RunResult$1 =
|
|
416
|
-
type EngineDecision =
|
|
417
|
-
type RenderContext =
|
|
418
|
-
type WaitReason =
|
|
419
|
-
type TaskDescriptor =
|
|
560
|
+
type RunResult$1 = _smithers_orchestrator_scheduler.RunResult;
|
|
561
|
+
type EngineDecision = _smithers_orchestrator_scheduler.EngineDecision;
|
|
562
|
+
type RenderContext = _smithers_orchestrator_scheduler.RenderContext;
|
|
563
|
+
type WaitReason = _smithers_orchestrator_scheduler.WaitReason;
|
|
564
|
+
type TaskDescriptor = _smithers_orchestrator_graph_types.TaskDescriptor;
|
|
420
565
|
|
|
421
566
|
type HotReloadOptions = HotReloadOptions$1;
|
|
422
567
|
type OutputAccessor<Schema = any> = OutputAccessor$2<Schema>;
|