{"id":51073589,"url":"https://github.com/conductor-oss/loop-engine","last_synced_at":"2026-06-23T12:33:22.551Z","repository":{"id":363756132,"uuid":"1264767964","full_name":"conductor-oss/loop-engine","owner":"conductor-oss","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-10T07:32:55.000Z","size":73,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-10T09:06:15.569Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/conductor-oss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-10T06:57:35.000Z","updated_at":"2026-06-10T07:33:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/conductor-oss/loop-engine","commit_stats":null,"previous_names":["conductor-oss/loop-engine"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/conductor-oss/loop-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conductor-oss%2Floop-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conductor-oss%2Floop-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conductor-oss%2Floop-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conductor-oss%2Floop-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/conductor-oss","download_url":"https://codeload.github.com/conductor-oss/loop-engine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conductor-oss%2Floop-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34688139,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-23T02:00:07.161Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-23T12:33:21.776Z","updated_at":"2026-06-23T12:33:22.545Z","avatar_url":"https://github.com/conductor-oss.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Loop Engine\n\n**Durable, bounded, self-correcting loops for AI agents — built on open-source\n[Conductor](https://github.com/conductor-oss/conductor).**\n\nRunning an agent once and hoping is not a system. The reliable pattern is a **loop**: act,\nevaluate against real evidence, feed back, retry — until the work passes or a guardrail says\nstop. Today that loop is usually a human babysitting a chat window. Loop Engine turns it into\nsoftware.\n\n```\nGoal\n  ↓\nPlanner            (sub-workflow — swap your own)\n  ↓\n┌──────────────── control loop (durable, capped) ──────────────────┐\n│  Actor          (sub-workflow — swap your own)                   │\n│    ↓                                                             │\n│  Evaluate       (sub-workflow — swap your own)                   │\n│    ↓            → verdict {passed, score, feedback}              │\n│  Decide         (deterministic policy + guardrails — no LLM)     │\n│    ↓                                                             │\n│  Route ── accept · retry · replan · delegate · escalate · stop   │\n└──────────────────────────────────────────────────────────────────┘\n  ↓\n{ status, result, score, decision_log, tokens_spent }\n```\n\n*Use agents for judgment; use workflows for control.* The planner, actor, and evaluator are\n**extension points** — sub-workflows you swap by name at runtime. The control logic stays\ndeterministic in the engine.\n\n## Why a workflow, not a `while` loop\n\n- **Durable** — every iteration is a checkpoint. Kill the server mid-run; the loop resumes where\n  it left off, state intact.\n- **Bounded** — iteration cap, token budget, retry/replan limits, wall-clock timeout. Bad input\n  is clamped; a runaway loop is structurally impossible.\n- **Evidence over self-report** — an independent evaluator judges the work; the actor never marks\n  its own work complete. Deterministic checks **gate** the LLM judge: if the judge loves the\n  tagline but the character count says 240 against a 200 limit, it fails, and the loop retries\n  with machine-measured feedback (\"Too long: 240 chars, limit 200 (cut 40)\").\n- **Observable** — every decision (and why) lands in a `decision_log`; every run is replayable in\n  the Conductor UI.\n\n## Quickstart\n\n```bash\n# 1. Export an LLM key — the server picks it up at startup\nexport ANTHROPIC_API_KEY=sk-ant-...     # demos default to Anthropic\n# export OPENAI_API_KEY=sk-...          # or OpenAI: set llm_provider/llm_model in the input file\n\n# 2. Start a Conductor server (needs Java 21+; skip if you have one)\nconductor server start                  # or: export CONDUCTOR_SERVER_URL=https://your-server/api\n\n# 3. Register the workflows + sanity-check the setup (idempotent)\n./quickstart.sh\n\n# 4. Run your first loop, then watch every iteration and decision\nconductor workflow start -w loop_engine -f inputs/demo-minimal.json\nconductor workflow get-execution \u003cworkflowId\u003e\n```\n\nOutput:\n\n```json\n{\n  \"status\": \"succeeded\",\n  \"result\": \"\u003cthe best deliverable produced\u003e\",\n  \"score\": 0.92,\n  \"iterations\": 3,\n  \"decision_log\": [ { \"iteration\": 0, \"decision\": \"retry\", \"reason\": \"...\", \"feedback\": \"...\" } ],\n  \"tokens_spent\": 12345\n}\n```\n\nTerminal statuses: `succeeded`, `stopped_no_progress`, `stopped_budget`, `stopped_max_iterations`,\n`stopped_infra_failure`, `escalated` — every run ends with an explicit reason, never a hang.\n\n## Demos — each proves a loop behavior\n\n| `conductor workflow start -w loop_engine -f ...` | Proves | Outcome |\n|---|---|---|\n| `inputs/demo-bounded-stop.json` | **No infinite loops.** Impossible constraint (5 keywords in 30 chars) | `retry → retry → retry → replan → retry`, then halts at the iteration guardrail |\n| `inputs/demo-tagline.json` | Deterministic evidence gates the LLM judge (≤120 chars) | `succeeded` the moment evidence confirms the criteria |\n| `inputs/demo-length-window.json` | The \"models can't count\" case (exactly 150–170 chars) | `succeeded` at 163 — the length check is authoritative, not the model |\n| `inputs/demo-infra-failure.json` | **Survives outages.** Actor points at a nonexistent workflow | bounded infra retries, then clean `stopped_infra_failure` |\n| `inputs/demo-generic.json` | The evaluator extension point is swappable | `succeeded` with the generic LLM judge |\n\n## Plug in your own agent\n\nEach extension point is a Conductor sub-workflow resolved **by name at runtime**. Register a\nworkflow with the matching contract, pass its name as input — the engine is unchanged:\n\n- **Pre-planner** (`pre_planner_workflow`, optional) — *code that shapes the planner*: runs before\n  every plan/replan → out: `{ context, plan_hints, tokens }`, merged into what the planner sees\n- **Planner** (`planner_workflow`) — in: objective, criteria, feedback, history → out: `{ plan, tokens }`\n- **Actor** (`actor_workflow`) — in: objective, plan, feedback, iteration → out: `{ result, summary, tokens }`\n- **Evaluator** (`evaluator_workflow`) — in: objective, criteria, result → out: `{ passed, score, feedback, tokens }`\n\nA custom extension that fails or returns garbage is treated as an infra failure with bounded\nretries — it degrades the run, it doesn't kill it. Set `enable_human: true` to escalate to a\n`HUMAN` task instead of stopping; resume with `conductor task signal`. Full contracts (every\nfield, plus `extension_params` passthrough) are in the [design notes](docs/design.md).\n\n## Or write the whole loop in Python — the `loop` SDK\n\nA loop is an agentic program: *loop to resolve a dispute, loop to review code, loop to onboard a\ncustomer.* With the [`loop` SDK](sdk/README.md), one Python file is the whole loop — plain\nfunctions become Conductor workers, the SDK generates the contract sub-workflows, and the durable\nengine still owns control:\n\n```python\nfrom loop import Loop\n\ndispute = Loop(name=\"credit_card_dispute\",\n               objective=\"Resolve the dispute in extension_params.case_id per policy.\",\n               acceptance_criteria=\"The ledger reflects a policy-correct decision.\",\n               llm_provider=\"anthropic\", llm_model=\"claude-opus-4-7\")\n\n@dispute.pre_planner                 # code that runs BEFORE the LLM planner and shapes it\ndef gather_case(extension_params=None):\n    return {\"context\": case_facts(extension_params), \"plan_hints\": POLICY}\n\n@dispute.actor                       # the work — a Conductor worker\ndef resolve(plan=\"\", feedback=\"\", extension_params=None):\n    return {\"result\": apply_policy_and_update_ledger(extension_params)}\n\n@dispute.evaluator                   # judge the LEDGER, not the model's claim\ndef verify(extension_params=None):\n    return {\"passed\": ledger_is_correct(extension_params), \"feedback\": \"...\"}\n\nrun = dispute.execute(extension_params={\"case_id\": \"D-1001\"})\nrun.watch()                          # live decision log until the loop terminates\n```\n\n```bash\npip install -e sdk/ \u0026\u0026 python sdk/examples/credit_card.py\n```\n\nRunnable example: [`sdk/examples/credit_card.py`](sdk/examples/credit_card.py) · SDK docs:\n[`sdk/README.md`](sdk/README.md).\n\n## Production examples ([`loop-examples/`](loop-examples/README.md))\n\nThree real loops, **each a single Python file** on the SDK, all reusing the engine unchanged:\n\n| Example | Evidence the loop closes on |\n|---|---|\n| **Coding agent** (`coding_agent.py`) — a prompted LLM writes Python | real test pass/fail (sandboxed subprocess) |\n| **Data-quality pipeline** (`data_quality.py`) — code cleans, a contract gates | deterministic data contract |\n| **Refund/support agent** (`refund_support.py`) — pre-planner facts, policy actor | the actual refund ledger, not the model's claim |\n\n```bash\ncd loop-examples \u0026\u0026 pip install -e ../sdk\npython coding_agent.py roman\n```\n\n## Built on Conductor\n\nEverything here is open-source Conductor doing the heavy lifting — the loop is the *pattern*,\nConductor is the *runtime*. Authored with the\n[Conductor skills](https://github.com/conductor-oss/conductor-skills).\n\n| What it demonstrates | Conductor primitive |\n|---|---|\n| Durable, restart-surviving control loop | `DO_WHILE` + workflow `variables` as checkpointed state |\n| Deterministic decisions \u0026 routing | `INLINE` + `SWITCH` — no LLM in the control path |\n| Swappable planner / actor / evaluator | `SUB_WORKFLOW` with a dynamically resolved name |\n| LLM calls without HTTP plumbing | built-in `LLM_CHAT_COMPLETE` task |\n| Real work behind the agents | SDK workers (`conductor-python`) |\n| Survives its own infrastructure | `optional` tasks + `failureWorkflow` |\n| Human-in-the-loop | `HUMAN` task + task signal |\n\n## Going deeper\n\n- **[Design notes](docs/design.md)** — the full decision policy, effort presets, guardrail\n  reference, failure handling, flow diagram, and Conductor specifics.\n- **Tests** — the decision policy is plain, unit-tested code (`src/decide.js`), inlined into the\n  workflow JSON by `scripts/build.mjs`:\n\n```bash\nnode --test 'tests/*.test.cjs'                          # policy, config, guards, JSON sync\n(cd loop-examples \u0026\u0026 python3 -m unittest discover)      # the example loops' role logic\n(cd sdk \u0026\u0026 PYTHONPATH=. python3 -m unittest discover -s tests)   # the loop SDK\n```\n\n---\n\nLoop Engine is the *pattern*. **[Conductor](https://github.com/conductor-oss/conductor)** is the\n*runtime* — if durable agent loops are your problem, that's the repo to star.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconductor-oss%2Floop-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fconductor-oss%2Floop-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconductor-oss%2Floop-engine/lists"}