loop-js/loop.js
loop.js is a TypeScript framework for "loop engineering": you define a goal and a verification bar, and the engine runs an agent through repeated Rounds (Execute → Handoff → Verify → Persist) with fresh context each round, memory read back from disk, until a separate skeptical judge agent rules the goal met, gives up, or a budget guard (rounds/usd/timeout) fires. It ships a CLI (`loop run`, `loop status`, `loop cron`) for one-off runs or scheduled/recurring loops (local or deployed to Modal), a typed async-iterable event stream for embedding in products, and runs its worker/judge agents on the Claude Agent SDK. It's positioned against bare `while(true)` agent loops and Claude Code's own `/loop` command, aiming at loops that outlive a terminal session — cron-triggered chores, financial-trade auditing, or long-running builds — where "done" must be judged by an agent the worker can't influence.
What it claims
loop.js (v0.2 beta, package @loop.js/core) makes a separate judge's verdict the only thing that can end a loop successfully. Rounds, USD and timeout limits are described as "runaway guards, never a definition of done." The project reports 800+ tests covering the Lock compare-and-swap, event ordering and crash-partial folding.
How it works
Each Round produces one of three verdicts, and they behave differently:
flowchart TD
E[Execute: worker, fresh context] --> H[Handoff note to successor]
H --> V{Verify: read-only judge}
V -->|ok| S[Settled: success]
V -->|impossible| G[Settled: give-up, budget preserved]
V -->|not yet| R[Reason written to disk]
R --> E
V -.guard fires.-> X[Guard exit, own exit code]- The judge is read-only. When it needs ground truth for a claim, it escalates to the work tree, the build or the transcript.
- Every Round is persisted to a record and journal, so a run can resume after a crash.
- "Impossible" settles the loop instead of retrying. The trading example relies on this: any order that breaches the strategy ends the loop as a give-up, so the worker cannot retry its way into placing more orders.
- Prompts share one shape.
goalis required.executeandverifyare optional and fall back to the goal. Each prompt can be: - a plain string,
{ file: "./verify.md" }, which is re-read every Round, so you can change the bar mid-loop,- a per-round function.
The object form also binds a phase's model or permissions, for example verify: { model: "claude-haiku-4-5" } for cheaper judging.
- Scheduled loops re-judge on every tick. A settled loop is re-judged each time the schedule fires. If the bar still holds, the tick costs one judge turn. If a time-dependent goal has gone stale ("today's brief exists"), the loop re-opens and does the work again.
How to use it
npm create @loop.js@latest my-loop
cd my-loop && npm install
export ANTHROPIC_API_KEY=sk-ant-…
npx loop run
npx loop status # human or --json, from any shellThe scaffold contains only a goal. Its defaults are tight: 3 Rounds, $1, and 5 minutes per Round. Raising those limits is deliberate.
Scheduling commands:
npx loop cron add "*/30 * * * *" --until settled
npx loop cron add "0 8 * * *" --until forever
npx loop cron add "0 8 * * *" --until forever --backend modal
npx loop cron list
npx loop cron remove <id>- Lifetimes.
--until settledremoves the entry at the first settle.--until foreverkeeps it until you remove it. - No runner of its own.
loop croninstalls into a real scheduler and never runs loops itself. A Lock makes overlapping ticks safe. - Modal backend. It deploys with your own Modal token and stores
ANTHROPIC_API_KEYonce, as amodal.Secretnamedloop-js. You rotate it withmodal secret create --force, with no redeploy.
Embedding. loop.run() returns an async-iterable of typed events: text-delta, verdict, exit and others.
- Iterating only observes the run.
- Breaking out of the loop unsubscribes but does not cancel the run.
- Startup failures throw. After startup, everything resolves to a final exit, and iteration never throws.
- Every event except
text-deltais journaled.
Single pass. Agent.define({ goal }).run().done() runs one ungraded Execute pass, with no verdict.
Limits
- Pre-1.0. It is a v0.2 beta, and the page says the API may still change.
- No sandboxing yet. Sandbox-contained runs, a public executor interface and remote observation are listed as future work.
- Claude-only for now. The Claude Agent SDK is currently the only executor.
- Network examples turn gating off. The examples that need the network use
permissions: "bypass", and the page says to run them inside your own container. - The USD guard covers model spend only. It caps what the model costs, not what a trading strategy trades. Position caps must live in your strategy file and in your broker account's own limits.
Organisations named here
Technologies named here
- loop.js
- Claude Haiku 4.5
- Agent.define
- Claude Agent SDK
- Modal
- loop CLI
- Claude Code
- Bun
- TypeScript
- Node.js
Techniques named here
- Loop engineering
- Round cycle
- Re-reading a prompt file every Round
- Time-dependent re-judging
Related in agents



