# klieo-workflow
Declarative no-code workflow substrate for the klieo agent framework.
Part of the [klieo](https://crates.io/crates/klieo) Rust agent framework.
Author an agent workflow as data (a `WorkflowDef`, typically JSON emitted by a
visual builder) and lower it into a runnable `klieo_flows::Flow` with
`compile` — no Rust compile step per workflow.
## Usage
```toml
[dependencies]
klieo-workflow = "3"
```
Via the umbrella crate:
```toml
[dependencies]
klieo = { version = "3", features = ["workflow"] }
```
## Envelope model
State is a single shared JSON **object** — the *envelope* — threaded through
every node. Each node reads one named field (`input_from`) as its input and
writes its result to another named field (`output_to`), returning the whole
envelope. Downstream nodes read the fields their predecessors wrote. The
compiled flow rejects a non-object run input with a typed error rather than
silently coercing it.
## Node palette
| `agent` | A data-configured `SimpleAgent` against a registered model | `agent: { model, system_prompt, tools? }` |
| `tool` | A registered tool, dispatched by id through the context | `tool: <id>` |
| `subflow` | A prebuilt `Flow` registered under an id | `ref: <id>` |
Every node also carries `input_from` / `output_to` (agent and tool nodes).
## The Registry is the authorization boundary
`compile` validates a `WorkflowDef` against a `Registry` — the allow-list of
primitives a workflow may reference. A model id must be registered
(`with_model`), a tool id must be allowed (`with_tool`), and a subflow id must
be registered (`with_subflow`). Anything a def references that is not in the
registry is rejected at compile time with a typed `CompileError`. This is the
authorization boundary for no-code workflows: authoring data cannot reach a
primitive an operator did not register.
## Linear example
```json
{
"id": "greet",
"entry": "greet",
"nodes": [
{ "id": "greet", "kind": "agent", "input_from": "q", "output_to": "r1",
"agent": { "model": "default", "system_prompt": "Greet the user." } },
{ "id": "elaborate", "kind": "agent", "input_from": "r1", "output_to": "r2",
"agent": { "model": "default", "system_prompt": "Add a follow-up." } }
],
"edges": [ { "from": "greet", "to": "elaborate" } ]
}
```
## Branching example
An edge is either unconditional (`to`) or conditional (`when` → `then` /
`else`); the two forms are mutually exclusive. A `when` is a bounded
comparison (`eq | ne | gt | gte | lt | lte | exists | contains`) over one
envelope field.
```json
{
"id": "triage",
"entry": "score",
"nodes": [
{ "id": "score", "kind": "subflow", "ref": "risk_scorer" },
{ "id": "review", "kind": "subflow", "ref": "human_review" },
{ "id": "approve", "kind": "subflow", "ref": "auto_approve" }
],
"edges": [
{ "from": "score",
"when": { "field": "risk", "op": "gte", "value": 0.8 },
"then": "review", "else": "approve" }
]
}
```
## Limits
- No human-in-the-loop, loop, or parallel node kinds; the palette is
`agent | tool | subflow`.
- At most one outgoing edge per node.
- A per-node model must be registered in the `Registry`; there is no implicit
default model.
- The top-level run input must be a JSON object.
## License
MIT