taskflow-core 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +248 -808
- package/dist/agents.d.ts +12 -0
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +29 -1
- package/dist/agents.js.map +1 -1
- package/dist/cwd-bridge.d.ts +61 -0
- package/dist/cwd-bridge.d.ts.map +1 -0
- package/dist/cwd-bridge.js +136 -0
- package/dist/cwd-bridge.js.map +1 -0
- package/dist/detached-runner.js +31 -5
- package/dist/detached-runner.js.map +1 -1
- package/dist/deterministic.d.ts +8 -0
- package/dist/deterministic.d.ts.map +1 -1
- package/dist/deterministic.js +23 -1
- package/dist/deterministic.js.map +1 -1
- package/dist/exec/driver.d.ts +48 -0
- package/dist/exec/driver.d.ts.map +1 -0
- package/dist/exec/driver.js +410 -0
- package/dist/exec/driver.js.map +1 -0
- package/dist/exec/events.d.ts +76 -0
- package/dist/exec/events.d.ts.map +1 -0
- package/dist/exec/events.js +108 -0
- package/dist/exec/events.js.map +1 -0
- package/dist/exec/fold.d.ts +45 -0
- package/dist/exec/fold.d.ts.map +1 -0
- package/dist/exec/fold.js +122 -0
- package/dist/exec/fold.js.map +1 -0
- package/dist/exec/index.d.ts +17 -0
- package/dist/exec/index.d.ts.map +1 -0
- package/dist/exec/index.js +17 -0
- package/dist/exec/index.js.map +1 -0
- package/dist/exec/kernel-policy.d.ts +35 -0
- package/dist/exec/kernel-policy.d.ts.map +1 -0
- package/dist/exec/kernel-policy.js +184 -0
- package/dist/exec/kernel-policy.js.map +1 -0
- package/dist/exec/step-kinds.d.ts +41 -0
- package/dist/exec/step-kinds.d.ts.map +1 -0
- package/dist/exec/step-kinds.js +599 -0
- package/dist/exec/step-kinds.js.map +1 -0
- package/dist/exec/step.d.ts +94 -0
- package/dist/exec/step.d.ts.map +1 -0
- package/dist/exec/step.js +454 -0
- package/dist/exec/step.js.map +1 -0
- package/dist/flowir/canonical-hash.d.ts +101 -0
- package/dist/flowir/canonical-hash.d.ts.map +1 -0
- package/dist/flowir/canonical-hash.js +219 -0
- package/dist/flowir/canonical-hash.js.map +1 -0
- package/dist/flowir/compile.d.ts +49 -0
- package/dist/flowir/compile.d.ts.map +1 -0
- package/dist/flowir/compile.js +232 -0
- package/dist/flowir/compile.js.map +1 -0
- package/dist/flowir/cond.d.ts +76 -0
- package/dist/flowir/cond.d.ts.map +1 -0
- package/dist/flowir/cond.js +214 -0
- package/dist/flowir/cond.js.map +1 -0
- package/dist/flowir/index.d.ts +20 -23
- package/dist/flowir/index.d.ts.map +1 -1
- package/dist/flowir/index.js +47 -35
- package/dist/flowir/index.js.map +1 -1
- package/dist/flowir/meta.d.ts +4 -4
- package/dist/flowir/meta.d.ts.map +1 -1
- package/dist/flowir/phasefp.d.ts.map +1 -1
- package/dist/flowir/phasefp.js +13 -1
- package/dist/flowir/phasefp.js.map +1 -1
- package/dist/flowir/schema.d.ts +260 -0
- package/dist/flowir/schema.d.ts.map +1 -0
- package/dist/flowir/schema.js +234 -0
- package/dist/flowir/schema.js.map +1 -0
- package/dist/flowir/translate.d.ts.map +1 -1
- package/dist/flowir/translate.js +3 -0
- package/dist/flowir/translate.js.map +1 -1
- package/dist/host/runner-types.d.ts +19 -0
- package/dist/host/runner-types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/interpolate.d.ts +4 -0
- package/dist/interpolate.d.ts.map +1 -1
- package/dist/interpolate.js +14 -0
- package/dist/interpolate.js.map +1 -1
- package/dist/rates.d.ts +121 -0
- package/dist/rates.d.ts.map +1 -0
- package/dist/rates.js +187 -0
- package/dist/rates.js.map +1 -0
- package/dist/replay.d.ts +43 -50
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +629 -19
- package/dist/replay.js.map +1 -1
- package/dist/resources/authority.d.ts +35 -0
- package/dist/resources/authority.d.ts.map +1 -0
- package/dist/resources/authority.js +67 -0
- package/dist/resources/authority.js.map +1 -0
- package/dist/resources/backend.d.ts +302 -0
- package/dist/resources/backend.d.ts.map +1 -0
- package/dist/resources/backend.js +16 -0
- package/dist/resources/backend.js.map +1 -0
- package/dist/resources/baseline.d.ts +116 -0
- package/dist/resources/baseline.d.ts.map +1 -0
- package/dist/resources/baseline.js +447 -0
- package/dist/resources/baseline.js.map +1 -0
- package/dist/resources/canonical-json.d.ts +7 -0
- package/dist/resources/canonical-json.d.ts.map +1 -0
- package/dist/resources/canonical-json.js +75 -0
- package/dist/resources/canonical-json.js.map +1 -0
- package/dist/resources/errors.d.ts +23 -0
- package/dist/resources/errors.d.ts.map +1 -0
- package/dist/resources/errors.js +89 -0
- package/dist/resources/errors.js.map +1 -0
- package/dist/resources/execution.d.ts +90 -0
- package/dist/resources/execution.d.ts.map +1 -0
- package/dist/resources/execution.js +581 -0
- package/dist/resources/execution.js.map +1 -0
- package/dist/resources/index.d.ts +15 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +15 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/journal.d.ts +138 -0
- package/dist/resources/journal.d.ts.map +1 -0
- package/dist/resources/journal.js +438 -0
- package/dist/resources/journal.js.map +1 -0
- package/dist/resources/leases.d.ts +51 -0
- package/dist/resources/leases.d.ts.map +1 -0
- package/dist/resources/leases.js +354 -0
- package/dist/resources/leases.js.map +1 -0
- package/dist/resources/permits.d.ts +52 -0
- package/dist/resources/permits.d.ts.map +1 -0
- package/dist/resources/permits.js +240 -0
- package/dist/resources/permits.js.map +1 -0
- package/dist/resources/persistence.d.ts +63 -0
- package/dist/resources/persistence.d.ts.map +1 -0
- package/dist/resources/persistence.js +522 -0
- package/dist/resources/persistence.js.map +1 -0
- package/dist/resources/registry.d.ts +56 -0
- package/dist/resources/registry.d.ts.map +1 -0
- package/dist/resources/registry.js +139 -0
- package/dist/resources/registry.js.map +1 -0
- package/dist/resources/resolve.d.ts +49 -0
- package/dist/resources/resolve.d.ts.map +1 -0
- package/dist/resources/resolve.js +309 -0
- package/dist/resources/resolve.js.map +1 -0
- package/dist/resources/sandbox.d.ts +72 -0
- package/dist/resources/sandbox.d.ts.map +1 -0
- package/dist/resources/sandbox.js +952 -0
- package/dist/resources/sandbox.js.map +1 -0
- package/dist/resources/schema.d.ts +195 -0
- package/dist/resources/schema.d.ts.map +1 -0
- package/dist/resources/schema.js +231 -0
- package/dist/resources/schema.js.map +1 -0
- package/dist/resources/types.d.ts +36 -0
- package/dist/resources/types.d.ts.map +1 -0
- package/dist/resources/types.js +75 -0
- package/dist/resources/types.js.map +1 -0
- package/dist/runner-core.d.ts +53 -0
- package/dist/runner-core.d.ts.map +1 -1
- package/dist/runner-core.js +521 -53
- package/dist/runner-core.js.map +1 -1
- package/dist/runtime/phases/approval.d.ts +23 -0
- package/dist/runtime/phases/approval.d.ts.map +1 -0
- package/dist/runtime/phases/approval.js +38 -0
- package/dist/runtime/phases/approval.js.map +1 -0
- package/dist/runtime/phases/expand.d.ts +29 -0
- package/dist/runtime/phases/expand.d.ts.map +1 -0
- package/dist/runtime/phases/expand.js +126 -0
- package/dist/runtime/phases/expand.js.map +1 -0
- package/dist/runtime/phases/parallel.d.ts +26 -0
- package/dist/runtime/phases/parallel.d.ts.map +1 -0
- package/dist/runtime/phases/parallel.js +15 -0
- package/dist/runtime/phases/parallel.js.map +1 -0
- package/dist/runtime/phases/race.d.ts +31 -0
- package/dist/runtime/phases/race.d.ts.map +1 -0
- package/dist/runtime/phases/race.js +220 -0
- package/dist/runtime/phases/race.js.map +1 -0
- package/dist/runtime/phases/script.d.ts +38 -0
- package/dist/runtime/phases/script.d.ts.map +1 -0
- package/dist/runtime/phases/script.js +177 -0
- package/dist/runtime/phases/script.js.map +1 -0
- package/dist/runtime.d.ts +68 -16
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1091 -337
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +104 -9
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +249 -32
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +13 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js.map +1 -1
- package/dist/trace.d.ts +7 -1
- package/dist/trace.d.ts.map +1 -1
- package/dist/trace.js +33 -26
- package/dist/trace.js.map +1 -1
- package/package.json +7 -5
package/dist/schema.d.ts
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* to a subagent (an isolated `pi` process). Phases form a DAG via `dependsOn`.
|
|
6
6
|
*/
|
|
7
7
|
import { Type, type Static } from "typebox";
|
|
8
|
+
/** Closed set of native phase kinds — single source of truth for DSL + FlowIR. */
|
|
9
|
+
export declare const PHASE_TYPES: readonly ["agent", "parallel", "map", "gate", "reduce", "approval", "flow", "loop", "tournament", "script",
|
|
10
|
+
/** First completed branch wins (Horizon B). */
|
|
11
|
+
"race",
|
|
12
|
+
/** Dynamic sub-DAG: nested sub-flow or graft-promote into parent (Horizon B). */
|
|
13
|
+
"expand"];
|
|
14
|
+
export type PhaseType = (typeof PHASE_TYPES)[number];
|
|
8
15
|
/** Loop iteration bounds. Authors may lower the max; the hard cap is a runaway guard. */
|
|
9
16
|
export declare const LOOP_DEFAULT_MAX_ITERATIONS = 10;
|
|
10
17
|
export declare const LOOP_HARD_MAX_ITERATIONS = 100;
|
|
@@ -25,6 +32,9 @@ export declare const TOURNAMENT_HARD_MAX_VARIANTS = 20;
|
|
|
25
32
|
declare const TOURNAMENT_MODES: readonly ["best", "aggregate"];
|
|
26
33
|
/** @internal */
|
|
27
34
|
export type TournamentMode = (typeof TOURNAMENT_MODES)[number];
|
|
35
|
+
/** Cross-host thinking levels and compatibility aliases accepted by authored flows. */
|
|
36
|
+
export declare const THINKING_LEVELS: readonly ["off", "none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
|
|
37
|
+
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
28
38
|
declare const CACHE_SCOPES: readonly ["run-only", "cross-run", "off"];
|
|
29
39
|
export type CacheScope = (typeof CACHE_SCOPES)[number];
|
|
30
40
|
declare const ParallelTaskSchema: Type.TObject<{
|
|
@@ -54,7 +64,7 @@ declare const BudgetSchema: Type.TObject<{
|
|
|
54
64
|
}>;
|
|
55
65
|
declare const PhaseSchema: Type.TObject<{
|
|
56
66
|
id: Type.TString;
|
|
57
|
-
type: Type.TOptional<Type.TUnsafe<"agent" | "approval" | "flow" | "gate" | "loop" | "map" | "parallel" | "reduce" | "script" | "tournament">>;
|
|
67
|
+
type: Type.TOptional<Type.TUnsafe<"agent" | "approval" | "expand" | "flow" | "gate" | "loop" | "map" | "parallel" | "race" | "reduce" | "script" | "tournament">>;
|
|
58
68
|
agent: Type.TOptional<Type.TString>;
|
|
59
69
|
task: Type.TOptional<Type.TString>;
|
|
60
70
|
over: Type.TOptional<Type.TString>;
|
|
@@ -63,9 +73,20 @@ declare const PhaseSchema: Type.TObject<{
|
|
|
63
73
|
task: Type.TString;
|
|
64
74
|
agent: Type.TOptional<Type.TString>;
|
|
65
75
|
}>>>;
|
|
76
|
+
/**
|
|
77
|
+
* [race] When true (default), abort in-flight loser branches after the first branch
|
|
78
|
+
* **succeeds** (best-effort `AbortSignal`). Race semantics are first-**success**
|
|
79
|
+
* (failed settles do not win). Set `false` to let losers run to natural completion.
|
|
80
|
+
*/
|
|
81
|
+
cancelLosers: Type.TOptional<Type.TBoolean>;
|
|
66
82
|
from: Type.TOptional<Type.TArray<Type.TString>>;
|
|
67
83
|
use: Type.TOptional<Type.TString>;
|
|
84
|
+
/** [expand] Fragment source — string interpolation or inline Taskflow / phases (same as flow.def). */
|
|
68
85
|
def: Type.TOptional<Type.TUnknown>;
|
|
86
|
+
/** [expand] nested (default) = isolated sub-flow; graft = run fragment then promote phase states onto the parent under prefixed ids. */
|
|
87
|
+
expandMode: Type.TOptional<Type.TUnsafe<"graft" | "nested">>;
|
|
88
|
+
/** [expand] Max nodes accepted from a dynamic fragment (default 50, hard 100). */
|
|
89
|
+
maxNodes: Type.TOptional<Type.TNumber>;
|
|
69
90
|
with: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
|
|
70
91
|
run: Type.TOptional<Type.TUnion<[Type.TString, Type.TArray<Type.TString>]>>;
|
|
71
92
|
input: Type.TOptional<Type.TString>;
|
|
@@ -89,7 +110,7 @@ declare const PhaseSchema: Type.TObject<{
|
|
|
89
110
|
output: Type.TOptional<Type.TUnsafe<"json" | "text">>;
|
|
90
111
|
expect: Type.TOptional<Type.TUnknown>;
|
|
91
112
|
model: Type.TOptional<Type.TString>;
|
|
92
|
-
thinking: Type.TOptional<Type.
|
|
113
|
+
thinking: Type.TOptional<Type.TUnsafe<"high" | "low" | "max" | "medium" | "minimal" | "none" | "off" | "ultra" | "xhigh">>;
|
|
93
114
|
tools: Type.TOptional<Type.TArray<Type.TString>>;
|
|
94
115
|
cwd: Type.TOptional<Type.TString>;
|
|
95
116
|
final: Type.TOptional<Type.TBoolean>;
|
|
@@ -108,20 +129,78 @@ declare const PhaseSchema: Type.TObject<{
|
|
|
108
129
|
}>>;
|
|
109
130
|
shareContext: Type.TOptional<Type.TBoolean>;
|
|
110
131
|
}>;
|
|
111
|
-
declare const ArgSpecSchema: Type.TObject<{
|
|
132
|
+
declare const ArgSpecSchema: Type.TUnion<[Type.TObject<{
|
|
133
|
+
type: Type.TOptional<Type.TNever>;
|
|
112
134
|
default: Type.TOptional<Type.TUnknown>;
|
|
113
135
|
description: Type.TOptional<Type.TString>;
|
|
114
136
|
required: Type.TOptional<Type.TBoolean>;
|
|
115
|
-
}
|
|
137
|
+
}>, Type.TObject<{
|
|
138
|
+
type: Type.TLiteral<"string">;
|
|
139
|
+
default: Type.TOptional<Type.TString>;
|
|
140
|
+
description: Type.TOptional<Type.TString>;
|
|
141
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
142
|
+
}>, Type.TObject<{
|
|
143
|
+
type: Type.TLiteral<"relative-path">;
|
|
144
|
+
default: Type.TOptional<Type.TString>;
|
|
145
|
+
description: Type.TOptional<Type.TString>;
|
|
146
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
147
|
+
}>, Type.TObject<{
|
|
148
|
+
type: Type.TLiteral<"number">;
|
|
149
|
+
default: Type.TOptional<Type.TNumber>;
|
|
150
|
+
minimum: Type.TOptional<Type.TNumber>;
|
|
151
|
+
maximum: Type.TOptional<Type.TNumber>;
|
|
152
|
+
description: Type.TOptional<Type.TString>;
|
|
153
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
154
|
+
}>, Type.TObject<{
|
|
155
|
+
type: Type.TLiteral<"boolean">;
|
|
156
|
+
default: Type.TOptional<Type.TBoolean>;
|
|
157
|
+
description: Type.TOptional<Type.TString>;
|
|
158
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
159
|
+
}>, Type.TObject<{
|
|
160
|
+
type: Type.TLiteral<"enum">;
|
|
161
|
+
values: Type.TArray<Type.TUnion<[Type.TString, Type.TNumber]>>;
|
|
162
|
+
default: Type.TOptional<Type.TUnion<[Type.TString, Type.TNumber]>>;
|
|
163
|
+
description: Type.TOptional<Type.TString>;
|
|
164
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
165
|
+
}>]>;
|
|
116
166
|
export declare const TaskflowSchema: Type.TObject<{
|
|
117
167
|
name: Type.TString;
|
|
118
168
|
description: Type.TOptional<Type.TString>;
|
|
119
169
|
version: Type.TOptional<Type.TNumber>;
|
|
120
|
-
args: Type.TOptional<Type.TRecord<"^.*$", Type.TObject<{
|
|
170
|
+
args: Type.TOptional<Type.TRecord<"^.*$", Type.TUnion<[Type.TObject<{
|
|
171
|
+
type: Type.TOptional<Type.TNever>;
|
|
121
172
|
default: Type.TOptional<Type.TUnknown>;
|
|
122
173
|
description: Type.TOptional<Type.TString>;
|
|
123
174
|
required: Type.TOptional<Type.TBoolean>;
|
|
124
|
-
}
|
|
175
|
+
}>, Type.TObject<{
|
|
176
|
+
type: Type.TLiteral<"string">;
|
|
177
|
+
default: Type.TOptional<Type.TString>;
|
|
178
|
+
description: Type.TOptional<Type.TString>;
|
|
179
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
180
|
+
}>, Type.TObject<{
|
|
181
|
+
type: Type.TLiteral<"relative-path">;
|
|
182
|
+
default: Type.TOptional<Type.TString>;
|
|
183
|
+
description: Type.TOptional<Type.TString>;
|
|
184
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
185
|
+
}>, Type.TObject<{
|
|
186
|
+
type: Type.TLiteral<"number">;
|
|
187
|
+
default: Type.TOptional<Type.TNumber>;
|
|
188
|
+
minimum: Type.TOptional<Type.TNumber>;
|
|
189
|
+
maximum: Type.TOptional<Type.TNumber>;
|
|
190
|
+
description: Type.TOptional<Type.TString>;
|
|
191
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
192
|
+
}>, Type.TObject<{
|
|
193
|
+
type: Type.TLiteral<"boolean">;
|
|
194
|
+
default: Type.TOptional<Type.TBoolean>;
|
|
195
|
+
description: Type.TOptional<Type.TString>;
|
|
196
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
197
|
+
}>, Type.TObject<{
|
|
198
|
+
type: Type.TLiteral<"enum">;
|
|
199
|
+
values: Type.TArray<Type.TUnion<[Type.TString, Type.TNumber]>>;
|
|
200
|
+
default: Type.TOptional<Type.TUnion<[Type.TString, Type.TNumber]>>;
|
|
201
|
+
description: Type.TOptional<Type.TString>;
|
|
202
|
+
required: Type.TOptional<Type.TBoolean>;
|
|
203
|
+
}>]>>>;
|
|
125
204
|
concurrency: Type.TOptional<Type.TNumber>;
|
|
126
205
|
budget: Type.TOptional<Type.TObject<{
|
|
127
206
|
maxUSD: Type.TOptional<Type.TNumber>;
|
|
@@ -133,7 +212,7 @@ export declare const TaskflowSchema: Type.TObject<{
|
|
|
133
212
|
incremental: Type.TOptional<Type.TBoolean>;
|
|
134
213
|
phases: Type.TArray<Type.TObject<{
|
|
135
214
|
id: Type.TString;
|
|
136
|
-
type: Type.TOptional<Type.TUnsafe<"agent" | "approval" | "flow" | "gate" | "loop" | "map" | "parallel" | "reduce" | "script" | "tournament">>;
|
|
215
|
+
type: Type.TOptional<Type.TUnsafe<"agent" | "approval" | "expand" | "flow" | "gate" | "loop" | "map" | "parallel" | "race" | "reduce" | "script" | "tournament">>;
|
|
137
216
|
agent: Type.TOptional<Type.TString>;
|
|
138
217
|
task: Type.TOptional<Type.TString>;
|
|
139
218
|
over: Type.TOptional<Type.TString>;
|
|
@@ -142,9 +221,20 @@ export declare const TaskflowSchema: Type.TObject<{
|
|
|
142
221
|
task: Type.TString;
|
|
143
222
|
agent: Type.TOptional<Type.TString>;
|
|
144
223
|
}>>>;
|
|
224
|
+
/**
|
|
225
|
+
* [race] When true (default), abort in-flight loser branches after the first branch
|
|
226
|
+
* **succeeds** (best-effort `AbortSignal`). Race semantics are first-**success**
|
|
227
|
+
* (failed settles do not win). Set `false` to let losers run to natural completion.
|
|
228
|
+
*/
|
|
229
|
+
cancelLosers: Type.TOptional<Type.TBoolean>;
|
|
145
230
|
from: Type.TOptional<Type.TArray<Type.TString>>;
|
|
146
231
|
use: Type.TOptional<Type.TString>;
|
|
232
|
+
/** [expand] Fragment source — string interpolation or inline Taskflow / phases (same as flow.def). */
|
|
147
233
|
def: Type.TOptional<Type.TUnknown>;
|
|
234
|
+
/** [expand] nested (default) = isolated sub-flow; graft = run fragment then promote phase states onto the parent under prefixed ids. */
|
|
235
|
+
expandMode: Type.TOptional<Type.TUnsafe<"graft" | "nested">>;
|
|
236
|
+
/** [expand] Max nodes accepted from a dynamic fragment (default 50, hard 100). */
|
|
237
|
+
maxNodes: Type.TOptional<Type.TNumber>;
|
|
148
238
|
with: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
|
|
149
239
|
run: Type.TOptional<Type.TUnion<[Type.TString, Type.TArray<Type.TString>]>>;
|
|
150
240
|
input: Type.TOptional<Type.TString>;
|
|
@@ -168,7 +258,7 @@ export declare const TaskflowSchema: Type.TObject<{
|
|
|
168
258
|
output: Type.TOptional<Type.TUnsafe<"json" | "text">>;
|
|
169
259
|
expect: Type.TOptional<Type.TUnknown>;
|
|
170
260
|
model: Type.TOptional<Type.TString>;
|
|
171
|
-
thinking: Type.TOptional<Type.
|
|
261
|
+
thinking: Type.TOptional<Type.TUnsafe<"high" | "low" | "max" | "medium" | "minimal" | "none" | "off" | "ultra" | "xhigh">>;
|
|
172
262
|
tools: Type.TOptional<Type.TArray<Type.TString>>;
|
|
173
263
|
cwd: Type.TOptional<Type.TString>;
|
|
174
264
|
final: Type.TOptional<Type.TBoolean>;
|
|
@@ -234,9 +324,14 @@ export interface ValidationOptions {
|
|
|
234
324
|
strict?: boolean;
|
|
235
325
|
/** When true, this flow is a runtime-generated (`flow { def }`) sub-flow whose
|
|
236
326
|
* content is LLM-authored / untrusted. Enables hardening checks: breadth caps
|
|
237
|
-
* (phase count, map items, concurrency) and
|
|
327
|
+
* (phase count, map items, concurrency) and denial of cwd/context/script
|
|
328
|
+
* resource capabilities until a FileBroker/sandbox exists. */
|
|
238
329
|
dynamic?: boolean;
|
|
239
330
|
}
|
|
331
|
+
/** Validate resolved invocation values independently of full flow structure.
|
|
332
|
+
* Runtime calls this at the Core boundary so direct, resume, and detached
|
|
333
|
+
* execution cannot bypass adapter-level typed-arg checks. */
|
|
334
|
+
export declare function validateInvocationArgs(def: Pick<Taskflow, "args">, args: Record<string, unknown>): string[];
|
|
240
335
|
export declare function validateTaskflow(def: unknown, opts?: ValidationOptions): ValidationResult;
|
|
241
336
|
export declare function collectRefs(phase: Phase): {
|
|
242
337
|
steps: string[];
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
CHANGED
|
@@ -9,11 +9,28 @@ import { StringEnum } from "./typebox-helpers.js";
|
|
|
9
9
|
import { contractShapeErrors } from "./contract.js";
|
|
10
10
|
import { scorerShapeErrors } from "./scorers.js";
|
|
11
11
|
import { Type } from "typebox";
|
|
12
|
-
import {
|
|
12
|
+
import { Errors as SchemaErrors } from "typebox/value";
|
|
13
|
+
import { cwdArgName, hasCwdPlaceholder, normalizeRelativePath } from "./cwd-bridge.js";
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
// Phase types
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
+
/** Closed set of native phase kinds — single source of truth for DSL + FlowIR. */
|
|
18
|
+
export const PHASE_TYPES = [
|
|
19
|
+
"agent",
|
|
20
|
+
"parallel",
|
|
21
|
+
"map",
|
|
22
|
+
"gate",
|
|
23
|
+
"reduce",
|
|
24
|
+
"approval",
|
|
25
|
+
"flow",
|
|
26
|
+
"loop",
|
|
27
|
+
"tournament",
|
|
28
|
+
"script",
|
|
29
|
+
/** First completed branch wins (Horizon B). */
|
|
30
|
+
"race",
|
|
31
|
+
/** Dynamic sub-DAG: nested sub-flow or graft-promote into parent (Horizon B). */
|
|
32
|
+
"expand",
|
|
33
|
+
];
|
|
17
34
|
/** Loop iteration bounds. Authors may lower the max; the hard cap is a runaway guard. */
|
|
18
35
|
export const LOOP_DEFAULT_MAX_ITERATIONS = 10;
|
|
19
36
|
export const LOOP_HARD_MAX_ITERATIONS = 100;
|
|
@@ -34,13 +51,13 @@ export const TOURNAMENT_HARD_MAX_VARIANTS = 20;
|
|
|
34
51
|
const TOURNAMENT_MODES = ["best", "aggregate"];
|
|
35
52
|
const OUTPUT_FORMATS = ["text", "json"];
|
|
36
53
|
const JOIN_MODES = ["all", "any"];
|
|
54
|
+
/** Cross-host thinking levels and compatibility aliases accepted by authored flows. */
|
|
55
|
+
export const THINKING_LEVELS = ["off", "none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
|
|
37
56
|
const CACHE_SCOPES = ["run-only", "cross-run", "off"];
|
|
38
57
|
/** Allowed fingerprint entry prefixes. `glob!:` = content-hash variant of `glob:`. */
|
|
39
58
|
const CACHE_FINGERPRINT_PREFIXES = ["git:", "glob:", "glob!:", "file:", "env:"];
|
|
40
59
|
/** Phase types that must NOT be cached across runs (a fresh result is required each run). */
|
|
41
|
-
const CACHE_CROSS_RUN_BLOCKED_TYPES = ["gate", "approval", "loop", "tournament", "script"];
|
|
42
|
-
/** `cwd` is a literal path / workspace keyword, not an interpolated field. */
|
|
43
|
-
const CWD_PLACEHOLDER_RE = /\{[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*\}/;
|
|
60
|
+
const CACHE_CROSS_RUN_BLOCKED_TYPES = ["gate", "approval", "loop", "tournament", "script", "race", "expand"];
|
|
44
61
|
const ParallelTaskSchema = Type.Object({
|
|
45
62
|
task: Type.String({ description: "Task for this parallel branch (supports interpolation)" }),
|
|
46
63
|
agent: Type.Optional(Type.String({ description: "Override the phase agent for this branch" })),
|
|
@@ -70,9 +87,9 @@ const CacheSchema = Type.Object({
|
|
|
70
87
|
}, { additionalProperties: false });
|
|
71
88
|
/** Run-wide cost / token ceiling. Exceeding it halts the run (remaining phases skipped). */
|
|
72
89
|
const BudgetSchema = Type.Object({
|
|
73
|
-
maxUSD: Type.Optional(Type.Number({ description: "Halt the run once accumulated cost exceeds this many USD" })),
|
|
74
|
-
maxTokens: Type.Optional(Type.Number({ description: "Halt the run once accumulated input+output tokens exceed this" })),
|
|
75
|
-
}, { additionalProperties: false });
|
|
90
|
+
maxUSD: Type.Optional(Type.Number({ minimum: 0, description: "Halt the run once accumulated cost exceeds this many USD" })),
|
|
91
|
+
maxTokens: Type.Optional(Type.Number({ minimum: 0, description: "Halt the run once accumulated input+output tokens exceed this" })),
|
|
92
|
+
}, { additionalProperties: false, minProperties: 1 });
|
|
76
93
|
const PhaseSchema = Type.Object({
|
|
77
94
|
id: Type.String({ description: "Unique phase identifier (referenced via {steps.<id>.output})" }),
|
|
78
95
|
type: Type.Optional(StringEnum(PHASE_TYPES, { description: "Phase kind", default: "agent" })),
|
|
@@ -81,15 +98,32 @@ const PhaseSchema = Type.Object({
|
|
|
81
98
|
// map fan-out
|
|
82
99
|
over: Type.Optional(Type.String({ description: "[map] Interpolation ref resolving to an array to fan out over" })),
|
|
83
100
|
as: Type.Optional(Type.String({ description: "[map] Loop variable name (default: item)", default: "item" })),
|
|
84
|
-
// parallel static branches
|
|
85
|
-
branches: Type.Optional(Type.Array(ParallelTaskSchema, {
|
|
101
|
+
// parallel / race static branches
|
|
102
|
+
branches: Type.Optional(Type.Array(ParallelTaskSchema, {
|
|
103
|
+
description: "[parallel|race|tournament] Static task branches",
|
|
104
|
+
})),
|
|
105
|
+
/**
|
|
106
|
+
* [race] When true (default), abort in-flight loser branches after the first branch
|
|
107
|
+
* **succeeds** (best-effort `AbortSignal`). Race semantics are first-**success**
|
|
108
|
+
* (failed settles do not win). Set `false` to let losers run to natural completion.
|
|
109
|
+
*/
|
|
110
|
+
cancelLosers: Type.Optional(Type.Boolean({ default: true })),
|
|
86
111
|
// reduce
|
|
87
112
|
from: Type.Optional(Type.Array(Type.String(), { description: "[reduce] Phase ids whose outputs are aggregated" })),
|
|
88
|
-
// sub-workflow (flow)
|
|
113
|
+
// sub-workflow (flow) + expand fragment
|
|
89
114
|
use: Type.Optional(Type.String({ description: "[flow] Name of a saved taskflow to run as this phase" })),
|
|
115
|
+
/** [expand] Fragment source — string interpolation or inline Taskflow / phases (same as flow.def). */
|
|
116
|
+
// reuses `def` below for expand as well
|
|
90
117
|
def: Type.Optional(Type.Unknown({
|
|
91
|
-
description: "[flow] Inline sub-flow definition, resolved at runtime. Mutually exclusive with 'use'. A string is interpolated (e.g. '{steps.plan.json}') then JSON-parsed; an object is used directly. The result must be a Taskflow ({name,phases}) or a bare phases array / {phases:[...]} (auto-wrapped). Validated + verified before execution; on any failure the phase fails-open (defError) without aborting the run.",
|
|
118
|
+
description: "[flow|expand] Inline sub-flow / fragment definition, resolved at runtime. Mutually exclusive with 'use' on flow. A string is interpolated (e.g. '{steps.plan.json}') then JSON-parsed; an object is used directly. The result must be a Taskflow ({name,phases}) or a bare phases array / {phases:[...]} (auto-wrapped). Validated + verified before execution; on any failure the phase fails-open (defError) without aborting the run.",
|
|
92
119
|
})),
|
|
120
|
+
/** [expand] nested (default) = isolated sub-flow; graft = run fragment then promote phase states onto the parent under prefixed ids. */
|
|
121
|
+
expandMode: Type.Optional(StringEnum(["nested", "graft"], {
|
|
122
|
+
description: "[expand] nested (default) | graft (promote child phase states onto parent)",
|
|
123
|
+
default: "nested",
|
|
124
|
+
})),
|
|
125
|
+
/** [expand] Max nodes accepted from a dynamic fragment (default 50, hard 100). */
|
|
126
|
+
maxNodes: Type.Optional(Type.Number({ default: 50 })),
|
|
93
127
|
with: Type.Optional(Type.Record(Type.String(), Type.Unknown(), {
|
|
94
128
|
description: "[flow] Args passed to the sub-flow (string values support interpolation)",
|
|
95
129
|
})),
|
|
@@ -146,9 +180,11 @@ const PhaseSchema = Type.Object({
|
|
|
146
180
|
description: "Output contract for this phase's JSON output (requires output:'json'). A small JSON-Schema-like shape: {type, properties, required, items, enum}. Validated the moment the subagent finishes; a violation fails the phase with a precise diagnostic (eligible for the phase's explicit 'retry'). Valid for agent/gate/reduce/loop phases.",
|
|
147
181
|
})),
|
|
148
182
|
model: Type.Optional(Type.String({ description: "Model override for this phase" })),
|
|
149
|
-
thinking: Type.Optional(
|
|
183
|
+
thinking: Type.Optional(StringEnum(THINKING_LEVELS, {
|
|
184
|
+
description: "Thinking level override for this phase. Unknown values are rejected instead of silently inheriting the host default.",
|
|
185
|
+
})),
|
|
150
186
|
tools: Type.Optional(Type.Array(Type.String(), { description: "Restrict tools for this phase's agent" })),
|
|
151
|
-
cwd: Type.Optional(Type.String({ description: "Working directory for this phase
|
|
187
|
+
cwd: Type.Optional(Type.String({ description: "Working directory for this phase. Accepts a literal path; a reserved keyword ('temp', 'dedicated', 'worktree'); or the exact whole placeholder {args.X} when X is declared type:'relative-path'. The 0.2.1 argument bridge requires an explicit host resolve-only opt-in." })),
|
|
152
188
|
final: Type.Optional(Type.Boolean({ description: "Mark this phase's output as the workflow result" })),
|
|
153
189
|
optional: Type.Optional(Type.Boolean({ description: "If true, a failure does not abort the run", default: false })),
|
|
154
190
|
idempotent: Type.Optional(Type.Boolean({
|
|
@@ -178,11 +214,53 @@ const PhaseSchema = Type.Object({
|
|
|
178
214
|
description: "Opt into the Shared Context Tree for this phase: the subagent gets ctx_read/ctx_write (a blackboard shared with siblings/ancestors, to avoid re-reading files) and ctx_report/ctx_spawn (report upward + queue child tasks the runtime picks up). Default false — existing flows are unaffected.",
|
|
179
215
|
})),
|
|
180
216
|
}, { additionalProperties: false });
|
|
181
|
-
const
|
|
217
|
+
const LegacyArgSpecSchema = Type.Object({
|
|
218
|
+
type: Type.Optional(Type.Never()),
|
|
182
219
|
default: Type.Optional(Type.Unknown()),
|
|
183
220
|
description: Type.Optional(Type.String()),
|
|
184
221
|
required: Type.Optional(Type.Boolean()),
|
|
185
222
|
}, { additionalProperties: false });
|
|
223
|
+
const StringArgSpecSchema = Type.Object({
|
|
224
|
+
type: Type.Literal("string"),
|
|
225
|
+
default: Type.Optional(Type.String()),
|
|
226
|
+
description: Type.Optional(Type.String()),
|
|
227
|
+
required: Type.Optional(Type.Boolean()),
|
|
228
|
+
}, { additionalProperties: false });
|
|
229
|
+
const RelativePathArgSpecSchema = Type.Object({
|
|
230
|
+
type: Type.Literal("relative-path"),
|
|
231
|
+
default: Type.Optional(Type.String()),
|
|
232
|
+
description: Type.Optional(Type.String()),
|
|
233
|
+
required: Type.Optional(Type.Boolean()),
|
|
234
|
+
}, { additionalProperties: false });
|
|
235
|
+
const NumberArgSpecSchema = Type.Object({
|
|
236
|
+
type: Type.Literal("number"),
|
|
237
|
+
default: Type.Optional(Type.Number()),
|
|
238
|
+
minimum: Type.Optional(Type.Number()),
|
|
239
|
+
maximum: Type.Optional(Type.Number()),
|
|
240
|
+
description: Type.Optional(Type.String()),
|
|
241
|
+
required: Type.Optional(Type.Boolean()),
|
|
242
|
+
}, { additionalProperties: false });
|
|
243
|
+
const BooleanArgSpecSchema = Type.Object({
|
|
244
|
+
type: Type.Literal("boolean"),
|
|
245
|
+
default: Type.Optional(Type.Boolean()),
|
|
246
|
+
description: Type.Optional(Type.String()),
|
|
247
|
+
required: Type.Optional(Type.Boolean()),
|
|
248
|
+
}, { additionalProperties: false });
|
|
249
|
+
const EnumArgSpecSchema = Type.Object({
|
|
250
|
+
type: Type.Literal("enum"),
|
|
251
|
+
values: Type.Array(Type.Union([Type.String(), Type.Number()]), { minItems: 1 }),
|
|
252
|
+
default: Type.Optional(Type.Union([Type.String(), Type.Number()])),
|
|
253
|
+
description: Type.Optional(Type.String()),
|
|
254
|
+
required: Type.Optional(Type.Boolean()),
|
|
255
|
+
}, { additionalProperties: false });
|
|
256
|
+
const ArgSpecSchema = Type.Union([
|
|
257
|
+
LegacyArgSpecSchema,
|
|
258
|
+
StringArgSpecSchema,
|
|
259
|
+
RelativePathArgSpecSchema,
|
|
260
|
+
NumberArgSpecSchema,
|
|
261
|
+
BooleanArgSpecSchema,
|
|
262
|
+
EnumArgSpecSchema,
|
|
263
|
+
]);
|
|
186
264
|
export const TaskflowSchema = Type.Object({
|
|
187
265
|
name: Type.String({ minLength: 1, description: "Workflow name (becomes /tf:<name> command when saved)" }),
|
|
188
266
|
description: Type.Optional(Type.String()),
|
|
@@ -332,6 +410,58 @@ export function parseTtlMs(ttl) {
|
|
|
332
410
|
const mult = { ms: 1, s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
333
411
|
return n * mult[unit];
|
|
334
412
|
}
|
|
413
|
+
function typedArgValueErrors(name, spec, value, source) {
|
|
414
|
+
const prefix = `Argument '${name}' ${source}`;
|
|
415
|
+
if (spec.type === "relative-path") {
|
|
416
|
+
const r = normalizeRelativePath(value);
|
|
417
|
+
return r.ok ? [] : [`${prefix} ${r.message}`];
|
|
418
|
+
}
|
|
419
|
+
if (spec.type === "string") {
|
|
420
|
+
if (typeof value !== "string")
|
|
421
|
+
return [`${prefix} must be a string, got ${value === null ? "null" : typeof value}`];
|
|
422
|
+
return [];
|
|
423
|
+
}
|
|
424
|
+
if (spec.type === "number") {
|
|
425
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
426
|
+
return [`${prefix} must be a finite number`];
|
|
427
|
+
if (spec.minimum !== undefined && value < spec.minimum)
|
|
428
|
+
return [`${prefix} must be >= ${spec.minimum}`];
|
|
429
|
+
if (spec.maximum !== undefined && value > spec.maximum)
|
|
430
|
+
return [`${prefix} must be <= ${spec.maximum}`];
|
|
431
|
+
return [];
|
|
432
|
+
}
|
|
433
|
+
if (spec.type === "boolean") {
|
|
434
|
+
return typeof value === "boolean" ? [] : [`${prefix} must be a boolean, got ${value === null ? "null" : typeof value}`];
|
|
435
|
+
}
|
|
436
|
+
if (spec.type === "enum") {
|
|
437
|
+
const values = Array.isArray(spec.values) ? spec.values : [];
|
|
438
|
+
return values.some((v) => Object.is(v, value)) ? [] : [`${prefix} must be one of ${JSON.stringify(values)}`];
|
|
439
|
+
}
|
|
440
|
+
return [];
|
|
441
|
+
}
|
|
442
|
+
/** Validate resolved invocation values independently of full flow structure.
|
|
443
|
+
* Runtime calls this at the Core boundary so direct, resume, and detached
|
|
444
|
+
* execution cannot bypass adapter-level typed-arg checks. */
|
|
445
|
+
export function validateInvocationArgs(def, args) {
|
|
446
|
+
const errors = [];
|
|
447
|
+
const specs = (def.args ?? {});
|
|
448
|
+
for (const [name, spec] of Object.entries(specs)) {
|
|
449
|
+
// Full structural validation owns malformed specs. Keep this boundary
|
|
450
|
+
// total so validateTaskflow() reports SchemaErrors instead of throwing
|
|
451
|
+
// while it performs the optional invocation-value pass.
|
|
452
|
+
if (typeof spec !== "object" || spec === null || Array.isArray(spec))
|
|
453
|
+
continue;
|
|
454
|
+
const typedSpec = spec;
|
|
455
|
+
if (!(name in args)) {
|
|
456
|
+
if (typedSpec.type !== undefined && typedSpec.required === true && typedSpec.default === undefined)
|
|
457
|
+
errors.push(`Missing required argument '${name}'`);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (typedSpec.type !== undefined)
|
|
461
|
+
errors.push(...typedArgValueErrors(name, typedSpec, args[name], "invocation"));
|
|
462
|
+
}
|
|
463
|
+
return errors;
|
|
464
|
+
}
|
|
335
465
|
export function validateTaskflow(def, opts = {}) {
|
|
336
466
|
const errors = [];
|
|
337
467
|
const warnings = [];
|
|
@@ -340,12 +470,60 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
340
470
|
}
|
|
341
471
|
const flow = def;
|
|
342
472
|
const strict = opts.strict ?? flow.strictInterpolation === true;
|
|
473
|
+
for (const issue of SchemaErrors(TaskflowSchema, def)) {
|
|
474
|
+
const pathLabel = issue.instancePath || "/";
|
|
475
|
+
const extras = issue.params && "additionalProperties" in issue.params
|
|
476
|
+
? issue.params.additionalProperties
|
|
477
|
+
: undefined;
|
|
478
|
+
if (Array.isArray(extras) && extras.length > 0) {
|
|
479
|
+
for (const key of extras)
|
|
480
|
+
errors.push(`${pathLabel}: unknown field '${String(key)}'`);
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
errors.push(`${pathLabel}: ${issue.message}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
343
486
|
if (!flow.name || typeof flow.name !== "string")
|
|
344
487
|
errors.push("Missing or invalid 'name'");
|
|
345
488
|
if (!Array.isArray(flow.phases) || flow.phases.length === 0) {
|
|
346
489
|
errors.push("Taskflow must have at least one phase");
|
|
347
490
|
return { ok: false, errors, warnings };
|
|
348
491
|
}
|
|
492
|
+
// Typed invocation args are the trust boundary for resource selectors. Legacy
|
|
493
|
+
// specs remain valid for every flow version, but can never feed a cwd bridge.
|
|
494
|
+
// `version` is informational metadata, not a schema-version selector.
|
|
495
|
+
const argSpecs = (typeof flow.args === "object" && flow.args !== null && !Array.isArray(flow.args)
|
|
496
|
+
? flow.args
|
|
497
|
+
: {});
|
|
498
|
+
for (const [name, spec] of Object.entries(argSpecs)) {
|
|
499
|
+
if (!spec || typeof spec !== "object" || Array.isArray(spec))
|
|
500
|
+
continue;
|
|
501
|
+
if (spec.type === "number" && spec.minimum !== undefined && spec.maximum !== undefined && spec.minimum > spec.maximum) {
|
|
502
|
+
errors.push(`Argument '${name}': minimum must be <= maximum`);
|
|
503
|
+
}
|
|
504
|
+
if (spec.type === "enum" && Array.isArray(spec.values) && new Set(spec.values.map((v) => `${typeof v}:${String(v)}`)).size !== spec.values.length) {
|
|
505
|
+
errors.push(`Argument '${name}': enum values must be unique`);
|
|
506
|
+
}
|
|
507
|
+
if (spec.default !== undefined && spec.type !== undefined) {
|
|
508
|
+
errors.push(...typedArgValueErrors(name, spec, spec.default, "default"));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (opts.args !== undefined) {
|
|
512
|
+
errors.push(...validateInvocationArgs(flow, opts.args));
|
|
513
|
+
for (const [name, spec] of Object.entries(argSpecs)) {
|
|
514
|
+
if (!spec || typeof spec !== "object" || Array.isArray(spec))
|
|
515
|
+
continue;
|
|
516
|
+
if (spec.type === undefined && spec.required === true && spec.default === undefined && !(name in opts.args)) {
|
|
517
|
+
warnings.push(`Legacy untyped argument '${name}' is marked required but remains advisory; add an explicit type to enforce it`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
for (const name of Object.keys(opts.args)) {
|
|
521
|
+
if (name in argSpecs)
|
|
522
|
+
continue;
|
|
523
|
+
if (strict)
|
|
524
|
+
warnings.push(`Invocation argument '${name}' is undeclared`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
349
527
|
// Hardening for runtime-generated (untrusted) sub-flows: bound breadth and
|
|
350
528
|
// contain filesystem access. These do NOT apply to authored/saved flows.
|
|
351
529
|
if (opts.dynamic) {
|
|
@@ -355,10 +533,12 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
355
533
|
if (typeof flow.concurrency === "number" && flow.concurrency > MAX_DYNAMIC_CONCURRENCY) {
|
|
356
534
|
errors.push(`Dynamic sub-flow concurrency too high (${flow.concurrency}, max ${MAX_DYNAMIC_CONCURRENCY})`);
|
|
357
535
|
}
|
|
358
|
-
const root = opts.cwd ? path.resolve(opts.cwd) : undefined;
|
|
359
536
|
for (const p of flow.phases) {
|
|
360
537
|
if (!p || typeof p !== "object")
|
|
361
538
|
continue;
|
|
539
|
+
if (Array.isArray(p.context) && p.context.length > 0) {
|
|
540
|
+
errors.push(`Dynamic sub-flow phase '${p.id}': context file pre-reads are not allowed in generated flows`);
|
|
541
|
+
}
|
|
362
542
|
// A generated phase may not execute shell commands. `script` runs an
|
|
363
543
|
// arbitrary command — a strictly larger capability than the reserved
|
|
364
544
|
// cwd keywords blocked below — so an LLM-authored plan (flow{def} /
|
|
@@ -393,20 +573,11 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
393
573
|
if (typeof p.concurrency === "number" && p.concurrency > MAX_DYNAMIC_CONCURRENCY) {
|
|
394
574
|
errors.push(`Dynamic sub-flow phase '${p.id}': concurrency too high (${p.concurrency}, max ${MAX_DYNAMIC_CONCURRENCY})`);
|
|
395
575
|
}
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
// worktrees that mutate the repo. Only author-written flows may.
|
|
576
|
+
// W1a/FileBroker is not available yet, so a generated phase may not
|
|
577
|
+
// choose any cwd. Lexical containment is insufficient because symlinks
|
|
578
|
+
// and time-of-check/time-of-use races can cross the invocation boundary.
|
|
400
579
|
if (typeof p.cwd === "string") {
|
|
401
|
-
|
|
402
|
-
errors.push(`Dynamic sub-flow phase '${p.id}': cwd '${p.cwd}' is a reserved workspace keyword not allowed in generated flows`);
|
|
403
|
-
}
|
|
404
|
-
else if (root) {
|
|
405
|
-
const resolved = path.resolve(root, p.cwd);
|
|
406
|
-
if (resolved !== root && !resolved.startsWith(root + path.sep)) {
|
|
407
|
-
errors.push(`Dynamic sub-flow phase '${p.id}': cwd '${p.cwd}' escapes the run directory`);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
580
|
+
errors.push(`Dynamic sub-flow phase '${p.id}': cwd selection is not allowed in generated flows`);
|
|
410
581
|
}
|
|
411
582
|
}
|
|
412
583
|
}
|
|
@@ -461,8 +632,30 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
461
632
|
errors.push(`Phase '${p.id}': '${key}' must be a string, got ${typeof v}`);
|
|
462
633
|
}
|
|
463
634
|
}
|
|
464
|
-
if (typeof p.
|
|
465
|
-
|
|
635
|
+
if (typeof p.thinking === "string" &&
|
|
636
|
+
!THINKING_LEVELS.includes(p.thinking)) {
|
|
637
|
+
errors.push(`Phase '${p.id}': 'thinking' must be one of ${THINKING_LEVELS.join(", ")}; got '${p.thinking}'`);
|
|
638
|
+
}
|
|
639
|
+
if (typeof p.cwd === "string" && hasCwdPlaceholder(p.cwd)) {
|
|
640
|
+
const argName = cwdArgName(p.cwd);
|
|
641
|
+
if (!argName) {
|
|
642
|
+
errors.push(`Phase '${p.id}': dynamic 'cwd' must be exactly one whole {args.X} placeholder; concatenation, multiple placeholders, and {steps.*} are forbidden.`);
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
const spec = argSpecs[argName];
|
|
646
|
+
if (!spec) {
|
|
647
|
+
errors.push(`Phase '${p.id}': cwd references undeclared argument '${argName}'`);
|
|
648
|
+
}
|
|
649
|
+
else if (spec.type !== "relative-path") {
|
|
650
|
+
errors.push(`Phase '${p.id}': cwd argument '${argName}' must be declared with type 'relative-path'`);
|
|
651
|
+
}
|
|
652
|
+
if (p.cache?.scope === "cross-run") {
|
|
653
|
+
errors.push(`Phase '${p.id}': cwd selected by an argument cannot use cache.scope 'cross-run' until workspace state restoration is available.`);
|
|
654
|
+
}
|
|
655
|
+
if (p.retry && typeof p.retry.max === "number" && p.retry.max > 0) {
|
|
656
|
+
errors.push(`Phase '${p.id}': cwd selected by an argument cannot use retry.max > 0 because a failed resolve-only write has an unknown filesystem outcome and must be reconciled before another attempt.`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
466
659
|
}
|
|
467
660
|
// dependsOn / from entries are string phase-id refs that flow into the graph
|
|
468
661
|
// helpers and nodeId(); a non-string entry would crash the renderer.
|
|
@@ -535,7 +728,9 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
535
728
|
warnings.push(`Phase '${p.id}' (gate): both 'eval' and 'score' are set — eval runs first (all-pass skips the gate entirely), then score. This is valid but usually one of the two suffices.`);
|
|
536
729
|
}
|
|
537
730
|
// Judge agent naming convention (mirrors the phase-agent check below).
|
|
538
|
-
const judgeAgent = scoreVal
|
|
731
|
+
const judgeAgent = scoreVal !== null && typeof scoreVal === "object"
|
|
732
|
+
? scoreVal.judge?.agent
|
|
733
|
+
: undefined;
|
|
539
734
|
if (typeof judgeAgent === "string" && judgeAgent.includes("_")) {
|
|
540
735
|
errors.push(`Phase '${p.id}': score.judge.agent '${judgeAgent}' uses underscores — use hyphens`);
|
|
541
736
|
}
|
|
@@ -591,6 +786,27 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
591
786
|
if (!p.branches || p.branches.length === 0)
|
|
592
787
|
errors.push(`Phase '${p.id}' (parallel) requires non-empty 'branches'`);
|
|
593
788
|
}
|
|
789
|
+
if (type === "race") {
|
|
790
|
+
if (!p.branches || p.branches.length === 0)
|
|
791
|
+
errors.push(`Phase '${p.id}' (race) requires non-empty 'branches'`);
|
|
792
|
+
else if (p.branches.length < 2)
|
|
793
|
+
errors.push(`Phase '${p.id}' (race): needs at least 2 branches`);
|
|
794
|
+
if (p.cancelLosers !== undefined && typeof p.cancelLosers !== "boolean") {
|
|
795
|
+
errors.push(`Phase '${p.id}' (race): cancelLosers must be a boolean`);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (type === "expand") {
|
|
799
|
+
if (p.def === undefined)
|
|
800
|
+
errors.push(`Phase '${p.id}' (expand) requires 'def' (fragment Taskflow or {steps.X.json})`);
|
|
801
|
+
const em = p.expandMode;
|
|
802
|
+
if (em !== undefined && em !== "nested" && em !== "graft") {
|
|
803
|
+
errors.push(`Phase '${p.id}' (expand): expandMode must be 'nested' or 'graft'`);
|
|
804
|
+
}
|
|
805
|
+
const maxN = p.maxNodes;
|
|
806
|
+
if (maxN !== undefined && (typeof maxN !== "number" || maxN < 1 || maxN > MAX_DYNAMIC_PHASES)) {
|
|
807
|
+
errors.push(`Phase '${p.id}' (expand): maxNodes must be 1..${MAX_DYNAMIC_PHASES}`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
594
810
|
if (type === "reduce") {
|
|
595
811
|
if (!p.from || p.from.length === 0)
|
|
596
812
|
errors.push(`Phase '${p.id}' (reduce) requires 'from'`);
|
|
@@ -896,6 +1112,7 @@ export function collectRefs(phase) {
|
|
|
896
1112
|
scan(phase.over);
|
|
897
1113
|
scan(phase.when);
|
|
898
1114
|
scan(phase.until);
|
|
1115
|
+
scan(phase.cwd);
|
|
899
1116
|
// Script phases: the array form of `run` supports {steps.X}/{args.X}
|
|
900
1117
|
// interpolation (the string form does NOT — it's a raw shell command,
|
|
901
1118
|
// validation rejects placeholders in it), and `input` (stdin) does too.
|