diff --git a/AGENTS.md b/AGENTS.md
index 091c50a1..3a61e387 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,11 +4,12 @@
TinyAgents is a Rust 2024 library crate rooted at `Cargo.toml`. Public API
exports live in `src/lib.rs`, with the crate-wide error type in `src/error.rs`.
-The five surfaces each live in their own module directory: `src/graph/`
+The four surfaces each live in their own module directory: `src/graph/`
(durable typed state graphs), `src/harness/` (provider-neutral model calls,
tools, middleware, streaming), `src/language/` (the declarative `.rag`
-blueprint format), `src/registry/` (the named capability catalog), and
-`src/repl/` (the imperative `.ragsh` session runtime).
+blueprint format), and `src/registry/` (the named capability catalog).
+Scripted, interpreter-backed orchestration (a CodeAct/REPL loop) is a host
+concern and is deliberately not implemented here.
Prefer small, focused modules that do one thing extremely well. New feature
areas should live in module directories instead of accumulating broad,
@@ -18,18 +19,18 @@ dedicated `types.rs` file and keep module-local unit tests in a dedicated
smallest useful API.
Two Cargo features gate optional dependencies: `sqlite` (embedded SQLite
-checkpointer, `graph::checkpoint::SqliteCheckpointer`) and `repl` (embedded
-Rhai engine backing `repl::session`); every other provider and surface is
+checkpointer, `graph::checkpoint::SqliteCheckpointer`) and `tools` (the builtin
+generic tool family, `harness::tools`); every other provider and surface is
compiled in by default.
Integration tests are in `tests/`, covering serialization, graph routing,
-registry binding, the expressive and REPL languages, streaming, subagents,
+registry binding, the expressive language, streaming, subagents,
and provider contracts (including live, network-gated tests such as
`tests/live_*.rs`). Runnable usage examples are in `examples/`, especially
`examples/basic_graph.rs`. Design notes and module-level specifications live
in `docs/`, with `docs/spec/README.md` as the top-level architecture
reference and `docs/modules/` holding per-surface design docs (`graph/`,
-`harness/`, `registry/`, `expressive-language/`, `repl-language/`). A `wiki/`
+`harness/`, `registry/`, `expressive-language/`). A `wiki/`
git submodule holds the published GitHub wiki pages; do not edit it as part
of unrelated work, and commit its pointer update separately when it does
change.
diff --git a/Cargo.lock b/Cargo.lock
index 69eccb00..852d1962 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,20 +2,6 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "ahash"
-version = "0.8.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
-dependencies = [
- "cfg-if",
- "const-random",
- "getrandom 0.3.4",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
[[package]]
name = "aho-corasick"
version = "1.1.5"
@@ -142,26 +128,6 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
-[[package]]
-name = "const-random"
-version = "0.1.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
-dependencies = [
- "const-random-macro",
-]
-
-[[package]]
-name = "const-random-macro"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
-dependencies = [
- "getrandom 0.2.17",
- "once_cell",
- "tiny-keccak",
-]
-
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@@ -177,12 +143,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "crunchy"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
-
[[package]]
name = "crypto-common"
version = "0.2.2"
@@ -726,15 +686,6 @@ dependencies = [
"windows-sys 0.61.2",
]
-[[package]]
-name = "no-std-compat"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
-dependencies = [
- "spin",
-]
-
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -749,9 +700,6 @@ name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
-dependencies = [
- "portable-atomic",
-]
[[package]]
name = "percent-encoding"
@@ -789,12 +737,6 @@ version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
-[[package]]
-name = "portable-atomic"
-version = "1.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
-
[[package]]
name = "potential_utf"
version = "0.1.5"
@@ -991,35 +933,6 @@ dependencies = [
"webpki-roots",
]
-[[package]]
-name = "rhai"
-version = "1.25.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd4dd0f8c36625202a4ba553c416c19b719947cd2a31d1bda06126e4a5727daf"
-dependencies = [
- "ahash",
- "bitflags",
- "no-std-compat",
- "num-traits",
- "once_cell",
- "rhai_codegen",
- "smallvec",
- "smartstring",
- "thin-vec",
- "web-time",
-]
-
-[[package]]
-name = "rhai_codegen"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cd3a7535e50bf36857e7be7bec276d334e8c2dfa469c2201226fd01638ea5ca"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.118",
-]
-
[[package]]
name = "ring"
version = "0.17.14"
@@ -1197,16 +1110,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
-dependencies = [
- "errno",
- "libc",
-]
-
[[package]]
name = "siphasher"
version = "1.0.3"
@@ -1225,17 +1128,6 @@ version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
-[[package]]
-name = "smartstring"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
-dependencies = [
- "autocfg",
- "static_assertions",
- "version_check",
-]
-
[[package]]
name = "socket2"
version = "0.6.4"
@@ -1246,12 +1138,6 @@ dependencies = [
"windows-sys 0.61.2",
]
-[[package]]
-name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
[[package]]
name = "sqlite-wasm-rs"
version = "0.5.5"
@@ -1270,12 +1156,6 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
[[package]]
name = "subtle"
version = "2.6.1"
@@ -1337,12 +1217,6 @@ dependencies = [
"windows-sys 0.61.2",
]
-[[package]]
-name = "thin-vec"
-version = "0.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0f7e269b48f0a7dd0146680fa24b50cc67fc0373f086a5b2f99bd084639b482"
-
[[package]]
name = "thiserror"
version = "2.0.19"
@@ -1363,15 +1237,6 @@ dependencies = [
"syn 3.0.2",
]
-[[package]]
-name = "tiny-keccak"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
-dependencies = [
- "crunchy",
-]
-
[[package]]
name = "tinyagents"
version = "2.1.0"
@@ -1384,7 +1249,6 @@ dependencies = [
"futures",
"regex",
"reqwest",
- "rhai",
"rusqlite",
"serde",
"serde_json",
@@ -1430,7 +1294,6 @@ dependencies = [
"libc",
"mio",
"pin-project-lite",
- "signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
@@ -1594,12 +1457,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
[[package]]
name = "want"
version = "0.3.1"
diff --git a/Cargo.toml b/Cargo.toml
index a6dd03da..6f068777 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@ name = "tinyagents"
version = "2.1.0"
edition = "2024"
license = "GPL-3.0-only"
-description = "A recursive language-model (RLM) harness for Rust."
+description = "A durable agent + graph harness for Rust."
repository = "https://github.com/tinyhumansai/tinyagents"
readme = "README.md"
keywords = ["llm", "agents", "graph", "langchain", "langgraph"]
@@ -59,12 +59,6 @@ reqwest = { version = "0.12", default-features = false, features = [
# not add one, it does not resolve.
rusqlite = { version = "0.40", features = ["bundled"], optional = true }
-# Optional embedded Rhai scripting engine powering the `.ragsh` REPL session
-# runtime (`repl::session`). The `sync` feature makes the engine and its values
-# `Send + Sync` so a session can live inside an async graph node. Pulled in only
-# by the `repl` feature to keep the default build light.
-rhai = { version = "1", features = ["sync"], optional = true }
-
# Optional builtin tool family for deterministic time/date helpers.
# `serde` is required by `harness::session_store`, whose records carry
# `DateTime` timestamps across the serde boundary.
@@ -75,14 +69,6 @@ chrono-tz = { version = "0.10", optional = true }
default = []
# Embedded SQLite-backed checkpointer (`graph::checkpoint::SqliteCheckpointer`).
sqlite = ["dep:rusqlite"]
-# Embedded Rhai-backed `.ragsh` REPL session runtime (`repl::session`).
-repl = ["dep:rhai"]
-# Recursive-language-model runtime (`rlm`): a driver model writes code cells
-# executed in a sandboxed interpreter (embedded Rhai, or an external Python /
-# JavaScript process) whose only host surface is capability calls back into
-# the registry (`llm`, `tool`, `agent`). `tokio/process` + `tokio/io-util`
-# drive the external interpreter subprocesses.
-rlm = ["dep:rhai", "tokio/process", "tokio/io-util"]
# Builtin generic tools (`harness::tools`) kept out of the default dependency
# graph so host applications can choose whether they want these implementations.
tools = ["dep:chrono-tz"]
@@ -102,12 +88,3 @@ futures = "0.3"
# The OpenAI provider is always compiled, so these build with a plain
# `cargo build --examples`; they only need a network call + `OPENAI_API_KEY`
# at run time.
-
-# --- RLM examples (need the `rlm` feature) ---
-[[example]]
-name = "rlm_rhai"
-required-features = ["rlm"]
-
-[[example]]
-name = "rlm_python"
-required-features = ["rlm"]
diff --git a/README.md b/README.md
index b8bb8b86..5c9cb2c7 100644
--- a/README.md
+++ b/README.md
@@ -11,33 +11,18 @@
-**TinyAgents is a recursive language-model (RLM) harness for Rust.** It is a
-typed, durable runtime where language models call models, agents call agents,
+**TinyAgents is a durable agent and graph harness for Rust.** It is a typed,
+checkpointed runtime where language models call models, agents call agents,
graphs run graphs, and a model can author, compile, and run the very workflow it
is standing inside — all as inspectable, checkpointed, policy-checked Rust.
-## What is an RLM, and why recursive?
+## Recursion, without an embedded interpreter
Most agent frameworks stuff everything into one ever-growing context window and
-hope the model copes. **Recursive Language Models (RLMs)** take a different
-stance: a long prompt is treated as an external *environment* that the model
-explores through a REPL — examining it, decomposing it, and **recursively calling
-itself (or sub-models) over snippets** instead of swallowing the whole thing at
-once. This mitigates "context rot" and lets effective context exceed the raw
-window.
-
-The idea comes from recent research:
-
-- **Paper:** "Recursive Language Models," Alex L. Zhang, Tim Kraska, Omar Khattab
- (MIT CSAIL), 2025 — [arXiv:2512.24601](https://arxiv.org/abs/2512.24601)
-- **Blog:** Alex L. Zhang, "Recursive Language Models" —
-
-- **Reference implementation:**
-
-TinyAgents is **inspired by and architected around** the RLM execution model — a
-production-shaped Rust harness for building RLM-style systems. It does not claim
-to reproduce the paper's benchmark numbers; instead it brings the *execution
-model* to Rust as concrete, implemented surfaces:
+hope the model copes. TinyAgents takes the other stance: a long task is an
+external *environment* that gets decomposed, and the runtime is re-entrant, so a
+model can recurse over pieces of it instead of swallowing the whole thing at
+once. The concrete surfaces:
- **Sub-agents (agents calling agents).** A harness agent is exposed *as a tool*
to another agent, so orchestration is literally a model calling a model
@@ -45,22 +30,24 @@ model* to Rust as concrete, implemented surfaces:
- **Recursion policy + depth tracking.** The runtime tracks `root_run_id` /
`parent_run_id`, enforces a recursion limit, and rolls child runs' events,
usage, and cost up to the parent as first-class observable runs.
-- **Graphs that run graphs.** A node can embed another compiled graph, and the
- `.ragsh` REPL can drive a graph from inside a graph node (graph → REPL →
- graph).
-- **The REPL as the RLM core.** In `.ragsh`, context and prompts are runtime
- *values*, not just prompt text. The model writes small programs, inspects their
- output, calls sub-models / sub-agents / sub-graphs as functions, and iterates —
- the RLM/CodeAct loop.
+- **Graphs that run graphs.** A node can embed another compiled graph, so a
+ whole compiled workflow can appear as a single step inside another one.
- **Self-authoring (the deepest recursion).** A model can emit a `.rag`
blueprint that compiles through the *same* registry-bound compiler path as a
human-authored file, then runs on the *same* runtime the model is already
executing in. The harness can describe and re-enter itself.
-Two languages, one runtime: `.rag` (declarative blueprint) and `.ragsh`
-(imperative REPL) both lower into the exact same `graph` + `harness` types as
-hand-written Rust — a language whose programs *are* the runtime that interprets
-them.
+One language, one runtime: `.rag` blueprints lower into the exact same `graph` +
+`harness` types as hand-written Rust — a language whose programs *are* the
+runtime that interprets them.
+
+**Not in this crate, by design:** the scripted CodeAct/REPL loop — an embedded
+interpreter (Rhai, Python, JavaScript) executing model-written code cells whose
+only host surface is capability calls. That is a host concern. TinyAgents gives
+it everything it needs (the capability `registry`, the harness, typed
+`SessionId`/`CellId`/`CallId`, and the `repl_agent` node kind for binding a
+host-provided scripted node by name) without pulling an interpreter into your
+dependency graph.
## Features
@@ -72,12 +59,10 @@ them.
checkpoints, interrupts, subgraphs, streaming, topology export, and time
travel.
- **Registry** — a named capability catalog (models, tools, agents, graphs,
- stores, middleware, policy) that `.rag` and `.ragsh` bind by name.
+ stores, middleware, policy) that `.rag` binds by name.
- **`.rag` expressive language** — a declarative, side-effect-free blueprint
format that compiles (lexer → parser → compiler) into the runtime; the safe
boundary for agent-authored plans.
-- **`.ragsh` REPL language** — imperative, capability-bound interactive
- orchestration; the RLM/CodeAct loop surface.
- **Recursion & sub-agents** — agents-as-tools, subgraphs, depth tracking, and a
recursion policy so deep call trees stay bounded and observable.
- **Durability & checkpoints** — resume long runs, replay history, and travel
@@ -92,13 +77,13 @@ them.
## Architecture
```text
- +-----------------------+ +-----------------------+
- | .rag blueprint | | .ragsh REPL |
- | declarative workflow | | imperative RLM loop |
- +-----------+-----------+ +-----------+-----------+
- \ /
- \ compile / lower (by name) /
- v v
+ +-----------------------+
+ | .rag blueprint |
+ | declarative workflow |
+ +-----------+-----------+
+ |
+ | compile / lower (by name)
+ v
+-------------+ +-------------------------------------------+
| Application |------->| Capability Registry |
| Rust code | | models | tools | agents | graphs | policy |
@@ -148,7 +133,7 @@ The recursion loop — agents call agents, and graphs run graphs:
| | depth +1, recursion policy,
| | child run rolls up usage/cost
+-- loops back --+--- re-enters the runtime ---+
- to Agent Node (graph -> REPL -> graph)
+ to Agent Node (graph -> subgraph -> graph)
```
## Quick start
@@ -163,7 +148,7 @@ tinyagents = "1.5"
The OpenAI (and OpenAI-compatible) provider is compiled in by default; the
build stays offline unless you actually make a call. Two optional Cargo
features gate heavier dependencies: `sqlite` (embedded SQLite checkpointer)
-and `repl` (embedded Rhai engine for the `.ragsh` session runtime).
+and `tools` (the builtin generic tool family).
To explore locally:
@@ -276,12 +261,11 @@ OpenAI-backed examples require `OPENAI_API_KEY` at run time.
- [crates.io](https://crates.io/crates/tinyagents)
- [docs.rs API reference](https://docs.rs/tinyagents)
- [Wiki home](https://github.com/tinyhumansai/tinyagents/wiki)
- - [Recursion and the RLM model](https://github.com/tinyhumansai/tinyagents/wiki/Recursion-and-RLM)
+ - [Recursion and sub-agents](https://github.com/tinyhumansai/tinyagents/wiki/Recursion-and-RLM)
- [Harness](https://github.com/tinyhumansai/tinyagents/wiki/Harness)
- [Graph runtime](https://github.com/tinyhumansai/tinyagents/wiki/Graph-Runtime)
- [Registry](https://github.com/tinyhumansai/tinyagents/wiki/Registry)
- [Expressive language `.rag`](https://github.com/tinyhumansai/tinyagents/wiki/Expressive-Language-RAG)
- - [REPL language `.ragsh`](https://github.com/tinyhumansai/tinyagents/wiki/REPL-Language-RAGSH)
- [Providers](https://github.com/tinyhumansai/tinyagents/wiki/Providers)
- [Quick start](https://github.com/tinyhumansai/tinyagents/wiki/Quick-Start)
- [Examples](https://github.com/tinyhumansai/tinyagents/wiki/Examples)
@@ -320,7 +304,7 @@ for the configuration format.
## Contributing
TinyAgents welcomes focused contributions that improve the graph runtime,
-harness contracts, the registry, the `.rag` / `.ragsh` languages, provider
+harness contracts, the registry, the `.rag` language, provider
adapters, tests, examples, and documentation.
Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
diff --git a/docs/modules/expressive-language/README.md b/docs/modules/expressive-language/README.md
index 3a7ac20b..c73cf4d5 100644
--- a/docs/modules/expressive-language/README.md
+++ b/docs/modules/expressive-language/README.md
@@ -9,16 +9,17 @@ hand-written Rust. The graph runtime should not know whether a graph came from
Rust builders or a source file.
The language is also the safe serialization boundary for agent-authored graph
-plans. If a REPL agent proposes a new workflow, that proposal should become
+plans. If an orchestrating agent proposes a new workflow, that proposal should
+become
`.rag` source or an equivalent AST, then pass through the same parser, resolver,
registry binding, policy checks, and graph compiler as a human-authored file.
Generated topology must never be installed directly into the runtime.
This module is intentionally declarative. Interactive scripting and
-CodeAct-style recursive execution belong to the
-[REPL language module](../repl-language/README.md). A `.rag` file defines graph topology
-and bindings; a `.ragsh` session inspects, scripts, and orchestrates harness or
-graph calls through capability-bound functions.
+CodeAct-style recursive execution are host concerns and are not implemented in
+this crate. A `.rag` file defines graph topology and bindings; a host session
+inspects, scripts, and orchestrates harness or graph calls through
+capability-bound functions of its own.
For what the parser/compiler implement today versus what is still aspirational,
see [Implementation status](implementation-status.md).
@@ -33,8 +34,8 @@ see [Implementation status](implementation-status.md).
node templates through registries.
- Declare graph input/output shape, state channels, reducer policies, and
checkpoint/interrupt policy when the compiled graph supports them.
-- Declare commands, fanout sends, joins, subgraphs, sub-agents, and REPL-backed
- nodes without embedding arbitrary executable code.
+- Declare commands, fanout sends, joins, subgraphs, sub-agents, and
+ host-script-backed nodes without embedding arbitrary executable code.
- Produce inspectable blueprints for registries, UIs, documentation, tests, and
generated workflow review.
- Preserve source spans for clear errors.
@@ -110,13 +111,13 @@ The docs can still describe the language as TinyAgents source.
## Expressiveness Targets
The long-term language should cover the graph concepts proven useful in
-LangGraph, LangChain agent graphs, OpenHuman's state-machine harness, and RLM
+LangGraph, LangChain agent graphs, OpenHuman's state-machine harness, and CodeAct
style orchestration:
- graph defaults: recursion limits, timeouts, checkpointing, durability,
streaming modes, cache policy, steering policy, and concurrency
- capabilities: allowed models, tools, agents, graphs, stores, middleware,
- retrievers, route functions, node templates, and REPL scripts
+ retrievers, route functions, node templates, and host scripts
- state channels: messages, scratch state, tool calls, artifacts, candidates,
usage/cost deltas, interrupt payloads, and custom app fields
- reducers: last value, append, aggregate, topic, messages-by-id, barrier,
@@ -124,7 +125,7 @@ style orchestration:
- routing: direct edges, conditional routes, typed route labels, command goto,
`Send` fanout, joins/barriers, parent graph handoff, and terminal output
- execution nodes: model, agent loop, tool executor, subgraph, sub-agent,
- interrupt, router, map/fanout, join, and REPL agent
+ interrupt, router, map/fanout, join, and host-script agent
- observability: source name, graph id, node ids, tags, metadata, event stream
projections, generated-by provenance, and blueprint version
- safety: source size limits, policy allowlists, review gates for generated
diff --git a/docs/modules/expressive-language/implementation-status.md b/docs/modules/expressive-language/implementation-status.md
index de3afe9b..66fed799 100644
--- a/docs/modules/expressive-language/implementation-status.md
+++ b/docs/modules/expressive-language/implementation-status.md
@@ -50,7 +50,7 @@ Extended (H2):
- `agent "name"` — sub-agent reference for a `subagent` node (`NodeSpec::agent`).
- `graph "name"` — subgraph reference for a `subgraph` node
(`NodeSpec::subgraph`; binding prefers it over the legacy `model` field).
-- `script "name"` — REPL script capability for a `repl_agent` node
+- `script "name"` — host script capability for a `repl_agent` node
(`NodeSpec::script`). Declaration only — never inline code.
- `input "mapping"` — input mapping for sub-agent / subgraph nodes.
- `command { goto update { key value … } }` — typed command
diff --git a/docs/modules/expressive-language/reference.md b/docs/modules/expressive-language/reference.md
index 6aa5aa6a..07672302 100644
--- a/docs/modules/expressive-language/reference.md
+++ b/docs/modules/expressive-language/reference.md
@@ -118,8 +118,9 @@ node, where the policy is actually attached to the run's `SteeringHandle`.
### `repl_agent`
-Runs a registered REPL script or model-driven CodeAct loop through the harness
-REPL runtime.
+Runs a host-provided script or model-driven CodeAct loop, bound by name to a
+registered `Script` component. The node implementation is supplied by the host;
+this crate ships no interpreter.
Supported fields:
@@ -175,9 +176,10 @@ Registries:
- graph registry for subgraphs
- store registry
- middleware registry
-- REPL script registry
+- host script registry
-When a graph is generated by a REPL session, the session may call the compiler
+When a graph is generated by a host orchestration 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.
diff --git a/docs/modules/graph/README.md b/docs/modules/graph/README.md
index 6dc46230..cebc2106 100644
--- a/docs/modules/graph/README.md
+++ b/docs/modules/graph/README.md
@@ -70,15 +70,15 @@ Rust-specific precedent:
HITL, observability events, blueprints, and RPC run control.
- TinyAgents should preserve the ergonomic Rust builder surface from these
precedents, but the target graph runtime must also be rich enough to be
- generated from `.rag`, inspected by UIs, driven from `.ragsh`, and tested with
- deterministic state/channel snapshots.
+ generated from `.rag`, inspected by UIs, driven from a host orchestrator, and
+ tested with deterministic state/channel snapshots.
## Design Stance
The graph module is the stable execution contract below all graph authoring
surfaces. A graph may be built by Rust code, loaded from a `.rag` blueprint,
-compiled from a REPL cell, generated by an agent, or restored from a registry
-record. Once compiled, those origins must converge on the same immutable
+compiled from a host orchestration session, generated by an agent, or restored
+from a registry record. Once compiled, those origins must converge on the same immutable
`CompiledGraph` behavior:
- topology is validated before execution
@@ -86,7 +86,7 @@ record. Once compiled, those origins must converge on the same immutable
- state writes pass through channel reducers
- commands and `Send` packets are explicit runtime values
- checkpoints and pending writes are owned by the graph runtime
-- nested graph, sub-agent, and REPL calls preserve run hierarchy
+- nested graph and sub-agent calls preserve run hierarchy
- events contain enough source/origin metadata to explain generated graphs
Agent-authored graph definitions are allowed only as source for the expressive
diff --git a/docs/modules/registry/README.md b/docs/modules/registry/README.md
index f19c57a7..0827a075 100644
--- a/docs/modules/registry/README.md
+++ b/docs/modules/registry/README.md
@@ -118,7 +118,8 @@ live handles.
`ComponentKind` partitions the registry namespace and now has **12** variants.
Alongside `Model`, `Tool`, `Graph`, `Router`, `Reducer`, `Store`, `Agent`, and
-`Script` (a REPL script a `repl_agent` node may reference), four kinds cover the
+`Script` (a host-provided script a `repl_agent` node may reference by name),
+four kinds cover the
runtime's durable roles:
| Kind | `as_str` |
diff --git a/docs/modules/repl-language/README.md b/docs/modules/repl-language/README.md
deleted file mode 100644
index 92d0c8c7..00000000
--- a/docs/modules/repl-language/README.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# REPL Language Module Specification
-
-The REPL language is an interactive orchestration layer for TinyAgents. It is
-inspired by Recursive Language Models (`rlm`) and CodeAct-style agents, where a
-model can write small programs, inspect their output, call sub-models, and
-iterate until it has a final answer.
-
-This module is separate from the expressive language:
-
-- the expressive language (`.rag`) is a declarative graph definition format
-- the REPL language (`.ragsh`) is an imperative session language for inspecting,
- scripting, and recursively orchestrating harness and graph runs
-
-Both layers compile or lower into the same harness and graph runtime. Neither
-layer should bypass the model registry, tool registry, graph registry, event
-system, recursion policy, or run limits.
-
-## Detailed Module Docs
-
-- [Design](design.md)
- - [RLM feature map, embedding, safety, events, testkit](operations.md)
-
-## Responsibilities
-
-- Provide an interactive session runtime over harness and graph primitives.
-- Execute small scripts with a persistent namespace.
-- Expose registered models, agents, graphs, tools, stores, and context as
- capability-bound functions.
-- Let sessions draft, validate, inspect, diff, compile, and optionally register
- graph blueprints through the expressive-language compiler.
-- Support model-driven CodeAct loops where model output contains fenced REPL
- blocks.
-- Capture stdout, return values, state changes, model calls, tool calls, graph
- calls, errors, and final answers as typed events.
-- Support recursive sub-model, sub-agent, and sub-graph calls with depth
- tracking.
-- Support batched model, agent, and graph calls with bounded concurrency.
-- Preserve source spans and session history for diagnostics and replay.
-- Provide deterministic test utilities for scripted sessions.
-
-## Non-Responsibilities
-
-- It is not a replacement for the declarative graph language.
-- It is not a general-purpose unsafe host-code execution layer.
-- It does not provide direct filesystem, network, environment variable, or
- process access.
-- It does not own model provider logic.
-- It does not own graph topology or checkpointing.
-- It does not allow scripts to call unregistered tools or models.
-- It does not install model-generated graph topology directly into the runtime;
- generated graphs must pass through the `.rag` compiler and policy checks.
-
-## Recommended Direction
-
-Use Rhai for the first in-process REPL runtime and document Python as a future
-out-of-process compatibility sandbox. Rhai gives TinyAgents a Rust-native,
-capability-bound embedding surface, while Python remains useful for training and
-RLM-compatible workflows where the sandbox boundary is explicit.
-
-Recommended extension: `.ragsh`.
diff --git a/docs/modules/repl-language/design.md b/docs/modules/repl-language/design.md
deleted file mode 100644
index 168fc42a..00000000
--- a/docs/modules/repl-language/design.md
+++ /dev/null
@@ -1,469 +0,0 @@
-# REPL Language Module Specification
-
-Parent module: [REPL language](README.md).
-
-The REPL language is an interactive orchestration layer for TinyAgents. It is
-inspired by Recursive Language Models (`rlm`) and CodeAct-style agents, where a
-model can write small programs, inspect their output, call sub-models, and
-iterate until it has a final answer.
-
-This module is separate from the expressive language:
-
-- the expressive language (`.rag`) is a declarative graph definition format
-- the REPL language (`.ragsh`) is an imperative session language for inspecting,
- scripting, and recursively orchestrating harness and graph runs
-
-Both layers compile or lower into the same harness and graph runtime. Neither
-layer should bypass the model registry, tool registry, graph registry, event
-system, recursion policy, or run limits.
-
-## Source Inspiration
-
-Primary references:
-
-- `alexzhang13/rlm`:
-- RLM paper and docs linked from that repository
-- Rhai book:
-- Rhai sandboxing:
-- Rhai operation limits:
-- Rhai API docs:
-
-The useful idea from `rlm` is not Python itself. The useful idea is that context
-and intermediate state live in a persistent REPL namespace, while language-model
-calls, recursive sub-calls, and tools are exposed as functions inside that
-namespace.
-
-TinyAgents should preserve that programming model while making every capability
-explicit and typed at the Rust boundary.
-
-## Responsibilities
-
-- Provide an interactive session runtime over harness and graph primitives.
-- Execute small scripts with a persistent namespace.
-- Expose registered models, agents, graphs, tools, stores, and context as
- capability-bound functions.
-- Let sessions draft, validate, inspect, diff, compile, and optionally register
- graph blueprints through the expressive-language compiler.
-- Support model-driven CodeAct loops where model output contains fenced REPL
- blocks.
-- Capture stdout, return values, state changes, model calls, tool calls, graph
- calls, errors, and final answers as typed events.
-- Support recursive sub-model, sub-agent, and sub-graph calls with depth
- tracking.
-- Support batched model, agent, and graph calls with bounded concurrency.
-- Preserve source spans and session history for diagnostics and replay.
-- Provide deterministic test utilities for scripted sessions.
-
-## Non-Responsibilities
-
-- It is not a replacement for the declarative graph language.
-- It is not a general-purpose unsafe host-code execution layer.
-- It does not provide direct filesystem, network, environment variable, or
- process access.
-- It does not own model provider logic.
-- It does not own graph topology or checkpointing.
-- It does not allow scripts to call unregistered tools or models.
-- It does not install model-generated graph topology directly into the runtime;
- generated graphs must pass through the `.rag` compiler and policy checks.
-
-## Why Rhai First
-
-The `rlm` repository uses Python as its default local REPL. Python is effective
-for long-context programming because models already know it well, but embedding
-Python in Rust would make TinyAgents depend on a large runtime, a separate
-sandbox story, and a weaker capability boundary.
-
-Rhai is a better first fit for TinyAgents because it is an embedded scripting
-language for Rust with a small host API. It lets TinyAgents register exactly the
-functions and values a script may use. The Rhai book describes Rhai as
-sandboxed from the host environment by default, with external access provided by
-registered functions. It also supports operation limits through
-`Engine::set_max_operations`, which gives TinyAgents a direct way to fail closed
-on runaway scripts.
-
-Rhai tradeoffs:
-
-- Pros:
- - Rust-native embedding.
- - No Python interpreter dependency.
- - Host-controlled function registration.
- - Familiar JavaScript/Rust-like syntax.
- - Resource limits such as operation counts.
- - Suitable for WASM and other Rust deployment targets.
-- Cons:
- - Models know Python better than Rhai.
- - Rhai is dynamically typed, so TinyAgents must validate values at capability
- boundaries.
- - Async host functions require an adapter design.
- - The default `Engine` is not `Send + Sync` unless configured with the Rhai
- `sync` feature, so runtime ownership must be explicit.
-
-Recommendation: use Rhai for the first in-process REPL runtime and document a
-future Python compatibility sandbox as a separate environment backend.
-
-## Language Extension
-
-Recommended extension: `.ragsh`.
-
-Reasoning:
-
-- pairs naturally with `.rag`
-- reads like "TinyAgents shell"
-- avoids implying that the syntax is Rust
-- leaves room for future non-Rhai backends
-
-Examples:
-
-```text
-support.rag declarative graph definition
-support.ragsh interactive orchestration script
-```
-
-## Runtime Model
-
-The REPL runtime is a session around a capability registry.
-
-```rust
-pub struct ReplSession {
- pub session_id: SessionId,
- pub run_context: RunContext,
- pub variables: ReplVariables,
- pub capabilities: ReplCapabilities,
- pub policy: ReplPolicy,
- pub events: EventSink,
-}
-
-pub struct ReplCapabilities {
- pub models: ModelRegistry,
- pub tools: ToolRegistry,
- pub graphs: GraphRegistry,
- pub agents: AgentRegistry,
- pub stores: StoreRegistry,
- pub language: Option>,
-}
-
-pub struct ReplPolicy {
- pub max_operations: u64,
- pub max_iterations: usize,
- pub max_script_bytes: usize,
- pub max_output_bytes: usize,
- pub max_model_calls: usize,
- pub max_tool_calls: usize,
- pub max_graph_calls: usize,
- pub max_graph_definitions: usize,
- pub max_depth: usize,
- pub timeout: Option,
- pub max_concurrency: usize,
- pub generated_graphs_require_review: bool,
-}
-```
-
-The session namespace persists across cells. Each cell produces a `ReplResult`.
-
-```rust
-pub struct ReplResult {
- pub stdout: String,
- pub value: Option,
- pub variables_changed: Vec,
- pub calls: Vec,
- pub final_answer: Option,
- pub elapsed: Duration,
-}
-```
-
-## Built-In Variables
-
-Initial variables:
-
-- `context`: user input or context payload
-- `state`: current graph or agent state when the REPL is used inside a node
-- `messages`: current message list when available
-- `history`: prior REPL cells and compacted summaries
-- `run`: run metadata such as run id, thread id, tags, and depth
-- `answer`: final-answer object or helper function
-
-The runtime should restore reserved names after each cell, similar to `rlm`.
-Scripts may create local variables, but they may not permanently replace core
-capabilities such as `model_query` or `graph_run`.
-
-Reserved names:
-
-- `model_query`
-- `model_query_batched`
-- `agent_query`
-- `agent_query_batched`
-- `graph_run`
-- `graph_run_batched`
-- `graph_define`
-- `graph_validate`
-- `graph_compile`
-- `graph_diff`
-- `graph_register`
-- `tool_call`
-- `tool_call_batched`
-- `emit`
-- `show_vars`
-- `answer`
-- `context`
-- `state`
-- `messages`
-- `history`
-- `run`
-
-## Built-In Functions
-
-The REPL should expose a small, stable surface. These functions are host
-capabilities, not script-native side effects.
-
-### `model_query`
-
-Single provider-neutral model call through the harness.
-
-```rhai
-let summary = model_query(#{
- model: "default",
- prompt: "Summarize the relevant facts:\n" + context
-});
-```
-
-Lowering:
-
-```text
-model_query(...) -> ModelRegistry -> ChatModel::invoke -> ModelResponse
-```
-
-Requirements:
-
-- validates model alias
-- applies harness middleware
-- records usage and cost
-- emits model events
-- increments model-call limits
-- returns text by default and structured metadata on request
-
-### `model_query_batched`
-
-Bounded concurrent model calls.
-
-```rhai
-let prompts = chunks.map(|chunk| #{
- model: "default",
- prompt: "Extract relevant names:\n" + chunk
-});
-
-let answers = model_query_batched(prompts);
-```
-
-Requirements:
-
-- preserves input order
-- records per-item failures without losing successful results when policy allows
-- respects `max_concurrency`
-- rolls usage and cost into the parent run
-
-### `agent_query`
-
-Run a registered harness agent loop.
-
-```rhai
-let result = agent_query(#{
- agent: "support_agent",
- input: #{
- messages: messages,
- notes: notes
- }
-});
-```
-
-Lowering:
-
-```text
-agent_query(...) -> AgentHarness::run -> AgentRun
-```
-
-Use this when the subtask should have model-tool iteration but does not need a
-full graph.
-
-### `graph_run`
-
-Run a registered compiled graph.
-
-```rhai
-let run = graph_run(#{
- graph: "approval_flow",
- input: state,
- thread_id: run.thread_id
-});
-```
-
-Lowering:
-
-```text
-graph_run(...) -> CompiledGraph::run/resume -> GraphRun
-```
-
-Use this when the subtask has explicit topology, routing, interrupts, or
-checkpointing.
-
-### `graph_define`
-
-Create a graph blueprint from `.rag` source without installing it.
-
-```rhai
-let draft = graph_define(#{
- name: "candidate_support_flow",
- source: `
-graph candidate_support_flow {
- start agent
-
- node agent {
- kind agent
- model "default"
- tools ["lookup_user"]
- routes {
- tool_call -> tools
- final -> END
- }
- }
-
- node tools {
- kind tool_executor
- next agent
- }
-}
-`
-});
-```
-
-Lowering:
-
-```text
-graph_define(...) -> LanguageCompiler::parse -> GraphBlueprint
-```
-
-Requirements:
-
-- preserves source spans
-- records generated-by provenance
-- does not compile, register, or run the graph
-- counts against `max_graph_definitions`
-- rejects source that exceeds policy limits
-
-### `graph_validate`
-
-Parse and resolve a graph blueprint against the current capability allowlist.
-
-```rhai
-let diagnostics = graph_validate(draft);
-```
-
-Requirements:
-
-- validates syntax, duplicate ids, routes, node kinds, and policies
-- checks model, tool, agent, graph, reducer, store, middleware, and script
- references against registries
-- returns structured diagnostics that can be shown to the model or user
-- does not mutate graph registry state
-
-### `graph_compile`
-
-Compile a validated blueprint into a `CompiledGraph` value under policy.
-
-```rhai
-let compiled = graph_compile(draft);
-```
-
-Requirements:
-
-- uses the same expressive-language compiler as file-backed `.rag` source
-- applies parent run capability allowlists
-- marks generated graphs as untrusted unless policy says otherwise
-- requires review when `generated_graphs_require_review` is true
-- emits compiler and graph blueprint events
-
-### `graph_diff`
-
-Compare two graph blueprints or a blueprint and a registered graph.
-
-```rhai
-let diff = graph_diff("support_flow", draft);
-```
-
-Requirements:
-
-- reports node, edge, channel, policy, capability, and metadata differences
-- preserves source locations where available
-- redacts prompt or metadata fields according to event policy
-- is deterministic for tests and review UIs
-
-### `graph_register`
-
-Register a compiled graph under a name only when policy permits it.
-
-```rhai
-graph_register(#{
- name: "candidate_support_flow",
- graph: compiled,
- review_id: "approval_123"
-});
-```
-
-Requirements:
-
-- never accepts raw source directly
-- requires a compiled graph
-- requires a review token when policy says generated graphs need approval
-- emits registry events
-- does not grant capabilities beyond the compiled graph's validated bindings
-
-### `tool_call`
-
-Call a registered tool by name.
-
-```rhai
-let user = tool_call(#{
- tool: "lookup_user",
- arguments: #{ user_id: "usr_123" }
-});
-```
-
-Requirements:
-
-- validates the tool exists
-- validates arguments against the tool schema
-- applies middleware
-- emits tool events
-- records raw and normalized result values
-
-### `emit`
-
-Emit a custom event for tracing and tests.
-
-```rhai
-emit("candidate_selected", #{ id: candidate.id, score: candidate.score });
-```
-
-### `answer`
-
-Mark the session complete.
-
-```rhai
-answer("The account should be escalated to human review.");
-```
-
-or, if an object style is preferred:
-
-```rhai
-answer.content = "The account should be escalated to human review.";
-answer.ready = true;
-```
-
-The function style should be the default because it is harder to accidentally
-partially mutate.
-
-
----
-
-Continues in [`operations.md`](operations.md) (RLM feature map,
-CodeAct loop, examples, Rhai embedding plan, Python compatibility
-backend, safety, events, diagnostics, testkit, milestones).
diff --git a/docs/modules/repl-language/operations.md b/docs/modules/repl-language/operations.md
deleted file mode 100644
index 3e4fb858..00000000
--- a/docs/modules/repl-language/operations.md
+++ /dev/null
@@ -1,351 +0,0 @@
-# REPL Language: RLM Feature Map, Embedding, Safety, Events, Testkit
-
-Continues from [`design.md`](design.md): RLM feature map, CodeAct
-loop, example session/graph node, Rhai embedding plan, Python
-compatibility backend, safety, events, diagnostics, testkit, and
-implementation milestones.
-
-## RLM Feature Map
-
-The goal is to port the useful `rlm` behavior into TinyAgents without porting
-Python's unsafe local execution model.
-
-| `rlm` feature | TinyAgents REPL equivalent |
-| --------------------------- | ----------------------------------------------------- |
-| Python `context` variable | Rhai `context` variable |
-| Python persistent locals | `ReplSession::variables` |
-| fenced `repl` blocks | fenced `ragsh` blocks |
-| `llm_query` | `model_query` |
-| `llm_query_batched` | `model_query_batched` |
-| `rlm_query` | `agent_query` or `repl_query` |
-| `rlm_query_batched` | `agent_query_batched` or `repl_query_batched` |
-| custom Python tools | registered Rust tool capabilities |
-| generated Python programs | `.ragsh` cells plus generated `.rag` graph blueprints |
-| `SHOW_VARS()` | `show_vars()` |
-| `answer["ready"] = True` | `answer(...)` |
-| max iterations | `ReplPolicy::max_iterations` for CodeAct loops |
-| max depth | graph/harness recursion policy |
-| max budget | harness cost policy |
-| token compaction | harness summarization feature |
-| JSONL trajectory logger | typed event stream plus store backend |
-| Docker/cloud REPL isolation | future `PythonSandboxRepl` backend |
-
-## CodeAct Loop
-
-A model-driven REPL agent has this lifecycle:
-
-1. Create `ReplSession`.
-2. Load `context`, `state`, `messages`, `history`, and `run` variables.
-3. Build a model request explaining the available REPL functions.
-4. Invoke the model through the harness.
-5. Extract fenced `ragsh` blocks from the assistant message.
-6. Execute each block in the REPL session.
-7. Capture stdout, changed variables, call records, events, and errors.
-8. Append a compact execution result as the next user message.
-9. Repeat until `answer(...)` is called or limits are reached.
-10. Persist events, usage, cost, and final answer.
-
-This loop is a harness feature. When used inside a graph node, the graph still
-owns node routing, checkpointing, interrupts, recursion depth, and failure
-policy.
-
-If the model writes `.rag` source, the loop should treat it as a graph proposal.
-The REPL may validate, diff, compile, and run that proposal only through the
-expressive-language compiler and the graph registry policy. This is how an
-agent can define its own graph without acquiring arbitrary topology mutation or
-host-code execution privileges.
-
-## Example Session
-
-```rhai
-let lines = context.split("\n");
-let candidates = [];
-
-for line in lines {
- if line.contains("SECRET_NUMBER=") {
- candidates.push(line);
- }
-}
-
-emit("candidates_found", #{ count: candidates.len() });
-
-let result = model_query(#{
- model: "default",
- prompt: "Return only the digits from this candidate line:\n" + candidates[0]
-});
-
-answer(result);
-```
-
-## Example Graph Node
-
-```tinyagents
-graph support_repl {
- start investigate
-
- node investigate {
- kind repl_agent
- model "default"
- script "support-investigation.ragsh"
- tools ["lookup_user", "create_ticket"]
- routes {
- final -> END
- needs_review -> review
- }
- }
-
- node review {
- kind interrupt
- prompt "Approve escalation?"
- routes {
- approved -> END
- rejected -> investigate
- }
- }
-}
-```
-
-The `repl_agent` node is a harness-backed node template. It may execute a fixed
-script, a model-driven CodeAct loop, or a combination where a fixed prologue
-sets up variables before the model starts writing cells.
-
-## Rhai Embedding Plan
-
-The Rhai runtime should be isolated behind an interface so future Python or WASM
-backends can reuse the same TinyAgents semantics.
-
-```rust
-#[async_trait]
-pub trait ReplBackend: Send {
- async fn execute_cell(
- &mut self,
- session: &mut ReplSession,
- source: SourceCell,
- ) -> Result;
-}
-
-pub struct RhaiReplBackend {
- engine: rhai::Engine,
- ast_cache: AstCache,
-}
-```
-
-Rhai-specific requirements:
-
-- configure `Engine::set_max_operations`
-- disable or avoid unneeded packages
-- register only TinyAgents capability functions
-- expose data through `Dynamic`, maps, and arrays with explicit conversion
-- compile and cache ASTs for repeated scripts
-- keep each session's `Scope` separate
-- restore reserved names after each cell
-- truncate stdout and returned values according to policy
-- convert Rhai errors into structured diagnostics with spans
-
-Async adapter requirement:
-
-Rhai host functions are easiest to expose as synchronous functions. TinyAgents
-model, tool, and graph calls are async. The backend should not hide blocking in
-unbounded threads. Use one of these designs:
-
-1. command recording: Rhai functions create `ReplCommand` values, then the
- Rust async runtime executes those commands after the cell
-2. blocking bridge: host functions call into a bounded runtime handle with
- strict timeouts
-3. staged syntax: `let x = model_query(...)` is transformed before evaluation
- into host-executed calls
-
-Recommendation for v1: use a blocking bridge only in examples and tests, but
-design the public API around command recording. Command recording is easier to
-make deterministic and safer under async graph execution.
-
-## Python Compatibility Backend
-
-Python should be a compatibility backend, not the default embedded runtime.
-
-```rust
-pub struct PythonSandboxReplBackend {
- sandbox: SandboxClient,
-}
-```
-
-Potential use cases:
-
-- training model behavior that already expects Python
-- local research workflows
-- compatibility with RLM-style prompts
-- data-heavy scripts where Python libraries are explicitly useful
-
-Requirements:
-
-- must run out of process
-- must have no direct host filesystem access by default
-- must communicate through a framed JSON protocol
-- must expose the same TinyAgents capability functions
-- must enforce the same `ReplPolicy`
-- must emit the same `ReplEvent` stream
-
-This lets TinyAgents support Python-like RLM ergonomics without making Python a
-trusted in-process extension language.
-
-## Safety
-
-Safety rules:
-
-- no arbitrary filesystem access in the default Rhai backend
-- no environment variable interpolation from scripts
-- no direct network access
-- no process spawning
-- no unregistered native functions
-- bounded script size
-- bounded operation count
-- bounded output size
-- bounded model/tool/graph calls
-- bounded recursion depth
-- bounded concurrency
-- typed conversion at every capability boundary
-- redaction before event and store writes
-
-The REPL is an orchestration surface, not a privilege escalation surface.
-
-## Events
-
-The REPL event stream should compose with graph and harness events.
-
-```rust
-pub enum ReplEvent {
- SessionStarted { session_id: SessionId, run_id: RunId },
- CellStarted { cell_id: CellId, source_name: String },
- CellStdout { cell_id: CellId, chunk: String },
- CellCompleted { cell_id: CellId, elapsed: Duration },
- CellFailed { cell_id: CellId, diagnostic: Diagnostic },
- VariableChanged { cell_id: CellId, name: String },
- CapabilityCallStarted { cell_id: CellId, call_id: CallId, name: String },
- CapabilityCallCompleted { cell_id: CellId, call_id: CallId },
- GraphBlueprintDefined { cell_id: CellId, graph_name: String },
- GraphBlueprintValidated { cell_id: CellId, graph_name: String },
- GraphBlueprintCompiled { cell_id: CellId, graph_name: String },
- GraphBlueprintRegistered { cell_id: CellId, graph_name: String },
- FinalAnswer { cell_id: CellId, content: String },
- SessionCompleted { session_id: SessionId },
- SessionFailed { session_id: SessionId, error: String },
-}
-```
-
-When the REPL calls a model, tool, agent, or graph, the child harness/graph
-events should preserve:
-
-- root run id
-- parent run id
-- cell id
-- node id when used inside a graph
-- recursion depth
-- capability name
-
-## Diagnostics
-
-Diagnostics should preserve source spans from scripts and model-generated cells.
-
-Required errors:
-
-- invalid script syntax
-- unknown capability
-- unknown model
-- unknown tool
-- unknown graph
-- invalid graph source
-- graph compilation failed
-- generated graph review required
-- graph registration denied
-- invalid arguments
-- unsupported value type
-- operation limit exceeded
-- timeout exceeded
-- output limit exceeded
-- call limit exceeded
-- recursion limit exceeded
-- unsafe backend requested
-- reserved name overwrite
-
-Example:
-
-```text
-error[E-ragsh-unknown-tool]: tool `lookup_usr` is not registered
- --> support.ragsh:8:18
- |
-8 | let user = tool_call(#{ tool: "lookup_usr", arguments: #{ id: id } });
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ unknown tool
- |
-help: did you mean `lookup_user`?
-```
-
-## Testkit
-
-`repl::testkit` should include:
-
-- fake model capability
-- fake tool capability
-- fake graph capability
-- deterministic event recorder
-- script execution helper
-- CodeAct loop helper with scripted model responses
-- operation-limit assertion
-- output-limit assertion
-- recursive-call assertion
-- batched-call ordering assertion
-- golden trajectory fixtures
-
-## Implementation Milestones
-
-### R1: Documentation And Types
-
-- add this module doc
-- add `repl` package shape to the spec
-- define `ReplSession`, `ReplPolicy`, `ReplResult`, and `ReplEvent`
-- no Rhai dependency yet
-
-### R2: Rhai Prototype
-
-- add optional `repl-rhai` feature
-- embed Rhai behind `ReplBackend`
-- support persistent variables
-- support `show_vars`, `emit`, and `answer`
-- enforce operation and output limits
-
-### R3: Harness Capabilities
-
-- add `model_query`
-- add `model_query_batched`
-- add fake-model tests
-- forward harness events through REPL events
-
-### R4: Tool And Agent Capabilities
-
-- add `tool_call`
-- add `agent_query`
-- validate schemas and limits
-- record usage and cost rollups
-
-### R5: Graph Capability
-
-- add `graph_run`
-- add `graph_define`, `graph_validate`, `graph_compile`, `graph_diff`, and
- `graph_register`
-- support graph-node `kind repl_agent`
-- preserve node id, parent run id, and depth in child events
-- require generated-graph review gates when policy enables them
-
-### R6: CodeAct Loop
-
-- parse fenced `ragsh` blocks from assistant messages
-- execute cells iteratively
-- append compact execution feedback to model history
-- stop on `answer(...)`
-- add trajectory logging and tests
-
-### R7: Python Sandbox Backend
-
-- add optional out-of-process backend
-- expose the same capability protocol
-- run RLM-compatible Python scripts under explicit policy
-- keep it disabled by default
diff --git a/docs/modules/rlm/README.md b/docs/modules/rlm/README.md
deleted file mode 100644
index 59b2f88b..00000000
--- a/docs/modules/rlm/README.md
+++ /dev/null
@@ -1,179 +0,0 @@
-# RLM — the recursive-language-model runtime (`src/rlm/`)
-
-The `rlm` module (Cargo feature `rlm`) turns a code sandbox into a
-recursive-language-model harness: a **driver model** writes code cells, the
-cells execute in a **sandboxed interpreter**, and the only host surface the
-scripts see is a set of **capability calls back into the registry** — sub-LLM
-queries, tools, and sub-agent delegation. Because scripts can call models
-(and agents that call models), the loop is recursive by construction, which
-is the execution pattern of the RLM literature this crate is architected
-around (see the crate-level docs in `src/lib.rs`).
-
-```text
- ┌──────────────────────────────────────────────┐
- task ───► │ RlmRunner: driver model ⇄ code cells ⇄ obs. │ ───► answer
- └──────────────┬───────────────────────────────┘
- │ eval(code)
- ┌──────────────▼───────────────┐
- │ RlmSession │
- │ ┌──────────┐ HostCall │
- │ │ RlmInter-│ ─────────────► │ RlmHost ──► CapabilityRegistry
- │ │ preter │ ◄───────────── │ (policy, counters, depth)
- │ └──────────┘ Value/error │ ├─ llm → ChatModel::invoke
- └──────────────────────────────┘ ├─ tool → Tool::call
- └─ agent → HarnessAgent::run
-```
-
-## The three layers
-
-| Layer | Type | What it is |
-|---|---|---|
-| Interpreter | `RlmInterpreter` (trait) | Pluggable cell execution: `eval_cell`, `set_variable`, `usage_guide`, `shutdown`. State persists across cells like a notebook. |
-| Session | `RlmSession` | One interpreter bound to one `RlmHost`; the programmatic "interpreter as an API" surface. Enforces per-cell policy fail-closed. |
-| Runner | `RlmRunner` | The model-driven loop: template → system prompt, fenced code cells in, observations out, stop on `final_answer(...)`. |
-
-Every layer is usable on its own: an embedder that wants its own loop (or a
-notebook UI) drives `RlmSession::eval` directly and never touches the runner.
-
-## Interpreter backends
-
-- **`InterpreterSpec::Rhai`** (default) — the embedded Rhai engine. This is
- the only *hermetic* sandbox: no filesystem, network, or process access
- exists inside the engine; the registered capability closures are its whole
- world. Bounded by `max_operations`, the cell deadline (`on_progress`
- hook), and the blocking bridge around every capability call (the same
- fail-closed adapter as `repl::session`).
-- **`InterpreterSpec::Python { binary, args }`** — an external CPython child
- process (`python3` by default). A bootstrap prelude is injected via `-c`;
- cells run in a persistent exec namespace.
-- **`InterpreterSpec::Javascript { binary, args }`** — a Node.js child
- (`node` by default, prelude via `-e`); cells run in a persistent `vm`
- context.
-- **`InterpreterSpec::Command { binary, args }`** — any command that speaks
- the wire protocol itself (a containerized runner, a jailed interpreter, a
- different language).
-
-### The wire protocol (external backends)
-
-Line-delimited JSON on the child's stdin/stdout; calls are strictly
-sequential so no correlation ids are needed. Child → host: `ready`,
-`call {call: HostCall}`, `result {stdout, value, error}`, `var_set`.
-Host → child: `eval {code}`, `set_var {name, value}`,
-`call_result {ok, value|error}`, `shutdown`. `HostCall` is the shared,
-serde-stable call shape (`{"capability": "llm"|"tool"|"agent"|"final_answer", ...}`).
-Any runtime that speaks this protocol is a valid backend — that is the
-extension point for other harnesses (e.g. openhuman) to bring their own
-interpreter.
-
-### Sandboxing honesty
-
-The host enforces every policy limit fail-closed for **all** backends (a
-child that exceeds its deadline or trips a bound is killed, not asked). But
-an external child process has whatever OS access the environment grants it.
-For untrusted driver models, either stay on the embedded Rhai backend or run
-the external interpreter inside real isolation (container/jail/seccomp) via
-`InterpreterSpec::Command`.
-
-## The capability surface inside scripts
-
-Identical semantics across languages (spelling varies per usage guide):
-
-- `llm(prompt)` / `llm({model, prompt, system})` → string — a sub-LLM call;
- the unnamed form uses the session's default sub-model.
-- `tool(name, args)` → tool result (raw JSON when the tool provides it).
- Arguments are validated against the tool's schema at the host boundary.
-- `agent(name, input)` → string — delegates to a registered
- `HarnessAgent`; a full nested agent run with event fan-out and the shared
- recursion-depth guard (`RunConfig::checked_child_depth`).
-- `final_answer(text)` — ends the run with this answer.
-- `print(...)` / `console.log(...)` — captured and echoed back to the
- driver next turn, bounded by `max_output_bytes` (explicit truncation
- marker).
-
-### Error contract
-
-Script-visible failures (unknown tool, schema mismatch, tool error, provider
-error) surface *inside* the script as catchable exceptions (`RlmError` in
-Python/JS, `try`/`catch` in Rhai) so the driving model can adapt — that
-feedback loop is the point of an RLM. Policy violations (`LimitExceeded`,
-`Timeout`, `Cancelled`, `SubAgentDepth`) are **fatal**: the cell aborts (and
-an external child is killed) so scripts can never observe and route around
-their own resource limits. `rlm::is_fatal` is the classifier.
-
-## Config-driven runs
-
-Everything a run needs is one serde document — the integration surface for
-external harnesses:
-
-```json
-{
- "interpreter": {"kind": "python", "binary": "/opt/venv/bin/python3"},
- "driver_model": "openai",
- "sub_model": "openai",
- "template": "context-explorer",
- "policy": {
- "max_cells": 8, "max_llm_calls": 16, "max_tool_calls": 64,
- "max_agent_calls": 8, "max_depth": 8,
- "max_script_bytes": 65536, "max_output_bytes": 262144,
- "cell_timeout": 90000, "max_operations": 5000000
- }
-}
-```
-
-`RlmConfig::from_json` → `RlmRunner::from_config(config, registry, state)` →
-`runner.set_context(json)` (optional) → `runner.run(task)`.
-
-### Templates
-
-`TemplateSpec::Named` selects a built-in; `TemplateSpec::Inline` carries a
-custom `RlmTemplate` in the config document. Placeholders `{{language}}`,
-`{{usage}}`, `{{capabilities}}`, `{{limits}}` are substituted at run time
-from the live session (so the prompt always reflects the actual registry and
-policy). Built-ins:
-
-- `general` — solve the task with code; sub-LLMs for fuzzy subproblems.
-- `context-explorer` — the RLM long-context pattern: material injected as
- the `context` variable, probed programmatically, never printed whole.
-- `orchestrator` — decompose and delegate to registered sub-agents.
-
-## Policy (all fail-closed)
-
-`RlmPolicy`: `max_cells`, `max_script_bytes`, `max_output_bytes`,
-`max_llm_calls`, `max_tool_calls`, `max_agent_calls`, `max_depth`,
-`cell_timeout` (ms in JSON), `max_operations` (Rhai only). Counters are
-session-cumulative. `RlmCancelFlag` provides sticky external cancellation,
-observed mid-script (Rhai `on_progress`), mid-call (blocking bridge), and
-before each cell.
-
-## Runner loop details
-
-- The driver must reply with exactly one fenced code block; the first fence
- is extracted regardless of its info string (models mislabel languages).
-- A fence-less reply earns one nudge (it is often raw unfenced code); a
- second consecutive fence-less reply is accepted as a prose answer
- (`RlmStopReason::ModelAnswered`).
-- Observations echo captured stdout, the cell value, and any script error.
-- Stop reasons: `Answered` (a cell called `final_answer`), `ModelAnswered`,
- `CellBudgetExhausted`. The full per-cell trajectory is returned in
- `RlmOutcome::steps`.
-
-## Tests and examples
-
-- Unit: `src/rlm/test.rs` (config round-trips, templates, extraction, the
- embedded backend, runner loop against `ScriptedModel`).
-- E2E: `tests/e2e_rlm.rs` — sub-agent delegation from scripts, real
- `python3`/`node` protocol round-trips (skipped when the binary is
- missing), fail-closed timeout kill, config-driven runner.
-- Live: `tests/live_rlm.rs` — network-gated on `OPENAI_API_KEY`.
-- Examples: `examples/rlm_rhai.rs` (context-explorer over expense records),
- `examples/rlm_python.rs` (tools + a real sub-agent, external Python).
- Run with `cargo run --features rlm --example rlm_rhai`.
-
-## Relationship to `repl::session`
-
-`repl::session` is the `.ragsh` interactive orchestration surface: Rhai
-only, richer built-ins (graph authoring, batching), REPL-first. `rlm` is the
-model-*driven* counterpart: interpreter-pluggable, config-first, and shaped
-for embedding in other harnesses. They share design DNA deliberately — the
-fail-closed blocking bridge, reserved capability boundary, and policy
-posture are the same pattern.
diff --git a/docs/spec/README.md b/docs/spec/README.md
index cfe17670..57638ff7 100644
--- a/docs/spec/README.md
+++ b/docs/spec/README.md
@@ -1,14 +1,16 @@
# TinyAgents System Specification
-TinyAgents is a Rust-native LLM application framework inspired by LangChain,
-LangGraph, and CodeAct-style recursive language-model runtimes. The system is
-organized around five modules:
+TinyAgents is a Rust-native LLM application framework inspired by LangChain and
+LangGraph. The system is organized around four modules:
1. the harness
2. the graph
3. the registry
4. the expressive language
-5. the REPL language
+
+Scripted, interpreter-backed orchestration (a CodeAct/REPL loop over
+model-written code cells) is deliberately a *host* concern built on top of these
+modules, not a surface this crate ships. See "Host-side surfaces" below.
The goal is to make agent systems easy to define, inspect, run, test, and
eventually serialize without hiding the Rust types that make production systems
@@ -34,16 +36,34 @@ them:
harness-decoupled graph engine, persistent checkpoints, HITL, graph
observability, blueprints, JSON-RPC run control, and a behavior-preserving
cutover from an implicit turn loop to an explicit phase machine.
-- RLM contributes the REPL/code-act model: context and prompts as runtime
- values, recursive sub-model or sub-agent calls as functions, persistent
- session variables, trajectory logging, and sandbox choices.
+- CodeAct/recursive-language-model runtimes contribute the recursion model:
+ context and prompts as runtime values, recursive sub-model or sub-agent calls
+ as functions, persistent session variables, and trajectory logging. TinyAgents
+ provides the primitives (registry capabilities, sub-agents, session/cell/call
+ ids, event journals); the interpreter and its sandbox stay host-side.
The target architecture is therefore layered: the harness owns model/tool
execution and policies, the graph owns deterministic state transition and
-durability, the registry owns named capabilities, `.rag` owns serializable graph
-blueprints, and `.ragsh` owns capability-bound interactive orchestration. No
-layer should bypass another layer's safety, policy, observability, or test
-contracts.
+durability, the registry owns named capabilities, and `.rag` owns serializable
+graph blueprints. No layer should bypass another layer's safety, policy,
+observability, or test contracts.
+
+## Host-side surfaces
+
+Some things a recursive agent system needs are intentionally *not* implemented
+here, because a host can implement them on top of the four modules and because
+shipping them would drag an embedded interpreter into every dependent's build:
+
+- the scripted CodeAct/REPL session loop (an embedded Rhai / Python / JavaScript
+ interpreter running model-written code cells)
+- the driver loop that prompts a model for the next code cell and feeds the
+ cell's output back in
+
+What this crate provides for those hosts: the capability `registry` (so a script
+can only reach named `llm` / `tool` / `agent` capabilities), the harness and its
+sub-agent recursion accounting, typed `SessionId` / `CellId` / `CallId`, the
+event journal, and the `.rag` `repl_agent` node kind, which binds a
+host-provided scripted node to a registered `Script` component by name.
## Detailed Module Docs
@@ -88,8 +108,6 @@ contracts.
- [Design](../modules/registry/design.md)
- [Model catalog and local snapshots](../modules/registry/model-catalog.md)
- [Expressive language module](../modules/expressive-language/README.md)
-- [REPL language module](../modules/repl-language/README.md)
- - [Design](../modules/repl-language/design.md)
Docs should follow the module layout. Do not place standalone specification
files directly in `docs/` or `docs/modules/`; each high-level topic should have
@@ -105,8 +123,6 @@ it.
- Keep model providers, tools, memory, and tracing behind stable traits.
- Support both Rust builder APIs and a compact expressive language for workflow
definitions.
-- Support a capability-bound REPL language for interactive graph and harness
- orchestration.
- Allow agents to author, inspect, compile, and run graph blueprints through the
same registry-bound compiler path used by human-authored `.rag` files.
- Allow parent orchestrators and humans to steer orchestrator agents and
@@ -157,7 +173,7 @@ for implementation status.
The crate is a single library at the repository root (`Cargo.toml`), with
`src/lib.rs` re-exporting the public surface and `src/error.rs` holding the
-crate-wide error type. Each of the five surfaces lives in its own module
+crate-wide error type. Each of the four surfaces lives in its own module
directory:
```text
@@ -168,14 +184,13 @@ src/
harness/ # provider-neutral model calls, tools, middleware, streaming, ...
language/ # the declarative `.rag` blueprint format (lexer/parser/compiler)
registry/ # the named capability catalog (models, tools, agents, stores, ...)
- repl/ # the imperative `.ragsh` session runtime
```
Provider implementations (OpenAI and the OpenAI-compatible endpoints for
Anthropic, Ollama, DeepSeek, Groq, xAI, OpenRouter, Together, and Mistral)
live inside `src/harness/providers/` and are compiled in unconditionally.
Two Cargo features gate optional dependencies: `sqlite` (embedded SQLite
-checkpointer) and `repl` (embedded Rhai engine for `.ragsh` sessions).
+checkpointer) and `tools` (the builtin generic tool family).
## Milestones
@@ -214,8 +229,9 @@ Langfuse tracing integration (`LangfuseClient`, `GraphLangfuseExporter`).
Historical decisions that have since been settled, kept for context:
-- The expressive language file extension is `.rag` (interactive/imperative
- orchestration uses the separate `.ragsh` extension).
+- The expressive language file extension is `.rag`. Interactive/imperative
+ orchestration was prototyped as a separate `.ragsh` surface and has since been
+ removed from this crate as a host concern.
- State schemas remain Rust-owned; `.rag` binds to them by name through the
registry rather than declaring schemas itself.
- Provider crates live in this crate as always-compiled modules behind
diff --git a/docs/spec/expressive-language-spec.md b/docs/spec/expressive-language-spec.md
index 3993c412..6890a68a 100644
--- a/docs/spec/expressive-language-spec.md
+++ b/docs/spec/expressive-language-spec.md
@@ -8,7 +8,8 @@ This language is not meant to replace Rust. It is a workflow definition layer fo
fast iteration, examples, documentation, and eventually user-authored agent
plans.
-It is also the safe boundary for agent-authored graph plans. A REPL or model may
+It is also the safe boundary for agent-authored graph plans. A host session or
+model may
propose `.rag` source, but that source must pass through the same parser,
diagnostics, registry binding, allowlist checks, review gates, and graph
compiler as human-authored source before it can run.
@@ -130,7 +131,7 @@ expressive language.
For generated source, the runtime relationship is:
```text
-REPL/model proposal -> .rag source or AST -> parser -> diagnostics -> resolver
+host/model proposal -> .rag source or AST -> parser -> diagnostics -> resolver
-> policy/review gate -> compiler -> GraphBuilder + Harness bindings
-> CompiledGraph -> optional registry registration
```
diff --git a/examples/rlm_python.rs b/examples/rlm_python.rs
deleted file mode 100644
index 13db721e..00000000
--- a/examples/rlm_python.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-//! A live RLM run over an **external Python interpreter**, with a real tool
-//! and a real sub-agent registered.
-//!
-//! The driver model writes Python cells; the child `python3` process executes
-//! them, and every capability call (`llm`, `tool`, `agent`, `final_answer`)
-//! travels back over the wire protocol to the host, which enforces the policy
-//! and lowers to the harness runtime. The `agent("summarizer", ...)` call
-//! drives a *complete nested agent run* on its own OpenAI-backed harness —
-//! scripts calling agents calling models.
-//!
-//! Run with (needs `OPENAI_API_KEY` and `python3` on PATH):
-//!
-//! ```text
-//! cargo run --features rlm --example rlm_python
-//! ```
-
-use std::sync::Arc;
-
-use async_trait::async_trait;
-use serde_json::json;
-
-use tinyagents::harness::providers::openai::OpenAiModel;
-use tinyagents::harness::runtime::AgentHarness;
-use tinyagents::harness::tool::{Tool, ToolCall, ToolResult, ToolSchema};
-use tinyagents::registry::CapabilityRegistry;
-use tinyagents::rlm::{RlmConfig, RlmRunner};
-use tinyagents::{HarnessSubAgent, Result, SubAgent};
-
-/// A deterministic metrics tool the script can query.
-struct MetricsTool;
-
-#[async_trait]
-impl Tool<()> for MetricsTool {
- fn name(&self) -> &str {
- "service_metrics"
- }
-
- fn description(&self) -> &str {
- "Returns latency and error-rate metrics for a named service."
- }
-
- fn schema(&self) -> ToolSchema {
- ToolSchema::new(
- "service_metrics",
- "Returns latency and error-rate metrics for a named service.",
- json!({
- "type": "object",
- "properties": { "service": { "type": "string" } },
- "required": ["service"],
- "additionalProperties": false
- }),
- )
- }
-
- async fn call(&self, _state: &(), call: ToolCall) -> Result {
- let service = call
- .arguments
- .get("service")
- .and_then(|v| v.as_str())
- .unwrap_or("unknown");
- let metrics = match service {
- "checkout" => json!({"p99_ms": 2140, "error_rate": 0.031, "deploys_today": 3}),
- "search" => json!({"p99_ms": 180, "error_rate": 0.002, "deploys_today": 0}),
- "auth" => json!({"p99_ms": 95, "error_rate": 0.001, "deploys_today": 1}),
- other => json!({"error": format!("unknown service `{other}`")}),
- };
- let mut result = ToolResult::text(call.id, call.name, metrics.to_string());
- result.raw = Some(metrics);
- Ok(result)
- }
-}
-
-#[tokio::main]
-async fn main() -> Result<()> {
- dotenvy::dotenv().ok();
-
- let mut registry: CapabilityRegistry<()> = CapabilityRegistry::new();
- registry.register_model("openai", Arc::new(OpenAiModel::from_env()?))?;
- registry.register_tool(Arc::new(MetricsTool))?;
-
- // A real sub-agent on its own harness: scripts delegate to it by name.
- let mut child: AgentHarness<()> = AgentHarness::new();
- child
- .register_model("openai", Arc::new(OpenAiModel::from_env()?))
- .set_default_model("openai");
- let summarizer = Arc::new(
- SubAgent::new(
- "summarizer",
- "Writes a crisp two-sentence incident summary from raw findings.",
- Arc::new(child),
- )
- .with_system_prompt(
- "You summarize incident findings for executives: two sentences, plain language, \
- lead with impact.",
- ),
- );
- registry.register_agent(Arc::new(HarnessSubAgent::new(summarizer)))?;
-
- let config = RlmConfig::from_json(
- r#"{
- "interpreter": {"kind": "python"},
- "driver_model": "openai",
- "template": "general",
- "policy": {"max_cells": 8, "cell_timeout": 90000}
- }"#,
- )?;
-
- let mut runner = RlmRunner::from_config(config, Arc::new(registry), Arc::new(()))?;
- let outcome = runner
- .run(
- "Check the service_metrics tool for the services checkout, search, and auth. \
- Identify which service looks unhealthy and why. Then delegate to the `summarizer` \
- agent to produce an executive summary of your findings, and return that summary \
- as the final answer.",
- )
- .await?;
-
- for (i, step) in outcome.steps.iter().enumerate() {
- println!("── cell {} ──\n{}\n", i + 1, step.code);
- if !step.outcome.stdout.is_empty() {
- println!("stdout:\n{}", step.outcome.stdout);
- }
- if let Some(error) = &step.outcome.error {
- println!("error: {error}");
- }
- }
- println!("── answer ({:?}) ──", outcome.stop_reason);
- println!("{}", outcome.answer.as_deref().unwrap_or("(none)"));
- println!(
- "\ndriver calls: {}, sub-llm calls: {}, tool calls: {}, agent calls: {}",
- outcome.driver_calls, outcome.sub_llm_calls, outcome.tool_calls, outcome.agent_calls
- );
- runner.shutdown().await?;
- Ok(())
-}
diff --git a/examples/rlm_rhai.rs b/examples/rlm_rhai.rs
deleted file mode 100644
index 26a304a9..00000000
--- a/examples/rlm_rhai.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-//! A live recursive-language-model run over the embedded Rhai sandbox.
-//!
-//! The classic RLM shape: a context too noisy to eyeball is injected into the
-//! sandbox as the `context` variable, and the driver model must *probe it
-//! with code* — slicing, filtering, and delegating fuzzy judgment on
-//! individual entries to sub-LLM calls (`llm(...)`) — before answering with
-//! `final_answer(...)`.
-//!
-//! Run with (needs `OPENAI_API_KEY`, optional `OPENAI_MODEL`):
-//!
-//! ```text
-//! cargo run --features rlm --example rlm_rhai
-//! ```
-
-use std::sync::Arc;
-
-use serde_json::json;
-
-use tinyagents::Result;
-use tinyagents::harness::providers::openai::OpenAiModel;
-use tinyagents::registry::CapabilityRegistry;
-use tinyagents::rlm::{RlmConfig, RlmRunner};
-
-#[tokio::main]
-async fn main() -> Result<()> {
- dotenvy::dotenv().ok();
- let model = OpenAiModel::from_env()?;
-
- let mut registry: CapabilityRegistry<()> = CapabilityRegistry::new();
- registry.register_model("openai", Arc::new(model))?;
-
- // The whole run is one JSON document — the same document an external
- // harness could load from disk.
- let config = RlmConfig::from_json(
- r#"{
- "interpreter": {"kind": "rhai"},
- "driver_model": "openai",
- "template": "context-explorer",
- "policy": {"max_cells": 8, "max_llm_calls": 16, "cell_timeout": 90000}
- }"#,
- )?;
-
- let mut runner = RlmRunner::from_config(config, Arc::new(registry), Arc::new(()))?;
-
- // A synthetic "too big to read" context: expense records with three
- // hidden anomalies among routine noise.
- let mut records = Vec::new();
- for i in 0..200usize {
- let team = ["platform", "growth", "ops"][i % 3];
- let amount = 40 + (i * 7) % 60;
- records.push(json!({
- "id": i,
- "team": team,
- "amount_usd": amount,
- "memo": format!("routine cloud spend, invoice {i}"),
- }));
- }
- records[57] = json!({"id": 57, "team": "growth", "amount_usd": 18400,
- "memo": "annual conference sponsorship paid twice, needs review"});
- records[121] = json!({"id": 121, "team": "ops", "amount_usd": 9750,
- "memo": "emergency hardware replacement after flood damage"});
- records[188] = json!({"id": 188, "team": "platform", "amount_usd": 12300,
- "memo": "contractor invoice with mismatched PO number"});
- runner.set_context(json!(records)).await?;
-
- println!("── system prompt ──\n{}\n", runner.system_prompt());
-
- let outcome = runner
- .run(
- "The `context` variable holds 200 expense records. Find every anomalous record \
- (unusual amount or memo), and summarize each anomaly in one line.",
- )
- .await?;
-
- for (i, step) in outcome.steps.iter().enumerate() {
- println!("── cell {} ──\n{}\n", i + 1, step.code);
- if !step.outcome.stdout.is_empty() {
- println!("stdout:\n{}", step.outcome.stdout);
- }
- if let Some(error) = &step.outcome.error {
- println!("error: {error}");
- }
- }
- println!("── answer ({:?}) ──", outcome.stop_reason);
- println!("{}", outcome.answer.as_deref().unwrap_or("(none)"));
- println!(
- "\ndriver calls: {}, sub-llm calls: {}, tool calls: {}, agent calls: {}",
- outcome.driver_calls, outcome.sub_llm_calls, outcome.tool_calls, outcome.agent_calls
- );
- Ok(())
-}
diff --git a/src/error.rs b/src/error.rs
index 823eae73..39894bfc 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,7 +1,7 @@
//! Crate-wide error type and `Result` alias.
//!
//! Every fallible surface of the recursive runtime — graph execution, the
-//! harness agent loop, sub-agent recursion, `.rag`/`.ragsh` compilation, and
+//! harness agent loop, sub-agent recursion, `.rag` compilation, and
//! registry binding — funnels through [`TinyAgentsError`] so failures from a
//! deeply nested child run roll up to the caller through one uniform type.
//! Downstream code should prefer the [`Result`] alias exported here.
@@ -16,7 +16,7 @@ pub type Result = std::result::Result;
///
/// Variants are grouped by the surface that raises them: graph construction and
/// execution, model/tool invocation, run limits and policy, graph durability,
-/// and `.rag`/`.ragsh` language processing.
+/// and `.rag` language processing.
#[derive(Debug, Error)]
pub enum TinyAgentsError {
/// A graph was compiled or run without a configured `START` edge, so there
@@ -220,7 +220,7 @@ pub enum TinyAgentsError {
Resume(String),
// --- language / blueprint errors ---
- /// A `.rag`/`.ragsh` source could not be tokenised or parsed.
+ /// A `.rag` source could not be tokenised or parsed.
#[error("parse error at line {line}, column {column}: {message}")]
Parse {
message: String,
diff --git a/src/graph/README.md b/src/graph/README.md
index 68a3e4ac..7c73d28a 100644
--- a/src/graph/README.md
+++ b/src/graph/README.md
@@ -1,13 +1,12 @@
# graph
TinyAgents' durable workflow runtime (LangGraph-style), and one of the
-load-bearing surfaces of the crate's recursive language-model (RLM)
-architecture.
+load-bearing surfaces of the crate's recursive architecture.
Because a node can embed another compiled graph (`subgraph`) or invoke a
sub-agent, **graphs run graphs** and orchestration recurses while every step
stays typed, checkpointed, and observable. A workflow authored from a `.rag`
-blueprint or driven from the `.ragsh` REPL lowers into exactly these same
+blueprint or driven from a host orchestrator lowers into exactly these same
types, so a model can describe, compile, and re-enter the very runtime it is
executing inside.
diff --git a/src/graph/builder/mod.rs b/src/graph/builder/mod.rs
index eaf4e8f6..bf163571 100644
--- a/src/graph/builder/mod.rs
+++ b/src/graph/builder/mod.rs
@@ -5,7 +5,7 @@
//! and [`GraphBuilder::compile`] validates that topology and freezes it into an
//! immutable [`crate::graph::CompiledGraph`]. Because a node handler can itself
//! drive another compiled graph or a sub-agent, the same builder API is what
-//! both hand-written Rust and model-authored `.rag`/`.ragsh` programs lower into
+//! both hand-written Rust and model-authored `.rag` programs lower into
//! when they assemble a workflow that may recurse into sub-workflows.
//!
//! See [`types`] for the builder data types. `compile` validates the topology
diff --git a/src/graph/mod.rs b/src/graph/mod.rs
index c8848373..17bfc3cc 100644
--- a/src/graph/mod.rs
+++ b/src/graph/mod.rs
@@ -2,10 +2,10 @@
//!
//! The graph module is TinyAgents' durable workflow runtime (LangGraph-style)
//! and one of the load-bearing surfaces of the crate's recursive language-model
-//! (RLM) architecture: because a node can embed another compiled graph
+//! architecture: because a node can embed another compiled graph
//! ([`subgraph`]) or invoke a sub-agent, **graphs run graphs** and orchestration
//! recurses while every step stays typed, checkpointed, and observable. A
-//! workflow authored from a `.rag` blueprint or driven from the `.ragsh` REPL
+//! workflow authored from a `.rag` blueprint or driven from a host orchestrator
//! lowers into exactly these same types, so a model can describe, compile, and
//! re-enter the very runtime it is executing inside.
//!
diff --git a/src/harness/agent_loop/README.md b/src/harness/agent_loop/README.md
index 53e567b4..c08272e9 100644
--- a/src/harness/agent_loop/README.md
+++ b/src/harness/agent_loop/README.md
@@ -1,7 +1,7 @@
# harness::agent_loop
The default model-tool-model agent loop: the innermost turn of the recursive
-(RLM-style) harness.
+harness.
This loop is where one model call is driven to completion. Because a whole
harness can be exposed as a tool (`harness::subagent::SubAgentTool`), the
diff --git a/src/harness/agent_loop/mod.rs b/src/harness/agent_loop/mod.rs
index bfb567a0..a835adfd 100644
--- a/src/harness/agent_loop/mod.rs
+++ b/src/harness/agent_loop/mod.rs
@@ -1,7 +1,7 @@
//! Default model-tool-model agent loop.
//!
-//! This loop is the innermost turn of the recursive-language-model (RLM)
-//! runtime: it is where one model call is driven to completion, and because a
+//! This loop is the innermost turn of the recursive runtime: it is where one
+//! model call is driven to completion, and because a
//! whole harness can be exposed as a tool
//! ([`crate::harness::subagent::SubAgentTool`]), the very tools this loop
//! executes may themselves be other agents — so "a model calling a model" is
diff --git a/src/harness/embeddings/mod.rs b/src/harness/embeddings/mod.rs
index 49aba051..3728ff98 100644
--- a/src/harness/embeddings/mod.rs
+++ b/src/harness/embeddings/mod.rs
@@ -1,6 +1,6 @@
//! Harness embeddings + retrieval module.
//!
-//! In the recursive (RLM-style) architecture this module is how a model reaches
+//! In the recursive architecture this module is how a model reaches
//! *outside* its context window: instead of stuffing a whole corpus into one
//! prompt, an agent (or a sub-agent / REPL step) embeds documents once and then
//! recursively retrieves only the snippets relevant to the current sub-question,
diff --git a/src/harness/events/types.rs b/src/harness/events/types.rs
index 11a7f921..02bf1e30 100644
--- a/src/harness/events/types.rs
+++ b/src/harness/events/types.rs
@@ -599,46 +599,6 @@ pub enum AgentEvent {
/// Human-readable error description.
error: String,
},
-
- /// A `.ragsh` REPL cell dispatched (or completed) a host capability call
- /// (`model_query`, `tool_call`, `agent_query`, or an `emit`).
- ///
- /// [`ReplResult::calls`](crate::repl::session::ReplResult) is only readable
- /// *after* a cell returns, so a long fan-out would otherwise look frozen to
- /// a live observer. This event streams each capability call as it starts and
- /// again as it completes, letting a host (which subscribes an
- /// [`EventListener`] on the session [`EventSink`]) forward REPL progress to
- /// its own UI/progress sink mid-cell. Gated behind the `repl` feature so the
- /// default build neither pulls in the Rhai engine nor references
- /// [`ReplCallRecord`](crate::repl::session::ReplCallRecord).
- #[cfg(feature = "repl")]
- ReplCall {
- /// The session label (its [`SessionId`](crate::harness::ids::SessionId)
- /// string) the call was issued from, correlating the event back to the
- /// REPL session that produced it.
- session_id: String,
- /// The call record. On the [`ReplCallPhase::Started`] phase the record's
- /// `elapsed` is zero and `detail` is minimal (the completed phase
- /// carries the full detail and measured `elapsed`); `call_id` is stable
- /// across the two phases so a listener can pair them.
- record: crate::repl::session::ReplCallRecord,
- /// Whether the call is starting or has completed.
- phase: ReplCallPhase,
- },
-}
-
-/// The lifecycle phase of an [`AgentEvent::ReplCall`] — whether a REPL host
-/// capability call is just starting or has completed.
-///
-/// Gated behind the `repl` feature alongside the event it annotates.
-#[cfg(feature = "repl")]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub enum ReplCallPhase {
- /// The capability call has been dispatched but has not yet returned.
- Started,
- /// The capability call returned (successfully or with a recorded error).
- Completed,
}
/// Names the kind of run limit that tripped in an [`AgentEvent::LimitReached`].
@@ -718,8 +678,6 @@ impl AgentEvent {
AgentEvent::StreamClosed => "stream.closed",
AgentEvent::RunCompleted { .. } => "run.completed",
AgentEvent::RunFailed { .. } => "run.failed",
- #[cfg(feature = "repl")]
- AgentEvent::ReplCall { .. } => "repl.call",
}
}
}
diff --git a/src/harness/ids/types.rs b/src/harness/ids/types.rs
index 8365e72e..b2655352 100644
--- a/src/harness/ids/types.rs
+++ b/src/harness/ids/types.rs
@@ -52,19 +52,19 @@ pub struct NodeId(pub(crate) String);
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TaskId(pub(crate) String);
-/// Identifies a single interactive `.ragsh` REPL session.
+/// Identifies a single interactive orchestration session.
///
-/// A `SessionId` names one [`crate::repl`] session: a persistent namespace and
+/// A `SessionId` names one host-driven session: a persistent namespace and
/// capability boundary that a (possibly model-driven) orchestrator drives one
-/// cell at a time. Pairing it with a [`RunId`] lets REPL events and the child
+/// cell at a time. Pairing it with a [`RunId`] lets session events and the child
/// model/tool/graph runs a session spawns be correlated back to the session
/// that issued them.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SessionId(pub(crate) String);
-/// Identifies a single executed cell within a [`SessionId`] REPL session.
+/// Identifies a single executed cell within a [`SessionId`] session.
///
-/// Each `.ragsh` cell evaluated against a session is named by a `CellId` so
+/// Each cell evaluated against a session is named by a `CellId` so
/// stdout chunks, variable changes, capability calls, and diagnostics can be
/// attributed to the exact cell that produced them.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
diff --git a/src/harness/middleware/README.md b/src/harness/middleware/README.md
index 695d889c..c95699c5 100644
--- a/src/harness/middleware/README.md
+++ b/src/harness/middleware/README.md
@@ -2,7 +2,7 @@
Cross-cutting extension points that wrap agent, model, and tool execution.
-In the recursive (RLM-style) harness a sub-agent or sub-graph is just another
+In the recursive harness a sub-agent or sub-graph is just another
agent loop, so the same before/after hooks bracket the parent run *and* every
nested model/tool/agent call beneath it. That uniform wrapping is what lets
concerns like tracing, usage/cost roll-up, and guardrails compose consistently
diff --git a/src/harness/middleware/mod.rs b/src/harness/middleware/mod.rs
index ba4df6e3..af4d8997 100644
--- a/src/harness/middleware/mod.rs
+++ b/src/harness/middleware/mod.rs
@@ -1,6 +1,6 @@
//! Middleware stack.
//!
-//! In the recursive (RLM-style) harness, middleware is the layer that wraps
+//! In the recursive harness, middleware is the layer that wraps
//! *every* level of the recursion identically: because a sub-agent or sub-graph
//! is just another agent loop, the same before/after hooks bracket the parent
//! run and each nested model/tool/agent call beneath it. That uniform wrapping
diff --git a/src/harness/model/mod.rs b/src/harness/model/mod.rs
index 9c4d9182..d58ad6ab 100644
--- a/src/harness/model/mod.rs
+++ b/src/harness/model/mod.rs
@@ -1,7 +1,7 @@
//! Harness model layer.
//!
//! The model layer is the innermost rung of the recursive ladder: every level
-//! of the RLM-style harness — a top-level agent, a sub-agent exposed as a tool,
+//! of the harness — a top-level agent, a sub-agent exposed as a tool,
//! or a node inside a subgraph — ultimately bottoms out in a [`ChatModel`] call
//! routed through this provider-neutral request/response shape. Because the
//! shapes are uniform, "a model calling a model" is the same typed surface at
diff --git a/src/harness/providers/mod.rs b/src/harness/providers/mod.rs
index 5280751b..fce10b18 100644
--- a/src/harness/providers/mod.rs
+++ b/src/harness/providers/mod.rs
@@ -1,7 +1,8 @@
//! Model provider integrations — the leaves of the recursion.
//!
//! Every recursive call in the runtime — an agent, a sub-agent, a graph node, a
-//! `.ragsh` step — ultimately bottoms out in a concrete model invocation, and
+//! host orchestration step — ultimately bottoms out in a concrete model
+//! invocation, and
//! that invocation goes through a provider adapter here. Adapters translate
//! between TinyAgents' provider-neutral request/response types
//! ([`ModelRequest`]/[`ModelResponse`]) and a provider's own wire API, so the
diff --git a/src/language/compiler.rs b/src/language/compiler.rs
index 2b445a9f..e6e75032 100644
--- a/src/language/compiler.rs
+++ b/src/language/compiler.rs
@@ -492,7 +492,7 @@ fn provenance_of(
}
}
-/// Parses, compiles, and registry-binds `.rag`/`.ragsh` `source` in one call.
+/// Parses, compiles, and registry-binds `.rag` `source` in one call.
///
/// This is the convenience façade for the common path: it runs
/// `parse -> compile -> registry-bind` and returns the validated blueprints.
diff --git a/src/language/mod.rs b/src/language/mod.rs
index f17a6425..bec2de50 100644
--- a/src/language/mod.rs
+++ b/src/language/mod.rs
@@ -1,7 +1,7 @@
//! Expressive language (`.rag`) — the declarative blueprint surface of the
//! recursive runtime.
//!
-//! In TinyAgents' recursive (RLM-style) architecture, a model can author the
+//! In TinyAgents' recursive architecture, a model can author the
//! very workflow it is standing inside. `.rag` is the *safe boundary* for that
//! self-authoring: a capability-by-name blueprint format that lowers into the
//! exact same [`crate::graph`] and [`crate::harness`] runtime as hand-written
diff --git a/src/language/resolver.rs b/src/language/resolver.rs
index a78f9898..0f8da39d 100644
--- a/src/language/resolver.rs
+++ b/src/language/resolver.rs
@@ -371,7 +371,7 @@ fn unregistered(what: &str, node: &str, target: &str) -> TinyAgentsError {
))
}
-/// Parses, registry-resolves (with full source spans), and lowers `.rag`/`.ragsh`
+/// Parses, registry-resolves (with full source spans), and lowers `.rag`
/// `source` into validated blueprints in one call.
///
/// This is the recommended single entry point: it routes generated and
diff --git a/src/lib.rs b/src/lib.rs
index 6db8b27e..c90b1d13 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,4 @@
-//! # TinyAgents — a recursive language-model (RLM) harness for Rust
+//! # TinyAgents — a durable agent + graph harness for Rust
//!
//! TinyAgents is a typed, durable runtime where **language models call models,
//! agents call agents, and graphs run graphs** — and where a model can author,
@@ -6,19 +6,15 @@
//! checkpointed, policy-checked Rust.
//!
//! The "recursive" framing is the through-line of the whole crate, not a
-//! footnote. It is architected around the execution model described in
-//! "Recursive Language Models" (Alex L. Zhang, Tim Kraska, Omar Khattab, MIT
-//! CSAIL, 2025; ): rather than stuffing
-//! everything into one context window, a model treats long context as an
-//! external *environment* it interacts with through a REPL — examining,
-//! decomposing, and recursively calling sub-models over snippets. TinyAgents
-//! brings that idea to Rust as a production-shaped harness (sub-model /
-//! sub-agent / sub-graph calls as functions, persistent session values, depth
-//! tracking, and trajectory/event logging). It is *inspired by and architected
-//! around* the RLM execution model, not a reimplementation of the paper's
-//! benchmarks.
+//! footnote: rather than stuffing everything into one context window, a model
+//! treats long context as an external *environment* it decomposes, recursively
+//! calling sub-models, sub-agents, and sub-graphs as functions. TinyAgents
+//! brings that to Rust as a production-shaped harness (capability calls by
+//! name, depth tracking, and trajectory/event logging). The *scripted* form of
+//! that loop — an embedded interpreter running model-written code cells — is a
+//! host concern built on these surfaces, not something this crate ships.
//!
-//! ## The five surfaces
+//! ## The four surfaces
//!
//! 1. **Harness** ([`harness`]) — provider-neutral model calls, typed tools,
//! middleware, structured output, streaming, usage/cost, retry/limits, cache,
@@ -30,18 +26,15 @@
//! durable [`ThreadGoal`] with graph-native continuation and a
//! [`TaskBoard`] kanban — exposed as harness tools.
//! 3. **Registry** ([`registry`]) — a named capability catalog (models, tools,
-//! agents, graphs, stores, middleware, policy) that `.rag`/`.ragsh` bind by
-//! name.
+//! agents, graphs, stores, middleware, policy) that `.rag` binds by name.
//! 4. **Expressive language `.rag`** ([`language`]) — a declarative,
//! side-effect-free blueprint format that compiles (lexer → parser →
//! compiler) into the same graph/harness runtime; the safe boundary for
//! agent-authored plans.
-//! 5. **REPL language `.ragsh`** ([`repl`]) — imperative, capability-bound
-//! interactive orchestration; the RLM/CodeAct loop surface.
//!
//! ## The recursion story
//!
-//! Both `.rag` and `.ragsh` lower into the *same* [`graph`] + [`harness`] types
+//! `.rag` lowers into the *same* [`graph`] + [`harness`] types
//! as hand-written Rust — a language whose programs are the runtime that
//! interprets them. A harness agent can be exposed *as a tool* to another agent
//! ([`SubAgent`], [`SubAgentTool`], [`SubAgentSession`]), so orchestration is
@@ -56,14 +49,15 @@
//! Hosted and local providers (OpenAI plus the OpenAI-compatible endpoints for
//! Anthropic, Ollama, DeepSeek, Groq, xAI, OpenRouter, Together, and Mistral)
//! are compiled in unconditionally alongside the offline, deterministic
-//! [`harness::providers::MockModel`]. Three Cargo features gate optional,
+//! [`harness::providers::MockModel`]. Two Cargo features gate optional,
//! heavier dependencies instead: `sqlite` (embedded SQLite checkpointer,
-//! [`graph::checkpoint::SqliteCheckpointer`]), `repl` (embedded Rhai engine
-//! powering the `.ragsh` session runtime, [`repl::session`]), and `rlm` (the
-//! recursive-language-model runtime: a driver model writes code cells run in
-//! a sandboxed interpreter — embedded Rhai or an external Python/JavaScript
-//! process — whose only host surface is capability calls back into the
-//! registry).
+//! [`graph::checkpoint::SqliteCheckpointer`]) and `tools` (the builtin generic
+//! tool family, [`harness::tools`]).
+//!
+//! Scripted, imperative orchestration surfaces (an embedded interpreter driving
+//! capability calls — the `.ragsh` REPL and the recursive-language-model
+//! runtime that used to ship here) are deliberately *not* part of this crate:
+//! they are host concerns, built on top of [`registry`] and [`harness`].
//!
//! ## Crate-root re-exports
//!
@@ -76,9 +70,6 @@ pub mod graph;
pub mod harness;
pub mod language;
pub mod registry;
-pub mod repl;
-#[cfg(feature = "rlm")]
-pub mod rlm;
/// Durable session history and run ledger — a persistence domain in its own
/// right, not part of the agent-loop harness. Requires the `sqlite` feature.
#[cfg(feature = "sqlite")]
@@ -106,7 +97,7 @@ pub use session::{
// --- Error: the crate-wide error type and `Result` alias ---
pub use error::{Result, TinyAgentsError};
-// --- Registry: named capability catalog (.rag/.ragsh binding by name) ---
+// --- Registry: named capability catalog (.rag binding by name) ---
pub use registry::{
AliasBinding, CapabilityRegistry, ComponentId, ComponentKind, ComponentMetadata,
DiagnosticSeverity, ModelCapabilities, ModelCatalog, ModelCatalogEntry, ModelCatalogSnapshot,
@@ -116,7 +107,7 @@ pub use registry::{
// --- Language: registry → blueprint binding façade ---
// The strict, registry-backed entry points the REPL and orchestrators use to
-// turn `.rag`/`.ragsh` source into validated blueprints. `compile_source` runs
+// turn `.rag` source into validated blueprints. `compile_source` runs
// parse -> compile -> registry-bind in one call.
pub use language::capability_resolver::{
CapabilityResolver, bind_capabilities, bind_capabilities_with_registry,
@@ -271,26 +262,3 @@ pub use graph::testkit::{
assert_graph, failing_node, fanout_node, interrupting_node, noop_node, run_recorded,
scripted_route_node, scripted_update_node, subagent_fake_node, subgraph_test_node,
};
-
-// --- REPL language `.ragsh` Rhai session runtime (feature = "repl") ---
-// The imperative orchestration surface. Gated behind the `repl` feature so the
-// default build does not pull in the embedded Rhai engine. `ReplSession` here is
-// the scripting session from `repl::session`; the line-oriented command session
-// remains available as `repl::ReplSession`.
-#[cfg(feature = "repl")]
-pub use repl::session::{
- LanguageCompiler, ReplCallKind, ReplCallRecord, ReplCancelFlag, ReplCapabilities, ReplPolicy,
- ReplResult, ReplSession, ReplValue, ReplVariables,
-};
-
-// --- RLM runtime (feature = "rlm") ---
-// The recursive-language-model surface: a driver model writes code cells that
-// run in a sandboxed interpreter (embedded Rhai or an external Python/Node
-// process) whose only host surface is capability calls (`llm`, `tool`,
-// `agent`) back into the registry. Config-driven end to end (`RlmConfig`).
-#[cfg(feature = "rlm")]
-pub use rlm::{
- CellOutcome, HostCall, InterpreterSpec, RlmCallKind, RlmCallRecord, RlmCancelFlag, RlmConfig,
- RlmHost, RlmHostApi, RlmInterpreter, RlmOutcome, RlmPolicy, RlmRunner, RlmSession, RlmStep,
- RlmStopReason, RlmTemplate, TemplateSpec,
-};
diff --git a/src/registry/capability/mod.rs b/src/registry/capability/mod.rs
index b79ce6c1..d3e85ad4 100644
--- a/src/registry/capability/mod.rs
+++ b/src/registry/capability/mod.rs
@@ -3,15 +3,15 @@
//!
//! This is where a name like `"researcher"` or `"summarize"` becomes a real,
//! callable handle. By registering capabilities here and then handing the
-//! registry to the language layer, a parent run lets a `.rag` blueprint or
-//! `.ragsh` line spawn sub-models, sub-agents, and sub-graphs it never
+//! registry to the language layer, a parent run lets a `.rag` blueprint or a
+//! host session spawn sub-models, sub-agents, and sub-graphs it never
//! hardcoded — while the registry's allowlist guarantees those references can
//! only resolve to capabilities a human actually registered.
//!
//! See [`types`] for the data definitions. This module provides registration,
//! lookup, aliasing, duplicate validation, and conveniences for handing the
//! catalog's models and tools to a harness ([`to_model_registry`] /
-//! [`to_tool_registry`]) or to the `.rag`/`.ragsh` capability resolver
+//! [`to_tool_registry`]) or to the `.rag` capability resolver
//! ([`capability_resolver`]).
//!
//! [`to_model_registry`]: CapabilityRegistry::to_model_registry
@@ -372,7 +372,7 @@ impl CapabilityRegistry {
/// Returns the canonical registered names for `kind` *and* every alias of
/// that kind, in sorted, de-duplicated order.
///
- /// This is the set of names declarative `.rag`/`.ragsh` source may reference
+ /// This is the set of names declarative `.rag` source may reference
/// for `kind`: both the canonical registration and any alias resolve to a
/// real component, so both are valid references. It backs
/// [`CapabilityResolver::from_registry`](crate::language::compiler::CapabilityResolver::from_registry).
@@ -432,7 +432,7 @@ impl CapabilityRegistry {
registry
}
- /// Builds a fully populated `.rag`/`.ragsh` [`CapabilityResolver`] from every
+ /// Builds a fully populated `.rag` [`CapabilityResolver`] from every
/// registered capability — models, tools, graph blueprints, routers, and
/// reducers, including their aliases — plus the default node kinds.
///
diff --git a/src/registry/capability/types.rs b/src/registry/capability/types.rs
index f576e875..8942cb1e 100644
--- a/src/registry/capability/types.rs
+++ b/src/registry/capability/types.rs
@@ -6,7 +6,7 @@
//! [`crate::harness::model::ModelRegistry`] and
//! [`crate::harness::tool::ToolRegistry`], which are per-run executable stores.
//! The [`CapabilityRegistry`] is a *capability catalog*: it owns named models,
-//! tools, graph blueprints, routers, and reducers so declarative `.rag`/`.ragsh`
+//! tools, graph blueprints, routers, and reducers so declarative `.rag`
//! sources can be bound by name, then validated against what Rust has actually
//! registered and allowed.
diff --git a/src/registry/component/mod.rs b/src/registry/component/mod.rs
index d89fc83c..81da3482 100644
--- a/src/registry/component/mod.rs
+++ b/src/registry/component/mod.rs
@@ -3,7 +3,7 @@
//! These are the vocabulary of the recursive catalog: a [`ComponentKind`]
//! ([`Model`](ComponentKind::Model), [`Tool`](ComponentKind::Tool),
//! [`Graph`](ComponentKind::Graph), [`Agent`](ComponentKind::Agent), …) plus a
-//! [`ComponentId`] name is exactly what a `.rag`/`.ragsh` reference carries, and
+//! [`ComponentId`] name is exactly what a `.rag` reference carries, and
//! [`ComponentMetadata`] is the durable, serializable description that lets a
//! capability be discovered, listed, and bound by name long after the process
//! that registered it has exited.
diff --git a/src/registry/mod.rs b/src/registry/mod.rs
index 01549f88..643e8307 100644
--- a/src/registry/mod.rs
+++ b/src/registry/mod.rs
@@ -1,9 +1,9 @@
//! Registry coordination and discovery primitives — the **named capability
//! catalog** that makes TinyAgents recursive.
//!
-//! In the recursive (RLM-style) architecture, a model, agent, or graph can
-//! reach for capabilities it never hardcoded: a `.rag` blueprint or `.ragsh`
-//! REPL line references a model/tool/agent/graph *by name*, and the registry is
+//! In the recursive architecture, a model, agent, or graph can reach for
+//! capabilities it never hardcoded: a `.rag` blueprint (or a host orchestration
+//! session) references a model/tool/agent/graph *by name*, and the registry is
//! what resolves that name to a real, Rust-registered handle. By owning the set
//! of legal names, the registry is also the boundary that makes agent-authored
//! plans safe to compile — a self-authored workflow can only bind to
@@ -13,7 +13,7 @@
//! two complementary pieces:
//!
//! - [`CapabilityRegistry`] ([`capability`]) — the name-addressable catalog of
-//! models, tools, graph blueprints, routers, and reducers that `.rag`/`.ragsh`
+//! models, tools, graph blueprints, routers, and reducers that `.rag`
//! sources bind against, plus the discovery [`component`] types
//! ([`ComponentKind`]/[`ComponentId`]/[`ComponentMetadata`]) that describe
//! what is registered.
diff --git a/src/repl/mod.rs b/src/repl/mod.rs
deleted file mode 100644
index 34a20277..00000000
--- a/src/repl/mod.rs
+++ /dev/null
@@ -1,303 +0,0 @@
-//! REPL language (`.ragsh`) — capability-bound interactive orchestration; the
-//! RLM/CodeAct surface of the runtime.
-//!
-//! `.ragsh` is TinyAgents' answer to the Recursive Language Model execution
-//! model: instead of stuffing everything into one context window, an
-//! orchestrator (a human, or a model acting as one) drives a session by issuing
-//! small typed commands — set/get session *values*, load and compile a `.rag`
-//! blueprint, run a graph, or `call` a registered capability — inspecting each
-//! [`ReplOutcome`] and iterating. Because every capability-bearing command is
-//! checked against a [`CapabilityPolicy`] allowlist before it can touch the
-//! runtime, the same surface is safe to expose to a model that is recursively
-//! orchestrating sub-models, sub-agents, and sub-graphs from inside a run.
-//!
-//! The REPL language is the interactive, session-oriented counterpart to the
-//! declarative `.rag` expressive language. An operator (human or parent
-//! orchestrator) drives a harness/graph session by issuing typed commands that
-//! are policy-checked before they reach the runtime.
-//!
-//! This module is currently at **milestone R1** (Documentation and Types). It
-//! establishes the command grammar, a line-oriented parser, a session/capability
-//! boundary, and structured outcomes. Commands that need live harness/graph
-//! integration are policy-checked and returned as [`ReplOutcome::Planned`]
-//! rather than executed; wiring to the live harness/graph runtime is deferred to
-//! milestones R2–R6.
-//!
-//! # Grammar
-//!
-//! ```text
-//! line = verb ( ws+ arg )* ws*
-//! verb = [a-zA-Z][a-zA-Z0-9_-]*
-//! arg = quoted | bare
-//! quoted = '"' ( | '\\' )* '"'
-//! bare = ( )+
-//! ```
-//!
-//! The first token is the command verb (matched case-insensitively). Subsequent
-//! tokens are positional arguments. For the `call` verb the *remainder* of the
-//! line after the capability name is parsed as a single JSON value, so
-//! multi-token JSON objects and arrays are accepted verbatim.
-//!
-//! ## Verb table
-//!
-//! | Verb | Signature | Notes |
-//! |------------|----------------------------------|--------------------------------|
-//! | `help` | `help` | Also: `?` |
-//! | `quit` | `quit` | Also: `exit`, `q` |
-//! | `load` | `load ` | Requires `"load"` capability |
-//! | `compile` | `compile ` | Requires `"compile"` capability|
-//! | `run` | `run ` | Requires `"run"` capability |
-//! | `set` | `set ` | `` may be quoted |
-//! | `get` | `get ` | |
-//! | `show` | `show vars\|graphs\|status` | |
-//! | `call` | `call ` | JSON may span multiple tokens |
-
-pub mod types;
-
-pub use types::*;
-
-/// Rhai-backed `.ragsh` session runtime (the imperative RLM/CodeAct surface).
-///
-/// This is the evolution of the line-oriented command REPL above into a full
-/// scripting session with a persistent namespace, policy-bounded capability
-/// calls, and typed cell results — the [`session::ReplSession`] described in the
-/// module design document. It is gated behind the `repl` cargo feature so the
-/// default build stays free of the embedded Rhai engine.
-///
-/// The command-driven [`ReplSession`](crate::repl::ReplSession) above remains
-/// available for the line-oriented REPL; the scripting engine is exposed as
-/// [`session::ReplSession`] (and re-exported at the crate root as
-/// [`crate::ReplSession`] when the feature is enabled) to keep both surfaces
-/// compiling side by side.
-#[cfg(feature = "repl")]
-pub mod session;
-
-// Re-export the non-colliding session types at the `repl` root for convenience.
-// `session::ReplSession` is intentionally *not* re-exported here because the
-// line-oriented `ReplSession` (above) already occupies that name in the default
-// build; reach the scripting session via `repl::session::ReplSession` or the
-// crate-root `crate::ReplSession` re-export.
-#[cfg(feature = "repl")]
-pub use session::{
- LanguageCompiler, ReplCallKind, ReplCallRecord, ReplCapabilities, ReplPolicy, ReplResult,
- ReplValue, ReplVariables,
-};
-
-#[cfg(test)]
-mod test;
-
-// ── Public parser ─────────────────────────────────────────────────────────────
-
-/// Parse a single `.ragsh` REPL command line into a [`ReplCommand`].
-///
-/// Leading and trailing whitespace is ignored. The first token is matched
-/// case-insensitively against the verb table. For `call`, the remainder of
-/// the line after the capability name is parsed as a JSON value.
-///
-/// # Errors
-///
-/// Returns [`crate::error::TinyAgentsError::Parse`] for:
-///
-/// * empty input
-/// * unknown verb
-/// * missing required argument(s)
-/// * unterminated quoted string
-/// * invalid JSON argument to `call`
-pub fn parse_command(line: &str) -> crate::error::Result {
- let trimmed = line.trim();
- if trimmed.is_empty() {
- return Err(parse_err_at(trimmed, trimmed, "empty input"));
- }
-
- let (verb, rest) = split_token(trimmed, trimmed)?;
-
- match verb.to_lowercase().as_str() {
- "help" | "?" => Ok(ReplCommand::Help),
-
- "quit" | "exit" | "q" => Ok(ReplCommand::Quit),
-
- "load" => {
- let (path, _) = require_token(trimmed, rest, "load ")?;
- Ok(ReplCommand::Load { path })
- }
-
- "compile" => {
- let (name, _) = require_token(trimmed, rest, "compile ")?;
- Ok(ReplCommand::Compile { name })
- }
-
- "run" => {
- let (graph, rest) = require_token(trimmed, rest, "run ")?;
- let (input, _) = require_token(trimmed, rest, "run ")?;
- Ok(ReplCommand::Run { graph, input })
- }
-
- "set" => {
- let (key, rest) = require_token(trimmed, rest, "set ")?;
- let (value, _) = require_token(trimmed, rest, "set ")?;
- Ok(ReplCommand::Set { key, value })
- }
-
- "get" => {
- let (key, _) = require_token(trimmed, rest, "get ")?;
- Ok(ReplCommand::Get { key })
- }
-
- "show" => {
- let (what, _) = require_token(trimmed, rest, "show ")?;
- Ok(ReplCommand::Show { what })
- }
-
- "call" => {
- let (capability, json_rest) = require_token(trimmed, rest, "call ")?;
- let json_str = json_rest.trim();
- if json_str.is_empty() {
- return Err(parse_err_at(
- trimmed,
- json_rest,
- "call requires a JSON argument: call ",
- ));
- }
- let args: serde_json::Value = serde_json::from_str(json_str).map_err(|e| {
- parse_err_at(
- trimmed,
- json_str,
- &format!("invalid JSON argument for `call`: {e}"),
- )
- })?;
- Ok(ReplCommand::Call { capability, args })
- }
-
- other => Err(parse_err_at(
- trimmed,
- trimmed,
- &format!("unknown command `{other}`"),
- )),
- }
-}
-
-// ── Private helpers ───────────────────────────────────────────────────────────
-
-/// Build a [`crate::error::TinyAgentsError::Parse`] with the given message and
-/// optional source position.
-fn parse_err(message: &str, line: usize, column: usize) -> crate::error::TinyAgentsError {
- crate::error::TinyAgentsError::Parse {
- message: message.to_string(),
- line,
- column,
- }
-}
-
-/// Builds a [`crate::error::TinyAgentsError::Parse`] pointing at `at` — a
-/// substring slice of `origin` — reporting a real 1-based line/column instead
-/// of the placeholder `(0, 0)`.
-///
-/// `parse_command` always parses a single command line, so the line is always
-/// `1`; the column is the 1-based character offset of `at` within `origin`.
-/// Falls back to the end of `origin` if `at` is not actually a subslice of it
-/// (defensive; should not happen given how callers use this).
-fn parse_err_at(origin: &str, at: &str, message: &str) -> crate::error::TinyAgentsError {
- let column = char_column(origin, at);
- parse_err(message, 1, column)
-}
-
-/// Computes the 1-based character column of `at` within `origin`, where `at`
-/// is a substring slice of `origin` obtained by slicing (not copying).
-fn char_column(origin: &str, at: &str) -> usize {
- let origin_start = origin.as_ptr() as usize;
- let origin_end = origin_start + origin.len();
- let at_start = at.as_ptr() as usize;
- let offset = if at_start >= origin_start && at_start <= origin_end {
- at_start - origin_start
- } else {
- // `at` isn't a subslice of `origin` (shouldn't happen); point at the end.
- origin.len()
- };
- origin[..offset].chars().count() + 1
-}
-
-/// Split the next token from `s`, returning `(token, remainder)`.
-///
-/// Handles quoted strings (`"..."`) with `\\`, `\"`, `\n`, `\t` escapes.
-/// Bare tokens end at the first whitespace character.
-///
-/// Returns `None` if `s` (after trimming leading whitespace) is empty.
-///
-/// `origin` is the full trimmed command line `s` was sliced from; it is used
-/// only to compute a real 1-based column for any parse error, via
-/// [`parse_err_at`].
-fn split_token<'a>(origin: &str, s: &'a str) -> crate::error::Result<(String, &'a str)> {
- let s = s.trim_start();
- if s.is_empty() {
- return Err(parse_err_at(origin, s, "unexpected end of input"));
- }
-
- if s.starts_with('"') {
- // Quoted string: scan from after the opening quote.
- // The labeled block returns the byte offset of the closing `"` within
- // `inner` so we can compute the remainder slice without a mutable
- // Option accumulator (which would trigger an unused-assignment warning).
- let inner = s
- .strip_prefix('"')
- .expect("already checked starts_with('\"')");
- let mut token = String::new();
-
- let inner_offset: usize = 'scan: {
- let mut chars = inner.char_indices();
- loop {
- match chars.next() {
- None => {
- return Err(parse_err_at(origin, s, "unterminated quoted string"));
- }
- Some((i, '"')) => break 'scan i,
- Some((_, '\\')) => match chars.next() {
- Some((_, '"')) => token.push('"'),
- Some((_, '\\')) => token.push('\\'),
- Some((_, 'n')) => token.push('\n'),
- Some((_, 't')) => token.push('\t'),
- Some((_, c)) => {
- token.push('\\');
- token.push(c);
- }
- None => {
- return Err(parse_err_at(origin, s, "unterminated escape sequence"));
- }
- },
- Some((_, c)) => token.push(c),
- }
- }
- };
-
- // Skip the opening `"` (1 byte) + bytes up to closing `"` + the closing `"` itself.
- let remainder = &s[1 + inner_offset + 1..];
- Ok((token, remainder))
- } else {
- // Bare word: ends at the first whitespace.
- let end = s
- .char_indices()
- .find(|(_, c)| c.is_whitespace())
- .map(|(i, _)| i)
- .unwrap_or(s.len());
- let token = s[..end].to_string();
- let remainder = &s[end..];
- Ok((token, remainder))
- }
-}
-
-/// Like [`split_token`] but returns a [`crate::error::TinyAgentsError::Parse`]
-/// mentioning the expected usage if the remaining input is empty.
-fn require_token<'a>(
- origin: &str,
- s: &'a str,
- usage: &str,
-) -> crate::error::Result<(String, &'a str)> {
- let s = s.trim_start();
- if s.is_empty() {
- return Err(parse_err_at(
- origin,
- s,
- &format!("missing argument — usage: {usage}"),
- ));
- }
- split_token(origin, s)
-}
diff --git a/src/repl/session/builtins/authoring.rs b/src/repl/session/builtins/authoring.rs
deleted file mode 100644
index e49970e0..00000000
--- a/src/repl/session/builtins/authoring.rs
+++ /dev/null
@@ -1,234 +0,0 @@
-//! Graph-authoring implementations (`graph_define`, `graph_validate`,
-//! `graph_compile`, `graph_diff`, `graph_register`) lowering through the
-//! expressive-language compiler and capability resolver.
-//!
-//! Split out of `session/builtins/mod.rs`; see that module's doc comment
-//! for the full built-in surface and the blocking-bridge design.
-
-use super::*;
-
-// ── Graph-authoring implementations ─────────────────────────────────────────
-
-pub(super) fn graph_define_impl(
- ctx: &HostContext,
- params: &Map,
-) -> Result> {
- let name =
- map_str(params, "name").ok_or_else(|| invalid(ctx, "graph_define: missing `name`"))?;
- let source =
- map_str(params, "source").ok_or_else(|| invalid(ctx, "graph_define: missing `source`"))?;
-
- // Check the limit up front (without consuming a slot) so a session that
- // has already hit the cap fails fast instead of paying for a parse and
- // compile it can't keep the result of anyway.
- if ctx.counters.lock().expect("counters poisoned").graph_def >= ctx.policy.max_graph_definitions
- {
- return Err(raise(
- ctx,
- TinyAgentsError::LimitExceeded(format!(
- "graph definition limit ({}) exceeded",
- ctx.policy.max_graph_definitions
- )),
- ));
- }
- if source.len() > ctx.policy.max_script_bytes {
- return Err(raise(
- ctx,
- TinyAgentsError::LimitExceeded(format!(
- "graph source is {} bytes, exceeding max_script_bytes ({})",
- source.len(),
- ctx.policy.max_script_bytes
- )),
- ));
- }
-
- let label = ctx
- .language
- .as_ref()
- .map(|l| l.provenance_label.clone())
- .unwrap_or_else(|| ctx.session_label.clone());
- let origin = Origin::generated_by(label);
- let program = parse_str(&source).map_err(|err| raise(ctx, err))?;
- let blueprints =
- compile_with_provenance(&program, origin.clone()).map_err(|err| raise(ctx, err))?;
- let blueprint = blueprints
- .into_iter()
- .find(|b| b.graph_id == name)
- .ok_or_else(|| {
- invalid(
- ctx,
- format!("graph_define: source has no graph named `{name}`"),
- )
- })?;
-
- // The draft is about to be recorded successfully; consume a slot now
- // (re-checking the limit under the same lock to guard against a
- // concurrent `graph_define` racing between the check above and here).
- {
- let mut counters = ctx.counters.lock().expect("counters poisoned");
- if counters.graph_def >= ctx.policy.max_graph_definitions {
- return Err(raise(
- ctx,
- TinyAgentsError::LimitExceeded(format!(
- "graph definition limit ({}) exceeded",
- ctx.policy.max_graph_definitions
- )),
- ));
- }
- counters.graph_def += 1;
- }
-
- let handle = GraphBlueprintHandle {
- name: blueprint.graph_id.clone(),
- source,
- blueprint: blueprint.clone(),
- origin,
- compiled: false,
- requires_review: ctx.policy.generated_graphs_require_review,
- };
- ctx.drafts
- .lock()
- .expect("drafts poisoned")
- .insert(handle.name.clone(), handle.clone());
- record(
- ctx,
- new_call_id(),
- ReplCallKind::Graph,
- "graph_define",
- json!({ "name": handle.name }),
- Duration::default(),
- );
- Ok(draft_descriptor(&handle))
-}
-
-/// Builds the script-visible descriptor map for a graph draft (carrying its
-/// name, node count, and compile/review status). The opaque
-/// [`GraphBlueprintHandle`] itself lives host-side in `ctx.drafts`.
-fn draft_descriptor(handle: &GraphBlueprintHandle) -> Dynamic {
- let mut map = Map::new();
- map.insert("name".into(), Dynamic::from(handle.name.clone()));
- map.insert(
- "nodes".into(),
- Dynamic::from(handle.blueprint.nodes.len() as i64),
- );
- map.insert("compiled".into(), Dynamic::from(handle.compiled));
- map.insert(
- "requires_review".into(),
- Dynamic::from(handle.requires_review),
- );
- Dynamic::from_map(map)
-}
-
-/// Looks up a graph draft by the `name` field of a descriptor map.
-pub(super) fn lookup_draft(
- ctx: &HostContext,
- descriptor: &Map,
- func: &str,
-) -> Result> {
- let name = map_str(descriptor, "name")
- .ok_or_else(|| invalid(ctx, format!("{func}: descriptor is missing `name`")))?;
- ctx.drafts
- .lock()
- .expect("drafts poisoned")
- .get(&name)
- .cloned()
- .ok_or_else(|| invalid(ctx, format!("{func}: no graph draft named `{name}`")))
-}
-
-pub(super) fn graph_validate_impl(
- ctx: &HostContext,
- descriptor: &Map,
-) -> Result> {
- let handle = lookup_draft(ctx, descriptor, "graph_validate")?;
- let program = parse_str(&handle.source).map_err(|err| raise(ctx, err))?;
- let diagnostics = Resolver::from_registry(&*ctx.registry).resolve_program(&program);
- let array: Array = diagnostics
- .iter()
- .map(|d| Dynamic::from(d.message.clone()))
- .collect();
- Ok(Dynamic::from_array(array))
-}
-
-pub(super) fn graph_compile_impl(
- ctx: &HostContext,
- descriptor: &Map,
-) -> Result> {
- let mut handle = lookup_draft(ctx, descriptor, "graph_compile")?;
- // Bind the blueprint through the same resolver gate file-backed `.rag`
- // source passes — generated topology is never trusted blindly.
- Resolver::from_registry(&*ctx.registry)
- .resolve_blueprint(&handle.blueprint)
- .map_err(|err| raise(ctx, err))?;
- handle.compiled = true;
- handle.requires_review = ctx.policy.generated_graphs_require_review;
- ctx.drafts
- .lock()
- .expect("drafts poisoned")
- .insert(handle.name.clone(), handle.clone());
- record(
- ctx,
- new_call_id(),
- ReplCallKind::Graph,
- "graph_compile",
- json!({ "name": handle.name, "requires_review": handle.requires_review }),
- Duration::default(),
- );
- Ok(draft_descriptor(&handle))
-}
-
-pub(super) fn graph_diff_handles(
- ctx: &HostContext,
- old: &Blueprint,
- new: &Blueprint,
-) -> Result> {
- let diff = blueprint_diff(old, new);
- let value = serde_json::to_value(&diff)
- .map_err(|err| raise(ctx, TinyAgentsError::Validation(err.to_string())))?;
- Ok(repl_value_to_dynamic(&json_to_repl_value(&value)))
-}
-
-pub(super) fn graph_register_impl(
- ctx: &HostContext,
- params: &Map,
-) -> Result> {
- let graph = params
- .get("graph")
- .and_then(|d| d.read_lock::