@codemation/core 0.2.3 → 0.4.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/CHANGELOG.md +23 -0
- package/README.md +2 -0
- package/dist/{EngineRuntimeRegistration.types-Bjeo7Sfq.d.ts → EngineRuntimeRegistration.types-DU6MsjU9.d.ts} +2 -2
- package/dist/{EngineWorkflowRunnerService-Dd4yD31l.d.cts → EngineWorkflowRunnerService-BBkL4VQF.d.cts} +2 -2
- package/dist/{InMemoryRunDataFactory-OUzDmAHt.d.cts → InMemoryRunDataFactory-CsYEMJK2.d.cts} +11 -3
- package/dist/{RunIntentService-Bkg4oYrM.d.cts → RunIntentService-BvlTpmEb.d.cts} +224 -237
- package/dist/{RunIntentService-BAKikN8h.d.ts → RunIntentService-zbTchO9T.d.ts} +305 -259
- package/dist/bootstrap/index.cjs +2 -2
- package/dist/bootstrap/index.d.cts +19 -7
- package/dist/bootstrap/index.d.ts +3 -3
- package/dist/bootstrap/index.js +2 -2
- package/dist/{bootstrap-DwS5S7s9.cjs → bootstrap-DHH2uo-W.cjs} +4 -2
- package/dist/bootstrap-DHH2uo-W.cjs.map +1 -0
- package/dist/{bootstrap-BD6CobHl.js → bootstrap-DbUlOl11.js} +4 -2
- package/dist/bootstrap-DbUlOl11.js.map +1 -0
- package/dist/{index-BDHCiN22.d.ts → index-CUt13qs1.d.ts} +85 -16
- package/dist/index.cjs +74 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +131 -24
- package/dist/index.d.ts +3 -3
- package/dist/index.js +64 -13
- package/dist/index.js.map +1 -1
- package/dist/{runtime-Cy-3FTI_.js → runtime-BdH94eBR.js} +502 -123
- package/dist/runtime-BdH94eBR.js.map +1 -0
- package/dist/{runtime-ZJUpWmPH.cjs → runtime-feFn8OmG.cjs} +561 -122
- package/dist/runtime-feFn8OmG.cjs.map +1 -0
- package/dist/testing.cjs +40 -36
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +17 -26
- package/dist/testing.d.ts +17 -26
- package/dist/testing.js +40 -36
- package/dist/testing.js.map +1 -1
- package/dist/{workflowActivationPolicy-BzyzXLa_.cjs → workflowActivationPolicy-6V3OJD3N.cjs} +65 -19
- package/dist/workflowActivationPolicy-6V3OJD3N.cjs.map +1 -0
- package/dist/{workflowActivationPolicy-B8HzTk3o.js → workflowActivationPolicy-Td9HTOuD.js} +65 -19
- package/dist/workflowActivationPolicy-Td9HTOuD.js.map +1 -0
- package/package.json +2 -1
- package/src/ai/AgentConfigInspectorFactory.ts +4 -0
- package/src/ai/AgentMessageConfigNormalizerFactory.ts +7 -0
- package/src/ai/AgentToolFactory.ts +2 -2
- package/src/ai/AiHost.ts +11 -10
- package/src/ai/NodeBackedToolConfig.ts +1 -1
- package/src/authoring/defineNode.types.ts +144 -25
- package/src/authoring/index.ts +3 -1
- package/src/bootstrap/runtime/EngineRuntimeRegistrar.ts +8 -0
- package/src/contracts/emitPorts.ts +27 -0
- package/src/contracts/index.ts +3 -0
- package/src/contracts/itemMeta.ts +11 -0
- package/src/contracts/itemValue.ts +147 -0
- package/src/contracts/runtimeTypes.ts +39 -22
- package/src/contracts/workflowTypes.ts +26 -56
- package/src/execution/FanInMergeByOriginMerger.ts +67 -0
- package/src/execution/ItemValueResolver.ts +27 -0
- package/src/execution/NodeActivationRequestComposer.ts +25 -0
- package/src/execution/NodeActivationRequestInputPreparer.ts +57 -25
- package/src/execution/NodeExecutor.ts +199 -30
- package/src/execution/NodeOutputNormalizer.ts +90 -0
- package/src/execution/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/orchestration/NodeExecutionRequestHandlerService.ts +39 -18
- package/src/orchestration/RunContinuationService.ts +11 -17
- package/src/planning/CurrentStateFrontierPlanner.ts +20 -20
- package/src/planning/RunQueuePlanner.ts +56 -19
- package/src/planning/WorkflowTopologyPlanner.ts +57 -33
- package/src/testing/ItemHarnessNode.ts +4 -10
- package/src/testing/ItemHarnessNodeConfig.ts +7 -16
- package/src/testing/RegistrarEngineTestKitFactory.ts +2 -0
- package/src/testing/SubWorkflowRunnerTestNode.ts +28 -43
- package/src/testing/SwitchHarnessNode.ts +54 -0
- package/src/types/index.ts +3 -0
- package/src/workflow/dsl/ChainCursorResolver.ts +68 -23
- package/src/workflow/dsl/WorkflowBuilder.ts +3 -5
- package/src/workflow/dsl/workflowBuilderTypes.ts +5 -8
- package/src/workflowSnapshots/MissingRuntimeNode.ts +4 -4
- package/src/workflowSnapshots/MissingRuntimeNodeConfig.ts +2 -2
- package/src/workflowSnapshots/WorkflowSnapshotCodec.ts +16 -7
- package/dist/bootstrap-BD6CobHl.js.map +0 -1
- package/dist/bootstrap-DwS5S7s9.cjs.map +0 -1
- package/dist/runtime-Cy-3FTI_.js.map +0 -1
- package/dist/runtime-ZJUpWmPH.cjs.map +0 -1
- package/dist/workflowActivationPolicy-B8HzTk3o.js.map +0 -1
- package/dist/workflowActivationPolicy-BzyzXLa_.cjs.map +0 -1
|
@@ -137,6 +137,74 @@ function chatModel(options = {}) {
|
|
|
137
137
|
return InjectableRuntimeDecoratorComposer.compose("chatModel", options, import.meta.url);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/contracts/itemValue.ts
|
|
142
|
+
const ITEM_VALUE_BRAND = Symbol.for("codemation.itemValue");
|
|
143
|
+
function itemValue(fn) {
|
|
144
|
+
return {
|
|
145
|
+
[ITEM_VALUE_BRAND]: true,
|
|
146
|
+
fn
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function isItemValue(value) {
|
|
150
|
+
if (typeof value !== "object" || value === null) return false;
|
|
151
|
+
const v = value;
|
|
152
|
+
if (v[ITEM_VALUE_BRAND] === true) return true;
|
|
153
|
+
const keys = Object.keys(v);
|
|
154
|
+
if (keys.length === 1 && keys[0] === "fn" && typeof v.fn === "function") return true;
|
|
155
|
+
for (const sym of Object.getOwnPropertySymbols(v)) if (sym.description === "codemation.itemValue" && v[sym] === true) return true;
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
function containsItemValueInUnknown(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
159
|
+
if (isItemValue(value)) return true;
|
|
160
|
+
if (value === null || typeof value !== "object") return false;
|
|
161
|
+
if (seen.has(value)) return false;
|
|
162
|
+
seen.add(value);
|
|
163
|
+
if (Array.isArray(value)) return value.some((entry) => containsItemValueInUnknown(entry, seen));
|
|
164
|
+
for (const entry of Object.values(value)) if (containsItemValueInUnknown(entry, seen)) return true;
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Deep-resolves {@link itemValue} leaves. Returns a new graph (does not mutate the original config object).
|
|
169
|
+
*/
|
|
170
|
+
async function resolveItemValuesInUnknown(value, args, seen = /* @__PURE__ */ new WeakSet()) {
|
|
171
|
+
if (isItemValue(value)) return await Promise.resolve(value.fn(args));
|
|
172
|
+
if (value === null || typeof value !== "object") return value;
|
|
173
|
+
if (seen.has(value)) return value;
|
|
174
|
+
seen.add(value);
|
|
175
|
+
if (Array.isArray(value)) {
|
|
176
|
+
const out$1 = [];
|
|
177
|
+
for (let i = 0; i < value.length; i++) out$1.push(await resolveItemValuesInUnknown(value[i], args, seen));
|
|
178
|
+
return out$1;
|
|
179
|
+
}
|
|
180
|
+
const rec = value;
|
|
181
|
+
const entries = Object.entries(rec);
|
|
182
|
+
const proto = Object.getPrototypeOf(value);
|
|
183
|
+
if (proto !== Object.prototype && proto !== null && entries.length === 0) return value;
|
|
184
|
+
const out = Object.create(proto);
|
|
185
|
+
for (const [k, v] of entries) out[k] = await resolveItemValuesInUnknown(v, args, seen);
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Clones runnable config (best-effort) so per-item {@link itemValue} resolution never mutates shared instances.
|
|
190
|
+
*/
|
|
191
|
+
async function resolveItemValuesForExecution(config, nodeCtx, item, itemIndex, items) {
|
|
192
|
+
const ivArgs = {
|
|
193
|
+
item,
|
|
194
|
+
itemIndex,
|
|
195
|
+
items,
|
|
196
|
+
ctx: {
|
|
197
|
+
runId: nodeCtx.runId,
|
|
198
|
+
workflowId: nodeCtx.workflowId,
|
|
199
|
+
nodeId: nodeCtx.nodeId,
|
|
200
|
+
activationId: nodeCtx.activationId,
|
|
201
|
+
data: nodeCtx.data
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
if (!containsItemValueInUnknown(config)) return;
|
|
205
|
+
return await resolveItemValuesInUnknown(config, ivArgs);
|
|
206
|
+
}
|
|
207
|
+
|
|
140
208
|
//#endregion
|
|
141
209
|
//#region src/workflow/definition/ConnectionNodeIdFactory.ts
|
|
142
210
|
/**
|
|
@@ -564,6 +632,64 @@ var ActivationEnqueueService = class {
|
|
|
564
632
|
}
|
|
565
633
|
};
|
|
566
634
|
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region src/contracts/itemMeta.ts
|
|
637
|
+
/**
|
|
638
|
+
* Reads `meta._cm.originIndex` when present (used for fan-in merge-by-origin and Merge routing).
|
|
639
|
+
*/
|
|
640
|
+
function getOriginIndexFromItem(item) {
|
|
641
|
+
const v = (item.meta?._cm)?.originIndex;
|
|
642
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region src/execution/FanInMergeByOriginMerger.ts
|
|
647
|
+
/**
|
|
648
|
+
* Default fan-in: combine multi-port {@link NodeInputsByPort} into one {@link Items} batch for per-item nodes.
|
|
649
|
+
*
|
|
650
|
+
* This is used when a single-input per-item node has multiple inbound edges (for example, branch reconverge
|
|
651
|
+
* after an `If` / `Switch`). The default behavior is **append / union** (preserving item payloads) with a
|
|
652
|
+
* deterministic order:
|
|
653
|
+
*
|
|
654
|
+
* - When router origin metadata exists (`meta._cm.originIndex`), items are sorted by origin index so the
|
|
655
|
+
* downstream batch preserves original ordering across branches.
|
|
656
|
+
* - Otherwise, items are appended by port-key order, preserving each port's local order.
|
|
657
|
+
*/
|
|
658
|
+
var FanInMergeByOriginMerger = class {
|
|
659
|
+
merge(inputsByPort) {
|
|
660
|
+
const portKeys = Object.keys(inputsByPort).sort();
|
|
661
|
+
if (portKeys.length === 0) return [];
|
|
662
|
+
if (portKeys.length === 1) return [...inputsByPort[portKeys[0]] ?? []];
|
|
663
|
+
const entries = [];
|
|
664
|
+
let anyOrigin = false;
|
|
665
|
+
for (let p = 0; p < portKeys.length; p++) {
|
|
666
|
+
const portKey = portKeys[p];
|
|
667
|
+
const items = inputsByPort[portKey] ?? [];
|
|
668
|
+
for (let i = 0; i < items.length; i++) {
|
|
669
|
+
const item = items[i];
|
|
670
|
+
const originIndex = getOriginIndexFromItem(item);
|
|
671
|
+
if (originIndex !== void 0) anyOrigin = true;
|
|
672
|
+
entries.push({
|
|
673
|
+
portKey,
|
|
674
|
+
portIndex: i,
|
|
675
|
+
item,
|
|
676
|
+
originIndex
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (!anyOrigin) return entries.map((e) => e.item);
|
|
681
|
+
const missingOriginRank = Number.MAX_SAFE_INTEGER;
|
|
682
|
+
return entries.slice().sort((a, b) => {
|
|
683
|
+
const ao = a.originIndex ?? missingOriginRank;
|
|
684
|
+
const bo = b.originIndex ?? missingOriginRank;
|
|
685
|
+
if (ao !== bo) return ao - bo;
|
|
686
|
+
const pk = a.portKey.localeCompare(b.portKey);
|
|
687
|
+
if (pk !== 0) return pk;
|
|
688
|
+
return a.portIndex - b.portIndex;
|
|
689
|
+
}).map((e) => e.item);
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
|
|
567
693
|
//#endregion
|
|
568
694
|
//#region src/execution/NodeInputContractError.ts
|
|
569
695
|
var NodeInputContractError = class extends Error {
|
|
@@ -579,45 +705,58 @@ var NodeInputContractError = class extends Error {
|
|
|
579
705
|
//#endregion
|
|
580
706
|
//#region src/execution/NodeActivationRequestInputPreparer.ts
|
|
581
707
|
/**
|
|
582
|
-
*
|
|
708
|
+
* Validates per-item inputs for {@link RunnableNode} before enqueue persistence (Zod on `item.json`).
|
|
709
|
+
* Does not rewrite `item.json` (wire stays as emitted upstream; engine passes parsed input via `execute` args).
|
|
710
|
+
* Converts multi-input activations into a single-input batch when the node is per-item only (engine fan-in).
|
|
583
711
|
*/
|
|
584
712
|
var NodeActivationRequestInputPreparer = class {
|
|
713
|
+
fanInMerger = new FanInMergeByOriginMerger();
|
|
585
714
|
constructor(workflowNodeInstanceFactory) {
|
|
586
715
|
this.workflowNodeInstanceFactory = workflowNodeInstanceFactory;
|
|
587
716
|
}
|
|
588
717
|
async prepare(request) {
|
|
589
|
-
if (request.kind
|
|
718
|
+
if (request.kind === "multi") return await this.prepareMulti(request);
|
|
719
|
+
return await this.prepareSingle(request);
|
|
720
|
+
}
|
|
721
|
+
async prepareMulti(request) {
|
|
722
|
+
const nodeInstance = this.workflowNodeInstanceFactory.createByType(request.ctx.config.type);
|
|
723
|
+
if (!this.hasRunnableExecute(nodeInstance) || this.hasExecuteMulti(nodeInstance) || this.isTriggerNode(nodeInstance)) return request;
|
|
724
|
+
const merged = this.fanInMerger.merge(request.inputsByPort);
|
|
725
|
+
const single = {
|
|
726
|
+
...request,
|
|
727
|
+
kind: "single",
|
|
728
|
+
input: merged
|
|
729
|
+
};
|
|
730
|
+
return await this.prepareSingle(single);
|
|
731
|
+
}
|
|
732
|
+
async prepareSingle(request) {
|
|
590
733
|
const nodeInstance = this.workflowNodeInstanceFactory.createByType(request.ctx.config.type);
|
|
591
|
-
if (!this.
|
|
734
|
+
if (!this.hasRunnableExecute(nodeInstance) || this.isTriggerNode(nodeInstance)) return request;
|
|
592
735
|
const inputSchema = this.resolveInputSchema(nodeInstance, request.ctx.config);
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
const item = request.input[i];
|
|
736
|
+
const inputBatch = request.input ?? [];
|
|
737
|
+
for (let i = 0; i < inputBatch.length; i++) {
|
|
738
|
+
const item = inputBatch[i];
|
|
597
739
|
try {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
itemIndex: i,
|
|
601
|
-
items: request.input,
|
|
602
|
-
ctx: request.ctx
|
|
603
|
-
})) : item.json;
|
|
604
|
-
const parsed = inputSchema.parse(mappedRaw);
|
|
605
|
-
mappedItems.push({
|
|
606
|
-
...item,
|
|
607
|
-
json: parsed
|
|
608
|
-
});
|
|
740
|
+
if (Array.isArray(item.json)) throw new Error("Item JSON must not be a top-level array");
|
|
741
|
+
inputSchema.parse(item.json);
|
|
609
742
|
} catch (cause) {
|
|
610
743
|
const message = this.formatContractFailure(cause);
|
|
611
744
|
throw new NodeInputContractError(`Node ${request.nodeId} activation ${request.activationId}: input contract failed: ${message}`, request.nodeId, request.activationId, cause);
|
|
612
745
|
}
|
|
613
746
|
}
|
|
614
|
-
return {
|
|
747
|
+
return request.input === void 0 ? {
|
|
615
748
|
...request,
|
|
616
|
-
input:
|
|
617
|
-
};
|
|
749
|
+
input: inputBatch
|
|
750
|
+
} : request;
|
|
751
|
+
}
|
|
752
|
+
isTriggerNode(nodeInstance) {
|
|
753
|
+
return typeof nodeInstance === "object" && nodeInstance !== null && nodeInstance.kind === "trigger";
|
|
618
754
|
}
|
|
619
|
-
|
|
620
|
-
return typeof nodeInstance === "object" && nodeInstance !== null && typeof nodeInstance.
|
|
755
|
+
hasRunnableExecute(nodeInstance) {
|
|
756
|
+
return typeof nodeInstance === "object" && nodeInstance !== null && nodeInstance.kind === "node" && typeof nodeInstance.execute === "function";
|
|
757
|
+
}
|
|
758
|
+
hasExecuteMulti(nodeInstance) {
|
|
759
|
+
return typeof nodeInstance.executeMulti === "function";
|
|
621
760
|
}
|
|
622
761
|
resolveInputSchema(nodeInstance, config) {
|
|
623
762
|
const fromInstance = nodeInstance.inputSchema;
|
|
@@ -774,6 +913,87 @@ var InProcessRetryRunner = class InProcessRetryRunner {
|
|
|
774
913
|
}
|
|
775
914
|
};
|
|
776
915
|
|
|
916
|
+
//#endregion
|
|
917
|
+
//#region src/execution/ItemValueResolver.ts
|
|
918
|
+
/**
|
|
919
|
+
* Resolves {@link import("../contracts/itemValue").ItemValue} leaves on runnable config before {@link RunnableNode.execute}.
|
|
920
|
+
*/
|
|
921
|
+
var ItemValueResolver = class {
|
|
922
|
+
async resolveConfigForItem(ctx, item, itemIndex, items) {
|
|
923
|
+
if (!ctx) throw new Error("ItemValueResolver.resolveConfigForItem: ctx is required");
|
|
924
|
+
const resolvedConfig = await resolveItemValuesForExecution(ctx.config, ctx, item, itemIndex, items);
|
|
925
|
+
const merged = resolvedConfig !== void 0 && resolvedConfig !== null ? resolvedConfig : ctx.config;
|
|
926
|
+
if (merged === void 0 || merged === null) return ctx;
|
|
927
|
+
return {
|
|
928
|
+
...ctx,
|
|
929
|
+
config: merged
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
//#endregion
|
|
935
|
+
//#region src/contracts/emitPorts.ts
|
|
936
|
+
const EMIT_PORTS_BRAND = Symbol.for("codemation.emitPorts");
|
|
937
|
+
function emitPorts(ports) {
|
|
938
|
+
return {
|
|
939
|
+
[EMIT_PORTS_BRAND]: true,
|
|
940
|
+
ports
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
function isPortsEmission(value) {
|
|
944
|
+
return typeof value === "object" && value !== null && EMIT_PORTS_BRAND in value && value[EMIT_PORTS_BRAND] === true;
|
|
945
|
+
}
|
|
946
|
+
function isUnbrandedPortsEmissionShape(value) {
|
|
947
|
+
return typeof value === "object" && value !== null && "ports" in value && !isPortsEmission(value);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
//#endregion
|
|
951
|
+
//#region src/execution/NodeOutputNormalizer.ts
|
|
952
|
+
var NodeOutputNormalizer = class {
|
|
953
|
+
normalizeExecuteResult(args) {
|
|
954
|
+
const { baseItem, raw, carry } = args;
|
|
955
|
+
if (isPortsEmission(raw)) return this.emitPortsToOutputs(baseItem, raw, carry);
|
|
956
|
+
if (isUnbrandedPortsEmissionShape(raw)) throw new Error("execute() returned an unbranded `{ ports: ... }` object. Use emitPorts(...) for multi-port runnable outputs.");
|
|
957
|
+
if (Array.isArray(raw)) return this.arrayFanOutToMain(baseItem, raw, carry);
|
|
958
|
+
if (this.isItemLike(raw)) return { main: [this.applyLineage(baseItem, raw, carry)] };
|
|
959
|
+
return { main: [this.applyLineage(baseItem, { json: raw }, carry)] };
|
|
960
|
+
}
|
|
961
|
+
arrayFanOutToMain(baseItem, raw, carry) {
|
|
962
|
+
for (const el of raw) if (Array.isArray(el)) throw new Error("execute() fan-out arrays must contain only non-array JSON elements (nested arrays belong inside objects).");
|
|
963
|
+
return { main: raw.map((json) => this.applyLineage(baseItem, { json }, carry)) };
|
|
964
|
+
}
|
|
965
|
+
emitPortsToOutputs(baseItem, emission, carry) {
|
|
966
|
+
const out = {};
|
|
967
|
+
for (const [port, payload] of Object.entries(emission.ports)) {
|
|
968
|
+
if (payload === void 0) continue;
|
|
969
|
+
out[port] = this.normalizePortPayload(baseItem, payload, carry);
|
|
970
|
+
}
|
|
971
|
+
return out;
|
|
972
|
+
}
|
|
973
|
+
normalizePortPayload(baseItem, payload, carry) {
|
|
974
|
+
if (payload.length === 0) return [];
|
|
975
|
+
const el0 = payload[0];
|
|
976
|
+
if (this.isItemLike(el0)) return payload.map((it) => this.applyLineage(baseItem, it, carry));
|
|
977
|
+
return payload.map((json) => this.applyLineage(baseItem, { json }, carry));
|
|
978
|
+
}
|
|
979
|
+
isItemLike(value) {
|
|
980
|
+
return typeof value === "object" && value !== null && "json" in value;
|
|
981
|
+
}
|
|
982
|
+
applyLineage(baseItem, next, carry) {
|
|
983
|
+
if (carry === "carryThrough") return {
|
|
984
|
+
...baseItem,
|
|
985
|
+
...next,
|
|
986
|
+
json: next.json
|
|
987
|
+
};
|
|
988
|
+
return {
|
|
989
|
+
json: next.json,
|
|
990
|
+
...next.binary ? { binary: next.binary } : {},
|
|
991
|
+
...next.meta ? { meta: next.meta } : {},
|
|
992
|
+
...next.paired ? { paired: next.paired } : {}
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
|
|
777
997
|
//#endregion
|
|
778
998
|
//#region src/execution/InProcessRetryRunnerFactory.ts
|
|
779
999
|
var InProcessRetryRunnerFactory = class {
|
|
@@ -813,6 +1033,21 @@ var NodeActivationRequestComposer = class {
|
|
|
813
1033
|
ctx
|
|
814
1034
|
};
|
|
815
1035
|
}
|
|
1036
|
+
createMultiFromDefinitionWithActivation(args) {
|
|
1037
|
+
const ctx = this.createNodeExecutionContext(args, args.definition, args.activationId);
|
|
1038
|
+
return {
|
|
1039
|
+
kind: "multi",
|
|
1040
|
+
runId: args.runId,
|
|
1041
|
+
activationId: args.activationId,
|
|
1042
|
+
workflowId: args.workflowId,
|
|
1043
|
+
nodeId: args.definition.id,
|
|
1044
|
+
parent: args.parent,
|
|
1045
|
+
executionOptions: args.executionOptions,
|
|
1046
|
+
batchId: args.batchId,
|
|
1047
|
+
inputsByPort: args.inputsByPort,
|
|
1048
|
+
ctx
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
816
1051
|
createFromPlannedActivation(args) {
|
|
817
1052
|
const activationId = this.activationIdFactory.makeActivationId();
|
|
818
1053
|
const ctx = this.createNodeExecutionContext(args, args.nodeDefinition, activationId);
|
|
@@ -860,49 +1095,152 @@ var NodeActivationRequestComposer = class {
|
|
|
860
1095
|
//#endregion
|
|
861
1096
|
//#region src/execution/NodeExecutor.ts
|
|
862
1097
|
var NodeExecutor = class {
|
|
863
|
-
|
|
1098
|
+
fanInMerger = new FanInMergeByOriginMerger();
|
|
1099
|
+
outputNormalizer = new NodeOutputNormalizer();
|
|
1100
|
+
itemValueResolver;
|
|
1101
|
+
constructor(nodeInstanceFactory, retryRunner, itemValueResolver) {
|
|
864
1102
|
this.nodeInstanceFactory = nodeInstanceFactory;
|
|
865
1103
|
this.retryRunner = retryRunner;
|
|
1104
|
+
this.itemValueResolver = itemValueResolver ?? new ItemValueResolver();
|
|
866
1105
|
}
|
|
867
1106
|
async execute(request) {
|
|
868
1107
|
const policy = request.ctx.config.retryPolicy;
|
|
869
1108
|
return await this.retryRunner.run(policy, async () => {
|
|
870
1109
|
const nodeInstance = this.nodeInstanceFactory.createByType(request.ctx.config.type);
|
|
871
|
-
if (request.kind === "multi") return await this.
|
|
1110
|
+
if (request.kind === "multi") return await this.executeMultiInputActivation(request, nodeInstance);
|
|
872
1111
|
return await this.executeSingleInputNode(request, nodeInstance);
|
|
873
1112
|
});
|
|
874
1113
|
}
|
|
875
|
-
async
|
|
1114
|
+
async executeMultiInputActivation(request, node$1) {
|
|
876
1115
|
const multiInputNode = node$1;
|
|
877
|
-
if (typeof multiInputNode.executeMulti
|
|
878
|
-
|
|
1116
|
+
if (typeof multiInputNode.executeMulti === "function") {
|
|
1117
|
+
const raw = await multiInputNode.executeMulti(request.inputsByPort, request.ctx);
|
|
1118
|
+
this.assertNoPortEnvelopeBypass(request.nodeId, raw, "executeMulti()");
|
|
1119
|
+
return raw;
|
|
1120
|
+
}
|
|
1121
|
+
if (this.isRunnableNode(node$1)) {
|
|
1122
|
+
const merged = this.fanInMerger.merge(request.inputsByPort);
|
|
1123
|
+
const single = {
|
|
1124
|
+
...request,
|
|
1125
|
+
kind: "single",
|
|
1126
|
+
input: merged
|
|
1127
|
+
};
|
|
1128
|
+
return await this.executeRunnableActivation(single, node$1);
|
|
1129
|
+
}
|
|
1130
|
+
throw new Error(`Node ${request.nodeId} does not support executeMulti or RunnableNode.execute but received multi-input activation`);
|
|
879
1131
|
}
|
|
880
1132
|
async executeSingleInputNode(request, node$1) {
|
|
881
|
-
if (this.
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1133
|
+
if (this.isTriggerNode(node$1)) {
|
|
1134
|
+
const raw = await node$1.execute(request.input, request.ctx);
|
|
1135
|
+
this.assertNoPortEnvelopeBypass(request.nodeId, raw, "trigger execute()");
|
|
1136
|
+
return raw;
|
|
1137
|
+
}
|
|
1138
|
+
if (this.isRunnableNode(node$1)) return await this.executeRunnableActivation(request, node$1);
|
|
1139
|
+
if (this.hasExecuteMulti(node$1)) return await this.executeMultiInputActivation(this.asMultiFromSingleActivation(request), node$1);
|
|
1140
|
+
throw new Error(`Node ${request.nodeId} does not support trigger or RunnableNode execution`);
|
|
885
1141
|
}
|
|
886
|
-
|
|
887
|
-
return typeof node$1 === "object" && node$1 !== null &&
|
|
1142
|
+
isTriggerNode(node$1) {
|
|
1143
|
+
return typeof node$1 === "object" && node$1 !== null && node$1.kind === "trigger";
|
|
888
1144
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1145
|
+
isRunnableNode(node$1) {
|
|
1146
|
+
return typeof node$1 === "object" && node$1 !== null && node$1.kind === "node" && typeof node$1.execute === "function";
|
|
1147
|
+
}
|
|
1148
|
+
hasExecuteMulti(node$1) {
|
|
1149
|
+
return typeof node$1?.executeMulti === "function";
|
|
1150
|
+
}
|
|
1151
|
+
asMultiFromSingleActivation(request) {
|
|
1152
|
+
return {
|
|
1153
|
+
kind: "multi",
|
|
1154
|
+
runId: request.runId,
|
|
1155
|
+
activationId: request.activationId,
|
|
1156
|
+
workflowId: request.workflowId,
|
|
1157
|
+
nodeId: request.nodeId,
|
|
1158
|
+
parent: request.parent,
|
|
1159
|
+
executionOptions: request.executionOptions,
|
|
1160
|
+
batchId: request.batchId,
|
|
1161
|
+
ctx: request.ctx,
|
|
1162
|
+
inputsByPort: { in: request.input ?? [] }
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
async executeRunnableActivation(request, node$1) {
|
|
1166
|
+
const runnableConfig = request.ctx.config;
|
|
1167
|
+
const carry = this.resolveLineageCarry(node$1, runnableConfig);
|
|
1168
|
+
const inputSchema = this.resolveInputSchema(node$1, runnableConfig);
|
|
1169
|
+
const inputBatch = request.input ?? [];
|
|
1170
|
+
if (inputBatch.length === 0 && runnableConfig.emptyBatchExecution === "runOnce") {
|
|
1171
|
+
const syntheticItem = { json: {} };
|
|
1172
|
+
const parsed = inputSchema.parse(syntheticItem.json);
|
|
1173
|
+
const runnableCtx = request.ctx;
|
|
1174
|
+
const resolvedCtx = await this.itemValueResolver.resolveConfigForItem(runnableCtx, syntheticItem, 0, inputBatch);
|
|
1175
|
+
const args = {
|
|
1176
|
+
input: parsed,
|
|
1177
|
+
item: syntheticItem,
|
|
1178
|
+
itemIndex: 0,
|
|
1179
|
+
items: inputBatch,
|
|
1180
|
+
ctx: this.pickExecutionContext(runnableCtx, resolvedCtx)
|
|
1181
|
+
};
|
|
1182
|
+
const raw = await Promise.resolve(node$1.execute(args));
|
|
1183
|
+
return this.outputNormalizer.normalizeExecuteResult({
|
|
1184
|
+
baseItem: syntheticItem,
|
|
1185
|
+
raw,
|
|
1186
|
+
carry
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
const byPort = {};
|
|
1190
|
+
for (let i = 0; i < inputBatch.length; i++) {
|
|
1191
|
+
const item = inputBatch[i];
|
|
1192
|
+
this.assertItemJsonNotTopLevelArray(request.nodeId, item);
|
|
1193
|
+
const parsed = inputSchema.parse(item.json);
|
|
1194
|
+
const runnableCtx = request.ctx;
|
|
1195
|
+
const resolvedCtx = await this.itemValueResolver.resolveConfigForItem(runnableCtx, item, i, inputBatch);
|
|
1196
|
+
const ctx = this.pickExecutionContext(runnableCtx, resolvedCtx);
|
|
1197
|
+
const args = {
|
|
1198
|
+
input: parsed,
|
|
895
1199
|
item,
|
|
896
1200
|
itemIndex: i,
|
|
897
|
-
items:
|
|
898
|
-
ctx
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1201
|
+
items: inputBatch,
|
|
1202
|
+
ctx
|
|
1203
|
+
};
|
|
1204
|
+
const raw = await Promise.resolve(node$1.execute(args));
|
|
1205
|
+
const normalized = this.outputNormalizer.normalizeExecuteResult({
|
|
1206
|
+
baseItem: item,
|
|
1207
|
+
raw,
|
|
1208
|
+
carry
|
|
903
1209
|
});
|
|
1210
|
+
for (const [port, batch] of Object.entries(normalized)) {
|
|
1211
|
+
if (!batch || batch.length === 0) continue;
|
|
1212
|
+
const list = byPort[port] ?? [];
|
|
1213
|
+
list.push(...batch);
|
|
1214
|
+
byPort[port] = list;
|
|
1215
|
+
}
|
|
904
1216
|
}
|
|
905
|
-
return
|
|
1217
|
+
return byPort;
|
|
1218
|
+
}
|
|
1219
|
+
/** Use resolver ctx only when {@link NodeExecutionContext.config} is non-nullish. */
|
|
1220
|
+
pickExecutionContext(runnableCtx, resolvedCtx) {
|
|
1221
|
+
if (resolvedCtx != null && resolvedCtx.config != null) return resolvedCtx;
|
|
1222
|
+
return runnableCtx;
|
|
1223
|
+
}
|
|
1224
|
+
resolveInputSchema(nodeInstance, config) {
|
|
1225
|
+
const fromInstance = nodeInstance.inputSchema;
|
|
1226
|
+
if (fromInstance && typeof fromInstance.parse === "function") return fromInstance;
|
|
1227
|
+
const fromConfig = config.inputSchema;
|
|
1228
|
+
if (fromConfig && typeof fromConfig.parse === "function") return fromConfig;
|
|
1229
|
+
return z.unknown();
|
|
1230
|
+
}
|
|
1231
|
+
assertItemJsonNotTopLevelArray(nodeId, item) {
|
|
1232
|
+
if (Array.isArray(item.json)) throw new Error(`Node ${nodeId}: item.json must not be a top-level JSON array`);
|
|
1233
|
+
}
|
|
1234
|
+
assertNoPortEnvelopeBypass(nodeId, value, methodName) {
|
|
1235
|
+
if (isPortsEmission(value)) throw new Error(`Node ${nodeId}: ${methodName} must return NodeOutputs, not emitPorts(...).`);
|
|
1236
|
+
if (isUnbrandedPortsEmissionShape(value)) throw new Error(`Node ${nodeId}: ${methodName} returned an unbranded \`{ ports: ... }\` object. Return NodeOutputs instead.`);
|
|
1237
|
+
}
|
|
1238
|
+
resolveLineageCarry(node$1, config) {
|
|
1239
|
+
if (config.lineageCarry) return config.lineageCarry;
|
|
1240
|
+
const base = config;
|
|
1241
|
+
const declared = base.declaredOutputPorts;
|
|
1242
|
+
if ((declared && declared.length > 0 ? [...new Set([...declared, ...base.nodeErrorHandler ? ["error"] : []])] : base.nodeErrorHandler ? ["main", "error"] : ["main"]).length > 1) return "carryThrough";
|
|
1243
|
+
return "emitOnly";
|
|
906
1244
|
}
|
|
907
1245
|
};
|
|
908
1246
|
|
|
@@ -980,8 +1318,8 @@ var MissingRuntimeFallbacks = class {
|
|
|
980
1318
|
var MissingRuntimeNode = class {
|
|
981
1319
|
kind = "node";
|
|
982
1320
|
outputPorts = ["main"];
|
|
983
|
-
|
|
984
|
-
return
|
|
1321
|
+
execute(args) {
|
|
1322
|
+
return args.item;
|
|
985
1323
|
}
|
|
986
1324
|
};
|
|
987
1325
|
|
|
@@ -1170,6 +1508,7 @@ var WorkflowSnapshotCodec = class {
|
|
|
1170
1508
|
}
|
|
1171
1509
|
restoreNonSerializableProperties(liveRecord, hydrated) {
|
|
1172
1510
|
for (const [key, value] of Object.entries(liveRecord)) if (typeof value === "function" || typeof value === "symbol") hydrated[key] = value;
|
|
1511
|
+
for (const sym of Object.getOwnPropertySymbols(liveRecord)) hydrated[sym] = liveRecord[sym];
|
|
1173
1512
|
}
|
|
1174
1513
|
restoreTypeProperty(record) {
|
|
1175
1514
|
const tokenId = typeof record.tokenId === "string" ? record.tokenId : void 0;
|
|
@@ -1189,7 +1528,10 @@ var WorkflowSnapshotCodec = class {
|
|
|
1189
1528
|
}
|
|
1190
1529
|
asRecord(value) {
|
|
1191
1530
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
1192
|
-
|
|
1531
|
+
const record = value;
|
|
1532
|
+
const out = { ...record };
|
|
1533
|
+
for (const sym of Object.getOwnPropertySymbols(value)) out[sym] = record[sym];
|
|
1534
|
+
return out;
|
|
1193
1535
|
}
|
|
1194
1536
|
};
|
|
1195
1537
|
|
|
@@ -1566,19 +1908,6 @@ var WorkflowTopology = class WorkflowTopology {
|
|
|
1566
1908
|
const classifier = WorkflowExecutableNodeClassifierFactory.create(wf);
|
|
1567
1909
|
const defs = /* @__PURE__ */ new Map();
|
|
1568
1910
|
for (const n of wf.nodes) if (classifier.isExecutableNodeId(n.id)) defs.set(n.id, n);
|
|
1569
|
-
const outgoing = /* @__PURE__ */ new Map();
|
|
1570
|
-
for (const e of wf.edges) {
|
|
1571
|
-
if (!classifier.isExecutableNodeId(e.from.nodeId) || !classifier.isExecutableNodeId(e.to.nodeId)) continue;
|
|
1572
|
-
const list = outgoing.get(e.from.nodeId) ?? [];
|
|
1573
|
-
list.push({
|
|
1574
|
-
output: e.from.output,
|
|
1575
|
-
to: {
|
|
1576
|
-
nodeId: e.to.nodeId,
|
|
1577
|
-
input: e.to.input
|
|
1578
|
-
}
|
|
1579
|
-
});
|
|
1580
|
-
outgoing.set(e.from.nodeId, list);
|
|
1581
|
-
}
|
|
1582
1911
|
const incomingByNode = /* @__PURE__ */ new Map();
|
|
1583
1912
|
for (const e of wf.edges) {
|
|
1584
1913
|
if (!classifier.isExecutableNodeId(e.from.nodeId) || !classifier.isExecutableNodeId(e.to.nodeId)) continue;
|
|
@@ -1588,21 +1917,51 @@ var WorkflowTopology = class WorkflowTopology {
|
|
|
1588
1917
|
nodeId: e.from.nodeId,
|
|
1589
1918
|
output: e.from.output
|
|
1590
1919
|
},
|
|
1591
|
-
input: e.to.input
|
|
1920
|
+
input: e.to.input,
|
|
1921
|
+
collectKey: e.to.input
|
|
1592
1922
|
});
|
|
1593
1923
|
incomingByNode.set(e.to.nodeId, list);
|
|
1594
1924
|
}
|
|
1595
|
-
const
|
|
1596
|
-
for (const [toNodeId,
|
|
1925
|
+
const duplicateInputCounts = /* @__PURE__ */ new Map();
|
|
1926
|
+
for (const [toNodeId, edges] of incomingByNode.entries()) {
|
|
1597
1927
|
const counts = /* @__PURE__ */ new Map();
|
|
1598
|
-
for (const edge of
|
|
1599
|
-
|
|
1928
|
+
for (const edge of edges) counts.set(edge.input, (counts.get(edge.input) ?? 0) + 1);
|
|
1929
|
+
duplicateInputCounts.set(toNodeId, counts);
|
|
1930
|
+
}
|
|
1931
|
+
for (const [toNodeId, edges] of incomingByNode.entries()) {
|
|
1932
|
+
const counts = duplicateInputCounts.get(toNodeId) ?? /* @__PURE__ */ new Map();
|
|
1933
|
+
for (let i = 0; i < edges.length; i++) {
|
|
1934
|
+
const edge = edges[i];
|
|
1935
|
+
const collectKey = (counts.get(edge.input) ?? 0) > 1 ? `${edge.from.nodeId}:${edge.from.output}` : edge.input;
|
|
1936
|
+
edges[i] = {
|
|
1937
|
+
...edge,
|
|
1938
|
+
collectKey
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
1943
|
+
for (const e of wf.edges) {
|
|
1944
|
+
if (!classifier.isExecutableNodeId(e.from.nodeId) || !classifier.isExecutableNodeId(e.to.nodeId)) continue;
|
|
1945
|
+
const collectKey = ((duplicateInputCounts.get(e.to.nodeId) ?? /* @__PURE__ */ new Map()).get(e.to.input) ?? 0) > 1 ? `${e.from.nodeId}:${e.from.output}` : e.to.input;
|
|
1946
|
+
const list = outgoing.get(e.from.nodeId) ?? [];
|
|
1947
|
+
list.push({
|
|
1948
|
+
output: e.from.output,
|
|
1949
|
+
to: {
|
|
1950
|
+
nodeId: e.to.nodeId,
|
|
1951
|
+
input: e.to.input,
|
|
1952
|
+
collectKey
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
outgoing.set(e.from.nodeId, list);
|
|
1956
|
+
}
|
|
1957
|
+
const expected = /* @__PURE__ */ new Map();
|
|
1958
|
+
for (const [toNodeId, edges] of incomingByNode.entries()) {
|
|
1600
1959
|
const order = [];
|
|
1601
1960
|
const seen = /* @__PURE__ */ new Set();
|
|
1602
|
-
for (const edge of
|
|
1603
|
-
if (seen.has(edge.
|
|
1604
|
-
seen.add(edge.
|
|
1605
|
-
order.push(edge.
|
|
1961
|
+
for (const edge of edges) {
|
|
1962
|
+
if (seen.has(edge.collectKey)) continue;
|
|
1963
|
+
seen.add(edge.collectKey);
|
|
1964
|
+
order.push(edge.collectKey);
|
|
1606
1965
|
}
|
|
1607
1966
|
expected.set(toNodeId, order);
|
|
1608
1967
|
}
|
|
@@ -2108,6 +2467,11 @@ var RunContinuationService = class {
|
|
|
2108
2467
|
batchId
|
|
2109
2468
|
});
|
|
2110
2469
|
const next = planner.nextActivation(queue);
|
|
2470
|
+
const finishedAt = completedSnapshot.finishedAt ?? completedSnapshot.updatedAt;
|
|
2471
|
+
const mergedSnapshots = {
|
|
2472
|
+
...args.state.nodeSnapshotsByNodeId ?? {},
|
|
2473
|
+
[args.args.nodeId]: completedSnapshot
|
|
2474
|
+
};
|
|
2111
2475
|
if (!next) {
|
|
2112
2476
|
const lastNodeId = WorkflowExecutableNodeClassifierFactory.create(args.workflow).lastExecutableNodeIdInDefinitionOrder(args.workflow);
|
|
2113
2477
|
const outputs = data.getOutputItems(lastNodeId, "main");
|
|
@@ -2117,11 +2481,8 @@ var RunContinuationService = class {
|
|
|
2117
2481
|
status: "completed",
|
|
2118
2482
|
queue: [],
|
|
2119
2483
|
outputsByNode: data.dump(),
|
|
2120
|
-
nodeSnapshotsByNodeId:
|
|
2121
|
-
|
|
2122
|
-
[args.args.nodeId]: completedSnapshot
|
|
2123
|
-
},
|
|
2124
|
-
finishedAtIso: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
2484
|
+
nodeSnapshotsByNodeId: mergedSnapshots,
|
|
2485
|
+
finishedAtIso: finishedAt
|
|
2125
2486
|
});
|
|
2126
2487
|
await this.workflowExecutionRepository.save(completedState);
|
|
2127
2488
|
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
@@ -2156,11 +2517,8 @@ var RunContinuationService = class {
|
|
|
2156
2517
|
status: "failed",
|
|
2157
2518
|
queue: queue.map((q) => ({ ...q })),
|
|
2158
2519
|
outputsByNode: data.dump(),
|
|
2159
|
-
nodeSnapshotsByNodeId:
|
|
2160
|
-
|
|
2161
|
-
[args.args.nodeId]: completedSnapshot
|
|
2162
|
-
},
|
|
2163
|
-
finishedAtIso: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
2520
|
+
nodeSnapshotsByNodeId: mergedSnapshots,
|
|
2521
|
+
finishedAtIso: finishedAt
|
|
2164
2522
|
});
|
|
2165
2523
|
await this.workflowExecutionRepository.save(failedState);
|
|
2166
2524
|
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
@@ -2211,10 +2569,6 @@ var RunContinuationService = class {
|
|
|
2211
2569
|
executionOptions: args.state.executionOptions,
|
|
2212
2570
|
nodeDefinition: nextDefinition
|
|
2213
2571
|
});
|
|
2214
|
-
const mergedSnapshots = {
|
|
2215
|
-
...args.state.nodeSnapshotsByNodeId ?? {},
|
|
2216
|
-
[args.args.nodeId]: completedSnapshot
|
|
2217
|
-
};
|
|
2218
2572
|
try {
|
|
2219
2573
|
const { queuedSnapshot, result } = await this.activationEnqueueService.enqueueActivationWithSnapshot({
|
|
2220
2574
|
runId: args.state.runId,
|
|
@@ -2245,7 +2599,6 @@ var RunContinuationService = class {
|
|
|
2245
2599
|
return result;
|
|
2246
2600
|
} catch (cause) {
|
|
2247
2601
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
2248
|
-
const finishedAt = completedSnapshot.finishedAt ?? completedSnapshot.updatedAt;
|
|
2249
2602
|
const result = await this.terminateRunAfterActivationEnqueueRejected({
|
|
2250
2603
|
wf: args.workflow,
|
|
2251
2604
|
state: args.state,
|
|
@@ -2533,7 +2886,7 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2533
2886
|
const frontierNodeIds = [];
|
|
2534
2887
|
for (const nodeId of this.topology.defsById.keys()) {
|
|
2535
2888
|
if (!requiredNodeIds.has(nodeId) || this.isNodeSatisfied(currentState, nodeId)) continue;
|
|
2536
|
-
if ((this.topology.incomingByNode.get(nodeId) ?? []).every((edge) => this.isEdgeSatisfied(currentState, nodeId, edge.
|
|
2889
|
+
if ((this.topology.incomingByNode.get(nodeId) ?? []).every((edge) => this.isEdgeSatisfied(currentState, nodeId, edge.collectKey))) frontierNodeIds.push(nodeId);
|
|
2537
2890
|
}
|
|
2538
2891
|
return frontierNodeIds;
|
|
2539
2892
|
}
|
|
@@ -2551,13 +2904,13 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2551
2904
|
if (requiredNodeIds.has(nodeId)) return;
|
|
2552
2905
|
if (this.isNodeSatisfied(currentState, nodeId) && !this.isNodeSatisfiedByOutputsOnly(currentState, nodeId)) return;
|
|
2553
2906
|
requiredNodeIds.add(nodeId);
|
|
2554
|
-
for (const edge of this.topology.incomingByNode.get(nodeId) ?? []) if (!this.isEdgeSatisfied(currentState, nodeId, edge.
|
|
2907
|
+
for (const edge of this.topology.incomingByNode.get(nodeId) ?? []) if (!this.isEdgeSatisfied(currentState, nodeId, edge.collectKey) || this.isNodeSatisfiedByOutputsOnly(currentState, edge.from.nodeId)) this.collectRequiredNode(requiredNodeIds, currentState, edge.from.nodeId);
|
|
2555
2908
|
}
|
|
2556
2909
|
buildFrontierQueue(nodeId, currentState) {
|
|
2557
2910
|
const incomingEdges = this.topology.incomingByNode.get(nodeId) ?? [];
|
|
2558
2911
|
if (incomingEdges.length === 0) return [];
|
|
2559
2912
|
const expectedInputs = this.topology.expectedInputsByNode.get(nodeId) ?? [];
|
|
2560
|
-
if (
|
|
2913
|
+
if (this.usesCollect(nodeId)) {
|
|
2561
2914
|
const received = {};
|
|
2562
2915
|
for (const input$2 of expectedInputs) received[input$2] = this.resolveInput(currentState, nodeId, input$2);
|
|
2563
2916
|
return [{
|
|
@@ -2571,7 +2924,7 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2571
2924
|
}];
|
|
2572
2925
|
}
|
|
2573
2926
|
const input$1 = expectedInputs[0] ?? "in";
|
|
2574
|
-
const incomingEdge = incomingEdges.find((edge) => edge.
|
|
2927
|
+
const incomingEdge = incomingEdges.find((edge) => edge.collectKey === input$1);
|
|
2575
2928
|
return [{
|
|
2576
2929
|
nodeId,
|
|
2577
2930
|
input: this.resolveInput(currentState, nodeId, input$1),
|
|
@@ -2591,16 +2944,17 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2591
2944
|
isNodeSatisfiedByOutputsOnly(currentState, nodeId) {
|
|
2592
2945
|
return this.hasOutputs(currentState, nodeId) && !this.hasCompletedSnapshot(currentState, nodeId);
|
|
2593
2946
|
}
|
|
2594
|
-
isEdgeSatisfied(currentState, nodeId,
|
|
2595
|
-
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.
|
|
2947
|
+
isEdgeSatisfied(currentState, nodeId, collectKey) {
|
|
2948
|
+
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.collectKey === collectKey);
|
|
2596
2949
|
if (!incomingEdge) return false;
|
|
2597
|
-
|
|
2950
|
+
const fromNodeId = incomingEdge.from.nodeId;
|
|
2951
|
+
if (!this.isNodeSatisfied(currentState, fromNodeId)) return false;
|
|
2598
2952
|
if (this.usesCollect(nodeId)) return true;
|
|
2599
|
-
if (this.resolveOutputItems(currentState,
|
|
2600
|
-
return this.shouldContinueAfterEmptyOutputFromSource(
|
|
2953
|
+
if (this.resolveOutputItems(currentState, fromNodeId, incomingEdge.from.output).length > 0) return true;
|
|
2954
|
+
return this.shouldContinueAfterEmptyOutputFromSource(fromNodeId);
|
|
2601
2955
|
}
|
|
2602
|
-
resolveInput(currentState, nodeId,
|
|
2603
|
-
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.
|
|
2956
|
+
resolveInput(currentState, nodeId, collectKey) {
|
|
2957
|
+
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.collectKey === collectKey);
|
|
2604
2958
|
if (!incomingEdge) return [];
|
|
2605
2959
|
return this.resolveOutputItems(currentState, incomingEdge.from.nodeId, incomingEdge.from.output);
|
|
2606
2960
|
}
|
|
@@ -2611,17 +2965,13 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2611
2965
|
const snapshot = currentState.nodeSnapshotsByNodeId[nodeId];
|
|
2612
2966
|
return snapshot?.status === "completed" || snapshot?.status === "skipped";
|
|
2613
2967
|
}
|
|
2614
|
-
hasOutputPort(currentState, nodeId, output$1) {
|
|
2615
|
-
const outputs = currentState.outputsByNode[nodeId];
|
|
2616
|
-
if (!outputs) return false;
|
|
2617
|
-
return Object.prototype.hasOwnProperty.call(outputs, output$1);
|
|
2618
|
-
}
|
|
2619
2968
|
resolveOutputItems(currentState, nodeId, output$1) {
|
|
2620
2969
|
return currentState.outputsByNode[nodeId]?.[output$1] ?? [];
|
|
2621
2970
|
}
|
|
2622
2971
|
usesCollect(nodeId) {
|
|
2623
2972
|
const expectedInputs = this.topology.expectedInputsByNode.get(nodeId) ?? [];
|
|
2624
|
-
|
|
2973
|
+
if (expectedInputs.length !== 1 || expectedInputs[0] !== "in") return true;
|
|
2974
|
+
return (this.topology.incomingByNode.get(nodeId) ?? []).length > 1;
|
|
2625
2975
|
}
|
|
2626
2976
|
shouldContinueAfterEmptyOutputFromSource(nodeId) {
|
|
2627
2977
|
const definition = this.topology.defsById.get(nodeId);
|
|
@@ -3513,7 +3863,6 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
3513
3863
|
const resolvedParent = request.parent ?? state.parent;
|
|
3514
3864
|
const data = this.runDataFactory.create(state.outputsByNode);
|
|
3515
3865
|
const limits = this.resolveEngineLimitsFromState(state);
|
|
3516
|
-
const persistedInput = pendingExecution.inputsByPort.in ?? request.input;
|
|
3517
3866
|
const base = this.runExecutionContextFactory.create({
|
|
3518
3867
|
runId: state.runId,
|
|
3519
3868
|
workflowId: state.workflowId,
|
|
@@ -3525,7 +3874,25 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
3525
3874
|
data,
|
|
3526
3875
|
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, resolvedParent)
|
|
3527
3876
|
});
|
|
3528
|
-
const
|
|
3877
|
+
const inputsByPort = pendingExecution.inputsByPort;
|
|
3878
|
+
const portKeys = Object.keys(inputsByPort);
|
|
3879
|
+
const kind = portKeys.length === 1 && portKeys[0] === "in" ? "single" : "multi";
|
|
3880
|
+
const batchId = pendingExecution.batchId ?? "batch_1";
|
|
3881
|
+
const activationRequest = kind === "multi" ? this.nodeActivationRequestComposer.createMultiFromDefinitionWithActivation({
|
|
3882
|
+
activationId: request.activationId,
|
|
3883
|
+
runId: request.runId,
|
|
3884
|
+
workflowId: request.workflowId,
|
|
3885
|
+
parent: resolvedParent,
|
|
3886
|
+
executionOptions: request.executionOptions ?? state.executionOptions,
|
|
3887
|
+
base,
|
|
3888
|
+
data,
|
|
3889
|
+
definition: {
|
|
3890
|
+
id: definition.id,
|
|
3891
|
+
config: definition.config
|
|
3892
|
+
},
|
|
3893
|
+
batchId,
|
|
3894
|
+
inputsByPort
|
|
3895
|
+
}) : this.nodeActivationRequestComposer.createSingleFromDefinitionWithActivation({
|
|
3529
3896
|
activationId: request.activationId,
|
|
3530
3897
|
runId: request.runId,
|
|
3531
3898
|
workflowId: request.workflowId,
|
|
@@ -3537,8 +3904,8 @@ var NodeExecutionRequestHandlerService = class {
|
|
|
3537
3904
|
id: definition.id,
|
|
3538
3905
|
config: definition.config
|
|
3539
3906
|
},
|
|
3540
|
-
batchId
|
|
3541
|
-
input:
|
|
3907
|
+
batchId,
|
|
3908
|
+
input: inputsByPort.in ?? request.input ?? []
|
|
3542
3909
|
});
|
|
3543
3910
|
await this.continuation.markNodeRunning({
|
|
3544
3911
|
runId: activationRequest.runId,
|
|
@@ -3623,7 +3990,7 @@ var RunQueuePlanner = class {
|
|
|
3623
3990
|
continue;
|
|
3624
3991
|
}
|
|
3625
3992
|
const inst = this.nodeInstances.get(toNodeId);
|
|
3626
|
-
if (!this.isMultiInputNode(inst)) throw new Error(`Node ${toNodeId} has ${inputs.length} inbound edges
|
|
3993
|
+
if (!this.isMultiInputNode(inst) && !this.supportsEngineFanInMerge(inst)) throw new Error(`Node ${toNodeId} has ${inputs.length} inbound edges but does not support multi-input execution.`);
|
|
3627
3994
|
}
|
|
3628
3995
|
}
|
|
3629
3996
|
seedFromTrigger(args) {
|
|
@@ -3632,7 +3999,7 @@ var RunQueuePlanner = class {
|
|
|
3632
3999
|
if (e.output !== "main") continue;
|
|
3633
4000
|
this.enqueueEdge(queue, {
|
|
3634
4001
|
batchId: args.batchId,
|
|
3635
|
-
to: e
|
|
4002
|
+
to: this.toEnqueueTarget(e),
|
|
3636
4003
|
from: {
|
|
3637
4004
|
nodeId: args.startNodeId,
|
|
3638
4005
|
output: "main"
|
|
@@ -3647,7 +4014,7 @@ var RunQueuePlanner = class {
|
|
|
3647
4014
|
const outItems = args.outputs[e.output] ?? [];
|
|
3648
4015
|
this.enqueueEdge(queue, {
|
|
3649
4016
|
batchId: args.batchId,
|
|
3650
|
-
to: e
|
|
4017
|
+
to: this.toEnqueueTarget(e),
|
|
3651
4018
|
from: {
|
|
3652
4019
|
nodeId: args.fromNodeId,
|
|
3653
4020
|
output: e.output
|
|
@@ -3729,29 +4096,35 @@ var RunQueuePlanner = class {
|
|
|
3729
4096
|
*/
|
|
3730
4097
|
usesTopologyCollectMerge(toNodeId) {
|
|
3731
4098
|
const expectedInputs = this.topology.expectedInputsByNode.get(toNodeId) ?? [];
|
|
3732
|
-
|
|
4099
|
+
if (expectedInputs.length !== 1 || expectedInputs[0] !== "in") return true;
|
|
4100
|
+
return (this.topology.incomingByNode.get(toNodeId) ?? []).length > 1;
|
|
4101
|
+
}
|
|
4102
|
+
toEnqueueTarget(edge) {
|
|
4103
|
+
return edge.to;
|
|
3733
4104
|
}
|
|
3734
|
-
enqueueEdge(queue, args) {
|
|
4105
|
+
enqueueEdge(queue, args, emptyPathSourceNodeId) {
|
|
3735
4106
|
const target = this.nodeInstances.get(args.to.nodeId);
|
|
3736
4107
|
if (!(this.usesTopologyCollectMerge(args.to.nodeId) || this.isMultiInputNode(target))) {
|
|
3737
4108
|
if (args.items.length === 0) {
|
|
3738
|
-
|
|
4109
|
+
const continueSourceNodeId = emptyPathSourceNodeId ?? args.from.nodeId;
|
|
4110
|
+
if (this.shouldContinueAfterEmptyOutputFromSource(continueSourceNodeId)) {
|
|
3739
4111
|
queue.push({
|
|
3740
4112
|
nodeId: args.to.nodeId,
|
|
3741
4113
|
input: args.items,
|
|
3742
|
-
toInput: args.to.
|
|
4114
|
+
toInput: args.to.collectKey,
|
|
3743
4115
|
batchId: args.batchId,
|
|
3744
4116
|
from: args.from
|
|
3745
4117
|
});
|
|
3746
4118
|
return;
|
|
3747
4119
|
}
|
|
3748
|
-
|
|
4120
|
+
const source = emptyPathSourceNodeId ?? args.from.nodeId;
|
|
4121
|
+
this.propagateEmptyPath(queue, args.to.nodeId, args.batchId, source);
|
|
3749
4122
|
return;
|
|
3750
4123
|
}
|
|
3751
4124
|
queue.push({
|
|
3752
4125
|
nodeId: args.to.nodeId,
|
|
3753
4126
|
input: args.items,
|
|
3754
|
-
toInput: args.to.
|
|
4127
|
+
toInput: args.to.collectKey,
|
|
3755
4128
|
batchId: args.batchId,
|
|
3756
4129
|
from: args.from
|
|
3757
4130
|
});
|
|
@@ -3772,14 +4145,14 @@ var RunQueuePlanner = class {
|
|
|
3772
4145
|
queue.push(collect);
|
|
3773
4146
|
}
|
|
3774
4147
|
const received = collect.collect.received;
|
|
3775
|
-
received[args.to.
|
|
4148
|
+
received[args.to.collectKey] = args.items;
|
|
3776
4149
|
}
|
|
3777
4150
|
shouldContinueAfterEmptyOutputFromSource(fromNodeId) {
|
|
3778
4151
|
const def = this.topology.defsById.get(fromNodeId);
|
|
3779
4152
|
if (!def) return false;
|
|
3780
4153
|
return def.config.continueWhenEmptyOutput === true;
|
|
3781
4154
|
}
|
|
3782
|
-
propagateEmptyPath(queue, nodeId, batchId) {
|
|
4155
|
+
propagateEmptyPath(queue, nodeId, batchId, emptyPathSourceNodeId) {
|
|
3783
4156
|
for (const edge of this.topology.outgoingByNode.get(nodeId) ?? []) this.enqueueEdge(queue, {
|
|
3784
4157
|
batchId,
|
|
3785
4158
|
to: edge.to,
|
|
@@ -3788,11 +4161,17 @@ var RunQueuePlanner = class {
|
|
|
3788
4161
|
output: edge.output
|
|
3789
4162
|
},
|
|
3790
4163
|
items: []
|
|
3791
|
-
});
|
|
4164
|
+
}, emptyPathSourceNodeId);
|
|
3792
4165
|
}
|
|
3793
4166
|
isMultiInputNode(n) {
|
|
3794
4167
|
return typeof n?.executeMulti === "function";
|
|
3795
4168
|
}
|
|
4169
|
+
hasRunnableExecute(n) {
|
|
4170
|
+
return typeof n === "object" && n !== null && n.kind === "node" && typeof n.execute === "function";
|
|
4171
|
+
}
|
|
4172
|
+
supportsEngineFanInMerge(n) {
|
|
4173
|
+
return this.hasRunnableExecute(n) && !this.isMultiInputNode(n);
|
|
4174
|
+
}
|
|
3796
4175
|
describeUnsatisfiedCollect(queueEntry) {
|
|
3797
4176
|
const batchId = queueEntry.batchId ?? "batch_1";
|
|
3798
4177
|
const expectedInputs = queueEntry.collect?.expectedInputs ?? [];
|
|
@@ -3824,7 +4203,7 @@ var RunQueuePlanner = class {
|
|
|
3824
4203
|
}
|
|
3825
4204
|
findSources(nodeId, input$1) {
|
|
3826
4205
|
const matches = [];
|
|
3827
|
-
for (const [sourceNodeId, edges] of this.topology.outgoingByNode.entries()) for (const edge of edges) if (edge.to.nodeId === nodeId && edge.to.
|
|
4206
|
+
for (const [sourceNodeId, edges] of this.topology.outgoingByNode.entries()) for (const edge of edges) if (edge.to.nodeId === nodeId && edge.to.collectKey === input$1) matches.push(this.formatNodeLabel(sourceNodeId));
|
|
3828
4207
|
return matches;
|
|
3829
4208
|
}
|
|
3830
4209
|
formatInputList(inputs) {
|
|
@@ -4499,5 +4878,5 @@ var WorkflowRepositoryWebhookTriggerMatcherFactory = class {
|
|
|
4499
4878
|
};
|
|
4500
4879
|
|
|
4501
4880
|
//#endregion
|
|
4502
|
-
export {
|
|
4503
|
-
//# sourceMappingURL=runtime-
|
|
4881
|
+
export { getPersistedRuntimeTypeMetadata as $, NodeExecutor as A, CredentialResolverFactory as B, RunPolicySnapshotFactory as C, PersistedWorkflowTokenRegistry as D, WorkflowSnapshotCodec as E, isUnbrandedPortsEmissionShape as F, WorkflowExecutableNodeClassifierFactory as G, DefaultExecutionBinaryService as H, ItemValueResolver as I, isItemValue as J, WorkflowExecutableNodeClassifier as K, InProcessRetryRunner as L, NodeOutputNormalizer as M, emitPorts as N, MissingRuntimeTriggerToken as O, isPortsEmission as P, chatModel as Q, DefaultExecutionContextFactory as R, ConfigDrivenOffloadPolicy as S, NodeInstanceFactory as T, UnavailableBinaryStorage as U, getOriginIndexFromItem as V, NodeEventPublisher as W, resolveItemValuesForExecution as X, itemValue as Y, resolveItemValuesInUnknown as Z, EngineExecutionLimitsPolicy as _, InMemoryLiveWorkflowRepository as a, PersistedRuntimeTypeNameResolver as at, HintOnlyOffloadPolicy as b, EngineFactory as c, inject as ct, InMemoryRunDataFactory as d, instanceCachingFactory as dt, node as et, InMemoryBinaryStorage as f, instancePerContainerCachingFactory as ft, ENGINE_EXECUTION_LIMITS_DEFAULTS as g, CoreTokens as gt, RunTerminalPersistenceCoordinator as h, singleton as ht, RunIntentService as i, StackTraceCallSitePathResolver as it, InProcessRetryRunnerFactory as j, NodeExecutorFactory as k, Engine as l, injectAll as lt, WorkflowPolicyErrorServices as m, registry as mt, WorkflowRepositoryWebhookTriggerMatcher as n, InjectableRuntimeDecoratorComposer as nt, EngineWorkflowRunnerServiceFactory as o, container$1 as ot, WorkflowStoragePolicyEvaluator as p, predicateAwareClassFactory as pt, ConnectionNodeIdFactory as q, RunIntentServiceFactory as r, PersistedRuntimeTypeMetadataStore as rt, EngineWorkflowRunnerService as s, delay as st, WorkflowRepositoryWebhookTriggerMatcherFactory as t, tool as tt, RunFinishedAtFactory as u, injectable as ut, LocalOnlyScheduler as v, NodeInstanceFactoryFactory as w, DefaultDrivingScheduler as x, InlineDrivingScheduler as y, DefaultAsyncSleeper as z };
|
|
4882
|
+
//# sourceMappingURL=runtime-BdH94eBR.js.map
|