taskflow-core 0.2.1 → 0.2.3
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 +5 -4
- package/dist/agents.d.ts +2 -2
- package/dist/agents.d.ts.map +1 -1
- package/dist/build-info.d.ts +48 -0
- package/dist/build-info.d.ts.map +1 -0
- package/dist/build-info.js +112 -0
- package/dist/build-info.js.map +1 -0
- package/dist/build-info.json +4 -0
- package/dist/detached-control.d.ts +52 -0
- package/dist/detached-control.d.ts.map +1 -0
- package/dist/detached-control.js +251 -0
- package/dist/detached-control.js.map +1 -0
- package/dist/detached-runner.js +183 -30
- package/dist/detached-runner.js.map +1 -1
- package/dist/exec/driver.d.ts +5 -0
- package/dist/exec/driver.d.ts.map +1 -1
- package/dist/exec/driver.js +27 -16
- package/dist/exec/driver.js.map +1 -1
- package/dist/exec/kernel-policy.d.ts.map +1 -1
- package/dist/exec/kernel-policy.js +10 -0
- package/dist/exec/kernel-policy.js.map +1 -1
- package/dist/exec/step-kinds.d.ts +14 -1
- package/dist/exec/step-kinds.d.ts.map +1 -1
- package/dist/exec/step-kinds.js +98 -3
- package/dist/exec/step-kinds.js.map +1 -1
- package/dist/exec/step.d.ts +21 -0
- package/dist/exec/step.d.ts.map +1 -1
- package/dist/exec/step.js +22 -5
- package/dist/exec/step.js.map +1 -1
- package/dist/final-output.d.ts +53 -0
- package/dist/final-output.d.ts.map +1 -0
- package/dist/final-output.js +65 -0
- package/dist/final-output.js.map +1 -0
- package/dist/flowir/canonical-hash.d.ts.map +1 -1
- package/dist/flowir/canonical-hash.js +2 -0
- package/dist/flowir/canonical-hash.js.map +1 -1
- package/dist/flowir/compile.d.ts.map +1 -1
- package/dist/flowir/compile.js +4 -0
- package/dist/flowir/compile.js.map +1 -1
- package/dist/flowir/meta.d.ts +3 -0
- package/dist/flowir/meta.d.ts.map +1 -1
- package/dist/flowir/schema.d.ts +3 -0
- package/dist/flowir/schema.d.ts.map +1 -1
- package/dist/flowir/schema.js.map +1 -1
- package/dist/flowir/translate.d.ts.map +1 -1
- package/dist/flowir/translate.js +4 -0
- package/dist/flowir/translate.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/resume.d.ts +76 -0
- package/dist/resume.d.ts.map +1 -0
- package/dist/resume.js +190 -0
- package/dist/resume.js.map +1 -0
- package/dist/runner-core.d.ts.map +1 -1
- package/dist/runner-core.js +10 -0
- package/dist/runner-core.js.map +1 -1
- package/dist/runtime/phases/parallel.d.ts +3 -0
- package/dist/runtime/phases/parallel.d.ts.map +1 -1
- package/dist/runtime/phases/parallel.js.map +1 -1
- package/dist/runtime/phases/reduce.d.ts +47 -0
- package/dist/runtime/phases/reduce.d.ts.map +1 -0
- package/dist/runtime/phases/reduce.js +195 -0
- package/dist/runtime/phases/reduce.js.map +1 -0
- package/dist/runtime.d.ts +28 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +412 -92
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +67 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +177 -1
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +90 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +494 -181
- package/dist/store.js.map +1 -1
- package/package.json +2 -2
package/dist/schema.d.ts
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
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
|
+
/** Hard cap on subagent calls made by one tree-reduce phase. This bounds
|
|
9
|
+
* author mistakes even when no run budget was declared. */
|
|
10
|
+
export declare const TREE_REDUCE_HARD_MAX_CALLS = 256;
|
|
8
11
|
/** Closed set of native phase kinds — single source of truth for DSL + FlowIR. */
|
|
9
12
|
export declare const PHASE_TYPES: readonly ["agent", "parallel", "map", "gate", "reduce", "approval", "flow", "loop", "tournament", "script",
|
|
10
13
|
/** First completed branch wins (Horizon B). */
|
|
@@ -12,6 +15,9 @@ export declare const PHASE_TYPES: readonly ["agent", "parallel", "map", "gate",
|
|
|
12
15
|
/** Dynamic sub-DAG: nested sub-flow or graft-promote into parent (Horizon B). */
|
|
13
16
|
"expand"];
|
|
14
17
|
export type PhaseType = (typeof PHASE_TYPES)[number];
|
|
18
|
+
/** Phase kinds that execute one or more subagents and therefore require an
|
|
19
|
+
* effective idle watchdog (or a finite wall timeout when the watchdog is off). */
|
|
20
|
+
export declare const AGENT_RUNNING_PHASE_TYPES: readonly ["agent", "gate", "reduce", "map", "parallel", "loop", "tournament", "race"];
|
|
15
21
|
/** Loop iteration bounds. Authors may lower the max; the hard cap is a runaway guard. */
|
|
16
22
|
export declare const LOOP_DEFAULT_MAX_ITERATIONS = 10;
|
|
17
23
|
export declare const LOOP_HARD_MAX_ITERATIONS = 100;
|
|
@@ -40,6 +46,7 @@ export type CacheScope = (typeof CACHE_SCOPES)[number];
|
|
|
40
46
|
declare const ParallelTaskSchema: Type.TObject<{
|
|
41
47
|
task: Type.TString;
|
|
42
48
|
agent: Type.TOptional<Type.TString>;
|
|
49
|
+
cwd: Type.TOptional<Type.TString>;
|
|
43
50
|
}>;
|
|
44
51
|
/** Declarative retry policy for a phase's subagent call(s). */
|
|
45
52
|
declare const RetrySchema: Type.TObject<{
|
|
@@ -72,6 +79,7 @@ declare const PhaseSchema: Type.TObject<{
|
|
|
72
79
|
branches: Type.TOptional<Type.TArray<Type.TObject<{
|
|
73
80
|
task: Type.TString;
|
|
74
81
|
agent: Type.TOptional<Type.TString>;
|
|
82
|
+
cwd: Type.TOptional<Type.TString>;
|
|
75
83
|
}>>>;
|
|
76
84
|
/**
|
|
77
85
|
* [race] When true (default), abort in-flight loser branches after the first branch
|
|
@@ -80,6 +88,21 @@ declare const PhaseSchema: Type.TObject<{
|
|
|
80
88
|
*/
|
|
81
89
|
cancelLosers: Type.TOptional<Type.TBoolean>;
|
|
82
90
|
from: Type.TOptional<Type.TArray<Type.TString>>;
|
|
91
|
+
/**
|
|
92
|
+
* [reduce] How the aggregated `from[]` inputs are reduced. `'one-shot'`
|
|
93
|
+
* (default) feeds all inputs to a single reducer call as `{previous.output}`.
|
|
94
|
+
* `'tree'` batches the inputs (see `batchSize`) and runs intermediate
|
|
95
|
+
* reducer calls over each batch, then reduces the round outputs until one
|
|
96
|
+
* remains — useful when the aggregated input would exceed a single prompt.
|
|
97
|
+
* Tree reduction uses the SAME agent/model/options/timeout/idleTimeout as
|
|
98
|
+
* the phase and forces the imperative runtime (the event kernel falls back).
|
|
99
|
+
* The corrected `{previous.output}` aggregation (all `from[]` sources) always
|
|
100
|
+
* applies regardless of strategy.
|
|
101
|
+
*/
|
|
102
|
+
reduceStrategy: Type.TOptional<Type.TUnsafe<"one-shot" | "tree">>;
|
|
103
|
+
/** [reduce] With `reduceStrategy:'tree'`, the max number of aggregated inputs
|
|
104
|
+
* fed to each intermediate reducer call (>= 2). Ignored for one-shot. */
|
|
105
|
+
batchSize: Type.TOptional<Type.TInteger>;
|
|
83
106
|
use: Type.TOptional<Type.TString>;
|
|
84
107
|
/** [expand] Fragment source — string interpolation or inline Taskflow / phases (same as flow.def). */
|
|
85
108
|
def: Type.TOptional<Type.TUnknown>;
|
|
@@ -91,6 +114,14 @@ declare const PhaseSchema: Type.TObject<{
|
|
|
91
114
|
run: Type.TOptional<Type.TUnion<[Type.TString, Type.TArray<Type.TString>]>>;
|
|
92
115
|
input: Type.TOptional<Type.TString>;
|
|
93
116
|
timeout: Type.TOptional<Type.TNumber>;
|
|
117
|
+
/**
|
|
118
|
+
* Idle watchdog override (ms) for agent-running phases. A positive value (>= 1000)
|
|
119
|
+
* replaces the host default (300000ms): if a subagent produces no output for this
|
|
120
|
+
* long it is killed as stalled. `0` DISABLES the watchdog — but then a finite
|
|
121
|
+
* wall `timeout` (>= 1000) is REQUIRED on this phase so it can never hang forever.
|
|
122
|
+
* Overrides the flow-level `idleTimeout`. Absent → flow-level or host default.
|
|
123
|
+
*/
|
|
124
|
+
idleTimeout: Type.TOptional<Type.TNumber>;
|
|
94
125
|
until: Type.TOptional<Type.TString>;
|
|
95
126
|
maxIterations: Type.TOptional<Type.TNumber>;
|
|
96
127
|
convergence: Type.TOptional<Type.TBoolean>;
|
|
@@ -210,6 +241,14 @@ export declare const TaskflowSchema: Type.TObject<{
|
|
|
210
241
|
strictInterpolation: Type.TOptional<Type.TBoolean>;
|
|
211
242
|
contextSharing: Type.TOptional<Type.TBoolean>;
|
|
212
243
|
incremental: Type.TOptional<Type.TBoolean>;
|
|
244
|
+
/**
|
|
245
|
+
* Flow-level idle watchdog (ms) for all agent-running phases that don't set
|
|
246
|
+
* their own `idleTimeout`. Positive (>= 1000) overrides the host default
|
|
247
|
+
* (300000ms); `0` disables the watchdog for those phases — but then every
|
|
248
|
+
* agent-running phase MUST declare a finite wall `timeout` (>= 1000) so the
|
|
249
|
+
* flow can never hang forever. A per-phase `idleTimeout` overrides this.
|
|
250
|
+
*/
|
|
251
|
+
idleTimeout: Type.TOptional<Type.TNumber>;
|
|
213
252
|
phases: Type.TArray<Type.TObject<{
|
|
214
253
|
id: Type.TString;
|
|
215
254
|
type: Type.TOptional<Type.TUnsafe<"agent" | "approval" | "expand" | "flow" | "gate" | "loop" | "map" | "parallel" | "race" | "reduce" | "script" | "tournament">>;
|
|
@@ -220,6 +259,7 @@ export declare const TaskflowSchema: Type.TObject<{
|
|
|
220
259
|
branches: Type.TOptional<Type.TArray<Type.TObject<{
|
|
221
260
|
task: Type.TString;
|
|
222
261
|
agent: Type.TOptional<Type.TString>;
|
|
262
|
+
cwd: Type.TOptional<Type.TString>;
|
|
223
263
|
}>>>;
|
|
224
264
|
/**
|
|
225
265
|
* [race] When true (default), abort in-flight loser branches after the first branch
|
|
@@ -228,6 +268,21 @@ export declare const TaskflowSchema: Type.TObject<{
|
|
|
228
268
|
*/
|
|
229
269
|
cancelLosers: Type.TOptional<Type.TBoolean>;
|
|
230
270
|
from: Type.TOptional<Type.TArray<Type.TString>>;
|
|
271
|
+
/**
|
|
272
|
+
* [reduce] How the aggregated `from[]` inputs are reduced. `'one-shot'`
|
|
273
|
+
* (default) feeds all inputs to a single reducer call as `{previous.output}`.
|
|
274
|
+
* `'tree'` batches the inputs (see `batchSize`) and runs intermediate
|
|
275
|
+
* reducer calls over each batch, then reduces the round outputs until one
|
|
276
|
+
* remains — useful when the aggregated input would exceed a single prompt.
|
|
277
|
+
* Tree reduction uses the SAME agent/model/options/timeout/idleTimeout as
|
|
278
|
+
* the phase and forces the imperative runtime (the event kernel falls back).
|
|
279
|
+
* The corrected `{previous.output}` aggregation (all `from[]` sources) always
|
|
280
|
+
* applies regardless of strategy.
|
|
281
|
+
*/
|
|
282
|
+
reduceStrategy: Type.TOptional<Type.TUnsafe<"one-shot" | "tree">>;
|
|
283
|
+
/** [reduce] With `reduceStrategy:'tree'`, the max number of aggregated inputs
|
|
284
|
+
* fed to each intermediate reducer call (>= 2). Ignored for one-shot. */
|
|
285
|
+
batchSize: Type.TOptional<Type.TInteger>;
|
|
231
286
|
use: Type.TOptional<Type.TString>;
|
|
232
287
|
/** [expand] Fragment source — string interpolation or inline Taskflow / phases (same as flow.def). */
|
|
233
288
|
def: Type.TOptional<Type.TUnknown>;
|
|
@@ -239,6 +294,14 @@ export declare const TaskflowSchema: Type.TObject<{
|
|
|
239
294
|
run: Type.TOptional<Type.TUnion<[Type.TString, Type.TArray<Type.TString>]>>;
|
|
240
295
|
input: Type.TOptional<Type.TString>;
|
|
241
296
|
timeout: Type.TOptional<Type.TNumber>;
|
|
297
|
+
/**
|
|
298
|
+
* Idle watchdog override (ms) for agent-running phases. A positive value (>= 1000)
|
|
299
|
+
* replaces the host default (300000ms): if a subagent produces no output for this
|
|
300
|
+
* long it is killed as stalled. `0` DISABLES the watchdog — but then a finite
|
|
301
|
+
* wall `timeout` (>= 1000) is REQUIRED on this phase so it can never hang forever.
|
|
302
|
+
* Overrides the flow-level `idleTimeout`. Absent → flow-level or host default.
|
|
303
|
+
*/
|
|
304
|
+
idleTimeout: Type.TOptional<Type.TNumber>;
|
|
242
305
|
until: Type.TOptional<Type.TString>;
|
|
243
306
|
maxIterations: Type.TOptional<Type.TNumber>;
|
|
244
307
|
convergence: Type.TOptional<Type.TBoolean>;
|
|
@@ -292,6 +355,10 @@ export interface ShorthandStep {
|
|
|
292
355
|
context?: string[];
|
|
293
356
|
/** Max characters per context file (pass-through to Phase.contextLimit). */
|
|
294
357
|
contextLimit?: number;
|
|
358
|
+
/** Working directory for this step's subagent (pass-through to Phase.cwd).
|
|
359
|
+
* A literal path or a reserved workspace keyword ('temp'/'dedicated'/
|
|
360
|
+
* 'worktree'). A top-level `cwd` is the default; a step cwd overrides it. */
|
|
361
|
+
cwd?: string;
|
|
295
362
|
}
|
|
296
363
|
/** True when `def` is a shorthand spec (no `phases`, but a task/tasks/chain field). */
|
|
297
364
|
export declare function isShorthand(def: unknown): boolean;
|
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
|
@@ -11,9 +11,13 @@ import { scorerShapeErrors } from "./scorers.js";
|
|
|
11
11
|
import { Type } from "typebox";
|
|
12
12
|
import { Errors as SchemaErrors } from "typebox/value";
|
|
13
13
|
import { cwdArgName, hasCwdPlaceholder, normalizeRelativePath } from "./cwd-bridge.js";
|
|
14
|
+
import { WORKSPACE_KEYWORDS } from "./workspace.js";
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
// Phase types
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
18
|
+
/** Hard cap on subagent calls made by one tree-reduce phase. This bounds
|
|
19
|
+
* author mistakes even when no run budget was declared. */
|
|
20
|
+
export const TREE_REDUCE_HARD_MAX_CALLS = 256;
|
|
17
21
|
/** Closed set of native phase kinds — single source of truth for DSL + FlowIR. */
|
|
18
22
|
export const PHASE_TYPES = [
|
|
19
23
|
"agent",
|
|
@@ -31,6 +35,18 @@ export const PHASE_TYPES = [
|
|
|
31
35
|
/** Dynamic sub-DAG: nested sub-flow or graft-promote into parent (Horizon B). */
|
|
32
36
|
"expand",
|
|
33
37
|
];
|
|
38
|
+
/** Phase kinds that execute one or more subagents and therefore require an
|
|
39
|
+
* effective idle watchdog (or a finite wall timeout when the watchdog is off). */
|
|
40
|
+
export const AGENT_RUNNING_PHASE_TYPES = [
|
|
41
|
+
"agent",
|
|
42
|
+
"gate",
|
|
43
|
+
"reduce",
|
|
44
|
+
"map",
|
|
45
|
+
"parallel",
|
|
46
|
+
"loop",
|
|
47
|
+
"tournament",
|
|
48
|
+
"race",
|
|
49
|
+
];
|
|
34
50
|
/** Loop iteration bounds. Authors may lower the max; the hard cap is a runaway guard. */
|
|
35
51
|
export const LOOP_DEFAULT_MAX_ITERATIONS = 10;
|
|
36
52
|
export const LOOP_HARD_MAX_ITERATIONS = 100;
|
|
@@ -61,6 +77,9 @@ const CACHE_CROSS_RUN_BLOCKED_TYPES = ["gate", "approval", "loop", "tournament",
|
|
|
61
77
|
const ParallelTaskSchema = Type.Object({
|
|
62
78
|
task: Type.String({ description: "Task for this parallel branch (supports interpolation)" }),
|
|
63
79
|
agent: Type.Optional(Type.String({ description: "Override the phase agent for this branch" })),
|
|
80
|
+
cwd: Type.Optional(Type.String({
|
|
81
|
+
description: "Working directory for this parallel branch's subagent (a literal path). Overrides the phase-level cwd for this branch only. Reserved workspace keywords ('temp'/'dedicated'/'worktree') are NOT supported per-branch (the workspace lifecycle is per-phase) — use the phase-level cwd for those.",
|
|
82
|
+
})),
|
|
64
83
|
}, { additionalProperties: false });
|
|
65
84
|
/** Declarative retry policy for a phase's subagent call(s). */
|
|
66
85
|
const RetrySchema = Type.Object({
|
|
@@ -110,6 +129,24 @@ const PhaseSchema = Type.Object({
|
|
|
110
129
|
cancelLosers: Type.Optional(Type.Boolean({ default: true })),
|
|
111
130
|
// reduce
|
|
112
131
|
from: Type.Optional(Type.Array(Type.String(), { description: "[reduce] Phase ids whose outputs are aggregated" })),
|
|
132
|
+
/**
|
|
133
|
+
* [reduce] How the aggregated `from[]` inputs are reduced. `'one-shot'`
|
|
134
|
+
* (default) feeds all inputs to a single reducer call as `{previous.output}`.
|
|
135
|
+
* `'tree'` batches the inputs (see `batchSize`) and runs intermediate
|
|
136
|
+
* reducer calls over each batch, then reduces the round outputs until one
|
|
137
|
+
* remains — useful when the aggregated input would exceed a single prompt.
|
|
138
|
+
* Tree reduction uses the SAME agent/model/options/timeout/idleTimeout as
|
|
139
|
+
* the phase and forces the imperative runtime (the event kernel falls back).
|
|
140
|
+
* The corrected `{previous.output}` aggregation (all `from[]` sources) always
|
|
141
|
+
* applies regardless of strategy.
|
|
142
|
+
*/
|
|
143
|
+
reduceStrategy: Type.Optional(StringEnum(["one-shot", "tree"], {
|
|
144
|
+
description: "[reduce] 'one-shot' (default) = single reducer call; 'tree' = batched intermediate rounds (see batchSize). Forces imperative runtime.",
|
|
145
|
+
default: "one-shot",
|
|
146
|
+
})),
|
|
147
|
+
/** [reduce] With `reduceStrategy:'tree'`, the max number of aggregated inputs
|
|
148
|
+
* fed to each intermediate reducer call (>= 2). Ignored for one-shot. */
|
|
149
|
+
batchSize: Type.Optional(Type.Integer({ minimum: 2, description: "[reduce] Batch size for reduceStrategy:'tree' (integer >= 2). Ignored for one-shot." })),
|
|
113
150
|
// sub-workflow (flow) + expand fragment
|
|
114
151
|
use: Type.Optional(Type.String({ description: "[flow] Name of a saved taskflow to run as this phase" })),
|
|
115
152
|
/** [expand] Fragment source — string interpolation or inline Taskflow / phases (same as flow.def). */
|
|
@@ -137,6 +174,16 @@ const PhaseSchema = Type.Object({
|
|
|
137
174
|
timeout: Type.Optional(Type.Number({
|
|
138
175
|
description: "Max execution time in milliseconds. For script phases: caps the shell command (default 60000, max 300000). For agent-running phases (agent/gate/reduce/map/parallel/loop/tournament): caps EACH subagent call — on expiry the subagent is aborted and the phase fails with a 'timedOut' marker (never retried). Not supported for approval/flow phases. Must be >= 1000.",
|
|
139
176
|
})),
|
|
177
|
+
/**
|
|
178
|
+
* Idle watchdog override (ms) for agent-running phases. A positive value (>= 1000)
|
|
179
|
+
* replaces the host default (300000ms): if a subagent produces no output for this
|
|
180
|
+
* long it is killed as stalled. `0` DISABLES the watchdog — but then a finite
|
|
181
|
+
* wall `timeout` (>= 1000) is REQUIRED on this phase so it can never hang forever.
|
|
182
|
+
* Overrides the flow-level `idleTimeout`. Absent → flow-level or host default.
|
|
183
|
+
*/
|
|
184
|
+
idleTimeout: Type.Optional(Type.Number({
|
|
185
|
+
description: "[agent-running] Idle watchdog in ms (>= 1000, or 0 to disable). 0 requires a finite wall 'timeout' >= 1000. Overrides the flow-level idleTimeout.",
|
|
186
|
+
})),
|
|
140
187
|
// loop-until-done
|
|
141
188
|
until: Type.Optional(Type.String({
|
|
142
189
|
description: "[loop] Stop condition evaluated after each iteration. The iteration's output is exposed as {steps.<thisId>.output}/.json. Supports the same operators as `when`. The loop stops when this is truthy, on convergence, or at maxIterations. A parse error stops the loop (fail-safe).",
|
|
@@ -279,6 +326,16 @@ export const TaskflowSchema = Type.Object({
|
|
|
279
326
|
incremental: Type.Optional(Type.Boolean({
|
|
280
327
|
description: "Default every phase to cross-run caching (scope:'cross-run') so re-running this flow reuses unchanged phases across runs/sessions. Equivalent to setting cache:{scope:'cross-run'} on every phase; per-phase cache settings and the cross-run-blocked types (gate/approval/loop/tournament) still take precedence. Default false (run-only — each run starts fresh unless a phase opts in). A run-time `incremental` argument overrides this.",
|
|
281
328
|
})),
|
|
329
|
+
/**
|
|
330
|
+
* Flow-level idle watchdog (ms) for all agent-running phases that don't set
|
|
331
|
+
* their own `idleTimeout`. Positive (>= 1000) overrides the host default
|
|
332
|
+
* (300000ms); `0` disables the watchdog for those phases — but then every
|
|
333
|
+
* agent-running phase MUST declare a finite wall `timeout` (>= 1000) so the
|
|
334
|
+
* flow can never hang forever. A per-phase `idleTimeout` overrides this.
|
|
335
|
+
*/
|
|
336
|
+
idleTimeout: Type.Optional(Type.Number({
|
|
337
|
+
description: "Flow-level idle watchdog in ms (>= 1000, or 0 to disable). Per-phase idleTimeout overrides. 0 requires every agent-running phase to declare a finite wall 'timeout' >= 1000.",
|
|
338
|
+
})),
|
|
282
339
|
phases: Type.Array(PhaseSchema, { minItems: 1, description: "Ordered phase definitions (DAG via dependsOn)" }),
|
|
283
340
|
}, { additionalProperties: false });
|
|
284
341
|
/** True when `def` is a shorthand spec (no `phases`, but a task/tasks/chain field). */
|
|
@@ -310,6 +367,8 @@ function readStep(s) {
|
|
|
310
367
|
step.context = ctx;
|
|
311
368
|
if (typeof o.contextLimit === "number")
|
|
312
369
|
step.contextLimit = o.contextLimit;
|
|
370
|
+
if (typeof o.cwd === "string")
|
|
371
|
+
step.cwd = o.cwd;
|
|
313
372
|
return step;
|
|
314
373
|
}
|
|
315
374
|
return { task: "" };
|
|
@@ -336,6 +395,13 @@ export function desugar(def) {
|
|
|
336
395
|
meta.budget = d.budget;
|
|
337
396
|
if (typeof d.strictInterpolation === "boolean")
|
|
338
397
|
meta.strictInterpolation = d.strictInterpolation;
|
|
398
|
+
/** Top-level shorthand cwd (literal path or workspace keyword). Becomes the
|
|
399
|
+
* default for every step; a per-step cwd overrides it. For single/chain it
|
|
400
|
+
* lands on each Phase.cwd (full workspace lifecycle). For parallel it lands
|
|
401
|
+
* on the phase cwd (shared), with per-branch cwds overriding per-branch.
|
|
402
|
+
* Note: `cwd` is a Phase field, NOT a Taskflow field, so it is distributed
|
|
403
|
+
* to each phase below rather than placed on the Taskflow object. */
|
|
404
|
+
const topCwd = typeof d.cwd === "string" && d.cwd.trim() ? d.cwd : undefined;
|
|
339
405
|
const nameOf = (fallback) => (typeof d.name === "string" && d.name.trim() ? d.name.trim() : fallback);
|
|
340
406
|
// chain → sequential agent phases
|
|
341
407
|
if (Array.isArray(d.chain) && d.chain.length > 0) {
|
|
@@ -353,6 +419,9 @@ export function desugar(def) {
|
|
|
353
419
|
phase.context = s.context;
|
|
354
420
|
if (s.contextLimit !== undefined)
|
|
355
421
|
phase.contextLimit = s.contextLimit;
|
|
422
|
+
const stepCwd = s.cwd ?? topCwd;
|
|
423
|
+
if (stepCwd)
|
|
424
|
+
phase.cwd = stepCwd;
|
|
356
425
|
if (i > 0)
|
|
357
426
|
phase.dependsOn = [`step${i}`];
|
|
358
427
|
if (i === steps.length - 1)
|
|
@@ -366,8 +435,18 @@ export function desugar(def) {
|
|
|
366
435
|
// per branch): spec-level context plus the union of step-level contexts.
|
|
367
436
|
if (Array.isArray(d.tasks) && d.tasks.length > 0) {
|
|
368
437
|
const steps = d.tasks.map(readStep);
|
|
369
|
-
const branches = steps.map((s) =>
|
|
438
|
+
const branches = steps.map((s) => {
|
|
439
|
+
const b = { task: s.task };
|
|
440
|
+
if (s.agent)
|
|
441
|
+
b.agent = s.agent;
|
|
442
|
+
// Per-branch literal cwd (workspace keywords are rejected by validation).
|
|
443
|
+
if (s.cwd)
|
|
444
|
+
b.cwd = s.cwd;
|
|
445
|
+
return b;
|
|
446
|
+
});
|
|
370
447
|
const phase = { id: "parallel", type: "parallel", branches, final: true };
|
|
448
|
+
if (topCwd)
|
|
449
|
+
phase.cwd = topCwd;
|
|
371
450
|
const shared = [...(readContextList(d.context) ?? []), ...steps.flatMap((s) => s.context ?? [])];
|
|
372
451
|
if (shared.length)
|
|
373
452
|
phase.context = Array.from(new Set(shared));
|
|
@@ -389,6 +468,8 @@ export function desugar(def) {
|
|
|
389
468
|
phase.context = ctx;
|
|
390
469
|
if (typeof d.contextLimit === "number")
|
|
391
470
|
phase.contextLimit = d.contextLimit;
|
|
471
|
+
if (topCwd)
|
|
472
|
+
phase.cwd = topCwd;
|
|
392
473
|
return { name: nameOf("task"), ...meta, phases: [phase] };
|
|
393
474
|
}
|
|
394
475
|
throw new Error("Shorthand spec needs one of: 'task' (single), 'tasks' (parallel), or 'chain' (sequential)");
|
|
@@ -524,6 +605,17 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
524
605
|
warnings.push(`Invocation argument '${name}' is undeclared`);
|
|
525
606
|
}
|
|
526
607
|
}
|
|
608
|
+
// Flow-level idleTimeout: positive must be >= 1000; 0 is allowed (disables the
|
|
609
|
+
// watchdog) but every agent-running phase must then declare a finite wall
|
|
610
|
+
// `timeout` so the run can never hang forever (critical invariant #5).
|
|
611
|
+
if (flow.idleTimeout !== undefined) {
|
|
612
|
+
if (typeof flow.idleTimeout !== "number" || !Number.isFinite(flow.idleTimeout) || flow.idleTimeout < 0) {
|
|
613
|
+
errors.push(`Flow 'idleTimeout' must be a non-negative finite number (ms), got ${typeof flow.idleTimeout === "number" ? flow.idleTimeout : typeof flow.idleTimeout}`);
|
|
614
|
+
}
|
|
615
|
+
else if (flow.idleTimeout > 0 && flow.idleTimeout < 1000) {
|
|
616
|
+
errors.push(`Flow 'idleTimeout' must be 0 (disable) or >= 1000 ms, got ${flow.idleTimeout}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
527
619
|
// Hardening for runtime-generated (untrusted) sub-flows: bound breadth and
|
|
528
620
|
// contain filesystem access. These do NOT apply to authored/saved flows.
|
|
529
621
|
if (opts.dynamic) {
|
|
@@ -579,6 +671,13 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
579
671
|
if (typeof p.cwd === "string") {
|
|
580
672
|
errors.push(`Dynamic sub-flow phase '${p.id}': cwd selection is not allowed in generated flows`);
|
|
581
673
|
}
|
|
674
|
+
if (Array.isArray(p.branches)) {
|
|
675
|
+
p.branches.forEach((branch, i) => {
|
|
676
|
+
if (branch && typeof branch === "object" && typeof branch.cwd === "string") {
|
|
677
|
+
errors.push(`Dynamic sub-flow phase '${p.id}': branches[${i}].cwd selection is not allowed in generated flows`);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
}
|
|
582
681
|
}
|
|
583
682
|
}
|
|
584
683
|
const ids = new Set();
|
|
@@ -670,11 +769,35 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
670
769
|
// Branch entries become competitors at runtime (b.task is interpolated); a
|
|
671
770
|
// non-object / non-string-task entry would crash the runtime, so reject it.
|
|
672
771
|
if (Array.isArray(p.branches)) {
|
|
772
|
+
const branchPhaseType = (p.type ?? "agent");
|
|
673
773
|
p.branches.forEach((b, i) => {
|
|
674
774
|
if (!b || typeof b !== "object" || Array.isArray(b))
|
|
675
775
|
errors.push(`Phase '${p.id}': branches[${i}] must be an object with a 'task', got ${b === null ? "null" : typeof b}`);
|
|
676
776
|
else if (typeof b.task !== "string")
|
|
677
777
|
errors.push(`Phase '${p.id}': branches[${i}].task must be a string`);
|
|
778
|
+
else {
|
|
779
|
+
// Per-branch cwd: a literal path is honored per-branch; a reserved
|
|
780
|
+
// workspace keyword ('temp'/'dedicated'/'worktree') is NOT supported
|
|
781
|
+
// per-branch (the workspace lifecycle is per-phase — a branch cannot
|
|
782
|
+
// safely allocate/teardown its own workspace). Reject that shape
|
|
783
|
+
// precisely rather than silently using the wrong cwd. Use the
|
|
784
|
+
// phase-level `cwd` for workspace isolation.
|
|
785
|
+
const bcwd = b.cwd;
|
|
786
|
+
if (typeof bcwd === "string") {
|
|
787
|
+
if (branchPhaseType !== "parallel") {
|
|
788
|
+
errors.push(`Phase '${p.id}' (${branchPhaseType}): branches[${i}].cwd is only supported for parallel phases`);
|
|
789
|
+
}
|
|
790
|
+
else if (hasCwdPlaceholder(bcwd)) {
|
|
791
|
+
errors.push(`Phase '${p.id}': branches[${i}].cwd does not support interpolation placeholders (${bcwd}). Use a literal path.`);
|
|
792
|
+
}
|
|
793
|
+
else if (WORKSPACE_KEYWORDS.includes(bcwd)) {
|
|
794
|
+
errors.push(`Phase '${p.id}': branches[${i}].cwd '${bcwd}' is a reserved workspace keyword not supported per-branch (the workspace lifecycle is per-phase). Use the phase-level 'cwd' for workspace isolation.`);
|
|
795
|
+
}
|
|
796
|
+
else if (typeof p.cwd === "string" && (WORKSPACE_KEYWORDS.includes(p.cwd) || hasCwdPlaceholder(p.cwd))) {
|
|
797
|
+
errors.push(`Phase '${p.id}': branches[${i}].cwd cannot override phase cwd '${p.cwd}' because that phase uses a managed workspace/cwd binding. Put the literal cwd on the phase or remove the branch override.`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
678
801
|
});
|
|
679
802
|
}
|
|
680
803
|
// tools entries are matched against a Set by the adapters (t => set.has(t));
|
|
@@ -812,6 +935,33 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
812
935
|
errors.push(`Phase '${p.id}' (reduce) requires 'from'`);
|
|
813
936
|
if (!p.task)
|
|
814
937
|
errors.push(`Phase '${p.id}' (reduce) requires 'task'`);
|
|
938
|
+
// reduceStrategy / batchSize (reduce-only).
|
|
939
|
+
const strat = p.reduceStrategy;
|
|
940
|
+
if (strat !== undefined && strat !== "tree" && strat !== "one-shot") {
|
|
941
|
+
errors.push(`Phase '${p.id}' (reduce): reduceStrategy must be 'tree' or 'one-shot', got '${String(strat)}'`);
|
|
942
|
+
}
|
|
943
|
+
const bs = p.batchSize;
|
|
944
|
+
if (bs !== undefined) {
|
|
945
|
+
if (typeof bs !== "number" || !Number.isFinite(bs) || bs < 2 || !Number.isInteger(bs)) {
|
|
946
|
+
errors.push(`Phase '${p.id}' (reduce): batchSize must be an integer >= 2`);
|
|
947
|
+
}
|
|
948
|
+
else if (strat !== "tree") {
|
|
949
|
+
warnings.push(`Phase '${p.id}' (reduce): batchSize is only used with reduceStrategy 'tree' — ignored for one-shot`);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (strat === "tree") {
|
|
953
|
+
const batchSize = typeof bs === "number" && Number.isInteger(bs) && bs >= 2 ? bs : 2;
|
|
954
|
+
let remaining = asArray(p.from).length;
|
|
955
|
+
let maxCalls = 0;
|
|
956
|
+
while (remaining > 1 && maxCalls <= TREE_REDUCE_HARD_MAX_CALLS) {
|
|
957
|
+
const callsThisRound = Math.ceil(remaining / batchSize);
|
|
958
|
+
maxCalls += callsThisRound;
|
|
959
|
+
remaining = callsThisRound;
|
|
960
|
+
}
|
|
961
|
+
if (maxCalls > TREE_REDUCE_HARD_MAX_CALLS) {
|
|
962
|
+
errors.push(`Phase '${p.id}' (reduce): tree strategy exceeds hard cap ${TREE_REDUCE_HARD_MAX_CALLS} subagent calls; increase batchSize or split the reduction`);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
815
965
|
}
|
|
816
966
|
if (type === "flow") {
|
|
817
967
|
const hasUse = typeof p.use === "string" && p.use.length > 0;
|
|
@@ -883,6 +1033,32 @@ export function validateTaskflow(def, opts = {}) {
|
|
|
883
1033
|
errors.push(`Phase '${p.id}' (${type}): 'timeout' must be a number >= 1000 ms`);
|
|
884
1034
|
}
|
|
885
1035
|
}
|
|
1036
|
+
// idleTimeout validation (agent-running phases that can use the watchdog).
|
|
1037
|
+
// The host default (300000ms) applies when neither phase nor flow sets it.
|
|
1038
|
+
const AGENT_RUNNING_FOR_IDLE = new Set(AGENT_RUNNING_PHASE_TYPES);
|
|
1039
|
+
if (AGENT_RUNNING_FOR_IDLE.has(type)) {
|
|
1040
|
+
const phaseIdle = p.idleTimeout;
|
|
1041
|
+
if (phaseIdle !== undefined) {
|
|
1042
|
+
if (typeof phaseIdle !== "number" || !Number.isFinite(phaseIdle) || phaseIdle < 0) {
|
|
1043
|
+
errors.push(`Phase '${p.id}': 'idleTimeout' must be a non-negative finite number (ms), got ${typeof phaseIdle === "number" ? phaseIdle : typeof phaseIdle}`);
|
|
1044
|
+
}
|
|
1045
|
+
else if (phaseIdle > 0 && phaseIdle < 1000) {
|
|
1046
|
+
errors.push(`Phase '${p.id}': 'idleTimeout' must be 0 (disable) or >= 1000 ms, got ${phaseIdle}`);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
// Effective idle watchdog = phase (wins) → flow → host default (undefined).
|
|
1050
|
+
const effectiveIdle = (typeof phaseIdle === "number" ? phaseIdle : flow.idleTimeout);
|
|
1051
|
+
if (effectiveIdle === 0) {
|
|
1052
|
+
// Disabling the watchdog requires a finite wall timeout so the phase can
|
|
1053
|
+
// never hang forever (critical invariant #5: never hang forever).
|
|
1054
|
+
if (typeof p.timeout !== "number" || !Number.isFinite(p.timeout) || p.timeout < 1000) {
|
|
1055
|
+
errors.push(`Phase '${p.id}': idleTimeout:0 disables the watchdog — a finite wall 'timeout' (>= 1000 ms) is required so this phase cannot hang forever. Add a 'timeout' or use a positive 'idleTimeout'.`);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
else if (p.idleTimeout !== undefined) {
|
|
1060
|
+
warnings.push(`Phase '${p.id}' (${type}): 'idleTimeout' only applies to agent-running phases (${AGENT_RUNNING_PHASE_TYPES.join("/")}) — ignored here`);
|
|
1061
|
+
}
|
|
886
1062
|
if (p.retry) {
|
|
887
1063
|
if (typeof p.retry.max !== "number" || p.retry.max < 0) {
|
|
888
1064
|
errors.push(`Phase '${p.id}': retry.max must be a number >= 0`);
|