An agent loop with excellent amnesia.

One POST /runs is one run. You send the provider, model, key, prompt, history and tools. theloop goes model → tools → model, streams the text, calls you back with usage you can actually bill — and then forgets it ever met you.

one run, the whole storyPOST /runs
curl localhost:8080/runs \
  -H "Authorization: Bearer $ENGINE_TOKEN" \
  -d '{
    "contract_version": "1",
    "llm": { "provider": "anthropic",
             "model": "claude-sonnet-5" },
    "messages": [ { "role": "user",
      "content": [ { "type": "text",
                     "text": "Hello!" } ] } ],
    "wait": true
  }'

# → result + usage. No DB was harmed.

Six things it does,
zero things it remembers.

Built for teams shipping assistants to lots of users. Multi-tenant by construction, because there's no tenant anything to configure.

01

Everything in the request. Nothing in the engine.

No database, no configs, no tenant registry. Your backend decides what the assistant is — on every single call.

stateless by design
02

One result, at least once.

Callbacks retry, dedupe by run_id, and stay readable at GET /runs/{id} for recovery. Late results land by history revision.

it will find you
03

Writes are intents.

A write tool answers "deferred" with an operation id. The mutation happens in your system, after approval — never inside a tool call.

effect: "write"
04

Usage you can bill.

One record per provider attempt — failed and interrupted ones too. Uncached input, cache read/write, an honest "estimated" flag, optional cost and budgets.

finance will like you
05

Controlled egress.

HTTPS only, host allowlist, no private IPs, no redirects. Provider keys never show up in logs, SSE, callbacks or error bodies.

keys stay keys
06

Bring any model.

Anthropic, OpenAI, DeepSeek, GLM, GigaChat, MiniMax or anything OpenAI-compatible — with per-key queues and AIMD rate limiting.

per-request keys

TRUST MODEL

A model can ask.
It can't act.

A prompt-injected model can request a refund all day long. Nothing happens until your system says yes. It's a two-sided deal, though — the engine enforces what it can see, your endpoints carry the rest.

The engine promises

  • Never executes a mutation itself
  • Never runs two calls of one write tool in one step
  • Refuses write tools missing idempotency context
  • Validates tool arguments before calling your endpoint
  • Sends every outbound request through the egress allowlist

You promise

  • Write endpoints only register an intent — they change nothing
  • Tools marked "read" really have no side effects
  • Approval and execution happen in your system, idempotently
  • Data is scoped by meta (tenant, user), never by ids in args
  • Only trusted servers hold ENGINE_TOKEN

Looping in
four lines.

Tests run without network or keys. The rest needs one provider key and a coffee you won't finish.

A · from sourceNode 22.9+

git clone https://github.com/paveler/theloop.git
cd theloop && npm install
cp .env.example .env   # ENGINE_TOKEN + a key
npm run dev            # → :8080

B · Docker, no cloningghcr.io

docker run -p 8080:8080 \
  -e ENGINE_TOKEN=change-me \
  -e ANTHROPIC_API_KEY=sk-ant-… \
  ghcr.io/paveler/theloop:1

Asked often,
remembered never.

The short answers. The long ones live in the SPEC, which is the contract.

What is theloop?

theloop is an open-source agent loop that runs as a stateless HTTP service. One POST /runs is one run: the request carries the provider, model, API key, prompt, full history, tools (URL + JSON schema) and limits. The engine runs model → tools → model until a final answer, streams text over SSE, delivers the result and billing-grade usage by callback, and keeps no state between calls. It is written in TypeScript for Node 22 and licensed under Apache 2.0.

How is theloop different from an agent framework?

theloop is a service you call over HTTP, not a framework or SDK you import. Your backend (the harness) decides what the assistant is on every request: prompt, tools, model and budget. The engine decides how the loop runs: steps, retries, limits, streaming, cancellation and accounting. Any language or low-code platform that can send an HTTP request can drive it.

Where does conversation history live?

In your system. History is a list of messages that your harness stores and sends in full with every run; theloop appends the new messages and returns them. Resuming a conversation means starting a new run with a longer history. That is why theloop is multi-tenant by construction: there is no tenant state to configure.

Which LLM providers does theloop support?

Anthropic, OpenAI, DeepSeek, GLM (z.ai), GigaChat, MiniMax and any OpenAI-compatible endpoint. Provider keys are passed per request, with per-key queues and AIMD rate limiting. More providers, including OpenRouter, native Gemini and local models, are proposed in the roadmap.

How does theloop limit the damage of prompt injection?

Write tools are intents. A tool marked effect: "write" only registers an operation and answers "deferred" with its id; the mutation happens in your system after approval, never inside a tool call. So a prompt-injected model can ask for an action but cannot carry it out. This holds only if your write endpoints change nothing, tools marked "read" have no side effects, and data is scoped by meta (tenant, user) rather than by ids in the arguments.

How does theloop report token usage for billing?

theloop returns one usage record per provider attempt, including failed and interrupted attempts, with uncached input, cache read and cache write tokens and an explicit estimated flag. It can optionally compute cost from your price list and enforce token and cost budgets.

Does theloop support MCP?

Not in v1. Today a tool is an HTTPS endpoint with a JSON schema that theloop calls through its egress allowlist. MCP tools are proposed for v1.1 in the roadmap, with the same rule that only read-only tools are executed by the engine.

Is theloop production-ready?

The v1 contract is frozen: semver, no breaking changes within 1.x. The implementation is young, so expect fixes rather than contract changes. v1 runs as a single instance and keeps runs in memory for a retention period; durable state stays in your system.