@controllerai/cli 0.2.20 → 0.2.22

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 (79) hide show
  1. package/README.md +6 -3
  2. package/dist/api/client.js +5 -38
  3. package/dist/api/client.js.map +1 -1
  4. package/dist/commands/agent.js +169 -146
  5. package/dist/commands/agent.js.map +1 -1
  6. package/dist/commands/expr.js +10 -3
  7. package/dist/commands/expr.js.map +1 -1
  8. package/dist/commands/integration.js +30 -10
  9. package/dist/commands/integration.js.map +1 -1
  10. package/dist/commands/skills.js +1 -0
  11. package/dist/commands/skills.js.map +1 -1
  12. package/dist/commands/templateSetup.js +42 -0
  13. package/dist/commands/templateSetup.js.map +1 -0
  14. package/dist/commands/workflow.js +6 -7
  15. package/dist/commands/workflow.js.map +1 -1
  16. package/dist/errors.js +2 -5
  17. package/dist/errors.js.map +1 -1
  18. package/dist/index.js +2 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/permissionTool.js +1 -1
  21. package/dist/permissionTool.js.map +1 -1
  22. package/package.json +6 -4
  23. package/skills/controllerai-agents/SKILL.md +109 -26
  24. package/skills/controllerai-agents/references/instructions-and-versioning.md +32 -34
  25. package/skills/controllerai-ai-nodes/SKILL.md +83 -10
  26. package/skills/controllerai-ai-nodes/references/selection-and-testing.md +44 -26
  27. package/skills/controllerai-data/SKILL.md +106 -34
  28. package/skills/controllerai-data/references/data-rails.md +24 -26
  29. package/skills/controllerai-expressions/SKILL.md +82 -29
  30. package/skills/controllerai-expressions/references/dsl-and-recipes.md +42 -40
  31. package/skills/controllerai-flow-control/SKILL.md +104 -21
  32. package/skills/controllerai-flow-control/references/patterns.md +23 -60
  33. package/skills/controllerai-integration-actions/SKILL.md +89 -11
  34. package/skills/controllerai-node-config/SKILL.md +108 -18
  35. package/skills/controllerai-node-config/references/prop-and-readiness.md +28 -78
  36. package/skills/controllerai-runs/SKILL.md +82 -25
  37. package/skills/controllerai-runs/references/debugging.md +24 -22
  38. package/skills/controllerai-start/SKILL.md +63 -35
  39. package/skills/controllerai-start/references/mcp-tool-map.md +22 -21
  40. package/skills/controllerai-template-setup/SKILL.md +132 -0
  41. package/skills/controllerai-template-setup/agents/openai.yaml +4 -0
  42. package/skills/controllerai-triggers/SKILL.md +113 -16
  43. package/skills/controllerai-triggers/references/lifecycle.md +36 -48
  44. package/skills/controllerai-workflows/SKILL.md +105 -25
  45. package/skills/controllerai-workflows/references/command-map.md +20 -44
  46. package/skills/manifest.json +96 -136
  47. package/skills-mcp/controllerai-agents/SKILL.md +121 -0
  48. package/skills-mcp/controllerai-agents/references/instructions-and-versioning.md +76 -0
  49. package/skills-mcp/controllerai-ai-nodes/SKILL.md +91 -0
  50. package/skills-mcp/controllerai-ai-nodes/references/selection-and-testing.md +66 -0
  51. package/skills-mcp/controllerai-data/SKILL.md +116 -0
  52. package/skills-mcp/controllerai-data/references/data-rails.md +43 -0
  53. package/skills-mcp/controllerai-expressions/SKILL.md +97 -0
  54. package/skills-mcp/controllerai-expressions/references/dsl-and-recipes.md +136 -0
  55. package/skills-mcp/controllerai-flow-control/SKILL.md +112 -0
  56. package/skills-mcp/controllerai-flow-control/references/patterns.md +82 -0
  57. package/skills-mcp/controllerai-integration-actions/SKILL.md +90 -0
  58. package/skills-mcp/controllerai-node-config/SKILL.md +106 -0
  59. package/skills-mcp/controllerai-node-config/references/prop-and-readiness.md +49 -0
  60. package/skills-mcp/controllerai-runs/SKILL.md +87 -0
  61. package/skills-mcp/controllerai-runs/references/debugging.md +53 -0
  62. package/skills-mcp/controllerai-start/SKILL.md +82 -0
  63. package/skills-mcp/controllerai-start/references/mcp-tool-map.md +177 -0
  64. package/skills-mcp/controllerai-template-setup/SKILL.md +10 -0
  65. package/skills-mcp/controllerai-triggers/SKILL.md +109 -0
  66. package/skills-mcp/controllerai-triggers/references/lifecycle.md +59 -0
  67. package/skills-mcp/controllerai-workflows/SKILL.md +108 -0
  68. package/skills-mcp/controllerai-workflows/references/command-map.md +1 -0
  69. package/skills-mcp/manifest.json +200 -0
  70. package/skills/controllerai-agents/references/command-reference.md +0 -205
  71. package/skills/controllerai-ai-nodes/references/command-reference.md +0 -98
  72. package/skills/controllerai-data/references/command-reference.md +0 -177
  73. package/skills/controllerai-expressions/references/command-reference.md +0 -166
  74. package/skills/controllerai-flow-control/references/command-reference.md +0 -171
  75. package/skills/controllerai-integration-actions/references/command-reference.md +0 -118
  76. package/skills/controllerai-node-config/references/command-reference.md +0 -186
  77. package/skills/controllerai-runs/references/command-reference.md +0 -113
  78. package/skills/controllerai-triggers/references/command-reference.md +0 -215
  79. package/skills/controllerai-workflows/references/command-reference.md +0 -118
