@controllerai/cli 0.2.14 → 0.2.16
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.
- package/README.md +13 -4
- package/dist/assistantHosts.js +234 -0
- package/dist/assistantHosts.js.map +1 -0
- package/dist/commands/agent.js +2 -0
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/agentSetup.js +627 -0
- package/dist/commands/agentSetup.js.map +1 -0
- package/dist/commands/connect.js +29 -26
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/connection.js +98 -13
- package/dist/commands/connection.js.map +1 -1
- package/dist/commands/data.js +26 -15
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/docs.js +95 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/doctor.js +357 -13
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/exec.js +284 -133
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/integration.js +170 -49
- package/dist/commands/integration.js.map +1 -1
- package/dist/commands/node.js +11 -287
- package/dist/commands/node.js.map +1 -1
- package/dist/commands/organize.js +182 -219
- package/dist/commands/organize.js.map +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/skills.js +2 -3
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/trigger.js +14 -8
- package/dist/commands/trigger.js.map +1 -1
- package/dist/commands/upgrade.js +69 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/workflow.js +203 -61
- package/dist/commands/workflow.js.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/nodeProps.js +1 -1
- package/dist/nodeProps.js.map +1 -1
- package/package.json +2 -2
- package/skills/controllerai-agents/SKILL.md +26 -18
- package/skills/controllerai-agents/references/instructions-and-versioning.md +2 -0
- package/skills/controllerai-ai-nodes/SKILL.md +9 -5
- package/skills/controllerai-ai-nodes/references/selection-and-testing.md +2 -0
- package/skills/controllerai-data/SKILL.md +17 -7
- package/skills/controllerai-data/references/data-rails.md +4 -2
- package/skills/controllerai-expressions/SKILL.md +17 -5
- package/skills/controllerai-expressions/references/dsl-and-recipes.md +5 -1
- package/skills/controllerai-flow-control/SKILL.md +11 -4
- package/skills/controllerai-flow-control/references/patterns.md +4 -0
- package/skills/controllerai-integration-actions/SKILL.md +20 -12
- package/skills/controllerai-node-config/SKILL.md +30 -9
- package/skills/controllerai-node-config/references/prop-and-readiness.md +9 -3
- package/skills/controllerai-runs/SKILL.md +20 -10
- package/skills/controllerai-runs/references/debugging.md +11 -7
- package/skills/controllerai-start/SKILL.md +36 -8
- package/skills/controllerai-start/references/mcp-tool-map.md +170 -0
- package/skills/controllerai-triggers/SKILL.md +13 -7
- package/skills/controllerai-triggers/references/lifecycle.md +5 -3
- package/skills/controllerai-workflows/SKILL.md +16 -10
- package/skills/controllerai-workflows/references/command-map.md +21 -6
- package/skills/manifest.json +79 -73
|
@@ -5,7 +5,7 @@ description: Define Controller AI custom data types and safely read, write, impo
|
|
|
5
5
|
|
|
6
6
|
# Controller AI data
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The `cai ...` examples are canonical grammar. Use the transport selected in `controllerai-start` and its MCP tool map; never mix transports in one task.
|
|
9
9
|
|
|
10
10
|
Controller AI has two separate concerns:
|
|
11
11
|
|
|
@@ -52,7 +52,9 @@ cai schema create custom.customer \
|
|
|
52
52
|
|
|
53
53
|
Creating the schema creates/synchronizes its collection. `cai data collection create` is only an alias for schema creation.
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
MCP: `cai data collection create` is merged into `schema_type_create`.
|
|
56
|
+
|
|
57
|
+
`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).
|
|
56
58
|
|
|
57
59
|
## How schema changes affect stored records
|
|
58
60
|
|
|
@@ -93,13 +95,15 @@ cai data query --type custom.customer --env dev --json
|
|
|
93
95
|
cai data create --type custom.customer --fields '{"email":"a@example.com","score":5}' --env dev --json
|
|
94
96
|
```
|
|
95
97
|
|
|
98
|
+
MCP: every data write requires explicit `env`; reads default to `dev` when `env` is omitted.
|
|
99
|
+
|
|
96
100
|
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.
|
|
97
101
|
|
|
98
102
|
`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.
|
|
99
103
|
|
|
100
104
|
## Delete records explicitly
|
|
101
105
|
|
|
102
|
-
Record deletion is
|
|
106
|
+
Record deletion is available only through the control surface; no workflow data node exposes it. Delete one known record or an explicit set of IDs:
|
|
103
107
|
|
|
104
108
|
```bash
|
|
105
109
|
cai data delete <recordId> --type custom.customer --env dev --json
|
|
@@ -112,18 +116,22 @@ Delete every record in a collection only with explicit user intent; `--all` also
|
|
|
112
116
|
cai data delete-many --type custom.customer --all --yes --env dev --json
|
|
113
117
|
```
|
|
114
118
|
|
|
115
|
-
|
|
119
|
+
MCP: `data_record_delete_many` requires explicit `env: dev|live` and exactly one of `recordIds` or `all: true`; it has no `yes` parameter.
|
|
120
|
+
|
|
121
|
+
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.
|
|
116
122
|
|
|
117
123
|
## Import and promote
|
|
118
124
|
|
|
119
125
|
Validate CSV before writing:
|
|
120
126
|
|
|
121
127
|
```bash
|
|
122
|
-
cai data import --type custom.customer --file customers.csv --mode dryRun --json
|
|
123
|
-
cai data import --type custom.customer --file customers.csv --mode commit --json
|
|
128
|
+
cai data import --type custom.customer --file customers.csv --mode dryRun --env dev --json
|
|
129
|
+
cai data import --type custom.customer --file customers.csv --mode commit --env dev --json
|
|
124
130
|
```
|
|
125
131
|
|
|
126
|
-
|
|
132
|
+
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.
|
|
133
|
+
|
|
134
|
+
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.
|
|
127
135
|
|
|
128
136
|
To seed live from development:
|
|
129
137
|
|
|
@@ -133,6 +141,8 @@ cai data copy-to-live --type custom.customer --json
|
|
|
133
141
|
cai data collections --env live --json
|
|
134
142
|
```
|
|
135
143
|
|
|
144
|
+
MCP: call `data_copy_to_live` with the explicit acknowledgement `env: live`.
|
|
145
|
+
|
|
136
146
|
The live schema must exist through a publish before copying. Treat copy-to-live as a production mutation and report counts/results.
|
|
137
147
|
|
|
138
148
|
## Workflow expressions
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
Use `cai data copy-to-live --type <custom.type>` only for an intentional data promotion.
|
|
12
12
|
|
|
13
|
+
MCP: `data_copy_to_live` requires the explicit acknowledgement `env: live`.
|
|
14
|
+
|
|
13
15
|
## Safe schema update
|
|
14
16
|
|
|
15
17
|
1. `cai schema get <type> --json`
|
|
@@ -25,9 +27,9 @@ Field IDs are contracts. Prefer keeping an ID stable and changing only its displ
|
|
|
25
27
|
## Destructive operations
|
|
26
28
|
|
|
27
29
|
- `data delete` soft-deletes one record.
|
|
28
|
-
- `data delete-many --all --yes` is broad
|
|
30
|
+
- `data delete-many --all --yes` is broad and REST-backed on either rail. With `--env live`, the deletion immediately changes what the published workflow reads.
|
|
29
31
|
- `schema delete` removes access to the whole collection and breaks its type references.
|
|
30
|
-
- CSV `--mode commit` appends rows.
|
|
32
|
+
- CSV `--mode commit` appends rows on the selected rail; a live commit is immediately visible to the published workflow. `--mode dryRun` writes nothing on either rail.
|
|
31
33
|
- `copy-to-live` changes production data.
|
|
32
34
|
|
|
33
35
|
Resolve exact workflow, environment, type, and record targets before these actions. Do not use a broad deletion to repair a schema mistake.
|
|
@@ -5,7 +5,7 @@ description: Author, validate, persist, and debug Controller AI expressions for
|
|
|
5
5
|
|
|
6
6
|
# Controller AI expressions
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The `cai ...` examples are canonical grammar. Use the transport selected in `controllerai-start` and its MCP tool map; never mix transports in one task.
|
|
9
9
|
|
|
10
10
|
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.
|
|
11
11
|
|
|
@@ -24,7 +24,7 @@ An empty input is not "nothing" — it evaluates to the type's empty form: empty
|
|
|
24
24
|
- **A filter comparing against an empty value still filters — and matches NO records.** This is deliberate (fail-closed; matching everything by accident would be worse). If "empty should mean skip this filter", opt that constraint into ignore-empty mode explicitly (`ignoreEmptyConstraints`, see [references/dsl-and-recipes.md](references/dsl-and-recipes.md)).
|
|
25
25
|
- **An optional TEXT input left empty arrives as `""`**, so an `equals` constraint against it matches nothing — the classic symptom is "search finds zero results only when the optional filter is blank", which then cascades (e.g. a create-if-missing step makes duplicates).
|
|
26
26
|
|
|
27
|
-
When a run unexpectedly returns zero results, check the resolved configs for empty comparison values first (`cai exec
|
|
27
|
+
When a run unexpectedly returns zero results, check the resolved configs for empty comparison values first (`cai exec node <nodeExecutionId> --full`).
|
|
28
28
|
|
|
29
29
|
## Required write loop
|
|
30
30
|
|
|
@@ -61,9 +61,11 @@ When compilation reports an unknown field, copy a typeable display-derived acces
|
|
|
61
61
|
Get the current operation catalog when needed:
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
cai expr ops --type <text|number|date|list|boolean|file|custom> --json
|
|
64
|
+
cai expr ops --workflow <workflowId> --type <text|number|date|list|boolean|file|custom> --json
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
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`.
|
|
68
|
+
|
|
67
69
|
Keep the program to optional `const` declarations followed by one final expression. Do not use loops, assignments, `new`, regular expressions, optional chaining, arbitrary library methods, or object construction unless the current compiler catalog explicitly supports them.
|
|
68
70
|
|
|
69
71
|
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.
|
|
@@ -78,14 +80,20 @@ For a node prop, prefer:
|
|
|
78
80
|
cai expr set <nodeId> --prop <propName> --js '<expression>' --json
|
|
79
81
|
```
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
MCP: use `node_prop_set` with `js`; it merges `cai expr set` with `cai node set`.
|
|
84
|
+
|
|
85
|
+
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`.
|
|
82
86
|
|
|
83
87
|
```bash
|
|
84
88
|
cai expr set <nodeId> --prop <propName> --js-file expression.js --json
|
|
85
89
|
```
|
|
86
90
|
|
|
91
|
+
MCP: pass the file contents inline as `js`; the hosted process does not read the local path or stdin.
|
|
92
|
+
|
|
87
93
|
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>`.
|
|
88
94
|
|
|
95
|
+
MCP: repair with `node_optional_prop_set` and `enabled: false`.
|
|
96
|
+
|
|
89
97
|
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.
|
|
90
98
|
|
|
91
99
|
For a side-effect-free check:
|
|
@@ -110,13 +118,17 @@ cai expr decompile <nodeId> --prop <propName> --json
|
|
|
110
118
|
cai run node <nodeId> --inputs <values.json> --json
|
|
111
119
|
```
|
|
112
120
|
|
|
121
|
+
MCP: `run_node` takes the parsed `inputs` object and `waitSeconds` (default 60, max 120).
|
|
122
|
+
|
|
113
123
|
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:
|
|
114
124
|
|
|
115
125
|
```bash
|
|
116
126
|
cai run flow <flowId> --inputs <values.json> --json
|
|
117
127
|
```
|
|
118
128
|
|
|
119
|
-
|
|
129
|
+
MCP: `run_flow` takes the parsed `inputs` object and `waitSeconds` (default 60, max 120).
|
|
130
|
+
|
|
131
|
+
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.
|
|
120
132
|
|
|
121
133
|
## Hard rules
|
|
122
134
|
|
|
@@ -27,6 +27,8 @@ cai expr validate <httpNodeId> --prop <jsonBodyProp> --js-file body-expression.j
|
|
|
27
27
|
cai expr set <httpNodeId> --prop <jsonBodyProp> --js-file body-expression.js --json
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
MCP: pass the file contents inline as `js`; the set call uses `node_prop_set`.
|
|
31
|
+
|
|
30
32
|
## Same-type fallback
|
|
31
33
|
|
|
32
34
|
```js
|
|
@@ -115,6 +117,8 @@ Confirm the exact binding and field path in context.
|
|
|
115
117
|
5. `cai expr decompile ...`
|
|
116
118
|
6. `cai run node ... --json` for isolated destination configuration
|
|
117
119
|
7. `cai run flow <flowId> ... --json` when the expression consumes a wired upstream input
|
|
118
|
-
8. `cai exec
|
|
120
|
+
8. `cai exec node <nodeExecutionId> --full --json`
|
|
121
|
+
|
|
122
|
+
MCP: step 4 uses `node_prop_set`; pass expression source and run inputs inline, and set `waitSeconds` on `run_node` or `run_flow` from 0 through 120.
|
|
119
123
|
|
|
120
124
|
If compilation succeeds but runtime is null or partially empty, inspect `runScope`, the actual input value, and `resolvedConfigs`; do not keep rewriting syntax blindly. A node-only run cannot prove an `inputs.<name>` binding because it does not execute upstream nodes.
|
|
@@ -5,7 +5,7 @@ description: Design and configure Controller AI execution paths using only-when
|
|
|
5
5
|
|
|
6
6
|
# Controller AI flow control
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The `cai ...` examples are canonical grammar. Use the transport selected in `controllerai-start` and its MCP tool map; never mix transports in one task.
|
|
9
9
|
|
|
10
10
|
Choose the primitive from the intended execution semantics before adding nodes.
|
|
11
11
|
|
|
@@ -73,7 +73,7 @@ For If/Else, order is behavior:
|
|
|
73
73
|
cai branch reorder <ifElseNodeId> --order <conditionId1,conditionId2> --json
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
List every condition branch exactly once and omit Else; the
|
|
76
|
+
List every condition branch exactly once and omit Else; the operation keeps Else last. Router routes cannot be reordered because every match runs.
|
|
77
77
|
|
|
78
78
|
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.
|
|
79
79
|
|
|
@@ -97,9 +97,12 @@ Use repeatable `--item-field <id:type[:display]>` only when creating a new `cust
|
|
|
97
97
|
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.
|
|
98
98
|
|
|
99
99
|
- If the source's whole output is the list, omit both `--list-field` and `--list-js`.
|
|
100
|
-
- If the list is nested in the source payload, use `--list-field` with the dot-delimited path below `.data`; the
|
|
100
|
+
- 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.
|
|
101
101
|
- 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.
|
|
102
|
-
|
|
102
|
+
|
|
103
|
+
MCP: pass expression source inline as `listJs` to `flow_for_each_scaffold`; the server cannot read a local `--js-file` path.
|
|
104
|
+
|
|
105
|
+
- 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.
|
|
103
106
|
|
|
104
107
|
Changing an existing loop: update its loop-node props directly:
|
|
105
108
|
|
|
@@ -108,6 +111,8 @@ cai node set <loopNodeId> --prop maxItems --value <n> --json
|
|
|
108
111
|
cai node set <loopNodeId> --prop continueOnIterationError --value true --json
|
|
109
112
|
```
|
|
110
113
|
|
|
114
|
+
MCP: use `node_prop_set` for both writes.
|
|
115
|
+
|
|
111
116
|
This is not `cai node continue-on-error`, which configures the node-level Error output — a different feature.
|
|
112
117
|
|
|
113
118
|
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.
|
|
@@ -127,6 +132,8 @@ cai node set-flow <runFlowNodeId> --flow <calledFlowId> --json
|
|
|
127
132
|
cai node props <runFlowNodeId> --json
|
|
128
133
|
```
|
|
129
134
|
|
|
135
|
+
MCP: `cai node set-flow` maps to `node_subflow_set`.
|
|
136
|
+
|
|
130
137
|
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.
|
|
131
138
|
|
|
132
139
|
When paths converge, inspect the concrete graph and execution behavior. Do not assume wiring two paths to one action creates a barrier or waits for both. If the requirement is a true join, discover a join-capable component and verify it with representative branch timing.
|
|
@@ -40,6 +40,8 @@ One order may take all three routes. If that is not acceptable, use If/Else.
|
|
|
40
40
|
|
|
41
41
|
Use `cai flow for-each` to create the item flow, typed item input, loop node, and list wiring atomically. First run `cai node get <sourceNodeId> --json`, choose the intended `outputs[]` entry, and pass its exact opaque `id` as `--source-output`; never pass its label or assume the first output. Omit list-selection flags when the whole selected output payload is a list; use `--list-field <nested.path>` for a list below `.data`; reserve `--list-js` for an exact advanced expression or `--js-file <path|->` for that expression in a UTF-8 file/stdin. The three selection forms are mutually exclusive.
|
|
42
42
|
|
|
43
|
+
MCP: call `flow_for_each_scaffold` and pass expression source inline as `listJs`.
|
|
44
|
+
|
|
43
45
|
The runtime limit defaults to 500 items and supports 1 through 10000. Use `--max-items` when the intended bound differs, then verify the returned read-back. Put meaningful work inside the generated body flow and declare body outputs before consuming aggregate results.
|
|
44
46
|
|
|
45
47
|
Decide explicitly whether one child-flow failure should stop the sequential loop. Use `--continue-on-error` only when partial completion is acceptable and downstream logic handles failed iteration records. It does not recover a list or mapping expression that fails before an iteration starts.
|
|
@@ -52,6 +54,8 @@ Continue-on-error is for steps whose failure must not block the main path — no
|
|
|
52
54
|
cai node continue-on-error <nodeId> --enable --json
|
|
53
55
|
```
|
|
54
56
|
|
|
57
|
+
MCP: call `node_continue_on_error_set` with `enabled: true` or `enabled: false`.
|
|
58
|
+
|
|
55
59
|
Wiring the Error output is OPTIONAL — add it when a failure needs explicit handling, not as a precondition:
|
|
56
60
|
|
|
57
61
|
```bash
|
|
@@ -5,9 +5,9 @@ description: Discover, connect, attach, update, and verify ready integration act
|
|
|
5
5
|
|
|
6
6
|
# Controller AI integration actions
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The `cai ...` examples are canonical grammar. Use the transport selected in `controllerai-start` and its MCP tool map; never mix transports in one task.
|
|
9
9
|
|
|
10
|
-
Ready integration actions give a hosted agent one app operation without first building a workflow. Use
|
|
10
|
+
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.
|
|
11
11
|
|
|
12
12
|
## Choose direct action or workflow
|
|
13
13
|
|
|
@@ -19,10 +19,11 @@ Use a workflow tool instead when the capability needs multiple operations, trans
|
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
cai integration search "<app or capability>" --json
|
|
22
|
-
cai integration actions <
|
|
22
|
+
cai integration actions "<operation>" --integration <integrationSlug> --json
|
|
23
|
+
cai integration action --integration <integrationSlug> --key <actionKey> --json
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
Preserve
|
|
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.
|
|
26
27
|
|
|
27
28
|
## Select a connection
|
|
28
29
|
|
|
@@ -30,25 +31,28 @@ Preserve the returned integration `slug` and action `key` exactly. Never derive
|
|
|
30
31
|
cai connection list --integration <integrationSlug> --json
|
|
31
32
|
```
|
|
32
33
|
|
|
33
|
-
Use an active connection whose `
|
|
34
|
+
Use an active connection whose `integration` exactly matches the integration slug. If none exists:
|
|
34
35
|
|
|
35
36
|
```bash
|
|
36
37
|
cai connection connect <integrationSlug>
|
|
37
38
|
```
|
|
38
39
|
|
|
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.
|
|
40
41
|
|
|
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.
|
|
42
48
|
|
|
43
49
|
## Decide approval before attaching
|
|
44
50
|
|
|
45
|
-
|
|
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.
|
|
46
52
|
|
|
47
|
-
-
|
|
53
|
+
- Prefer confirmation for permission changes, irreversible writes, or an ambiguous destination.
|
|
48
54
|
- Usually omit confirmation for read-only retrieval and low-risk, reversible operations the user clearly asked the agent to perform autonomously.
|
|
49
55
|
|
|
50
|
-
Tell the user which policy was selected and why when it has a meaningful consequence.
|
|
51
|
-
|
|
52
56
|
```bash
|
|
53
57
|
cai agent add-action <agentId> \
|
|
54
58
|
--integration <integrationSlug> \
|
|
@@ -59,7 +63,7 @@ cai agent add-action <agentId> \
|
|
|
59
63
|
--json
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
|
|
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.
|
|
63
67
|
|
|
64
68
|
Read the saved grant back:
|
|
65
69
|
|
|
@@ -77,6 +81,8 @@ cai agent update-tool <agentId> --tool <toolId> --require-confirmation --json
|
|
|
77
81
|
cai agent update-tool <agentId> --tool <toolId> --no-confirmation --json
|
|
78
82
|
```
|
|
79
83
|
|
|
84
|
+
MCP: call `agent_tool_update`; use `connectionId` and `requireConfirmation: true` or `requireConfirmation: false`.
|
|
85
|
+
|
|
80
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.
|
|
81
87
|
|
|
82
88
|
## Verify through the hosted agent
|
|
@@ -88,6 +94,8 @@ cai agent test <agentId> --draft --message "<request>" --json
|
|
|
88
94
|
cai agent conversation history <conversationId> --json
|
|
89
95
|
```
|
|
90
96
|
|
|
97
|
+
MCP: call `agent_test` with `rail: draft` and `waitSeconds`.
|
|
98
|
+
|
|
91
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.
|
|
92
100
|
|
|
93
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.
|
|
@@ -5,7 +5,7 @@ description: Discover, initialize, configure, connect, and verify Controller AI
|
|
|
5
5
|
|
|
6
6
|
# Controller AI node configuration
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The `cai ...` examples are canonical grammar. Use the transport selected in `controllerai-start` and its MCP tool map; never mix transports in one task.
|
|
9
9
|
|
|
10
10
|
A node is ready only after its component, connection, dependency-ordered props, wiring, and output type are valid. Treat configuration as a state machine, not a bag of property writes.
|
|
11
11
|
|
|
@@ -14,15 +14,15 @@ A node is ready only after its component, connection, dependency-ordered props,
|
|
|
14
14
|
### 1. Find an exact component
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
cai
|
|
18
|
-
cai
|
|
17
|
+
cai integration actions "<capability>" --json
|
|
18
|
+
cai integration action --integration <nodeSlug> --key <nodeKey> --json
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Use the returned `nodeSlug` and full `
|
|
21
|
+
Use the returned `nodeSlug` and full `key`. Do not infer a component key from its display name. If discovery is broad, narrow it with:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
cai
|
|
25
|
-
cai
|
|
24
|
+
cai integration search "<integration>" --actions --json
|
|
25
|
+
cai integration actions "<action>" --integration <nodeSlug> --json
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
Inspect static prop metadata before creating the node. `required`, `remoteOptions`, and `reloadProps` determine configuration order.
|
|
@@ -35,12 +35,19 @@ If the component requires auth:
|
|
|
35
35
|
cai connection list --node <nodeSlug> --json
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
- One active connection: use its exact `connectionId`.
|
|
38
|
+
- One active connection: use its exact `id` as the `connectionId`.
|
|
39
39
|
- Several: use an explicit user preference; otherwise ask which account.
|
|
40
|
-
- None: add the real component without `--connection`; `missingConnection: true` is a valid handoff state.
|
|
40
|
+
- None: add the real component without `--connection`; `missingConnection: true` is a valid handoff state. On CLI, get the link with `cai connection connect <nodeSlug> --no-open` and share it right away; do not block on `--wait` before sharing.
|
|
41
|
+
|
|
42
|
+
MCP: call `connection_connect_url` with `nodeSlug`, show its `url` as a markdown link, ask the user to finish, then confirm with `connection_list` using its `integration`.
|
|
43
|
+
|
|
44
|
+
After they confirm, run `cai connection list --integration <nodeSlug> --json` and bind the new ID with `cai node set-connection <nodeId> --connection <connectionId>`.
|
|
41
45
|
|
|
42
46
|
Never substitute a connection from a similar slug.
|
|
43
47
|
|
|
48
|
+
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`).
|
|
49
|
+
Deleting a connection immediately removes authorization from bound nodes and agent tools.
|
|
50
|
+
|
|
44
51
|
A missing connection is never a reason to substitute `registry_http-http-request` for a catalog component. A raw endpoint mentioned by the user is background context, not an instruction to hand-roll the call.
|
|
45
52
|
|
|
46
53
|
### 3. Initialize once
|
|
@@ -56,6 +63,8 @@ cai node init \
|
|
|
56
63
|
--json
|
|
57
64
|
```
|
|
58
65
|
|
|
66
|
+
MCP: use `node_add`; `cai node init` is the CLI alias of `cai node add`.
|
|
67
|
+
|
|
59
68
|
Omit flags that do not apply. `--after` creates the ordinary main/error wire at creation. Use it when later props need upstream data. Initialization also accepts `--props`, but use it only for static, non-option values already present in the inspected component metadata. Never inline a `remoteOptions` value, a reload selector and its dependents, or a prop that has not materialized yet; initialize first and set those sequentially. Use `--enable-prop <name...>` only for optional props confirmed by component metadata.
|
|
60
69
|
|
|
61
70
|
Capture the returned `nodeId`; do not rediscover it by name.
|
|
@@ -91,6 +100,8 @@ Static value:
|
|
|
91
100
|
cai node set <nodeId> --prop <exactName> --value '<literal-or-json>' --json
|
|
92
101
|
```
|
|
93
102
|
|
|
103
|
+
MCP: use `node_prop_set` with `value`; it also handles expression writes through `js`.
|
|
104
|
+
|
|
94
105
|
Optional prop:
|
|
95
106
|
|
|
96
107
|
```bash
|
|
@@ -99,8 +110,12 @@ cai node set <nodeId> --prop <exactName> --value '<literal-or-json>' --json
|
|
|
99
110
|
# or: cai expr set <nodeId> --prop <exactName> --js '<expression>' --json
|
|
100
111
|
```
|
|
101
112
|
|
|
113
|
+
MCP: `cai expr set` is the `js` form of `node_prop_set`.
|
|
114
|
+
|
|
102
115
|
`node set` and `expr set` auto-enable a catalog-only optional prop, refresh its metadata, then write. Do not run a separate enable step first. If validation or the later write fails, the prop stays enabled; repair with `cai node disable-prop <nodeId> --prop <exactName>` and re-read props. This is also the repair for the Update-data wipe footgun: that action writes every enabled field, so disable any accidentally enabled field before running it.
|
|
103
116
|
|
|
117
|
+
MCP: use `node_optional_prop_set` with `enabled: false` to disable or `enabled: true` to enable.
|
|
118
|
+
|
|
104
119
|
Remote or dynamic option:
|
|
105
120
|
|
|
106
121
|
```bash
|
|
@@ -114,7 +129,7 @@ cai node set <nodeId> \
|
|
|
114
129
|
|
|
115
130
|
`node options` also supports `--prev-context '<context JSON>'` from the preceding page and repeatable `--for '<earlierProp>=<value>'` overrides for read-only hypothetical resolution; JSON literals are accepted and bare text stays a string. If options are `blocked_upstream`, both `node options` and `node set` name the blocking prop and print the exact `cai node set` command to run first.
|
|
116
131
|
|
|
117
|
-
For an ordinary remote option, preserve both fields from the same returned option. The label is not cosmetic:
|
|
132
|
+
For an ordinary remote option, preserve both fields from the same returned option. The label is not cosmetic: it is used to search providers that do not index opaque IDs, then the returned value is verified before writing. For a multi-select, pass a JSON array of returned values and one `--label` argument per value, in the same order. The write supplies the persisted label shape, chooses the correct expression storage channel, and verifies read-back.
|
|
118
133
|
|
|
119
134
|
When an earlier selector makes the prop `dynamic_upstream`, no option list exists. Use a known exact ID or an expression:
|
|
120
135
|
|
|
@@ -146,14 +161,20 @@ cai node status <nodeId> --json
|
|
|
146
161
|
cai run node <nodeId> --inputs <values.json> --json
|
|
147
162
|
```
|
|
148
163
|
|
|
164
|
+
MCP: `run_node` takes the parsed `inputs` object and `waitSeconds` (default 60, max 120).
|
|
165
|
+
|
|
149
166
|
If the node has a wired input or consumes `inputs.<name>`, the node test is only an isolated configuration check. Run `cai run flow <flowId> --inputs <values.json> --json` before declaring its upstream expressions verified.
|
|
150
167
|
|
|
168
|
+
MCP: `run_flow` takes the parsed `inputs` object and `waitSeconds` (default 60, max 120).
|
|
169
|
+
|
|
151
170
|
If a dynamic provider action cannot be run but its response is documented, declare an unverified output contract:
|
|
152
171
|
|
|
153
172
|
```bash
|
|
154
173
|
cai node declare-output <nodeId> --sample-file provider-response.json --json
|
|
155
174
|
```
|
|
156
175
|
|
|
176
|
+
MCP: call `node_output_type_declare` with the parsed provider response in `sample`; the server cannot read `provider-response.json`.
|
|
177
|
+
|
|
157
178
|
The sample must be a JSON object or a non-empty JSON array of provider items. Re-read status and expression context afterward; the first real completed run verifies the declaration.
|
|
158
179
|
|
|
159
180
|
Read [references/prop-and-readiness.md](references/prop-and-readiness.md) when a prop is dynamic, option-backed, expression-capable, or unexpectedly absent.
|
|
@@ -13,7 +13,7 @@ The JSON response reports:
|
|
|
13
13
|
- `readyForExpressions`: connection and required/reload props are complete. The source still must be wired before its binding appears in expression context.
|
|
14
14
|
- `readyForRun`: connection and required/reload props are complete; running may be the step that discovers a concrete output type.
|
|
15
15
|
- `readyForDownstreamExpressions`: the node is runnable and its actual process output type is concrete enough for downstream field bindings.
|
|
16
|
-
- `next`: the
|
|
16
|
+
- `next`: the canonical next diagnostic command.
|
|
17
17
|
|
|
18
18
|
Follow `next`, then read status again. Do not treat `readyForRun` as proof that downstream fields are typed; require `readyForDownstreamExpressions` for that.
|
|
19
19
|
|
|
@@ -26,6 +26,8 @@ cai node set-connection <nodeId> --connection <connectionId> --json
|
|
|
26
26
|
cai node status <nodeId> --json
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
MCP: replace `cai connection connect` with `connection_connect_url { nodeSlug }`; the other calls map directly.
|
|
30
|
+
|
|
29
31
|
## Prop channels
|
|
30
32
|
|
|
31
33
|
Use `cai node set`; it selects the persisted shape from live metadata.
|
|
@@ -36,6 +38,8 @@ Use `cai node set`; it selects the persisted shape from live metadata.
|
|
|
36
38
|
| Dynamic text, JSON text, or custom data | `--js` | `valueJs` |
|
|
37
39
|
| Dynamic non-text prop with expression mode | `--js` | `expressionValueJs` |
|
|
38
40
|
|
|
41
|
+
MCP: both `cai node set` and `cai expr set` use `node_prop_set`; pass exactly one of `value`, `js`, or `rawExpressionJson`.
|
|
42
|
+
|
|
39
43
|
Do not force a channel with internal fields. `cai node set` and `cai expr set` compile first and verify the actual channel after writing.
|
|
40
44
|
|
|
41
45
|
## Option-backed props
|
|
@@ -55,7 +59,7 @@ cai node options <nodeId> --prop <name> --query "<specific search>" --json
|
|
|
55
59
|
cai node set <nodeId> --prop <name> --value '<option.value>' --label '<option.label>' --json
|
|
56
60
|
```
|
|
57
61
|
|
|
58
|
-
Pass the value and label from the same live option. Remote providers often search labels rather than opaque values, so omitting `--label` can make a valid copied ID impossible to re-verify. For multi-select, pass a JSON array of returned values plus one `--label` argument per value in matching order. The
|
|
62
|
+
Pass the value and label from the same live option. Remote providers often search labels rather than opaque values, so omitting `--label` can make a valid copied ID impossible to re-verify. For multi-select, pass a JSON array of returned values plus one `--label` argument per value in matching order. The write constructs the required array of `{value,label}` records for read-back/UI compatibility.
|
|
59
63
|
|
|
60
64
|
Continue a provider page with the opaque context printed by the previous call:
|
|
61
65
|
|
|
@@ -103,4 +107,6 @@ An unresolved output blocks trustworthy downstream field expressions. Resolve it
|
|
|
103
107
|
|
|
104
108
|
After the run, read `cai node status` and `cai expr context` again. Do not infer fields from an example response alone if the platform did not adopt a concrete type.
|
|
105
109
|
|
|
106
|
-
|
|
110
|
+
Completed node and flow runs pass the exact node-execution ID back for ownership, workflow, node, component-snapshot, and terminal-status checks before deriving the persisted output shape; callers never supply the type name. Adoption updates only that non-static node, so separate instances of generic components such as HTTP can learn different schemas. Inspect `outputTypeAdoption`/`outputTypeAdoptions` in the run result. If it reports `no_observed_output`, use a representative input that returns data and rerun. If it reports `error`, stop and surface that error rather than inventing a schema.
|
|
111
|
+
|
|
112
|
+
MCP: `run_node` and `run_flow` take `inputs` inline and `waitSeconds` from 0 through 120.
|
|
@@ -5,7 +5,7 @@ description: Run and debug Controller AI nodes on development and flows on devel
|
|
|
5
5
|
|
|
6
6
|
# Controller AI runs
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The `cai ...` examples are canonical grammar. Use the transport selected in `controllerai-start` and its MCP tool map; never mix transports in one task.
|
|
9
9
|
|
|
10
10
|
Think "real run", not "simulation". `run node` always uses development; `run flow` defaults to development and can target the published live version. Both rails execute real integration actions (messages send, records change, provider usage is spent); development isolation covers the data store only. Running is a required part of building — one successful run is how a dynamic node's output type gets resolved for the next step — so mitigate risk before running.
|
|
11
11
|
|
|
@@ -23,6 +23,8 @@ If the user declines running the action, accept it. Then keep the build honest:
|
|
|
23
23
|
cai node declare-output <nodeId> --sample-file shape.json
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
MCP: call `node_output_type_declare` with parsed JSON in `sample`; the server cannot read `shape.json`.
|
|
27
|
+
|
|
26
28
|
— where `shape.json` is a representative response object from the provider's API reference. The contract is marked `declared (unverified)`; the first real completed run verifies and upgrades it automatically. State clearly that it is unverified until then, and make the first live run the explicit verification step. Never silently skip the question and never present an unrun action as verified.
|
|
27
29
|
|
|
28
30
|
## First live run is part of development
|
|
@@ -33,6 +35,8 @@ After publishing, run the published version directly under supervision:
|
|
|
33
35
|
cai run flow <flowId> --inputs values.json --target live --confirm-live --json
|
|
34
36
|
```
|
|
35
37
|
|
|
38
|
+
MCP: pass parsed values in `inputs` and set `waitSeconds` on `run_flow`.
|
|
39
|
+
|
|
36
40
|
This command fires external actions and spends usage. For risky sends, build the safe-audience harness: branch on the `isLiveVersion` data source with If/Else — the false branch targets the developer (own email/channel), the true branch targets the real audience. Use this direct live run for explicit verification; do not wait for a real trigger event.
|
|
37
41
|
|
|
38
42
|
## Prepare exact run inputs
|
|
@@ -42,7 +46,7 @@ cai run inputs <nodeId> --json
|
|
|
42
46
|
cai run inputs <flowId> --flow --json
|
|
43
47
|
```
|
|
44
48
|
|
|
45
|
-
Use returned opaque input IDs. Flow mode lists EVERY declared input with a `used` flag: supply the used ones (an unsupplied used input falls back to its saved test value), while values keyed to unused inputs are accepted and ignored.
|
|
49
|
+
Use returned opaque input IDs. Flow mode lists EVERY declared input with a `used` flag: supply the used ones (an unsupplied used input falls back to its saved test value), while values keyed to unused inputs are accepted and ignored. The input object can be a flat map; the CLI may load it from a file:
|
|
46
50
|
|
|
47
51
|
```json
|
|
48
52
|
{
|
|
@@ -65,9 +69,11 @@ cai run node <nodeId> --inputs values.json --json
|
|
|
65
69
|
cai run flow <flowId> --inputs values.json --json
|
|
66
70
|
```
|
|
67
71
|
|
|
72
|
+
MCP: pass parsed values in `inputs` and set `waitSeconds` on `run_node` or `run_flow`.
|
|
73
|
+
|
|
68
74
|
`run node` is deliberately isolated: it does not execute wired upstream nodes. Read its `runScope`. If `endToEndValidationRequired` is true, values derived from upstream can be empty even when the selected node is green; do not accept that run as expression proof. `run flow` is the end-to-end check that executes upstream wiring.
|
|
69
75
|
|
|
70
|
-
The CLI waits for a terminal execution state. Capture `workflowExecutionId` and `nodeExecutionId` from the result.
|
|
76
|
+
The CLI waits for a terminal execution state by default. MCP polling is bounded by `waitSeconds` and may return an unfinished execution ID. Capture `workflowExecutionId` and `nodeExecutionId` from the result.
|
|
71
77
|
|
|
72
78
|
For every node test, inspect:
|
|
73
79
|
|
|
@@ -75,7 +81,7 @@ For every node test, inspect:
|
|
|
75
81
|
- `skipReason`
|
|
76
82
|
- `runScope`, especially `upstreamNodesExecuted` and `endToEndValidationRequired`
|
|
77
83
|
- `resolvedConfigs`
|
|
78
|
-
- `resolvedConfigs[].resolutionWarnings`: trust every structured entry; `[]` is authoritatively clean.
|
|
84
|
+
- `resolvedConfigs[].resolutionWarnings`: trust every structured entry; `[]` is authoritatively clean. Also read any transport warnings; CLI runs may mirror them on stderr.
|
|
79
85
|
- `outputTypeAdoption`: the server verifies the exact completed `nodeExecutionId`, derives its persisted output shape, and updates only that non-static node. `adopted` or `already_typed` lets the next `node status` converge; `no_observed_output` means the representative run returned no reusable shape. `adoptedNodeIds` must be empty or contain only the tested node; stop if it names a sibling.
|
|
80
86
|
- output shape and values
|
|
81
87
|
|
|
@@ -84,14 +90,16 @@ A dynamic prop with a null/empty resolved config is a failure even when status i
|
|
|
84
90
|
## Debug from the execution tree
|
|
85
91
|
|
|
86
92
|
```bash
|
|
87
|
-
cai exec tree <workflowExecutionId> --json
|
|
93
|
+
cai exec tree <workflowExecutionId> --limit 50 --json
|
|
88
94
|
cai exec get <workflowExecutionId> --json
|
|
89
95
|
cai exec flow <flowExecutionId> --json
|
|
90
|
-
cai exec node <nodeExecutionId> --json
|
|
91
|
-
cai exec ports <nodeExecutionId> --json
|
|
96
|
+
cai exec node <nodeExecutionId> --full --json
|
|
92
97
|
```
|
|
93
98
|
|
|
94
|
-
|
|
99
|
+
Page with the returned `nextCursor` via `--cursor`; narrow large runs with repeatable `--status`
|
|
100
|
+
or `--flow <flowId>`. Use the tree to locate the first unexpected status. Use the full node
|
|
101
|
+
execution to inspect its status, resolved configuration, and untruncated output. Read
|
|
102
|
+
[references/debugging.md](references/debugging.md) for the fault-isolation order.
|
|
95
103
|
|
|
96
104
|
## Output discovery
|
|
97
105
|
|
|
@@ -106,13 +114,15 @@ cai run discover \
|
|
|
106
114
|
--json
|
|
107
115
|
```
|
|
108
116
|
|
|
109
|
-
|
|
117
|
+
MCP: `run_discover` always removes the scratch graph and exposes neither `keep` nor `waitSeconds`.
|
|
118
|
+
|
|
119
|
+
This calls the real third-party action and spends usage. For a destructive action, do not probe merely to learn its output — first ask the user for a safe target (see "Consequential actions" above); if they decline, declare the expected shape instead. On CLI, use `--keep` only when the user wants the scratch node retained.
|
|
110
120
|
|
|
111
121
|
After successful output discovery, re-read `cai node status` and downstream `cai expr context`; verify the adopted output type is concrete.
|
|
112
122
|
|
|
113
123
|
## Completion gate
|
|
114
124
|
|
|
115
|
-
-
|
|
125
|
+
- CLI scripts may gate on `cai workflow validate` exiting 1; MCP callers inspect `valid` and `errors`.
|
|
116
126
|
- The test exercised the changed path with credible values.
|
|
117
127
|
- The first unexpected node, if any, was identified rather than inferred from the final error.
|
|
118
128
|
- All dynamic configs resolved to intended values.
|