The workflow runtime · v2026.2.5
tuvl തൂവൽ · "feather"

Deterministic, Declarative
Workflows from YAML.

One runtime, one contract for functions and AI. Compose deterministic functions, reasoning agents, tools, and live supervision into a single governed flow — declared in YAML, observable and secured by default.

Functions · Agents · Tools AutonomousAgent · Supervisor MCP · pgvector RAG Any model via LiteLLM
tuvl
The runtime

Nine step kinds. One YAML contract.

No orchestration framework to assemble, nothing to glue. The engine ships the whole surface — deterministic functions and governed AI — as a closed set of composable primitives, each mounted as a live API route at startup.

Functions · deterministic
  • Functional
  • Router
  • APICall
  • MCP
  • ModelOp
  • Response
  • HumanInTheLoop

The backbone of every workflow: run your code, branch on data, call APIs and MCP tools, do CRUD on Postgres, pause for a human, and shape the response.

Reasoning · governed
  • Agent
  • AutonomousAgent

Bring a model in for a single governed call, or a bounded tool-loop that decides for itself — placed only on the steps that genuinely need judgment.

Supervisor LLM-as-a-Judge OpenTelemetry Biscuit IAM Multi-tenant RLS
Every step is a declared, observable, secured primitive — reason · act · govern · evaluate · secure.
tuvl
Functions · the deterministic core

Real work, declared — no model required.

Most of a workflow is deterministic, and tuvl treats that as first-class. Seven step kinds do the actual work — fast, testable, and free of any token cost.

Functional

Your Python, as a step

One @node per file, typed context in and out. Drop in any logic the declarative kinds don't cover.

Router

Deterministic branching

A binary condition: or a multi-way match: switch — routing decided by data, never by a prompt.

APICall

Call any HTTP API

Templated URL, headers, and body; extract JSON straight into context with declared paths.

ModelOp

Declarative CRUD

Create / get / list / update / delete on your Postgres models — IAM-scoped, no query code.

Response

Shape the output

Map context into the terminal HTTP response — the workflow's public contract, declared.

HumanInTheLoop

Durable pause / resume

Suspend on a human decision and resume exactly where it stopped — replay-proof, state intact.

The backbone of every workflow — and none of it calls a model.
tuvl
Reasoning · Agents

Two ways to bring a model into the flow.

kind: Agent

A single governed LLM call

Declared model, prompt, and structured output; deterministic routing on the result. Every model it touches must be allow-listed in spec.context.models — enforced at load, or it fails to start.

kind: AutonomousAgent

A bounded ReAct tool-loop

The model picks declared tools, observes results, and re-decides — until it emits an author-defined outcome or a reserved exit. Guided by steering and per-agent skills, bounded by max_iterations + token_budget.

outcome.enum
author-defined exits, each routed deterministically
4 reserved exits
max_iterations · budget_exceeded · error · aborted
steering + skills
per-agent scoped .md instructions
0 new deps
LiteLLM native tool-calling — no torch, no langchain
Add intelligence exactly where a step needs judgment — and nowhere else.
tuvl
Tools & MCP

An agent's tools are your own declared steps.

Autonomy is bounded by construction: the agent chooses which tool to call, but never what tools exist. Every tool is a real, governed step — nothing is a free-floating function.

Steps as tools

Wire, don't code

Any Functional / APICall / ModelOp becomes a tool by wiring it to the agent. A mandatory description tells the model when to call it.

kind: MCP

External tools, native

Connect any MCP server over stdio or SSE — GitHub, filesystems, internal services — and its tools become callable steps in the same flow.

Closed & governed

Bounded toolset

The toolset is a fixed, author-declared set. Results return to the agent; a tool merges into shared context only with writes_context.

Grounding, too — pgvector RAG and any model via LiteLLM. The agent acts through the same primitives you already trust.
tuvl
Governance · the Supervisor

A supervisor you drop onto the canvas.

Add one off-spine Supervisor node per workflow. It watches every AutonomousAgent run out-of-band and intervenes cooperatively — at the turn boundary, never mid-call, so no state is torn.

⦿ SUPERVISOR
watches this workflow's agents

Configured in the canvas — written straight to YAML

Double-click it: pick the judge model from a dropdown, set the criteria inline or as a scoped .md policy you edit in place, choose the on_violation action, and add deterministic rules. Every field serializes to spec.supervisor — no hand-written YAML.

Deterministic rules

Free, every turn

tool_repeated, budget_fraction, iteration_reached — cheap guardrails, no LLM.

LLM criteria

Every N iterations

A judge model checks the run against a natural-language policy — or a scoped .md — and flags violations.

Interventions

pause · steer · abort

Steer a drifting run back, pause it, or exit cleanly via aborted. Every action logged and streamed.

Plus a human escape hatch — /api/agents: observe, pause, resume, steer, or kill any live run. Runaway agents can't run away.
tuvl
Evaluate & observe

Grade the output. Trace every turn.

Non-deterministic output needs deterministic verdicts and full visibility. tuvl builds both in — one judge core, and OpenTelemetry from the first span.

LLM-as-a-Judge. One strict Scorecard{passed, reason} — grade a workflow in CI tests, live inside the Supervisor, or on a replayed run from the Insight portal. The same verdict, everywhere.
OpenTelemetry by default. LiteLLM emits gen_ai.* semantic-convention spans — model, tokens, latency — exported via TUVL_OTLP_ENDPOINT to Grafana, Datadog, or Honeycomb.
Per-iteration agent trace. An autonomous run streams iteration / tool_call / outcome / supervisor frames live over SSE and gRPC — not one opaque step.
Structured logs + PII masking. structlog logfmt/JSON correlated to the trace; fields marked secure: true masked on every stream snapshot — REST, SSE, gRPC alike.
Gradeable and observable by construction — not after you bolt on a tracer.
tuvl
The payoff · token economy

A structure that happens to cost far less.

Because deterministic functions carry most of the flow and every reasoning loop is capped, you spend tokens only where they earn it — a direct result of the design, not a feature to tune.

Deterministic work is free. Routing, API calls, CRUD, and rule checks run at zero tokens — even as an agent's tools.
Every loop is capped. max_iterations + token_budget bound each run; the Supervisor kills a runaway before it burns the budget.
Right model for the job. Route a cheap local model for routine steps, a frontier model only where it pays — swapped in YAML, not code.
~40–70%
est. fewer tokens on a typical mixed workflow vs. an all-LLM design
up to ~90%
est. saved on a stuck run that would otherwise loop to its cap
100%
of routing, CRUD, and rule checks run at zero tokens
Estimates, illustrative — actual savings track how much of your flow is deterministic.
tuvl
Security · and the close

Secured at the edge, the model, and the data.

Biscuit IAM. Offline-verifiable, attenuable capabilities; every route scope-guarded, with dedicated agent:observe / agent:control scopes for the operator API. iam:admin is the only bypass.
Closed-set enforcement at load. Touch a model absent from spec.context.modelsPermissionError. Unmapped routes raise. No hidden behavior slips in.
Multi-tenant by option. Row-level tenant isolation in Postgres, enabled at init — one deployment, cleanly separated tenants.
Passes a security review. A deep audit of 86 findings — all closed. PII masking and sanitized errors on every surface.
Functions and AI, declared in YAML — governed, observable, and secure by construction.
tuvl · tuvl.dev