@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,11 @@
1
+ export type { DiagnosticSeverity, DiagnosticCategory, Diagnostic, CanvasValidationResult, ValidationResult } from "./diagnostics";
2
+ export {
3
+ computeNodeDiagnostics,
4
+ computeEdgeDiagnostics,
5
+ validateChannel,
6
+ validateMemory,
7
+ validateModel,
8
+ validateFunction,
9
+ validateFunctionOutputs,
10
+ validateWorkflowState,
11
+ } from "./diagnostics";
@@ -0,0 +1,23 @@
1
+ import type { Expression } from "../api";
2
+
3
+ export interface EdgeData extends Record<string, unknown> {
4
+ prompt?: Expression; // agentTask and agentDelegate
5
+ description?: string; // agentChoice and agentDelegate
6
+ }
7
+
8
+ /**
9
+ * Full domain edge entity held on a {@link Canvas}: connectivity topology plus
10
+ * the optional {@link EdgeData} payload. Low-level core fns that read only
11
+ * connectivity accept it directly, and the editor's React Flow `Edge[]` is
12
+ * structurally assignable
13
+ * without an adapter (core stays free of `@xyflow/react`).
14
+ */
15
+ export interface Edge {
16
+ id: string;
17
+ type?: string;
18
+ source: string;
19
+ sourceHandle?: string | null;
20
+ target: string;
21
+ targetHandle?: string | null;
22
+ data?: EdgeData;
23
+ }
@@ -0,0 +1,45 @@
1
+ import { Parameter } from "../parameter";
2
+ import type { EdgeType } from "./EdgeType";
3
+
4
+ export interface EdgeDefinition {
5
+ label: string;
6
+ description: string;
7
+ parameters: Parameter[];
8
+ }
9
+
10
+ export const EDGE_DEFINITIONS: Record<EdgeType, EdgeDefinition> = {
11
+ agentTask: {
12
+ label: "Task",
13
+ description: "Assigns a task to an agent with a prompt",
14
+ parameters: [{ id: "prompt", label: "Prompt", description: "", type: "expression", expressionType: "string", default: { expression: "", references: [], dataType: "string" } }],
15
+ },
16
+ agentChoice: {
17
+ label: "Choice",
18
+ description: "A path the agent can choose based on its description",
19
+ parameters: [
20
+ { id: "description", label: "Description", description: "", type: "string", default: "When should the agent take this path?" },
21
+ ],
22
+ },
23
+ agentDelegate: {
24
+ label: "Delegation",
25
+ description: "Lets an agent hand off control to another agent",
26
+ parameters: [
27
+ { id: "description", label: "Description", description: "", type: "string", default: "When should the agent take this path?" },
28
+ { id: "prompt", label: "Prompt", description: "", type: "expression", expressionType: "string", default: { expression: "", references: [], dataType: "string" } },
29
+ ],
30
+ },
31
+ control: {
32
+ label: "Control",
33
+ description: "Sequential control flow between nodes",
34
+ parameters: [],
35
+ },
36
+ tool: {
37
+ label: "Tool",
38
+ description: "Tool connection between nodes",
39
+ parameters: [],
40
+ },
41
+ };
42
+
43
+ export function getEdgeDefinition(type: EdgeType): EdgeDefinition {
44
+ return EDGE_DEFINITIONS[type];
45
+ }
@@ -0,0 +1,19 @@
1
+ // Edge taxonomy: a plain control/tool edge, or one of the three agent-specific
2
+ // control-flow refinements resolved at connection time from source/target context.
3
+ // Node port handles themselves only carry `control` or `tool`. Tool edges have no
4
+ // agent-specific variant — every tool edge points at an agent by construction.
5
+ export type EdgeType = "control" | "tool" | "agentTask" | "agentChoice" | "agentDelegate";
6
+
7
+ // All types that behave like control flow (horizontal bezier, side ports)
8
+ export type ControlFlowType = "control" | "agentTask" | "agentChoice" | "agentDelegate";
9
+
10
+ // All types that behave like tool connections (vertical bezier, top/bottom ports)
11
+ export type ToolFlowType = "tool";
12
+
13
+ export function isControlFlow(type: EdgeType): type is ControlFlowType {
14
+ return type === "control" || type === "agentTask" || type === "agentChoice" || type === "agentDelegate";
15
+ }
16
+
17
+ export function isToolFlow(type: EdgeType): type is ToolFlowType {
18
+ return type === "tool";
19
+ }
@@ -0,0 +1,8 @@
1
+ // Public surface of the edge module. The base type (EdgeData) lives in
2
+ // ./Edge; this file is a barrel only. Mirrors channel/memory/model.
3
+
4
+ export type { EdgeData, Edge } from "./Edge";
5
+ export { type EdgeDefinition, getEdgeDefinition, EDGE_DEFINITIONS } from "./EdgeDefinition";
6
+ export { type EdgeType, type ControlFlowType, type ToolFlowType, isControlFlow, isToolFlow } from "./EdgeType";
7
+ export { serialize, deserialize } from "./serialization";
8
+ export type { ApiEdge } from "./serialization";
@@ -0,0 +1,83 @@
1
+ import type { Schemas } from "../api";
2
+ import type { Edge, EdgeData } from "./Edge";
3
+ import type { EdgeType } from "./EdgeType";
4
+
5
+ export type ApiEdge = Schemas["Edge"];
6
+
7
+ /**
8
+ * Serialize a domain {@link Edge} to the api `Edge`. The `id` is preserved
9
+ * (the api requires it). Edge-type-conditional metadata (`prompt`,
10
+ * `description`) is reattached from the edge's `data` payload.
11
+ */
12
+ export function serialize(edge: Edge): ApiEdge {
13
+ const sourceHandle = edge.sourceHandle || "";
14
+ const targetHandle = edge.targetHandle || "";
15
+ const edgeType = edge.type as EdgeType | undefined;
16
+ const from = { nodeId: edge.source, port: sourceHandle };
17
+ const to = { nodeId: edge.target, port: targetHandle };
18
+
19
+ switch (edgeType) {
20
+ case "agentTask":
21
+ return {
22
+ id: edge.id,
23
+ type: "agentTask",
24
+ from,
25
+ to,
26
+ prompt: (edge.data?.prompt as Schemas["Expression"]) ?? { expression: "", references: [], dataType: "string" },
27
+ };
28
+ case "agentChoice":
29
+ return {
30
+ id: edge.id,
31
+ type: "agentChoice",
32
+ from,
33
+ to,
34
+ ...(edge.data?.description ? { description: edge.data.description as string } : {}),
35
+ };
36
+ case "agentDelegate":
37
+ return {
38
+ id: edge.id,
39
+ type: "agentDelegate",
40
+ from,
41
+ to,
42
+ ...(edge.data?.prompt ? { prompt: edge.data.prompt as Schemas["Expression"] } : {}),
43
+ ...(edge.data?.description ? { description: edge.data.description as string } : {}),
44
+ };
45
+ case "control":
46
+ return { id: edge.id, type: "control", from, to };
47
+ case "tool":
48
+ return { id: edge.id, type: "tool", from, to };
49
+ default:
50
+ return {
51
+ id: edge.id,
52
+ type: sourceHandle.startsWith("ctrl") || targetHandle.startsWith("ctrl") ? "control" : "tool",
53
+ from,
54
+ to,
55
+ };
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Convert an api `Edge` into a domain {@link Edge}. The api's `id` is preserved
61
+ * verbatim (earlier code synthesized `e${index+1}`, breaking roundtrip
62
+ * identity). Edge-type-conditional metadata (`prompt` on agentTask/agentDelegate;
63
+ * `description` on agentChoice/agentDelegate) is folded into `data` as
64
+ * {@link EdgeData}.
65
+ */
66
+ export function deserialize(apiEdge: ApiEdge): Edge {
67
+ let data: EdgeData | undefined;
68
+ if ((apiEdge.type === "agentTask" || apiEdge.type === "agentDelegate") && apiEdge.prompt) {
69
+ data = { ...data, prompt: apiEdge.prompt };
70
+ }
71
+ if ((apiEdge.type === "agentChoice" || apiEdge.type === "agentDelegate") && apiEdge.description) {
72
+ data = { ...data, description: apiEdge.description };
73
+ }
74
+ return {
75
+ id: apiEdge.id,
76
+ type: apiEdge.type,
77
+ source: apiEdge.from.nodeId,
78
+ sourceHandle: apiEdge.from.port,
79
+ target: apiEdge.to.nodeId,
80
+ targetHandle: apiEdge.to.port,
81
+ ...(data ? { data } : {}),
82
+ };
83
+ }
@@ -0,0 +1,4 @@
1
+ export type { ParseResult } from "./parser";
2
+ export { parseExpression } from "./parser";
3
+ export type { ResolvedExpr } from "./types";
4
+ export { resolveExpression, isExpression } from "./types";
@@ -0,0 +1,362 @@
1
+ import jsep from "jsep";
2
+ import type { DataType } from "../api";
3
+ import { ResolvedExpr } from "./types";
4
+
5
+ export interface ParseResult {
6
+ isValid: boolean;
7
+ inferredType: DataType | null;
8
+ errors: string[];
9
+ }
10
+
11
+ // Map of placeholder names to their types (populated from variables)
12
+ type TypeContext = Map<string, DataType>;
13
+
14
+ /**
15
+ * Parse and validate an expression against C-style type rules.
16
+ * Replaces ${} placeholders with temporary identifiers, parses with jsep,
17
+ * infers types from the AST, and validates against the expected type.
18
+ */
19
+ export function parseExpression(expr: ResolvedExpr): ParseResult {
20
+ // Handle early returns
21
+ if (!expr.expression.trim()) {
22
+ return { isValid: false, inferredType: null, errors: ["Expression is empty"] };
23
+ }
24
+ if (expr.variables.some((v) => v === null)) {
25
+ return { isValid: false, inferredType: null, errors: ["Expression contains stale variable references"] };
26
+ }
27
+
28
+ const errors: string[] = [];
29
+
30
+ // 1. Build type context from variables
31
+ const typeContext: TypeContext = new Map();
32
+
33
+ // Replace ${} placeholders with temp identifiers for jsep
34
+ let parsableExpr = expr.expression;
35
+
36
+ // Count placeholders in expression
37
+ const placeholderCount = (expr.expression.match(/\$\{\}/g) || []).length;
38
+
39
+ if (placeholderCount !== expr.variables.length) {
40
+ errors.push(`Placeholder count (${placeholderCount}) doesn't match reference count (${expr.variables.length})`);
41
+ return { isValid: false, inferredType: null, errors };
42
+ }
43
+
44
+ expr.variables.forEach((variable, i) => {
45
+ const placeholder = `__var${i}__`;
46
+ parsableExpr = parsableExpr.replace("${}", placeholder);
47
+
48
+ if (variable) {
49
+ typeContext.set(placeholder, variable.dataType);
50
+ } else {
51
+ // Stale reference - variable was deleted
52
+ errors.push(`Referenced variable at position ${i + 1} not found`);
53
+ }
54
+ });
55
+
56
+ // If we have stale/null variables, don't proceed with parsing
57
+ if (errors.length > 0) {
58
+ return { isValid: false, inferredType: null, errors };
59
+ }
60
+
61
+ // 2. For string-type expressions, auto-wrap bare text as string literals
62
+ // so users don't need to type quotes. E.g. `hello __var0__` → `"hello " + __var0__`
63
+ if (expr.expectedType === "string") {
64
+ parsableExpr = wrapStringTemplate(parsableExpr);
65
+ }
66
+
67
+ // 3. Parse with jsep
68
+ let ast: jsep.Expression;
69
+ try {
70
+ ast = jsep(parsableExpr);
71
+ } catch (e) {
72
+ const message = e instanceof Error ? e.message : String(e);
73
+ return { isValid: false, inferredType: null, errors: [`Parse error: ${message}`] };
74
+ }
75
+
76
+ // 4. Infer type by walking AST
77
+ const inferredType = inferType(ast, typeContext, errors);
78
+
79
+ // 5. Check against expected type
80
+ if (inferredType && expr.expectedType !== inferredType) {
81
+ // Allow implicit conversions (int → float, etc.)
82
+ if (!isCompatible(inferredType, expr.expectedType)) {
83
+ errors.push(`Type mismatch: expression evaluates to '${inferredType}', expected '${expr.expectedType}'`);
84
+ }
85
+ }
86
+
87
+ return {
88
+ isValid: errors.length === 0 && inferredType !== null,
89
+ inferredType,
90
+ errors,
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Recursively infer the type of an AST node
96
+ */
97
+ function inferType(node: jsep.Expression, ctx: TypeContext, errors: string[]): DataType | null {
98
+ switch (node.type) {
99
+ case "Literal":
100
+ return inferLiteralType(node as jsep.Literal);
101
+
102
+ case "Identifier": {
103
+ const name = (node as jsep.Identifier).name;
104
+ const type = ctx.get(name);
105
+ if (!type) {
106
+ // Check if it's a placeholder variable that's missing
107
+ if (name.startsWith("__var") && name.endsWith("__")) {
108
+ errors.push(`Missing type for variable reference`);
109
+ } else {
110
+ errors.push(`Unknown identifier: '${name}'`);
111
+ }
112
+ }
113
+ return type ?? null;
114
+ }
115
+
116
+ case "BinaryExpression":
117
+ return inferBinaryType(node as jsep.BinaryExpression, ctx, errors);
118
+
119
+ case "UnaryExpression":
120
+ return inferUnaryType(node as jsep.UnaryExpression, ctx, errors);
121
+
122
+ case "ConditionalExpression": {
123
+ // Ternary: condition ? consequent : alternate
124
+ const cond = node as jsep.ConditionalExpression;
125
+ const condType = inferType(cond.test, ctx, errors);
126
+ if (condType !== "bool") {
127
+ errors.push("Ternary condition must be boolean");
128
+ }
129
+ const consType = inferType(cond.consequent, ctx, errors);
130
+ const altType = inferType(cond.alternate, ctx, errors);
131
+ if (consType && altType && consType !== altType) {
132
+ // Allow numeric promotion
133
+ if (isNumeric(consType) && isNumeric(altType)) {
134
+ return "float";
135
+ }
136
+ errors.push(`Ternary branches must have same type (got '${consType}' and '${altType}')`);
137
+ }
138
+ return consType;
139
+ }
140
+
141
+ case "MemberExpression": {
142
+ // For now, we don't support member expressions in C code generation
143
+ errors.push("Member expressions (e.g., obj.property) are not supported");
144
+ return null;
145
+ }
146
+
147
+ case "CallExpression": {
148
+ return inferCallType(node as jsep.CallExpression, ctx, errors);
149
+ }
150
+
151
+ default:
152
+ errors.push(`Unsupported expression type: ${node.type}`);
153
+ return null;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Infer the type of a literal value
159
+ */
160
+ function inferLiteralType(node: jsep.Literal): DataType {
161
+ const value = node.value;
162
+
163
+ if (typeof value === "boolean") return "bool";
164
+
165
+ if (typeof value === "number") {
166
+ // Check if it's an integer or float
167
+ return Number.isInteger(value) ? "int" : "float";
168
+ }
169
+
170
+ if (typeof value === "string") return "string";
171
+
172
+ // null/undefined - default to int
173
+ return "int";
174
+ }
175
+
176
+ /**
177
+ * Infer the result type of a binary operation
178
+ */
179
+ function inferBinaryType(node: jsep.BinaryExpression, ctx: TypeContext, errors: string[]): DataType | null {
180
+ const leftType = inferType(node.left, ctx, errors);
181
+ const rightType = inferType(node.right, ctx, errors);
182
+ const op = node.operator;
183
+
184
+ // If either operand failed to type, propagate null
185
+ if (!leftType || !rightType) return null;
186
+
187
+ // String concatenation: string + any → string
188
+ if (op === "+" && (leftType === "string" || rightType === "string")) {
189
+ return "string";
190
+ }
191
+
192
+ // Arithmetic operators: int/float → int/float (promote to float if either is float)
193
+ if (["+", "-", "*", "/"].includes(op)) {
194
+ if (!isNumeric(leftType) || !isNumeric(rightType)) {
195
+ errors.push(`Operator '${op}' requires numeric operands (got '${leftType}' and '${rightType}')`);
196
+ return null;
197
+ }
198
+ return leftType === "float" || rightType === "float" ? "float" : "int";
199
+ }
200
+
201
+ // Modulo: int only
202
+ if (op === "%") {
203
+ if (leftType !== "int" || rightType !== "int") {
204
+ errors.push(`Operator '%' requires integer operands (got '${leftType}' and '${rightType}')`);
205
+ return null;
206
+ }
207
+ return "int";
208
+ }
209
+
210
+ // Comparison operators: numeric → bool
211
+ if (["<", ">", "<=", ">="].includes(op)) {
212
+ if (!isNumeric(leftType) || !isNumeric(rightType)) {
213
+ errors.push(`Operator '${op}' requires numeric operands (got '${leftType}' and '${rightType}')`);
214
+ }
215
+ return "bool";
216
+ }
217
+
218
+ // Equality operators: any → bool (but types should match)
219
+ if (["==", "!="].includes(op)) {
220
+ if (leftType !== rightType && !(isNumeric(leftType) && isNumeric(rightType))) {
221
+ errors.push(`Comparing incompatible types: '${leftType}' and '${rightType}'`);
222
+ }
223
+ return "bool";
224
+ }
225
+
226
+ // Logical operators: bool → bool
227
+ if (["&&", "||"].includes(op)) {
228
+ if (leftType !== "bool" || rightType !== "bool") {
229
+ errors.push(`Operator '${op}' requires boolean operands (got '${leftType}' and '${rightType}')`);
230
+ }
231
+ return "bool";
232
+ }
233
+
234
+ // Bitwise operators: int → int
235
+ if (["&", "|", "^", "<<", ">>"].includes(op)) {
236
+ if (leftType !== "int" || rightType !== "int") {
237
+ errors.push(`Bitwise operator '${op}' requires integer operands (got '${leftType}' and '${rightType}')`);
238
+ return null;
239
+ }
240
+ return "int";
241
+ }
242
+
243
+ errors.push(`Unknown operator: '${op}'`);
244
+ return null;
245
+ }
246
+
247
+ /**
248
+ * Infer the result type of a unary operation
249
+ */
250
+ function inferUnaryType(node: jsep.UnaryExpression, ctx: TypeContext, errors: string[]): DataType | null {
251
+ const argType = inferType(node.argument, ctx, errors);
252
+
253
+ if (!argType) return null;
254
+
255
+ // Logical NOT: bool → bool
256
+ if (node.operator === "!") {
257
+ if (argType !== "bool") {
258
+ errors.push(`Logical NOT '!' requires boolean operand (got '${argType}')`);
259
+ }
260
+ return "bool";
261
+ }
262
+
263
+ // Unary plus/minus: numeric → same type
264
+ if (node.operator === "-" || node.operator === "+") {
265
+ if (!isNumeric(argType)) {
266
+ errors.push(`Unary '${node.operator}' requires numeric operand (got '${argType}')`);
267
+ }
268
+ return argType;
269
+ }
270
+
271
+ // Bitwise NOT: int → int
272
+ if (node.operator === "~") {
273
+ if (argType !== "int") {
274
+ errors.push(`Bitwise NOT '~' requires integer operand (got '${argType}')`);
275
+ }
276
+ return "int";
277
+ }
278
+
279
+ errors.push(`Unknown unary operator: '${node.operator}'`);
280
+ return argType;
281
+ }
282
+
283
+ /** Cast functions: expression syntax uses int(), float(), bool(), str()
284
+ * which the code generator translates to C casts / conversion calls.
285
+ */
286
+ const CAST_FUNCTIONS: Record<string, DataType> = {
287
+ int: "int",
288
+ float: "float",
289
+ bool: "bool",
290
+ str: "string",
291
+ };
292
+
293
+ /**
294
+ * Infer the result type of a cast function call, e.g. int(expr), str(expr).
295
+ * Any type can be cast to any other type — it's the user's explicit intent.
296
+ */
297
+ function inferCallType(node: jsep.CallExpression, ctx: TypeContext, errors: string[]): DataType | null {
298
+ const callee = node.callee;
299
+ if (callee.type !== "Identifier") {
300
+ errors.push("Only cast functions are supported (int, float, bool, str)");
301
+ return null;
302
+ }
303
+
304
+ const name = (callee as jsep.Identifier).name;
305
+ const targetType = CAST_FUNCTIONS[name];
306
+ if (!targetType) {
307
+ errors.push(`Unknown function '${name}'. Available cast functions: int(), float(), bool(), str()`);
308
+ return null;
309
+ }
310
+
311
+ if (node.arguments.length !== 1) {
312
+ errors.push(`${name}() expects exactly 1 argument, got ${node.arguments.length}`);
313
+ return null;
314
+ }
315
+
316
+ // Validate the argument expression (ensure it type-checks). Length checked as 1 above.
317
+ inferType(node.arguments[0]!, ctx, errors);
318
+
319
+ return targetType;
320
+ }
321
+
322
+ /**
323
+ * Check if a type is numeric (int or float)
324
+ */
325
+ function isNumeric(type: DataType | null): boolean {
326
+ return type === "int" || type === "float";
327
+ }
328
+
329
+ /**
330
+ * Check if a type can be implicitly converted to another.
331
+ * - int ↔ float: both directions (truncation for float→int, common in embedded)
332
+ * - any → string: allowed (format strings)
333
+ * - bool is strict: requires explicit cast to/from bool
334
+ */
335
+ function isCompatible(from: DataType, to: DataType): boolean {
336
+ if (from === to) return true;
337
+ if (isNumeric(from) && isNumeric(to)) return true;
338
+ if (to === "string") return true;
339
+ return false;
340
+ }
341
+
342
+ /**
343
+ * Wrap bare text in a string-type expression into quoted string literals for jsep.
344
+ * Splits by variable placeholders (__varN__), quotes text segments, joins with +.
345
+ * E.g. `hello __var0__` → `"hello " + __var0__`
346
+ */
347
+ function wrapStringTemplate(expr: string): string {
348
+ // Split by __varN__ placeholders but keep them as delimiters
349
+ const parts = expr.split(/(__var\d+__)/);
350
+ const segments: string[] = [];
351
+
352
+ for (const part of parts) {
353
+ if (/^__var\d+__$/.test(part)) {
354
+ segments.push(part);
355
+ } else if (part !== "") {
356
+ const escaped = part.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
357
+ segments.push(`"${escaped}"`);
358
+ }
359
+ }
360
+
361
+ return segments.join(" + ");
362
+ }
@@ -0,0 +1,30 @@
1
+ import type { NodeOutput } from "../node";
2
+ import type { DataType, Expression } from "../api";
3
+ import type { Variable } from "../variable";
4
+ import { refToLookupKey } from "../variable";
5
+
6
+ // ResolvedExpr represents an expression with its variable references resolved to runtime variables
7
+ export interface ResolvedExpr {
8
+ expression: string; // The expression string with variable references, e.g. "${} + ${}"
9
+ variables: (NodeOutput | null)[]; // List of variables used in the expression (null if stale/invalid)
10
+ expectedType: DataType; // The expected data type of the expression result
11
+ }
12
+
13
+ // Resolve an expression by converting variable references to runtime variables
14
+ export function resolveExpression(apiExpr: Expression, availableVars: Record<string, Variable>): ResolvedExpr {
15
+ return {
16
+ expression: apiExpr.expression,
17
+ expectedType: apiExpr.dataType,
18
+ variables: apiExpr.references.map((ref) => {
19
+ if (!ref.varId) return null;
20
+ const key = refToLookupKey(ref);
21
+ const v = availableVars[key];
22
+ return v ? { name: v.name, dataType: v.dataType } : null;
23
+ }),
24
+ };
25
+ }
26
+
27
+ // Check if a value is an expression (has references array)
28
+ export function isExpression(value: unknown): value is Expression {
29
+ return typeof value === "object" && value !== null && "expression" in value && "references" in value;
30
+ }
@@ -0,0 +1,54 @@
1
+ import type { Expression, Schemas } from "../api";
2
+ import type { ApiVariable } from "../variable";
3
+ import type { DataType } from "../parameter";
4
+
5
+ /**
6
+ * The flat call-site signature of a function: identity + version + ports, with no
7
+ * expressions (the contract's `FunctionInfo`, used both inside `Function` on the wire
8
+ * and here in the domain). It is the snapshot a {@link FunctionCallNode} caches — to
9
+ * detect drift against the live declaration and render its ports without a registry
10
+ * lookup — and the shape {@link buildFunctionNodeDef} consumes.
11
+ */
12
+ export type FunctionInfo = Schemas["FunctionInfo"];
13
+
14
+ /**
15
+ * A function output: its declaration (`uid`/`name`/`dataType`) bundled with the
16
+ * `expression` that produces it (evaluated in callee scope at the function's end).
17
+ * Acts as a supertype of {@link ApiVariable}.
18
+ */
19
+ export interface OutputAssignment {
20
+ uid: string;
21
+ name: string;
22
+ dataType: DataType;
23
+ expression: Expression;
24
+ }
25
+
26
+ /**
27
+ * The domain function declaration: a signature with its outputs bundled
28
+ * (declaration + assignment per output).
29
+ * Separate from the function body (which is a canvas of nodes, edges, variables).
30
+ */
31
+ export interface FunctionDeclaration {
32
+ id: string;
33
+ version: number;
34
+ name: string;
35
+ arguments: ApiVariable[];
36
+ outputs: OutputAssignment[];
37
+ }
38
+
39
+ /**
40
+ * Project a declaration to the flat call-site signature, dropping the expressions
41
+ * (the caller has no business storing the callee's internals). Used only when
42
+ * crossing from declaration to a call-site snapshot — dropping or migrating a
43
+ * `FunctionCall` node — and by serialization. Never used to represent a function
44
+ * within the domain.
45
+ */
46
+ export function toFunctionInfo(fn: FunctionDeclaration): FunctionInfo {
47
+ return {
48
+ id: fn.id,
49
+ version: fn.version,
50
+ name: fn.name,
51
+ arguments: fn.arguments,
52
+ returns: fn.outputs.map(({ uid, name, dataType }) => ({ uid, name, dataType })),
53
+ };
54
+ }
@@ -0,0 +1,3 @@
1
+ export type { OutputAssignment, FunctionDeclaration, FunctionInfo } from "./FunctionDeclaration";
2
+ export { toFunctionInfo } from "./FunctionDeclaration";
3
+ export { serialize, deserialize } from "./serialization";