@foresthubai/workflow-core 0.4.4 → 0.4.6

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 (287) hide show
  1. package/dist/api/deployment.d.ts +26 -19
  2. package/dist/api/deployment.d.ts.map +1 -1
  3. package/dist/api/engine.d.ts +26 -26
  4. package/dist/api/engine.d.ts.map +1 -1
  5. package/dist/api/index.d.ts.map +1 -1
  6. package/dist/api/index.js +2 -0
  7. package/dist/api/index.js.map +1 -1
  8. package/dist/api/workflow.d.ts +10 -10
  9. package/dist/api/workflow.d.ts.map +1 -1
  10. package/dist/channel/Channel.d.ts.map +1 -1
  11. package/dist/channel/Channel.js +2 -0
  12. package/dist/channel/Channel.js.map +1 -1
  13. package/dist/channel/ChannelDefinition.d.ts.map +1 -1
  14. package/dist/channel/ChannelDefinition.js +2 -0
  15. package/dist/channel/ChannelDefinition.js.map +1 -1
  16. package/dist/channel/index.d.ts.map +1 -1
  17. package/dist/channel/index.js +2 -0
  18. package/dist/channel/index.js.map +1 -1
  19. package/dist/channel/serialization.d.ts.map +1 -1
  20. package/dist/channel/serialization.js +2 -0
  21. package/dist/channel/serialization.js.map +1 -1
  22. package/dist/deploy/index.d.ts +1 -1
  23. package/dist/deploy/index.d.ts.map +1 -1
  24. package/dist/deploy/index.js +2 -0
  25. package/dist/deploy/index.js.map +1 -1
  26. package/dist/deploy/inputs.d.ts.map +1 -1
  27. package/dist/deploy/inputs.js +2 -10
  28. package/dist/deploy/inputs.js.map +1 -1
  29. package/dist/deploy/requirements.d.ts.map +1 -1
  30. package/dist/deploy/requirements.js +2 -0
  31. package/dist/deploy/requirements.js.map +1 -1
  32. package/dist/deploy/spec.d.ts +2 -7
  33. package/dist/deploy/spec.d.ts.map +1 -1
  34. package/dist/deploy/spec.js +32 -23
  35. package/dist/deploy/spec.js.map +1 -1
  36. package/dist/diagnostics/__fixtures__/diagnosticFixtures.d.ts.map +1 -1
  37. package/dist/diagnostics/__fixtures__/diagnosticFixtures.js +2 -0
  38. package/dist/diagnostics/__fixtures__/diagnosticFixtures.js.map +1 -1
  39. package/dist/diagnostics/diagnostics.d.ts.map +1 -1
  40. package/dist/diagnostics/diagnostics.js +2 -0
  41. package/dist/diagnostics/diagnostics.js.map +1 -1
  42. package/dist/diagnostics/index.d.ts.map +1 -1
  43. package/dist/diagnostics/index.js +2 -0
  44. package/dist/diagnostics/index.js.map +1 -1
  45. package/dist/edge/Edge.d.ts.map +1 -1
  46. package/dist/edge/Edge.js +2 -0
  47. package/dist/edge/Edge.js.map +1 -1
  48. package/dist/edge/EdgeDefinition.d.ts.map +1 -1
  49. package/dist/edge/EdgeDefinition.js +2 -0
  50. package/dist/edge/EdgeDefinition.js.map +1 -1
  51. package/dist/edge/EdgeType.d.ts.map +1 -1
  52. package/dist/edge/EdgeType.js +2 -0
  53. package/dist/edge/EdgeType.js.map +1 -1
  54. package/dist/edge/index.d.ts.map +1 -1
  55. package/dist/edge/index.js +2 -2
  56. package/dist/edge/index.js.map +1 -1
  57. package/dist/edge/serialization.d.ts.map +1 -1
  58. package/dist/edge/serialization.js +2 -0
  59. package/dist/edge/serialization.js.map +1 -1
  60. package/dist/expression/index.d.ts.map +1 -1
  61. package/dist/expression/index.js +2 -0
  62. package/dist/expression/index.js.map +1 -1
  63. package/dist/expression/parser.d.ts.map +1 -1
  64. package/dist/expression/parser.js +2 -0
  65. package/dist/expression/parser.js.map +1 -1
  66. package/dist/expression/types.d.ts.map +1 -1
  67. package/dist/expression/types.js +2 -0
  68. package/dist/expression/types.js.map +1 -1
  69. package/dist/function/FunctionDeclaration.d.ts.map +1 -1
  70. package/dist/function/FunctionDeclaration.js +2 -0
  71. package/dist/function/FunctionDeclaration.js.map +1 -1
  72. package/dist/function/index.d.ts.map +1 -1
  73. package/dist/function/index.js +2 -0
  74. package/dist/function/index.js.map +1 -1
  75. package/dist/function/serialization.d.ts.map +1 -1
  76. package/dist/function/serialization.js +2 -0
  77. package/dist/function/serialization.js.map +1 -1
  78. package/dist/id/index.d.ts.map +1 -1
  79. package/dist/id/index.js +2 -0
  80. package/dist/id/index.js.map +1 -1
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +2 -0
  83. package/dist/index.js.map +1 -1
  84. package/dist/memory/Memory.d.ts.map +1 -1
  85. package/dist/memory/Memory.js +2 -0
  86. package/dist/memory/Memory.js.map +1 -1
  87. package/dist/memory/MemoryDefinition.d.ts.map +1 -1
  88. package/dist/memory/MemoryDefinition.js +2 -0
  89. package/dist/memory/MemoryDefinition.js.map +1 -1
  90. package/dist/memory/MemoryFileDefinition.d.ts.map +1 -1
  91. package/dist/memory/MemoryFileDefinition.js +2 -0
  92. package/dist/memory/MemoryFileDefinition.js.map +1 -1
  93. package/dist/memory/MemoryRegistry.d.ts.map +1 -1
  94. package/dist/memory/MemoryRegistry.js +2 -0
  95. package/dist/memory/MemoryRegistry.js.map +1 -1
  96. package/dist/memory/VectorDatabaseDefinition.d.ts.map +1 -1
  97. package/dist/memory/VectorDatabaseDefinition.js +2 -0
  98. package/dist/memory/VectorDatabaseDefinition.js.map +1 -1
  99. package/dist/memory/index.d.ts.map +1 -1
  100. package/dist/memory/index.js +2 -0
  101. package/dist/memory/index.js.map +1 -1
  102. package/dist/memory/serialization.d.ts.map +1 -1
  103. package/dist/memory/serialization.js +2 -0
  104. package/dist/memory/serialization.js.map +1 -1
  105. package/dist/migration/index.d.ts.map +1 -1
  106. package/dist/migration/index.js +2 -0
  107. package/dist/migration/index.js.map +1 -1
  108. package/dist/migration/migrate.d.ts.map +1 -1
  109. package/dist/migration/migrate.js +2 -0
  110. package/dist/migration/migrate.js.map +1 -1
  111. package/dist/migration/migrations.d.ts.map +1 -1
  112. package/dist/migration/migrations.js +2 -0
  113. package/dist/migration/migrations.js.map +1 -1
  114. package/dist/migration/version.d.ts.map +1 -1
  115. package/dist/migration/version.js +2 -0
  116. package/dist/migration/version.js.map +1 -1
  117. package/dist/model/LLMModelDefinition.d.ts.map +1 -1
  118. package/dist/model/LLMModelDefinition.js +2 -0
  119. package/dist/model/LLMModelDefinition.js.map +1 -1
  120. package/dist/model/Model.d.ts.map +1 -1
  121. package/dist/model/Model.js +2 -13
  122. package/dist/model/Model.js.map +1 -1
  123. package/dist/model/ModelDefinition.d.ts.map +1 -1
  124. package/dist/model/ModelDefinition.js +2 -0
  125. package/dist/model/ModelDefinition.js.map +1 -1
  126. package/dist/model/ModelRegistry.d.ts.map +1 -1
  127. package/dist/model/ModelRegistry.js +2 -0
  128. package/dist/model/ModelRegistry.js.map +1 -1
  129. package/dist/model/index.d.ts.map +1 -1
  130. package/dist/model/index.js +2 -0
  131. package/dist/model/index.js.map +1 -1
  132. package/dist/model/serialization.d.ts.map +1 -1
  133. package/dist/model/serialization.js +2 -0
  134. package/dist/model/serialization.js.map +1 -1
  135. package/dist/node/AgentNode.d.ts.map +1 -1
  136. package/dist/node/AgentNode.js +2 -0
  137. package/dist/node/AgentNode.js.map +1 -1
  138. package/dist/node/DataNode.d.ts.map +1 -1
  139. package/dist/node/DataNode.js +2 -0
  140. package/dist/node/DataNode.js.map +1 -1
  141. package/dist/node/FunctionNode.d.ts.map +1 -1
  142. package/dist/node/FunctionNode.js +2 -0
  143. package/dist/node/FunctionNode.js.map +1 -1
  144. package/dist/node/InputNode.d.ts.map +1 -1
  145. package/dist/node/InputNode.js +2 -0
  146. package/dist/node/InputNode.js.map +1 -1
  147. package/dist/node/LogicNode.d.ts.map +1 -1
  148. package/dist/node/LogicNode.js +2 -0
  149. package/dist/node/LogicNode.js.map +1 -1
  150. package/dist/node/MqttNode.d.ts.map +1 -1
  151. package/dist/node/MqttNode.js +2 -0
  152. package/dist/node/MqttNode.js.map +1 -1
  153. package/dist/node/Node.d.ts.map +1 -1
  154. package/dist/node/Node.js +2 -7
  155. package/dist/node/Node.js.map +1 -1
  156. package/dist/node/NodeDefinition.d.ts.map +1 -1
  157. package/dist/node/NodeDefinition.js +2 -0
  158. package/dist/node/NodeDefinition.js.map +1 -1
  159. package/dist/node/NodeRegistry.d.ts.map +1 -1
  160. package/dist/node/NodeRegistry.js +2 -0
  161. package/dist/node/NodeRegistry.js.map +1 -1
  162. package/dist/node/OutputNode.d.ts.map +1 -1
  163. package/dist/node/OutputNode.js +2 -0
  164. package/dist/node/OutputNode.js.map +1 -1
  165. package/dist/node/ToolNode.d.ts.map +1 -1
  166. package/dist/node/ToolNode.js +2 -0
  167. package/dist/node/ToolNode.js.map +1 -1
  168. package/dist/node/TriggerNode.d.ts.map +1 -1
  169. package/dist/node/TriggerNode.js +2 -0
  170. package/dist/node/TriggerNode.js.map +1 -1
  171. package/dist/node/constants.d.ts.map +1 -1
  172. package/dist/node/constants.js +2 -0
  173. package/dist/node/constants.js.map +1 -1
  174. package/dist/node/index.d.ts.map +1 -1
  175. package/dist/node/index.js +2 -0
  176. package/dist/node/index.js.map +1 -1
  177. package/dist/node/methods.d.ts.map +1 -1
  178. package/dist/node/methods.js +2 -0
  179. package/dist/node/methods.js.map +1 -1
  180. package/dist/node/serialization.d.ts.map +1 -1
  181. package/dist/node/serialization.js +33 -13
  182. package/dist/node/serialization.js.map +1 -1
  183. package/dist/parameter/OutputParameter.d.ts.map +1 -1
  184. package/dist/parameter/OutputParameter.js +2 -0
  185. package/dist/parameter/OutputParameter.js.map +1 -1
  186. package/dist/parameter/Parameter.d.ts.map +1 -1
  187. package/dist/parameter/Parameter.js +2 -0
  188. package/dist/parameter/Parameter.js.map +1 -1
  189. package/dist/parameter/index.d.ts.map +1 -1
  190. package/dist/parameter/index.js +2 -0
  191. package/dist/parameter/index.js.map +1 -1
  192. package/dist/variable/Variable.d.ts.map +1 -1
  193. package/dist/variable/Variable.js +2 -0
  194. package/dist/variable/Variable.js.map +1 -1
  195. package/dist/variable/index.d.ts.map +1 -1
  196. package/dist/variable/index.js +2 -3
  197. package/dist/variable/index.js.map +1 -1
  198. package/dist/variable/operations.d.ts.map +1 -1
  199. package/dist/variable/operations.js +2 -0
  200. package/dist/variable/operations.js.map +1 -1
  201. package/dist/workflow/Workflow.d.ts.map +1 -1
  202. package/dist/workflow/Workflow.js +2 -0
  203. package/dist/workflow/Workflow.js.map +1 -1
  204. package/dist/workflow/index.d.ts.map +1 -1
  205. package/dist/workflow/index.js +2 -0
  206. package/dist/workflow/index.js.map +1 -1
  207. package/dist/workflow/serialization.d.ts.map +1 -1
  208. package/dist/workflow/serialization.js +2 -4
  209. package/dist/workflow/serialization.js.map +1 -1
  210. package/package.json +1 -1
  211. package/src/api/deployment.ts +26 -19
  212. package/src/api/engine.ts +26 -26
  213. package/src/api/index.ts +3 -0
  214. package/src/api/workflow.ts +10 -10
  215. package/src/channel/Channel.ts +3 -0
  216. package/src/channel/ChannelDefinition.ts +3 -0
  217. package/src/channel/index.ts +3 -0
  218. package/src/channel/serialization.ts +3 -0
  219. package/src/deploy/index.ts +4 -1
  220. package/src/deploy/inputs.ts +3 -0
  221. package/src/deploy/requirements.test.ts +3 -0
  222. package/src/deploy/requirements.ts +3 -0
  223. package/src/deploy/spec.test.ts +7 -6
  224. package/src/deploy/spec.ts +46 -28
  225. package/src/diagnostics/__fixtures__/diagnosticFixtures.ts +3 -0
  226. package/src/diagnostics/diagnostics.test.ts +3 -0
  227. package/src/diagnostics/diagnostics.ts +3 -0
  228. package/src/diagnostics/index.ts +3 -0
  229. package/src/edge/Edge.ts +3 -0
  230. package/src/edge/EdgeDefinition.ts +3 -0
  231. package/src/edge/EdgeType.ts +3 -0
  232. package/src/edge/index.ts +3 -0
  233. package/src/edge/serialization.ts +3 -0
  234. package/src/expression/index.ts +3 -0
  235. package/src/expression/parser.ts +3 -0
  236. package/src/expression/types.ts +3 -0
  237. package/src/function/FunctionDeclaration.ts +3 -0
  238. package/src/function/index.ts +3 -0
  239. package/src/function/serialization.ts +3 -0
  240. package/src/globals.d.ts +3 -0
  241. package/src/id/index.ts +3 -0
  242. package/src/index.ts +3 -0
  243. package/src/memory/Memory.ts +3 -0
  244. package/src/memory/MemoryDefinition.ts +3 -0
  245. package/src/memory/MemoryFileDefinition.ts +3 -0
  246. package/src/memory/MemoryRegistry.ts +3 -0
  247. package/src/memory/VectorDatabaseDefinition.ts +3 -0
  248. package/src/memory/index.ts +3 -0
  249. package/src/memory/serialization.ts +3 -0
  250. package/src/migration/index.ts +3 -0
  251. package/src/migration/migrate.test.ts +3 -0
  252. package/src/migration/migrate.ts +3 -0
  253. package/src/migration/migrations.ts +3 -0
  254. package/src/migration/version.ts +3 -0
  255. package/src/model/LLMModelDefinition.ts +3 -0
  256. package/src/model/Model.ts +3 -0
  257. package/src/model/ModelDefinition.ts +3 -0
  258. package/src/model/ModelRegistry.ts +3 -0
  259. package/src/model/index.ts +3 -0
  260. package/src/model/serialization.ts +3 -0
  261. package/src/node/AgentNode.ts +3 -0
  262. package/src/node/DataNode.ts +3 -0
  263. package/src/node/FunctionNode.ts +3 -0
  264. package/src/node/InputNode.ts +3 -0
  265. package/src/node/LogicNode.ts +3 -0
  266. package/src/node/MqttNode.ts +3 -0
  267. package/src/node/Node.ts +3 -0
  268. package/src/node/NodeDefinition.ts +3 -0
  269. package/src/node/NodeRegistry.ts +3 -0
  270. package/src/node/OutputNode.ts +3 -0
  271. package/src/node/ToolNode.ts +3 -0
  272. package/src/node/TriggerNode.ts +3 -0
  273. package/src/node/constants.ts +3 -0
  274. package/src/node/index.ts +3 -0
  275. package/src/node/methods.ts +3 -0
  276. package/src/node/serialization.test.ts +51 -0
  277. package/src/node/serialization.ts +34 -13
  278. package/src/parameter/OutputParameter.ts +3 -0
  279. package/src/parameter/Parameter.ts +3 -0
  280. package/src/parameter/index.ts +3 -0
  281. package/src/variable/Variable.ts +3 -0
  282. package/src/variable/index.ts +3 -0
  283. package/src/variable/operations.ts +3 -0
  284. package/src/workflow/Workflow.ts +3 -0
  285. package/src/workflow/index.ts +3 -0
  286. package/src/workflow/serialization.test.ts +3 -0
  287. package/src/workflow/serialization.ts +3 -0
