@oomol-lab/open-flow 0.1.0-beta.55 → 0.1.0-beta.57

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 (51) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/{createResourceDialog-4H5_lZ6s.js → createResourceDialog-C23SBjYd.js} +2 -2
  3. package/dist/browser/flow-authoring-edge.d.ts +3 -3
  4. package/dist/browser/flow-authoring-node.d.ts +30 -46
  5. package/dist/browser/flow-authoring.js +9215 -21030
  6. package/dist/browser/flow-change-schema.d.ts +267 -1
  7. package/dist/browser/flow-change.d.ts +19 -104
  8. package/dist/browser/flow-change.js +154 -298
  9. package/dist/browser/{flowChanges-ZzG-f9cg.js → flowChanges-fBiAeTzR.js} +2347 -2216
  10. package/dist/browser/{flowWorkspace-ChpRaTMX.js → flowWorkspace-MqcEfg7T.js} +118 -293
  11. package/dist/browser/{icons-CdAB9wyS.js → icons-6oQ8Q9Wm.js} +4309 -4454
  12. package/dist/browser/lib-DrKav3HG.js +11403 -0
  13. package/dist/browser/preview.css +1 -1
  14. package/dist/browser/preview.js +9609 -9548
  15. package/dist/browser/ui-idTooltip.d.ts +11 -0
  16. package/dist/browser/ui.d.ts +5 -0
  17. package/dist/browser/ui.js +83 -1
  18. package/dist/browser/workbench.css +1 -1
  19. package/dist/browser/workbench.js +227 -417
  20. package/dist/browser/{workbenchSelect-BRZN4ish.js → workbenchSelect-DpLbIr29.js} +1 -1
  21. package/dist/common/authoring.d.ts +207 -0
  22. package/dist/common/authoringSchema.d.ts +1293 -0
  23. package/dist/common/control-api-conformance.js +1060 -623
  24. package/dist/common/control-api-errors.d.ts +2 -1
  25. package/dist/common/control-api.d.ts +87 -6
  26. package/dist/common/control-api.js +1058 -705
  27. package/dist/common/control-requests.d.ts +128 -0
  28. package/dist/common/control-requests.js +2913 -1145
  29. package/dist/common/decision.js +6337 -6371
  30. package/dist/common/flow-agent.d.ts +1 -1
  31. package/dist/common/flow-connectionUsage.d.ts +1 -2
  32. package/dist/common/flow-encoding.d.ts +2 -4
  33. package/dist/common/flow-encoding.js +131 -182
  34. package/dist/common/flow-graph.d.ts +10 -10
  35. package/dist/common/flow-semantics.d.ts +13 -9
  36. package/dist/common/flow-semantics.js +9051 -9232
  37. package/dist/common/flowInspection.d.ts +3 -21
  38. package/dist/common/integration-trigger.js +8653 -8687
  39. package/dist/common/lib-DrKav3HG.js +11403 -0
  40. package/dist/common/mcp.d.ts +120 -12
  41. package/dist/common/mcp.js +537 -188
  42. package/dist/common/openapi.js +6337 -6371
  43. package/dist/common/poll-trigger.js +8653 -8687
  44. package/dist/common/run-events.js +0 -1
  45. package/dist/common/scheduler.d.ts +3 -9
  46. package/dist/common/scheduler.js +128 -209
  47. package/dist/common/webhook-trigger.js +6337 -6371
  48. package/package.json +7 -1
  49. package/skills/open-flow/SKILL.md +32 -0
  50. package/skills/open-flow/references/flow-authoring.md +356 -0
  51. package/skills/open-flow/references/flow-n8n-conversion.md +196 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oomol-lab/open-flow",
3
- "version": "0.1.0-beta.55",
3
+ "version": "0.1.0-beta.57",
4
4
  "description": "TypeScript contracts and Browser runtime for Open Flow.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -10,6 +10,10 @@
10
10
  },
11
11
  "types": "./dist/index.d.ts",
12
12
  "exports": {
13
+ "./package.json": "./package.json",
14
+ "./skills/open-flow/SKILL.md": "./skills/open-flow/SKILL.md",
15
+ "./skills/open-flow/references/flow-authoring.md": "./skills/open-flow/references/flow-authoring.md",
16
+ "./skills/open-flow/references/flow-n8n-conversion.md": "./skills/open-flow/references/flow-n8n-conversion.md",
13
17
  ".": {
14
18
  "types": "./dist/index.d.ts"
15
19
  },
@@ -136,12 +140,14 @@
136
140
  },
137
141
  "files": [
138
142
  "dist",
143
+ "skills",
139
144
  "README.md",
140
145
  "NOTICE",
141
146
  "LICENSE"
142
147
  ],
