@oomol-lab/open-flow 0.1.0-beta.65 → 0.1.0-beta.66

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 (49) hide show
  1. package/dist/browser/{createResourceDialog-BAJXbzNH.js → createResourceDialog-C-Si_cYs.js} +2 -2
  2. package/dist/browser/flow-authoring-node.d.ts +12 -3
  3. package/dist/browser/flow-authoring.js +114 -19
  4. package/dist/browser/flow-change-schema.d.ts +3 -1
  5. package/dist/browser/flow-change.d.ts +15 -3
  6. package/dist/browser/flow-change.js +111 -16
  7. package/dist/browser/{flowChanges-DkvG5uDq.js → flowChanges-T4_R5p1f.js} +2654 -1858
  8. package/dist/browser/{flowRunInputEditorStore-B2Rw4VsT.js → flowRunInputEditorStore-DJqmR6Qh.js} +3 -2
  9. package/dist/browser/{flowWorkspace-BEp-lxqF.js → flowWorkspace-C0J5C0oM.js} +700 -224
  10. package/dist/browser/{icons-Db2Jb2iX.js → icons-BD3m0RQq.js} +4484 -4370
  11. package/dist/browser/preview.css +152 -64
  12. package/dist/browser/preview.js +4460 -3412
  13. package/dist/browser/ui.css +85 -3
  14. package/dist/browser/ui.js +8 -5
  15. package/dist/browser/workbench.css +152 -64
  16. package/dist/browser/workbench.js +146 -57
  17. package/dist/browser/{workbenchSelect-C-jf6NIM.js → workbenchSelect-B-PFpF_Q.js} +1 -1
  18. package/dist/command/index.js +49 -49
  19. package/dist/common/authoring.d.ts +7 -5
  20. package/dist/common/authoringExamples.d.ts +3 -3
  21. package/dist/common/authoringFields.d.ts +20 -0
  22. package/dist/common/authoringSchema.d.ts +404 -15
  23. package/dist/common/control-api-conformance.js +384 -137
  24. package/dist/common/control-api.d.ts +63 -7
  25. package/dist/common/control-api.js +3562 -3428
  26. package/dist/common/control-requests.d.ts +10 -3
  27. package/dist/common/control-requests.js +418 -43
  28. package/dist/common/decision.js +28 -3
  29. package/dist/common/flow-encoding.d.ts +4 -2
  30. package/dist/common/flow-encoding.js +144 -10
  31. package/dist/common/flow-graph.d.ts +1 -1
  32. package/dist/common/flow-semantics.d.ts +12 -2
  33. package/dist/common/flow-semantics.js +146 -33
  34. package/dist/common/integration-trigger.js +28 -3
  35. package/dist/common/liveDetails.d.ts +26 -11
  36. package/dist/common/mcp.d.ts +6 -3
  37. package/dist/common/mcp.js +203 -84
  38. package/dist/common/openapi.js +28 -3
  39. package/dist/common/poll-trigger.js +28 -3
  40. package/dist/common/run-events.d.ts +2 -0
  41. package/dist/common/run-events.js +9 -3
  42. package/dist/common/scheduler.d.ts +10 -1
  43. package/dist/common/scheduler.js +167 -45
  44. package/dist/common/trigger-contract.d.ts +2 -0
  45. package/dist/common/webhook-trigger.js +28 -3
  46. package/dist/common/workflow.d.ts +93 -0
  47. package/dist/common/workflow.js +10491 -0
  48. package/package.json +5 -1
  49. package/skills/open-flow/references/flow-authoring.md +26 -16
@@ -9504,6 +9504,7 @@ var port = object({
9504
9504
  nullable: boolean(),
9505
9505
  handle: text
9506
9506
  });
9507
+ var workflowInputDefs = array(port.strict());
9507
9508
  var input = port.extend({ value: json.optional() });
9508
9509
  var group = object({
9509
9510
  collapsed: boolean().optional(),
@@ -9661,6 +9662,11 @@ var managedFields = object({
9661
9662
  messageHandle: text,
9662
9663
  inputs: record$1(text, agentValue)
9663
9664
  }).optional()
9665
+ }),
9666
+ strictObject({
9667
+ kind: literal("workflow"),
9668
+ flowId: text,
9669
+ revisionId: text
9664
9670
  })
9665
9671
  ])
9666
9672
  });
@@ -9769,6 +9775,12 @@ var base = {
9769
9775
  timeoutMs: number().optional()
9770
9776
  };
