@codemation/core 0.0.18 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/EngineRuntimeRegistration.types-0sgV2XL2.d.ts +42 -0
- package/dist/EngineWorkflowRunnerService-Dx7bJsJR.d.cts +73 -0
- package/dist/InMemoryRunDataFactory-qIYQEar7.d.cts +94 -0
- package/dist/{InMemoryLiveWorkflowRepository-DxoualoC.d.ts → RunIntentService-BCvGdOSY.d.ts} +438 -9
- package/dist/{RunIntentService-BB4nqX3-.js → RunIntentService-BFA48UpH.js} +308 -71
- package/dist/RunIntentService-BFA48UpH.js.map +1 -0
- package/dist/{InMemoryLiveWorkflowRepository-orY1VsWG.d.cts → RunIntentService-CV8izV8t.d.cts} +214 -7
- package/dist/{RunIntentService-nRx-m0Xs.cjs → RunIntentService-DcxXf_AM.cjs} +318 -69
- package/dist/RunIntentService-DcxXf_AM.cjs.map +1 -0
- package/dist/bootstrap/index.cjs +14 -1135
- package/dist/bootstrap/index.d.cts +7 -60
- package/dist/bootstrap/index.d.ts +4 -40
- package/dist/bootstrap/index.js +3 -1122
- package/dist/bootstrap-D67Sf2BF.js +1136 -0
- package/dist/bootstrap-D67Sf2BF.js.map +1 -0
- package/dist/bootstrap-DoQHAEQJ.cjs +1203 -0
- package/dist/bootstrap-DoQHAEQJ.cjs.map +1 -0
- package/dist/{index-B4_ZRTyI.d.ts → index-BHmrZIHp.d.ts} +32 -251
- package/dist/index.cjs +98 -223
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +196 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +92 -218
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +329 -3
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +181 -4
- package/dist/testing.d.ts +181 -3
- package/dist/testing.js +319 -2
- package/dist/testing.js.map +1 -1
- package/dist/workflowActivationPolicy-B8HzTk3o.js +201 -0
- package/dist/workflowActivationPolicy-B8HzTk3o.js.map +1 -0
- package/dist/workflowActivationPolicy-BzyzXLa_.cjs +231 -0
- package/dist/workflowActivationPolicy-BzyzXLa_.cjs.map +1 -0
- package/package.json +1 -1
- package/src/ai/AgentConnectionNodeCollector.ts +99 -0
- package/src/ai/AgentToolFactory.ts +38 -2
- package/src/ai/AiHost.ts +1 -1
- package/src/browser.ts +11 -0
- package/src/contracts/executionPersistenceContracts.ts +186 -0
- package/src/contracts/index.ts +1 -0
- package/src/contracts/runFinishedAtFactory.ts +5 -2
- package/src/contracts/runTypes.ts +10 -0
- package/src/contracts/runtimeTypes.ts +6 -2
- package/src/contracts/workflowTypes.ts +3 -2
- package/src/events/EventPublishingWorkflowExecutionRepository.ts +5 -0
- package/src/execution/ActivationEnqueueService.ts +8 -8
- package/src/execution/PersistedRunStateTerminalBuilder.ts +3 -0
- package/src/index.ts +6 -0
- package/src/orchestration/NodeExecutionRequestHandlerService.ts +11 -6
- package/src/orchestration/RunContinuationService.ts +94 -24
- package/src/planning/CurrentStateFrontierPlanner.ts +24 -1
- package/src/runStorage/InMemoryWorkflowExecutionRepository.ts +14 -1
- package/src/runtime/RunIntentService.ts +68 -14
- package/src/scheduler/DefaultDrivingScheduler.ts +21 -11
- package/src/scheduler/InlineDrivingScheduler.ts +17 -21
- package/src/testing/CapturingScheduler.ts +15 -0
- package/src/testing/EngineTestKitRunIdFactory.ts +24 -0
- package/src/testing/InMemoryTriggerSetupStateRepository.ts +21 -0
- package/src/testing/PrefixedSequentialIdGenerator.ts +17 -0
- package/src/testing/RegistrarEngineTestKit.types.ts +76 -0
- package/src/testing/RegistrarEngineTestKitFactory.ts +154 -0
- package/src/testing/SubWorkflowRunnerTestNode.ts +83 -0
- package/src/testing/WorkflowTestHarnessManualTrigger.ts +39 -0
- package/src/testing/WorkflowTestKit.types.ts +9 -0
- package/src/testing/WorkflowTestKitBuilder.ts +77 -0
- package/src/testing/WorkflowTestKitNodeRegistrationContextFactory.ts +17 -0
- package/src/testing/WorkflowTestKitRunNodeWorkflowFactory.ts +26 -0
- package/src/testing.ts +19 -0
- package/src/types/index.ts +1 -0
- package/src/workflow/definition/ConnectionNodeIdFactory.ts +28 -0
- package/dist/InMemoryLiveWorkflowRepository-BTzHpQ6e.cjs +0 -151
- package/dist/InMemoryLiveWorkflowRepository-BTzHpQ6e.cjs.map +0 -1
- package/dist/InMemoryLiveWorkflowRepository-BoLNnVLg.js +0 -139
- package/dist/InMemoryLiveWorkflowRepository-BoLNnVLg.js.map +0 -1
- package/dist/RunIntentService-BB4nqX3-.js.map +0 -1
- package/dist/RunIntentService-ByuUYsAL.d.cts +0 -279
- package/dist/RunIntentService-nRx-m0Xs.cjs.map +0 -1
- package/dist/WorkflowSnapshotCodec-DSEzKyt3.d.cts +0 -22
- package/dist/bootstrap/index.cjs.map +0 -1
- package/dist/bootstrap/index.js.map +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
//#region src/workflow/dsl/WhenBuilder.ts
|
|
2
|
+
var WhenBuilder = class WhenBuilder {
|
|
3
|
+
constructor(wf, from, branchPort) {
|
|
4
|
+
this.wf = wf;
|
|
5
|
+
this.from = from;
|
|
6
|
+
this.branchPort = branchPort;
|
|
7
|
+
}
|
|
8
|
+
addBranch(steps) {
|
|
9
|
+
const created = [];
|
|
10
|
+
let prev = null;
|
|
11
|
+
for (const cfg of steps) {
|
|
12
|
+
const ref = this.wf.add(cfg);
|
|
13
|
+
created.push(ref);
|
|
14
|
+
if (!prev) this.wf.connect(this.from, ref, this.branchPort, "in");
|
|
15
|
+
else this.wf.connect(prev, ref, "main", "in");
|
|
16
|
+
prev = ref;
|
|
17
|
+
}
|
|
18
|
+
for (const cfg of steps) {
|
|
19
|
+
const maybe = cfg;
|
|
20
|
+
if (!Array.isArray(maybe.upstreamRefs) || maybe.upstreamRefs.length === 0) continue;
|
|
21
|
+
maybe.upstreamRefs = maybe.upstreamRefs.map((r) => {
|
|
22
|
+
if (typeof r !== "string") return r;
|
|
23
|
+
const nodeId = created[parseInt(r.slice(1), 10)]?.id;
|
|
24
|
+
return nodeId ? { nodeId } : { nodeId: r };
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
when = (branch, steps, ...more) => {
|
|
30
|
+
const list = Array.isArray(steps) ? steps : [steps, ...more];
|
|
31
|
+
const port = branch ? "true" : "false";
|
|
32
|
+
const b = new WhenBuilder(this.wf, this.from, port);
|
|
33
|
+
b.addBranch(list);
|
|
34
|
+
return b;
|
|
35
|
+
};
|
|
36
|
+
build() {
|
|
37
|
+
return this.wf.build();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/workflow/dsl/ChainCursorResolver.ts
|
|
43
|
+
var ChainCursor = class ChainCursor {
|
|
44
|
+
constructor(wf, cursor, cursorOutput) {
|
|
45
|
+
this.wf = wf;
|
|
46
|
+
this.cursor = cursor;
|
|
47
|
+
this.cursorOutput = cursorOutput;
|
|
48
|
+
}
|
|
49
|
+
then(config) {
|
|
50
|
+
const next = this.wf.add(config);
|
|
51
|
+
this.wf.connect(this.cursor, next, this.cursorOutput);
|
|
52
|
+
return new ChainCursor(this.wf, next, "main");
|
|
53
|
+
}
|
|
54
|
+
when = ((arg1, steps, ...more) => {
|
|
55
|
+
if (typeof arg1 === "boolean") {
|
|
56
|
+
const list = Array.isArray(steps) ? steps : steps ? [steps, ...more] : more;
|
|
57
|
+
const port = arg1 ? "true" : "false";
|
|
58
|
+
const b = new WhenBuilder(this.wf, this.cursor, port);
|
|
59
|
+
b.addBranch(list);
|
|
60
|
+
return b;
|
|
61
|
+
}
|
|
62
|
+
const branches = arg1;
|
|
63
|
+
const makeMerge = this.wf.options?.makeMergeNode;
|
|
64
|
+
if (!makeMerge) throw new Error("WorkflowBuilder is missing options.makeMergeNode (required for when({true,false}). Use createWorkflowBuilder from \"@codemation/core-nodes\".");
|
|
65
|
+
const wfAny = this.wf;
|
|
66
|
+
const buildBranch = (port, branchSteps) => {
|
|
67
|
+
const list = branchSteps ?? [];
|
|
68
|
+
let prev = null;
|
|
69
|
+
for (const cfg of list) {
|
|
70
|
+
const ref = wfAny.add(cfg);
|
|
71
|
+
if (!prev) wfAny.connect(this.cursor, ref, port, "in");
|
|
72
|
+
else wfAny.connect(prev, ref, "main", "in");
|
|
73
|
+
prev = ref;
|
|
74
|
+
}
|
|
75
|
+
if (!prev) return {
|
|
76
|
+
end: this.cursor,
|
|
77
|
+
endOutput: port
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
end: prev,
|
|
81
|
+
endOutput: "main"
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
const t = buildBranch("true", branches.true);
|
|
85
|
+
const f = buildBranch("false", branches.false);
|
|
86
|
+
const merge = wfAny.add(makeMerge("Merge (auto)"));
|
|
87
|
+
wfAny.connect(t.end, merge, t.endOutput, "true");
|
|
88
|
+
wfAny.connect(f.end, merge, f.endOutput, "false");
|
|
89
|
+
return new ChainCursor(this.wf, merge, "main");
|
|
90
|
+
});
|
|
91
|
+
build() {
|
|
92
|
+
return this.wf.build();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/workflow/dsl/WorkflowBuilder.ts
|
|
98
|
+
var WorkflowBuilder = class {
|
|
99
|
+
nodes = [];
|
|
100
|
+
edges = [];
|
|
101
|
+
seq = 0;
|
|
102
|
+
constructor(meta, options) {
|
|
103
|
+
this.meta = meta;
|
|
104
|
+
this.options = options;
|
|
105
|
+
}
|
|
106
|
+
add(config) {
|
|
107
|
+
const tokenName = typeof config.type === "function" ? config.type.name : String(config.type);
|
|
108
|
+
const id = config.id ?? `${tokenName}:${++this.seq}`;
|
|
109
|
+
this.nodes.push({
|
|
110
|
+
id,
|
|
111
|
+
kind: config.kind,
|
|
112
|
+
type: config.type,
|
|
113
|
+
name: config.name,
|
|
114
|
+
config
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
id,
|
|
118
|
+
kind: config.kind,
|
|
119
|
+
name: config.name
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
connect(from, to, fromOutput = "main", toInput = "in") {
|
|
123
|
+
this.edges.push({
|
|
124
|
+
from: {
|
|
125
|
+
nodeId: from.id,
|
|
126
|
+
output: fromOutput
|
|
127
|
+
},
|
|
128
|
+
to: {
|
|
129
|
+
nodeId: to.id,
|
|
130
|
+
input: toInput
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
trigger(config) {
|
|
135
|
+
const ref = this.add(config);
|
|
136
|
+
return new ChainCursor(this, ref, "main");
|
|
137
|
+
}
|
|
138
|
+
start(config) {
|
|
139
|
+
const ref = this.add(config);
|
|
140
|
+
return new ChainCursor(this, ref, "main");
|
|
141
|
+
}
|
|
142
|
+
build() {
|
|
143
|
+
return {
|
|
144
|
+
...this.meta,
|
|
145
|
+
nodes: this.nodes,
|
|
146
|
+
edges: this.edges
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/events/InMemoryRunEventSubscription.ts
|
|
153
|
+
var InMemoryRunEventSubscription = class {
|
|
154
|
+
constructor(onClose) {
|
|
155
|
+
this.onClose = onClose;
|
|
156
|
+
}
|
|
157
|
+
async close() {
|
|
158
|
+
this.onClose();
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region src/events/InMemoryRunEventBusRegistry.ts
|
|
164
|
+
var InMemoryRunEventBus = class {
|
|
165
|
+
globalListeners = /* @__PURE__ */ new Set();
|
|
166
|
+
listenersByWorkflowId = /* @__PURE__ */ new Map();
|
|
167
|
+
async publish(event) {
|
|
168
|
+
for (const listener of this.globalListeners) listener(event);
|
|
169
|
+
for (const listener of this.listenersByWorkflowId.get(event.workflowId) ?? []) listener(event);
|
|
170
|
+
}
|
|
171
|
+
async subscribe(onEvent) {
|
|
172
|
+
this.globalListeners.add(onEvent);
|
|
173
|
+
return new InMemoryRunEventSubscription(() => {
|
|
174
|
+
this.globalListeners.delete(onEvent);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async subscribeToWorkflow(workflowId, onEvent) {
|
|
178
|
+
const existing = this.listenersByWorkflowId.get(workflowId) ?? /* @__PURE__ */ new Set();
|
|
179
|
+
existing.add(onEvent);
|
|
180
|
+
this.listenersByWorkflowId.set(workflowId, existing);
|
|
181
|
+
return new InMemoryRunEventSubscription(() => {
|
|
182
|
+
const listeners = this.listenersByWorkflowId.get(workflowId);
|
|
183
|
+
if (!listeners) return;
|
|
184
|
+
listeners.delete(onEvent);
|
|
185
|
+
if (listeners.size === 0) this.listenersByWorkflowId.delete(workflowId);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/contracts/workflowActivationPolicy.ts
|
|
192
|
+
/** Default for tests and harnesses: every workflow is treated as active (legacy behavior). */
|
|
193
|
+
var AllWorkflowsActiveWorkflowActivationPolicy = class {
|
|
194
|
+
isActive(_workflowId) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
export { WhenBuilder as a, ChainCursor as i, InMemoryRunEventBus as n, WorkflowBuilder as r, AllWorkflowsActiveWorkflowActivationPolicy as t };
|
|
201
|
+
//# sourceMappingURL=workflowActivationPolicy-B8HzTk3o.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowActivationPolicy-B8HzTk3o.js","names":["wf: WorkflowBuilder","from: NodeRef","branchPort: OutputPortKey","created: NodeRef[]","prev: NodeRef | null","port: OutputPortKey","wf: WorkflowBuilder","cursor: NodeRef","cursorOutput: OutputPortKey","port: OutputPortKey","prev: NodeRef | null","meta: { id: WorkflowId; name: string }","options?: Readonly<{\n makeMergeNode?: (name: string) => AnyRunnableNodeConfig;\n }>","onClose: () => void"],"sources":["../src/workflow/dsl/WhenBuilder.ts","../src/workflow/dsl/ChainCursorResolver.ts","../src/workflow/dsl/WorkflowBuilder.ts","../src/events/InMemoryRunEventSubscription.ts","../src/events/InMemoryRunEventBusRegistry.ts","../src/contracts/workflowActivationPolicy.ts"],"sourcesContent":["import type { NodeId, NodeRef, OutputPortKey, UpstreamRefPlaceholder, WorkflowDefinition } from \"../../types\";\n\nimport { WorkflowBuilder } from \"./WorkflowBuilder\";\nimport type { AnyRunnableNodeConfig, BooleanWhenOverloads, ValidStepSequence } from \"./workflowBuilderTypes\";\n\nexport class WhenBuilder<TCurrentJson> {\n constructor(\n private readonly wf: WorkflowBuilder,\n private readonly from: NodeRef,\n private readonly branchPort: OutputPortKey,\n ) {}\n\n addBranch<TSteps extends ReadonlyArray<AnyRunnableNodeConfig>>(\n steps: TSteps & ValidStepSequence<TCurrentJson, TSteps>,\n ): this {\n const created: NodeRef[] = [];\n\n let prev: NodeRef | null = null;\n for (const cfg of steps) {\n const ref = (this.wf as any).add(cfg) as NodeRef;\n created.push(ref);\n if (!prev) (this.wf as any).connect(this.from, ref, this.branchPort, \"in\");\n else (this.wf as any).connect(prev, ref, \"main\", \"in\");\n prev = ref;\n }\n\n for (const cfg of steps) {\n const maybe = cfg as unknown as { upstreamRefs?: Array<{ nodeId: NodeId } | UpstreamRefPlaceholder> };\n if (!Array.isArray(maybe.upstreamRefs) || maybe.upstreamRefs.length === 0) continue;\n\n maybe.upstreamRefs = maybe.upstreamRefs.map((r) => {\n if (typeof r !== \"string\") return r;\n const idx = parseInt(r.slice(1), 10);\n const nodeId = created[idx]?.id;\n return nodeId ? { nodeId } : { nodeId: r };\n });\n }\n\n return this;\n }\n\n readonly when: BooleanWhenOverloads<TCurrentJson, WhenBuilder<TCurrentJson>> = (\n branch: boolean,\n steps: ReadonlyArray<AnyRunnableNodeConfig> | AnyRunnableNodeConfig,\n ...more: AnyRunnableNodeConfig[]\n ): WhenBuilder<TCurrentJson> => {\n const list = Array.isArray(steps) ? steps : [steps, ...more];\n const port: OutputPortKey = branch ? \"true\" : \"false\";\n const b = new WhenBuilder<TCurrentJson>(this.wf, this.from, port);\n b.addBranch(list);\n return b;\n };\n\n build(): WorkflowDefinition {\n return this.wf.build();\n }\n}\n","import type {\n NodeRef,\n OutputPortKey,\n RunnableNodeConfig,\n RunnableNodeOutputJson,\n WorkflowDefinition,\n} from \"../../types\";\n\nimport { WorkflowBuilder } from \"./WorkflowBuilder\";\nimport { WhenBuilder } from \"./WhenBuilder\";\nimport type {\n AnyRunnableNodeConfig,\n BooleanWhenOverloads,\n BranchOutputGuard,\n BranchStepsArg,\n StepSequenceOutput,\n} from \"./workflowBuilderTypes\";\n\ntype ChainCursorWhenOverloads<TCurrentJson> = BooleanWhenOverloads<TCurrentJson, WhenBuilder<TCurrentJson>> & {\n <\n TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined,\n TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined,\n >(\n branches: Readonly<{\n true?: TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> ? BranchStepsArg<TCurrentJson, TTrueSteps> : never;\n false?: TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig>\n ? BranchStepsArg<TCurrentJson, TFalseSteps>\n : never;\n }> &\n BranchOutputGuard<TCurrentJson, TTrueSteps, TFalseSteps>,\n ): ChainCursor<StepSequenceOutput<TCurrentJson, TTrueSteps>>;\n};\n\nexport class ChainCursor<TCurrentJson> {\n constructor(\n private readonly wf: WorkflowBuilder,\n private readonly cursor: NodeRef,\n private readonly cursorOutput: OutputPortKey,\n ) {}\n\n then<TConfig extends RunnableNodeConfig<TCurrentJson, any>>(\n config: TConfig,\n ): ChainCursor<RunnableNodeOutputJson<TConfig>> {\n const next = (this.wf as any).add(config) as NodeRef;\n (this.wf as any).connect(this.cursor, next, this.cursorOutput);\n return new ChainCursor<RunnableNodeOutputJson<TConfig>>(this.wf, next, \"main\");\n }\n\n readonly when: ChainCursorWhenOverloads<TCurrentJson> = ((\n arg1:\n | boolean\n | Readonly<{ true?: ReadonlyArray<AnyRunnableNodeConfig>; false?: ReadonlyArray<AnyRunnableNodeConfig> }>,\n steps?: ReadonlyArray<AnyRunnableNodeConfig> | AnyRunnableNodeConfig,\n ...more: AnyRunnableNodeConfig[]\n ): WhenBuilder<TCurrentJson> | ChainCursor<TCurrentJson> => {\n if (typeof arg1 === \"boolean\") {\n const list = Array.isArray(steps) ? steps : steps ? [steps, ...more] : more;\n const port: OutputPortKey = arg1 ? \"true\" : \"false\";\n const b = new WhenBuilder<TCurrentJson>(this.wf, this.cursor, port);\n b.addBranch(list);\n return b;\n }\n\n const branches = arg1;\n const makeMerge = (this.wf as any).options?.makeMergeNode as ((name: string) => AnyRunnableNodeConfig) | undefined;\n if (!makeMerge) {\n throw new Error(\n 'WorkflowBuilder is missing options.makeMergeNode (required for when({true,false}). Use createWorkflowBuilder from \"@codemation/core-nodes\".',\n );\n }\n\n const wfAny = this.wf as any;\n\n const buildBranch = (\n port: OutputPortKey,\n branchSteps: ReadonlyArray<AnyRunnableNodeConfig> | undefined,\n ): Readonly<{ end: NodeRef; endOutput: OutputPortKey }> => {\n const list = branchSteps ?? [];\n let prev: NodeRef | null = null;\n for (const cfg of list) {\n const ref = wfAny.add(cfg) as NodeRef;\n if (!prev) wfAny.connect(this.cursor, ref, port, \"in\");\n else wfAny.connect(prev, ref, \"main\", \"in\");\n prev = ref;\n }\n if (!prev) return { end: this.cursor, endOutput: port };\n return { end: prev, endOutput: \"main\" };\n };\n\n const t = buildBranch(\"true\", branches.true);\n const f = buildBranch(\"false\", branches.false);\n\n const merge = wfAny.add(makeMerge(\"Merge (auto)\")) as NodeRef;\n wfAny.connect(t.end, merge, t.endOutput, \"true\");\n wfAny.connect(f.end, merge, f.endOutput, \"false\");\n\n return new ChainCursor<TCurrentJson>(this.wf, merge, \"main\");\n }) as ChainCursorWhenOverloads<TCurrentJson>;\n\n build(): WorkflowDefinition {\n return this.wf.build();\n }\n}\n","import type {\n InputPortKey,\n NodeConfigBase,\n NodeDefinition,\n NodeRef,\n OutputPortKey,\n RunnableNodeOutputJson,\n TriggerNodeOutputJson,\n WorkflowDefinition,\n WorkflowId,\n} from \"../../types\";\n\nimport { ChainCursor } from \"./ChainCursorResolver\";\nimport type { AnyRunnableNodeConfig, AnyTriggerNodeConfig } from \"./workflowBuilderTypes\";\n\nexport class WorkflowBuilder {\n private readonly nodes: NodeDefinition[] = [];\n private readonly edges: WorkflowDefinition[\"edges\"] = [];\n private seq = 0;\n\n constructor(\n private readonly meta: { id: WorkflowId; name: string },\n private readonly options?: Readonly<{\n makeMergeNode?: (name: string) => AnyRunnableNodeConfig;\n }>,\n ) {}\n\n private add(config: NodeConfigBase): NodeRef {\n const tokenName = typeof config.type === \"function\" ? config.type.name : String(config.type);\n const id = config.id ?? `${tokenName}:${++this.seq}`;\n this.nodes.push({ id, kind: config.kind, type: config.type, name: config.name, config });\n return { id, kind: config.kind, name: config.name };\n }\n\n private connect(from: NodeRef, to: NodeRef, fromOutput: OutputPortKey = \"main\", toInput: InputPortKey = \"in\"): void {\n this.edges.push({ from: { nodeId: from.id, output: fromOutput }, to: { nodeId: to.id, input: toInput } });\n }\n\n trigger<TConfig extends AnyTriggerNodeConfig>(config: TConfig): ChainCursor<TriggerNodeOutputJson<TConfig>> {\n const ref = this.add(config);\n return new ChainCursor<TriggerNodeOutputJson<TConfig>>(this, ref, \"main\");\n }\n\n start<TConfig extends AnyRunnableNodeConfig>(config: TConfig): ChainCursor<RunnableNodeOutputJson<TConfig>> {\n const ref = this.add(config);\n return new ChainCursor<RunnableNodeOutputJson<TConfig>>(this, ref, \"main\");\n }\n\n build(): WorkflowDefinition {\n return { ...this.meta, nodes: this.nodes, edges: this.edges };\n }\n}\n\nexport { ChainCursor } from \"./ChainCursorResolver\";\nexport { WhenBuilder } from \"./WhenBuilder\";\n","import type { RunEventSubscription } from \"./runEvents\";\n\nexport class InMemoryRunEventSubscription implements RunEventSubscription {\n constructor(private readonly onClose: () => void) {}\n\n async close(): Promise<void> {\n this.onClose();\n }\n}\n","import type { WorkflowId } from \"../types\";\n\nimport type { RunEvent, RunEventBus, RunEventSubscription } from \"./runEvents\";\n\nimport { InMemoryRunEventSubscription } from \"./InMemoryRunEventSubscription\";\n\nexport class InMemoryRunEventBus implements RunEventBus {\n private readonly globalListeners = new Set<(event: RunEvent) => void>();\n private readonly listenersByWorkflowId = new Map<WorkflowId, Set<(event: RunEvent) => void>>();\n\n async publish(event: RunEvent): Promise<void> {\n for (const listener of this.globalListeners) listener(event);\n for (const listener of this.listenersByWorkflowId.get(event.workflowId) ?? []) listener(event);\n }\n\n async subscribe(onEvent: (event: RunEvent) => void): Promise<RunEventSubscription> {\n this.globalListeners.add(onEvent);\n return new InMemoryRunEventSubscription(() => {\n this.globalListeners.delete(onEvent);\n });\n }\n\n async subscribeToWorkflow(workflowId: WorkflowId, onEvent: (event: RunEvent) => void): Promise<RunEventSubscription> {\n const existing = this.listenersByWorkflowId.get(workflowId) ?? new Set<(event: RunEvent) => void>();\n existing.add(onEvent);\n this.listenersByWorkflowId.set(workflowId, existing);\n\n return new InMemoryRunEventSubscription(() => {\n const listeners = this.listenersByWorkflowId.get(workflowId);\n if (!listeners) return;\n listeners.delete(onEvent);\n if (listeners.size === 0) this.listenersByWorkflowId.delete(workflowId);\n });\n }\n}\n\nexport { InMemoryRunEventSubscription } from \"./InMemoryRunEventSubscription\";\n","import type { WorkflowId } from \"./workflowTypes\";\n\n/**\n * Host-controlled policy: when false, trigger {@link TriggerNode} setup is skipped and webhook routes\n * for that workflow are not registered (see engine trigger runtime + webhook matcher).\n */\nexport interface WorkflowActivationPolicy {\n isActive(workflowId: WorkflowId): boolean;\n}\n\n/** Default for tests and harnesses: every workflow is treated as active (legacy behavior). */\nexport class AllWorkflowsActiveWorkflowActivationPolicy implements WorkflowActivationPolicy {\n isActive(_workflowId: WorkflowId): boolean {\n return true;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/workflow/dsl/WhenBuilder.ts
|
|
3
|
+
var WhenBuilder = class WhenBuilder {
|
|
4
|
+
constructor(wf, from, branchPort) {
|
|
5
|
+
this.wf = wf;
|
|
6
|
+
this.from = from;
|
|
7
|
+
this.branchPort = branchPort;
|
|
8
|
+
}
|
|
9
|
+
addBranch(steps) {
|
|
10
|
+
const created = [];
|
|
11
|
+
let prev = null;
|
|
12
|
+
for (const cfg of steps) {
|
|
13
|
+
const ref = this.wf.add(cfg);
|
|
14
|
+
created.push(ref);
|
|
15
|
+
if (!prev) this.wf.connect(this.from, ref, this.branchPort, "in");
|
|
16
|
+
else this.wf.connect(prev, ref, "main", "in");
|
|
17
|
+
prev = ref;
|
|
18
|
+
}
|
|
19
|
+
for (const cfg of steps) {
|
|
20
|
+
const maybe = cfg;
|
|
21
|
+
if (!Array.isArray(maybe.upstreamRefs) || maybe.upstreamRefs.length === 0) continue;
|
|
22
|
+
maybe.upstreamRefs = maybe.upstreamRefs.map((r) => {
|
|
23
|
+
if (typeof r !== "string") return r;
|
|
24
|
+
const nodeId = created[parseInt(r.slice(1), 10)]?.id;
|
|
25
|
+
return nodeId ? { nodeId } : { nodeId: r };
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
when = (branch, steps, ...more) => {
|
|
31
|
+
const list = Array.isArray(steps) ? steps : [steps, ...more];
|
|
32
|
+
const port = branch ? "true" : "false";
|
|
33
|
+
const b = new WhenBuilder(this.wf, this.from, port);
|
|
34
|
+
b.addBranch(list);
|
|
35
|
+
return b;
|
|
36
|
+
};
|
|
37
|
+
build() {
|
|
38
|
+
return this.wf.build();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/workflow/dsl/ChainCursorResolver.ts
|
|
44
|
+
var ChainCursor = class ChainCursor {
|
|
45
|
+
constructor(wf, cursor, cursorOutput) {
|
|
46
|
+
this.wf = wf;
|
|
47
|
+
this.cursor = cursor;
|
|
48
|
+
this.cursorOutput = cursorOutput;
|
|
49
|
+
}
|
|
50
|
+
then(config) {
|
|
51
|
+
const next = this.wf.add(config);
|
|
52
|
+
this.wf.connect(this.cursor, next, this.cursorOutput);
|
|
53
|
+
return new ChainCursor(this.wf, next, "main");
|
|
54
|
+
}
|
|
55
|
+
when = ((arg1, steps, ...more) => {
|
|
56
|
+
if (typeof arg1 === "boolean") {
|
|
57
|
+
const list = Array.isArray(steps) ? steps : steps ? [steps, ...more] : more;
|
|
58
|
+
const port = arg1 ? "true" : "false";
|
|
59
|
+
const b = new WhenBuilder(this.wf, this.cursor, port);
|
|
60
|
+
b.addBranch(list);
|
|
61
|
+
return b;
|
|
62
|
+
}
|
|
63
|
+
const branches = arg1;
|
|
64
|
+
const makeMerge = this.wf.options?.makeMergeNode;
|
|
65
|
+
if (!makeMerge) throw new Error("WorkflowBuilder is missing options.makeMergeNode (required for when({true,false}). Use createWorkflowBuilder from \"@codemation/core-nodes\".");
|
|
66
|
+
const wfAny = this.wf;
|
|
67
|
+
const buildBranch = (port, branchSteps) => {
|
|
68
|
+
const list = branchSteps ?? [];
|
|
69
|
+
let prev = null;
|
|
70
|
+
for (const cfg of list) {
|
|
71
|
+
const ref = wfAny.add(cfg);
|
|
72
|
+
if (!prev) wfAny.connect(this.cursor, ref, port, "in");
|
|
73
|
+
else wfAny.connect(prev, ref, "main", "in");
|
|
74
|
+
prev = ref;
|
|
75
|
+
}
|
|
76
|
+
if (!prev) return {
|
|
77
|
+
end: this.cursor,
|
|
78
|
+
endOutput: port
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
end: prev,
|
|
82
|
+
endOutput: "main"
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
const t = buildBranch("true", branches.true);
|
|
86
|
+
const f = buildBranch("false", branches.false);
|
|
87
|
+
const merge = wfAny.add(makeMerge("Merge (auto)"));
|
|
88
|
+
wfAny.connect(t.end, merge, t.endOutput, "true");
|
|
89
|
+
wfAny.connect(f.end, merge, f.endOutput, "false");
|
|
90
|
+
return new ChainCursor(this.wf, merge, "main");
|
|
91
|
+
});
|
|
92
|
+
build() {
|
|
93
|
+
return this.wf.build();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/workflow/dsl/WorkflowBuilder.ts
|
|
99
|
+
var WorkflowBuilder = class {
|
|
100
|
+
nodes = [];
|
|
101
|
+
edges = [];
|
|
102
|
+
seq = 0;
|
|
103
|
+
constructor(meta, options) {
|
|
104
|
+
this.meta = meta;
|
|
105
|
+
this.options = options;
|
|
106
|
+
}
|
|
107
|
+
add(config) {
|
|
108
|
+
const tokenName = typeof config.type === "function" ? config.type.name : String(config.type);
|
|
109
|
+
const id = config.id ?? `${tokenName}:${++this.seq}`;
|
|
110
|
+
this.nodes.push({
|
|
111
|
+
id,
|
|
112
|
+
kind: config.kind,
|
|
113
|
+
type: config.type,
|
|
114
|
+
name: config.name,
|
|
115
|
+
config
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
id,
|
|
119
|
+
kind: config.kind,
|
|
120
|
+
name: config.name
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
connect(from, to, fromOutput = "main", toInput = "in") {
|
|
124
|
+
this.edges.push({
|
|
125
|
+
from: {
|
|
126
|
+
nodeId: from.id,
|
|
127
|
+
output: fromOutput
|
|
128
|
+
},
|
|
129
|
+
to: {
|
|
130
|
+
nodeId: to.id,
|
|
131
|
+
input: toInput
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
trigger(config) {
|
|
136
|
+
const ref = this.add(config);
|
|
137
|
+
return new ChainCursor(this, ref, "main");
|
|
138
|
+
}
|
|
139
|
+
start(config) {
|
|
140
|
+
const ref = this.add(config);
|
|
141
|
+
return new ChainCursor(this, ref, "main");
|
|
142
|
+
}
|
|
143
|
+
build() {
|
|
144
|
+
return {
|
|
145
|
+
...this.meta,
|
|
146
|
+
nodes: this.nodes,
|
|
147
|
+
edges: this.edges
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/events/InMemoryRunEventSubscription.ts
|
|
154
|
+
var InMemoryRunEventSubscription = class {
|
|
155
|
+
constructor(onClose) {
|
|
156
|
+
this.onClose = onClose;
|
|
157
|
+
}
|
|
158
|
+
async close() {
|
|
159
|
+
this.onClose();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/events/InMemoryRunEventBusRegistry.ts
|
|
165
|
+
var InMemoryRunEventBus = class {
|
|
166
|
+
globalListeners = /* @__PURE__ */ new Set();
|
|
167
|
+
listenersByWorkflowId = /* @__PURE__ */ new Map();
|
|
168
|
+
async publish(event) {
|
|
169
|
+
for (const listener of this.globalListeners) listener(event);
|
|
170
|
+
for (const listener of this.listenersByWorkflowId.get(event.workflowId) ?? []) listener(event);
|
|
171
|
+
}
|
|
172
|
+
async subscribe(onEvent) {
|
|
173
|
+
this.globalListeners.add(onEvent);
|
|
174
|
+
return new InMemoryRunEventSubscription(() => {
|
|
175
|
+
this.globalListeners.delete(onEvent);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
async subscribeToWorkflow(workflowId, onEvent) {
|
|
179
|
+
const existing = this.listenersByWorkflowId.get(workflowId) ?? /* @__PURE__ */ new Set();
|
|
180
|
+
existing.add(onEvent);
|
|
181
|
+
this.listenersByWorkflowId.set(workflowId, existing);
|
|
182
|
+
return new InMemoryRunEventSubscription(() => {
|
|
183
|
+
const listeners = this.listenersByWorkflowId.get(workflowId);
|
|
184
|
+
if (!listeners) return;
|
|
185
|
+
listeners.delete(onEvent);
|
|
186
|
+
if (listeners.size === 0) this.listenersByWorkflowId.delete(workflowId);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/contracts/workflowActivationPolicy.ts
|
|
193
|
+
/** Default for tests and harnesses: every workflow is treated as active (legacy behavior). */
|
|
194
|
+
var AllWorkflowsActiveWorkflowActivationPolicy = class {
|
|
195
|
+
isActive(_workflowId) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
//#endregion
|
|
201
|
+
Object.defineProperty(exports, 'AllWorkflowsActiveWorkflowActivationPolicy', {
|
|
202
|
+
enumerable: true,
|
|
203
|
+
get: function () {
|
|
204
|
+
return AllWorkflowsActiveWorkflowActivationPolicy;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
Object.defineProperty(exports, 'ChainCursor', {
|
|
208
|
+
enumerable: true,
|
|
209
|
+
get: function () {
|
|
210
|
+
return ChainCursor;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
Object.defineProperty(exports, 'InMemoryRunEventBus', {
|
|
214
|
+
enumerable: true,
|
|
215
|
+
get: function () {
|
|
216
|
+
return InMemoryRunEventBus;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
Object.defineProperty(exports, 'WhenBuilder', {
|
|
220
|
+
enumerable: true,
|
|
221
|
+
get: function () {
|
|
222
|
+
return WhenBuilder;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
Object.defineProperty(exports, 'WorkflowBuilder', {
|
|
226
|
+
enumerable: true,
|
|
227
|
+
get: function () {
|
|
228
|
+
return WorkflowBuilder;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
//# sourceMappingURL=workflowActivationPolicy-BzyzXLa_.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowActivationPolicy-BzyzXLa_.cjs","names":["wf: WorkflowBuilder","from: NodeRef","branchPort: OutputPortKey","created: NodeRef[]","prev: NodeRef | null","port: OutputPortKey","wf: WorkflowBuilder","cursor: NodeRef","cursorOutput: OutputPortKey","port: OutputPortKey","prev: NodeRef | null","meta: { id: WorkflowId; name: string }","options?: Readonly<{\n makeMergeNode?: (name: string) => AnyRunnableNodeConfig;\n }>","onClose: () => void"],"sources":["../src/workflow/dsl/WhenBuilder.ts","../src/workflow/dsl/ChainCursorResolver.ts","../src/workflow/dsl/WorkflowBuilder.ts","../src/events/InMemoryRunEventSubscription.ts","../src/events/InMemoryRunEventBusRegistry.ts","../src/contracts/workflowActivationPolicy.ts"],"sourcesContent":["import type { NodeId, NodeRef, OutputPortKey, UpstreamRefPlaceholder, WorkflowDefinition } from \"../../types\";\n\nimport { WorkflowBuilder } from \"./WorkflowBuilder\";\nimport type { AnyRunnableNodeConfig, BooleanWhenOverloads, ValidStepSequence } from \"./workflowBuilderTypes\";\n\nexport class WhenBuilder<TCurrentJson> {\n constructor(\n private readonly wf: WorkflowBuilder,\n private readonly from: NodeRef,\n private readonly branchPort: OutputPortKey,\n ) {}\n\n addBranch<TSteps extends ReadonlyArray<AnyRunnableNodeConfig>>(\n steps: TSteps & ValidStepSequence<TCurrentJson, TSteps>,\n ): this {\n const created: NodeRef[] = [];\n\n let prev: NodeRef | null = null;\n for (const cfg of steps) {\n const ref = (this.wf as any).add(cfg) as NodeRef;\n created.push(ref);\n if (!prev) (this.wf as any).connect(this.from, ref, this.branchPort, \"in\");\n else (this.wf as any).connect(prev, ref, \"main\", \"in\");\n prev = ref;\n }\n\n for (const cfg of steps) {\n const maybe = cfg as unknown as { upstreamRefs?: Array<{ nodeId: NodeId } | UpstreamRefPlaceholder> };\n if (!Array.isArray(maybe.upstreamRefs) || maybe.upstreamRefs.length === 0) continue;\n\n maybe.upstreamRefs = maybe.upstreamRefs.map((r) => {\n if (typeof r !== \"string\") return r;\n const idx = parseInt(r.slice(1), 10);\n const nodeId = created[idx]?.id;\n return nodeId ? { nodeId } : { nodeId: r };\n });\n }\n\n return this;\n }\n\n readonly when: BooleanWhenOverloads<TCurrentJson, WhenBuilder<TCurrentJson>> = (\n branch: boolean,\n steps: ReadonlyArray<AnyRunnableNodeConfig> | AnyRunnableNodeConfig,\n ...more: AnyRunnableNodeConfig[]\n ): WhenBuilder<TCurrentJson> => {\n const list = Array.isArray(steps) ? steps : [steps, ...more];\n const port: OutputPortKey = branch ? \"true\" : \"false\";\n const b = new WhenBuilder<TCurrentJson>(this.wf, this.from, port);\n b.addBranch(list);\n return b;\n };\n\n build(): WorkflowDefinition {\n return this.wf.build();\n }\n}\n","import type {\n NodeRef,\n OutputPortKey,\n RunnableNodeConfig,\n RunnableNodeOutputJson,\n WorkflowDefinition,\n} from \"../../types\";\n\nimport { WorkflowBuilder } from \"./WorkflowBuilder\";\nimport { WhenBuilder } from \"./WhenBuilder\";\nimport type {\n AnyRunnableNodeConfig,\n BooleanWhenOverloads,\n BranchOutputGuard,\n BranchStepsArg,\n StepSequenceOutput,\n} from \"./workflowBuilderTypes\";\n\ntype ChainCursorWhenOverloads<TCurrentJson> = BooleanWhenOverloads<TCurrentJson, WhenBuilder<TCurrentJson>> & {\n <\n TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined,\n TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined,\n >(\n branches: Readonly<{\n true?: TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> ? BranchStepsArg<TCurrentJson, TTrueSteps> : never;\n false?: TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig>\n ? BranchStepsArg<TCurrentJson, TFalseSteps>\n : never;\n }> &\n BranchOutputGuard<TCurrentJson, TTrueSteps, TFalseSteps>,\n ): ChainCursor<StepSequenceOutput<TCurrentJson, TTrueSteps>>;\n};\n\nexport class ChainCursor<TCurrentJson> {\n constructor(\n private readonly wf: WorkflowBuilder,\n private readonly cursor: NodeRef,\n private readonly cursorOutput: OutputPortKey,\n ) {}\n\n then<TConfig extends RunnableNodeConfig<TCurrentJson, any>>(\n config: TConfig,\n ): ChainCursor<RunnableNodeOutputJson<TConfig>> {\n const next = (this.wf as any).add(config) as NodeRef;\n (this.wf as any).connect(this.cursor, next, this.cursorOutput);\n return new ChainCursor<RunnableNodeOutputJson<TConfig>>(this.wf, next, \"main\");\n }\n\n readonly when: ChainCursorWhenOverloads<TCurrentJson> = ((\n arg1:\n | boolean\n | Readonly<{ true?: ReadonlyArray<AnyRunnableNodeConfig>; false?: ReadonlyArray<AnyRunnableNodeConfig> }>,\n steps?: ReadonlyArray<AnyRunnableNodeConfig> | AnyRunnableNodeConfig,\n ...more: AnyRunnableNodeConfig[]\n ): WhenBuilder<TCurrentJson> | ChainCursor<TCurrentJson> => {\n if (typeof arg1 === \"boolean\") {\n const list = Array.isArray(steps) ? steps : steps ? [steps, ...more] : more;\n const port: OutputPortKey = arg1 ? \"true\" : \"false\";\n const b = new WhenBuilder<TCurrentJson>(this.wf, this.cursor, port);\n b.addBranch(list);\n return b;\n }\n\n const branches = arg1;\n const makeMerge = (this.wf as any).options?.makeMergeNode as ((name: string) => AnyRunnableNodeConfig) | undefined;\n if (!makeMerge) {\n throw new Error(\n 'WorkflowBuilder is missing options.makeMergeNode (required for when({true,false}). Use createWorkflowBuilder from \"@codemation/core-nodes\".',\n );\n }\n\n const wfAny = this.wf as any;\n\n const buildBranch = (\n port: OutputPortKey,\n branchSteps: ReadonlyArray<AnyRunnableNodeConfig> | undefined,\n ): Readonly<{ end: NodeRef; endOutput: OutputPortKey }> => {\n const list = branchSteps ?? [];\n let prev: NodeRef | null = null;\n for (const cfg of list) {\n const ref = wfAny.add(cfg) as NodeRef;\n if (!prev) wfAny.connect(this.cursor, ref, port, \"in\");\n else wfAny.connect(prev, ref, \"main\", \"in\");\n prev = ref;\n }\n if (!prev) return { end: this.cursor, endOutput: port };\n return { end: prev, endOutput: \"main\" };\n };\n\n const t = buildBranch(\"true\", branches.true);\n const f = buildBranch(\"false\", branches.false);\n\n const merge = wfAny.add(makeMerge(\"Merge (auto)\")) as NodeRef;\n wfAny.connect(t.end, merge, t.endOutput, \"true\");\n wfAny.connect(f.end, merge, f.endOutput, \"false\");\n\n return new ChainCursor<TCurrentJson>(this.wf, merge, \"main\");\n }) as ChainCursorWhenOverloads<TCurrentJson>;\n\n build(): WorkflowDefinition {\n return this.wf.build();\n }\n}\n","import type {\n InputPortKey,\n NodeConfigBase,\n NodeDefinition,\n NodeRef,\n OutputPortKey,\n RunnableNodeOutputJson,\n TriggerNodeOutputJson,\n WorkflowDefinition,\n WorkflowId,\n} from \"../../types\";\n\nimport { ChainCursor } from \"./ChainCursorResolver\";\nimport type { AnyRunnableNodeConfig, AnyTriggerNodeConfig } from \"./workflowBuilderTypes\";\n\nexport class WorkflowBuilder {\n private readonly nodes: NodeDefinition[] = [];\n private readonly edges: WorkflowDefinition[\"edges\"] = [];\n private seq = 0;\n\n constructor(\n private readonly meta: { id: WorkflowId; name: string },\n private readonly options?: Readonly<{\n makeMergeNode?: (name: string) => AnyRunnableNodeConfig;\n }>,\n ) {}\n\n private add(config: NodeConfigBase): NodeRef {\n const tokenName = typeof config.type === \"function\" ? config.type.name : String(config.type);\n const id = config.id ?? `${tokenName}:${++this.seq}`;\n this.nodes.push({ id, kind: config.kind, type: config.type, name: config.name, config });\n return { id, kind: config.kind, name: config.name };\n }\n\n private connect(from: NodeRef, to: NodeRef, fromOutput: OutputPortKey = \"main\", toInput: InputPortKey = \"in\"): void {\n this.edges.push({ from: { nodeId: from.id, output: fromOutput }, to: { nodeId: to.id, input: toInput } });\n }\n\n trigger<TConfig extends AnyTriggerNodeConfig>(config: TConfig): ChainCursor<TriggerNodeOutputJson<TConfig>> {\n const ref = this.add(config);\n return new ChainCursor<TriggerNodeOutputJson<TConfig>>(this, ref, \"main\");\n }\n\n start<TConfig extends AnyRunnableNodeConfig>(config: TConfig): ChainCursor<RunnableNodeOutputJson<TConfig>> {\n const ref = this.add(config);\n return new ChainCursor<RunnableNodeOutputJson<TConfig>>(this, ref, \"main\");\n }\n\n build(): WorkflowDefinition {\n return { ...this.meta, nodes: this.nodes, edges: this.edges };\n }\n}\n\nexport { ChainCursor } from \"./ChainCursorResolver\";\nexport { WhenBuilder } from \"./WhenBuilder\";\n","import type { RunEventSubscription } from \"./runEvents\";\n\nexport class InMemoryRunEventSubscription implements RunEventSubscription {\n constructor(private readonly onClose: () => void) {}\n\n async close(): Promise<void> {\n this.onClose();\n }\n}\n","import type { WorkflowId } from \"../types\";\n\nimport type { RunEvent, RunEventBus, RunEventSubscription } from \"./runEvents\";\n\nimport { InMemoryRunEventSubscription } from \"./InMemoryRunEventSubscription\";\n\nexport class InMemoryRunEventBus implements RunEventBus {\n private readonly globalListeners = new Set<(event: RunEvent) => void>();\n private readonly listenersByWorkflowId = new Map<WorkflowId, Set<(event: RunEvent) => void>>();\n\n async publish(event: RunEvent): Promise<void> {\n for (const listener of this.globalListeners) listener(event);\n for (const listener of this.listenersByWorkflowId.get(event.workflowId) ?? []) listener(event);\n }\n\n async subscribe(onEvent: (event: RunEvent) => void): Promise<RunEventSubscription> {\n this.globalListeners.add(onEvent);\n return new InMemoryRunEventSubscription(() => {\n this.globalListeners.delete(onEvent);\n });\n }\n\n async subscribeToWorkflow(workflowId: WorkflowId, onEvent: (event: RunEvent) => void): Promise<RunEventSubscription> {\n const existing = this.listenersByWorkflowId.get(workflowId) ?? new Set<(event: RunEvent) => void>();\n existing.add(onEvent);\n this.listenersByWorkflowId.set(workflowId, existing);\n\n return new InMemoryRunEventSubscription(() => {\n const listeners = this.listenersByWorkflowId.get(workflowId);\n if (!listeners) return;\n listeners.delete(onEvent);\n if (listeners.size === 0) this.listenersByWorkflowId.delete(workflowId);\n });\n }\n}\n\nexport { InMemoryRunEventSubscription } from \"./InMemoryRunEventSubscription\";\n","import type { WorkflowId } from \"./workflowTypes\";\n\n/**\n * Host-controlled policy: when false, trigger {@link TriggerNode} setup is skipped and webhook routes\n * for that workflow are not registered (see engine trigger runtime + webhook matcher).\n */\nexport interface WorkflowActivationPolicy {\n isActive(workflowId: WorkflowId): boolean;\n}\n\n/** Default for tests and harnesses: every workflow is treated as active (legacy behavior). */\nexport class AllWorkflowsActiveWorkflowActivationPolicy implements WorkflowActivationPolicy {\n isActive(_workflowId: WorkflowId): boolean {\n return true;\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { CredentialRequirement } from "../contracts/credentialTypes";
|
|
2
|
+
import type { NodeConfigBase, NodeConnectionName, NodeId } from "../types";
|
|
3
|
+
import { ConnectionNodeIdFactory } from "../workflow/definition/ConnectionNodeIdFactory";
|
|
4
|
+
import { AgentConfigInspector } from "./AgentConfigInspectorFactory";
|
|
5
|
+
import type { AgentNodeConfig, ToolConfig } from "./AiHost";
|
|
6
|
+
import { NodeBackedToolConfig } from "./NodeBackedToolConfig";
|
|
7
|
+
|
|
8
|
+
export type AgentConnectionNodeRole = "languageModel" | "tool" | "nestedAgent";
|
|
9
|
+
|
|
10
|
+
export type AgentConnectionCredentialSource = Readonly<{
|
|
11
|
+
getCredentialRequirements?(): ReadonlyArray<CredentialRequirement>;
|
|
12
|
+
}>;
|
|
13
|
+
|
|
14
|
+
export type AgentConnectionNodeDescriptor = Readonly<{
|
|
15
|
+
nodeId: NodeId;
|
|
16
|
+
parentNodeId: NodeId;
|
|
17
|
+
connectionName: NodeConnectionName;
|
|
18
|
+
role: AgentConnectionNodeRole;
|
|
19
|
+
name: string;
|
|
20
|
+
typeName: string;
|
|
21
|
+
icon?: string;
|
|
22
|
+
credentialSource: AgentConnectionCredentialSource;
|
|
23
|
+
}>;
|
|
24
|
+
|
|
25
|
+
type AgentConnectionNodeCollectorApi = Readonly<{
|
|
26
|
+
collect(parentNodeId: NodeId, agentConfig: AgentNodeConfig<any, any>): ReadonlyArray<AgentConnectionNodeDescriptor>;
|
|
27
|
+
}>;
|
|
28
|
+
|
|
29
|
+
export const AgentConnectionNodeCollector: AgentConnectionNodeCollectorApi = new (class {
|
|
30
|
+
collect(parentNodeId: NodeId, agentConfig: AgentNodeConfig<any, any>): ReadonlyArray<AgentConnectionNodeDescriptor> {
|
|
31
|
+
const collected: AgentConnectionNodeDescriptor[] = [];
|
|
32
|
+
this.collectInto(parentNodeId, agentConfig, collected);
|
|
33
|
+
return collected;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private collectInto(
|
|
37
|
+
parentNodeId: NodeId,
|
|
38
|
+
agentConfig: AgentNodeConfig<any, any>,
|
|
39
|
+
collected: AgentConnectionNodeDescriptor[],
|
|
40
|
+
): void {
|
|
41
|
+
collected.push({
|
|
42
|
+
nodeId: ConnectionNodeIdFactory.languageModelConnectionNodeId(parentNodeId),
|
|
43
|
+
parentNodeId,
|
|
44
|
+
connectionName: "llm",
|
|
45
|
+
role: "languageModel",
|
|
46
|
+
name: agentConfig.chatModel.presentation?.label ?? agentConfig.chatModel.name,
|
|
47
|
+
typeName: agentConfig.chatModel.name,
|
|
48
|
+
icon: agentConfig.chatModel.presentation?.icon,
|
|
49
|
+
credentialSource: agentConfig.chatModel,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
for (const tool of agentConfig.tools ?? []) {
|
|
53
|
+
const toolNodeId = ConnectionNodeIdFactory.toolConnectionNodeId(parentNodeId, tool.name);
|
|
54
|
+
const isNestedAgent = this.isNodeBackedAgentTool(tool);
|
|
55
|
+
collected.push({
|
|
56
|
+
nodeId: toolNodeId,
|
|
57
|
+
parentNodeId,
|
|
58
|
+
connectionName: "tools",
|
|
59
|
+
role: isNestedAgent ? "nestedAgent" : "tool",
|
|
60
|
+
name: tool.presentation?.label ?? tool.name,
|
|
61
|
+
typeName: tool.name,
|
|
62
|
+
icon: tool.presentation?.icon,
|
|
63
|
+
credentialSource: tool,
|
|
64
|
+
});
|
|
65
|
+
this.collectNestedAgentTools(toolNodeId, tool, collected);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private collectNestedAgentTools(
|
|
70
|
+
toolNodeId: NodeId,
|
|
71
|
+
tool: ToolConfig,
|
|
72
|
+
collected: AgentConnectionNodeDescriptor[],
|
|
73
|
+
): void {
|
|
74
|
+
if (!this.isNodeBackedAgentTool(tool)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const innerAgent =
|
|
78
|
+
tool instanceof NodeBackedToolConfig ? tool.node : (tool as unknown as { node: AgentNodeConfig<any, any> }).node;
|
|
79
|
+
this.collectInto(toolNodeId, innerAgent, collected);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* After JSON round-trip (persisted snapshots), tools are plain objects — `instanceof NodeBackedToolConfig` fails.
|
|
84
|
+
* Detect node-backed tools structurally via {@link NodeBackedToolConfig#toolKind}.
|
|
85
|
+
*/
|
|
86
|
+
private isNodeBackedAgentTool(tool: ToolConfig): boolean {
|
|
87
|
+
if (tool instanceof NodeBackedToolConfig) {
|
|
88
|
+
return AgentConfigInspector.isAgentNodeConfig(tool.node);
|
|
89
|
+
}
|
|
90
|
+
if (!tool || typeof tool !== "object") {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const t = tool as unknown as Record<string, unknown>;
|
|
94
|
+
if (t.toolKind !== "nodeBacked") {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return AgentConfigInspector.isAgentNodeConfig(t.node as NodeConfigBase);
|
|
98
|
+
}
|
|
99
|
+
})();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { RunnableNodeConfig } from "../types";
|
|
1
|
+
import type { Item, RunnableNodeConfig } from "../types";
|
|
2
2
|
import type { NodeBackedToolConfigOptions, ZodSchemaAny } from "./AiHost";
|
|
3
|
+
import { AgentConfigInspector } from "./AgentConfigInspectorFactory";
|
|
3
4
|
import { NodeBackedToolConfig } from "./NodeBackedToolConfig";
|
|
4
5
|
|
|
5
6
|
class AgentToolFactoryImpl {
|
|
@@ -11,7 +12,42 @@ class AgentToolFactoryImpl {
|
|
|
11
12
|
node: TNodeConfig,
|
|
12
13
|
options: Readonly<{ name?: string } & NodeBackedToolConfigOptions<TNodeConfig, TInputSchema, TOutputSchema>>,
|
|
13
14
|
): NodeBackedToolConfig<TNodeConfig, TInputSchema, TOutputSchema> {
|
|
14
|
-
return new NodeBackedToolConfig(
|
|
15
|
+
return new NodeBackedToolConfig(
|
|
16
|
+
options.name ?? node.name ?? "tool",
|
|
17
|
+
node,
|
|
18
|
+
this.withDefaultAgentInputMapper(node, options),
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private withDefaultAgentInputMapper<
|
|
23
|
+
TNodeConfig extends RunnableNodeConfig<any, any>,
|
|
24
|
+
TInputSchema extends ZodSchemaAny,
|
|
25
|
+
TOutputSchema extends ZodSchemaAny,
|
|
26
|
+
>(
|
|
27
|
+
node: TNodeConfig,
|
|
28
|
+
options: Readonly<{ name?: string } & NodeBackedToolConfigOptions<TNodeConfig, TInputSchema, TOutputSchema>>,
|
|
29
|
+
): Readonly<{ name?: string } & NodeBackedToolConfigOptions<TNodeConfig, TInputSchema, TOutputSchema>> {
|
|
30
|
+
if (options.mapInput || !AgentConfigInspector.isAgentNodeConfig(node)) {
|
|
31
|
+
return options;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...options,
|
|
35
|
+
mapInput: ({ input, item }) => this.mergeAgentToolInputWithCurrentItem(input, item) as never,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private mergeAgentToolInputWithCurrentItem(input: unknown, item: Item): unknown {
|
|
40
|
+
if (!this.isMergeableRecord(input) || !this.isMergeableRecord(item.json)) {
|
|
41
|
+
return input;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
...item.json,
|
|
45
|
+
...input,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private isMergeableRecord(value: unknown): value is Record<string, unknown> {
|
|
50
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15
51
|
}
|
|
16
52
|
}
|
|
17
53
|
|
package/src/ai/AiHost.ts
CHANGED
|
@@ -207,7 +207,7 @@ export interface AgentNodeConfig<TInputJson = unknown, TOutputJson = unknown> ex
|
|
|
207
207
|
readonly guardrails?: AgentGuardrailConfig;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
export type AgentAttachmentRole = "languageModel" | "tool";
|
|
210
|
+
export type AgentAttachmentRole = "languageModel" | "tool" | "nestedAgent";
|
|
211
211
|
|
|
212
212
|
export { NodeBackedToolConfig } from "./NodeBackedToolConfig";
|
|
213
213
|
export { AgentToolFactory } from "./AgentToolFactory";
|