@@ -1,177 +0,0 @@
1
- # Controller AI data
2
-
3
-
4
- Controller AI has two separate concerns:
5
-
6
- - A `custom.*` schema defines fields and makes a collection available.
7
- - Development and live record stores hold separate rows. Publishing graph/configuration does not copy records.
8
-
9
-
10
- - Persisting data requires both a schema and a create-record node; see A type vs a stored record.
11
- - `schema update --field` replaces the full field list; see Define a schema deliberately.
12
- - Relabel a stable key instead of recreating it; see How schema changes affect stored records.
13
- - Query results and writes use different field keys; see Respect field-key asymmetry.
14
- - Read and write the requested dev or live store; see Work on the correct rail.
15
- - Remove only scratch schemas created for abandoned attempts; see Before you finish.
16
-
17
- ## A type vs a stored record
18
-
19
- When a request says store, save, log, or record with no external destination, create the schema and configure a create-record node. A flow output alone is not persistence.
20
-
21
- `custom.customer` and `dataRecord.custom.customer` are different things, and the difference carries through everything:
22
-
23
- - `custom.customer` is the TYPE — the shape, like a TypeScript interface. A value of this type is just data in that shape.
24
- - `dataRecord.custom.customer` is an ACTUAL STORED RECORD of that shape — the same fields plus record identity (`id`, `createdAt`, `updatedAt`) proving it lives in the collection.
25
-
26
- In the product UI this appears as the "data record" checkbox when selecting a type for a flow input or run-subflow-on-list: unchecked means "data shaped like a customer", checked means "a real stored customer record". Users find this checkbox confusing — be ready to explain it in these terms.
27
-
28
- Consequences:
29
-
30
- - Only real records satisfy a `dataRecord.*` input. An expression that CONSTRUCTS record-shaped objects (e.g. a `.map()` that builds new objects) produces `custom.*`-shaped values with no identity — pass fetched records through instead.
31
- - Record metadata (`id`, `createdAt`) exists only on `dataRecord.*` values; on plain `custom.*` values those paths resolve empty.
32
-
33
- ## Data nodes: update semantics
34
-
35
- - "Update multiple data records" takes an expression typed `list.dataRecord.custom.<type>` — real stored records passed through from a query or `workflowData(...)`. The batch is ONE transaction: if any resolved record is stale (deleted since fetched) or foreign to the collection, the node errors and NOTHING updates. A failed run therefore means zero changes — fix and rerun safely.
36
- - On the single-record "Update data" node, every ENABLED field is written. Enable only the fields you intend to change; disabled fields keep their stored values.
37
-
38
- ## Define a schema deliberately
39
-
40
- Inspect existing types first:
41
-
42
- ```bash
43
- cai schema list --json
44
- cai schema get custom.<name> --json
45
- ```
46
-
47
- Create a type with stable field IDs:
48
-
49
- ```bash
50
- cai schema create custom.customer \
51
- --display "Customer" \
52
- --field email:text:"Email" \
53
- --field score:number:"Score" \
54
- --json
55
- ```
56
-
57
- Creating the schema creates/synchronizes its collection. `cai data collection create` is only an alias for schema creation.
58
-
59
- MCP: `cai data collection create` is merged into `schema_type_create`.
60
-
61
- `schema update --field` replaces the complete field list; it does not append — this is deliberate, so a subset CAN intentionally replace the whole schema. Read the current schema, preserve intended fields, and disclose any removals. The transport response warns about every field being dropped and echoes back the COMPLETE stored shape after the write — always read that echo and confirm it matches intent. Deleting a schema hides its collection and records and breaks dependent expressions — tell the user, and know it is recoverable (see below).
62
-
63
- ## How schema changes affect stored records
64
-
65
- Schema changes never hard-delete data. There is no delete path for records in schema sync — everything is deactivation and hiding:
66
-
67
- - Delete a type → its collection goes INACTIVE on the next saved state; every record row survives. Re-add the same `custom.*` key and the same collection reactivates with all records intact.
68
- - Delete a field → its values stay inside each record, unaddressable. Re-add the SAME field id and the old values reappear.
69
- - Add a field → purely additive; existing records read the new field as null.
70
- - Change a field's type → values are never converted or erased; incompatible values read as null and return if the type is changed back. The schema hash changes, which blocks copy-to-live until dev and live match again.
71
- - Publish applies the same deactivate/reactivate logic to live. Live records are hidden, never deleted.
72
- - Restoring an older workflow state rolls back the DEFINITION (graph and schemas), never the data. The record store is not versioned and only moves forward: a deleted type reactivates with whatever the store holds NOW — records created, edited, or soft-deleted after the restored state stay exactly as they are. Restore is not a data snapshot.
73
-
74
- The truly destructive operations are record-level only: `data delete`, `data delete-many`, and overwriting edits. Those have no undo — treat them with explicit user intent; everything schema-shaped is safely reversible.
75
-
76
- Relabel a type without changing its stable key:
77
-
78
- ```bash
79
- cai schema update custom.customer --display "Customers" --json
80
- ```
81
-
82
- Changing `custom.customer` itself is a key migration, not a relabel. It creates a new type and deactivates the old collection, stranding its records under the old key. Migrate records explicitly by export/import or restore the old key.
83
-
84
- ## Respect field-key asymmetry
85
-
86
- ```bash
87
- cai data fields --type custom.customer --json
88
- ```
89
-
90
- - `data create` and `data update` take field IDs.
91
- - Query/get results are keyed by field display names.
92
- - CSV columns are also matched by display names.
93
-
94
- Never feed a queried record directly into a write. Build a new fields object keyed by the `writeKey` mapping and omit reserved `id`, `createdAt`, and `updatedAt`.
95
-
96
- Fields may be typed `date`. Their values are strict ISO-8601 strings (`"2026-06-05"` or `"2026-06-05T12:30:00Z"`) on both write and read; anything else is rejected. Reserved `createdAt`/`updatedAt` are currently `text` — use `.toDate()` before date operations on them.
97
-
98
- ## Work on the correct rail
99
-
100
- Development is the default:
101
-
102
- ```bash
103
- cai data collections --env dev --json
104
- cai data query --type custom.customer --env dev --json
105
- cai data create --type custom.customer --fields '{"email":"a@example.com","score":5}' --env dev --json
106
- ```
107
-
108
- MCP: every data write requires explicit `env`; reads default to `dev` when `env` is omitted.
109
-
110
- Use `--env live` only when the user intends to inspect or mutate production state. Before a live write, say that published/triggered runs read this store.
111
-
112
- `data query --query` is plain-text search over serialized records and metadata, not a field predicate. Perform exact field filtering inside workflow expressions or inspect results explicitly.
113
-
114
- ## Delete records explicitly
115
-
116
- Native workflow Delete Record actions provide reusable deletion inside a flow. Discover their exact keys under registry_data. The data tools below also delete one known record or an explicit set of IDs during setup and debugging:
117
-
118
- ```bash
119
- cai data delete <recordId> --type custom.customer --env dev --json
120
- cai data delete-many --type custom.customer --records <id1,id2> --env dev --json
121
- ```
122
-
123
- Delete every record in a collection only with explicit user intent; `--all` also requires `--yes`:
124
-
125
- ```bash
126
- cai data delete-many --type custom.customer --all --yes --env dev --json
127
- ```
128
-
129
- MCP: `data_record_delete_many` requires explicit `env: dev|live` and exactly one of `recordIds` or `all: true`; it has no `yes` parameter.
130
-
131
- The CLI default rail is development. Targeted and `--all --yes` bulk deletions also support `--env live`; a live deletion immediately changes the data read by the published workflow, so use it only with explicit production intent and query that rail afterward.
132
-
133
- ## Import and promote
134
-
135
- Validate CSV before writing:
136
-
137
- ```bash
138
- cai data import --type custom.customer --file customers.csv --mode dryRun --env dev --json
139
- cai data import --type custom.customer --file customers.csv --mode commit --env dev --json
140
- ```
141
-
142
- MCP: pass CSV text inline as `content` with explicit `env: dev|live` to `data_record_import_validate`, then use `data_record_import_commit` to write the validated content.
143
-
144
- Both CLI modes support `--env live`: dry-run remains non-writing, while a live commit appends rows immediately visible to the published workflow. Verify result counts and query the imported rows on the same rail.
145
-
146
- To seed live from development:
147
-
148
- ```bash
149
- cai workflow publish --json
150
- cai data copy-to-live --type custom.customer --json
151
- cai data collections --env live --json
152
- ```
153
-
154
- MCP: call `data_copy_to_live` with the explicit acknowledgement `env: live`.
155
-
156
- The live schema must exist through a publish before copying. Treat copy-to-live as a production mutation and report counts/results.
157
-
158
- ## Workflow expressions
159
-
160
- `workflowData("custom.customer")` requires an active collection on the executing rail. Read `controllerai-expressions` for filtering. Test stateful behavior against development, then independently verify required live seed data.
161
-
162
- Read [data-rails.md](data-rails.md) before schema rewrites, deletion, bulk deletion, import, or copy-to-live.
163
-
164
- ## Before you finish
165
-
166
- ```bash
167
- cai schema get custom.<type> --json
168
- cai data collections --env <dev|live> --json
169
- cai data query --type custom.<type> --env <dev|live> --json
170
- cai workflow outline --json
171
- cai workflow validate --json
172
- ```
173
-
174
- - Schema read-back must show the intended stable key, display, complete field list, and field types.
175
- - The same-rail query must prove the expected persisted records; node output alone is insufficient.
176
- - Compare schema and workflow inventory with the request. Remove only schemas created during abandoned attempts, never pre-existing user objects.
177
- - For a live request, verify the live collection after publish or copy and report the affected record count.
@@ -1,166 +0,0 @@
1
- # Controller AI expressions
2
-
3
-
4
- Controller AI uses a restricted JavaScript-like authoring language that is compiled into the workflow engine's expression AST. It is not general JavaScript. Discover the real scope and compile before every write.
5
-
6
-
7
- - Wire first; refresh context after structural changes. See Required write loop.
8
- - `--value` is static; dynamic values use `--js`. See Typed values and templates.
9
- - Copy current bindings, not labels or stale names. See Read the destination's exact scope.
10
- - Never use raw ASTs, general JavaScript, or private APIs. See Use only advertised syntax.
11
- - Keep ternaries in the destination type. See Typed values and templates.
12
- - Prove resolution in a containing-flow run. See Verify runtime resolution.
13
-
14
- Data types are the TypeScript types of workflows: every expression is typed, and an expression can only be written against a known type. Integration types describe what integration actions output (resolved by a successful dev run); `custom.*` types describe shapes for data-store records and AI-node structured outputs. Two wrappers matter: `dataRecord.custom.x` means "an actual stored record of that shape" — the fields plus record identity (`id`, `createdAt`) — while bare `custom.x` is just data in that shape; `list.` prefixes a list of any type. When an expression cannot be authored, the cause is usually a missing type — resolve the upstream node's output type first, don't guess field paths.
15
-
16
- `.every()` accepts the same multi-condition predicates as `.filter()` and compiles to `:all match`. A removed schema field makes `.every()` false but is dropped by `.filter()`. Comparator semantics govern missing, null, and unparseable values; guards must require presence explicitly.
17
-
18
- ## How empty values evaluate
19
-
20
- An empty input evaluates to its typed empty form: `""` for TEXT, null/undefined for objects. Both cases below fail silently:
21
-
22
- - **A filter compared with empty matches no records.** To skip an omitted constraint, opt into `ignoreEmptyConstraints`; see [dsl-and-recipes.md](dsl-and-recipes.md).
23
- - **An omitted optional TEXT input arrives as `""`.** Equality then matches nothing and can make a create-if-missing step duplicate data.
24
-
25
- When a run unexpectedly returns zero results, check the resolved configs for empty comparison values first (`cai exec node <nodeExecutionId> --full`).
26
-
27
- ## Required write loop
28
-
29
- ### 1. Wire first
30
-
31
- An upstream output is available only after it is connected to the target node. If context lists it under `unwiredUpstreamOutputs`, connect it, then fetch context again.
32
-
33
- ```bash
34
- cai node connect --flow <flowId> --from <sourceNodeId> --to <targetNodeId> --json
35
- ```
36
-
37
- Use `cai branch connect` for Router or If/Else outputs.
38
-
39
- ### 2. Read the destination's exact scope
40
-
41
- ```bash
42
- cai expr context <nodeId> --prop <propName> --json
43
- ```
44
-
45
- Copy binding and field names exactly. Important sources include:
46
-
47
- - `flow.<name>`: flow input
48
- - `inputs.<name>`: wired node input; the payload is usually already unwrapped
49
- - `item`: injected current item, only where context exposes it
50
- - `workflowData("custom.type")`: records from an active workflow collection
51
- - `now`, `isLiveVersion`: constants shown by context
52
-
53
- Do not derive a binding from a display label. Context flags duplicate display names and supplies the de-duplicated JS name, input ID, and source node.
54
-
55
- When compilation reports an unknown field, copy a typeable display-derived accessor it advertises; field IDs appear second for disambiguation.
56
-
57
- ### 3. Use only advertised syntax
58
-
59
- Get the current operation catalog when needed:
60
-
61
- ```bash
62
- cai expr ops --workflow <workflowId> --type <text|number|date|list|boolean|file|custom> --json
63
- ```
64
-
65
- The catalog is workflow-scoped because it is read from expression context. On CLI, an existing `cai use` or `CAI_WORKFLOW` selection can replace the explicit flag; MCP `expr_operation_list` always requires `workflowId`.
66
-
67
- Keep the program to optional `const` declarations followed by one final expression. Do not submit raw AST JSON or probe private APIs. Do not use loops, assignments, `new`, regular expressions, optional chaining, arbitrary library methods, or object construction unless the compiler catalog supports them.
68
-
69
- Date values are ISO-8601 strings on the wire (`"2026-06-05"` or `"2026-06-05T12:30:00Z"`). A `date`-typed flow input or field is used directly with date operations; only `text` values need `.toDate()` first. For epoch text, pass the unit literal explicitly: `flow.timestamp.toDate("epoch_seconds")` or `flow.timestamp.toDate("epoch_millis")`. The ops table renders that argument generically; never guess the unit from digit count.
70
-
71
- #### Typed values and templates
72
-
73
- `--value` stores a literal, so `flow.name`, `{inputs.Name}`, `{{name}}`, and `${name}` stay static. `--js` and `--js-file` compile typed expressions. `${...}` interpolates only inside a backtick template expression, which returns text. Keep non-text values as direct typed expressions.
74
-
75
- Ternaries preserve compatible scalar branch types: number and boolean branches stay number and boolean. A different inferred destination type means the write is incomplete. Report the compiler error; do not quote or coerce branches to text.
76
-
77
- Read [dsl-and-recipes.md](dsl-and-recipes.md) for canonical patterns.
78
-
79
- ### 4. Compile, write, and read back
80
-
81
- For a node prop, prefer:
82
-
83
- ```bash
84
- cai expr set <nodeId> --prop <propName> --js '<expression>' --json
85
- ```
86
-
87
- MCP: use `node_prop_set` with `js`; it merges `cai expr set` with `cai node set`.
88
-
89
- For multiline expressions or shell-sensitive templates on CLI, put the exact source in a UTF-8 file and use `--js-file <path>`. Use `--js-file -` to read stdin. It is mutually exclusive with inline `--js` and is available on `expr validate/set`, `node set/only-when`, and `branch add/update`.
90
-
91
- ```bash
92
- cai expr set <nodeId> --prop <propName> --js-file expression.js --json
93
- ```
94
-
95
- MCP: pass the file contents inline as `js`; the hosted process does not read the local path or stdin.
96
-
97
- This fetches target metadata, auto-enables a catalog-only optional target when needed, compiles before writing the value, persists to the correct channel, and verifies read-back. If validation or the value write then fails, the prop stays enabled; repair with `cai node disable-prop <nodeId> --prop <propName>`.
98
-
99
- MCP: repair with `node_optional_prop_set` and `enabled: false`.
100
-
101
- Array channels are not interchangeable. On a value-channel `SHORT_TEXT_ARRAY`, a scalar expression replaces the whole array with one dynamic element; it does not append. List-typed expressions on value-channel array props are rejected. Use `cai node set <nodeId> --prop <propName> --value '["a","b"]'` for a static list, or use a list-typed expression only where prop metadata exposes whole-array expression mode.
102
-
103
- For a side-effect-free check:
104
-
105
- ```bash
106
- cai expr validate <nodeId> --prop <propName> --js '<expression>' --json
107
- ```
108
-
109
- For conditions:
110
-
111
- ```bash
112
- cai expr validate <nodeId> --mode onlyWhen --js '<boolean expression>' --json
113
- cai node only-when <nodeId> --js '<boolean expression>' --json
114
- ```
115
-
116
- Node `only-when` takes one restricted boolean through `--js` or `--js-file`, or grouped conditions through `--groups-json '[["a","b"],["c"]]'` (inner arrays are AND, outer rows are OR). Root scalar `&&` or `||` inside one expression is unsupported; use `--groups-json` for compound guards. Branch add/update accepts `string[][]`: outer rows are OR, inner entries are AND, and each entry compiles separately.
117
-
118
- ```bash
119
- cai branch update <nodeId> --route <routeId> --condition-json '[["flow.email.isNotEmpty()","flow.units < 5"],["flow.override === true"]]' --json
120
- ```
121
-
122
- Use grouping only when branch semantics fit. For a compound node-only guard, use `--groups-json`; do not weaken the rule or probe private APIs. To make missing numeric input fail `< 5`, use `(flow.unitsLeft ?? 5) < 5`.
123
-
124
- Router and If/Else conditions are written with `cai branch add` or `cai branch update` after the same context/validation discipline.
125
-
126
- ### 5. Verify runtime resolution
127
-
128
- ```bash
129
- cai expr decompile <nodeId> --prop <propName> --json
130
- cai run node <nodeId> --inputs <values.json> --json
131
- ```
132
-
133
- MCP: `run_node` takes the parsed `inputs` object and `waitSeconds` (default 60, max 120).
134
-
135
- Use `run node` to isolate the destination's own configuration. It never executes wired upstream nodes. If the expression consumes `inputs.<name>`, also run its containing flow:
136
-
137
- ```bash
138
- cai run flow <flowId> --inputs <values.json> --json
139
- ```
140
-
141
- MCP: `run_flow` takes the parsed `inputs` object and `waitSeconds` (default 60, max 120).
142
-
143
- Inspect `runScope` and `resolvedConfigs` in the run result. A null or missing dynamic config is an expression failure, not success; a partially empty value is equally suspect. Only the full flow run proves an upstream-derived value. Use `cai exec node <nodeExecutionId> --full --json` to inspect the final status, resolved configs, and untruncated output.
144
-
145
- ## Hard rules
146
-
147
- - Never guess a binding, field, method, or target type.
148
- - Never author against an unwired output.
149
- - Never add `.data` automatically. Use the exact path shown by context.
150
- - Re-read context after rewiring, renaming inputs, changing schemas, changing dynamic selectors, or resolving an output type.
151
- - Preserve `0` and `false`; they are values, not missing data.
152
- - Optional filters need an explicit behavior decision. Do not silently change fail-closed filtering to match-all.
153
- - A compiler success proves syntax and types, not correct source selection. Verify the source node/input IDs when names are ambiguous and inspect runtime values.
154
-
155
- ## Before you finish
156
-
157
- ```bash
158
- cai expr context <nodeId> --prop <propName> --json
159
- cai expr validate <nodeId> --prop <propName> --js '<expression>' --json
160
- cai expr decompile <nodeId> --prop <propName> --json
161
- cai run flow <flowId> --inputs <values.json> --json
162
- ```
163
-
164
- - Context must show current source IDs and bindings.
165
- - Validation and decompile must agree on source and type.
166
- - The flow run must show the intended value in `runScope` and `resolvedConfigs`.
@@ -1,171 +0,0 @@
1
- # Controller AI flow control
2
-
3
-
4
- Choose by intended runtime semantics.
5
-
6
-
7
- - Guard the exact action with node `only-when`; see Conditions.
8
- - Use If/Else for one path and Router for all matches; see Pick the correct primitive.
9
- - Keep downstream wiring after node-only skip; see Conditions.
10
- - Never invent a missing condition or Router order; see If/Else and Router.
11
- - Build standard loops only with `flow for-each`; see Loops.
12
- - Converge paths through Join Paths; see Subflows and convergence.
13
- - Fail missing comparisons closed; keep money paths blocking. See Conditions, Loops, and the error pattern.
14
- - Never acknowledge validation errors without explicit approval; see `controllerai-workflows` Lifecycle rules.
15
-
16
- ## Pick the correct primitive
17
-
18
- | Intent | Primitive | Semantics |
19
- | --- | --- | --- |
20
- | Skip one action when a condition is false | Node `only-when` | No alternate path |
21
- | Choose exactly one path | If/Else | First matching branch runs; Else is last |
22
- | Run every matching path | Router | Conditions are independent; multiple routes may run |
23
- | Apply work to every list item | `flow for-each` | Atomic loop scaffold with typed item flow |
24
- | Reuse a typed unit of work once | Run flow/subflow node | Explicit input and output contract |
25
- | Continue after an action error | Error output or continue-on-error | Error behavior is explicit and testable |
26
-
27
- Do not substitute one primitive merely because its component is easier to find.
28
-
29
- ## Conditions
30
-
31
- Before writing any condition:
32
-
33
- ```bash
34
- cai expr context <controlNodeId> --json
35
- cai expr validate <controlNodeId> --mode onlyWhen --js '<boolean expression>' --json
36
- ```
37
-
38
- For one guarded action:
39
-
40
- ```bash
41
- cai node only-when <nodeId> --js '<boolean expression>' --json
42
- ```
43
-
44
- Use `--clear` only when the user's intent is to remove the guard.
45
-
46
- Guard the action that must skip. Do not substitute If/Else for a node-only skip. Its `only_when_false` does not block ordinary output; downstream remains eligible. Keep wiring. Guard downstream separately or branch if it must also skip.
47
-
48
- Node `only-when` takes one restricted boolean through `--js` or `--js-file`, or grouped conditions through `--groups-json '[["a","b"],["c"]]'` (inner arrays are AND, outer rows are OR). Root scalar `&&` or `||` inside one expression is unsupported; use `--groups-json` for compound guards. Grouped `string[][]` conditions exist only on branch add/update, with outer rows OR and inner entries AND. Use them only when branch semantics fit. If an exact compound node-only guard is required, report it as unsupported; do not weaken the rule or probe private APIs. For a missing number that must fail `< 5`, use a same-type boundary such as `(flow.unitsLeft ?? 5) < 5`.
49
-
50
- Choose the primitive by the runtime signal that callers and tests need. A false node `only-when` records `skipReason: "only_when_false"`; a node on an untaken If/Else branch or unmatched Router route records `skipReason: "upstream_skipped"`.
51
-
52
- ## If/Else and Router
53
-
54
- Discover and initialize the appropriate control component using `controllerai-node-config`, then inspect its exact branch IDs:
55
-
56
- ```bash
57
- cai branch list <controlNodeId> --json
58
- ```
59
-
60
- A fresh If/Else already contains one blank `If` branch and an Else branch. The blank condition is a publish/runtime blocker: copy that default branch ID from `branch list` and update it before adding another condition branch. Do not leave the default blank and create a replacement beside it.
61
-
62
- Never invent a missing condition from node names, current values, or one observed run. If no authoritative platform surface supplies the business rule, leave the branch blank, report the blocker, and ask the user.
63
-
64
- ```bash
65
- cai branch update <ifElseNodeId> \
66
- --route <defaultIfBranchId> \
67
- --name "<meaning>" \
68
- --js '<boolean expression>' \
69
- --json
70
- ```
71
-
72
- Add another condition only when the decision needs another non-Else case. Router routes are independent, so update an existing blank route or add each intended route explicitly:
73
-
74
- ```bash
75
- cai branch add <controlNodeId> --name "<meaning>" --js '<boolean expression>' --json
76
- cai branch update <controlNodeId> --route <routeOrBranchId> --js '<boolean expression>' --json
77
- cai branch connect <controlNodeId> --route <routeOrBranchId> --to <targetNodeId> --flow <flowId> --json
78
- ```
79
-
80
- For If/Else, order is behavior:
81
-
82
- ```bash
83
- cai branch reorder <ifElseNodeId> --order <conditionId1,conditionId2> --json
84
- ```
85
-
86
- List every condition branch exactly once and omit Else; the operation keeps Else last. Router routes cannot be reordered because every match runs. Do not simulate order by deleting a route or serializing its downstream nodes. Leave the fan-out unchanged unless the user explicitly authorizes a redesign.
87
-
88
- After edits, run `cai branch list` again. Treat its warnings about missing conditions or unwired paths as blockers: an unwired selected path can end with an apparently green execution.
89
-
90
- ## Loops
91
-
92
- Never assemble the standard per-item subflow pattern by hand:
93
-
94
- ```bash
95
- cai node get <listProducingNodeId> --json
96
- cai flow for-each \
97
- --flow <parentFlowId> \
98
- --from <listProducingNodeId> \
99
- --source-output <exactOutputId> \
100
- --name "<item flow name>" \
101
- --item-type <text|number|date|boolean|custom.type> \
102
- --json
103
- ```
104
-
105
- Use repeatable `--item-field <id:type[:display]>` only when creating a new `custom.*` item type.
106
-
107
- Copy `--source-output` from the selected node's exact `outputs[].id`. It is an opaque ID, not `displayName`, and there is no first-output default. This matters for Router, If/Else, and error outputs as well as ordinary action nodes.
108
-
109
- - If the source's whole output is the list, omit both `--list-field` and `--list-js`.
110
- - If the list is nested in the source payload, use `--list-field` with the dot-delimited path below `.data`; the operation derives the not-yet-created loop input binding deterministically.
111
- - Use `--list-js` only for an advanced exact expression. For multiline or shell-sensitive source, use `--js-file <path|->` instead. Both are mutually exclusive with `--list-field` and with each other; copy the wrapper/binding shape from expression context rather than guessing it.
112
-
113
- MCP: pass expression source inline as `listJs` to `flow_for_each_scaffold`; the server cannot read a local `--js-file` path.
114
-
115
- - The runtime cap defaults to 500 items and accepts 1 through 10000. Set `--max-items` explicitly when the product requirement differs. The scaffold operation creates the core graph first, then materializes and verifies this optional prop. The cap is all-or-nothing at runtime: a list larger than the cap throws and NOTHING iterates — there is no partial truncation.
116
-
117
- Changing an existing loop: update its loop-node props directly:
118
-
119
- ```bash
120
- cai node set <loopNodeId> --prop maxItems --value <n> --json
121
- cai node set <loopNodeId> --prop continueOnIterationError --value true --json
122
- ```
123
-
124
- MCP: use `node_prop_set` for both writes.
125
-
126
- This is not `cai node continue-on-error`, which configures the node-level Error output — a different feature.
127
-
128
- Read every returned warning. A list/item type mismatch means the scaffold is not verified even though it was created. Follow the returned `nextSteps` in order.
129
-
130
- Inside loop mappings, `item` exists only where context exposes it. Inside the body flow, use its `flow.<itemInput>` binding. Downstream loop results are iteration records whose declared child outputs are under `.outputs`.
131
-
132
- Loops run sequentially. `--continue-on-error` applies to a failed child iteration; a list expression or mapping/config expression that fails before the child starts still aborts the loop. Test empty, one-item, several-item, over-limit, and intended iteration-error behavior.
133
-
134
- Never enable continue-on-error on a money path; charges, purchases, transfers, refunds, and other spend must fail fast.
135
-
136
- ## Subflows and convergence
137
-
138
- Define the called flow's inputs and outputs first. Then initialize a Run flow component and bind it:
139
-
140
- ```bash
141
- cai node set-flow <runFlowNodeId> --flow <calledFlowId> --json
142
- cai node props <runFlowNodeId> --json
143
- ```
144
-
145
- MCP: `cai node set-flow` maps to `node_subflow_set`.
146
-
147
- Setting the target flow creates mapping props from the target's current inputs. If those inputs later change, run `set-flow` again and reconfigure mappings.
148
-
149
- For one action after whichever exclusive branch runs, use `registry_flow-merge` (Join Paths) in `chooseActivePath` mode, then place the shared action after it. Never wire several branches directly to an ordinary node or duplicate the shared action. Follow the exact recipe in [patterns.md](patterns.md) and test each branch once.
150
-
151
- Return semantics: a flow that should return data needs `registry_flow-return-response`; without it callers see empty success. Declare outputs first, initialize that exact component after the producer, then bind its generated prop with `cai expr set`. Do not guess component keys. See [patterns.md](patterns.md). Multiple completed Return nodes merge, with later values overwriting earlier ones; design for one Return per path unless merging is intended.
152
-
153
- Deleting a flow removes its graph, contract, triggers, and caller links. Validate and restore if mistaken. Turning `continueOnError` off also deletes its Error output and edges; validate and restore likewise.
154
-
155
- Read [patterns.md](patterns.md) for concise canonical patterns.
156
-
157
- ## Before you finish
158
-
159
- ```bash
160
- cai branch list <controlNodeId> --json
161
- cai workflow validate --json
162
- cai run flow <flowId> --inputs <values.json> --json
163
- cai exec tree <workflowExecutionId> --json
164
- ```
165
-
166
- - Every branch has the intended condition and a wired target.
167
- - If/Else ordering matches priority and Else remains last.
168
- - Router fan-out is intentional when more than one condition can be true.
169
- - Loop item type and list expression compile against actual source data.
170
- - Expected true, false/Else, empty-list, one-item, several-item, over-limit, and error paths are tested.
171
- - The execution tree shows intended `only_when_false` or `upstream_skipped` reasons and no unexplained missing nodes.
@@ -1,118 +0,0 @@
1
- # Controller AI integration actions
2
-
3
-
4
- Ready integration actions give a hosted agent one app operation without first building a workflow. Use the canonical operations below through the selected transport; do not call Controller AI REST routes directly.
5
-
6
-
7
- - Attach one existing app operation directly; never wrap it in a one-node workflow. See Choose direct action or workflow.
8
- - Capture the exact integration, action, connection, and tool IDs. See Discover exact identities.
9
- - Decide confirmation before attachment, especially for writes or spend. See Decide approval before attaching.
10
- - Verify persisted direct-action events, not a workflow execution ID. See Verify through the hosted agent.
11
-
12
- ## Choose direct action or workflow
13
-
14
- Use a direct action for one existing operation such as sending one message, creating one record, or adding one row.
15
-
16
- Use a workflow tool instead when the capability needs multiple operations, transformations, branches, loops, durable state, triggers, a custom typed contract, or reuse outside the agent. Do not build a one-node workflow when the same ready action already fits.
17
-
18
- ## Discover exact identities
19
-
20
- ```bash
21
- cai integration search "<app or capability>" --json
22
- cai integration actions "<operation>" --integration <integrationSlug> --json
23
- cai integration action --integration <integrationSlug> --key <actionKey> --json
24
- ```
25
-
26
- Preserve each returned `nodeSlug` and `key` pair exactly, then inspect that action's static props before using it. Never derive either identifier from a display label. Narrow the query or raise `--limit` when more matches exist.
27
-
28
- ## Select a connection
29
-
30
- ```bash
31
- cai connection list --integration <integrationSlug> --json
32
- ```
33
-
34
- Use an active connection whose `integration` exactly matches the integration slug. If none exists:
35
-
36
- ```bash
37
- cai connection connect <integrationSlug>
38
- ```
39
-
40
- MCP: call `connection_connect_url`, show its `url` as a markdown link, ask the user to finish, then confirm with `connection_list` using its `integration`; it has no `open` or `wait` parameter.
41
-
42
- The connection handoff keeps provider credentials out of the agent context. The CLI may open the URL; MCP returns it. Re-list connections after the user finishes, then attach the action.
43
-
44
- Rename or change the default with `cai connection update <id> [--name "<displayName>"] [--default|--no-default]` (MCP: `connection_update`); delete only intentionally with `cai connection delete <id> --yes` (MCP: `connection_delete`).
45
- Deleting a connection immediately removes authorization from bound nodes and agent tools.
46
-
47
- A missing connection is never a reason to substitute `registry_http-http-request` for a catalog component. For a workflow node, add the real component and leave it unconnected; `missingConnection: true` is a valid handoff state. For a direct action, preserve the selected integration and action and stop at the connection handoff. Give the user the returned connection URL; on CLI, `cai connection connect <slug>` obtains it. A raw endpoint mentioned by the user is background context, not an instruction to hand-roll the call.
48
-
49
- ## Decide approval before attaching
50
-
51
- Attached actions run without a human approval step unless `requireConfirmation` is `true`. When attaching an action that sends, posts, spends, or deletes, choose `true` or explicitly tell the user that it will run unattended. Mention the confirmation choice in the report to the user.
52
-
53
- - Prefer confirmation for permission changes, irreversible writes, or an ambiguous destination.
54
- - Usually omit confirmation for read-only retrieval and low-risk, reversible operations the user clearly asked the agent to perform autonomously.
55
-
56
- ```bash
57
- cai agent add-action <agentId> \
58
- --integration <integrationSlug> \
59
- --action <actionKey> \
60
- --connection <connectionId> \
61
- --description "Use when ..." \
62
- --require-confirmation \
63
- --json
64
- ```
65
-
66
- MCP: call `agent_tool_action_add`; `requireConfirmation` is optional and defaults to `false`. On CLI, `--require-confirmation` selects `true` and omission preserves the `false` default. Do not attach the same action twice.
67
-
68
- Read the saved grant back:
69
-
70
- ```bash
71
- cai agent tools <agentId> --json
72
- ```
73
-
74
- Verify `toolType`, integration, action key, connection, schema tier, description, and confirmation policy. The hosted runtime configures dynamic action properties when it invokes the action; the builder does not preconfigure runtime-only action arguments here.
75
-
76
- To rebind the live authorization grant or change a versioned policy:
77
-
78
- ```bash
79
- cai agent update-tool <agentId> --tool <toolId> --connection <connectionId> --json
80
- cai agent update-tool <agentId> --tool <toolId> --require-confirmation --json
81
- cai agent update-tool <agentId> --tool <toolId> --no-confirmation --json
82
- ```
83
-
84
- MCP: call `agent_tool_update`; use `connectionId` and `requireConfirmation: true` or `requireConfirmation: false`.
85
-
86
- `--connection` can redirect the published action immediately because authorization is live-bound. Description and confirmation-policy edits remain draft changes until the agent is published.
87
-
88
- ## Verify through the hosted agent
89
-
90
- Use a representative draft conversation:
91
-
92
- ```bash
93
- cai agent test <agentId> --draft --message "<request>" --json
94
- cai agent conversation history <conversationId> --json
95
- ```
96
-
97
- MCP: call `agent_test` with `rail: draft` and `waitSeconds`.
98
-
99
- A direct action runs inline and does not create a workflow execution ID. Evidence is the persisted conversation, its direct-action tool/run events, the returned result, and any observed external side effect. Never invent a workflow execution ID for it.
100
-
101
- If confirmation is required, inspect the pending action and exact arguments before approving or denying its request. A successful assistant reply alone does not prove the intended action ran.
102
-
103
- ## Publishing and sharing semantics
104
-
105
- Publishing snapshots the action identity, description, and approval policy into the agent version. The connection and authorization grant remain live security bindings: rebinding can redirect the published action, while breakage, revocation, or detachment can stop it immediately without another agent publish. Detachment also removes the draft tool; publish afterward to remove the unavailable tool from the published list.
106
-
107
- Organization members use the published agent, never its mutable draft. Direct actions run through the agent owner's connected external account. Controller AI usage draws from the organization's shared usage pool, regardless of which member calls the agent. State that credential boundary before organization sharing.
108
-
109
- ## Before you finish
110
-
111
- ```bash
112
- cai agent tools <agentId> --json
113
- cai agent conversation history <conversationId> --json
114
- ```
115
-
116
- - Tools must show one intended direct attachment with the exact returned identities, connection, description, and confirmation policy.
117
- - History must contain the persisted action event, exact arguments, and result; confirm any requested external effect separately.
118
- - No wrapper workflow should exist unless the capability actually required orchestration or reuse.