The agent loop breaker. Watches runs, classifies stuck vs productive, injects a targeted nudge.
Detector, rule classifier, and soft-nudge templates — usable fully offline.
Quick Start · Why LexLoop · How It Works · Features · Configuration · File Tree
LexLoop watches agent runs, classifies stuck vs productive loops with a belief-graph detector, and injects a targeted message to unstick the run. OSS and MIT: detector, rule classifier, soft-nudge templates, TypeScript SDK, CLI, golden corpus, and JSONL log. Hosted aggregate loop summaries are invite-only (summaries only, no transcripts).
Built by LatticeAG.
- Agents get stuck; bills grow - polling loops, ping-pong tool calls, and repeated cycles burn tokens while making no progress. LexLoop detects the pattern and intervenes.
- Classify before you nudge - a belief-graph classifier distinguishes genuinely stuck runs from productive repetition, so healthy loops are left alone.
- Soft nudges, not kills - intervention injects a targeted message from versioned templates (
fixtures/interventions/tmpl_soft_v1.txt), steering the run instead of terminating it. - Safe by default - the SDK defaults to
log-only; onlylexloop watchdefaults tointervene. Replays (--from-jsonl) are observe-only and refuseintervenewithLL4004. - Redaction built in - reports redact by default;
--no-redactrequires a TTY andLEXLOOP_ALLOW_UNREDACTED=1.
- Belief-graph classification, not a step counter - detectors cover stuck runs, polling, ping-pong exchanges, cycles, and semantic stalls, each with tuned thresholds (
min_run,window,min_cycles). - Golden corpus + conformance -
fixtures/golden(loops vs productive) andconformance/(canonicalize parity, outcome cases) pin behavior;lexloop bench goldenreplays them. - Kit adapters, not framework lock-in -
--attach openai-completions|anthropic-messages|custominstruments the run; LangGraph users callmaybeInjectfrom abefore_modelhook with nograph.attach()magic.
# 1. Prerequisites: Node >= 20.19 (CI matrix: 20.19 and 22), pnpm 9.15.0
git clone https://github.com/LatticeAG/lexloop.git
cd lexloop
pnpm install
pnpm --filter @latticeag/lexloop-cli build
# 2. Scaffold defaults
lexloop init # writes lexloop.json defaults + creates .lexloop/
lexloop doctor # node, config, .lexloop/, proxy, redact checks
# 3. Watch a run (default mode: intervene)
lexloop watch --cmd "node my-agent.js" --attach openai-completions
# 4. Replay offline (observe-only)
lexloop watch --from-jsonl .lexloop/events.jsonl
# 5. Report on a run
lexloop report <run-id>
lexloop report <run-id> --jsonLangGraph (custom kit — no graph.attach() in v0.1):
import { LexLoop } from "@latticeag/lexloop";
const lex = new LexLoop(); // SDK default mode: log-only
async function before_model(state: { messages: Array<{ role: string; content?: string | null }> }) {
const messages = await lex.maybeInject(state.messages);
return { messages };
}
// Observe tool results from a ToolNode wrapper with lex.observe(...).
// A first-class wrapLangGraph is later.flowchart LR
A[Agent run] --> B[Observe: steps + tool results]
B --> C[Canonicalize + redact]
C --> D[Detectors: stuck, poll, ping-pong, cycle, semantic]
D --> E{Stuck?}
E -->|No| F[Log-only: JSONL]
E -->|Yes| G{Mode?}
G -->|log-only| F
G -->|intervene| H[Inject soft-nudge message]
H --> F
F --> I[lexloop report RUN-ID]
| Command | Description |
|---|---|
lexloop init [--force] |
Write lexloop.json defaults and create .lexloop/. |
lexloop watch |
Run a command with LexLoop env, or replay JSONL. Default mode intervene; --from-jsonl forces observe-only. |
lexloop report <run-id> [--json] [--no-redact] |
Print a run report (redacted unless TTY + LEXLOOP_ALLOW_UNREDACTED=1). |
lexloop config |
Validate, show, or set lexloop.json. |
lexloop bench golden|detect |
Replay fixtures/golden, or run the synthetic detect-latency bench. |
lexloop doctor |
Check Node, config, local wiring, proxy port, and redact posture. |
lexloop version |
Print 0.1.0. |
| Feature | Description |
|---|---|
| Stuck detector | Flags runs repeating without progress (detect.stuck.min_run, default 3). |
| Poll detector | Catches polling-style tool calls via name regex + allowlist (detect.poll.min_run, default 8). |
| Ping-pong detector | Spots back-and-forth exchanges in a sliding window (window 12, min_cycles 3). |
| Cycle detector | Finds repeated action cycles (min_cycles, default 3). |
| Semantic detector | Flags semantically stalled runs even when surface actions vary. |
| Budgets | Caps spend per run (budget module). |
| Soft-nudge templates | Versioned intervention messages (fixtures/interventions/tmpl_soft_v1.txt). |
| Redaction | PII-aware log redaction; unredacted output needs TTY + LEXLOOP_ALLOW_UNREDACTED=1. |
| Run log | Append-only JSONL (default .lexloop/events.jsonl, 64 MiB cap). |
| Package | Description |
|---|---|
@latticeag/lexloop |
Public SDK (LexLoop: observe, maybeInject). |
@latticeag/lexloop-core |
Detectors, classifier, intervention, redact, budget, templates. |
@latticeag/lexloop-events |
Event schema + @latticeag/events bridge (events-ext). |
@latticeag/lexloop-config |
Strict zod config schema (lexloop.json). |
@latticeag/lexloop-cli |
The lexloop binary. |
lexloop init writes lexloop.json (strict schema — unknown keys fail). Key knobs:
| Key | Default | Description |
|---|---|---|
mode |
log-only (SDK) / intervene (watch) |
LEXLOOP_MODE env overrides for watch. |
detect.stuck.min_run |
3 |
Repeats before a run counts as stuck. |
detect.poll.min_run |
8 |
Poll-pattern repeats; allowlist + name_regex tune matching. |
detect.pingpong.window / min_cycles |
12 / 3 |
Sliding window and cycle threshold. |
detect.cycle.min_cycles |
3 |
Repeated action cycles. |
log.path / log.max_bytes |
.lexloop/events.jsonl / 67108864 |
JSONL path and 64 MiB cap. |
.env.example documents the env surface (LEXLOOP_MODE, LEXLOOP_CONFIG, LEXLOOP_RUN_ID, LATTICEAG_* session/event keys, upstream/proxy hooks). Do not use Node 23-only APIs — engines.node is >=20.19.
pnpm install
pnpm --filter @latticeag/lexloop-cli build
pnpm test # vitest run over packages/**
pnpm typecheck # tsc --noEmitTest suite (verified): 171 passed, 0 failed across 20 test files — detectors, canonicalize (13), redact (14), budget, ids, signature, SDK attach/splice, events-ext schema, and config load.
packages/
cli/ lexloop binary (init, watch, report, config, bench, doctor)
core/ detectors + classifier + intervene + redact + budget + templates
sdk/ public LexLoop SDK + kit attaches (openai, anthropic, splice)
config/ strict zod schema + lexloop.json load/write
events-ext/ event schema + toLattice bridge
bench/ golden.ts, detect_load.ts, stats.ts
fixtures/
golden/ loops/ vs productive/ replay corpus
interventions/ tmpl_soft_v1.txt nudge template
conformance/
canonicalize/ axion-parity.json
outcome/ cases.json
schemas/
.lexloop/ local run state (gitignored)
- No first-class LangGraph wrapper yet - v0.1 wires via
maybeInjectin abefore_modelhook plus a ToolNodeobservewrapper;wrapLangGraphis planned. packages/proxy/is a placeholder - empty in v0.1.0 (.gitkeeponly); proxy-related doctor checks reportskip/okwith proxy disabled.- Node 24 is untested upstream - CI matrix is Node 20.19 and 22; this host runs Node 24 and passes, but 23-only APIs are still off-limits per
engines. - Hosted aggregate summaries are invite-only - OSS is the local detector + log; aggregates share summaries only, never transcripts.
MIT — see LICENSE. Copyright 2026 LatticeAG.