@foresthubai/workflow-core 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +14 -0
  3. package/README.md +63 -0
  4. package/dist/api/index.d.ts +7 -0
  5. package/dist/api/index.d.ts.map +1 -0
  6. package/dist/api/index.js +2 -0
  7. package/dist/api/index.js.map +1 -0
  8. package/dist/api/workflow.d.ts +607 -0
  9. package/dist/api/workflow.d.ts.map +1 -0
  10. package/dist/api/workflow.js +6 -0
  11. package/dist/api/workflow.js.map +1 -0
  12. package/dist/channel/Channel.d.ts +10 -0
  13. package/dist/channel/Channel.d.ts.map +1 -0
  14. package/dist/channel/Channel.js +2 -0
  15. package/dist/channel/Channel.js.map +1 -0
  16. package/dist/channel/ChannelDefinition.d.ts +27 -0
  17. package/dist/channel/ChannelDefinition.d.ts.map +1 -0
  18. package/dist/channel/ChannelDefinition.js +50 -0
  19. package/dist/channel/ChannelDefinition.js.map +1 -0
  20. package/dist/channel/index.d.ts +7 -0
  21. package/dist/channel/index.d.ts.map +1 -0
  22. package/dist/channel/index.js +4 -0
  23. package/dist/channel/index.js.map +1 -0
  24. package/dist/channel/serialization.d.ts +17 -0
  25. package/dist/channel/serialization.d.ts.map +1 -0
  26. package/dist/channel/serialization.js +63 -0
  27. package/dist/channel/serialization.js.map +1 -0
  28. package/dist/deploy/index.d.ts +2 -0
  29. package/dist/deploy/index.d.ts.map +1 -0
  30. package/dist/deploy/index.js +2 -0
  31. package/dist/deploy/index.js.map +1 -0
  32. package/dist/deploy/requirements.d.ts +17 -0
  33. package/dist/deploy/requirements.d.ts.map +1 -0
  34. package/dist/deploy/requirements.js +41 -0
  35. package/dist/deploy/requirements.js.map +1 -0
  36. package/dist/diagnostics/__fixtures__/diagnosticFixtures.d.ts +28 -0
  37. package/dist/diagnostics/__fixtures__/diagnosticFixtures.d.ts.map +1 -0
  38. package/dist/diagnostics/__fixtures__/diagnosticFixtures.js +125 -0
  39. package/dist/diagnostics/__fixtures__/diagnosticFixtures.js.map +1 -0
  40. package/dist/diagnostics/diagnostics.d.ts +128 -0
  41. package/dist/diagnostics/diagnostics.d.ts.map +1 -0
  42. package/dist/diagnostics/diagnostics.js +783 -0
  43. package/dist/diagnostics/diagnostics.js.map +1 -0
  44. package/dist/diagnostics/index.d.ts +3 -0
  45. package/dist/diagnostics/index.d.ts.map +1 -0
  46. package/dist/diagnostics/index.js +2 -0
  47. package/dist/diagnostics/index.js.map +1 -0
  48. package/dist/edge/Edge.d.ts +22 -0
  49. package/dist/edge/Edge.d.ts.map +1 -0
  50. package/dist/edge/Edge.js +2 -0
  51. package/dist/edge/Edge.js.map +1 -0
  52. package/dist/edge/EdgeDefinition.d.ts +10 -0
  53. package/dist/edge/EdgeDefinition.d.ts.map +1 -0
  54. package/dist/edge/EdgeDefinition.js +36 -0
  55. package/dist/edge/EdgeDefinition.js.map +1 -0
  56. package/dist/edge/EdgeType.d.ts +6 -0
  57. package/dist/edge/EdgeType.d.ts.map +1 -0
  58. package/dist/edge/EdgeType.js +7 -0
  59. package/dist/edge/EdgeType.js.map +1 -0
  60. package/dist/edge/index.d.ts +6 -0
  61. package/dist/edge/index.d.ts.map +1 -0
  62. package/dist/edge/index.js +6 -0
  63. package/dist/edge/index.js.map +1 -0
  64. package/dist/edge/serialization.d.ts +18 -0
  65. package/dist/edge/serialization.d.ts.map +1 -0
  66. package/dist/edge/serialization.js +76 -0
  67. package/dist/edge/serialization.js.map +1 -0
  68. package/dist/expression/index.d.ts +5 -0
  69. package/dist/expression/index.d.ts.map +1 -0
  70. package/dist/expression/index.js +3 -0
  71. package/dist/expression/index.js.map +1 -0
  72. package/dist/expression/parser.d.ts +14 -0
  73. package/dist/expression/parser.d.ts.map +1 -0
  74. package/dist/expression/parser.js +309 -0
  75. package/dist/expression/parser.js.map +1 -0
  76. package/dist/expression/types.d.ts +11 -0
  77. package/dist/expression/types.d.ts.map +1 -0
  78. package/dist/expression/types.js +20 -0
  79. package/dist/expression/types.js.map +1 -0
  80. package/dist/function/FunctionDeclaration.d.ts +43 -0
  81. package/dist/function/FunctionDeclaration.d.ts.map +1 -0
  82. package/dist/function/FunctionDeclaration.js +17 -0
  83. package/dist/function/FunctionDeclaration.js.map +1 -0
  84. package/dist/function/index.d.ts +4 -0
  85. package/dist/function/index.d.ts.map +1 -0
  86. package/dist/function/index.js +3 -0
  87. package/dist/function/index.js.map +1 -0
  88. package/dist/function/serialization.d.ts +20 -0
  89. package/dist/function/serialization.d.ts.map +1 -0
  90. package/dist/function/serialization.js +26 -0
  91. package/dist/function/serialization.js.map +1 -0
  92. package/dist/id/index.d.ts +7 -0
  93. package/dist/id/index.d.ts.map +1 -0
  94. package/dist/id/index.js +9 -0
  95. package/dist/id/index.js.map +1 -0
  96. package/dist/index.d.ts +11 -0
  97. package/dist/index.d.ts.map +1 -0
  98. package/dist/index.js +14 -0
  99. package/dist/index.js.map +1 -0
  100. package/dist/memory/Memory.d.ts +12 -0
  101. package/dist/memory/Memory.d.ts.map +1 -0
  102. package/dist/memory/Memory.js +2 -0
  103. package/dist/memory/Memory.js.map +1 -0
  104. package/dist/memory/MemoryDefinition.d.ts +16 -0
  105. package/dist/memory/MemoryDefinition.d.ts.map +1 -0
  106. package/dist/memory/MemoryDefinition.js +2 -0
  107. package/dist/memory/MemoryDefinition.js.map +1 -0
  108. package/dist/memory/MemoryFileDefinition.d.ts +8 -0
  109. package/dist/memory/MemoryFileDefinition.d.ts.map +1 -0
  110. package/dist/memory/MemoryFileDefinition.js +36 -0
  111. package/dist/memory/MemoryFileDefinition.js.map +1 -0
  112. package/dist/memory/MemoryRegistry.d.ts +17 -0
  113. package/dist/memory/MemoryRegistry.d.ts.map +1 -0
  114. package/dist/memory/MemoryRegistry.js +29 -0
  115. package/dist/memory/MemoryRegistry.js.map +1 -0
  116. package/dist/memory/VectorDatabaseDefinition.d.ts +7 -0
  117. package/dist/memory/VectorDatabaseDefinition.d.ts.map +1 -0
  118. package/dist/memory/VectorDatabaseDefinition.js +20 -0
  119. package/dist/memory/VectorDatabaseDefinition.js.map +1 -0
  120. package/dist/memory/index.d.ts +9 -0
  121. package/dist/memory/index.d.ts.map +1 -0
  122. package/dist/memory/index.js +6 -0
  123. package/dist/memory/index.js.map +1 -0
  124. package/dist/memory/serialization.d.ts +11 -0
  125. package/dist/memory/serialization.d.ts.map +1 -0
  126. package/dist/memory/serialization.js +44 -0
  127. package/dist/memory/serialization.js.map +1 -0
  128. package/dist/migration/index.d.ts +5 -0
  129. package/dist/migration/index.d.ts.map +1 -0
  130. package/dist/migration/index.js +4 -0
  131. package/dist/migration/index.js.map +1 -0
  132. package/dist/migration/migrate.d.ts +11 -0
  133. package/dist/migration/migrate.d.ts.map +1 -0
  134. package/dist/migration/migrate.js +52 -0
  135. package/dist/migration/migrate.js.map +1 -0
  136. package/dist/migration/migrations.d.ts +22 -0
  137. package/dist/migration/migrations.d.ts.map +1 -0
  138. package/dist/migration/migrations.js +10 -0
  139. package/dist/migration/migrations.js.map +1 -0
  140. package/dist/migration/version.d.ts +9 -0
  141. package/dist/migration/version.d.ts.map +1 -0
  142. package/dist/migration/version.js +9 -0
  143. package/dist/migration/version.js.map +1 -0
  144. package/dist/model/LLMModelDefinition.d.ts +7 -0
  145. package/dist/model/LLMModelDefinition.d.ts.map +1 -0
  146. package/dist/model/LLMModelDefinition.js +11 -0
  147. package/dist/model/LLMModelDefinition.js.map +1 -0
  148. package/dist/model/Model.d.ts +21 -0
  149. package/dist/model/Model.d.ts.map +1 -0
  150. package/dist/model/Model.js +15 -0
  151. package/dist/model/Model.js.map +1 -0
  152. package/dist/model/ModelDefinition.d.ts +15 -0
  153. package/dist/model/ModelDefinition.d.ts.map +1 -0
  154. package/dist/model/ModelDefinition.js +2 -0
  155. package/dist/model/ModelDefinition.js.map +1 -0
  156. package/dist/model/ModelRegistry.d.ts +17 -0
  157. package/dist/model/ModelRegistry.d.ts.map +1 -0
  158. package/dist/model/ModelRegistry.js +27 -0
  159. package/dist/model/ModelRegistry.js.map +1 -0
  160. package/dist/model/index.d.ts +8 -0
  161. package/dist/model/index.d.ts.map +1 -0
  162. package/dist/model/index.js +5 -0
  163. package/dist/model/index.js.map +1 -0
  164. package/dist/model/serialization.d.ts +8 -0
  165. package/dist/model/serialization.d.ts.map +1 -0
  166. package/dist/model/serialization.js +25 -0
  167. package/dist/model/serialization.js.map +1 -0
  168. package/dist/node/AgentNode.d.ts +21 -0
  169. package/dist/node/AgentNode.d.ts.map +1 -0
  170. package/dist/node/AgentNode.js +60 -0
  171. package/dist/node/AgentNode.js.map +1 -0
  172. package/dist/node/DataNode.d.ts +14 -0
  173. package/dist/node/DataNode.d.ts.map +1 -0
  174. package/dist/node/DataNode.js +26 -0
  175. package/dist/node/DataNode.js.map +1 -0
  176. package/dist/node/FunctionNode.d.ts +24 -0
  177. package/dist/node/FunctionNode.d.ts.map +1 -0
  178. package/dist/node/FunctionNode.js +44 -0
  179. package/dist/node/FunctionNode.js.map +1 -0
  180. package/dist/node/InputNode.d.ts +46 -0
  181. package/dist/node/InputNode.d.ts.map +1 -0
  182. package/dist/node/InputNode.js +133 -0
  183. package/dist/node/InputNode.js.map +1 -0
  184. package/dist/node/LogicNode.d.ts +13 -0
  185. package/dist/node/LogicNode.d.ts.map +1 -0
  186. package/dist/node/LogicNode.js +19 -0
  187. package/dist/node/LogicNode.js.map +1 -0
  188. package/dist/node/MqttNode.d.ts +27 -0
  189. package/dist/node/MqttNode.d.ts.map +1 -0
  190. package/dist/node/MqttNode.js +96 -0
  191. package/dist/node/MqttNode.js.map +1 -0
  192. package/dist/node/Node.d.ts +49 -0
  193. package/dist/node/Node.d.ts.map +1 -0
  194. package/dist/node/Node.js +9 -0
  195. package/dist/node/Node.js.map +1 -0
  196. package/dist/node/NodeDefinition.d.ts +30 -0
  197. package/dist/node/NodeDefinition.d.ts.map +1 -0
  198. package/dist/node/NodeDefinition.js +2 -0
  199. package/dist/node/NodeDefinition.js.map +1 -0
  200. package/dist/node/NodeRegistry.d.ts +19 -0
  201. package/dist/node/NodeRegistry.d.ts.map +1 -0
  202. package/dist/node/NodeRegistry.js +65 -0
  203. package/dist/node/NodeRegistry.js.map +1 -0
  204. package/dist/node/OutputNode.d.ts +23 -0
  205. package/dist/node/OutputNode.d.ts.map +1 -0
  206. package/dist/node/OutputNode.js +62 -0
  207. package/dist/node/OutputNode.js.map +1 -0
  208. package/dist/node/ToolNode.d.ts +12 -0
  209. package/dist/node/ToolNode.d.ts.map +1 -0
  210. package/dist/node/ToolNode.js +18 -0
  211. package/dist/node/ToolNode.js.map +1 -0
  212. package/dist/node/TriggerNode.d.ts +67 -0
  213. package/dist/node/TriggerNode.d.ts.map +1 -0
  214. package/dist/node/TriggerNode.js +172 -0
  215. package/dist/node/TriggerNode.js.map +1 -0
  216. package/dist/node/constants.d.ts +16 -0
  217. package/dist/node/constants.d.ts.map +1 -0
  218. package/dist/node/constants.js +18 -0
  219. package/dist/node/constants.js.map +1 -0
  220. package/dist/node/index.d.ts +12 -0
  221. package/dist/node/index.d.ts.map +1 -0
  222. package/dist/node/index.js +9 -0
  223. package/dist/node/index.js.map +1 -0
  224. package/dist/node/methods.d.ts +73 -0
  225. package/dist/node/methods.d.ts.map +1 -0
  226. package/dist/node/methods.js +261 -0
  227. package/dist/node/methods.js.map +1 -0
  228. package/dist/node/serialization.d.ts +25 -0
  229. package/dist/node/serialization.d.ts.map +1 -0
  230. package/dist/node/serialization.js +525 -0
  231. package/dist/node/serialization.js.map +1 -0
  232. package/dist/parameter/OutputParameter.d.ts +69 -0
  233. package/dist/parameter/OutputParameter.d.ts.map +1 -0
  234. package/dist/parameter/OutputParameter.js +6 -0
  235. package/dist/parameter/OutputParameter.js.map +1 -0
  236. package/dist/parameter/Parameter.d.ts +152 -0
  237. package/dist/parameter/Parameter.d.ts.map +1 -0
  238. package/dist/parameter/Parameter.js +86 -0
  239. package/dist/parameter/Parameter.js.map +1 -0
  240. package/dist/parameter/index.d.ts +5 -0
  241. package/dist/parameter/index.d.ts.map +1 -0
  242. package/dist/parameter/index.js +3 -0
  243. package/dist/parameter/index.js.map +1 -0
  244. package/dist/variable/Variable.d.ts +25 -0
  245. package/dist/variable/Variable.d.ts.map +1 -0
  246. package/dist/variable/Variable.js +2 -0
  247. package/dist/variable/Variable.js.map +1 -0
  248. package/dist/variable/index.d.ts +3 -0
  249. package/dist/variable/index.d.ts.map +1 -0
  250. package/dist/variable/index.js +5 -0
  251. package/dist/variable/index.js.map +1 -0
  252. package/dist/variable/operations.d.ts +37 -0
  253. package/dist/variable/operations.d.ts.map +1 -0
  254. package/dist/variable/operations.js +88 -0
  255. package/dist/variable/operations.js.map +1 -0
  256. package/dist/workflow/Workflow.d.ts +38 -0
  257. package/dist/workflow/Workflow.d.ts.map +1 -0
  258. package/dist/workflow/Workflow.js +8 -0
  259. package/dist/workflow/Workflow.js.map +1 -0
  260. package/dist/workflow/index.d.ts +4 -0
  261. package/dist/workflow/index.d.ts.map +1 -0
  262. package/dist/workflow/index.js +3 -0
  263. package/dist/workflow/index.js.map +1 -0
  264. package/dist/workflow/serialization.d.ts +43 -0
  265. package/dist/workflow/serialization.d.ts.map +1 -0
  266. package/dist/workflow/serialization.js +215 -0
  267. package/dist/workflow/serialization.js.map +1 -0
  268. package/package.json +105 -0
  269. package/src/api/index.ts +11 -0
  270. package/src/api/workflow.ts +607 -0
  271. package/src/channel/Channel.ts +11 -0
  272. package/src/channel/ChannelDefinition.ts +76 -0
  273. package/src/channel/index.ts +6 -0
  274. package/src/channel/serialization.ts +68 -0
  275. package/src/deploy/index.ts +1 -0
  276. package/src/deploy/requirements.test.ts +61 -0
  277. package/src/deploy/requirements.ts +41 -0
  278. package/src/diagnostics/__fixtures__/diagnosticFixtures.ts +158 -0
  279. package/src/diagnostics/diagnostics.test.ts +878 -0
  280. package/src/diagnostics/diagnostics.ts +936 -0
  281. package/src/diagnostics/index.ts +11 -0
  282. package/src/edge/Edge.ts +23 -0
  283. package/src/edge/EdgeDefinition.ts +45 -0
  284. package/src/edge/EdgeType.ts +19 -0
  285. package/src/edge/index.ts +8 -0
  286. package/src/edge/serialization.ts +83 -0
  287. package/src/expression/index.ts +4 -0
  288. package/src/expression/parser.ts +362 -0
  289. package/src/expression/types.ts +30 -0
  290. package/src/function/FunctionDeclaration.ts +54 -0
  291. package/src/function/index.ts +3 -0
  292. package/src/function/serialization.ts +40 -0
  293. package/src/globals.d.ts +9 -0
  294. package/src/id/index.ts +8 -0
  295. package/src/index.ts +22 -0
  296. package/src/memory/Memory.ts +15 -0
  297. package/src/memory/MemoryDefinition.ts +16 -0
  298. package/src/memory/MemoryFileDefinition.ts +37 -0
  299. package/src/memory/MemoryRegistry.ts +35 -0
  300. package/src/memory/VectorDatabaseDefinition.ts +21 -0
  301. package/src/memory/index.ts +8 -0
  302. package/src/memory/serialization.ts +47 -0
  303. package/src/migration/index.ts +4 -0
  304. package/src/migration/migrate.test.ts +44 -0
  305. package/src/migration/migrate.ts +58 -0
  306. package/src/migration/migrations.ts +24 -0
  307. package/src/migration/version.ts +9 -0
  308. package/src/model/LLMModelDefinition.ts +12 -0
  309. package/src/model/Model.ts +39 -0
  310. package/src/model/ModelDefinition.ts +15 -0
  311. package/src/model/ModelRegistry.ts +33 -0
  312. package/src/model/index.ts +7 -0
  313. package/src/model/serialization.ts +30 -0
  314. package/src/node/AgentNode.ts +82 -0
  315. package/src/node/DataNode.ts +41 -0
  316. package/src/node/FunctionNode.ts +76 -0
  317. package/src/node/InputNode.ts +185 -0
  318. package/src/node/LogicNode.ts +33 -0
  319. package/src/node/MqttNode.ts +127 -0
  320. package/src/node/Node.ts +61 -0
  321. package/src/node/NodeDefinition.ts +37 -0
  322. package/src/node/NodeRegistry.ts +85 -0
  323. package/src/node/OutputNode.ts +87 -0
  324. package/src/node/ToolNode.ts +32 -0
  325. package/src/node/TriggerNode.ts +272 -0
  326. package/src/node/constants.ts +16 -0
  327. package/src/node/index.ts +26 -0
  328. package/src/node/methods.ts +278 -0
  329. package/src/node/serialization.ts +544 -0
  330. package/src/parameter/OutputParameter.ts +68 -0
  331. package/src/parameter/Parameter.ts +243 -0
  332. package/src/parameter/index.ts +33 -0
  333. package/src/variable/Variable.ts +10 -0
  334. package/src/variable/index.ts +16 -0
  335. package/src/variable/operations.ts +106 -0
  336. package/src/workflow/Workflow.ts +41 -0
  337. package/src/workflow/index.ts +3 -0
  338. package/src/workflow/serialization.test.ts +240 -0
  339. package/src/workflow/serialization.ts +242 -0
