# Expressive Language: Node Kinds, Binding, State, Routes, Examples
Continues from [`README.md`](README.md): node kinds, binding to Rust,
the state model, routes, policies, comments/strings, safety, examples,
formatting, testkit, and implementation milestones.
## Node Kinds
Initial built-in node kinds:
### `agent`
Uses the harness agent loop or one model call depending on config.
Supported fields:
- `model`
- `system`
- `prompt`
- `tools`
- `routes`
- `retry`
- `timeout`
### `model`
Single model invocation. Does not automatically execute tools.
Supported fields:
- `model`
- `system`
- `prompt`
- `routes`
- `retry`
- `timeout`
### `tool_executor`
Executes tool calls already present in state.
Supported fields:
- `tools`
- `next`
- `retry`
- `timeout`
### `router`
Routes based on a named route function provided from Rust.
Supported fields:
- `routes`
- `metadata`
### `subgraph`
Calls another compiled graph.
Supported fields:
- `graph`
- `next`
- `routes`
### `subagent`
Calls a registered harness agent as a graph node.
Supported fields:
- `agent`
- `input`
- `next`
- `routes`
- `retry`
- `timeout`
- `steering`
Example:
```tinyagents
node research {
kind subagent
agent "researcher"
steering {
parent allow ["add_instruction", "request_status", "cancel"]
human allow ["add_instruction", "pause", "resume", "cancel"]
delivery "safe_boundary"
}
next synthesize
}
```
Steering policies lower into harness steering policy and graph task policy. They
can narrow a child agent's model/tool/runtime limits but cannot grant
capabilities absent from the registry or parent run policy.
### `repl_agent`
Runs a registered REPL script or model-driven CodeAct loop through the harness
REPL runtime.
Supported fields:
- `model`
- `script`
- `tools`
- `routes`
- `retry`
- `timeout`
### `interrupt`
Emits a resumable human-in-the-loop interrupt.
Supported fields:
- `prompt`
- `options`
- `routes`
- `metadata`
### `join`
Waits for named upstream nodes or barrier channels before continuing.
Supported fields:
- `sources`
- `next`
- `timeout`
## Binding To Rust
The language should not define arbitrary Rust closures. Instead, it should bind
to Rust-provided registries:
```rust
let workflow = LanguageCompiler::new()
.with_models(models)
.with_tools(tools)
.with_node_templates(templates)
.compile_source("support.rag", source)?;
```
Registries:
- model registry
- tool registry
- agent registry
- node template registry
- route function registry
- reducer registry
- graph registry for subgraphs
- store registry
- middleware registry
- REPL script registry
When a graph is generated by a REPL session, the session may call the compiler
with source text or an AST, but the compiler must use the same registries and
policy checks. Generated source can request capabilities only from the allowed
set attached to the parent run or registry namespace.
This keeps source files declarative and prevents unsafe dynamic execution.
## State Model
Version 1 should keep state Rust-owned. The language can refer to standard
channels by convention and bind them to registered reducers:
- `messages`
- `tool_calls`
- `structured_response`
- `metadata`
- `artifacts`
- `candidates`
- `usage`
- `interrupts`
Example:
```tinyagents
channel messages messages
channel candidates append
channel usage aggregate "usage_delta"
channel review overwrite
```
Future versions may add state schema declarations:
```tinyagents
state SupportState {
messages: messages append
customer_id: string overwrite
ticket_id: string? overwrite
}
```
State schemas should be delayed until reducer-based graph execution exists.
## Routes
Routes are named outcomes.
```tinyagents
routes {
tool_call -> tools
final -> END
escalate -> human_review
}
```
Rules:
- route names are unique per node
- route targets must exist or be `END`
- route names are ASCII identifiers
- a node may use `routes` or `next`, not both
- `END` is reserved
Future typed route support can generate Rust enums from route declarations.
## Policies
Node-level policies:
```tinyagents
node agent {
timeout 30s
retry {
max_attempts: 3
backoff: "exponential"
}
}
```
Graph-level defaults:
```tinyagents
graph support_agent {
defaults {
timeout 60s
recursion_limit 50
}
}
```
Policies lower into graph node policies and harness request policies.
## Comments And Strings
Comments:
```tinyagents
// line comment
```
Strings:
```tinyagents
system "single line"
prompt """
multi-line prompt
"""
```
Multi-line strings should preserve content exactly except for one predictable
dedent rule.
## Safety
The language must be safe to parse and validate from untrusted text.
Safety rules:
- no arbitrary code execution
- no filesystem access from language source
- no network access from language source
- no dynamic provider lookup without registry binding
- no environment variable interpolation in v1
- bounded parser recursion
- bounded source size
## Examples
### Minimal Model Graph
```tinyagents
graph summarize {
start model
node model {
kind model
model "default"
system "Summarize the user request."
next END
}
}
```
### Agent With Tools
```tinyagents
graph support_agent {
start agent
node agent {
kind agent
model "default"
system "Resolve support requests using tools when useful."
tools ["lookup_user", "create_ticket"]
routes {
tool_call -> tools
final -> END
}
}
node tools {
kind tool_executor
next agent
}
}
```
### Human Review
```tinyagents
graph approval_flow {
start draft
node draft {
kind model
model "default"
system "Draft a response."
next review
}
node review {
kind interrupt
prompt "Approve this response?"
routes {
approved -> send
rejected -> draft
}
}
node send {
kind tool_executor
tools ["send_email"]
next END
}
}
```
## Formatting
Formatter goals:
- stable ordering within declarations
- preserve comments
- normalize indentation to two spaces
- one item per line for lists longer than one entry
- avoid rewriting prompt body content
The formatter can come after parser and diagnostics.
## Testkit
`language::testkit` should include:
- parse snapshot helper
- diagnostic snapshot helper
- compile helper with fake registries
- golden source fixtures
- round-trip formatter tests once formatter exists
## Implementation Milestones
### L1: Parser Skeleton
- token model
- spans
- lexer
- parser for graph, start, node, next, routes
### L2: Diagnostics
- structured diagnostics
- duplicate node validation
- unknown route target validation
### L3: Compiler Preview
- compile topology into `GraphBuilder`
- support `kind model`
- support `kind agent`
- bind model names
### L4: Tool Binding
- validate tool names
- compile `tool_executor`
- add agent/tool graph example
### L5: Policies And Subgraphs
- parse timeout and retry
- bind subgraphs
- compile node policies
### L6: Channels, Commands, And Fanout
- parse channel declarations
- bind reducer registry entries
- lower `command` and `sends`
- compile join/barrier nodes
### L7: Agent-Authored Graphs
- compile generated source under parent run policy
- require review gates when policy marks generated graphs as untrusted
- store generated blueprint provenance
- expose graph diff and preview diagnostics
### L8: Formatter
- stable formatting
- golden tests