143
148
  "peerDependencies": {
144
149
  "effect": "4.0.0-rc.112",
150
+ "zod": "^4.6.5",
145
151
  "react": "^18.3.1 || ^19.0.0",
146
152
  "react-dom": "^18.3.1 || ^19.0.0"
147
153
  },
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: open-flow
3
+ description: Create, inspect, edit, check, run, and publish persistent Open Flow workflows through an existing Server, or assess and convert n8n workflows to Open Flow. Use for workflow operations, not changes to the Open Flow implementation.
4
+ ---
5
+
6
+ # Open Flow
7
+
8
+ Use `<!-- agentic:var flowCommand -->` to operate the selected deployment.
9
+ Consult the references relevant to the task:
10
+
11
+ - [Flow capabilities and examples](references/flow-authoring.md): inspect, edit,
12
+ connect, validate, run and publish; concurrency and Run diagnostics.
13
+ - [n8n conversion](references/flow-n8n-conversion.md): represent collections,
14
+ branches, provider operations and state while preserving source behavior.
15
+
16
+ <!-- agentic:if localDevelopment -->
17
+
18
+ Run commands from this repository's root. The development CLI connects to the
19
+ existing local Server by default, using `OPEN_FLOW_PORT` (default `3001`) and
20
+ `OPEN_FLOW_TOKEN`, or the token generated by `bun run dev`. It reads the root
21
+ `.env`. If the local Server has not been started, report that prerequisite.
22
+ Do not start or reconfigure a Server merely to perform a Flow operation.
23
+
24
+ To select another Server, the user must provide both `OPEN_FLOW_URL` and
25
+ `OPEN_FLOW_TOKEN` in the environment. Never read, print, or copy credential files
26
+ into prompts or command arguments. Connector and LLM configuration stay on the
27
+ Server; do not duplicate them in the CLI.
28
+ <!-- agentic:endif -->
29
+
30
+ Keep the requested boundary: editing a Draft does not authorize a Run,
31
+ publication, or enabling automatic execution. Report the resulting Flow and
32
+ Revision, verification result, and any unresolved configuration.
@@ -0,0 +1,356 @@
1
+ # Build and operate Open Flow workflows
2
+
3
+ `<!-- agentic:var flowCommand -->` operates persistent workflows on the selected
4
+ Server. Commands with `--json` return machine-readable results. Help and local
5
+ schemas work offline; Flow data and provider discovery require the Server.
6
+
7
+ ## The working model
8
+
9
+ A **Flow** has an editable **Draft**. Each saved change creates a **Revision**.
10
+ A **Run** executes a fixed version starting at a Trigger. A **Publication** makes
11
+ a Revision Live; enabling the Flow allows its automatic Triggers to execute.
12
+ Saving a Draft, running it, publishing it, and enabling it are separate actions.
13
+ Choose the actions covered by the user's request. A Run can have external effects.
14
+
15
+ Nodes describe business operations. **Execution edges** determine which nodes
16
+ run and in what order. **Input bindings** supply their data. A binding does not
17
+ create an execution edge. A Condition selects named execution branches; its
18
+ branch names are not output ports.
19
+
20
+ The authoring interface accepts business configuration, named inputs and source
21
+ text. It builds internal definitions, ports and Module identities for you.
22
+ `schema TYPE` supplies the current contract and a node example; `schema edit`
23
+ supplies the edit operations. Use these when a configuration is unfamiliar.
24
+
25
+ ## Find a Flow or a relevant part
26
+
27
+ ```bash
28
+ <!-- agentic:var flowCommand --> list --json
29
+ <!-- agentic:var flowCommand --> create "Order summary" --json
30
+ <!-- agentic:var flowCommand --> show FLOW_ID --json
31
+ <!-- agentic:var flowCommand --> read FLOW_ID --json
32
+ <!-- agentic:var flowCommand --> search FLOW_ID --input '{"query":"Customer summary","type":"agent"}' --json
33
+ <!-- agentic:var flowCommand --> read FLOW_ID --input '{"revision":"REVISION","nodes":["NODE"]}' --json
34
+ <!-- agentic:var flowCommand --> read FLOW_ID --input '{"revision":"REVISION","text":{"node":"NODE","field":"prompt","start":1,"lines":40}}' --json
35
+ ```
36
+
37
+ A known Flow ID avoids a list lookup. `show` gives lifecycle and publication
38
+ state. `read` gives a graph outline, selected node details, or a text excerpt;
39
+ `nodes` and `text` are alternative modes. `search` finds matches in names,
40
+ configuration and source text, optionally restricted by node type.
41
+
42
+ The returned `revision` pins subsequent reads and the eventual edit to the same
43
+ snapshot. Pagination uses `nextCursor` for lists, `nextOffset` for search, and
44
+ `nextStart` for text. Keep the Revision when reading more matches or text pages.
45
+ Long text is complete when `truncated` is false. Read as much context as the
46
+ change needs; a small edit need not fetch every node or every prompt.
47
+
48
+ ## Save changes
49
+
50
+ ```bash
51
+ <!-- agentic:var flowCommand --> schema edit --json
52
+ <!-- agentic:var flowCommand --> schema code --json
53
+ <!-- agentic:var flowCommand --> edit FLOW_ID --file edits.json --json
54
+ ```
55
+
56
+ An edit request contains `baseRevision`, `requestId`, and an ordered `edits`
57
+ array. The batch is atomic. Use the observed Revision and a unique request ID
58
+ for each new change; retain both when retrying an uncertain submission.
59
+ Examples below use `REVISION` and uppercase node placeholders for observed IDs.
60
+ Their request IDs are illustrative; choose fresh IDs when adapting them.
61
+
62
+ `read`, `search`, and `edit` accept `--input` (JSON, `@file`, or `-`) or `--file`
63
+ (path or `-`). Use one input mechanism per command. Nested strings are literal:
64
+ a `code` value of `@transform.js` does not load that file.
65
+
66
+ | Operation | Effect |
67
+ | --------------------------------- | ----------------------------------------------------------------------------------- |
68
+ | `node.add` | Create a node with optional business configuration, input bindings and code/prompt. |
69
+ | `node.update` | Patch metadata or configuration; `clear` removes selected paths. |
70
+ | `node.remove` | Remove a node. |
71
+ | `input.set` | Set a named input's source. |
72
+ | `edge.connect`, `edge.disconnect` | Change execution routing, with an optional Condition `branch`. |
73
+ | `text.edit` | Replace one exact occurrence in code or prompt. |
74
+ | `text.set` | Replace the entire code or prompt. |
75
+
76
+ `node.add.as` declares a batch-local alias. Later operations in that batch use
77
+ `$alias`; later requests use stable references from the response's `nodes` map.
78
+ Updates recursively merge objects, including JSON schemas; arrays replace whole
79
+ arrays. An empty object preserves existing fields. For example,
80
+ `clear: [["config", "inputs", "amount", "schema", "type"]]` removes that constraint.
81
+ Absence, explicit null, an unset binding and an inherited default are distinct.
82
+
83
+ ### Connect execution and data
84
+
85
+ This complete edit adds a Manual Trigger, a calculation, and a formatter. It
86
+ needs no Connector or model. `total` is a business output; `amount` is a named
87
+ input whose binding reads it. Both execution edges and data bindings are visible.
88
+
89
+ ```json
90
+ {
91
+ "baseRevision": "REVISION",
92
+ "requestId": "example-code-and-data",
93
+ "edits": [
94
+ { "op": "node.add", "as": "start", "type": "manual", "name": "Start" },
95
+ {
96
+ "op": "node.add",
97
+ "as": "calculate",
98
+ "type": "code",
99
+ "name": "Calculate total",
100
+ "config": { "outputs": { "total": { "schema": { "type": "number" }, "nullable": false } } },
101
+ "inputs": { "prices": { "kind": "value", "value": [12, 30] } },
102
+ "code": "export default ({prices}) => ({total: prices.reduce((sum, price) => sum + price, 0)})"
103
+ },
104
+ {
105
+ "op": "node.add",
106
+ "as": "format",
107
+ "type": "code",
108
+ "name": "Format total",
109
+ "config": { "outputs": { "text": { "schema": { "type": "string" }, "nullable": false } } },
110
+ "inputs": { "amount": { "kind": "output", "node": "$calculate", "port": "total" } },
111
+ "code": "export default ({amount}) => ({text: `Total: ${amount}`})"
112
+ },
113
+ { "op": "edge.connect", "source": "$start", "target": "$calculate" },
114
+ { "op": "edge.connect", "source": "$calculate", "target": "$format" }
115
+ ]
116
+ }
117
+ ```
118
+
119
+ Code uses JavaScript ESM: a default-exported function receives a named input
120
+ record and returns a record matching `config.outputs`. Collections can be JSON
121
+ arrays inside a field. Bindings to new Code, Agent, LLM, Wait and Approval inputs
122
+ automatically declare inputs accepting JSON; `config.inputs` adds business
123
+ constraints or defaults where needed.
124
+
125
+ Common sources are `{"kind":"value","value":...}`,
126
+ `{"kind":"output","node":"NODE","port":"PORT"}`, and
127
+ `{"kind":"variable","name":"VARIABLE"}`. An output source may omit `port`
128
+ when there is one output, or select a nested `field`. Each node schema advertises
129
+ supported sources. A Trigger cannot read downstream results before the Run starts.
130
+ Schema `{}` accepts dynamic JSON; it does not convert objects or arrays into text.
131
+
132
+ ### Change one prompt rule
133
+
134
+ For an existing Agent whose prompt contains `Include customer email.`, this edit
135
+ changes that rule while retaining the rest of the prompt and node configuration.
136
+ Read the target text to choose an exact, unique match.
137
+
138
+ ```json
139
+ {
140
+ "baseRevision": "REVISION",
141
+ "requestId": "example-prompt-edit",
142
+ "edits": [
143
+ {
144
+ "op": "text.edit",
145
+ "node": "AGENT_NODE",
146
+ "field": "prompt",
147
+ "oldText": "Include customer email.",
148
+ "newText": "Omit customer email."
149
+ }
150
+ ]
151
+ }
152
+ ```
153
+
154
+ Zero or multiple matches reject the replacement. A longer surrounding excerpt
155
+ can disambiguate it. `text.set` suits an intentional full rewrite. LLM message
156
+ templates live in `config.template`, rather than the Agent's `prompt` field.
157
+
158
+ ### Request structured model output
159
+
160
+ An LLM node supports message templates, conversation history and model settings.
161
+ In this example, `MODEL_ID` is a model supported by the deployment and
162
+ `SOURCE_NODE` has one output containing an order. `{{order}}` interpolates the
163
+ bound business input. `resultSchema` defines the result without manually building
164
+ an output port.
165
+
166
+ ```json
167
+ {
168
+ "baseRevision": "REVISION",
169
+ "requestId": "example-structured-output",
170
+ "edits": [
171
+ {
172
+ "op": "node.add",
173
+ "as": "summary",
174
+ "type": "llm",
175
+ "name": "Summarize order",
176
+ "config": {
177
+ "mode": "json",
178
+ "model": { "model": "MODEL_ID" },
179
+ "template": [{ "role": "user", "content": "Summarize this order: {{order}}" }],
180
+ "resultSchema": {
181
+ "type": "object",
182
+ "properties": { "summary": { "type": "string" } },
183
+ "required": ["summary"],
184
+ "additionalProperties": false
185
+ }
186
+ },
187
+ "inputs": { "order": { "kind": "output", "node": "SOURCE_NODE" } }
188
+ },
189
+ { "op": "edge.connect", "source": "SOURCE_NODE", "target": "$summary" }
190
+ ]
191
+ }
192
+ ```
193
+
194
+ A downstream node can bind the sole result and select `field: "summary"`.
195
+ For model-driven tool use, `schema agent` describes prompts, tools and structured
196
+ results. For classification, `schema decision` describes named questions and
197
+ answer objects. A Decision answer still needs a Condition if it should control
198
+ execution. Schemas constrain data shape; task-specific tests establish whether a
199
+ model's answers are useful.
200
+
201
+ ### Put an existing operation behind a condition
202
+
203
+ Suppose `SOURCE_NODE` outputs `total` and directly precedes `TARGET_NODE`.
204
+ This edit inserts a threshold gate. The target runs only for totals above 100;
205
+ its existing data bindings stay unchanged. The same pattern can guard an external
206
+ write, notification, or a local calculation.
207
+
208
+ ```json
209
+ {
210
+ "baseRevision": "REVISION",
211
+ "requestId": "example-condition",
212
+ "edits": [
213
+ {
214
+ "op": "node.add",
215
+ "as": "gate",
216
+ "type": "condition",
217
+ "name": "Above threshold",
218
+ "config": {
219
+ "match": "first",
220
+ "branches": [
221
+ {
222
+ "name": "large",
223
+ "when": {
224
+ "left": { "kind": "output", "node": "SOURCE_NODE", "port": "total" },
225
+ "operator": ">",
226
+ "right": { "kind": "value", "value": 100 }
227
+ }
228
+ }
229
+ ]
230
+ }
231
+ },
232
+ { "op": "edge.disconnect", "source": "SOURCE_NODE", "target": "TARGET_NODE" },
233
+ { "op": "edge.connect", "source": "SOURCE_NODE", "target": "$gate" },
234
+ { "op": "edge.connect", "source": "$gate", "target": "TARGET_NODE", "branch": "large" }
235
+ ]
236
+ }
237
+ ```
238
+
239
+ `match: "first"` selects the first matching branch; `"all"` allows multiple
240
+ matches. `otherwise` handles unmatched execution if connected. Predicates can
241
+ combine expressions with `all` or `any`; see `schema condition`. Merely binding
242
+ data from a Condition's upstream node does not guard a side effect.
243
+
244
+ ## Provider actions, events and other node types
245
+
246
+ ```bash
247
+ <!-- agentic:var flowCommand --> connector search "<provider and operation>" --flow FLOW_ID --json
248
+ <!-- agentic:var flowCommand --> connector show ACTION_ID --flow FLOW_ID --json
249
+ <!-- agentic:var flowCommand --> schema --flow FLOW_ID --input '{"action":"ACTION_ID"}' --json
250
+ <!-- agentic:var flowCommand --> connector connections SERVICE --flow FLOW_ID --json
251
+ <!-- agentic:var flowCommand --> trigger search "<provider and event>" --json
252
+ <!-- agentic:var flowCommand --> trigger show TRIGGER_KEY --json
253
+ ```
254
+
255
+ Discovery supplies actual Action IDs, Trigger keys, ports and Connections. A
256
+ search match is a candidate; its schema describes the operation and data. An
257
+ unavailable action and an action lacking an active Connection are different
258
+ problems. Actions marked `authenticated: false` need no account. Connection
259
+ selection rules come from the selected node contract.
260
+
261
+ | Capability | Relevant schema and configuration |
262
+ | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
263
+ | Provider read or write | `connector`: discovered `action`, optional `connectionId`, named input bindings. |
264
+ | Provider event | `poll` or `integration`: discovered `key`, Connection and any static inputs; polling also has a schedule. |
265
+ | Manual, HTTP or scheduled entry | `manual`, `webhook`, `cron`: business payload fields or schedule. A Manual Trigger has no output parameters. |
266
+ | Workflow failure handler | `error`: published upstream Flows' automatic Run failures; not a node-level catch or a handler for manual Run failures. |
267
+ | Fixed HTTP operation | `openapi`: an OpenAPI source, path and method; resolved authentication choices and ports appear in node details. |
268
+ | Reusable constants | `value`: named JSON values and optional output constraints. |
269
+ | Durable interaction | `wait`, `approval`: prompt and named inputs; resume through the Run's Wait ID. |
270
+
271
+ Connector and LLM services are configured on the Server. Missing capabilities
272
+ should be described in terms of the required operation or event, without silently
273
+ replacing the requested Flow with direct provider calls outside it.
274
+
275
+ ## Validation and execution
276
+
277
+ An accepted edit returns `saved: true`, the new `revision`, alias-to-node mappings,
278
+ and `validation`. A Draft may save with diagnostics. `valid`, `invalid` and
279
+ `unavailable` describe validation, not whether the write happened.
280
+
281
+ ```bash
282
+ <!-- agentic:var flowCommand --> check FLOW_ID --revision REVISION --json
283
+ <!-- agentic:var flowCommand --> run FLOW_ID --source draft --trigger TRIGGER_ID --expected-revision REVISION --idempotency-key RUN_KEY --wait --json
284
+ <!-- agentic:var flowCommand --> runs result RUN_ID --json
285
+ ```
286
+
287
+ Use the edit's validation or request a check of a specific Revision. Structural
288
+ validity does not demonstrate business behavior, model quality or runtime account
289
+ access. An authorized test Run can exercise meaningful input cases. Draft Run
290
+ readiness concerns the selected Trigger's path and dependencies; publishing and
291
+ Live Runs require the complete Flow to be ready.
292
+
293
+ A sole Manual Trigger is selected automatically; otherwise specify `--trigger`.
294
+ `--outputs` supplies named Trigger outputs. `--input` supplies per-node input
295
+ overrides; command help gives their shape. Live Runs use `--source live` and
296
+ `--expected-publication` instead of `--expected-revision`.
297
+
298
+ ### Inspect or continue an existing Run
299
+
300
+ ```bash
301
+ <!-- agentic:var flowCommand --> runs show RUN_ID --json
302
+ <!-- agentic:var flowCommand --> runs events RUN_ID --json
303
+ <!-- agentic:var flowCommand --> runs wait RUN_ID --timeout 60000 --json
304
+ <!-- agentic:var flowCommand --> runs resolve RUN_ID WAIT_ID approve --json
305
+ ```
306
+
307
+ A Wait or Approval suspends the existing Run. `run.waits` carries its Wait IDs;
308
+ resolution uses `continue`, `approve` or `reject` as appropriate to that wait and
309
+ the authorized decision. A client timeout also leaves the operation running.
310
+ Waiting again observes the same Run; starting another Run repeats execution.
311
+
312
+ | Exit code | Meaning |
313
+ | --------- | ------------------------------------------------------------------------------------------ |
314
+ | `0` | Command succeeded or asynchronous work was accepted; inspect the returned status. |
315
+ | `1` | Command error or unsuccessful terminal Run; inspect the error or Run events. |
316
+ | `2` | Run is blocked on an unresolved Wait or Approval. |
317
+ | `3` | Client wait expired or publication is pending; retain its identity and continue observing. |
318
+
319
+ `--timeout` is a wait budget in milliseconds (default 60000). Event following
320
+ with `runs events RUN_ID --follow --json` emits NDJSON; `nextAfter` supports
321
+ resuming. `runs results`, `runs read-result` and `runs download-result` expose
322
+ large results without requiring the whole value in one response.
323
+
324
+ ## Publish and open
325
+
326
+ ```bash
327
+ <!-- agentic:var flowCommand --> publish FLOW_ID --expected-revision REVISION --expected-publication PUBLICATION_OR_NONE --idempotency-key PUBLISH_KEY --json
328
+ <!-- agentic:var flowCommand --> enable FLOW_ID --expected-publication PUBLICATION --json
329
+ <!-- agentic:var flowCommand --> workbench FLOW_ID --json
330
+ <!-- agentic:var flowCommand --> open FLOW_ID
331
+ ```
332
+
333
+ `none` means no prior Live Publication was observed. Publication and enablement
334
+ are for a requested release or automatic execution, and are unnecessary for a
335
+ Draft test Run. `publications wait` follows an accepted pending publication.
336
+
337
+ `workbench` returns a URL; `open` opens the system browser. Browser authentication
338
+ belongs to the host. An open Workbench receives Revision notifications, so a CLI
339
+ edit does not require a page reload.
340
+
341
+ ## Recover without losing another change or repeating an effect
342
+
343
+ - **Revision conflict:** another writer changed the Draft. Read the latest
344
+ relevant state, recompute the edit, and use a new request ID. Merely replacing
345
+ `baseRevision` can overwrite the other writer's intent.
346
+ - **`flow.mutation-outcome-unknown`:** the submission may have succeeded. Preserve
347
+ its identity, key and complete payload. Only an identical retry can safely
348
+ resolve that uncertainty; a new key describes a new mutation.
349
+ - **Saved with diagnostics:** the edit happened. Repair the saved Revision or
350
+ investigate unavailable validation; resubmitting it is not a validation step.
351
+ - **Failed Run:** events and results identify the failing node and available
352
+ inputs. Consider already-completed external effects before running it again.
353
+
354
+ A useful completion report identifies the Flow and Revision, what changed,
355
+ what was actually checked or executed, and any remaining blocker. Its detail
356
+ should match the task.
@@ -0,0 +1,196 @@
1
+ # Convert n8n behavior into Open Flow
2
+
3
+ An n8n export describes a workflow's behavior and its n8n-specific machinery.
4
+ Use the behavior to design an Open Flow graph with current node contracts.
5
+ Equivalent results may need a different number of nodes or a different graph.
6
+ [Flow authoring](flow-authoring.md) covers `<!-- agentic:var flowCommand -->`,
7
+ atomic edits, input bindings and execution.
8
+
9
+ The requested outcome can be a feasibility assessment, a complete conversion,
10
+ or a useful partial Draft. Unresolved behavior limits what can be claimed as
11
+ converted; it does not prevent saving an agreed partial result. Running,
12
+ publishing and enabling remain separate actions within the user's authorization.
13
+
14
+ ## Choose a representation
15
+
16
+ | Source behavior | Open Flow building blocks | What determines a good fit |
17
+ | --------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------ |
18
+ | Scheduled, webhook or provider event | Cron, Webhook, Poll or Integration Trigger | Schedule/timezone, event payload, delivery and response behavior. |
19
+ | Provider read or write | Connector | Exact operation, input/output shape, account and number of calls. |
20
+ | Field shaping, filtering, aggregation or join | Code with named input/output fields | Explicit collection representation, ordering, keys and empty-input behavior. |
21
+ | IF or Switch | Condition and named execution branches | Predicate, first/all matches, fallback route and data sent onward. |
22
+ | Prompt plus model and output parser | LLM with template, inputs and `resultSchema` | Supported model, result shape and any parser repair behavior. |
23
+ | Agent with tools or memory | Agent where its current contract fits | Tool permissions, state lifetime and how context is supplied. |
24
+ | HTTP request | OpenAPI where an actual operation contract fits | Method, parameters, auth, response format and error behavior. |
25
+ | Human response or durable pause | Wait or Approval | Who resumes it, payload, timing, notification and external response lifecycle. |
26
+ | Workflow failure handling | Error Trigger where its scope fits | Which failures trigger it and what failure data it receives. |
27
+
28
+ Use `schema TYPE` and provider discovery for the actual deployment. A node's
29
+ name alone does not establish equivalence. Provider operations can differ even
30
+ when both products use the same service name. Model, parser, tool, memory and
31
+ retrieval nodes connected by specialized AI edges often form one semantic unit;
32
+ those edges do not translate directly into execution edges.
33
+
34
+ ## Records, collections and item identity
35
+
36
+ n8n passes arrays of items and many nodes process each item automatically.
37
+ Open Flow Code receives one named input record; a collection is an explicit
38
+ array field in that record. Passing an array to a Connector does not imply one
39
+ call per element. This distinction matters most for external writes. See
40
+ [n8n data structure](https://docs.n8n.io/build/work-with-data/understand-n8ns-data-structure.md).
41
+
42
+ For example, a source step that filters paid orders and sums their amounts can
43
+ be represented by a Code node with an `orders` input and `count` and `total`
44
+ outputs. Assuming numeric amounts, its code can be:
45
+
46
+ ```javascript
47
+ export default ({ orders }) => {
48
+ const paid = orders.filter((order) => order.status === 'paid')
49
+ return {
50
+ count: paid.length,
51
+ total: paid.reduce((sum, order) => sum + order.amount, 0),
52
+ }
53
+ }
54
+ ```
55
+
56
+ Declare those business outputs in `config.outputs` and bind the input to the
57
+ actual upstream collection. Empty input produces zero count and total. If the
58
+ source accepts null or string amounts, preserve that behavior explicitly. If
59
+ its next step sends one message per order, this aggregation would change the
60
+ behavior; preserve the individual calls or discuss a batch-message redesign.
61
+
62
+ n8n also links output items to their originating items. An expression such as
63
+ `$('Customer').item.json.email` depends on that lineage, whereas `.first()`
64
+ explicitly chooses a position. Neither implies an arbitrary first available
65
+ customer. An Open Flow representation can use a single record when the relation
66
+ is one-to-one, or an explicit keyed join when working with collections. Missing
67
+ and duplicate keys need the source's intended behavior. See
68
+ [n8n item linking](https://docs.n8n.io/build/work-with-data/reference-data/link-data-items.md).
69
+
70
+ ### Expression examples
71
+
72
+ | n8n intent | Possible Open Flow expression |
73
+ | -------------------------------------- | ------------------------------------------------------------------------------------------------------ |
74
+ | Read `orderId` from the current item | Bind a named `order` input; Code reads `order.orderId`, or select an upstream output's `field`. |
75
+ | Read all records from an earlier step | Bind its explicit array output and process the array. |
76
+ | Read a linked customer for each order | Pass the linked record explicitly, or join collections on the real customer key. |
77
+ | Interpolate an order in a model prompt | Bind `order` to Agent/LLM and use `{{order}}`. |
78
+ | Read a file or attachment | Use the selected Trigger/Connector's file or artifact contract; there is no implicit n8n binary store. |
79
+ | Read environment or execution metadata | Use an equivalent supported binding, or an explicit business input when appropriate. |
80
+
81
+ n8n helpers such as `$input`, `$json` and `$node` are not Open Flow JavaScript
82
+ globals. Rewrite their meaning using named inputs. Execution-time clocks, timezones
83
+ and persisted state may also affect the result; an ordinary JavaScript value is
84
+ only equivalent when those lifetimes agree.
85
+
86
+ ## Branches control execution separately from data
87
+
88
+ Consider “if an order total exceeds 100, notify operations; otherwise archive it.”
89
+ The Open Flow Condition reads the total, selects a named branch, and execution
90
+ edges reach the notification or archive node. Both destinations bind their
91
+ payload from the original order output.
92
+
93
+ The [condition example](flow-authoring.md#put-an-existing-operation-behind-a-condition)
94
+ shows inserting such a gate without altering the destination's input bindings.
95
+ A data reference to an upstream node does not restrict when a side effect runs.
96
+
97
+ Preserve the meaning of source branch indexes: inspect what each output of the
98
+ specific node represents. For multi-case routing, `match: "first"` and
99
+ `match: "all"` support different behavior; `otherwise` is the unmatched route.
100
+ For item-by-item IF routing, partitioning a collection may be necessary before
101
+ choosing execution branches. One condition over an array is not automatically
102
+ one condition per item.
103
+
104
+ Merge behavior also depends on join mode, keys, ordering, duplicates and absent
105
+ inputs. Code can implement a bounded, explicit join. A generic connection
106
+ between two predecessors does not reproduce every Merge mode.
107
+
108
+ ## Provider access and credentials
109
+
110
+ ```bash
111
+ <!-- agentic:var flowCommand --> connector search "<provider and operation>" --flow FLOW_ID --json
112
+ <!-- agentic:var flowCommand --> schema --flow FLOW_ID --input '{"action":"ACTION_ID"}' --json
113
+ <!-- agentic:var flowCommand --> connector connections SERVICE --flow FLOW_ID --json
114
+ <!-- agentic:var flowCommand --> trigger search "<provider and event>" --json
115
+ ```
116
+
117
+ Select real Action IDs, Trigger keys and Connections from discovery. An n8n
118
+ credential ID is not an Open Flow Connection ID. Exports can also contain secrets
119
+ inside URLs, headers, expressions or Code; keep those out of Drafts and reports.
120
+ Use supported credential bindings for the selected operation. A missing account
121
+ can leave a Draft incomplete without implying that the action itself is absent.
122
+
123
+ For HTTP Request conversion, `schema openapi` describes source URL, operation
124
+ selection and supported authentication. Check the actual operation's response,
125
+ non-success status, redirect, binary and retry requirements against that contract.
126
+ An invented API specification does not establish support for an unknown operation.
127
+
128
+ A provider event may expose an ID where the source expected a full record. A
129
+ Connector read can supply the missing data if it preserves the desired behavior.
130
+ Likewise, a polling Trigger may own state or deduplication that a schedule plus
131
+ list action would need to reproduce explicitly.
132
+
133
+ ## Models and stateful interactions
134
+
135
+ A standard prompt/model/parser cluster can often become one LLM node. The
136
+ [structured result example](flow-authoring.md#request-structured-model-output)
137
+ shows a message template, a bound input and a result schema. Business rules that
138
+ depend on particular fields may also need validation before a side effect.
139
+ A result schema alone does not reproduce an arbitrary parser's retry or repair
140
+ policy.
141
+
142
+ Agent tools, memory, retrievers and vector stores affect both available actions
143
+ and state lifetime. Inspect them as part of the source behavior. A successful
144
+ single prompt response does not establish equivalence with a stateful conversation.
145
+
146
+ Wait and Approval suspend a Run with a durable Wait ID. `runs resolve` continues
147
+ that Run. Match the source's resume payload, timing and external interaction
148
+ before calling it equivalent. Forms, conversational memory, delayed responses and
149
+ webhook response lifecycles may require capabilities beyond a simple pause;
150
+ a polling loop inside Code does not provide those lifecycles.
151
+
152
+ ## Failures and execution settings
153
+
154
+ Source execution settings can change the number of processed items, output on
155
+ empty results, retries and failure routes. Account for settings such as
156
+ `executeOnce`, `alwaysOutputData`, `retryOnFail`, `onError` and legacy
157
+ `continueOnFail` when present. Their effective behavior depends on the source
158
+ node and n8n version; use its implementation or a representative execution when
159
+ the export is insufficient evidence.
160
+
161
+ The observable questions are whether a failed operation is retried, whether a
162
+ retry repeats a side effect, which output receives failure data, and whether the
163
+ workflow ultimately fails or continues. Handling a returned error item and
164
+ handling a thrown exception can be different paths. Preserve both when relevant;
165
+ do not infer their payload or route from the setting's label.
166
+
167
+ Open Flow Error Triggers observe automatic Run failures of published upstream
168
+ Flows. They expose `workflow`, `execution` and `error`; they do not implement a
169
+ node's continue-on-error route or catch manual Run failures. Node-level recovery
170
+ needs a separately supported design.
171
+
172
+ ## Describe gaps and verify the requested result
173
+
174
+ Useful distinctions for a conversion report are:
175
+
176
+ - **Converted:** current contracts express the required behavior.
177
+ - **Needs configuration:** the design fits but needs an account or business value.
178
+ - **Missing provider capability:** the required event or operation is absent
179
+ from the available catalog. Describe its inputs, outputs and affected behavior.
180
+ - **Behavioral mismatch:** execution, state, lineage or response semantics need
181
+ a redesign or cannot currently be preserved. More catalog searching will not
182
+ resolve an execution-model mismatch.
183
+
184
+ Discovery effort should follow the uncertainty. A close search result warrants
185
+ contract inspection; a broad catalog query can help resolve ambiguous terminology.
186
+ There is no prescribed query count or requirement to produce a full inventory
187
+ before a useful local edit. When a gap would change the promised result, explain
188
+ it and align on the reduced scope or redesign before implementing that choice.
189
+
190
+ A saved, valid graph establishes structural correctness. Behavioral comparison
191
+ needs representative inputs and observable results: empty and multiple items,
192
+ branch boundaries, duplicate join keys, failures and external call counts as
193
+ relevant. Test execution stays within the user's authorization. Reports should
194
+ distinguish what was mapped, saved, checked and actually run, identify remaining
195
+ gaps, and link the resulting Flow/Revision when one was created. Annotations and
196
+ disabled nodes need mention only when their omission or behavior matters.