# ☕ Expresso **Deploy LLM-in-the-loop automations that provably act _exactly once_ and replay for audit — no queue, no Temporal.** Write a few rules. `expresso serve` it. Point a webhook (Slack, Stripe, GitHub, cron) at it. For each event it makes a **typed AI judgment** (journaled), then fires a **real action** (webhook / Slack / your API) — and that action lands **exactly once** even across retries, a redelivery, a process restart, and a `kill -9` mid-send. The whole history **replays** for your auditor. Zero dependencies. What makes it a *language*, not a config file: the line between "guaranteed" and "AI judgment" is a **type system** (`PURE ⊑ MODEL ⊑ WORLD`), so the compiler *proves* the exactly-once and replay properties and statically bounds each agent's blast radius (`grants { … }`). > Expresso is to LLM automations what **Temporal** is to workflows — minus the cluster, the SDK, and the ceremony. ## The guarantees (proved by the checker + runtime — not hoped for) Every leaf is statically typed `PURE ⊑ MODEL ⊑ WORLD`. From that: - **T1** — an agent with no AI step is a deterministic function of the event log. - **T2** — AI answers are journaled on first run and **replayed, never re-called**. - **T3** — every `WORLD` action carries a replay-stable idempotency key, so replay and re-delivery **never double-act**. The host (`host.js`) closes the crash window with a write-ahead log: the action is journaled as `intent` **before** the side effect and marked `done` **after** it succeeds, so a crash mid-send re-delivers (at-least-once) instead of losing the action — and the `Idempotency-Key` header makes the receiver idempotent too, giving end-to-end exactly-once. Proven by `examples/serve-demo/demo.sh` (outage + `kill -9`). Plus capability typing with **two enforced classes** (`grants { … }` — no ambient authority): *action* capabilities (`tasks.create`, `notify.send`, …) gate what an agent may do, and *observation* capabilities (`ai.classify`, `ai.summarize`, `ai.match`) gate what it may learn through a model — so one `grants` block is the agent's entire reviewed surface. A declared ceiling (`at most pure|model|world`) is enforced at compile time, with `E_CEILING` / `E_GRANT` / `E_TYPE` diagnostics. Typed AI outputs cover records, enums, optionals and `list<…>`, and `for item in r.items { … }` fans out with per-iteration idempotency keys — N extracted items are N exactly-once actions. ## What's here | File | What it is | |---|---| | `expresso.js` | The engine: lexer · parser · **effect type-checker** · typed model leaves · journaled streaming **`session()`** runtime · idempotent world layer · `replay` (re-derives & **compares** bit-for-bit) · execution trace. Zero deps; Node + browser. | | `SPEC.md` | Grammar (EBNF), event model, **small-step operational semantics**, the effect type rules, the **theorems** with proofs, a hand-verifiable worked example, and honest open problems. | | `ROADMAP.md` | The phased plan to v1.0 (real model provider, durable transport, watermarks, real Coffee integration) — with a status banner for what's already built. | | `examples.js` | Five playground programs in the real grammar: the flagship **meeting → follow-up** (one AI read fans out into exactly-once tasks), incident paging, refund guard, secret-leak warden, and a **Safety check** that fails to compile with four distinct errors. | | `host.js` | The **deployment host** (Node): real sinks (webhook · slack · http · file · stdout), a durable on-disk journal + world table, the **write-ahead log** that closes the crash window, and `serve()` — an HTTP listener that turns a program into a service. | | `coffee.js` | The **Coffee bridge**: `expresso coffee agent.expr --apply` makes the agent a live member of a Coffee workspace — it polls a messenger channel (or a meeting's transcript via `--meeting`) and its actions become **real Coffee tasks, notes, messages, reactions, DMs, meetings, and contacts** (event ids are Coffee's own hashes → exactly-once; loop-safe; WAL-recovered). | | `demo.js` · `demo.html` | The **click-button live demo**: `expresso demo` opens a control panel where each button runs a real scenario against your Coffee workspace and streams back the created object hashes — plus Redeliver (0 new actions) and Replay (0 AI calls) proof buttons. | | `examples/serve-demo/` | The headline demo: `demo.sh` boots `expresso serve`, fires an incident into a **downstream outage**, `kill -9`s the daemon mid-send, restarts, and proves each action delivered **exactly once**. | | `test/smoke.js` · `test/host.js` | Conformance — T1/T2/T3, static rejection, deploy-stable keys; plus real sinks, **durable exactly-once across restarts**, and WAL crash-recovery. | | `test/replay-proof.js` · `test/real-adapter.js` | The proof where it bites: a **genuinely nondeterministic** AI step replayed bit-identical with the model poisoned to throw; and the real OpenAI/Anthropic port exercised offline. | | `USAGE.md` · `bin/expresso.js` · `package.json` | **How to actually use it**: install, the `expresso` CLI (`check`/`run`/`replay`/`serve`), sink config, and the SDK + full API reference. | | `index.html` · `app.js` · `styles.css` | The landing page + live playground (the offline tour). The grade gutter is the type-checker's own output. | ## Run it ```bash npm install https://expresso.meetcoffee.dev/expresso-lang.tgz # engine + `expresso` CLI, zero deps # Deploy it: a 14-line agent becomes a service. POST events in; actions fire exactly-once. expresso serve triage.expr --apply --config expresso.config.json --journal ./state --port 8080 curl -XPOST localhost:8080 -d '{"id":"i1","type":"message","channel":"#support","text":"checkout is down, 500s"}' # Or run a batch / prove the guarantees offline: expresso check triage.expr # type-check; show each agent's effect signature + blast radius expresso run triage.expr events.json --model openai # run against a REAL model (OPENAI_API_KEY) expresso replay triage.expr events.json # replay from the journal — 0 AI calls, bit-identical ``` **The 30-second proof it's real** — an outage *and* a hard crash, still exactly-once: ```bash bash examples/serve-demo/demo.sh # → boots `expresso serve`, fires an incident while the downstream is 500ing, kill -9's the # daemon mid-send, restarts on the same journal, and the receiver log shows: each action # delivered EXACTLY ONCE, 0 lost, 0 duplicated, carrying an Idempotency-Key header. node test/smoke.js && node test/lang2.js && node test/replay-proof.js \ && node test/real-adapter.js && node test/host.js && node test/docs.js # docs.js COMPILES every program published in the docs, the playground, and the # marketing site against the real engine — the documentation cannot drift. ``` The default model is an offline deterministic mock, so everything above runs with **no API key**. Add `--model openai` / `--model anthropic` (key from `OPENAI_API_KEY` / `ANTHROPIC_API_KEY`) and the AI steps call a **real** model — then `replay` reproduces that run with zero further calls, because every model answer was journaled. **Embed it in your own code** (a real model + your real task API, exactly-once) → see [`USAGE.md`](USAGE.md): ```js const E = require("expresso-lang"); const sess = E.session(E.parse(source), { model: E.RealModel({ provider: "openai" }), // or anthropic, or your own { call(req) } onEffect: e => myApi[e.verb](e.payload, { idempotencyKey: e.idemKey }), }); for (const event of myTranscriptStream) sess.feed(event); // agents act, live, exactly once ``` ## The part a skeptic should run first `node test/replay-proof.js` answers the obvious objection ("of course it replays, the mock is deterministic"): the AI step is genuinely random — run #1 and run #2 of the same program produce different actions — yet replaying run #1 from its journal, with the model port poisoned to throw on any call, reproduces run #1 **exactly, with zero AI calls**. Determinism is bought by the journal, not by a fake model. ## Status `expresso.js` implements §1–§5 of the spec (grammar, semantics, effect types, typed leaves, journaled replay, idempotent world) **and ships a real model port** — `E.RealModel({ provider: "openai" | "anthropic" })` — so leaves and fuzzy matches call a live LLM behind the typed, journaled boundary (`test/real-adapter.js` exercises the exact OpenAI/Anthropic wire format offline). **The live Coffee wiring ships too** (`coffee.js` + `expresso coffee`): an agent polls a real Coffee messenger channel and its actions land as real Coffee tasks/notes/messages, exactly-once, loop-safe, WAL-recovered — verified against a live workspace. Durable transport at scale (async runner, watermarks) remains **staged** (`ROADMAP.md`) behind the same leaf taxonomy — a port-swap, not a rewrite. The browser playground uses the deterministic mock (labeled as such) so T1/T2 are provable with no API key. Open language, batteries-included runtime: Expresso speaks to any backend through MCP-style providers; **Coffee** is the reference runtime where the world ports (live transcript, identity, presence, task/note/voice, an exactly-once ledger) already exist — the openCypher→Neo4j / Terraform→providers shape. ## The one-line pitch > Expresso is the runtime for trustworthy AI agents: the live conversation is the program, > nondeterminism is confined to typed, journaled AI steps, and every action is idempotent — > so an agent is a deterministic function of the conversation, replayable bit-for-bit and > exactly-once **by construction, proven by the compiler.**