@@ -0,0 +1,40 @@
1
+ import type { Expression } from "../api";
2
+ import type { FunctionDeclaration, FunctionInfo } from "./FunctionDeclaration";
3
+ import { toFunctionInfo } from "./FunctionDeclaration";
4
+
5
+ /**
6
+ * The two wire pieces a {@link FunctionDeclaration} splits into. The contract keeps
7
+ * the signature (`functionInfo`) and the return-value expressions (`outputAssignments`)
8
+ * apart; the domain bundles them on `outputs`. The function *body*
9
+ * (nodes/edges/declaredVariables) is added separately by the workflow serializer —
10
+ * this only handles the declaration ⇄ wire mapping.
11
+ */
12
+ export interface SerializedFunction {
13
+ functionInfo: FunctionInfo;
14
+ outputAssignments: Record<string, Expression>;
15
+ }
16
+
17
+ /** Domain declaration → wire pieces: flatten the signature and lift each output's
18
+ * expression into the `outputAssignments` map keyed by output uid. */
19
+ export function serialize(fn: FunctionDeclaration): SerializedFunction {
20
+ const outputAssignments: Record<string, Expression> = {};
21
+ for (const o of fn.outputs) outputAssignments[o.uid] = o.expression;
22
+ return { functionInfo: toFunctionInfo(fn), outputAssignments };
23
+ }
24
+
25
+ /** Wire pieces → domain declaration: bundle each `return` with its assignment. A
26
+ * return with no stored assignment gets an empty expression of the right dataType. */
27
+ export function deserialize(functionInfo: FunctionInfo, outputAssignments: Record<string, Expression>): FunctionDeclaration {
28
+ return {
29
+ id: functionInfo.id,
30
+ version: functionInfo.version,
31
+ name: functionInfo.name,
32
+ arguments: functionInfo.arguments,
33
+ outputs: functionInfo.returns.map((r) => ({
34
+ uid: r.uid,
35
+ name: r.name,
36
+ dataType: r.dataType,
37
+ expression: outputAssignments[r.uid] ?? { expression: "", references: [], dataType: r.dataType },
38
+ })),
39
+ };
40
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * This package drops the `DOM` lib (see tsconfig.json) to stay headless, so the
3
+ * Web Crypto `crypto` global is untyped. It exists at runtime everywhere we run
4
+ * — browsers, Node >= 19, Deno, Bun — so we declare only the one member we use
5
+ * rather than pulling in `DOM` or `@types/node` and their global scope.
6
+ */
7
+ declare const crypto: {
8
+ randomUUID(): `${string}-${string}-${string}-${string}-${string}`;
9
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Centralized UID creation for every entity in a workflow — nodes, edges,
3
+ * channels, memory, models, declared variables, function arguments, and
4
+ * function canvases. IDs are opaque.
5
+ */
6
+ export function generateId(): string {
7
+ return crypto.randomUUID();
8
+ }
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { deserialize } from "./workflow/serialization";
2
+ import { validateWorkflowState, type ValidationResult } from "./diagnostics/diagnostics";
3
+ import type { ApiWorkflow } from "./workflow/Workflow";
4
+
5
+ // Api-layer types consumers use directly (no domain twin). Sourced from `api`
6
+ // so they're available at the package root rather than via a domain subpath.
7
+ // (`FunctionInfo` is the exception — it's the flat twin of the domain
8
+ // `FunctionDeclaration`, so it lives on the `function` subpath, not here.)
9
+ export type { DataType, Reference, Expression } from "./api";
10
+
11
+ // Format versioning: load persisted documents through `migrate` before
12
+ // deserializing.
13
+ export { migrate, CURRENT_SCHEMA_VERSION } from "./migration";
14
+
15
+ /**
16
+ * Validate a workflow against the headless validator.
17
+ * Pure: no I/O, no Zustand, no React, no DOM. Runnable in Node, a CLI, or
18
+ * a Claude Code skill.
19
+ */
20
+ export function validateWorkflow(workflow: ApiWorkflow): ValidationResult {
21
+ return validateWorkflowState(deserialize(workflow));
22
+ }
@@ -0,0 +1,15 @@
1
+ import type { Schemas } from "../api";
2
+
3
+ export type MemoryType = "MemoryFile" | "VectorDatabase";
4
+
5
+ export const ALL_MEMORY_TYPES: MemoryType[] = ["MemoryFile", "VectorDatabase"];
6
+
7
+ export interface Memory {
8
+ id: string;
9
+ label: string;
10
+ type: MemoryType;
11
+ arguments: Record<string, unknown>;
12
+ }
13
+
14
+ /** Reference from an agent node to a declared MemoryFile, with access mode. Round-trips 1:1 with the API. */
15
+ export type MemoryRef = Schemas["MemoryRef"];
@@ -0,0 +1,16 @@
1
+ import type { Parameter } from "../parameter";
2
+ import type { MemoryType } from "./Memory";
3
+
4
+ /**
5
+ * Static, per-type metadata for a memory variant. Mirrors NodeDefinition:
6
+ * one definition object per MemoryType, registered in MemoryRegistry — unlike
7
+ * channels, which use a single union definition gated by activation rules.
8
+ * `label` is a top-level instance field (edited like a channel label), so it is
9
+ * never a parameter here.
10
+ */
11
+ export interface MemoryDefinition {
12
+ type: MemoryType;
13
+ label: string;
14
+ description: string;
15
+ parameters: Parameter[];
16
+ }
@@ -0,0 +1,37 @@
1
+ import type { MemoryDefinition } from "./MemoryDefinition";
2
+
3
+ /**
4
+ * Agent-scoped durable text storage. The instance `label` (edited like a channel
5
+ * label) is the identifier the LLM sees in tool calls, so it is not a parameter.
6
+ * The config panel renders these parameters via ParameterEditor, same as channels.
7
+ */
8
+ export const MemoryFileDefinition: MemoryDefinition = {
9
+ type: "MemoryFile",
10
+ label: "Memory File",
11
+ description: "Durable text storage an agent can read and write",
12
+ parameters: [
13
+ {
14
+ id: "description",
15
+ label: "Description",
16
+ description: "What this memory file is for (shown to the LLM as tool description)",
17
+ type: "string",
18
+ multiline: true,
19
+ optional: true,
20
+ },
21
+ {
22
+ id: "content",
23
+ label: "Initial Content",
24
+ description: "Seed content written on fresh deploy; existing rows keep their content on redeploy",
25
+ type: "string",
26
+ multiline: true,
27
+ optional: true,
28
+ },
29
+ {
30
+ id: "maxSizeBytes",
31
+ label: "Max Size (bytes)",
32
+ description: "Byte cap on writes; leave empty for unlimited",
33
+ type: "int",
34
+ optional: true,
35
+ },
36
+ ],
37
+ };
@@ -0,0 +1,35 @@
1
+ import type { MemoryType } from "./Memory";
2
+ import type { MemoryDefinition } from "./MemoryDefinition";
3
+ import { MemoryFileDefinition } from "./MemoryFileDefinition";
4
+ import { VectorDatabaseDefinition } from "./VectorDatabaseDefinition";
5
+
6
+ /**
7
+ * Central registry for memory variant definitions (one per MemoryType).
8
+ * Mirrors NodeRegistry.
9
+ */
10
+ class MemoryDefinitionRegistry {
11
+ private memories: Map<MemoryType, MemoryDefinition> = new Map();
12
+ private initialized = false;
13
+
14
+ initialize() {
15
+ if (this.initialized) return;
16
+ this.register(MemoryFileDefinition);
17
+ this.register(VectorDatabaseDefinition);
18
+ this.initialized = true;
19
+ }
20
+
21
+ private register(definition: MemoryDefinition) {
22
+ this.memories.set(definition.type, definition);
23
+ }
24
+
25
+ getAll(): MemoryDefinition[] {
26
+ return Array.from(this.memories.values());
27
+ }
28
+
29
+ getByType(type: MemoryType): MemoryDefinition | undefined {
30
+ return this.memories.get(type);
31
+ }
32
+ }
33
+
34
+ export const MemoryRegistry = new MemoryDefinitionRegistry();
35
+ MemoryRegistry.initialize();
@@ -0,0 +1,21 @@
1
+ import type { MemoryDefinition } from "./MemoryDefinition";
2
+
3
+ /**
4
+ * RAG knowledge base referenced from Retriever nodes. Only descriptive config
5
+ * lives here; the backend collection it binds to is supplied at deploy time.
6
+ */
7
+ export const VectorDatabaseDefinition: MemoryDefinition = {
8
+ type: "VectorDatabase",
9
+ label: "Vector Database",
10
+ description: "RAG knowledge base that Retriever nodes can query",
11
+ parameters: [
12
+ {
13
+ id: "description",
14
+ label: "Description",
15
+ description: "What this knowledge base contains",
16
+ type: "string",
17
+ multiline: true,
18
+ optional: true,
19
+ },
20
+ ],
21
+ };
@@ -0,0 +1,8 @@
1
+ export type { MemoryType, Memory, MemoryRef } from "./Memory";
2
+ export { ALL_MEMORY_TYPES } from "./Memory";
3
+ export type { MemoryDefinition } from "./MemoryDefinition";
4
+ export { MemoryFileDefinition } from "./MemoryFileDefinition";
5
+ export { VectorDatabaseDefinition } from "./VectorDatabaseDefinition";
6
+ export { MemoryRegistry } from "./MemoryRegistry";
7
+ export { serialize, deserialize } from "./serialization";
8
+ export type { ApiMemory } from "./serialization";
@@ -0,0 +1,47 @@
1
+ import type { Schemas } from "../api";
2
+ import type { Memory } from "./Memory";
3
+
4
+ export type ApiMemory = Schemas["Memory"];
5
+
6
+ /**
7
+ * Serialize a domain Memory to the API discriminated-union shape.
8
+ * MemoryFile drops `maxSizeBytes` when unset (absent and null both mean "unlimited").
9
+ */
10
+ export function serialize(mem: Memory): ApiMemory {
11
+ const { id, label, type, arguments: args } = mem;
12
+ switch (type) {
13
+ case "MemoryFile":
14
+ return {
15
+ type,
16
+ id,
17
+ label,
18
+ description: (args.description as string) ?? "",
19
+ content: (args.content as string) ?? "",
20
+ ...(args.maxSizeBytes != null ? { maxSizeBytes: args.maxSizeBytes as number } : {}),
21
+ };
22
+ case "VectorDatabase":
23
+ return {
24
+ type,
25
+ id,
26
+ label,
27
+ ...(args.description != null ? { description: args.description as string } : {}),
28
+ };
29
+ }
30
+ }
31
+
32
+ /** Convert an API Memory into a domain Memory. */
33
+ export function deserialize(api: ApiMemory): Memory {
34
+ const { id, label, type } = api;
35
+ const args: Record<string, unknown> = {};
36
+ switch (type) {
37
+ case "MemoryFile":
38
+ args.description = api.description ?? "";
39
+ args.content = api.content ?? "";
40
+ if (api.maxSizeBytes != null) args.maxSizeBytes = api.maxSizeBytes;
41
+ break;
42
+ case "VectorDatabase":
43
+ if (api.description != null) args.description = api.description;
44
+ break;
45
+ }
46
+ return { id, label, type, arguments: args };
47
+ }
@@ -0,0 +1,4 @@
1
+ export { CURRENT_SCHEMA_VERSION, BASELINE_SCHEMA_VERSION } from "./version";
2
+ export { migrate, readSchemaVersion } from "./migrate";
3
+ export { MIGRATIONS } from "./migrations";
4
+ export type { Migration } from "./migrations";
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { migrate, readSchemaVersion } from "./migrate";
3
+ import { CURRENT_SCHEMA_VERSION } from "./version";
4
+
5
+ describe("readSchemaVersion", () => {
6
+ it("reads an explicit integer version", () => {
7
+ expect(readSchemaVersion({ schemaVersion: 3 })).toBe(3);
8
+ });
9
+
10
+ it("defaults missing, non-integer, or out-of-range to the baseline", () => {
11
+ expect(readSchemaVersion({})).toBe(1);
12
+ expect(readSchemaVersion({ schemaVersion: 1.5 })).toBe(1);
13
+ expect(readSchemaVersion({ schemaVersion: 0 })).toBe(1);
14
+ expect(readSchemaVersion({ schemaVersion: "2" })).toBe(1);
15
+ });
16
+ });
17
+
18
+ describe("migrate", () => {
19
+ it("stamps the current version on a baseline document", () => {
20
+ const out = migrate({ nodes: [], edges: [] });
21
+ expect(out.schemaVersion).toBe(CURRENT_SCHEMA_VERSION);
22
+ });
23
+
24
+ it("passes through a document already at the current version", () => {
25
+ const doc = { schemaVersion: CURRENT_SCHEMA_VERSION, nodes: [] };
26
+ expect(migrate(doc).schemaVersion).toBe(CURRENT_SCHEMA_VERSION);
27
+ });
28
+
29
+ it("does not mutate its input", () => {
30
+ const doc: Record<string, unknown> = { nodes: [] };
31
+ migrate(doc);
32
+ expect("schemaVersion" in doc).toBe(false);
33
+ });
34
+
35
+ it("rejects a document newer than this build supports", () => {
36
+ expect(() => migrate({ schemaVersion: CURRENT_SCHEMA_VERSION + 1 })).toThrow(/newer than this build/);
37
+ });
38
+
39
+ it("rejects a non-object document", () => {
40
+ expect(() => migrate(null)).toThrow(/must be a JSON object/);
41
+ expect(() => migrate([])).toThrow(/must be a JSON object/);
42
+ expect(() => migrate(42)).toThrow(/must be a JSON object/);
43
+ });
44
+ });
@@ -0,0 +1,58 @@
1
+ import type { ApiWorkflow } from "../workflow/Workflow";
2
+ import { MIGRATIONS, type Migration } from "./migrations";
3
+ import { BASELINE_SCHEMA_VERSION, CURRENT_SCHEMA_VERSION } from "./version";
4
+
5
+ /**
6
+ * Index the registry by `from` and assert it forms a contiguous chain over
7
+ * [BASELINE_SCHEMA_VERSION, CURRENT_SCHEMA_VERSION). A gap or duplicate is a
8
+ * programming error, so it throws at module load rather than on first use.
9
+ */
10
+ function buildChain(migrations: readonly Migration[]): ReadonlyMap<number, Migration> {
11
+ const byFrom = new Map<number, Migration>();
12
+ for (const m of migrations) {
13
+ if (!Number.isInteger(m.from) || m.from < BASELINE_SCHEMA_VERSION || m.from >= CURRENT_SCHEMA_VERSION) {
14
+ throw new Error(`[migration] from must be an integer in [${BASELINE_SCHEMA_VERSION}, ${CURRENT_SCHEMA_VERSION}), got ${m.from}`);
15
+ }
16
+ if (byFrom.has(m.from)) {
17
+ throw new Error(`[migration] duplicate migration from ${m.from}`);
18
+ }
19
+ byFrom.set(m.from, m);
20
+ }
21
+ for (let v = BASELINE_SCHEMA_VERSION; v < CURRENT_SCHEMA_VERSION; v++) {
22
+ if (!byFrom.has(v)) throw new Error(`[migration] missing migration from ${v} -> ${v + 1}`);
23
+ }
24
+ return byFrom;
25
+ }
26
+
27
+ const CHAIN = buildChain(MIGRATIONS);
28
+
29
+ /** Read the schema version, defaulting absent/invalid to the baseline. */
30
+ export function readSchemaVersion(doc: Record<string, unknown>): number {
31
+ const v = doc.schemaVersion;
32
+ return typeof v === "number" && Number.isInteger(v) && v >= BASELINE_SCHEMA_VERSION ? v : BASELINE_SCHEMA_VERSION;
33
+ }
34
+
35
+ /**
36
+ * Upgrade a raw, parsed workflow document to CURRENT_SCHEMA_VERSION and return
37
+ * it as the current {@link ApiWorkflow}, ready for `deserialize`. Runs on
38
+ * untyped JSON so each migration stays pinned to the shape it was written for.
39
+ * Throws on a non-object, or a version newer than this build supports.
40
+ */
41
+ export function migrate(raw: unknown): ApiWorkflow {
42
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
43
+ throw new Error("[migration] workflow document must be a JSON object");
44
+ }
45
+ let doc: Record<string, unknown> = { ...(raw as Record<string, unknown>) };
46
+
47
+ const version = readSchemaVersion(doc);
48
+ if (version > CURRENT_SCHEMA_VERSION) {
49
+ throw new Error(`[migration] schemaVersion ${version} is newer than this build supports (${CURRENT_SCHEMA_VERSION})`);
50
+ }
51
+
52
+ for (let v = version; v < CURRENT_SCHEMA_VERSION; v++) {
53
+ doc = CHAIN.get(v)!.migrate(doc); // non-null: buildChain covers every v in range
54
+ }
55
+
56
+ doc.schemaVersion = CURRENT_SCHEMA_VERSION;
57
+ return doc as ApiWorkflow;
58
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A single-step upgrade of the persisted workflow format, from `from` to
3
+ * `from + 1`. Operates on the raw parsed document, never the domain types.
4
+ * Forward-only: there is no down-path.
5
+ *
6
+ * A shipped migration is immutable — saved files depend on its exact behaviour.
7
+ * Express the next change as a new migration, never an edit to an old one.
8
+ */
9
+ export interface Migration {
10
+ /** Schema version this migration expects as input. Produces `from + 1`. */
11
+ readonly from: number;
12
+ /** Pure transform on the raw document; must not mutate its input. */
13
+ migrate(doc: Record<string, unknown>): Record<string, unknown>;
14
+ }
15
+
16
+ /**
17
+ * Ordered registry of single-step format migrations. To change the format:
18
+ * bump CURRENT_SCHEMA_VERSION, append one migration with `from` set to the
19
+ * previous value, and reconcile serialize/deserialize. {@link migrate} asserts
20
+ * at load that the chain is contiguous over [BASELINE, CURRENT).
21
+ */
22
+ export const MIGRATIONS: readonly Migration[] = [
23
+ // First entry will be `{ from: 1, migrate: (doc) => ... }`.
24
+ ];
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Persisted-format version this build reads and writes. A plain monotonic
3
+ * integer, decoupled from the contract/package semver. Bump only when the
4
+ * serialized {@link ApiWorkflow} shape changes, adding a matching migration.
5
+ */
6
+ export const CURRENT_SCHEMA_VERSION = 1;
7
+
8
+ /** Version assumed for documents that carry no `schemaVersion`. */
9
+ export const BASELINE_SCHEMA_VERSION = 1;
@@ -0,0 +1,12 @@
1
+ import type { ModelDefinition } from "./ModelDefinition";
2
+
3
+ /**
4
+ * A declared custom/self-hosted LLM model.
5
+ * Capabilities default to ["chat"] on serialize; a capability editor can be added later.
6
+ */
7
+ export const LLMModelDefinition: ModelDefinition = {
8
+ type: "LLMModel",
9
+ label: "Custom LLM Model",
10
+ description: "A self-hosted or custom LLM the llmproxy doesn't ship by default",
11
+ parameters: [],
12
+ };
@@ -0,0 +1,39 @@
1
+ // Domain Model — covers two distinct things that share a picker:
2
+ // 1. The STATIC catalog (ModelInfo[]): the set of models the llmproxy already
3
+ // supports. Supplied to the editor as data (props), not declared per-workflow.
4
+ // A node simply stores the chosen catalog ModelID directly.
5
+ // 2. DECLARED custom models (Model): self-hosted / custom models the
6
+ // llmproxy doesn't ship. These are channel-like — declared in the workflow,
7
+ // referenced by id from nodes, and mapped to an llmproxy provider at deploy.
8
+ //
9
+ // A node's `model` field is always just a ModelID string either way; static ids
10
+ // resolve via the catalog (no mapping), custom ids via a declared Model.
11
+ //
12
+ // `type` is the api discriminator; new model families (e.g. future YOLO/ONNX
13
+ // variants) register their own definition in ModelRegistry, mirroring nodes.
14
+
15
+ import type { Schemas } from "../api";
16
+
17
+ /** A capability a model supports (chat, embedding, vision, ...). From the api. */
18
+ export type ModelCapability = Schemas["ModelCapability"];
19
+
20
+ export type ModelType = "LLMModel";
21
+
22
+ export const ALL_MODEL_TYPES: ModelType[] = ["LLMModel"];
23
+
24
+ export interface Model {
25
+ id: string;
26
+ label: string;
27
+ type: ModelType;
28
+ arguments: Record<string, unknown>;
29
+ }
30
+
31
+ /**
32
+ * One entry in the static model catalog handed to the builder via props. The
33
+ * embedder maps the llmproxy's richer ModelInfo down to this minimal shape.
34
+ */
35
+ export interface ModelInfo {
36
+ id: string;
37
+ label: string;
38
+ capabilities: ModelCapability[];
39
+ }
@@ -0,0 +1,15 @@
1
+ import type { Parameter } from "../parameter";
2
+ import type { ModelType } from "./Model";
3
+
4
+ /**
5
+ * Static, per-type metadata for a declared (custom) model variant. Mirrors
6
+ * MemoryDefinition / NodeDefinition: one definition object per ModelType,
7
+ * registered in ModelRegistry. `label` is a top-level instance field (edited
8
+ * like a channel label), so it is never a parameter here.
9
+ */
10
+ export interface ModelDefinition {
11
+ type: ModelType;
12
+ label: string;
13
+ description: string;
14
+ parameters: Parameter[];
15
+ }
@@ -0,0 +1,33 @@
1
+ import type { ModelType } from "./Model";
2
+ import type { ModelDefinition } from "./ModelDefinition";
3
+ import { LLMModelDefinition } from "./LLMModelDefinition";
4
+
5
+ /**
6
+ * Central registry for declared (custom) model variant definitions (one per
7
+ * ModelType). Mirrors MemoryRegistry / NodeRegistry.
8
+ */
9
+ class ModelDefinitionRegistry {
10
+ private models: Map<ModelType, ModelDefinition> = new Map();
11
+ private initialized = false;
12
+
13
+ initialize() {
14
+ if (this.initialized) return;
15
+ this.register(LLMModelDefinition);
16
+ this.initialized = true;
17
+ }
18
+
19
+ private register(definition: ModelDefinition) {
20
+ this.models.set(definition.type, definition);
21
+ }
22
+
23
+ getAll(): ModelDefinition[] {
24
+ return Array.from(this.models.values());
25
+ }
26
+
27
+ getByType(type: ModelType): ModelDefinition | undefined {
28
+ return this.models.get(type);
29
+ }
30
+ }
31
+
32
+ export const ModelRegistry = new ModelDefinitionRegistry();
33
+ ModelRegistry.initialize();
@@ -0,0 +1,7 @@
1
+ export type { ModelType, Model, ModelCapability, ModelInfo } from "./Model";
2
+ export { ALL_MODEL_TYPES } from "./Model";
3
+ export type { ModelDefinition } from "./ModelDefinition";
4
+ export { LLMModelDefinition } from "./LLMModelDefinition";
5
+ export { ModelRegistry } from "./ModelRegistry";
6
+ export { serialize, deserialize } from "./serialization";
7
+ export type { ApiModel } from "./serialization";
@@ -0,0 +1,30 @@
1
+ import type { Schemas } from "../api";
2
+ import type { Model, ModelCapability } from "./Model";
3
+
4
+ export type ApiModel = Schemas["Model"];
5
+
6
+ /** Serialize a domain Model to the API discriminated-union shape. */
7
+ export function serialize(model: Model): ApiModel {
8
+ const { id, label, type, arguments: args } = model;
9
+ switch (type) {
10
+ case "LLMModel":
11
+ return {
12
+ type,
13
+ id,
14
+ label,
15
+ capabilities: (args.capabilities as ModelCapability[]) ?? ["chat"],
16
+ };
17
+ }
18
+ }
19
+
20
+ /** Convert an API Model into a domain Model. */
21
+ export function deserialize(api: ApiModel): Model {
22
+ const { id, label, type } = api;
23
+ const args: Record<string, unknown> = {};
24
+ switch (type) {
25
+ case "LLMModel":
26
+ args.capabilities = api.capabilities;
27
+ break;
28
+ }
29
+ return { id, label, type, arguments: args };
30
+ }
@@ -0,0 +1,82 @@
1
+ import { NodeBase } from "./Node";
2
+ import { OutputBinding, OutputDeclaration } from "../parameter";
3
+ import type { Schemas } from "../api";
4
+ import { NodeCategory } from "./constants";
5
+ import { NodeDefinition } from "./NodeDefinition";
6
+
7
+ export type MemoryRef = Schemas["MemoryRef"];
8
+
9
+ export interface AgentNode extends NodeBase {
10
+ type: "Agent";
11
+ arguments: {
12
+ name: string;
13
+ model: string;
14
+ instructions: string;
15
+ maxTurns: number | undefined;
16
+ outputDeclarations: OutputDeclaration[];
17
+ memoryRefs: MemoryRef[];
18
+ answer: OutputBinding;
19
+ toolDescription?: string;
20
+ };
21
+ }
22
+
23
+ export type AgentNodeType = "Agent";
24
+
25
+ export const AgentNodeDefinition: NodeDefinition = {
26
+ type: "Agent",
27
+ label: "LLM Agent",
28
+ category: NodeCategory.AI,
29
+ description: "AI-powered agent for intelligent processing",
30
+ canBranch: true, // Control output may fan out to multiple branches (tool output is always multi-target).
31
+ outputs: [
32
+ { id: "answer", label: "Answer", type: "static", dataType: "string" },
33
+ { id: "outputDeclarations", label: "Structured Output", type: "list" },
34
+ ],
35
+ parameters: [
36
+ {
37
+ id: "name",
38
+ label: "Name",
39
+ description: "Name of the agent",
40
+ optional: true,
41
+ type: "string",
42
+ },
43
+ {
44
+ id: "model",
45
+ label: "Model",
46
+ description: "AI model to use for the agent",
47
+ type: "modelSelect",
48
+ modelType: ["LLMModel"],
49
+ capabilities: ["chat"],
50
+ },
51
+ {
52
+ id: "instructions",
53
+ label: "Instructions",
54
+ description: "Instructions for the cloud agent that act as system prompt",
55
+ type: "string",
56
+ multiline: true,
57
+ optional: true,
58
+ },
59
+ {
60
+ id: "maxTurns",
61
+ label: "Max Turns",
62
+ description: "Maximum number of agent runner turns",
63
+ type: "int",
64
+ optional: true,
65
+ },
66
+ {
67
+ id: "memoryRefs",
68
+ label: "Memory Files",
69
+ description: "Project memory files this agent can access, with per-file read or read+write mode",
70
+ type: "memory-refs",
71
+ default: [],
72
+ },
73
+ {
74
+ id: "toolDescription",
75
+ label: "Tool Description",
76
+ description: "Description shown to the calling agent when this agent is wired as a tool",
77
+ type: "string",
78
+ multiline: true,
79
+ activationRules: [{ type: "isToolInput" }],
80
+ },
81
+ ],
82
+ };