ares-server 0.10.0

ARES agent server with multi-provider LLM support, tool calling, RAG, and MCP integration
Documentation
# Rhai as default for ARES tools + Cordis

Rhai `1.25` `serde` is the default `tool_type` for `runtime_tools` and the safe HMR bridge for Cordis.

## Why Rhai

- `toon`/`toml` are data, not programs, no `if`/`for`/`fn`. Rhai gives `fn execute(args){ if args["amount"]>3*avg { "CRITICAL" } }` with `Scope` isolation.
- `boa_engine 0.20` (JS) + `python3 -c` subprocess are two runtimes, `fork` + `GIL` + `python3` on VPS. Rhai is `~500KB` pure Rust, `AST` compiles once at `RuntimeToolRegistry::materialise`, reused via `Engine::call_fn(&mut scope, &ast, "execute", (Dynamic,))` inside `spawn_blocking` + `timeout` + `max_operations`.
- Cordis `libloading` HMR (`hmr.rs` `dlopen` `unsafe`) is deferred. Rhai `AST` is `Send+Sync`, `compile` pure, `Scope::rewind_scope(true)` gives LIFO `EffectGuard` discipline, `watch_many` hashes `script` for `Fiber::refresh` epoch.

## Tool usage

DB `runtime_tools` row:
```json
{
  "tool_type": "rhai",
  "parameters_schema": {"type":"object","properties":{"a":{"type":"number"}}},
  "execution_config": {
    "script": "fn execute(args){ args[\"a\"] + args[\"b\"] }",
    "entry": "execute",
    "max_ops": 50000,
    "timeout_ms": 2000
  }
}
```

Validation: `RuntimeToolRegistry::validate_execution_config("rhai", &value)` does `Engine::new().set_max_operations(...).compile(script)` and returns `400` with `line:col` on syntax error.

Execution: `RhaiTool::execute(Value) -> Value`
- `Value` -> `Dynamic` via `rhai::serde::to_dynamic`
- `Scope::push("args", dynamic)` plus individual keys
- `engine.call_fn(&mut scope, &ast, &entry, (dynamic,))` inside `tokio::task::spawn_blocking` with `tokio::time::timeout`
- `Dynamic` -> `Value` via `rhai::serde::from_dynamic` / `serde_json::to_value`, fallback `Null` for `()`

Limits: `max_operations 50k`, `max_string 8192`, `max_call_levels 64`, `max_expr_depth 128`, `on_print false`, no `eval`, no `fs`/`net` unless registered. Module allow-list only `json`, `math`.

Tests: `crates/ares-tools/src/rhai_tool.rs` 10 tests `cargo test -p ares-tools --lib rhai_tool --features postgres,mcp`.

## Cordis bridge

`crates/cordis/src/rhai_service.rs` `#[cfg(feature="rhai")]` `RhaiService{engine Arc<Engine>, ast AST}` `impl Service { name/init/check via call_fn }` + `RhaiPlugin: Plugin<Config=RhaiServiceConfig, Provides=RhaiService>` `apply: ctx.provide(service)`.

Feature: `cordis/rhai = ["dep:rhai"]` optional, default `inventory`. `cargo check --features rhai` and `cargo test --features rhai` 27 tests `hmd`.

Loader: `Entry{plugin:"rhai:my_tool", config: Value::String(script)}`, `Loader::reconcile` hashes `script`, `Fiber::refresh` epoch `:uid` changes → `reload()` without `libloading`.

Hot-reload: `AST` compiled once at `build_and_swap`, `ArcSwap` readers get `Arc<RhaiTool>`. Source stored in `runtime_tool_versions`, recomputed on `reload()`.

## Call-fn reference

```rust
let ast = engine.compile(script)?;
let mut scope = Scope::new();
scope.push("args", dynamic);
let result: Dynamic = engine.call_fn(&mut scope, &ast, "execute", (dynamic,))?;
// options:
CallFnOptions::new().eval_ast(true).rewind_scope(true).bind_this_ptr(&mut ctx)
```

Limits: `engine.set_max_operations(50000)`, `set_max_string_size`, `set_max_call_levels`, `set_max_expr_depth`, `on_progress`.

See `https://rhai.rs/book/engine/call-fn.html` for `Scope` rewind, `FuncArgs`, `this_ptr`.