Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

v0.2.1 — latest release

The Keel Language

A small, statically-typed language where AI agents are first-class citizens.

Agents are primitives

The actor model is the one concurrency primitive. Per-agent serial mailboxes, isolated mutable state via self — no shared-memory races to reason about.

Small core, deep stdlib

AI calls, scheduling, HTTP, email, memory — all live in a standard library you import one line at a time with use std/<name>. The core language stays tiny.

Capabilities, deny-by-default

An agent can only touch what its @tools list declares. Undeclared effects are compile-time errors — every program is auditable at a glance.

Statically typed

Full inference, exhaustive pattern matching, nullable safety, no implicit any. Misconfiguration fails loud at startup — never with plausible nonsense at runtime.

A first agent

Everything you need to triage and reply to email — model calls, human-in-the-loop, scheduling — in one file:

use std/ai
use std/email
use std/io
use std/schedule

type Urgency = low | medium | high | critical

agent EmailAssistant {
  @role "Professional email assistant"
  @tools [ai, io, email]

  on message(msg: Message) {
    urgency = ai.classify(msg.body, as: Urgency) ?? Urgency.medium

    when urgency {
      low, medium => {
        reply = ai.draft("response to {msg.body}", tone: "friendly")
        if io.confirm(reply) { email.send(reply, to: msg.from) }
      }
      high, critical => {
        io.notify("{urgency}: {msg.subject}")
        guidance = io.ask("How should I respond?")
        reply = ai.draft("response to {msg.body}", guidance: guidance)
        if io.confirm(reply) { email.send(reply, to: msg.from) }
      }
    }
  }

  @on_start {
    schedule.every(5.minutes, () => {
      for email in email.fetch(unread: true) {
        send(self, email.as_message())
      }
    })
  }
}

run(EmailAssistant)

Four imports, one capability list, and the whole program is auditable at a glance: this agent can call models, talk to a human, and send email — and nothing else.

Design Principles

  1. Small core, deep stdlib. If a feature can be a library, it is one. The core language has a small reserved keyword set; everything else is a std/ module imported with the same use syntax as your own files.
  2. Agents are primitives. agent is the only concurrency model. Per-agent serial mailboxes with isolated mutable state via self.
  3. Capabilities are deny-by-default. Effectful modules must be declared in @tools before an agent can use them. Undeclared calls are compile-time errors, not surprises in production.
  4. Interfaces everywhere. LLM providers, memory stores, HTTP clients, loggers — all behind interfaces. Users swap implementations without leaving the language.
  5. Statically typed. Full inference. Exhaustive pattern matching. Nullable safety. No implicit any.
  6. No silent fallbacks. Misconfiguration fails loud at startup, not with plausible-looking nonsense at runtime.

Try It

Install and run your first agent:

curl https://keel-lang.dev/install.sh | sh
keel run examples/showcase.keel

Installation

Versioning and Breaking Changes

Alpha. Keel is pre-1.0. Semver is not respected between 0.x minor versions, and breaking changes can land in patch releases.

  • 0.2.x — current alpha. One module system (use std/<name> and local file imports), deny-by-default @tools, built-in test blocks.
  • 0.x — further pre-1.0 releases will keep breaking things where the design demands it. The changelog flags every break.
  • 1.0.x — first API-stable release. Semver begins.

See SPEC.md for the authoritative design and ROADMAP.md for the plan.