Cleat is a compiled, statically typed language for auditable, safe AI agents. Agents, guards, tools, streams, state machines, provenance chains, and HTTP servers are compiler-enforced primitives—not library abstractions you hope someone wired up correctly.
Most languages bolt orchestration on through frameworks. Cleat bakes it in. agent, guard, tool, stream, state, chain, server, and fn are keywords—and the type-checker has opinions about all of them.
Tools have timeouts, retries, and explicit side-effect declarations. The compiler refuses to compile a tool that lies about what it touches.
tool fetch_data(url: string) -> Result[string, string] needs { net } timeout: 10s retry: 1 { ... }
First-class streaming with typed yields and bounded buffers. No more wrapping callbacks in a Promise wrapped in a generator wrapped in regret.
stream tokens(prompt: string) -> string needs { llm } buffer: 16 { yield "chunk1" yield "chunk2" }
Declarative state machines with deadlock and reachability detection. Unreachable states won't compile. Neither will deadlocks.
state PRReview { initial: Pending Pending -> InReview { when: assigned } InReview -> Approved { when: approved && ci_green } any -> Cancelled { when: closed } terminal: Merged, Cancelled }
Append-only, signed records with retention policies. Every action your AI takes can be cryptographically attested without bolting on a separate audit pipeline.
chain AuditTrail { signing: ed25519 @retention(7y) record Entry { source: string, hash: sha256, timestamp: time } }
Agents declare their system prompt, model, tool set, and max turns inline. The compiler wires the tool-calling loop and extracts typed output from the final response — no prompt-engineering a JSON schema.
agent Reviewer { model: "claude-sonnet-4-5" tools: [read_file, list_files] max_turns: 8 system: "You are a code reviewer." }
Guards run as a pre-flight check on tool calls, agent prompts, and HTTP handlers. Return allow, deny, or transform. Composable, testable, unskippable.
guard no_secrets(input: string) -> GuardResult { if strings.contains(input, "sk-") { return Deny("api key leak") } return Allow }
Route declarations are part of the language. Handler signatures are type-checked against the route pattern. JSON in, JSON out — no framework, no codegen, no surprises.
server API { port: 8080 GET "/users/:id" -> get_user POST "/users" -> create_user middleware: [log, auth] }
Ordinary functions default to zero effects. Add needs { } to let them touch the world. A pure caller cannot silently invoke an effectful callee.
fn score(review: Review) -> int { match review.severity { High -> 3, Medium -> 2, Low -> 1, } }
Every function declares what it touches with needs. A pure function calling an effectful one is a compile error—not a runtime surprise found in production at 3am.
Tests use using to swap real effects for mocks. Type-checked, scoped, and impossible to forget to remove.
fn pure_transform(s: string) -> string { http.get(s) ^─── missing needs effect: net return strings.to_upper(s) }
fn fetch(url: string) -> Result[string, string] needs { net } { let resp = http.get(url)? return Ok(resp.body) }
type Review = struct { @description("Safe to merge?") approved: bool, severity: Severity, issues: []string, notes: string = "", } let review: Review = llm.generate(model: m, prompt: p)? // review.approved is bool, // review.issues is []string — // guaranteed at compile time.
Three lines of Cleat replace forty lines of Python with stronger safety guarantees. The compiler lifts a JSON Schema out of your struct, sends it as a tool constraint, and deserializes the response into a typed value.
Non-serializable fields (functions, channels, streams) fail at compile time. Fields with defaults drop out of the schema's required array. @description annotations steer the model. Agents use the same trick — let r: MyStruct = Agent.run(p)? runs the tool-calling loop, then extracts into MyStruct.
Everything you need to build a real backend, in the box. Each package declares its effects so callers know exactly what they're inheriting.
Pattern matching, pipes, string interpolation, named arguments. The things you actually want, without writing a 12-line type signature first.
Exhaustive, with bindings. The compiler tells you what you forgot.
match expr { Ok(v) => v, Err(e) => 0, _ => -1 }
Read left-to-right like the data flows. Compose without nesting hell.
xs |> filter(x > 0) |> map(x * 2) |> sum()
Errors are values. The question mark propagates them, no try/catch.
let data = risky_call()?
Templates with full expression support. No printf. No backticks.
"hello, ${name}! found ${items.len()} items"
Tests live next to code. cleat test just works.
test "low score is Low risk" { assert(...) }
Call sites that read like English. Defaults that document themselves.
analyze(repo: "acme", pr: 42)
Cleat compiles to native binaries through Go. One build step, zero runtime dependencies.