Loopy: Agent workflows that run when your data changes

handfuloflight1 pts0 comments

Loopy is an orchestrator for autonomous loops

loopy

Agent workflows that run when your data changes.

open source&middot;<br>agent neutral&middot;<br>code-first

View examples &rarr;<br>Read docs &rarr;

Get started

$uv tool install loopy-computer<br>copy

View on GitHub &rarr;

webhooks &rarr; sensors &rarr; event bus &rarr; workflows

3rd-party services (sending webhooks)

Sentrywebhook

Datadogwebhook

Zendeskwebhook

sensors on Loopy (receive hooks, emit typed events)

errors

monitors

feedback

event bus<br>Incident<br>Feedback

agent workflows

bug-fixer.mdon: Incident

product-feedback.mdon: Feedback

Why loopy.

What it does. Loopy runs an agent when your data changes: application errors, a metric crossing a threshold, a new message from a customer.

How it works. You write each workflow as markdown files in your repo. Loopy watches for the event and triggers agent workflows.

Why it matters. Loopy is open source, agent-neutral, and code-first: an alternative to automation tools that lock you in to a single model provider.

Get started in three commands.

Install the CLI, scaffold a project, and start the engine. The scaffold<br>compiles green out of the box, so your first loopy run works<br>before you change a line.

Install the CLI

Run uv tool install loopy-computer to put the loopy command on your path. Needs Python 3.12 or newer.

Scaffold a project

loopy init writes a registry.yml, a runnable starter workflow, and the two env files for secrets. It asks which repos the agent works on and offers to wire up the keys it finds.

Run the engine

loopy run compiles the graph, then starts the server: it hosts sensors that handle webhooks and runs agent workflows with Daytona sandboxes.

Sensors turn signals into typed events.

A sensor is a small Python function in sensors/sensors.py. It<br>listens for a webhook or polls on a timer, shapes the raw payload into a<br>typed event, and publishes it to an event bus. Agents subscribe to the bus and run when specific events are published.

sensors/sensors.py

from loopy import sensor<br>from loopy.events import CustomerTicket

# webhook: Zendesk POSTs a new ticket; you shape it into a CustomerTicket<br>@sensor(webhook="/hooks/zendesk", emits="CustomerTicket")<br>def zendesk_tickets(req) -> CustomerTicket:<br>ticket = req.json["ticket"]<br>return CustomerTicket(ticket_id=ticket["id"], subject=ticket["subject"],<br>body=ticket["description"], link=ticket["url"])

For GitHub, skip the sensor entirely. A step can trigger on a built-in<br>Github.* event (a PR opened or merged, an issue opened, a comment,<br>a push), and the compiler wires the webhook on /hooks/github for you.<br>Here a PR opening triggers a code review; see the<br>full example.

workflows/review/code-review.md

on: Github.PullRequestOpened # built in: no sensor, no event declaration<br>agent: Reviewer<br>Review PR #{{ event.number }} ("{{ event.title }}"). Diff it<br>against {{ event.base }}, post each finding as an inline review<br>comment on {{ event.url }} via `gh`, and return your verdict.

Workflows are markdown, one file per step.

A workflow is a directory under workflows/, one markdown file per<br>step. The config header says what triggers the step (on: an event,<br>or after: another step), which agent runs it, and what it<br>returns.

workflows/customer-feedback/entry.md

on: CustomerTicket # the one entry step, a registered Event<br>agent: SupportEngineer # an agent defined in registry.yml<br>output: { pr_url: url, verdict: str } # structured, typed outputs<br>A customer opened Zendesk ticket {{ event.ticket_id }}:<br>"{{ event.subject }}".

{{ event.body }}

Decide whether this ticket points at real work in this codebase.

1. Search the code for the behavior the ticket describes.<br>2. If it is a bug or a small feature you can address, implement<br>the change on a branch and open a pull request that links back<br>to {{ event.link }}. If it is a question, a duplicate, or not<br>actionable in code, do nothing.<br>3. Return the PR URL (empty if you skipped) and a one-line verdict.

The registry defines agents, sandboxes, and events.

(registry.yml) defines the key abstractions your workflows and sensors refer to by name: the agents that run steps,<br>the sandboxes their code runs in, and the typed events<br>that flow on the bus.

registry.yml

defaults: # every agent inherits these unless it overrides<br>agent: { sandbox: BaseSandbox, model: claude-sonnet-4-6, harness: claude-code }

sandboxes: # where an agent's code runs: image + env<br>BaseSandbox:<br>provider: daytona<br>image: { debian_slim: "3.12", apt: [git, gh], workdir: /home/loopy, user: loopy }<br>repos: [octocat/Hello-World] # cloned into the workspace, auth injected<br>env_file: secrets/base.env

agents: # named roles a step runs as; each inherits the defaults<br>SupportEngineer: {} # judges a ticket, opens a PR when it warrants work<br>Reviewer: {} # reviews a pull request when it opens

events: # typed messages on the bus; Github.* triggers are built in<br>CustomerTicket: { ticket_id: str, subject: str, body: str, link: url } # inbound, from the Zendesk...

loopy event agent workflows ticket code

Related Articles