taskflow-core 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +80 -25
  2. package/dist/deterministic.d.ts +8 -0
  3. package/dist/deterministic.d.ts.map +1 -1
  4. package/dist/deterministic.js +23 -1
  5. package/dist/deterministic.js.map +1 -1
  6. package/dist/exec/driver.d.ts +47 -0
  7. package/dist/exec/driver.d.ts.map +1 -0
  8. package/dist/exec/driver.js +397 -0
  9. package/dist/exec/driver.js.map +1 -0
  10. package/dist/exec/events.d.ts +76 -0
  11. package/dist/exec/events.d.ts.map +1 -0
  12. package/dist/exec/events.js +108 -0
  13. package/dist/exec/events.js.map +1 -0
  14. package/dist/exec/fold.d.ts +45 -0
  15. package/dist/exec/fold.d.ts.map +1 -0
  16. package/dist/exec/fold.js +122 -0
  17. package/dist/exec/fold.js.map +1 -0
  18. package/dist/exec/index.d.ts +17 -0
  19. package/dist/exec/index.d.ts.map +1 -0
  20. package/dist/exec/index.js +17 -0
  21. package/dist/exec/index.js.map +1 -0
  22. package/dist/exec/kernel-policy.d.ts +35 -0
  23. package/dist/exec/kernel-policy.d.ts.map +1 -0
  24. package/dist/exec/kernel-policy.js +184 -0
  25. package/dist/exec/kernel-policy.js.map +1 -0
  26. package/dist/exec/step-kinds.d.ts +41 -0
  27. package/dist/exec/step-kinds.d.ts.map +1 -0
  28. package/dist/exec/step-kinds.js +574 -0
  29. package/dist/exec/step-kinds.js.map +1 -0
  30. package/dist/exec/step.d.ts +93 -0
  31. package/dist/exec/step.d.ts.map +1 -0
  32. package/dist/exec/step.js +436 -0
  33. package/dist/exec/step.js.map +1 -0
  34. package/dist/flowir/canonical-hash.d.ts +101 -0
  35. package/dist/flowir/canonical-hash.d.ts.map +1 -0
  36. package/dist/flowir/canonical-hash.js +219 -0
  37. package/dist/flowir/canonical-hash.js.map +1 -0
  38. package/dist/flowir/compile.d.ts +49 -0
  39. package/dist/flowir/compile.d.ts.map +1 -0
  40. package/dist/flowir/compile.js +219 -0
  41. package/dist/flowir/compile.js.map +1 -0
  42. package/dist/flowir/cond.d.ts +76 -0
  43. package/dist/flowir/cond.d.ts.map +1 -0
  44. package/dist/flowir/cond.js +214 -0
  45. package/dist/flowir/cond.js.map +1 -0
  46. package/dist/flowir/index.d.ts +20 -23
  47. package/dist/flowir/index.d.ts.map +1 -1
  48. package/dist/flowir/index.js +47 -35
  49. package/dist/flowir/index.js.map +1 -1
  50. package/dist/flowir/meta.d.ts +4 -4
  51. package/dist/flowir/meta.d.ts.map +1 -1
  52. package/dist/flowir/phasefp.d.ts.map +1 -1
  53. package/dist/flowir/phasefp.js +13 -1
  54. package/dist/flowir/phasefp.js.map +1 -1
  55. package/dist/flowir/schema.d.ts +260 -0
  56. package/dist/flowir/schema.d.ts.map +1 -0
  57. package/dist/flowir/schema.js +234 -0
  58. package/dist/flowir/schema.js.map +1 -0
  59. package/dist/flowir/translate.d.ts.map +1 -1
  60. package/dist/flowir/translate.js +3 -0
  61. package/dist/flowir/translate.js.map +1 -1
  62. package/dist/host/runner-types.d.ts +3 -0
  63. package/dist/host/runner-types.d.ts.map +1 -1
  64. package/dist/index.d.ts +2 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +2 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/rates.d.ts +121 -0
  69. package/dist/rates.d.ts.map +1 -0
  70. package/dist/rates.js +187 -0
  71. package/dist/rates.js.map +1 -0
  72. package/dist/replay.d.ts +43 -50
  73. package/dist/replay.d.ts.map +1 -1
  74. package/dist/replay.js +629 -19
  75. package/dist/replay.js.map +1 -1
  76. package/dist/runner-core.d.ts +24 -0
  77. package/dist/runner-core.d.ts.map +1 -1
  78. package/dist/runner-core.js +225 -32
  79. package/dist/runner-core.js.map +1 -1
  80. package/dist/runtime/phases/approval.d.ts +23 -0
  81. package/dist/runtime/phases/approval.d.ts.map +1 -0
  82. package/dist/runtime/phases/approval.js +38 -0
  83. package/dist/runtime/phases/approval.js.map +1 -0
  84. package/dist/runtime/phases/expand.d.ts +29 -0
  85. package/dist/runtime/phases/expand.d.ts.map +1 -0
  86. package/dist/runtime/phases/expand.js +126 -0
  87. package/dist/runtime/phases/expand.js.map +1 -0
  88. package/dist/runtime/phases/parallel.d.ts +26 -0
  89. package/dist/runtime/phases/parallel.d.ts.map +1 -0
  90. package/dist/runtime/phases/parallel.js +15 -0
  91. package/dist/runtime/phases/parallel.js.map +1 -0
  92. package/dist/runtime/phases/race.d.ts +31 -0
  93. package/dist/runtime/phases/race.d.ts.map +1 -0
  94. package/dist/runtime/phases/race.js +220 -0
  95. package/dist/runtime/phases/race.js.map +1 -0
  96. package/dist/runtime/phases/script.d.ts +38 -0
  97. package/dist/runtime/phases/script.d.ts.map +1 -0
  98. package/dist/runtime/phases/script.js +139 -0
  99. package/dist/runtime/phases/script.js.map +1 -0
  100. package/dist/runtime.d.ts +29 -16
  101. package/dist/runtime.d.ts.map +1 -1
  102. package/dist/runtime.js +554 -292
  103. package/dist/runtime.js.map +1 -1
  104. package/dist/schema.d.ts +36 -4
  105. package/dist/schema.d.ts.map +1 -1
  106. package/dist/schema.js +86 -10
  107. package/dist/schema.js.map +1 -1
  108. package/dist/store.d.ts +6 -0
  109. package/dist/store.d.ts.map +1 -1
  110. package/dist/store.js.map +1 -1
  111. package/dist/trace.d.ts +4 -1
  112. package/dist/trace.d.ts.map +1 -1
  113. package/dist/trace.js +33 -26
  114. package/dist/trace.js.map +1 -1
  115. package/package.json +5 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-hash.d.ts","sourceRoot":"","sources":["../../src/flowir/canonical-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA8EtD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAoBrD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEzD;AAMD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAGjD"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * # Genuine content-addressed hash over canonical FlowIR