9771
9777
  var node = union([
9778
+ strictObject({
9779
+ ...base,
9780
+ kind: literal("outputs"),
9781
+ outputDefs: array(port.strict()),
9782
+ outputMappings: inputs
9783
+ }).omit({ inputs: true }),
9772
9784
  object({
9773
9785
  ...base,
9774
9786
  inputs: record$1(text, never()),
@@ -9801,6 +9813,11 @@ var node = union([
9801
9813
  ...trigger,
9802
9814
  kind: literal("manual")
9803
9815
  }),
9816
+ strictObject({
9817
+ ...trigger,
9818
+ kind: literal("start"),
9819
+ inputDefs: workflowInputDefs
9820
+ }),
9804
9821
  strictObject({
9805
9822
  ...trigger,
9806
9823
  kind: literal("error"),
@@ -9865,7 +9882,7 @@ var module = object({
9865
9882
  imports: strings,
9866
9883
  source: text
9867
9884
  });
9868
- var document = preprocess(rejectRetiredDefinitions, object({
9885
+ var document = preprocess(normalizeDocument, object({
9869
9886
  bindings: record$1(text, binding),
9870
9887
  graph
9871
9888
  }));
@@ -9874,7 +9891,9 @@ object({
9874
9891
  literal(2),
9875
9892
  literal(4),
9876
9893
  literal(5),
9877
- literal(6)
9894
+ literal(6),
9895
+ literal(7),
9896
+ literal(8)
9878
9897
  ]),
9879
9898
  document,
9880
9899
  modules: record$1(text, module)
@@ -9882,13 +9901,19 @@ object({
9882
9901
  kind: literal("open-flow-flow-revision"),
9883
9902
  version: literal(1)
9884
9903
  });
9885
- function rejectRetiredDefinitions(value) {
9904
+ function normalizeDocument(value) {
9886
9905
  const candidate = record(value);
9887
9906
  if (candidate.tasks !== void 0 && (candidate.tasks === null || typeof candidate.tasks !== "object" || Array.isArray(candidate.tasks) || Object.keys(candidate.tasks).length > 0)) throw new TypeError("Independent Task definitions are no longer supported.");
9888
9907
  if (Object.values(record(record(candidate.graph).nodes)).some((candidateNode) => Object.hasOwn(record(candidateNode), "taskId"))) throw new TypeError("Task references are no longer supported.");
9889
9908
  const subflows = candidate.subflows;
9890
9909
  if (subflows !== void 0 && (subflows === null || typeof subflows !== "object" || Array.isArray(subflows) || Object.keys(subflows).length > 0)) throw new TypeError("Subflows are no longer supported.");
9891
9910
  if (Object.values(record(record(candidate.graph).nodes)).some((candidateNode) => record(candidateNode).kind === "subflow")) throw new TypeError("Subflow nodes are no longer supported.");
9911
+ if ([
9912
+ "inputDefs",
9913
+ "outputDefs",
9914
+ "outputMappings"
9915
+ ].some((field) => Object.hasOwn(candidate, field))) throw new TypeError("Workflow interface configuration must belong to Start and Outputs nodes.");
9916
+ if (Object.values(record(record(candidate.graph).nodes)).some((candidateNode) => record(candidateNode).kind == "start" && Object.hasOwn(record(candidateNode), "parameters"))) throw new TypeError("Legacy Start.parameters is unsupported; configure node.inputDefs.");
9892
9917
  return value;
9893
9918
  }
9894
9919
  function record(value) {
@@ -10088,6 +10113,7 @@ function conditionOperands(node) {
10088
10113
  }] : []])));
10089
10114
  }
10090
10115
  function nodeInputMappings(node) {
10116
+ if (node.kind == "outputs") return node.outputMappings;
10091
10117
  if (node.kind != "condition") return "inputs" in node ? node.inputs : {};
10092
10118
  return Object.fromEntries(conditionOperands(node).flatMap(({ handle, operand }) => operand.kind == "source" ? [[handle, {
10093
10119
  kind: "sources",
@@ -10243,6 +10269,17 @@ function selectConditionBranches(node, values) {
10243
10269
  function portsByHandle(ports) {
10244
10270
  return Object.fromEntries(ports.flatMap((port) => "handle" in port ? [[port.handle, port]] : []));
10245
10271
  }
10272
+ function isTriggerNode(node) {
10273
+ return [
10274
+ "manual",
10275
+ "start",
10276
+ "error",
10277
+ "webhook",
10278
+ "cron",
10279
+ "poll",
10280
+ "integration"
10281
+ ].includes(node.kind);
10282
+ }
10246
10283
  //#endregion
10247
10284
  //#region src/flow/common/agent.ts
10248
10285
  var schema = lazy(() => union([boolean(), strictObject({
@@ -10515,6 +10552,7 @@ function triggerOutputDefinitions(trigger) {
10515
10552
  switch (trigger.kind) {
10516
10553
  case "error": return errorOutputs;
10517
10554
  case "manual": return [];
10555
+ case "start": return trigger.inputDefs;
10518
10556
  case "cron": return cronOutputs;
10519
10557
  case "poll":
10520
10558
  case "integration": return trigger.definition.outputs;
@@ -10534,11 +10572,37 @@ function matchesTriggerOutputs(trigger, outputs) {
10534
10572
  const ports = triggerOutputDefinitions(trigger);
10535
10573
  return new Set(ports.map((port) => port.handle)).size === ports.length && Object.keys(outputs).length === ports.length && ports.every((port) => Object.hasOwn(outputs, port.handle) && (outputs[port.handle] === null && port.nullable || matchesSchema(outputs[port.handle], port.jsonSchema)));
10536
10574
  }
10575
+ /** Resolve caller-supplied Start parameters into the complete output snapshot used by execution. */
10576
+ function normalizeTriggerOutputs(trigger, outputs) {
10577
+ if (!isJsonObject(outputs)) return;
10578
+ const normalized = trigger.kind == "start" ? {
10579
+ ...outputs,
10580
+ ...Object.fromEntries(trigger.inputDefs.filter((port) => port.nullable && !Object.hasOwn(outputs, port.handle)).map((port) => [port.handle, null]))
10581
+ } : outputs;
10582
+ return matchesTriggerOutputs(trigger, normalized) ? normalized : void 0;
10583
+ }
10537
10584
  //#endregion
10538
10585
  //#region src/flow/common/graph.ts
10539
10586
  function isResolutionNode(node) {
10540
10587
  return node?.kind == "approval" || node?.kind == "wait";
10541
10588
  }
10589
+ function nodeInputPorts(node) {
10590
+ switch (node.kind) {
10591
+ case "outputs": return portsByHandle(node.outputDefs);
10592
+ case "condition": return conditionInputPorts(node);
10593
+ case "value": return {};
10594
+ case "task": return portsByHandle([...node.task.inputs, ...node.additionalInputs ?? []]);
10595
+ case "approval":
10596
+ case "wait": return portsByHandle(node.inputDefinitions);
10597
+ case "cron":
10598
+ case "integration":
10599
+ case "poll":
10600
+ case "error":
10601
+ case "manual":
10602
+ case "start":
10603
+ case "webhook": return {};
10604
+ }
10605
+ }
10542
10606
  function resolutionActions(node) {
10543
10607
  return node.kind == "wait" ? ["continue"] : ["approve", "reject"];
10544
10608
  }
@@ -10582,6 +10646,7 @@ function resolutionOutputPorts(node) {
10582
10646
  }
10583
10647
  function nodeOutputPorts(node) {
10584
10648
  switch (node.kind) {
10649
+ case "outputs":
10585
10650
  case "condition": return {};
10586
10651
  case "value": return portsByHandle(node.values);
10587
10652
  case "task": return portsByHandle(node.task.outputs);
@@ -10592,6 +10657,7 @@ function nodeOutputPorts(node) {
10592
10657
  case "poll":
10593
10658
  case "error":
10594
10659
  case "manual":
10660
+ case "start":
10595
10661
  case "webhook": return triggerOutputPorts(node);
10596
10662
  }
10597
10663
  }
@@ -10658,7 +10724,7 @@ var Dependencies = class {
10658
10724
  stack.delete(id);
10659
10725
  };
10660
10726
  const ids = Object.keys(graph.nodes).toSorted();
10661
- for (const triggerId of ids.filter((id) => !("inputs" in graph.nodes[id]))) visit(triggerId);
10727
+ for (const triggerId of ids.filter((id) => isTriggerNode(graph.nodes[id]))) visit(triggerId);
10662
10728
  for (const id of ids) visit(id);
10663
10729
  for (const edge of this.forward) {
10664
10730
  const edges = this.incoming.get(edge.target) ?? [];
@@ -10733,7 +10799,7 @@ var Dependencies = class {
10733
10799
  }
10734
10800
  start(nodeId, outputs) {
10735
10801
  for (const [id, node] of Object.entries(this.graph.nodes)) {
10736
- if ("inputs" in node) continue;
10802
+ if (!isTriggerNode(node)) continue;
10737
10803
  const state = this.state.nodes[id];
10738
10804
  state.status = id == nodeId ? "completed" : "skipped";
10739
10805
  if (id == nodeId) {
@@ -10902,7 +10968,7 @@ var withWaitNotificationMessage = withWaitNotificationMessage$1;
10902
10968
  var normalizeWaitComment = normalizeWaitComment$1;
10903
10969
  var PortValidationError = class extends Error {};
10904
10970
  function usesPending(graph, nodeId) {
10905
- return graph.edges.some((edge) => edge.source == nodeId && edge.sourceHandle == "pending") || Object.values(graph.nodes).some((node) => "inputs" in node && Object.values(node.inputs).some((mapping) => mapping.kind == "sources" && mapping.sources.some((source) => source.kind == "node" && source.nodeId == nodeId && source.output == "pending")));
10971
+ return graph.edges.some((edge) => edge.source == nodeId && edge.sourceHandle == "pending") || Object.values(graph.nodes).some((node) => !isTriggerNode(node) && Object.values(nodeInputMappings(node)).some((mapping) => mapping.kind == "sources" && mapping.sources.some((source) => source.kind == "node" && source.nodeId == nodeId && source.output == "pending")));
10906
10972
  }
10907
10973
  var waitRetentionMs = 12e4;
10908
10974
  function nodeFailure(error, project) {
@@ -10911,19 +10977,11 @@ function nodeFailure(error, project) {
10911
10977
  message: error instanceof Error ? error.message : String(error)
10912
10978
  };
10913
10979
  }
10914
- function nodePorts(node) {
10915
- switch (node.kind) {
10916
- case "condition": return conditionInputPorts(node);
10917
- case "value": return {};
10918
- case "task": return portsByHandle([...node.task.inputs, ...node.additionalInputs ?? []]);
10919
- case "approval":
10920
- case "wait": return portsByHandle(node.inputDefinitions);
10921
- }
10922
- }
10923
10980
  function nodeTitle(node) {
10924
10981
  if (node.name != null) return node.name;
10925
10982
  switch (node.kind) {
10926
10983
  case "condition": return;
10984
+ case "outputs": return "Workflow Outputs";
10927
10985
  case "value": return "Fixed Values";
10928
10986
  case "task": return node.task.name;
10929
10987
  case "approval": return "Approval";
@@ -10979,7 +11037,8 @@ function decodeFlowRunCheckpoint(input) {
10979
11037
  "inputs",
10980
11038
  "results",
10981
11039
  "version",
10982
- "waits"
11040
+ "waits",
11041
+ ...source.calls === void 0 ? [] : ["calls"]
10983
11042
  ], "Flow Run checkpoint");
10984
11043
  if (source.version != 5) throw new Error("Flow Run checkpoint version is unsupported.");
10985
11044
  const bindingValues = Object.fromEntries(Object.entries(checkpointRecord(source.bindingValues, "Checkpoint bindings")).map(([id, value]) => {
@@ -11003,7 +11062,14 @@ function decodeFlowRunCheckpoint(input) {
11003
11062
  value: json$1(),
11004
11063
  waitId: string().min(1),
11005
11064
  pending: json$1().optional()
11006
- })).min(1).parse(source.waits);
11065
+ })).parse(source.waits);
11066
+ const calls = array(strictObject({
11067
+ jobId: string().min(1),
11068
+ nodeId: string().min(1),
11069
+ childRunId: string().min(1),
11070
+ input: record$1(string(), json$1())
11071
+ })).parse(source.calls ?? []);
11072
+ if (pendingWaits.length + calls.length == 0) throw new Error("Checkpoint has no pending execution.");
11007
11073
  const agents = record$1(string(), strictObject({
11008
11074
  invocationId: string().min(1),
11009
11075
  input: record$1(string(), json$1()),
@@ -11017,6 +11083,10 @@ function decodeFlowRunCheckpoint(input) {
11017
11083
  jobs.add(pending.jobId);
11018
11084
  waits.add(pending.waitId);
11019
11085
  }
11086
+ for (const call of calls) {
11087
+ if (jobs.has(call.jobId) || dependencies.nodes[call.nodeId]?.status != "running" || dependencies.nodes[call.nodeId]?.jobId != call.jobId || Object.values(results).some((result) => result.jobId == call.jobId)) throw new Error("Checkpoint call execution conflicts.");
11088
+ jobs.add(call.jobId);
11089
+ }
11020
11090
  if (Object.values(dependencies.nodes).some((state) => state.status == "running" && !jobs.has(state.jobId))) throw new Error("Checkpoint dependency has no waiting execution.");
11021
11091
  if (Object.keys(agents).some((id) => !jobs.has(id))) throw new Error("Checkpoint Agent has no waiting execution.");
11022
11092
  return {
@@ -11027,7 +11097,8 @@ function decodeFlowRunCheckpoint(input) {
11027
11097
  inputs,
11028
11098
  results,
11029
11099
  version: 5,
11030
- waits: pendingWaits
11100
+ waits: pendingWaits,
11101
+ ...source.calls === void 0 ? {} : { calls }
11031
11102
  };
11032
11103
  }
11033
11104
  function jsonEqual(left, right) {
@@ -11079,14 +11150,14 @@ function agentApproval(config, checkpoint, inputs) {
11079
11150
  }
11080
11151
  function validateOutputs(nodeId, node, value) {
11081
11152
  const raw = outputRecord(value, nodeId);
11082
- const discardUndeclared = node.kind == "task" && "executor" in node.task && node.task.executor.kind == "connector";
11083
- const ports = isResolutionNode(node) ? resolutionOutputPorts(node) : node.kind == "task" ? portsByHandle(node.task.outputs) : node.kind == "value" ? portsByHandle(node.values) : {};
11153
+ const discardUndeclared = node.kind == "task" && "executor" in node.task && (node.task.executor.kind == "connector" || node.task.executor.kind == "workflow");
11154
+ const ports = node.kind == "outputs" ? nodeInputPorts(node) : isResolutionNode(node) ? resolutionOutputPorts(node) : node.kind == "task" ? portsByHandle(node.task.outputs) : node.kind == "value" ? portsByHandle(node.values) : {};
11084
11155
  const outputs = outputRecord(checkpointJson(Object.fromEntries(Object.entries(raw).map(([handle, item]) => [handle, Object.hasOwn(ports, handle) && item === void 0 ? null : item])), `Node "${nodeId}" outputs`), nodeId);
11085
11156
  const complete = node.kind == "condition" || isResolutionNode(node) ? outputs : {
11086
11157
  ...Object.fromEntries(Object.keys(ports).map((handle) => [handle, null])),
11087
11158
  ...outputs
11088
11159
  };
11089
- const validated = {};
11160
+ const validated = [];
11090
11161
  for (const [handle, output] of Object.entries(complete)) {
11091
11162
  const port = ports[handle];
11092
11163
  if (port == null) {
@@ -11094,9 +11165,9 @@ function validateOutputs(nodeId, node, value) {
11094
11165
  throw new PortValidationError(`Node "${nodeId}" output "${handle}" does not match its declaration.`);
11095
11166
  }
11096
11167
  if (!(output === null && port.nullable) && !matchesSchema(output, port.jsonSchema)) throw new PortValidationError(`Node "${nodeId}" output "${handle}" does not match its declaration.`);
11097
- validated[handle] = output;
11168
+ validated.push([handle, output]);
11098
11169
  }
11099
- return validated;
11170
+ return Object.fromEntries(validated);
11100
11171
  }
11101
11172
  function validateCheckpoint(target, checkpoint, resolveInputs) {
11102
11173
  const launch = checkpoint.inputs;
@@ -11107,14 +11178,14 @@ function validateCheckpoint(target, checkpoint, resolveInputs) {
11107
11178
  if (graph == null) throw new Error("Checkpoint execution counts reference an unknown graph.");
11108
11179
  for (const [id, count] of Object.entries(counts)) {
11109
11180
  const node = graph.nodes[id];
11110
- if (node == null || !("inputs" in node) || count > (node.maxExecutions ?? 1e3)) throw new Error("Checkpoint execution count is invalid.");
11181
+ if (node == null || isTriggerNode(node) || count > (node.maxExecutions ?? 1e3)) throw new Error("Checkpoint execution count is invalid.");
11111
11182
  }
11112
11183
  }
11113
11184
  const validateResults = (results, frame = false) => {
11114
11185
  for (const [id, outputs] of Object.entries(results)) {
11115
11186
  const node = target.graph.nodes[id];
11116
11187
  if (node == null) throw new Error("Checkpoint result node is missing.");
11117
- if (!("inputs" in node)) {
11188
+ if (isTriggerNode(node)) {
11118
11189
  if (!matchesTriggerOutputs(node, outputs)) throw new Error("Checkpoint Trigger output is invalid.");
11119
11190
  } else {
11120
11191
  if (checkpoint.counts[""]?.[id] == null) throw new Error("Checkpoint execution count is missing.");
@@ -11125,20 +11196,20 @@ function validateCheckpoint(target, checkpoint, resolveInputs) {
11125
11196
  }
11126
11197
  };
11127
11198
  validateResults(Object.fromEntries(Object.entries(checkpoint.results).map(([id, result]) => [id, result.outputs])));
11128
- const triggerIds = Object.keys(checkpoint.results).filter((id) => !("inputs" in target.graph.nodes[id]));
11199
+ const triggerIds = Object.keys(checkpoint.results).filter((id) => isTriggerNode(target.graph.nodes[id]));
11129
11200
  if (triggerIds.length != 1) throw new Error("Checkpoint must contain exactly one selected Trigger.");
11130
11201
  const pendingCounts = /* @__PURE__ */ new Map();
11131
- for (const waiting of checkpoint.waits) pendingCounts.set(waiting.nodeId, (pendingCounts.get(waiting.nodeId) ?? 0) + 1);
11202
+ for (const waiting of [...checkpoint.waits, ...checkpoint.calls ?? []]) pendingCounts.set(waiting.nodeId, (pendingCounts.get(waiting.nodeId) ?? 0) + 1);
11132
11203
  for (const [id, count] of pendingCounts) if ((checkpoint.counts[""]?.[id] ?? 0) < count + (checkpoint.results[id] == null ? 0 : 1)) throw new Error("Checkpoint waiting execution count is incomplete.");
11133
11204
  for (const [id, values] of Object.entries(launch)) {
11134
11205
  const node = target.graph.nodes[id];
11135
- if (!("inputs" in node) || Object.keys(values).some((handle) => nodePorts(node)[handle] == null)) throw new Error("Checkpoint launch inputs do not match the prepared graph.");
11206
+ if (isTriggerNode(node) || Object.keys(values).some((handle) => nodeInputPorts(node)[handle] == null)) throw new Error("Checkpoint launch inputs do not match the prepared graph.");
11136
11207
  }
11137
11208
  for (const [nodeId, state] of Object.entries(checkpoint.dependencies.nodes)) {
11138
11209
  const frame = dependencyValues(state.input);
11139
11210
  validateResults(frame, true);
11140
- if ("inputs" in target.graph.nodes[nodeId] && (state.status == "running" || state.status == "completed")) {
11141
- const triggers = Object.keys(frame).filter((id) => !("inputs" in target.graph.nodes[id]));
11211
+ if (!isTriggerNode(target.graph.nodes[nodeId]) && (state.status == "running" || state.status == "completed")) {
11212
+ const triggers = Object.keys(frame).filter((id) => isTriggerNode(target.graph.nodes[id]));
11142
11213
  if (triggers.length != 1 || triggers[0] != triggerIds[0] || !jsonEqual(frame[triggers[0]], checkpoint.results[triggers[0]].outputs)) throw new Error("Checkpoint input Trigger does not match the Run.");
11143
11214
  }
11144
11215
  if (state.output != null) validateResults({ [nodeId]: state.output }, state.status == "running");
@@ -11147,9 +11218,13 @@ function validateCheckpoint(target, checkpoint, resolveInputs) {
11147
11218
  if (result == null || state.jobId != null && state.jobId != result.jobId || !jsonEqual(state.output, result.outputs)) throw new Error("Checkpoint completed dependency conflicts with its result.");
11148
11219
  }
11149
11220
  }
11221
+ for (const call of checkpoint.calls ?? []) {
11222
+ const node = target.graph.nodes[call.nodeId];
11223
+ if (node?.kind != "task" || !("executor" in node.task) || node.task.executor.kind != "workflow" || !jsonEqual(resolveInputs(call.nodeId, node), call.input) || checkpoint.agents[call.jobId] != null || checkpoint.dependencies.nodes[call.nodeId]?.output != null) throw new Error("Checkpoint workflow call does not match its node.");
11224
+ }
11150
11225
  for (const waiting of checkpoint.waits) {
11151
11226
  const node = target.graph.nodes[waiting.nodeId];
11152
- if (node == null || !("inputs" in node)) throw new Error("Checkpoint waiting node is missing.");
11227
+ if (node == null || isTriggerNode(node)) throw new Error("Checkpoint waiting node is missing.");
11153
11228
  const state = checkpoint.dependencies.nodes[waiting.nodeId];
11154
11229
  const expectedOutput = waiting.pending == null ? void 0 : { pending: waiting.pending };
11155
11230
  if (state.output == null != (expectedOutput == null) || state.output != null && !jsonEqual(state.output, expectedOutput)) throw new Error("Checkpoint waiting dependency output is inconsistent.");
@@ -11178,7 +11253,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11178
11253
  runId,
11179
11254
  type: "run.started"
11180
11255
  });
11181
- const order = Object.keys(target.graph.nodes).filter((id) => "inputs" in target.graph.nodes[id]);
11256
+ const order = Object.keys(target.graph.nodes).filter((id) => !isTriggerNode(target.graph.nodes[id]));
11182
11257
  const outgoing = /* @__PURE__ */ new Map();
11183
11258
  for (const edge of target.graph.edges) {
11184
11259
  const targets = outgoing.get(edge.source) ?? [];
@@ -11193,10 +11268,10 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11193
11268
  if (resume == null) {
11194
11269
  if (trigger != null) {
11195
11270
  const node = target.graph.nodes[trigger.nodeId];
11196
- if (node == null || "inputs" in node || !matchesTriggerOutputs(node, trigger.outputs)) throw new Error("Trigger outputs are invalid.");
11271
+ if (node == null || !isTriggerNode(node) || !matchesTriggerOutputs(node, trigger.outputs)) throw new Error("Trigger outputs are invalid.");
11197
11272
  }
11198
11273
  for (const [id, node] of Object.entries(target.graph.nodes)) {
11199
- if ("inputs" in node) continue;
11274
+ if (!isTriggerNode(node)) continue;
11200
11275
  if (trigger?.nodeId == id) completed.set(id, {
11201
11276
  jobId: id,
11202
11277
  outputs: trigger.outputs
@@ -11207,6 +11282,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11207
11282
  const runNode = yield* FiberSet.runtime(active)();
11208
11283
  let firstCause;
11209
11284
  let firstFailure;
11285
+ const pendingCalls = new Map((resume?.checkpoint.calls ?? []).map((call) => [call.jobId, call]));
11210
11286
  const pendingWaits = new Map((resume?.checkpoint.waits ?? []).map((wait) => [wait.jobId, wait]));
11211
11287
  const changes = yield* Queue.unbounded();
11212
11288
  let remainingMs = context.remainingMs;
@@ -11236,7 +11312,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11236
11312
  const resolveInputs = (nodeId, node) => {
11237
11313
  const mappings = nodeInputMappings(node);
11238
11314
  const frame = dependencies.inputValues(nodeId);
11239
- return Object.fromEntries(Object.entries(nodePorts(node)).map(([handle, port]) => [handle, resolveInput(mappings[handle], port, launch[nodeId]?.[handle], `Node "${nodeId}" input "${handle}"`, frame, node.kind == "condition")]));
11315
+ return Object.fromEntries(Object.entries(nodeInputPorts(node)).map(([handle, port]) => [handle, resolveInput(mappings[handle], port, launch[nodeId]?.[handle], `Node "${nodeId}" input "${handle}"`, frame, node.kind == "condition")]));
11240
11316
  };
11241
11317
  const commit = (nodeId, jobId, outputs, releasedHandles = Object.keys(outputs)) => Effect.uninterruptible(Effect.gen(function* () {
11242
11318
  yield* context.emit({
@@ -11265,7 +11341,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11265
11341
  const mapping = nodeInputMappings(node)[handle];
11266
11342
  return mapping?.kind != "sources" || mapping.sources.every((source) => source.kind != "binding");
11267
11343
  }));
11268
- if (resolution == null) yield* context.emit({
11344
+ if (resolution == null && !pendingCalls.has(jobId)) yield* context.emit({
11269
11345
  inputs: projectedInputs,
11270
11346
  jobId,
11271
11347
  nodeId,
@@ -11276,6 +11352,9 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11276
11352
  });
11277
11353
  let outputs;
11278
11354
  switch (node.kind) {
11355
+ case "outputs":
11356
+ outputs = nodeInputs;
11357
+ break;
11279
11358
  case "condition": return {
11280
11359
  kind: "completed",
11281
11360
  outputs: {},
@@ -11289,6 +11368,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11289
11368
  const previousIds = dependencies.state.nodes[nodeId].previous;
11290
11369
  const additional = new Set((node.additionalInputs ?? []).map((port) => port.handle));
11291
11370
  const result = yield* context.invokeTask({
11371
+ ...pendingCalls.has(jobId) ? { workflowRunId: pendingCalls.get(jobId).childRunId } : {},
11292
11372
  additionalInputs: Object.fromEntries(Object.entries(nodeInputs).filter(([handle]) => additional.has(handle))),
11293
11373
  blockId: "moduleId" in node.task ? node.task.moduleId : nodeId,
11294
11374
  flowId: target.flowId,
@@ -11315,7 +11395,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11315
11395
  const previous = target.graph.nodes[previousId];
11316
11396
  return {
11317
11397
  id: previousId,
11318
- name: previous.name ?? ("inputs" in previous ? nodeTitle(previous) : void 0) ?? previousId,
11398
+ name: previous.name ?? (!isTriggerNode(previous) ? nodeTitle(previous) : void 0) ?? previousId,
11319
11399
  outputs: frame[previousId],
11320
11400
  outputDefs: Object.entries(nodeOutputPorts(previous)).map(([handle, definition]) => Object.assign({
11321
11401
  handle,
@@ -11327,7 +11407,21 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11327
11407
  }
11328
11408
  } : { kind: "managed" }
11329
11409
  });
11330
- if (config != null) {
11410
+ if ("executor" in node.task && node.task.executor.kind == "workflow") {
11411
+ const response = discriminatedUnion("kind", [strictObject({
11412
+ kind: literal("calling"),
11413
+ childRunId: string().min(1)
11414
+ }), strictObject({
11415
+ kind: literal("completed"),
11416
+ outputs: record$1(string(), json$1())
11417
+ })]).parse(result);
11418
+ if (response.kind == "calling") return {
11419
+ kind: "calling",
11420
+ childRunId: response.childRunId,
11421
+ input: nodeInputs
11422
+ };
11423
+ outputs = response.outputs;
11424
+ } else if (config != null) {
11331
11425
  const response = agentResultSchema.parse(result);
11332
11426
  if (response.kind == "suspended") {
11333
11427
  const checkpoint = response.checkpoint;
@@ -11396,6 +11490,16 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11396
11490
  yield* Queue.offer(changes, void 0);
11397
11491
  });
11398
11492
  const settleNode = (nodeId, jobId, result) => {
11493
+ if (result.kind == "calling") {
11494
+ pendingCalls.set(jobId, {
11495
+ jobId,
11496
+ nodeId,
11497
+ childRunId: result.childRunId,
11498
+ input: result.input
11499
+ });
11500
+ return Effect.void;
11501
+ }
11502
+ pendingCalls.delete(jobId);
11399
11503
  if (result.kind == "completed") {
11400
11504
  delete agents[jobId];
11401
11505
  return commit(nodeId, jobId, result.outputs, result.releasedHandles);
@@ -11423,7 +11527,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11423
11527
  const executeReady = (nodeId) => {
11424
11528
  if (firstCause != null) return;
11425
11529
  const node = target.graph.nodes[nodeId];
11426
- if (!("inputs" in node)) return;
11530
+ if (isTriggerNode(node)) return;
11427
11531
  const jobId = context.createId();
11428
11532
  const count = counts[nodeId] ?? 0;
11429
11533
  const limit = node.maxExecutions ?? 1e3;
@@ -11511,18 +11615,22 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11511
11615
  return yield* Effect.failCause(firstCause);
11512
11616
  }
11513
11617
  if (pendingWaits.size > 0 && context.waits != null) applyResolutions(yield* context.waits.resolutions([...pendingWaits.values()].map((wait) => wait.waitId), false), now);
11618
+ for (const call of pendingCalls.values()) yield* observe(call.nodeId, call.jobId, Effect.gen(function* () {
11619
+ const result = yield* executeNode(call.nodeId, target.graph.nodes[call.nodeId], call.jobId, call.input);
11620
+ yield* settleNode(call.nodeId, call.jobId, result);
11621
+ })).pipe(Effect.catch(() => Effect.void));
11514
11622
  const ready = dependencies.ready();
11515
11623
  for (const nodeId of ready) executeReady(nodeId);
11516
11624
  const running = yield* FiberSet.size(active);
11517
11625
  if (firstCause != null) continue;
11518
- if (running == 0 && pendingWaits.size == 0 && ready.length == 0) {
11626
+ if (running == 0 && pendingWaits.size == 0 && pendingCalls.size == 0 && ready.length == 0) {
11519
11627
  if (dependencies.incomplete) return yield* Effect.fail(/* @__PURE__ */ new Error("Execution graph contains unresolved dependencies."));
11520
11628
  break;
11521
11629
  }
11522
11630
  if (running > 0 || ready.length > 0) idleSince = void 0;
11523
11631
  else idleSince ??= now;
11524
11632
  const left = idleSince == null ? remainingMs : waitRetentionMs - (now - idleSince);
11525
- if (idleSince != null && left <= 0) {
11633
+ if (idleSince != null && (left <= 0 || pendingCalls.size > 0)) {
11526
11634
  const checkpoint = {
11527
11635
  bindingValues: context.bindingValues,
11528
11636
  inputs: launch,
@@ -11531,7 +11639,8 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11531
11639
  dependencies: dependencies.state,
11532
11640
  version: 5,
11533
11641
  agents,
11534
- waits: [...pendingWaits.values()]
11642
+ waits: [...pendingWaits.values()],
11643
+ ...pendingCalls.size == 0 ? {} : { calls: [...pendingCalls.values()] }
11535
11644
  };
11536
11645
  const encoded = JSON.stringify(checkpoint);
11537
11646
  if (new TextEncoder().encode(encoded).byteLength > 16777216) return yield* Effect.fail(/* @__PURE__ */ new Error("Flow Run checkpoint exceeds 16 MiB."));
@@ -11544,6 +11653,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11544
11653
  const activity = Queue.take(changes).pipe(Effect.as({}));
11545
11654
  const decision = pendingWaits.size == 0 || context.waits == null ? Effect.never : context.waits.resolutions([...pendingWaits.values()].map((wait) => wait.waitId), true);
11546
11655
  let next = Effect.raceFirst(activity, decision);
11656
+ if (pendingCalls.size > 0) next = Effect.raceFirst(next, Effect.sleep(1e3).pipe(Effect.as({})));
11547
11657
  if (left != null) next = Effect.raceFirst(next, Effect.sleep(Math.max(0, left)).pipe(Effect.as({})));
11548
11658
  applyResolutions(yield* next, yield* Clock.currentTimeMillis);
11549
11659
  }
@@ -11552,8 +11662,11 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11552
11662
  runId,
11553
11663
  type: "run.progress"
11554
11664
  });
11665
+ const outputNodeId = order.find((nodeId) => target.graph.nodes[nodeId]?.kind == "outputs");
11666
+ const businessOutputs = outputNodeId == null ? void 0 : completed.get(outputNodeId)?.outputs;
11555
11667
  const result = {
11556
11668
  kind: "node-results",
11669
+ ...businessOutputs == null ? {} : { outputs: businessOutputs },
11557
11670
  nodes: resultNodes.flatMap((nodeId) => {
11558
11671
  const saved = completed.get(nodeId);
11559
11672
  return saved == null ? [] : [{
@@ -11603,7 +11716,13 @@ function runFlow(prepared, options) {
11603
11716
  if (checkpoint != null && new TextEncoder().encode(JSON.stringify(checkpoint)).byteLength > 16777216) return yield* Effect.fail(/* @__PURE__ */ new Error("Flow Run checkpoint exceeds 16 MiB."));
11604
11717
  if (checkpoint == null && options.trigger == null) return yield* Effect.fail(/* @__PURE__ */ new Error("A Flow Run requires a Trigger seed."));
11605
11718
  const triggerNode = options.trigger == null ? void 0 : prepared.graph.nodes[options.trigger.nodeId];
11606
- if (options.trigger != null && (triggerNode == null || "inputs" in triggerNode)) return yield* Effect.fail(/* @__PURE__ */ new Error(`Node "${options.trigger.nodeId}" is not a TriggerNode in Flow "${options.flowId}".`));
11719
+ if (options.trigger != null && (triggerNode == null || !isTriggerNode(triggerNode))) return yield* Effect.fail(/* @__PURE__ */ new Error(`Node "${options.trigger.nodeId}" is not a TriggerNode in Flow "${options.flowId}".`));
11720
+ const triggerOutputs = triggerNode == null || !isTriggerNode(triggerNode) ? void 0 : normalizeTriggerOutputs(triggerNode, options.trigger.outputs);
11721
+ if (options.trigger != null && triggerOutputs == null) return yield* Effect.fail(/* @__PURE__ */ new Error("Trigger outputs are invalid."));
11722
+ const trigger = options.trigger == null ? void 0 : {
11723
+ nodeId: options.trigger.nodeId,
11724
+ outputs: triggerOutputs
11725
+ };
11607
11726
  return yield* runGraph({
11608
11727
  failures: /* @__PURE__ */ new WeakMap(),
11609
11728
  counts: Object.fromEntries(Object.entries(checkpoint?.counts ?? {}).map(([scope, counts]) => [scope, { ...counts }])),
@@ -11617,8 +11736,11 @@ function runFlow(prepared, options) {
11617
11736
  remainingMs: options.remainingMs
11618
11737
  }, {
11619
11738
  flowId: options.flowId,
11620
- graph: prepared.graph
11621
- }, options.runId, options.inputs, options.trigger, checkpoint == null ? void 0 : { checkpoint });
11739
+ graph: prepared.graph,
11740
+ inputDefs: prepared.inputDefs,
11741
+ outputDefs: prepared.outputDefs,
11742
+ outputMappings: prepared.outputMappings
11743
+ }, options.runId, options.inputs, trigger, checkpoint == null ? void 0 : { checkpoint });
11622
11744
  });
11623
11745
  }
11624
11746
  //#endregion
@@ -7,3 +7,5 @@ export declare const webhookOutputs: readonly Port[];
7
7
  export declare function triggerOutputDefinitions(trigger: TriggerNode): readonly Port[];
8
8
  export declare function triggerOutputPorts(trigger: TriggerNode): Readonly<Record<string, PortDefinition>>;
9
9
  export declare function matchesTriggerOutputs(trigger: TriggerNode, outputs: unknown): outputs is Readonly<Record<string, JsonValue>>;
10
+ /** Resolve caller-supplied Start parameters into the complete output snapshot used by execution. */
11
+ export declare function normalizeTriggerOutputs(trigger: TriggerNode, outputs: unknown): Readonly<Record<string, JsonValue>> | undefined;
@@ -5818,6 +5818,7 @@ var port = object({
5818
5818
  nullable: boolean(),
5819
5819
  handle: text
5820
5820
  });
5821
+ var workflowInputDefs = array(port.strict());
5821
5822
  var input = port.extend({ value: json.optional() });
5822
5823
  var group = object({
5823
5824
  collapsed: boolean().optional(),
@@ -5975,6 +5976,11 @@ var managedFields = object({
5975
5976
  messageHandle: text,
5976
5977
  inputs: record$1(text, agentValue)
5977
5978
  }).optional()
5979
+ }),
5980
+ strictObject({
5981
+ kind: literal("workflow"),
5982
+ flowId: text,
5983
+ revisionId: text
5978
5984
  })
5979
5985
  ])
5980
5986
  });
@@ -6083,6 +6089,12 @@ var base = {
6083
6089
  timeoutMs: number().optional()
6084
6090
  };
6085
6091
  var node = union([
6092
+ strictObject({
6093
+ ...base,
6094
+ kind: literal("outputs"),
6095
+ outputDefs: array(port.strict()),
6096
+ outputMappings: inputs
6097
+ }).omit({ inputs: true }),
6086
6098
  object({
6087
6099
  ...base,
6088
6100
  inputs: record$1(text, never()),
@@ -6115,6 +6127,11 @@ var node = union([
6115
6127
  ...trigger,
6116
6128
  kind: literal("manual")
6117
6129
  }),
6130
+ strictObject({
6131
+ ...trigger,
6132
+ kind: literal("start"),
6133
+ inputDefs: workflowInputDefs
6134
+ }),
6118
6135
  strictObject({
6119
6136
  ...trigger,
6120
6137
  kind: literal("error"),
@@ -6179,7 +6196,7 @@ var module = object({
6179
6196
  imports: strings,
6180
6197
  source: text
6181
6198
  });
6182
- var document = preprocess(rejectRetiredDefinitions, object({
6199
+ var document = preprocess(normalizeDocument, object({
6183
6200
  bindings: record$1(text, binding),
6184
6201
  graph
6185
6202
  }));
@@ -6188,7 +6205,9 @@ object({
6188
6205
  literal(2),
6189
6206
  literal(4),
6190
6207
  literal(5),
6191
- literal(6)
6208
+ literal(6),
6209
+ literal(7),
6210
+ literal(8)
6192
6211
  ]),
6193
6212
  document,
6194
6213
  modules: record$1(text, module)
@@ -6196,13 +6215,19 @@ object({
6196
6215
  kind: literal("open-flow-flow-revision"),
6197
6216
  version: literal(1)
6198
6217
  });
6199
- function rejectRetiredDefinitions(value) {
6218
+ function normalizeDocument(value) {
6200
6219
  const candidate = record(value);
6201
6220
  if (candidate.tasks !== void 0 && (candidate.tasks === null || typeof candidate.tasks !== "object" || Array.isArray(candidate.tasks) || Object.keys(candidate.tasks).length > 0)) throw new TypeError("Independent Task definitions are no longer supported.");
6202
6221
  if (Object.values(record(record(candidate.graph).nodes)).some((candidateNode) => Object.hasOwn(record(candidateNode), "taskId"))) throw new TypeError("Task references are no longer supported.");
6203
6222
  const subflows = candidate.subflows;
6204
6223
  if (subflows !== void 0 && (subflows === null || typeof subflows !== "object" || Array.isArray(subflows) || Object.keys(subflows).length > 0)) throw new TypeError("Subflows are no longer supported.");
6205
6224
  if (Object.values(record(record(candidate.graph).nodes)).some((candidateNode) => record(candidateNode).kind === "subflow")) throw new TypeError("Subflow nodes are no longer supported.");
6225
+ if ([
6226
+ "inputDefs",
6227
+ "outputDefs",
6228
+ "outputMappings"
6229
+ ].some((field) => Object.hasOwn(candidate, field))) throw new TypeError("Workflow interface configuration must belong to Start and Outputs nodes.");
6230
+ if (Object.values(record(record(candidate.graph).nodes)).some((candidateNode) => record(candidateNode).kind == "start" && Object.hasOwn(record(candidateNode), "parameters"))) throw new TypeError("Legacy Start.parameters is unsupported; configure node.inputDefs.");
6206
6231
  return value;
6207
6232
  }
6208
6233
  function record(value) {