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.
Files changed (193) hide show
  1. package/README.md +248 -808
  2. package/dist/agents.d.ts +12 -0
  3. package/dist/agents.d.ts.map +1 -1
  4. package/dist/agents.js +29 -1
  5. package/dist/agents.js.map +1 -1
  6. package/dist/cwd-bridge.d.ts +61 -0
  7. package/dist/cwd-bridge.d.ts.map +1 -0
  8. package/dist/cwd-bridge.js +136 -0
  9. package/dist/cwd-bridge.js.map +1 -0
  10. package/dist/detached-runner.js +31 -5
  11. package/dist/detached-runner.js.map +1 -1
  12. package/dist/deterministic.d.ts +8 -0
  13. package/dist/deterministic.d.ts.map +1 -1
  14. package/dist/deterministic.js +23 -1
  15. package/dist/deterministic.js.map +1 -1
  16. package/dist/exec/driver.d.ts +48 -0
  17. package/dist/exec/driver.d.ts.map +1 -0
  18. package/dist/exec/driver.js +410 -0
  19. package/dist/exec/driver.js.map +1 -0
  20. package/dist/exec/events.d.ts +76 -0
  21. package/dist/exec/events.d.ts.map +1 -0
  22. package/dist/exec/events.js +108 -0
  23. package/dist/exec/events.js.map +1 -0
  24. package/dist/exec/fold.d.ts +45 -0
  25. package/dist/exec/fold.d.ts.map +1 -0
  26. package/dist/exec/fold.js +122 -0
  27. package/dist/exec/fold.js.map +1 -0
  28. package/dist/exec/index.d.ts +17 -0
  29. package/dist/exec/index.d.ts.map +1 -0
  30. package/dist/exec/index.js +17 -0
  31. package/dist/exec/index.js.map +1 -0
  32. package/dist/exec/kernel-policy.d.ts +35 -0
  33. package/dist/exec/kernel-policy.d.ts.map +1 -0
  34. package/dist/exec/kernel-policy.js +184 -0
  35. package/dist/exec/kernel-policy.js.map +1 -0
  36. package/dist/exec/step-kinds.d.ts +41 -0
  37. package/dist/exec/step-kinds.d.ts.map +1 -0
  38. package/dist/exec/step-kinds.js +599 -0
  39. package/dist/exec/step-kinds.js.map +1 -0
  40. package/dist/exec/step.d.ts +94 -0
  41. package/dist/exec/step.d.ts.map +1 -0
  42. package/dist/exec/step.js +454 -0
  43. package/dist/exec/step.js.map +1 -0
  44. package/dist/flowir/canonical-hash.d.ts +101 -0
  45. package/dist/flowir/canonical-hash.d.ts.map +1 -0
  46. package/dist/flowir/canonical-hash.js +219 -0
  47. package/dist/flowir/canonical-hash.js.map +1 -0
  48. package/dist/flowir/compile.d.ts +49 -0
  49. package/dist/flowir/compile.d.ts.map +1 -0
  50. package/dist/flowir/compile.js +232 -0
  51. package/dist/flowir/compile.js.map +1 -0
  52. package/dist/flowir/cond.d.ts +76 -0
  53. package/dist/flowir/cond.d.ts.map +1 -0
  54. package/dist/flowir/cond.js +214 -0
  55. package/dist/flowir/cond.js.map +1 -0
  56. package/dist/flowir/index.d.ts +20 -23
  57. package/dist/flowir/index.d.ts.map +1 -1
  58. package/dist/flowir/index.js +47 -35
  59. package/dist/flowir/index.js.map +1 -1
  60. package/dist/flowir/meta.d.ts +4 -4
  61. package/dist/flowir/meta.d.ts.map +1 -1
  62. package/dist/flowir/phasefp.d.ts.map +1 -1
  63. package/dist/flowir/phasefp.js +13 -1
  64. package/dist/flowir/phasefp.js.map +1 -1
  65. package/dist/flowir/schema.d.ts +260 -0
  66. package/dist/flowir/schema.d.ts.map +1 -0
  67. package/dist/flowir/schema.js +234 -0
  68. package/dist/flowir/schema.js.map +1 -0
  69. package/dist/flowir/translate.d.ts.map +1 -1
  70. package/dist/flowir/translate.js +3 -0
  71. package/dist/flowir/translate.js.map +1 -1
  72. package/dist/host/runner-types.d.ts +19 -0
  73. package/dist/host/runner-types.d.ts.map +1 -1
  74. package/dist/index.d.ts +4 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +8 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/interpolate.d.ts +4 -0
  79. package/dist/interpolate.d.ts.map +1 -1
  80. package/dist/interpolate.js +14 -0
  81. package/dist/interpolate.js.map +1 -1
  82. package/dist/rates.d.ts +121 -0
  83. package/dist/rates.d.ts.map +1 -0
  84. package/dist/rates.js +187 -0
  85. package/dist/rates.js.map +1 -0
  86. package/dist/replay.d.ts +43 -50
  87. package/dist/replay.d.ts.map +1 -1
  88. package/dist/replay.js +629 -19
  89. package/dist/replay.js.map +1 -1
  90. package/dist/resources/authority.d.ts +35 -0
  91. package/dist/resources/authority.d.ts.map +1 -0
  92. package/dist/resources/authority.js +67 -0
  93. package/dist/resources/authority.js.map +1 -0
  94. package/dist/resources/backend.d.ts +302 -0
  95. package/dist/resources/backend.d.ts.map +1 -0
  96. package/dist/resources/backend.js +16 -0
  97. package/dist/resources/backend.js.map +1 -0
  98. package/dist/resources/baseline.d.ts +116 -0
  99. package/dist/resources/baseline.d.ts.map +1 -0
  100. package/dist/resources/baseline.js +447 -0
  101. package/dist/resources/baseline.js.map +1 -0
  102. package/dist/resources/canonical-json.d.ts +7 -0
  103. package/dist/resources/canonical-json.d.ts.map +1 -0
  104. package/dist/resources/canonical-json.js +75 -0
  105. package/dist/resources/canonical-json.js.map +1 -0
  106. package/dist/resources/errors.d.ts +23 -0
  107. package/dist/resources/errors.d.ts.map +1 -0
  108. package/dist/resources/errors.js +89 -0
  109. package/dist/resources/errors.js.map +1 -0
  110. package/dist/resources/execution.d.ts +90 -0
  111. package/dist/resources/execution.d.ts.map +1 -0
  112. package/dist/resources/execution.js +581 -0
  113. package/dist/resources/execution.js.map +1 -0
  114. package/dist/resources/index.d.ts +15 -0
  115. package/dist/resources/index.d.ts.map +1 -0
  116. package/dist/resources/index.js +15 -0
  117. package/dist/resources/index.js.map +1 -0
  118. package/dist/resources/journal.d.ts +138 -0
  119. package/dist/resources/journal.d.ts.map +1 -0
  120. package/dist/resources/journal.js +438 -0
  121. package/dist/resources/journal.js.map +1 -0
  122. package/dist/resources/leases.d.ts +51 -0
  123. package/dist/resources/leases.d.ts.map +1 -0
  124. package/dist/resources/leases.js +354 -0
  125. package/dist/resources/leases.js.map +1 -0
  126. package/dist/resources/permits.d.ts +52 -0
  127. package/dist/resources/permits.d.ts.map +1 -0
  128. package/dist/resources/permits.js +240 -0
  129. package/dist/resources/permits.js.map +1 -0
  130. package/dist/resources/persistence.d.ts +63 -0
  131. package/dist/resources/persistence.d.ts.map +1 -0
  132. package/dist/resources/persistence.js +522 -0
  133. package/dist/resources/persistence.js.map +1 -0
  134. package/dist/resources/registry.d.ts +56 -0
  135. package/dist/resources/registry.d.ts.map +1 -0
  136. package/dist/resources/registry.js +139 -0
  137. package/dist/resources/registry.js.map +1 -0
  138. package/dist/resources/resolve.d.ts +49 -0
  139. package/dist/resources/resolve.d.ts.map +1 -0
  140. package/dist/resources/resolve.js +309 -0
  141. package/dist/resources/resolve.js.map +1 -0
  142. package/dist/resources/sandbox.d.ts +72 -0
  143. package/dist/resources/sandbox.d.ts.map +1 -0
  144. package/dist/resources/sandbox.js +952 -0
  145. package/dist/resources/sandbox.js.map +1 -0
  146. package/dist/resources/schema.d.ts +195 -0
  147. package/dist/resources/schema.d.ts.map +1 -0
  148. package/dist/resources/schema.js +231 -0
  149. package/dist/resources/schema.js.map +1 -0
  150. package/dist/resources/types.d.ts +36 -0
  151. package/dist/resources/types.d.ts.map +1 -0
  152. package/dist/resources/types.js +75 -0
  153. package/dist/resources/types.js.map +1 -0
  154. package/dist/runner-core.d.ts +53 -0
  155. package/dist/runner-core.d.ts.map +1 -1
  156. package/dist/runner-core.js +521 -53
  157. package/dist/runner-core.js.map +1 -1
  158. package/dist/runtime/phases/approval.d.ts +23 -0
  159. package/dist/runtime/phases/approval.d.ts.map +1 -0
  160. package/dist/runtime/phases/approval.js +38 -0
  161. package/dist/runtime/phases/approval.js.map +1 -0
  162. package/dist/runtime/phases/expand.d.ts +29 -0
  163. package/dist/runtime/phases/expand.d.ts.map +1 -0
  164. package/dist/runtime/phases/expand.js +126 -0
  165. package/dist/runtime/phases/expand.js.map +1 -0
  166. package/dist/runtime/phases/parallel.d.ts +26 -0
  167. package/dist/runtime/phases/parallel.d.ts.map +1 -0
  168. package/dist/runtime/phases/parallel.js +15 -0
  169. package/dist/runtime/phases/parallel.js.map +1 -0
  170. package/dist/runtime/phases/race.d.ts +31 -0
  171. package/dist/runtime/phases/race.d.ts.map +1 -0
  172. package/dist/runtime/phases/race.js +220 -0
  173. package/dist/runtime/phases/race.js.map +1 -0
  174. package/dist/runtime/phases/script.d.ts +38 -0
  175. package/dist/runtime/phases/script.d.ts.map +1 -0
  176. package/dist/runtime/phases/script.js +177 -0
  177. package/dist/runtime/phases/script.js.map +1 -0
  178. package/dist/runtime.d.ts +68 -16
  179. package/dist/runtime.d.ts.map +1 -1
  180. package/dist/runtime.js +1091 -337
  181. package/dist/runtime.js.map +1 -1
  182. package/dist/schema.d.ts +104 -9
  183. package/dist/schema.d.ts.map +1 -1
  184. package/dist/schema.js +249 -32
  185. package/dist/schema.js.map +1 -1
  186. package/dist/store.d.ts +13 -0
  187. package/dist/store.d.ts.map +1 -1
  188. package/dist/store.js.map +1 -1
  189. package/dist/trace.d.ts +7 -1
  190. package/dist/trace.d.ts.map +1 -1
  191. package/dist/trace.js +33 -26
  192. package/dist/trace.js.map +1 -1
  193. 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.TString>;
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.TString>;
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 cwd containment under `cwd`. */
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[];
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
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 { WORKSPACE_KEYWORDS } from "./workspace.js";
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
- const PHASE_TYPES = ["agent", "parallel", "map", "gate", "reduce", "approval", "flow", "loop", "tournament", "script"];
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, { description: "[parallel] Static task branches" })),
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(Type.String({ description: "Thinking level override for this phase" })),
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's subagent. A literal path, or a reserved keyword: 'temp' (ephemeral dir, removed after the phase), 'dedicated' (persistent dir under the run state, kept), or 'worktree' (a git worktree on a throwaway branch, removed after the 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 ArgSpecSchema = Type.Object({
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
- // cwd containment: a generated phase may not escape the run's cwd, and
397
- // may not request a reserved workspace keyword (temp/dedicated/worktree)
398
- // — LLM-authored sub-flows must not allocate isolated dirs or git
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
- if (WORKSPACE_KEYWORDS.includes(p.cwd)) {
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.cwd === "string" && CWD_PLACEHOLDER_RE.test(p.cwd)) {
465
- errors.push(`Phase '${p.id}': 'cwd' does not support interpolation placeholders (${p.cwd}). Use a literal path, or a reserved workspace keyword ('temp', 'dedicated', 'worktree').`);
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.judge?.agent;
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.