3
+ *
4
+ * This is pi-taskflow's **own** content-addressed hash (Q5=own) over the
5
+ * canonical {@link FlowIR} type contract defined in `./schema.ts`. Unlike
6
+ * `./hash.ts`'s `flowDefHash` — which fingerprints the *DSL Taskflow
7
+ * definition* (vendored from overstory, async Web Crypto, truncated to 16
8
+ * bytes / 32 hex) — this module hashes the *compiled IR* synchronously with
9
+ * `node:crypto` SHA-256 (full 64-hex-char digest). The two answer different
10
+ * questions:
11
+ *
12
+ * - `flowDefHash(def)` — "did the *flow definition* change?" (DSL-level)
13
+ * - `hashFlowIR(ir)` — "is this *compiled IR* the same IR?" (IR-level)
14
+ *
15
+ * Wired into `compileTaskflowToIR` via `hashFlowIR` (`ir:<64-hex>`).
16
+ * The IR-level hash is the content-addressed key the event-sourced kernel
17
+ * (batch 2, `flowir/compile.ts`) will use to deduplicate compiled graphs and
18
+ * to key per-phase fingerprints (`hashNode`) independent of surface formatting.
19
+ *
20
+ * ## Canonicalization guarantees
21
+ *
22
+ * {@link canonicalizeFlowIR} produces a DETERMINISTIC serialization that is
23
+ * invariant under the cosmetic differences two logically-equivalent IRs may
24
+ * differ by:
25
+ *
26
+ * - **Object key order** — all object keys are stable-sorted (UTF-16 code
27
+ * units), so reordering fields on a node or on the IR object does not
28
+ * change the canonical form.
29
+ * - **Node array order** — `nodes` is re-sorted by `id` (stable), so two IRs
30
+ * that list the same nodes in different order canonicalize identically.
31
+ * Other arrays (`inject`, `emits`, `deps`, `edges`) keep their order —
32
+ * their ordering is semantically meaningful (declared-read order, edge
33
+ * declaration order) and must be preserved.
34
+ * - **Condition spelling** — a node's `when` guard is canonicalized via
35
+ * {@link normalizeCond} (`./cond.ts`), which collapses whitespace, operator
36
+ * spacing, and redundant enclosing parens while preserving string-literal
37
+ * contents. Equivalent conditions canonicalize identically.
38
+ * - **Insignificant formatting** — `undefined` optional fields are dropped,
39
+ * so their presence/absence does not change the hash.
40
+ *
41
+ * Two logically-equivalent FlowIRs (key reorder, whitespace, equivalent
42
+ * condition spelling) MUST canonicalize to the same string.
43
+ *
44
+ * ## Domain-separated digests
45
+ *
46
+ * Return values are prefixed:
47
+ * - `hashFlowIR` → `ir:<64-hex>`
48
+ * - `hashNode` → `node:<64-hex>`
49
+ *
50
+ * so IR-level and node-level digests never collide in a shared cache key
51
+ * namespace.
52
+ *
53
+ * ## Purity
54
+ *
55
+ * Zero runtime deps besides `node:crypto`. No IO, no `Date`, no randomness,
56
+ * never throws. The hash is synchronous (unlike `flowDefHash`).
57
+ *
58
+ * Exported from the FlowIR barrel (`./index.ts`) for consumers; **not yet
59
+ * wired** into runtime/cache — `usedFallbackHash` is untouched. Wiring
60
+ * happens in batch 2.
61
+ *
62
+ * @see ./schema.ts for the canonical FlowIR type contract.
63
+ * @see ./cond.ts for condition normalization (`normalizeCond`).
64
+ * @see ./hash.ts for the DSL-level `flowDefHash` (vendored from overstory).
65
+ */
66
+ import { createHash } from "node:crypto";
67
+ import { normalizeCond } from "./cond.js";
68
+ // ---------------------------------------------------------------------------
69
+ // Canonical serializer (key-sorted, undefined-dropped, array-order-preserving)
70
+ // ---------------------------------------------------------------------------
71
+ /**
72
+ * Deterministic serialization of a plain-JSON-compatible value: recursively
73
+ * key-sorts objects (UTF-16 code units), drops `undefined` values, preserves
74
+ * array order, and uses `JSON.stringify` for primitives/null. This mirrors
75
+ * the canonical-JSON convention used by `./hash.ts`'s `canonicalJson` (itself
76
+ * vendored from overstory) so the two hashes share the same primitive
77
+ * encoding — but this serializer is local to the IR hash so the IR-level
78
+ * contract can evolve independently of the overstory-vendored DSL hash.
79
+ *
80
+ * The value passed in MUST be pre-canonicalized (nodes sorted by id, `when`
81
+ * normalized) — this function does not know about FlowIR semantics, it only
82
+ * guarantees key-order independence and `undefined`-dropping.
83
+ */
84
+ function canonicalSerialize(value) {
85
+ if (value === null)
86
+ return "null";
87
+ if (typeof value === "boolean")
88
+ return JSON.stringify(value);
89
+ if (typeof value === "number")
90
+ return JSON.stringify(value);
91
+ if (typeof value === "string")
92
+ return JSON.stringify(value);
93
+ if (Array.isArray(value)) {
94
+ return `[${value.map((item) => canonicalSerialize(item)).join(",")}]`;
95
+ }
96
+ if (typeof value === "object") {
97
+ const record = value;
98
+ const keys = Object.keys(record)
99
+ .filter((key) => record[key] !== undefined)
100
+ .sort();
101
+ const body = keys.map((key) => `${JSON.stringify(key)}:${canonicalSerialize(record[key])}`);
102
+ return `{${body.join(",")}}`;
103
+ }
104
+ // undefined / function / symbol / bigint at this layer — not representable.
105
+ return "null";
106
+ }
107
+ // ---------------------------------------------------------------------------
108
+ // Canonical node object (drops undefined, normalizes `when`)
109
+ // ---------------------------------------------------------------------------
110
+ /**
111
+ * Project a {@link FlowIRNode} into a plain canonical object form: fields are
112
+ * emitted in a fixed order (order is irrelevant — `canonicalSerialize`
113
+ * re-sorts keys, but emitting canonically makes the intermediate form
114
+ * human-inspectable), `when` is normalized via {@link normalizeCond} so
115
+ * equivalent condition spellings collapse, and `undefined` optionals are
116
+ * omitted so their presence/absence does not affect the hash.
117
+ *
118
+ * `inject`/`emits`/`deps` arrays are preserved verbatim (order is semantic —
119
+ * declared-read order matters for fingerprinting).
120
+ */
121
+ function canonicalNodeObject(node) {
122
+ const obj = {
123
+ id: node.id,
124
+ kind: node.kind,
125
+ inject: node.inject,
126
+ emits: node.emits,
127
+ };
128
+ if (node.when !== undefined) {
129
+ obj.when = normalizeCond(node.when).canonical;
130
+ }
131
+ if (node.task !== undefined)
132
+ obj.task = node.task;
133
+ if (node.condRef !== undefined)
134
+ obj.condRef = node.condRef;
135
+ if (node.deps !== undefined)
136
+ obj.deps = node.deps;
137
+ if (node.join !== undefined)
138
+ obj.join = node.join;
139
+ if (node.timeout !== undefined)
140
+ obj.timeout = node.timeout;
141
+ if (node.payload !== undefined)
142
+ obj.payload = node.payload;
143
+ return obj;
144
+ }
145
+ // ---------------------------------------------------------------------------
146
+ // Public canonicalization
147
+ // ---------------------------------------------------------------------------
148
+ /**
149
+ * Deterministic, order- and whitespace-independent canonical serialization of
150
+ * a {@link FlowIR}.
151
+ *
152
+ * - `nodes` is re-sorted by `id` (stable) so node-list order does not affect
153
+ * the result.
154
+ * - Each node's `when` guard is canonicalized via {@link normalizeCond}.
155
+ * - All object keys are stable-sorted; `undefined` optionals are dropped.
156
+ * - `inject`/`emits`/`deps`/`edges` arrays keep their order (semantic).
157
+ *
158
+ * Two logically-equivalent FlowIRs MUST canonicalize to the same string.
159
+ * Never throws.
160
+ */
161
+ export function canonicalizeFlowIR(ir) {
162
+ // Stable sort by node id — node-list order is not semantic.
163
+ const sortedNodes = [...ir.nodes].sort((a, b) => {
164
+ if (a.id < b.id)
165
+ return -1;
166
+ if (a.id > b.id)
167
+ return 1;
168
+ return 0;
169
+ });
170
+ const obj = {
171
+ name: ir.name,
172
+ nodes: sortedNodes.map((n) => canonicalNodeObject(n)),
173
+ };
174
+ if (ir.version !== undefined)
175
+ obj.version = ir.version;
176
+ if (ir.edges !== undefined)
177
+ obj.edges = ir.edges;
178
+ if (ir.args !== undefined)
179
+ obj.args = ir.args;
180
+ if (ir.budget !== undefined)
181
+ obj.budget = ir.budget;
182
+ if (ir.concurrency !== undefined)
183
+ obj.concurrency = ir.concurrency;
184
+ if (ir.meta !== undefined)
185
+ obj.meta = ir.meta;
186
+ return canonicalSerialize(obj);
187
+ }
188
+ /**
189
+ * Deterministic canonical serialization of a single {@link FlowIRNode}, for
190
+ * per-node content addressing. Same canonicalization rules as
191
+ * {@link canonicalizeFlowIR} applied to one node (`when` normalized, keys
192
+ * sorted, `undefined` optionals dropped, `inject`/`emits`/`deps` order
193
+ * preserved). Never throws.
194
+ */
195
+ export function canonicalizeNode(node) {
196
+ return canonicalSerialize(canonicalNodeObject(node));
197
+ }
198
+ // ---------------------------------------------------------------------------
199
+ // Content-addressed hashing (node:crypto SHA-256, domain-prefixed)
200
+ // ---------------------------------------------------------------------------
201
+ /**
202
+ * Domain-separated content hash of a compiled FlowIR.
203
+ * Returns `ir:<64 lowercase hex SHA-256 chars>` over {@link canonicalizeFlowIR}.
204
+ * Synchronous. Never throws.
205
+ */
206
+ export function hashFlowIR(ir) {
207
+ const hex = createHash("sha256").update(canonicalizeFlowIR(ir), "utf8").digest("hex");
208
+ return `ir:${hex}`;
209
+ }
210
+ /**
211
+ * Domain-separated per-node content hash.
212
+ * Returns `node:<64 lowercase hex SHA-256 chars>` over {@link canonicalizeNode}.
213
+ * Synchronous. Never throws.
214
+ */
215
+ export function hashNode(node) {
216
+ const hex = createHash("sha256").update(canonicalizeNode(node), "utf8").digest("hex");
217
+ return `node:${hex}`;
218
+ }
219
+ //# sourceMappingURL=canonical-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-hash.js","sourceRoot":"","sources":["../../src/flowir/canonical-hash.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Genuine Taskflow → FlowIR compiler (RFC §5.2, S0).
3
+ *
4
+ * This graduates the stub `translateTaskflow` into a **canonical** IR emitter:
5
+ * 1:1 phase→node projection (native multi-node lowering deferred per §12),
6
+ * declared inject/emits, explicit edges, normalized `when` via {@link normalizeCond},
7
+ * and richer optional node fields (`task`, `deps`, `join`, `timeout`, `condRef`).
8
+ *
9
+ * Pure, synchronous, never throws — diagnostics live in the return value so
10
+ * `/tf ir` on a broken flow still yields a structured report.
11
+ *
12
+ * @see docs/rfc-0.2.0-architecture.md §5
13
+ * @see ./translate.ts (stub; still used for sidecar field list parity)
14
+ */
15
+ import { type Taskflow } from "../schema.ts";
16
+ import type { FlowIR as CanonicalFlowIR } from "./schema.ts";
17
+ import type { CompileError, CompileWarning, FlowIR, TaskflowIRMeta } from "./meta.ts";
18
+ /**
19
+ * Result of {@link compileTaskflowToFlowIR}: canonical IR + declared meta + diagnostics.
20
+ * `usedFallbackHash` is **false** when the IR is well-formed enough to content-address
21
+ * (always, unless hard errors prevent IR emission).
22
+ *
23
+ * Named separately from Mermaid `compileTaskflow` in `../compile.ts` (diagram).
24
+ */
25
+ export interface CompileTaskflowToFlowIRResult {
26
+ /** Canonical FlowIR (superset of the stub projection). */
27
+ canonical: CanonicalFlowIR;
28
+ /** Stub-compatible IR projection (same nodes without requiring consumers to import schema.ts). */
29
+ ir: FlowIR;
30
+ meta: TaskflowIRMeta;
31
+ warnings: CompileWarning[];
32
+ errors: CompileError[];
33
+ /** False once the genuine compiler owns the hash (S0). */
34
+ usedFallbackHash: boolean;
35
+ }
36
+ /**
37
+ * Compile a (desugared) Taskflow into canonical FlowIR.
38
+ *
39
+ * - One node per phase (1:1).
40
+ * - `inject` = `{steps.*}` refs ∪ `dependsOn` (minus self).
41
+ * - `emits` = `[phase.id]`.
42
+ * - `edges` synthesized from inject for graph consumers.
43
+ * - `when` kept as source text; `condRef` = normalizeCond(canonical) when present.
44
+ * - Optional fields: task, deps, join, timeout copied when set.
45
+ *
46
+ * Never throws.
47
+ */
48
+ export declare function compileTaskflowToFlowIR(def: Taskflow): CompileTaskflowToFlowIRResult;
49
+ //# sourceMappingURL=compile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/flowir/compile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAwD,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEnG,OAAO,KAAK,EACX,MAAM,IAAI,eAAe,EAIzB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACX,YAAY,EACZ,cAAc,EAEd,MAAM,EAEN,cAAc,EACd,MAAM,WAAW,CAAC;AA4EnB;;;;;;GAMG;AACH,MAAM,WAAW,6BAA6B;IAC7C,0DAA0D;IAC1D,SAAS,EAAE,eAAe,CAAC;IAC3B,kGAAkG;IAClG,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,0DAA0D;IAC1D,gBAAgB,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,QAAQ,GAAG,6BAA6B,CA+HpF"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Genuine Taskflow → FlowIR compiler (RFC §5.2, S0).
3
+ *
4
+ * This graduates the stub `translateTaskflow` into a **canonical** IR emitter:
5
+ * 1:1 phase→node projection (native multi-node lowering deferred per §12),
6
+ * declared inject/emits, explicit edges, normalized `when` via {@link normalizeCond},
7
+ * and richer optional node fields (`task`, `deps`, `join`, `timeout`, `condRef`).
8
+ *
9
+ * Pure, synchronous, never throws — diagnostics live in the return value so
10
+ * `/tf ir` on a broken flow still yields a structured report.
11
+ *
12
+ * @see docs/rfc-0.2.0-architecture.md §5
13
+ * @see ./translate.ts (stub; still used for sidecar field list parity)
14
+ */
15
+ import { collectRefs, PHASE_TYPES } from "../schema.js";
16
+ import { normalizeCond } from "./cond.js";
17
+ // Keep in sync with translate.ts SIDECAR_PHASE_FIELDS (round-trip lossless).
18
+ const SIDECAR_PHASE_FIELDS = [
19
+ "agent",
20
+ "task",
21
+ "over",
22
+ "as",
23
+ "branches",
24
+ "from",
25
+ "use",
26
+ "def",
27
+ "with",
28
+ "run",
29
+ "input",
30
+ "timeout",
31
+ "until",
32
+ "maxIterations",
33
+ "convergence",
34
+ "reflexion",
35
+ "variants",
36
+ "judge",
37
+ "judgeAgent",
38
+ "mode",
39
+ "dependsOn",
40
+ "join",
41
+ "when",
42
+ "retry",
43
+ "output",
44
+ "expect",
45
+ "model",
46
+ "thinking",
47
+ "tools",
48
+ "cwd",
49
+ "final",
50
+ "optional",
51
+ "idempotent",
52
+ "concurrency",
53
+ "context",
54
+ "contextLimit",
55
+ "onBlock",
56
+ "eval",
57
+ "score",
58
+ "cache",
59
+ "shareContext",
60
+ "cancelLosers",
61
+ "expandMode",
62
+ "maxNodes",
63
+ ];
64
+ const NODE_FIELD_KEYS = new Set(["task", "dependsOn", "join", "when", "timeout"]);
65
+ const VALID_KINDS = new Set(PHASE_TYPES);
66
+ function sidecarForPhase(phase) {
67
+ const out = {};
68
+ const rec = phase;
69
+ for (const k of SIDECAR_PHASE_FIELDS) {
70
+ if (k in rec && rec[k] !== undefined)
71
+ out[k] = rec[k];
72
+ }
73
+ return out;
74
+ }
75
+ function payloadForPhase(phase) {
76
+ const sidecar = sidecarForPhase(phase);
77
+ for (const key of NODE_FIELD_KEYS)
78
+ delete sidecar[key];
79
+ return Object.keys(sidecar).length > 0 ? sidecar : undefined;
80
+ }
81
+ function asKind(type) {
82
+ const k = (type ?? "agent");
83
+ if (VALID_KINDS.has(k))
84
+ return k;
85
+ // Unknown kinds still project as agent for IR shape; validation is source of truth.
86
+ return "agent";
87
+ }
88
+ /**
89
+ * Compile a (desugared) Taskflow into canonical FlowIR.
90
+ *
91
+ * - One node per phase (1:1).
92
+ * - `inject` = `{steps.*}` refs ∪ `dependsOn` (minus self).
93
+ * - `emits` = `[phase.id]`.
94
+ * - `edges` synthesized from inject for graph consumers.
95
+ * - `when` kept as source text; `condRef` = normalizeCond(canonical) when present.
96
+ * - Optional fields: task, deps, join, timeout copied when set.
97
+ *
98
+ * Never throws.
99
+ */
100
+ export function compileTaskflowToFlowIR(def) {
101
+ const warnings = [];
102
+ const errors = [];
103
+ const declaredDeps = {};
104
+ const sidecarPhases = {};
105
+ const knownIds = new Set((def.phases ?? []).map((p) => p.id));
106
+ if (!def.name || typeof def.name !== "string") {
107
+ errors.push({ code: "missing-name", message: "Taskflow.name is required" });
108
+ }
109
+ if (!Array.isArray(def.phases) || def.phases.length === 0) {
110
+ errors.push({ code: "empty-phases", message: "Taskflow.phases must be a non-empty array" });
111
+ }
112
+ const nodes = [];
113
+ const edges = [];
114
+ for (const phase of def.phases ?? []) {
115
+ if (!phase?.id) {
116
+ errors.push({ code: "missing-phase-id", message: "Phase missing id" });
117
+ continue;
118
+ }
119
+ const refs = collectRefs(phase);
120
+ const reads = new Set(refs.steps.filter((id) => id !== phase.id));
121
+ for (const d of phase.dependsOn ?? []) {
122
+ if (d !== phase.id)
123
+ reads.add(d);
124
+ }
125
+ for (const d of phase.from ?? []) {
126
+ if (d !== phase.id)
127
+ reads.add(d);
128
+ }
129
+ const inject = Array.from(reads);
130
+ declaredDeps[phase.id] = { reads: inject, writes: [phase.id] };
131
+ for (const r of refs.steps) {
132
+ if (r !== phase.id && !knownIds.has(r)) {
133
+ warnings.push({
134
+ phaseId: phase.id,
135
+ message: `references {steps.${r}.*} but no phase '${r}' exists`,
136
+ });
137
+ }
138
+ }
139
+ const kind = asKind(phase.type);
140
+ if (phase.type && !VALID_KINDS.has(phase.type)) {
141
+ warnings.push({
142
+ phaseId: phase.id,
143
+ message: `unknown phase type '${phase.type}' — projected as kind 'agent'`,
144
+ });
145
+ }
146
+ const node = {
147
+ id: phase.id,
148
+ kind,
149
+ inject,
150
+ emits: [phase.id],
151
+ };
152
+ if (phase.when !== undefined) {
153
+ node.when = phase.when;
154
+ const n = normalizeCond(phase.when);
155
+ // condRef is a stable, hashable form of the guard (not a storage key).
156
+ node.condRef = n.canonical || undefined;
157
+ }
158
+ if (typeof phase.task === "string")
159
+ node.task = phase.task;
160
+ if (phase.dependsOn && phase.dependsOn.length > 0)
161
+ node.deps = [...phase.dependsOn];
162
+ if (phase.join === "all" || phase.join === "any")
163
+ node.join = phase.join;
164
+ if (typeof phase.timeout === "number")
165
+ node.timeout = phase.timeout;
166
+ const payload = payloadForPhase(phase);
167
+ if (payload)
168
+ node.payload = payload;
169
+ for (const from of inject) {
170
+ edges.push({ from, to: phase.id });
171
+ }
172
+ sidecarPhases[phase.id] = sidecarForPhase(phase);
173
+ nodes.push(node);
174
+ }
175
+ const canonical = {
176
+ name: def.name || "unnamed",
177
+ version: typeof def.version === "number" ? def.version : 1,
178
+ nodes,
179
+ edges: edges.length > 0 ? edges : undefined,
180
+ args: def.args,
181
+ budget: def.budget,
182
+ concurrency: def.concurrency,
183
+ meta: {
184
+ source: "taskflow-core",
185
+ irVersion: 1,
186
+ // These flow-level switches change agent discovery / runtime inputs and
187
+ // therefore MUST participate in the content-addressed cache identity.
188
+ // Keeping them in canonical meta preserves the 1:1 node projection while
189
+ // preventing two semantically different flows from sharing an IR hash.
190
+ annotations: {
191
+ agentScope: def.agentScope ?? "user",
192
+ contextSharing: def.contextSharing ?? false,
193
+ },
194
+ },
195
+ };
196
+ // Stub-compatible projection: strip fields meta.FlowIR doesn't declare but keep inject/emits/when/kind.
197
+ const ir = {
198
+ name: canonical.name,
199
+ nodes: nodes.map((n) => ({
200
+ id: n.id,
201
+ kind: n.kind,
202
+ inject: n.inject,
203
+ emits: n.emits,
204
+ when: n.when,
205
+ })),
206
+ args: def.args,
207
+ budget: def.budget,
208
+ concurrency: def.concurrency,
209
+ };
210
+ const meta = {
211
+ sourceFlowName: def.name || "unnamed",
212
+ declaredDeps,
213
+ sidecar: { phases: sidecarPhases },
214
+ };
215
+ // Genuine compiler owns the IR hash when we produced nodes.
216
+ const usedFallbackHash = nodes.length === 0 || errors.some((e) => e.code === "empty-phases");
217
+ return { canonical, ir, meta, warnings, errors, usedFallbackHash };
218
+ }
219
+ //# sourceMappingURL=compile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/flowir/compile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Canonical normalization of `when` / `until` / `eval` condition expressions.
3
+ *
4
+ * This is the **condition-IR seam** of the overstory-convergence roadmap's
5
+ * FlowIR compiler: it lifts a raw condition string into a stable
6
+ * {@link NormalizedCond} descriptor that two future consumers share:
7
+ *
8
+ * - **Hashing** (consumer a): structurally-equivalent conditions MUST
9
+ * normalize to the same `canonical` string so they fold identically into
10
+ * the FlowIR content hash (whitespace, operator spacing, and redundant
11
+ * enclosing parens do not change meaning → must not change the hash).
12
+ * - **Deterministic replay** (consumer b): the `when-guard` decision is
13
+ * re-evaluated from the recorded expression; `refs` tells replay exactly
14
+ * which `steps.*` / `args.*` / `env.*` inputs the guard read so it can
15
+ * rebind them from the event log without token spend.
16
+ *
17
+ * **Single source of truth — no semantic drift.** The condition grammar
18
+ * (tokenizer + recursive-descent parser + comparison/truthiness semantics)
19
+ * lives in {@link ../interpolate.ts}. This module REUSES that parser by
20
+ * delegating parse-validation (and fail-open semantics) to
21
+ * {@link tryEvaluateCondition} — it does NOT duplicate or reimplement the
22
+ * tokenizer/parser. If the parser accepts an expression, it is structurally
23
+ * valid and we normalize its surface text; if the parser rejects it, we
24
+ * fail open (see {@link normalizeCond}) so a broken guard never silently
25
+ * drops a phase — matching the project's fail-open-for-guards invariant.
26
+ *
27
+ * The `canonical` form is a surface normalization (whitespace removal +
28
+ * redundant enclosing-paren stripping with string-literal contents
29
+ * preserved), not a full AST rewrite: for valid expressions whitespace only
30
+ * separates tokens, so removing it (after protecting quoted literals) yields
31
+ * a stable concatenation that is invariant under the cosmetic differences
32
+ * hashing cares about. Reference extraction runs on the **literal-protected**
33
+ * surface so `{steps.*}` text inside string literals is never treated as a
34
+ * real dependency.
35
+ *
36
+ * Pure module: no IO, no `Date`, no randomness, never throws.
37
+ *
38
+ * @see docs/rfc-0.2.0-architecture.md §5.4 (`flowir/cond.ts` — 条件归一化)
39
+ * @see ../interpolate.ts for the shared condition parser (tokenize / CondParser / compare / tryEvaluateCondition)
40
+ */
41
+ /**
42
+ * A condition expression lifted into a stable, hashable descriptor.
43
+ *
44
+ * - `source` — the original expression text, verbatim (never normalized).
45
+ * - `canonical` — a stable normalized surface form: whitespace-collapsed,
46
+ * operator-spacing-invariant, redundant enclosing parens stripped, with
47
+ * quoted-string contents preserved. Structurally-equivalent expressions
48
+ * produce byte-identical `canonical` strings.
49
+ * - `refs` — the `steps.<id>…` / `args.<name>…` / `env.<VAR>…` references
50
+ * the expression reads (in first-occurrence order, de-duplicated). Empty
51
+ * when the expression failed to parse (fail-open). Placeholders that only
52
+ * appear inside string literals are **not** counted as refs.
53
+ */
54
+ export interface NormalizedCond {
55
+ source: string;
56
+ canonical: string;
57
+ refs: string[];
58
+ }
59
+ /**
60
+ * Normalize a `when` / `until` / `eval` condition expression.
61
+ *
62
+ * The expression is parsed (validated) by reusing the shared condition
63
+ * parser in {@link ../interpolate.ts} via {@link tryEvaluateCondition}; on a
64
+ * parse error this **fails open** — returning `{ source, canonical:
65
+ * source.trim(), refs: [] }` — so a malformed guard is never silently
66
+ * dropped and never crashes a compile/replay pass. This mirrors the
67
+ * project's fail-open-for-guards invariant (`when` parse errors → phase
68
+ * still runs). Never throws.
69
+ *
70
+ * On success, `canonical` is the whitespace/paren-normalized surface form
71
+ * (stable for hashing) and `refs` are the `steps.*` / `args.*` / `env.*`
72
+ * references the expression reads (for replay rebinding). Placeholders that
73
+ * only appear inside string literals are excluded from `refs`.
74
+ */
75
+ export declare function normalizeCond(expr: string): NormalizedCond;
76
+ //# sourceMappingURL=cond.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cond.d.ts","sourceRoot":"","sources":["../../src/flowir/cond.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAQH;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;CACf;AA6ID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAsB1D"}