@agentproto/workflow-runtime 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,10 @@
1
1
  import { runTool } from '@agentproto/driver';
2
2
  import { createHash } from 'crypto';
3
+ import { execFile } from 'child_process';
4
+ import { stat, readFile, mkdir, writeFile, appendFile, readdir } from 'fs/promises';
5
+ import { join, dirname, isAbsolute, basename, resolve } from 'path';
6
+ import { assertKnownStepRefs } from '@agentproto/workflow';
7
+ import { isRefinedKind, filterEntriesByAllOf, resolveKnowledge } from '@agentproto/corpus';
3
8
  import { parseWorkflowManifest, workflowFromManifest } from '@agentproto/workflow/manifest';
4
9
 
5
10
  /**
@@ -7,6 +12,663 @@ import { parseWorkflowManifest, workflowFromManifest } from '@agentproto/workflo
7
12
  * AIP-15 step-walker over @agentproto/driver runTool.
8
13
  */
9
14
 
15
+
16
+ // src/build-agent-step.ts
17
+ function buildAgentStep(id, fields) {
18
+ const prompt = fields.prompt;
19
+ return {
20
+ kind: "agent",
21
+ id,
22
+ ...fields.adapter !== void 0 ? { adapter: fields.adapter } : {},
23
+ ...fields.sessionRef !== void 0 ? { sessionRef: fields.sessionRef } : {},
24
+ ...fields.sandbox !== void 0 ? { sandbox: fields.sandbox } : {},
25
+ ...fields.cacheable ? { cacheable: true } : {},
26
+ ...fields.options !== void 0 ? { options: fields.options } : {},
27
+ prompt: typeof prompt === "function" ? prompt : ((_b) => prompt),
28
+ policy: fields.policy ?? { awaiting: "fail" },
29
+ ...fields.outputSchema !== void 0 ? { outputSchema: fields.outputSchema } : {},
30
+ ...fields.maxRetries !== void 0 ? { maxRetries: fields.maxRetries } : {},
31
+ ...fields.harness !== void 0 ? { harness: fields.harness } : {}
32
+ };
33
+ }
34
+
35
+ // src/compile-workflow.ts
36
+ var WorkflowCompileError = class extends Error {
37
+ constructor(message) {
38
+ super(`compileWorkflow: ${message}`);
39
+ this.name = "WorkflowCompileError";
40
+ }
41
+ };
42
+ var get = (m, key) => m instanceof Map ? m.get(key) : m[key];
43
+ function dig(value, path) {
44
+ let v = value;
45
+ for (const seg of path) {
46
+ if (v == null) return void 0;
47
+ v = v[seg];
48
+ }
49
+ return v;
50
+ }
51
+ var REF_RE = /^\$(input|item|steps)((?:\.[^.]+)*)$/;
52
+ var REF_PREFIX_RE = /^\$(?:input|item|steps|index)((?:\.[^.$/]+)*)/;
53
+ function resolveRef(ref, b) {
54
+ if (ref === "$index") return b.index;
55
+ const m = ref.match(REF_RE);
56
+ if (!m) throw new WorkflowCompileError(`bad reference '${ref}'`);
57
+ const segs = m[2] ? m[2].slice(1).split(".") : [];
58
+ switch (m[1]) {
59
+ case "input":
60
+ return dig(b.input, segs);
61
+ case "item":
62
+ return dig(b.item, segs);
63
+ case "steps": {
64
+ const [stepId, ...rest] = segs;
65
+ if (!stepId) throw new WorkflowCompileError(`'$steps' needs a step id`);
66
+ return dig(b.steps[stepId], rest);
67
+ }
68
+ }
69
+ }
70
+ function resolveRefPrefixed(value, b) {
71
+ const m = value.match(REF_PREFIX_RE);
72
+ if (!m) return void 0;
73
+ const token = value.slice(0, m[0].length);
74
+ return { resolved: resolveRef(token, b), rest: value.slice(m[0].length) };
75
+ }
76
+ function resolveValue(node, b) {
77
+ if (typeof node === "string") {
78
+ if (node.startsWith("$$")) return node.slice(1);
79
+ if (node.startsWith("$")) return resolveRef(node, b);
80
+ return node;
81
+ }
82
+ if (Array.isArray(node)) return node.map((n) => resolveValue(n, b));
83
+ if (node && typeof node === "object") {
84
+ const out = {};
85
+ for (const [k, v] of Object.entries(node))
86
+ out[k] = resolveValue(v, b);
87
+ return out;
88
+ }
89
+ return node;
90
+ }
91
+ function refPathExists(ref, b) {
92
+ const m = ref.match(REF_RE);
93
+ if (!m) return false;
94
+ const segs = m[2] ? m[2].slice(1).split(".") : [];
95
+ let v;
96
+ if (m[1] === "steps") {
97
+ const [stepId, ...rest] = segs;
98
+ if (!stepId || !(stepId in b.steps)) return false;
99
+ v = b.steps[stepId];
100
+ segs.length = 0;
101
+ segs.push(...rest);
102
+ } else {
103
+ v = m[1] === "input" ? b.input : b.item;
104
+ }
105
+ for (const seg of segs) {
106
+ if (v === null || typeof v !== "object") return false;
107
+ const o = v;
108
+ if (!(seg in o)) return false;
109
+ v = o[seg];
110
+ }
111
+ return true;
112
+ }
113
+ function resolveValueStrict(node, b, label) {
114
+ if (typeof node === "string") {
115
+ if (node.startsWith("$$")) return node.slice(1);
116
+ if (node.startsWith("$")) {
117
+ const v = resolveRef(node, b);
118
+ if (v === void 0 && !refPathExists(node, b)) {
119
+ throw new WorkflowCompileError(
120
+ `${label}: reference '${node}' resolves to nothing \u2014 the referenced field does not exist`
121
+ );
122
+ }
123
+ return v;
124
+ }
125
+ return node;
126
+ }
127
+ if (Array.isArray(node)) return node.map((n) => resolveValueStrict(n, b, label));
128
+ if (node && typeof node === "object") {
129
+ const out = {};
130
+ for (const [k, v] of Object.entries(node))
131
+ out[k] = resolveValueStrict(v, b, label);
132
+ return out;
133
+ }
134
+ return node;
135
+ }
136
+ var COMPARATORS = {
137
+ "<": (a, c) => a < c,
138
+ "<=": (a, c) => a <= c,
139
+ ">": (a, c) => a > c,
140
+ ">=": (a, c) => a >= c
141
+ };
142
+ function literal(token, b) {
143
+ if (token.startsWith("$")) return resolveRef(token, b);
144
+ if (token === "true") return true;
145
+ if (token === "false") return false;
146
+ if (token === "null") return null;
147
+ if (/^-?\d+(\.\d+)?$/.test(token)) return Number(token);
148
+ return token.replace(/^['"]|['"]$/g, "");
149
+ }
150
+ function evalPredicate(expr, b) {
151
+ const m = expr.match(/^(.+?)\s*(==|!=|<=|>=|<|>)\s*(.+)$/);
152
+ if (!m) return Boolean(literal(expr.trim(), b));
153
+ const left = literal(m[1].trim(), b);
154
+ const right = literal(m[3].trim(), b);
155
+ switch (m[2]) {
156
+ case "==":
157
+ return left === right;
158
+ case "!=":
159
+ return left !== right;
160
+ default: {
161
+ const cmp = COMPARATORS[m[2]];
162
+ return cmp(Number(left), Number(right));
163
+ }
164
+ }
165
+ }
166
+ function normalizeToolId(ref) {
167
+ let s = ref.trim();
168
+ if (s.endsWith("/TOOL.md")) s = s.slice(0, -"/TOOL.md".length);
169
+ const slash = s.lastIndexOf("/");
170
+ return slash === -1 ? s : s.slice(slash + 1);
171
+ }
172
+ var f = (step, key) => step[key];
173
+ function assertLinear(steps) {
174
+ steps.forEach((s, i) => {
175
+ const next = s.next;
176
+ const followingId = steps[i + 1]?.id;
177
+ if (next && next !== followingId) {
178
+ throw new WorkflowCompileError(
179
+ `step '${s.id}' has a non-linear next='${next}' \u2014 the structured-subset compiler only runs steps in document order. Hand-author a BranchStep or nest structurally.`
180
+ );
181
+ }
182
+ });
183
+ }
184
+ function collectStepIds(steps, ids = /* @__PURE__ */ new Set()) {
185
+ for (const s of steps) {
186
+ if (typeof s?.id === "string") ids.add(s.id);
187
+ if (Array.isArray(s?.steps)) collectStepIds(s.steps, ids);
188
+ if (Array.isArray(s?.branches)) {
189
+ for (const br of s.branches) {
190
+ if (Array.isArray(br?.steps)) collectStepIds(br.steps, ids);
191
+ }
192
+ }
193
+ }
194
+ return ids;
195
+ }
196
+ function compileWorkflow(handle, opts) {
197
+ const steps = handle.steps;
198
+ if (handle.start && handle.start !== steps[0]?.id) {
199
+ throw new WorkflowCompileError(
200
+ `start='${handle.start}' is not the first step \u2014 structured-subset runs in order`
201
+ );
202
+ }
203
+ const ctx = { opts, knownStepIds: collectStepIds(steps) };
204
+ const result = handle.result;
205
+ const output = result !== void 0 ? (b) => resolveValue(result, b) : void 0;
206
+ return {
207
+ id: handle.id,
208
+ description: handle.description,
209
+ steps: compileSiblingsToSteps(steps, ctx),
210
+ ...output ? { output } : {}
211
+ };
212
+ }
213
+ function compileSiblingsToSteps(steps, ctx) {
214
+ assertLinear(steps);
215
+ const out = [];
216
+ for (let i = 0; i < steps.length; i++) {
217
+ const s = steps[i];
218
+ if (s.kind === "branch") {
219
+ out.push(compileBranchChain(steps, i, ctx));
220
+ return out;
221
+ }
222
+ out.push(compileStep(s, ctx));
223
+ }
224
+ return out;
225
+ }
226
+ function compileStepList(steps, id, ctx) {
227
+ const compiled = compileSiblingsToSteps(steps, ctx);
228
+ if (compiled.length === 1) return compiled[0];
229
+ return { kind: "group", id, steps: compiled };
230
+ }
231
+ function compileBranchChain(steps, i, ctx) {
232
+ const branchStep = steps[i];
233
+ const branches = f(branchStep, "branches");
234
+ const defaultTarget = f(branchStep, "default");
235
+ const resolveTarget = (targetId, label) => {
236
+ const idx = steps.findIndex((s) => s.id === targetId);
237
+ if (idx === -1 || idx <= i) {
238
+ throw new WorkflowCompileError(
239
+ `branch '${branchStep.id}' ${label} targets '${targetId}', which is not a sibling later in this step list \u2014 the forward-only branch compiler only supports jumping to a later sibling in the SAME list (not backward, and not into a nested map/loop/parallel body). Hand-author a 'loop' step for retry-style control flow instead.`
240
+ );
241
+ }
242
+ return idx;
243
+ };
244
+ const defaultIdx = defaultTarget !== void 0 ? resolveTarget(defaultTarget, "default") : i + 1;
245
+ let otherwise = compileSiblingsToSteps(steps.slice(defaultIdx), ctx);
246
+ for (let k = branches.length - 1; k >= 0; k--) {
247
+ const br = branches[k];
248
+ const targetIdx = resolveTarget(br.next, `branches[${k}].next`);
249
+ const thenSteps = compileSiblingsToSteps(steps.slice(targetIdx), ctx);
250
+ const condExpr = br.when;
251
+ const nodeId = k === 0 ? branchStep.id : `${branchStep.id}__branch${k}`;
252
+ otherwise = [
253
+ {
254
+ kind: "branch",
255
+ id: nodeId,
256
+ cond: (b) => evalPredicate(condExpr, b),
257
+ then: thenSteps,
258
+ ...otherwise.length > 0 ? { otherwise } : {}
259
+ }
260
+ ];
261
+ }
262
+ return otherwise[0];
263
+ }
264
+ function compileAgentStep(step, id, ctx) {
265
+ const opts = ctx.opts;
266
+ const prompt = f(step, "prompt");
267
+ if (typeof prompt !== "string" || prompt.length === 0) {
268
+ throw new WorkflowCompileError(`agent step '${id}' needs a non-empty 'prompt'`);
269
+ }
270
+ let adapter = typeof step.adapter === "string" ? step.adapter : void 0;
271
+ let options = step.options !== void 0 ? step.options : void 0;
272
+ const agentRef = step.agent?.ref;
273
+ if (agentRef !== void 0) {
274
+ if (typeof agentRef !== "string" || agentRef.trim().length === 0) {
275
+ throw new WorkflowCompileError(`agent step '${id}' has an empty 'agent.ref'`);
276
+ }
277
+ const resolved = opts.agentRefs ? get(opts.agentRefs, agentRef) : void 0;
278
+ if (!resolved) {
279
+ const available = opts.agentRefs ? opts.agentRefs instanceof Map ? [...opts.agentRefs.keys()] : Object.keys(opts.agentRefs) : [];
280
+ throw new WorkflowCompileError(
281
+ `agent step '${id}' references unknown agent ref '${agentRef}'` + (available.length > 0 ? ` \u2014 available refs: ${available.join(", ")}` : " \u2014 no agent refs are configured for this compile (not running in an app context?)")
282
+ );
283
+ }
284
+ adapter = resolved.adapter;
285
+ options = resolved.options;
286
+ }
287
+ return buildAgentStep(id, {
288
+ prompt: (b) => String(resolveValue(prompt, b)),
289
+ ...adapter !== void 0 ? { adapter } : {},
290
+ ...step.sessionRef !== void 0 ? { sessionRef: step.sessionRef } : {},
291
+ ...step.sandbox !== void 0 ? { sandbox: step.sandbox } : {},
292
+ ...step.cacheable ? { cacheable: true } : {},
293
+ ...options !== void 0 ? { options } : {},
294
+ policy: step.policy,
295
+ ...step.outputSchema !== void 0 ? { outputSchema: step.outputSchema } : {},
296
+ ...step.maxRetries !== void 0 ? { maxRetries: step.maxRetries } : {},
297
+ ...step.harness !== void 0 ? { harness: step.harness } : {}
298
+ });
299
+ }
300
+ function compileGateStep(step, id) {
301
+ const command = f(step, "command");
302
+ if (typeof command !== "string" || command.trim().length === 0) {
303
+ throw new WorkflowCompileError(`gate step '${id}' needs a non-empty 'command'`);
304
+ }
305
+ const args = f(step, "args");
306
+ const cwd = f(step, "cwd");
307
+ const reportPath = f(step, "report");
308
+ const timeoutMs = f(step, "timeout_ms");
309
+ const retry = f(step, "retry");
310
+ const onFail = f(
311
+ step,
312
+ "on_fail"
313
+ );
314
+ return {
315
+ kind: "gate",
316
+ id,
317
+ command,
318
+ ...args !== void 0 ? { args } : {},
319
+ ...cwd !== void 0 ? { cwd } : {},
320
+ ...reportPath !== void 0 ? { reportPath } : {},
321
+ ...timeoutMs !== void 0 ? { timeoutMs } : {},
322
+ ...retry !== void 0 ? {
323
+ retry: {
324
+ maxAttempts: retry.max_attempts,
325
+ backoff: retry.backoff,
326
+ ...retry.initial_ms !== void 0 ? { initialMs: retry.initial_ms } : {}
327
+ }
328
+ } : {},
329
+ ...onFail !== void 0 ? { onFail: { reprompt: onFail.reprompt, ...onFail.with !== void 0 ? { with: onFail.with } : {} } } : {}
330
+ };
331
+ }
332
+ function compileStep(step, ctx) {
333
+ const opts = ctx.opts;
334
+ const id = step.id;
335
+ switch (step.kind) {
336
+ case "tool": {
337
+ const ref = typeof step.tool === "string" ? step.tool : step.tool.entry;
338
+ const toolId = normalizeToolId(ref);
339
+ const tool = get(opts.tools, toolId);
340
+ if (!tool)
341
+ throw new WorkflowCompileError(`no tool registered for '${toolId}'`);
342
+ const inputs = f(step, "inputs") ?? {};
343
+ assertKnownStepRefs(inputs, ctx.knownStepIds, `tool step '${id}' inputs`, {
344
+ makeError: (message) => new WorkflowCompileError(message)
345
+ });
346
+ return {
347
+ kind: "tool",
348
+ id,
349
+ tool,
350
+ candidates: opts.candidates,
351
+ input: (b) => resolveValue(inputs, b),
352
+ context: opts.contextFor ? (b) => opts.contextFor(toolId, b) : void 0
353
+ };
354
+ }
355
+ case "map": {
356
+ const over = f(step, "over");
357
+ const inner = compileStepList(f(step, "steps"), `${id}__body`, ctx);
358
+ const onError = f(step, "onError");
359
+ return {
360
+ kind: "map",
361
+ id,
362
+ parallelism: f(step, "parallelism"),
363
+ over: (b) => resolveRef(over, b),
364
+ body: () => inner,
365
+ ...onError !== void 0 ? { onError } : {}
366
+ };
367
+ }
368
+ case "loop": {
369
+ const whileExpr = f(step, "while");
370
+ return {
371
+ kind: "loop",
372
+ id,
373
+ maxIterations: f(step, "max_iterations"),
374
+ while: (b) => evalPredicate(whileExpr, b),
375
+ body: [compileStepList(f(step, "steps"), `${id}__body`, ctx)]
376
+ };
377
+ }
378
+ case "parallel": {
379
+ const branches = f(step, "branches");
380
+ return {
381
+ kind: "parallel",
382
+ id,
383
+ branches: branches.map((br) => ({
384
+ id: br.id,
385
+ steps: [compileStepList(br.steps, `${id}__${br.id}`, ctx)]
386
+ }))
387
+ };
388
+ }
389
+ case "approval": {
390
+ const prompt = f(step, "prompt");
391
+ const approvers = (f(step, "approvers") ?? []).map(
392
+ (a) => a.role ?? a.userId ?? String(a)
393
+ );
394
+ const artifacts = f(step, "artifacts");
395
+ const timeoutMs = f(step, "timeout_ms");
396
+ return {
397
+ kind: "approval",
398
+ id,
399
+ approvers,
400
+ ...artifacts !== void 0 ? { artifacts } : {},
401
+ ...timeoutMs !== void 0 ? { timeoutMs } : {},
402
+ prompt: (b) => String(resolveValue(prompt, b))
403
+ };
404
+ }
405
+ case "suspend": {
406
+ const resume = f(step, "resume");
407
+ return { kind: "suspend", id, on: resume.on, timeoutMs: resume.timeout_ms };
408
+ }
409
+ case "subworkflow": {
410
+ const wfId = f(step, "workflow");
411
+ const child = opts.workflows ? get(opts.workflows, wfId) : void 0;
412
+ if (!child)
413
+ throw new WorkflowCompileError(
414
+ `subworkflow '${wfId}' not found \u2014 pass it in opts.workflows`
415
+ );
416
+ const compiledChild = compileWorkflow(child, opts);
417
+ const inputs = f(step, "inputs");
418
+ if (inputs === void 0) {
419
+ return { kind: "subworkflow", id, workflow: compiledChild };
420
+ }
421
+ assertKnownStepRefs(inputs, ctx.knownStepIds, `subworkflow step '${id}' inputs`, {
422
+ makeError: (message) => new WorkflowCompileError(message)
423
+ });
424
+ const entries = Object.entries(inputs);
425
+ return {
426
+ kind: "subworkflow",
427
+ id,
428
+ workflow: compiledChild,
429
+ // Each top-level key resolves strictly: a missing referenced field
430
+ // throws at run time naming this step + key (no silent `undefined`).
431
+ input: (b) => {
432
+ const out = {};
433
+ for (const [k, v] of entries)
434
+ out[k] = resolveValueStrict(v, b, `subworkflow step '${id}' input key '${k}'`);
435
+ return out;
436
+ }
437
+ };
438
+ }
439
+ case "agent": {
440
+ if (typeof step.prompt === "function") {
441
+ const passthrough = step;
442
+ return passthrough;
443
+ }
444
+ return compileAgentStep(step, id, ctx);
445
+ }
446
+ case "gate":
447
+ return compileGateStep(step, id);
448
+ case "transform": {
449
+ const passthrough = step;
450
+ return passthrough;
451
+ }
452
+ case "branch":
453
+ throw new WorkflowCompileError(
454
+ `internal: branch step '${id}' reached compileStep directly \u2014 branch steps must be compiled via the sibling-list walker`
455
+ );
456
+ default:
457
+ throw new WorkflowCompileError(
458
+ `unsupported step kind '${step.kind}' (step '${id}')`
459
+ );
460
+ }
461
+ }
462
+
463
+ // src/ref-string.ts
464
+ function resolveRefString(stepId, field, value, b, onUnmatched = "passthrough") {
465
+ if (value.startsWith("$$")) return value.slice(1);
466
+ let hit;
467
+ try {
468
+ hit = resolveRefPrefixed(value, b);
469
+ } catch (err) {
470
+ throw new Error(
471
+ `step '${stepId}': ${field} '${value}' is not a valid reference \u2014 ${err instanceof Error ? err.message : String(err)}`
472
+ );
473
+ }
474
+ if (!hit) {
475
+ if (onUnmatched === "error" && value.startsWith("$")) {
476
+ throw new Error(
477
+ `step '${stepId}': ${field} '${value}' is not a valid reference \u2014 a leading '$' must open a $input/$item/$steps.<id>/$index reference ('$$' escapes a literal '$')`
478
+ );
479
+ }
480
+ return value;
481
+ }
482
+ const token = value.slice(0, value.length - hit.rest.length);
483
+ if (hit.resolved === void 0) {
484
+ throw new Error(
485
+ `step '${stepId}': ${field} '${token}' resolves to nothing \u2014 the referenced field does not exist`
486
+ );
487
+ }
488
+ return String(hit.resolved) + hit.rest;
489
+ }
490
+ var NodeFsPort = class {
491
+ root;
492
+ constructor(root) {
493
+ this.root = root;
494
+ }
495
+ resolve(p) {
496
+ const normalized = p.replace(/^\/+/, "");
497
+ if (normalized.startsWith("..")) {
498
+ throw new Error(`NodeFsPort: path "${p}" escapes the workspace root "${this.root}"`);
499
+ }
500
+ return join(this.root, normalized);
501
+ }
502
+ async exists(p) {
503
+ try {
504
+ await stat(this.resolve(p));
505
+ return true;
506
+ } catch {
507
+ return false;
508
+ }
509
+ }
510
+ async readFile(p) {
511
+ return readFile(this.resolve(p), "utf8");
512
+ }
513
+ async writeFile(p, content) {
514
+ const target = this.resolve(p);
515
+ await mkdir(dirname(target), { recursive: true });
516
+ await writeFile(target, content, "utf8");
517
+ }
518
+ async appendFile(p, content) {
519
+ const target = this.resolve(p);
520
+ await mkdir(dirname(target), { recursive: true });
521
+ await appendFile(target, content, "utf8");
522
+ }
523
+ async readdir(p) {
524
+ return readdir(this.resolve(p));
525
+ }
526
+ async walk(p) {
527
+ const out = [];
528
+ const visit = async (dir) => {
529
+ let entries = [];
530
+ try {
531
+ entries = await readdir(dir, { withFileTypes: true });
532
+ } catch {
533
+ return;
534
+ }
535
+ for (const ent of entries) {
536
+ if (ent.name.startsWith(".")) continue;
537
+ const full = join(dir, ent.name);
538
+ if (ent.isDirectory()) {
539
+ await visit(full);
540
+ } else if (ent.isFile()) {
541
+ out.push(full);
542
+ }
543
+ }
544
+ };
545
+ await visit(this.resolve(p));
546
+ const prefix = this.resolve(p);
547
+ return out.map((f2) => f2.slice(prefix.length).replace(/^\//, ""));
548
+ }
549
+ async stat(p) {
550
+ try {
551
+ const s = await stat(this.resolve(p));
552
+ return {
553
+ kind: s.isDirectory() ? "directory" : "file",
554
+ ...s.isFile() ? { bytes: s.size } : {},
555
+ modifiedAt: s.mtime
556
+ };
557
+ } catch {
558
+ return null;
559
+ }
560
+ }
561
+ async lock() {
562
+ return { release: async () => {
563
+ } };
564
+ }
565
+ };
566
+
567
+ // src/knowledge.ts
568
+ var DEFAULT_MAX_ENTRIES = 50;
569
+ function resolveStringArray(stepId, index, field, values, b) {
570
+ if (values === void 0) return void 0;
571
+ return values.map(
572
+ (v) => resolveRefString(stepId, `harness.knowledge[${index}].${field}`, v, b)
573
+ );
574
+ }
575
+ function resolveKnowledgeSelectors(stepId, selectors, b) {
576
+ return selectors.map((sel, index) => {
577
+ const deferred = sel.deferred === true;
578
+ const workspace = deferred ? resolveRefString(stepId, `harness.knowledge[${index}].workspace`, sel.workspace, b) : sel.workspace;
579
+ return {
580
+ ...sel,
581
+ deferred: void 0,
582
+ workspace,
583
+ ...sel.anyOf !== void 0 ? { anyOf: deferred ? resolveStringArray(stepId, index, "anyOf", sel.anyOf, b) : [...sel.anyOf] } : {},
584
+ ...sel.allOf !== void 0 ? { allOf: deferred ? resolveStringArray(stepId, index, "allOf", sel.allOf, b) : [...sel.allOf] } : {},
585
+ ...sel.kinds !== void 0 ? { kinds: deferred ? resolveStringArray(stepId, index, "kinds", sel.kinds, b) : [...sel.kinds] } : {}
586
+ };
587
+ });
588
+ }
589
+ function workspaceDirName(workspace) {
590
+ const trimmed = workspace.replace(/\/+$/, "");
591
+ return basename(trimmed) || trimmed;
592
+ }
593
+ function safeSlug(slug) {
594
+ return slug.replace(/[^a-zA-Z0-9._-]+/g, "-");
595
+ }
596
+ async function materializeKnowledge(stepId, selectors, stepCwd) {
597
+ const knowledgeDir = join(stepCwd, ".knowledge");
598
+ const records = [];
599
+ const warnings = [];
600
+ const indexSections = [];
601
+ let written = 0;
602
+ for (const [i, sel] of selectors.entries()) {
603
+ if (sel.mode !== void 0 && sel.mode !== "files") {
604
+ throw new Error(
605
+ `step '${stepId}': harness.knowledge[${i}].mode must be "files" (v1 supports no other mode)`
606
+ );
607
+ }
608
+ const workspace = sel.workspace;
609
+ if (typeof workspace !== "string" || workspace.length === 0) {
610
+ throw new Error(
611
+ `step '${stepId}': harness.knowledge[${i}] needs a non-empty 'workspace'`
612
+ );
613
+ }
614
+ if (!isAbsolute(workspace)) {
615
+ throw new Error(
616
+ `step '${stepId}': harness.knowledge[${i}].workspace '${workspace}' must be absolute by run time \u2014 the loader resolves relative paths; did you bypass loadWorkflowHandle?`
617
+ );
618
+ }
619
+ const fs = new NodeFsPort(workspace);
620
+ const rootStat = await fs.stat(".");
621
+ if (rootStat === null || rootStat.kind !== "directory") {
622
+ const warning = `knowledge-workspace-missing: harness.knowledge[${i}] (workspace '${workspace}') does not name an existing directory`;
623
+ warnings.push(warning);
624
+ records.push({ workspace, matched: 0, written: 0 });
625
+ continue;
626
+ }
627
+ const kinds = (sel.kinds ?? []).filter((k) => isRefinedKind(k));
628
+ const resolved = filterEntriesByAllOf(
629
+ await resolveKnowledge({
630
+ fs,
631
+ query: {
632
+ ...sel.anyOf && sel.anyOf.length > 0 ? { tags: [...sel.anyOf] } : {},
633
+ ...kinds.length > 0 ? { kinds } : {}
634
+ }
635
+ }),
636
+ sel.allOf
637
+ );
638
+ const sorted = [...resolved].sort((a, b) => a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0);
639
+ const cap = sel.maxEntries ?? DEFAULT_MAX_ENTRIES;
640
+ const picked = sorted.slice(0, cap);
641
+ const dirName = workspaceDirName(workspace);
642
+ const lines = [];
643
+ for (const entry of picked) {
644
+ const rel = join(dirName, `${safeSlug(entry.slug)}.md`);
645
+ const raw = await fs.readFile(entry.path);
646
+ await mkdir(join(knowledgeDir, dirName), { recursive: true });
647
+ await writeFile(join(knowledgeDir, rel), raw, "utf8");
648
+ lines.push(
649
+ `- [${entry.title || entry.slug}](${rel.split("\\").join("/")}) \u2014 ${entry.kind}, ${entry.tags.join(", ")}`
650
+ );
651
+ }
652
+ written += picked.length;
653
+ if (lines.length > 0) {
654
+ indexSections.push(`## ${dirName}
655
+
656
+ ${lines.join("\n")}`);
657
+ }
658
+ records.push({ workspace, matched: resolved.length, written: picked.length });
659
+ if (resolved.length === 0) {
660
+ warnings.push(
661
+ `knowledge-empty: harness.knowledge[${i}] (workspace '${workspace}') matched 0 entries`
662
+ );
663
+ }
664
+ }
665
+ const index = "# Knowledge index\n\n" + (indexSections.length > 0 ? indexSections.join("\n\n") : "_No entries matched._") + "\n";
666
+ await mkdir(knowledgeDir, { recursive: true });
667
+ await writeFile(join(knowledgeDir, "INDEX.md"), index, "utf8");
668
+ return { records, written, warnings };
669
+ }
670
+
671
+ // src/run-workflow.ts
10
672
  var WorkflowSuspendedError = class extends Error {
11
673
  constructor(stepId, on) {
12
674
  super(
@@ -86,16 +748,53 @@ async function execAgentStep(step, ctx, b) {
86
748
  `step '${step.id}': budget_exceeded \u2014 run spend $${spentUsd(ctx.state).toFixed(4)} >= cap $${ctx.state.maxTotalCostUsd}`
87
749
  );
88
750
  }
89
- const cwd = step.cwd ? resolveSel(step.cwd, b) : ctx.cwd;
751
+ const cwd = step.harness?.cwd ?? (step.cwd ? resolveSel(step.cwd, b) : ctx.cwd);
752
+ let knowledgeOut;
753
+ let knowledgeWarnings = [];
754
+ if (step.harness?.knowledge && step.harness.knowledge.length > 0) {
755
+ if (cwd === void 0) {
756
+ throw new Error(
757
+ `step '${step.id}': harness.knowledge requires a resolvable working directory (set step cwd, harness.cwd, or the run cwd)`
758
+ );
759
+ }
760
+ const knowledgeSelectors = resolveKnowledgeSelectors(step.id, step.harness.knowledge, b).map(
761
+ (sel) => ({
762
+ ...sel,
763
+ workspace: isAbsolute(sel.workspace) ? sel.workspace : join(cwd, sel.workspace)
764
+ })
765
+ );
766
+ const materialized = await materializeKnowledge(step.id, knowledgeSelectors, cwd);
767
+ knowledgeOut = materialized.records;
768
+ knowledgeWarnings = materialized.warnings;
769
+ if (materialized.written > 0) {
770
+ const note = `Knowledge for this step is materialized under .knowledge/ (see .knowledge/INDEX.md, ${materialized.written} entries).`;
771
+ const inner = step.prompt;
772
+ step = { ...step, prompt: (b2) => `${note}
773
+
774
+ ${inner(b2)}` };
775
+ }
776
+ }
90
777
  const sandbox = typeof step.sandbox === "function" ? step.sandbox(b) : step.sandbox;
91
778
  const sessionId = step.adapter ? await ctx.agents.spawn(resolveSel(step.adapter, b), {
92
779
  cwd,
93
780
  workspaceSlug: ctx.workspaceSlug,
94
781
  stepId: step.id,
95
782
  ...sandbox !== void 0 ? { sandbox } : {},
96
- ...step.options !== void 0 ? { options: step.options } : {}
783
+ ...step.options !== void 0 ? { options: step.options } : {},
784
+ ...step.harness !== void 0 ? { harness: step.harness } : {}
97
785
  }) : ctx.agents.resolveByLabel(step.sessionRef);
98
786
  if (!sessionId) throw new Error(`step '${step.id}': no session (adapter and sessionRef both unresolved)`);
787
+ if (knowledgeWarnings.length > 0 && ctx.agents.emitHarnessWarning) {
788
+ ctx.agents.emitHarnessWarning({
789
+ sessionId,
790
+ warnings: knowledgeWarnings,
791
+ label: step.id
792
+ });
793
+ }
794
+ const harnessOut = step.harness !== void 0 ? {
795
+ ...step.harness,
796
+ ...step.harness.tools && step.harness.tools.length > 0 ? { toolsApplied: false } : {}
797
+ } : void 0;
99
798
  await ctx.agents.sendPromptAndWait(sessionId, step.prompt(b));
100
799
  if (step.policy && ctx.agents.onAwaitingInput) {
101
800
  await ctx.agents.onAwaitingInput(sessionId, step.policy);
@@ -111,7 +810,7 @@ async function execAgentStep(step, ctx, b) {
111
810
  } catch {
112
811
  }
113
812
  }
114
- return { sessionId, ...text !== void 0 ? { text } : {} };
813
+ return { sessionId, ...text !== void 0 ? { text } : {}, ...harnessOut ? { harness: harnessOut } : {}, ...knowledgeOut ? { knowledgeApplied: knowledgeOut } : {} };
115
814
  }
116
815
  if (!ctx.agents.readFinalMessage) {
117
816
  throw new Error(`step '${step.id}': outputSchema requires a host with readFinalMessage`);
@@ -135,7 +834,7 @@ async function execAgentStep(step, ctx, b) {
135
834
  continue;
136
835
  }
137
836
  const res = step.outputSchema.safeParse(value);
138
- if (res.success) return { sessionId, output: res.data };
837
+ if (res.success) return { sessionId, output: res.data, ...harnessOut ? { harness: harnessOut } : {}, ...knowledgeOut ? { knowledgeApplied: knowledgeOut } : {} };
139
838
  lastErr = formatZodError(res.error);
140
839
  if (attempt < maxRetries) {
141
840
  await ctx.agents.sendPromptAndWait(
@@ -146,6 +845,109 @@ async function execAgentStep(step, ctx, b) {
146
845
  }
147
846
  throw new Error(`step '${step.id}': output_invalid \u2014 final message never matched outputSchema (${lastErr})`);
148
847
  }
848
+ function tryParseJson(text) {
849
+ const trimmed = text.trim();
850
+ if (!trimmed) return void 0;
851
+ try {
852
+ return JSON.parse(trimmed);
853
+ } catch {
854
+ return void 0;
855
+ }
856
+ }
857
+ function defaultRunGateCommand(spec) {
858
+ return new Promise((resolve2) => {
859
+ execFile(
860
+ spec.command,
861
+ [...spec.args],
862
+ { cwd: spec.cwd, timeout: spec.timeoutMs, maxBuffer: 10 * 1024 * 1024 },
863
+ (err, stdout, stderr) => {
864
+ if (!err) {
865
+ resolve2({ exitCode: 0, stdout, stderr });
866
+ return;
867
+ }
868
+ const nodeErr = err;
869
+ const timedOut = nodeErr.killed === true && nodeErr.signal !== void 0 && spec.timeoutMs !== void 0;
870
+ const exitCode = typeof nodeErr.code === "number" ? nodeErr.code : 1;
871
+ resolve2({ exitCode, stdout, stderr, ...timedOut ? { timedOut: true } : {} });
872
+ }
873
+ );
874
+ });
875
+ }
876
+ async function resolveGateReport(cmd, cwd, reportPath) {
877
+ const fromStdout = tryParseJson(cmd.stdout);
878
+ if (fromStdout !== void 0) return fromStdout;
879
+ if (!reportPath) return void 0;
880
+ try {
881
+ const abs = isAbsolute(reportPath) ? reportPath : join(cwd, reportPath);
882
+ const raw = await readFile(abs, "utf8");
883
+ return tryParseJson(raw);
884
+ } catch {
885
+ return void 0;
886
+ }
887
+ }
888
+ function resolveGateCwd(step, ctx, b) {
889
+ const fallback = ctx.cwd ?? process.cwd();
890
+ if (!step.cwd) return fallback;
891
+ const resolved = resolveRefString(step.id, "cwd", resolveSel(step.cwd, b), b, "error");
892
+ return isAbsolute(resolved) ? resolved : resolve(fallback, resolved);
893
+ }
894
+ async function execGateStep(step, ctx, b) {
895
+ const cwd = resolveGateCwd(step, ctx, b);
896
+ const args = (step.args ?? []).map(
897
+ (arg, index) => resolveRefString(step.id, `args[${index}]`, resolveSel(arg, b), b, "error")
898
+ );
899
+ const maxAttempts = Math.max(1, step.retry?.maxAttempts ?? 1);
900
+ let last;
901
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
902
+ if (attempt > 1 && step.onFail?.reprompt) {
903
+ if (!ctx.agents) {
904
+ throw new Error(`step '${step.id}': on_fail.reprompt requires a host agents implementation`);
905
+ }
906
+ const targetSessionId = ctx.agents.resolveByLabel(step.onFail.reprompt);
907
+ if (!targetSessionId) {
908
+ throw new Error(
909
+ `step '${step.id}': on_fail.reprompt targets unknown step '${step.onFail.reprompt}' \u2014 no session found`
910
+ );
911
+ }
912
+ const reportText = JSON.stringify(last?.report ?? null, null, 2);
913
+ const extra = step.onFail.with ? `
914
+
915
+ Additional context:
916
+ ${JSON.stringify(step.onFail.with, null, 2)}` : "";
917
+ await ctx.agents.sendPromptAndWait(
918
+ targetSessionId,
919
+ `Gate '${step.id}' failed (exit code ${last?.exitCode}). Report:
920
+ ${reportText}${extra}
921
+
922
+ Please address the findings above, then reply when done.`
923
+ );
924
+ }
925
+ if (attempt > 1 && step.retry?.initialMs) {
926
+ const delayMs = step.retry.backoff === "exponential" ? step.retry.initialMs * 2 ** (attempt - 2) : step.retry.initialMs;
927
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
928
+ }
929
+ const cmdResult = await (ctx.runGateCommand ?? defaultRunGateCommand)({
930
+ command: step.command,
931
+ args,
932
+ cwd,
933
+ timeoutMs: step.timeoutMs
934
+ });
935
+ const report = await resolveGateReport(cmdResult, cwd, step.reportPath);
936
+ const ok = cmdResult.exitCode === 0;
937
+ last = { ok, exitCode: cmdResult.exitCode, report };
938
+ ctx.onGateReport?.({ stepId: step.id, ok, exitCode: cmdResult.exitCode, report, attempt });
939
+ if (ok) break;
940
+ }
941
+ if (!last.ok) {
942
+ const err = new Error(
943
+ `step '${step.id}': gate failed after ${maxAttempts} attempt(s) \u2014 exit code ${last.exitCode}`
944
+ );
945
+ err.exitCode = last.exitCode;
946
+ err.report = last.report;
947
+ throw err;
948
+ }
949
+ return last;
950
+ }
149
951
  async function execStep(step, ctx, item, index) {
150
952
  const { state, signal } = ctx;
151
953
  const b = view(state, item, index);
@@ -274,10 +1076,18 @@ async function execStep(step, ctx, item, index) {
274
1076
  case "approval": {
275
1077
  const prompt = step.prompt(b);
276
1078
  const approvers = step.approvers ?? [];
277
- const approved = ctx.approve ? await ctx.approve({ stepId: step.id, prompt, approvers }) : true;
1079
+ const raw = ctx.approve ? await ctx.approve({
1080
+ stepId: step.id,
1081
+ prompt,
1082
+ approvers,
1083
+ ...step.artifacts !== void 0 ? { artifacts: step.artifacts } : {},
1084
+ ...step.timeoutMs !== void 0 ? { timeoutMs: step.timeoutMs } : {}
1085
+ }) : true;
1086
+ const decision = typeof raw === "boolean" ? { approved: raw, who: "host" } : raw;
1087
+ const approved = decision.approved;
278
1088
  const followups = approved ? step.onApprove ?? [] : step.onReject ?? [];
279
1089
  await runSequence(followups, ctx, item, index);
280
- return { approved };
1090
+ return { approved, who: decision.who, ...decision.note !== void 0 ? { note: decision.note } : {} };
281
1091
  }
282
1092
  case "suspend": {
283
1093
  if (!ctx.resume) throw new WorkflowSuspendedError(step.id, step.on);
@@ -295,7 +1105,9 @@ async function execStep(step, ctx, item, index) {
295
1105
  cwd: ctx.cwd,
296
1106
  workspaceSlug: ctx.workspaceSlug,
297
1107
  cache: ctx.cache,
298
- cacheKey: ctx.cacheKey
1108
+ cacheKey: ctx.cacheKey,
1109
+ runGateCommand: ctx.runGateCommand,
1110
+ onGateReport: ctx.onGateReport
299
1111
  });
300
1112
  return child.output;
301
1113
  }
@@ -313,6 +1125,8 @@ async function execStep(step, ctx, item, index) {
313
1125
  await ctx.cache.set(c.key, { output: out, resolvedInputHash: c.hash });
314
1126
  return out;
315
1127
  }
1128
+ case "gate":
1129
+ return execGateStep(step, ctx, b);
316
1130
  }
317
1131
  }
318
1132
  async function runWorkflowInner(workflow, input, hooks, maxTotalCostUsd) {
@@ -340,286 +1154,17 @@ async function runWorkflow(args) {
340
1154
  cache: args.cache,
341
1155
  cacheKey: args.cacheKey,
342
1156
  onStepStart: args.onStepStart,
343
- onStepComplete: args.onStepComplete
1157
+ onStepComplete: args.onStepComplete,
1158
+ runGateCommand: args.runGateCommand,
1159
+ onGateReport: args.onGateReport
344
1160
  }, args.maxTotalCostUsd);
345
1161
  }
346
-
347
- // src/build-agent-step.ts
348
- function buildAgentStep(id, fields) {
349
- const prompt = fields.prompt;
350
- return {
351
- kind: "agent",
352
- id,
353
- ...fields.adapter !== void 0 ? { adapter: fields.adapter } : {},
354
- ...fields.sessionRef !== void 0 ? { sessionRef: fields.sessionRef } : {},
355
- ...fields.sandbox !== void 0 ? { sandbox: fields.sandbox } : {},
356
- ...fields.cacheable ? { cacheable: true } : {},
357
- ...fields.options !== void 0 ? { options: fields.options } : {},
358
- prompt: typeof prompt === "function" ? prompt : ((_b) => prompt),
359
- policy: fields.policy ?? { awaiting: "fail" },
360
- ...fields.outputSchema !== void 0 ? { outputSchema: fields.outputSchema } : {},
361
- ...fields.maxRetries !== void 0 ? { maxRetries: fields.maxRetries } : {}
362
- };
363
- }
364
-
365
- // src/compile-workflow.ts
366
- var WorkflowCompileError = class extends Error {
367
- constructor(message) {
368
- super(`compileWorkflow: ${message}`);
369
- this.name = "WorkflowCompileError";
370
- }
371
- };
372
- var get = (m, key) => m instanceof Map ? m.get(key) : m[key];
373
- function dig(value, path) {
374
- let v = value;
375
- for (const seg of path) {
376
- if (v == null) return void 0;
377
- v = v[seg];
378
- }
379
- return v;
380
- }
381
- function resolveRef(ref, b) {
382
- if (ref === "$index") return b.index;
383
- const m = ref.match(/^\$(input|item|steps)((?:\.[^.]+)*)$/);
384
- if (!m) throw new WorkflowCompileError(`bad reference '${ref}'`);
385
- const segs = m[2] ? m[2].slice(1).split(".") : [];
386
- switch (m[1]) {
387
- case "input":
388
- return dig(b.input, segs);
389
- case "item":
390
- return dig(b.item, segs);
391
- case "steps": {
392
- const [stepId, ...rest] = segs;
393
- if (!stepId) throw new WorkflowCompileError(`'$steps' needs a step id`);
394
- return dig(b.steps[stepId], rest);
395
- }
396
- }
397
- }
398
- function resolveValue(node, b) {
399
- if (typeof node === "string") {
400
- if (node.startsWith("$$")) return node.slice(1);
401
- if (node.startsWith("$")) return resolveRef(node, b);
402
- return node;
403
- }
404
- if (Array.isArray(node)) return node.map((n) => resolveValue(n, b));
405
- if (node && typeof node === "object") {
406
- const out = {};
407
- for (const [k, v] of Object.entries(node))
408
- out[k] = resolveValue(v, b);
409
- return out;
410
- }
411
- return node;
412
- }
413
- var COMPARATORS = {
414
- "<": (a, c) => a < c,
415
- "<=": (a, c) => a <= c,
416
- ">": (a, c) => a > c,
417
- ">=": (a, c) => a >= c
418
- };
419
- function literal(token, b) {
420
- if (token.startsWith("$")) return resolveRef(token, b);
421
- if (token === "true") return true;
422
- if (token === "false") return false;
423
- if (token === "null") return null;
424
- if (/^-?\d+(\.\d+)?$/.test(token)) return Number(token);
425
- return token.replace(/^['"]|['"]$/g, "");
426
- }
427
- function evalPredicate(expr, b) {
428
- const m = expr.match(/^(.+?)\s*(==|!=|<=|>=|<|>)\s*(.+)$/);
429
- if (!m) return Boolean(literal(expr.trim(), b));
430
- const left = literal(m[1].trim(), b);
431
- const right = literal(m[3].trim(), b);
432
- switch (m[2]) {
433
- case "==":
434
- return left === right;
435
- case "!=":
436
- return left !== right;
437
- default: {
438
- const cmp = COMPARATORS[m[2]];
439
- return cmp(Number(left), Number(right));
440
- }
441
- }
442
- }
443
- function normalizeToolId(ref) {
444
- let s = ref.trim();
445
- if (s.endsWith("/TOOL.md")) s = s.slice(0, -"/TOOL.md".length);
446
- const slash = s.lastIndexOf("/");
447
- return slash === -1 ? s : s.slice(slash + 1);
448
- }
449
- var f = (step, key) => step[key];
450
- function assertLinear(steps) {
451
- steps.forEach((s, i) => {
452
- const next = s.next;
453
- const followingId = steps[i + 1]?.id;
454
- if (next && next !== followingId) {
455
- throw new WorkflowCompileError(
456
- `step '${s.id}' has a non-linear next='${next}' \u2014 the structured-subset compiler only runs steps in document order. Hand-author a BranchStep or nest structurally.`
457
- );
458
- }
459
- });
460
- }
461
- function compileWorkflow(handle, opts) {
462
- const steps = handle.steps;
463
- if (handle.start && handle.start !== steps[0]?.id) {
464
- throw new WorkflowCompileError(
465
- `start='${handle.start}' is not the first step \u2014 structured-subset runs in order`
466
- );
467
- }
468
- assertLinear(steps);
469
- const result = handle.result;
470
- const output = result !== void 0 ? (b) => resolveValue(result, b) : void 0;
471
- return {
472
- id: handle.id,
473
- description: handle.description,
474
- steps: steps.map((s) => compileStep(s, opts)),
475
- ...output ? { output } : {}
476
- };
477
- }
478
- function compileStepList(steps, id, opts) {
479
- assertLinear(steps);
480
- if (steps.length === 1) return compileStep(steps[0], opts);
481
- return { kind: "group", id, steps: steps.map((s) => compileStep(s, opts)) };
482
- }
483
- function compileAgentStep(step, id, opts) {
484
- const prompt = f(step, "prompt");
485
- if (typeof prompt !== "string" || prompt.length === 0) {
486
- throw new WorkflowCompileError(`agent step '${id}' needs a non-empty 'prompt'`);
487
- }
488
- let adapter = typeof step.adapter === "string" ? step.adapter : void 0;
489
- let options = step.options !== void 0 ? step.options : void 0;
490
- const agentRef = step.agent?.ref;
491
- if (agentRef !== void 0) {
492
- if (typeof agentRef !== "string" || agentRef.trim().length === 0) {
493
- throw new WorkflowCompileError(`agent step '${id}' has an empty 'agent.ref'`);
494
- }
495
- const resolved = opts.agentRefs ? get(opts.agentRefs, agentRef) : void 0;
496
- if (!resolved) {
497
- const available = opts.agentRefs ? opts.agentRefs instanceof Map ? [...opts.agentRefs.keys()] : Object.keys(opts.agentRefs) : [];
498
- throw new WorkflowCompileError(
499
- `agent step '${id}' references unknown agent ref '${agentRef}'` + (available.length > 0 ? ` \u2014 available refs: ${available.join(", ")}` : " \u2014 no agent refs are configured for this compile (not running in an app context?)")
500
- );
501
- }
502
- adapter = resolved.adapter;
503
- options = resolved.options;
504
- }
505
- return buildAgentStep(id, {
506
- prompt: (b) => String(resolveValue(prompt, b)),
507
- ...adapter !== void 0 ? { adapter } : {},
508
- ...step.sessionRef !== void 0 ? { sessionRef: step.sessionRef } : {},
509
- ...step.sandbox !== void 0 ? { sandbox: step.sandbox } : {},
510
- ...step.cacheable ? { cacheable: true } : {},
511
- ...options !== void 0 ? { options } : {},
512
- policy: step.policy,
513
- ...step.outputSchema !== void 0 ? { outputSchema: step.outputSchema } : {},
514
- ...step.maxRetries !== void 0 ? { maxRetries: step.maxRetries } : {}
515
- });
516
- }
517
- function compileStep(step, opts) {
518
- const id = step.id;
519
- switch (step.kind) {
520
- case "tool": {
521
- const ref = typeof step.tool === "string" ? step.tool : step.tool.entry;
522
- const toolId = normalizeToolId(ref);
523
- const tool = get(opts.tools, toolId);
524
- if (!tool)
525
- throw new WorkflowCompileError(`no tool registered for '${toolId}'`);
526
- const inputs = f(step, "inputs") ?? {};
527
- return {
528
- kind: "tool",
529
- id,
530
- tool,
531
- candidates: opts.candidates,
532
- input: (b) => resolveValue(inputs, b),
533
- context: opts.contextFor ? (b) => opts.contextFor(toolId, b) : void 0
534
- };
535
- }
536
- case "map": {
537
- const over = f(step, "over");
538
- const inner = compileStepList(f(step, "steps"), `${id}__body`, opts);
539
- const onError = f(step, "onError");
540
- return {
541
- kind: "map",
542
- id,
543
- parallelism: f(step, "parallelism"),
544
- over: (b) => resolveRef(over, b),
545
- body: () => inner,
546
- ...onError !== void 0 ? { onError } : {}
547
- };
548
- }
549
- case "loop": {
550
- const whileExpr = f(step, "while");
551
- return {
552
- kind: "loop",
553
- id,
554
- maxIterations: f(step, "max_iterations"),
555
- while: (b) => evalPredicate(whileExpr, b),
556
- body: [compileStepList(f(step, "steps"), `${id}__body`, opts)]
557
- };
558
- }
559
- case "parallel": {
560
- const branches = f(step, "branches");
561
- return {
562
- kind: "parallel",
563
- id,
564
- branches: branches.map((br) => ({
565
- id: br.id,
566
- steps: [compileStepList(br.steps, `${id}__${br.id}`, opts)]
567
- }))
568
- };
569
- }
570
- case "approval": {
571
- const prompt = f(step, "prompt");
572
- const approvers = (f(step, "approvers") ?? []).map(
573
- (a) => a.role ?? a.userId ?? String(a)
574
- );
575
- return {
576
- kind: "approval",
577
- id,
578
- approvers,
579
- prompt: (b) => String(resolveValue(prompt, b))
580
- };
581
- }
582
- case "suspend": {
583
- const resume = f(step, "resume");
584
- return { kind: "suspend", id, on: resume.on, timeoutMs: resume.timeout_ms };
585
- }
586
- case "subworkflow": {
587
- const wfId = f(step, "workflow");
588
- const child = opts.workflows ? get(opts.workflows, wfId) : void 0;
589
- if (!child)
590
- throw new WorkflowCompileError(
591
- `subworkflow '${wfId}' not found \u2014 pass it in opts.workflows`
592
- );
593
- const compiledChild = compileWorkflow(child, opts);
594
- return { kind: "subworkflow", id, workflow: compiledChild };
595
- }
596
- case "agent": {
597
- if (typeof step.prompt === "function") {
598
- const passthrough = step;
599
- return passthrough;
600
- }
601
- return compileAgentStep(step, id, opts);
602
- }
603
- case "transform": {
604
- const passthrough = step;
605
- return passthrough;
606
- }
607
- case "branch":
608
- throw new WorkflowCompileError(
609
- `goto-style 'branch' (step '${id}') is unsupported in the structured subset \u2014 hand-author a structured BranchStep, or nest.`
610
- );
611
- default:
612
- throw new WorkflowCompileError(
613
- `unsupported step kind '${step.kind}' (step '${id}')`
614
- );
615
- }
616
- }
617
1162
  function compileWorkflowManifest(source, opts) {
618
1163
  const manifest = parseWorkflowManifest(source);
619
1164
  const handle = workflowFromManifest(manifest);
620
1165
  return compileWorkflow(handle, opts);
621
1166
  }
622
1167
 
623
- export { WorkflowCompileError, WorkflowSuspendedError, buildAgentStep, compileWorkflow, compileWorkflowManifest, evalPredicate, resolveRef, resolveValue, runWorkflow };
1168
+ export { NodeFsPort, WorkflowCompileError, WorkflowSuspendedError, buildAgentStep, compileWorkflow, compileWorkflowManifest, evalPredicate, materializeKnowledge, resolveKnowledgeSelectors, resolveRef, resolveRefPrefixed, resolveValue, runWorkflow };
624
1169
  //# sourceMappingURL=index.mjs.map
625
1170
  //# sourceMappingURL=index.mjs.map