package/src/api/engine.ts CHANGED
@@ -7,31 +7,35 @@ export type paths = Record<string, never>;
7
7
  export type webhooks = Record<string, never>;
8
8
  export interface components {
9
9
  schemas: {
10
- /** @description The engine's complete boot input, loaded once at startup from a single file — the engine is immutable, with no runtime hot-swap. Bundles the binding-free workflow, the deploy mapping that binds its logical resource ids to this environment, the resolved external-resource configs those bindings point at, and the device manifest (the hardware catalog the mapping resolves against). The deployment-scoped parts and the device-scoped manifest have different owners in the control plane; the renderer merges them into this one blob for the engine. A workflow is required — an engine exists only to run one. The engine still validates it at boot and fails fast if it is missing, since JSON unmarshalling does not enforce required fields. */
10
+ /** @description The engine's complete boot input, loaded once at startup. */
11
11
  EngineConfig: {
12
12
  workflow: components["schemas"]["Workflow"];
13
- mapping?: components["schemas"]["DeploymentMapping"];
13
+ mapping?: components["schemas"]["ResourceMapping"];
14
14
  externalResources?: components["schemas"]["ExternalResources"];
15
15
  manifest?: components["schemas"]["DeviceManifest"];
16
16
  };
17
- /** @description Binds a binding-free workflow's logical resource ids to concrete platform resources for one deploy, keyed by workflow resource id. The pool a binding's ref resolves against is determined by the workflow resource's type: hardware channels resolve against the boot DeviceManifest; MQTT channels and custom models against the deploy ExternalResources; RAG memory against the boot-configured backend (the ref is the collection id). */
18
- DeploymentMapping: {
17
+ /** @description The engine's secret store: a flat map of secret id -> opaque secret value. */
18
+ EngineSecrets: {
19
+ [key: string]: string;
20
+ };
21
+ /** @description Binds a binding-free workflow's logical resource ids to concrete platform resources, keyed by workflow resource id. */
22
+ ResourceMapping: {
19
23
  [key: string]: components["schemas"]["ResourceBinding"];
20
24
  };
21
- /** @description How one workflow resource binds to the environment. `ref` is the shared platform resource it points at (driver instance id, external resource id, or RAG collection id) — a sharing identity, so many workflow resources may share one ref and the engine builds it once. `index` is the optional per-channel physical sub-address within that resource: GPIO line, or ADC/PWM/DAC channel number. Absent for UART, MQTT, memory and models. */
25
+ /** @description How one workflow resource binds to the environment: a shared platform resource `ref` with an optional per-channel physical sub-address `index`. */
22
26
  ResourceBinding: {
23
27
  /** @description Shared platform resource id this binds to. */
24
28
  ref: string;
25
29
  /** @description Per-channel physical sub-address within the driver (GPIO line / ADC-PWM-DAC channel). Omitted when the resource has no sub-address. */
26
30
  index?: number;
27
31
  };
28
- /** @description Deploy-time configs for a workflow's non-device external resources (MQTT transports, custom-model providers, ...), keyed by the platform resource id the DeploymentMapping points at. Replaces the former NetworkManifest. Device-owned configs (drivers) stay in the boot DeviceManifest; this carries only deploy-delivered, swappable configs. */
32
+ /** @description Deploy-time configs for a workflow's non-device external resources (MQTT transports, custom-model providers, ...), keyed by platform resource id. */
29
33
  ExternalResources: {
30
34
  [key: string]: components["schemas"]["ExternalResourceConfig"];
31
35
  };
32
36
  /** @description Tagged union of deploy-time external-resource configs, discriminated by runtime kind (not by ownership — locality like on-device vs cloud lives inside an arm). New kinds extend this oneOf. */
33
37
  ExternalResourceConfig: components["schemas"]["MQTTConnection"] | components["schemas"]["LLMProviderConfig"];
34
- /** @description Resolved connection to a self-hosted/custom LLM endpoint the llmproxy doesn't ship. The engine registers it as an llmproxy provider for the workflow's custom model; the model's capabilities come from its declared workflow entry, so they are not repeated here. The bearer credential is NOT here — it is a secret, delivered out-of-band and injected at runtime (keyed by this resource's id), never stored in the deployment spec. */
38
+ /** @description Resolved connection to a self-hosted/custom LLM endpoint the llmproxy doesn't ship. */
35
39
  LLMProviderConfig: {
36
40
  /**
37
41
  * @description discriminator enum property added by openapi-typescript
@@ -43,7 +47,7 @@ export interface components {
43
47
  /** @description Upstream model name the endpoint serves; defaults to the workflow model id when empty. */
44
48
  model?: string;
45
49
  };
46
- /** @description Resolved connection metadata for an MQTT broker. The password is NOT here — it is a secret, delivered out-of-band and injected at runtime (keyed by this resource's id), never stored in the deployment spec. username is connection metadata (an identifier), not a credential, so it stays. */
50
+ /** @description Resolved connection metadata for an MQTT broker. */
47
51
  MQTTConnection: {
48
52
  /**
49
53
  * @description discriminator enum property added by openapi-typescript
@@ -53,20 +57,20 @@ export interface components {
53
57
  brokerUrl: string;
54
58
  clientId?: string;
55
59
  username?: string;
56
- /** @description Topic prefix the engine prepends to workflow-level publish topics ({networkId}/{agentId}/). */
60
+ /** @description Topic prefix for workflow-level publish topics ({networkId}/{agentId}/). */
57
61
  publishPrefix?: string;
58
- /** @description Topic prefix the engine prepends to workflow-level subscribe filters ({networkId}/+/). */
62
+ /** @description Topic prefix for workflow-level subscribe filters ({networkId}/+/). */
59
63
  subscribePrefix?: string;
60
64
  will?: components["schemas"]["MQTTWill"];
61
65
  };
62
66
  MQTTWill: {
63
67
  topic: string;
64
68
  payload: string;
65
- /** @description MQTT QoS (0,1,2). Cast to byte at the paho call site. */
69
+ /** @description MQTT QoS (0, 1, or 2). */
66
70
  qos: number;
67
71
  retain: boolean;
68
72
  };
69
- /** @description Hardware resources available on the device, keyed by driver instance ID. Drives runtime driver instantiation on the engine. */
73
+ /** @description Hardware resources available on the device, keyed by driver instance ID. */
70
74
  DeviceManifest: {
71
75
  gpios?: {
72
76
  [key: string]: components["schemas"]["GPIOConfig"];
@@ -106,10 +110,6 @@ export interface components {
106
110
  /** @description sysfs path to the pwmchip directory, e.g. "/sys/class/pwm/pwmchip0" */
107
111
  chip: string;
108
112
  };
109
- /** @description Body of PUT /agents/memory/{name}. */
110
- MemoryFileWrite: {
111
- content: string;
112
- };
113
113
  RagQueryRequest: {
114
114
  /** @description Collection to query */
115
115
  collectionId: string;
@@ -162,7 +162,7 @@ export interface components {
162
162
  pinReference?: string;
163
163
  signalType: components["schemas"]["SignalType"];
164
164
  output: components["schemas"]["OutputBinding"];
165
- /** @description Description exposed to the LLM when this node is wired as a tool. Ignored in exec mode. */
165
+ /** @description Description used when this node is exposed as a tool; ignored in exec mode. */
166
166
  toolDescription?: string;
167
167
  };
168
168
  };
@@ -232,7 +232,7 @@ export interface components {
232
232
  outputDeclarations: components["schemas"]["OutputDeclaration"][];
233
233
  /** @description Memory files this agent can access, each with an access mode. */
234
234
  memoryRefs: components["schemas"]["MemoryRef"][];
235
- /** @description Description exposed to the LLM when this node is wired as a tool. Ignored in exec mode. */
235
+ /** @description Description used when this node is exposed as a tool; ignored in exec mode. */
236
236
  toolDescription?: string;
237
237
  };
238
238
  };
@@ -297,7 +297,7 @@ export interface components {
297
297
  /** @description Search query expression */
298
298
  query: components["schemas"]["Expression"];
299
299
  output: components["schemas"]["OutputBinding"];
300
- /** @description Description exposed to the LLM when this node is wired as a tool. Ignored in exec mode. */
300
+ /** @description Description used when this node is exposed as a tool; ignored in exec mode. */
301
301
  toolDescription?: string;
302
302
  };
303
303
  };
@@ -336,7 +336,7 @@ export interface components {
336
336
  outputBindings?: {
337
337
  [key: string]: components["schemas"]["OutputBinding"];
338
338
  };
339
- /** @description Description exposed to the LLM when this function is wired as a tool. Ignored in exec mode. */
339
+ /** @description Description used when this function is exposed as a tool; ignored in exec mode. */
340
340
  toolDescription?: string;
341
341
  };
342
342
  };
@@ -650,7 +650,7 @@ export interface components {
650
650
  type: "MQTT";
651
651
  id: string;
652
652
  label: string;
653
- /** @description Topic this channel publishes to / subscribes on. The engine wraps it with the bound broker's prefix at runtime. */
653
+ /** @description Topic this channel publishes to / subscribes on. */
654
654
  topic: string;
655
655
  };
656
656
  LOGChannel: {
@@ -662,11 +662,11 @@ export interface components {
662
662
  id: string;
663
663
  label: string;
664
664
  /**
665
- * @description Severity the engine records messages written to this channel at.
665
+ * @description Severity for messages written to this channel.
666
666
  * @enum {string}
667
667
  */
668
668
  level: "debug" | "info" | "warn" | "error";
669
- /** @description Optional category stamped on each line so the backend can group workflow-emitted logs apart from engine diagnostics. */
669
+ /** @description Optional category label stamped on each line written to this channel. */
670
670
  tag?: string;
671
671
  };
672
672
  Channel: components["schemas"]["GPIOINChannel"] | components["schemas"]["GPIOOUTChannel"] | components["schemas"]["ADCChannel"] | components["schemas"]["PWMChannel"] | components["schemas"]["DACChannel"] | components["schemas"]["UARTChannel"] | components["schemas"]["MQTTChannel"] | components["schemas"]["LOGChannel"];
@@ -679,7 +679,7 @@ export interface components {
679
679
  type: "MemoryFile";
680
680
  /** @description Stable identifier that survives renames; referenced from MemoryRef. */
681
681
  id: string;
682
- /** @description Display name. Unique per agent (LLM tool enums use it). */
682
+ /** @description Display name. Unique per agent. */
683
683
  label: string;
684
684
  description: string;
685
685
  content: string;
@@ -713,11 +713,11 @@ export interface components {
713
713
  id: string;
714
714
  /** @description Display name. */
715
715
  label: string;
716
- /** @description Capabilities this model supports (used to filter model pickers). */
716
+ /** @description Capabilities this model supports. */
717
717
  capabilities: components["schemas"]["ModelCapability"][];
718
718
  };
719
719
  Model: components["schemas"]["LLMModel"];
720
- /** @description Workflow represents the deployment format of a project, passed to agents. */
720
+ /** @description The deployment format of a workflow project. */
721
721
  Workflow: {
722
722
  /**
723
723
  * Format: int32
package/src/api/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  export type { components } from "./workflow";
2
5
 
3
6
  import type { components } from "./workflow";
@@ -17,7 +17,7 @@ export interface components {
17
17
  x: number;
18
18
  y: number;
19
19
  };
20
- /** @description Workflow represents the deployment format of a project, passed to agents. */
20
+ /** @description The deployment format of a workflow project. */
21
21
  Workflow: {
22
22
  /**
23
23
  * Format: int32
@@ -115,7 +115,7 @@ export interface components {
115
115
  type: "MemoryFile";
116
116
  /** @description Stable identifier that survives renames; referenced from MemoryRef. */
117
117
  id: string;
118
- /** @description Display name. Unique per agent (LLM tool enums use it). */
118
+ /** @description Display name. Unique per agent. */
119
119
  label: string;
120
120
  description: string;
121
121
  content: string;
@@ -157,7 +157,7 @@ export interface components {
157
157
  id: string;
158
158
  /** @description Display name. */
159
159
  label: string;
160
- /** @description Capabilities this model supports (used to filter model pickers). */
160
+ /** @description Capabilities this model supports. */
161
161
  capabilities: components["schemas"]["ModelCapability"][];
162
162
  };
163
163
  Node: components["schemas"]["ReadPinNode"] | components["schemas"]["WritePinNode"] | components["schemas"]["AgentNode"] | components["schemas"]["IfNode"] | components["schemas"]["SerialReadNode"] | components["schemas"]["SerialWriteNode"] | components["schemas"]["RetrieverNode"] | components["schemas"]["WebFetchNode"] | components["schemas"]["FunctionCallNode"] | components["schemas"]["OnFunctionCallNode"] | components["schemas"]["DelayNode"] | components["schemas"]["TickerNode"] | components["schemas"]["AlarmNode"] | components["schemas"]["WebSearchToolNode"] | components["schemas"]["OnStartupNode"] | components["schemas"]["OnPinEdgeNode"] | components["schemas"]["OnSerialReceiveNode"] | components["schemas"]["OnThresholdNode"] | components["schemas"]["SetVariableNode"] | components["schemas"]["MqttPublishNode"] | components["schemas"]["OnMqttMessageNode"];
@@ -242,7 +242,7 @@ export interface components {
242
242
  outputBindings?: {
243
243
  [key: string]: components["schemas"]["OutputBinding"];
244
244
  };
245
- /** @description Description exposed to the LLM when this function is wired as a tool. Ignored in exec mode. */
245
+ /** @description Description used when this function is exposed as a tool; ignored in exec mode. */
246
246
  toolDescription?: string;
247
247
  };
248
248
  };
@@ -342,7 +342,7 @@ export interface components {
342
342
  /** @description Search query expression */
343
343
  query: components["schemas"]["Expression"];
344
344
  output: components["schemas"]["OutputBinding"];
345
- /** @description Description exposed to the LLM when this node is wired as a tool. Ignored in exec mode. */
345
+ /** @description Description used when this node is exposed as a tool; ignored in exec mode. */
346
346
  toolDescription?: string;
347
347
  };
348
348
  };
@@ -377,7 +377,7 @@ export interface components {
377
377
  pinReference?: string;
378
378
  signalType: components["schemas"]["SignalType"];
379
379
  output: components["schemas"]["OutputBinding"];
380
- /** @description Description exposed to the LLM when this node is wired as a tool. Ignored in exec mode. */
380
+ /** @description Description used when this node is exposed as a tool; ignored in exec mode. */
381
381
  toolDescription?: string;
382
382
  };
383
383
  };
@@ -418,7 +418,7 @@ export interface components {
418
418
  outputDeclarations: components["schemas"]["OutputDeclaration"][];
419
419
  /** @description Memory files this agent can access, each with an access mode. */
420
420
  memoryRefs: components["schemas"]["MemoryRef"][];
421
- /** @description Description exposed to the LLM when this node is wired as a tool. Ignored in exec mode. */
421
+ /** @description Description used when this node is exposed as a tool; ignored in exec mode. */
422
422
  toolDescription?: string;
423
423
  };
424
424
  };
@@ -591,7 +591,7 @@ export interface components {
591
591
  type: "MQTT";
592
592
  id: string;
593
593
  label: string;
594
- /** @description Topic this channel publishes to / subscribes on. The engine wraps it with the bound broker's prefix at runtime. */
594
+ /** @description Topic this channel publishes to / subscribes on. */
595
595
  topic: string;
596
596
  };
597
597
  LOGChannel: {
@@ -603,11 +603,11 @@ export interface components {
603
603
  id: string;
604
604
  label: string;
605
605
  /**
606
- * @description Severity the engine records messages written to this channel at.
606
+ * @description Severity for messages written to this channel.
607
607
  * @enum {string}
608
608
  */
609
609
  level: "debug" | "info" | "warn" | "error";
610
- /** @description Optional category stamped on each line so the backend can group workflow-emitted logs apart from engine diagnostics. */
610
+ /** @description Optional category label stamped on each line written to this channel. */
611
611
  tag?: string;
612
612
  };
613
613
  /** @enum {string} */
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  export type ChannelType = "GPIOIN" | "GPIOOUT" | "ADC" | "PWM" | "DAC" | "UART" | "MQTT" | "LOG";
2
5
 
3
6
  export const ALL_CHANNEL_TYPES: ChannelType[] = ["GPIOIN", "GPIOOUT", "ADC", "PWM", "DAC", "UART", "MQTT", "LOG"];
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import type { Parameter } from "../parameter";
2
5
  import { ALL_CHANNEL_TYPES } from "./Channel";
3
6
 
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  export type { ChannelType, Channel } from "./Channel";
2
5
  export { ALL_CHANNEL_TYPES } from "./Channel";
3
6
  export type { ChannelDefinition } from "./ChannelDefinition";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import type { Schemas } from "../api";
2
5
  import { pruneArguments } from "../parameter";
3
6
  import { CHANNEL_DEFINITION } from "./ChannelDefinition";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  export { getReferencedCatalogModelIds, deriveRequirements } from "./requirements";
2
5
  export type { DeployRequirements, HardwareChannel, MqttChannel, CustomModel, HardwareFamily } from "./requirements";
3
6
  export type { DeploymentInputs, HardwareBinding, MqttBinding, ModelBinding } from "./inputs";
@@ -11,4 +14,4 @@ export {
11
14
  ggufNameError,
12
15
  sidecarServiceName,
13
16
  } from "./spec";
14
- export type { DeploymentSpecMeta, DeploymentSpecResult, ResourceSecret, ResourceSecrets } from "./spec";
17
+ export type { DeploymentSpecMeta, DeploymentSpecResult, EngineSecrets } from "./spec";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  // Operator-supplied bindings: the per-deploy values that bind a workflow's
2
5
  // logical resource ids to a concrete environment. Shared by every spec
3
6
  // producer (the CLI prompts/--values, the FE deploy forms) — how the values are
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import { describe, it, expect } from "vitest";
2
5
  import { getReferencedCatalogModelIds } from "./requirements";
3
6
  import { MAIN_CANVAS_ID, type Workflow, type Canvas } from "../workflow";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import type { Workflow } from "../workflow";
2
5
  import { NodeRegistry, isNodeUsedAsTool } from "../node";
3
6
  import { isParameterActive } from "../parameter";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import { describe, it, expect } from "vitest";
2
5
  import { buildDeploymentSpec, assertDeployable, sidecarServiceName } from "./spec";
3
6
  import { deriveRequirements } from "./requirements";
@@ -57,7 +60,6 @@ const fullInputs: DeploymentInputs = {
57
60
 
58
61
  const meta = {
59
62
  id: "dep-1",
60
- status: "active" as const,
61
63
  engineImage: "fh-engine:0.4.2",
62
64
  llamaServerImage: "ghcr.io/ggml-org/llama.cpp:server-b8589",
63
65
  };
@@ -68,7 +70,6 @@ describe("buildDeploymentSpec", () => {
68
70
 
69
71
  expect(spec.schemaVersion).toBe(1);
70
72
  expect(spec.id).toBe("dep-1");
71
- expect(spec.status).toBe("active");
72
73
 
73
74
  const engine = engineOf(spec);
74
75
  expect(engine.image).toBe("fh-engine:0.4.2");
@@ -102,8 +103,8 @@ describe("buildDeploymentSpec", () => {
102
103
  expect(llamaOf(spec, "local-llm")).toMatchObject({
103
104
  name: sidecarServiceName("local-llm"),
104
105
  image: "ghcr.io/ggml-org/llama.cpp:server-b8589",
105
- command: ["--model", "/models/model.gguf", "--host", "0.0.0.0", "--port", "8080", "--ctx-size", "4096"],
106
- volumes: ["./models:/models:ro"],
106
+ command: ["--model", "/var/lib/foresthub/workspace/model.gguf", "--host", "0.0.0.0", "--port", "8080", "--ctx-size", "4096"],
107
+ volumes: ["./workspaces/llama-local-llm:/var/lib/foresthub/workspace:ro"],
107
108
  });
108
109
  // The external-resource provider URL must point at the sidecar service name.
109
110
  const ext = engineConfigOf(spec).externalResources!;
@@ -145,7 +146,7 @@ describe("buildDeploymentSpec", () => {
145
146
  const ext = engineConfigOf(spec).externalResources!;
146
147
  const [ref, provider] = Object.entries(ext).find(([, r]) => r.type === "selfhosted")!;
147
148
  expect(provider).toEqual({ type: "selfhosted", url: "https://infer.example/v1" });
148
- expect(resourceSecrets[ref]).toEqual({ apiKey: "k" });
149
+ expect(resourceSecrets[ref]).toEqual("k");
149
150
  });
150
151
 
151
152
  it("pulls MQTT/endpoint secrets out of the spec, keyed by resource ref", () => {
@@ -155,7 +156,7 @@ describe("buildDeploymentSpec", () => {
155
156
  const [mqttRef, mqttConn] = Object.entries(ext).find(([, r]) => r.type === "mqtt")!;
156
157
  expect(mqttConn).not.toHaveProperty("password");
157
158
  expect(mqttConn).toMatchObject({ username: "u" });
158
- expect(resourceSecrets[mqttRef]).toEqual({ password: "p" });
159
+ expect(resourceSecrets[mqttRef]).toEqual("p");
159
160
  // Whole spec serialized carries no secret value.
160
161
  expect(JSON.stringify(spec)).not.toContain('"p"');
161
162
  });
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  // The spec resolver: (workflow + bindings) -> DeploymentSpec. The shared
2
5
  // "packaging library" the migration doc calls for — component-set derivation
3
6
  // and device-grant resolution computed once here, frozen into the contract spec,
@@ -19,9 +22,22 @@ type DeployComponent = DeploymentSchemas["DeployComponent"];
19
22
  // the spec carries it as an opaque blob in DeployComponent.config.
20
23
  type EngineConfig = EngineSchemas["EngineConfig"];
21
24
 
22
- // Mount path for the engine's memory volume matches the image's ENGINE_MEMORY_DIR
23
- // default, so the renderer mounts here without wiring an env var.
24
- const ENGINE_MEMORY_PATH = "/var/lib/foresthub/memory";
25
+ // In-container mount path for a component's durable workspace. MUST equal the Go
26
+ // component.Workspace contract constant (go/component/paths.go) the component
27
+ // reads/writes there, so we mount the host workspace dir there and wire no env var.
28
+ // A cross-repo contract: changing it requires changing the Go constant in lockstep.
29
+ const COMPONENT_WORKSPACE_PATH = "/var/lib/foresthub/workspace";
30
+
31
+ // The OSS renderer's state root (docs/device-filesystem.md §2). Bind-mount sources
32
+ // hang off it as `<root>/workspaces/<container>/`. "." makes Docker resolve them
33
+ // relative to the compose file, so the bundle stays drop-anywhere; Ranger is a
34
+ // separate renderer that sets root to the absolute `/var/lib/foresthub`.
35
+ const STATE_ROOT = ".";
36
+
37
+ // Host bind-mount source for a container's workspace under the state root.
38
+ function workspaceDir(container: string): string {
39
+ return `${STATE_ROOT}/workspaces/${container}`;
40
+ }
25
41
 
26
42
  // Deploy-time metadata the resolver cannot derive from the workflow: identity,
27
43
  // lifecycle, and the full image reference each component runs (frozen here so the
@@ -29,26 +45,24 @@ const ENGINE_MEMORY_PATH = "/var/lib/foresthub/memory";
29
45
  // used only when the workflow has an on-device model.
30
46
  export interface DeploymentSpecMeta {
31
47
  id: string;
32
- status: DeploymentSpec["status"];
33
48
  createdAt?: string;
34
49
  engineImage: string;
35
50
  llamaServerImage: string;
36
51
  }
37
52
 
38
- // One external resource's credentials. Secrets are NEVER part of the spec (not
39
- // rotation-safe, breach-exposed if stored): the resolver returns them separately,
40
- // keyed by the same resource ref the spec's externalResources use, for the
41
- // renderer to deliver out-of-band (engine env FH_RESOURCE_SECRETS).
42
- export interface ResourceSecret {
43
- password?: string; // MQTT broker password
44
- apiKey?: string; // self-hosted LLM endpoint bearer
45
- }
46
- export type ResourceSecrets = Record<string, ResourceSecret>;
53
+ // The engine's secret store: a flat map of secret id -> opaque secret value,
54
+ // keyed by the same resource ref the spec's externalResources use. Each value is
55
+ // the single credential that resource needs (MQTT password, self-hosted-LLM
56
+ // bearer token). Secrets are NEVER part of the spec (not rotation-safe, breach-
57
+ // exposed if stored): the resolver returns them separately, for the renderer to
58
+ // deliver out-of-band as a mounted secret document (secrets.json). Mirrors the
59
+ // wire EngineSecrets.
60
+ export type EngineSecrets = Record<string, string>;
47
61
 
48
62
  // buildDeploymentSpec's output: the secret-free spec plus the pulled-out secrets.
49
63
  export interface DeploymentSpecResult {
50
64
  spec: DeploymentSpec;
51
- resourceSecrets: ResourceSecrets;
65
+ resourceSecrets: EngineSecrets;
52
66
  }
53
67
 
54
68
  // Hands out stable, collision-free ref names. Same dedup key -> same ref (the
@@ -104,7 +118,7 @@ export function ggufNameError(name: string | undefined): string | null {
104
118
  const t = (name ?? "").trim();
105
119
  if (!t) return "a model filename is required";
106
120
  if (!t.toLowerCase().endsWith(".gguf")) return "must be a .gguf file (llama-server only loads GGUF)";
107
- if (t.includes("/")) return "just the filename, not a path — the file goes in ./models/";
121
+ if (t.includes("/")) return "just the filename, not a path — the file goes in the model's workspace dir (./workspaces/llama-…/)";
108
122
  return null;
109
123
  }
110
124
 
@@ -223,7 +237,7 @@ export function buildDeploymentSpec(
223
237
  assertDeployable(req, inputs);
224
238
 
225
239
  const refs = new RefAllocator();
226
- const resourceSecrets: ResourceSecrets = {};
240
+ const resourceSecrets: EngineSecrets = {};
227
241
 
228
242
  // DeviceManifest is split per family; accumulate each separately, attach only
229
243
  // the non-empty ones (all slots optional).
@@ -234,7 +248,7 @@ export function buildDeploymentSpec(
234
248
  const serials: Record<string, EngineSchemas["SerialConfig"]> = {};
235
249
 
236
250
  const externalResources: EngineSchemas["ExternalResources"] = {};
237
- const mapping: EngineSchemas["DeploymentMapping"] = {};
251
+ const mapping: EngineSchemas["ResourceMapping"] = {};
238
252
 
239
253
  // Container-level hardware access, resolved once: cdev nodes (GPIO, UART) map
240
254
  // one-to-one into the engine component's devices; sysfs families (ADC/DAC/PWM)
@@ -292,7 +306,7 @@ export function buildDeploymentSpec(
292
306
  const ref = refs.alloc(`mqtt:${JSON.stringify(conn)}:${b.password ?? ""}`, `mqtt-${brokerHost(b.brokerUrl)}`);
293
307
  externalResources[ref] = conn;
294
308
  mapping[ch.id] = { ref };
295
- if (b.password) resourceSecrets[ref] = { ...resourceSecrets[ref], password: b.password };
309
+ if (b.password) resourceSecrets[ref] = b.password;
296
310
  }
297
311
 
298
312
  // Custom models: one self-hosted provider per model id. A device model points
@@ -318,7 +332,7 @@ export function buildDeploymentSpec(
318
332
  image: meta.llamaServerImage,
319
333
  command: [
320
334
  "--model",
321
- `/models/${b.modelFile}`,
335
+ `${COMPONENT_WORKSPACE_PATH}/${b.modelFile}`,
322
336
  "--host",
323
337
  "0.0.0.0",
324
338
  "--port",
@@ -326,12 +340,14 @@ export function buildDeploymentSpec(
326
340
  "--ctx-size",
327
341
  String(b.ctxSize ?? 4096),
328
342
  ],
329
- volumes: ["./models:/models:ro"],
343
+ // The model lives in this sidecar's own workspace; read-only, so no chown
344
+ // is needed (docs §5). The operator drops the GGUF in workspaceDir(service).
345
+ volumes: [`${workspaceDir(service)}:${COMPONENT_WORKSPACE_PATH}:ro`],
330
346
  });
331
347
  } else {
332
348
  externalResources[ref] = { type: "selfhosted", url: b.url };
333
349
  // The endpoint bearer is a secret — out of the spec, returned separately.
334
- if (b.apiKey) resourceSecrets[ref] = { apiKey: b.apiKey };
350
+ if (b.apiKey) resourceSecrets[ref] = b.apiKey;
335
351
  }
336
352
  }
337
353
 
@@ -350,20 +366,23 @@ export function buildDeploymentSpec(
350
366
  if (Object.keys(externalResources).length) config.externalResources = externalResources;
351
367
  if (Object.keys(manifest).length) config.manifest = manifest;
352
368
 
353
- // The engine component. configPath is omitted (the image reads the convention
354
- // path it defaults to); the config blob is mounted there by the renderer. cdev
355
- // nodes pass through as devices; sysfs families force privileged; either case
356
- // needs root to open the root-owned nodes the nonroot image otherwise can't.
369
+ // The engine component. The config blob is mounted at the fixed convention path
370
+ // (component.ConfigFile) the engine image reads.
357
371
  const engine: DeployComponent = {
358
372
  name: "engine",
359
373
  image: meta.engineImage,
360
374
  pull: "never", // built locally before deploy, in no registry
361
375
  config,
362
- volumes: [`engine-memory:${ENGINE_MEMORY_PATH}`],
376
+ // Durable memory: a host bind mount under the state root, read-write, at the
377
+ // in-container workspace path (docs/device-filesystem.md §10).
378
+ volumes: [`${workspaceDir("engine")}:${COMPONENT_WORKSPACE_PATH}`],
379
+ // Run as root: writes the rw workspace bind mount without a host chown step
380
+ // (the OSS default, §5), and also opens root-owned device nodes when hardware
381
+ // (cdev passthrough / sysfs-privileged) is mapped in below.
382
+ user: "0:0",
363
383
  };
364
384
  if (cdev.size > 0) engine.devices = [...cdev];
365
385
  if (privileged) engine.privileged = true;
366
- if (cdev.size > 0 || privileged) engine.user = "0:0";
367
386
 
368
387
  const components = [engine, ...llamaComponents, ...customComponents];
369
388
  assertNoNameCollisions(components);
@@ -371,7 +390,6 @@ export function buildDeploymentSpec(
371
390
  const spec: DeploymentSpec = {
372
391
  schemaVersion: 1,
373
392
  id: meta.id,
374
- status: meta.status,
375
393
  components,
376
394
  };
377
395
  if (meta.createdAt) spec.createdAt = meta.createdAt;
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import type { Edge } from "../../edge";
2
5
  import type { NodeDefinition, NodeData } from "../../node";
3
6
  import type { DataType, Expression, Reference } from "../../api";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import { describe, it, expect } from "vitest";
2
5
  import { computeNodeDiagnostics, validateFunction, type Diagnostic } from "./diagnostics";
3
6
  import type { Expression } from "../api";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import type { NodeDefinition, NodeData } from "../node";
2
5
  import type { Expression } from "../api";
3
6
  import { NodeCategory, NodeRegistry } from "../node";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  export type { DiagnosticSeverity, DiagnosticCategory, Diagnostic, CanvasValidationResult, ValidationResult } from "./diagnostics";
2
5
  export {
3
6
  computeNodeDiagnostics,
package/src/edge/Edge.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import type { Expression } from "../api";
2
5
 
3
6
  export interface EdgeData extends Record<string, unknown> {
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import { Parameter } from "../parameter";
2
5
  import type { EdgeType } from "./EdgeType";
3
6
 
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  // Edge taxonomy: a plain control/tool edge, or one of the three agent-specific
2
5
  // control-flow refinements resolved at connection time from source/target context.
3
6
  // Node port handles themselves only carry `control` or `tool`. Tool edges have no
package/src/edge/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  // Public surface of the edge module. The base type (EdgeData) lives in
2
5
  // ./Edge; this file is a barrel only. Mirrors channel/memory/model.
3
6
 
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  import type { Schemas } from "../api";
2
5
  import type { Edge, EdgeData } from "./Edge";
3
6
  import type { EdgeType } from "./EdgeType";
@@ -1,3 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 ForestHub.
3
+
1
4
  export type { ParseResult } from "./parser";
2
5
  export { parseExpression } from "./parser";
3
6
  export type { ResolvedExpr } from "./types";