Skip to main content

OpenLoom: a lightweight agent task harness — schedule, monitor, and verify AI coding tasks.

Project description

OpenLoom

CI PyPI

Don't trust the agent's word — trust the file system.

OpenLoom is a webhook-driven task harness and observer for OpenCode. Webhook handlers call POST /api/tasks to dispatch a goal; OpenLoom attaches the goal to an OpenCode session, watches the session for activity, and emits a stream of TASK_UPDATED events on the bus. Webhook consumers can take over a stuck session with POST /api/tasks/{id}/abort and follow up with a fresh prompt — even from a phone, without touching the host.

OpenLoom does not replace OpenCode. It fills the gaps in OpenCode's HTTP API: a JSON task API, a session monitor, a webhook notification fan-out, and a web dashboard for browsing live work.

What's in 0.12

0.12 is a YAGNI cut. The previous release also offered an openloom watch runner (single-spec file mode with manual checks / acceptance / step-acknowledgement), a file-inbox dispatch path, a stale-busy alert, file-based notification sinks, and an AI task planner. All of that is gone. The webhook API below is the single control surface.

What stayed:

  • The 0.10 task lifecycle (create / start / update / complete / fail / archive / delete).
  • The 0.11 webhook notification format with task_name + timestamp + timestamp_iso + recent_activity (last 3 assistant messages, 1 000 chars each, with a tool-call summary).
  • The web dashboard (Dashboard / Activity / Tasks / Permissions / session drawer).

What's new in 0.12:

  • POST /api/tasks/{id}/abort — break a stuck agent loop on the task's session before sending a follow-up prompt.
  • TaskSpec reduced to three fields (name, workspace, goal) — every webhook handler payload is now 4 lines.
  • Single CLI subcommandopenloom serve is the only thing the CLI does. init, watch, status, log are removed.

Install

Requires Python 3.11+ and a running OpenCode server.

pip install openloom
# or
uv tool install openloom

Web dashboard (FastAPI + bundled Svelte UI):

pip install "openloom[ui]"

Optional extras — pick what you need; there is intentionally no [all]:

Extra Purpose
ui Web dashboard (openloom serve)
server Same as ui (alias)

Quick start

1. Start OpenCode

OpenLoom talks to OpenCode over HTTP. In a separate terminal:

opencode serve
# or launch the OpenCode app / TUI (it also exposes the API on port 4096)

If OpenCode is not running, openloom serve starts the dashboard but session features stay offline until OpenCode is up.

2. Configure the OpenCode connection (optional)

Most local setups need no env vars — OpenLoom defaults to http://127.0.0.1:4096 with no HTTP auth (same as opencode serve without OPENCODE_SERVER_PASSWORD).

If your OpenCode server uses HTTP basic auth:

export OPENLOOM_OPENCODE_URL=http://127.0.0.1:4096
export OPENLOOM_OPENCODE_USERNAME=opencode
export OPENLOOM_OPENCODE_PASSWORD=your-password

Full env-var reference: docs/configuration.md.

3. Start OpenLoom

openloom serve
# or with overrides
openloom serve --host 0.0.0.0 --port 55413

Open http://127.0.0.1:55413 for:

  • Dashboard — task list, per-session token usage, model breakdown.
  • Activity — sessions by workspace, archived tasks.
  • New Task — the same POST /api/tasks body as a webhook would send.
  • Permissions — pending tool approvals with Once / Always / Deny.
  • Session drawer — full transcript, diff, metadata.

The UI static assets are pre-built inside the wheel — no Node.js required at install time.

4. Create a task via webhook

curl -X POST http://127.0.0.1:55413/api/tasks \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Fix the type error",
    "workspace": "/Users/you/project",
    "goal": "There is a type error in src/foo.ts line 42. Fix it and run npm test."
  }'

The response is {ok, taskId, status, name, workspace, sessionId}. The task is now pending; on the next harness tick (within 8 seconds) it transitions to running with an OpenCode session id bound to it.

If you have a webhook configured, the next events arrive on it:

{
  "event": "TASK_STARTED",
  "task_id": "task_abc123",
  "task_name": "Fix the type error",
  "timestamp": 1749999999.123,
  "timestamp_iso": "2026-06-15T00:00:00Z",
  "store_version": 1,
  "data": {
    "session_id": "ses_xyz789",
    "summary": "Harness started and bootstrap prompt sent"
  }
}

5. Take over a stuck session

# 1. Get the stuck task's id from the webhook or dashboard
TASK=task_abc123

# 2. Abort the in-flight agent loop on the task's session
curl -X POST http://127.0.0.1:55413/api/tasks/$TASK/abort

# 3. Archive the old task so it stops showing in the active list
curl -X POST http://127.0.0.1:55413/api/tasks/$TASK/archive

# 4. Send a follow-up prompt as a new task bound to the same session
curl -X POST http://127.0.0.1:55413/api/tasks \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Take over: fix the type error",
    "sessionId": "ses_xyz789",
    "goal": "Forget what you were doing. Just look at src/foo.ts line 42 and fix the type error."
  }'

The new task binds to the same OpenCode session; the old task is archived. The session itself keeps its full transcript — both the old and new task can be opened in the dashboard and read each other's messages.

API surface

POST   /api/tasks                       create a task from {name, workspace, goal, sessionId?}
GET    /api/tasks                       list tasks
GET    /api/tasks/{id}                  task detail
POST   /api/tasks/{id}/abort            release any in-flight agent loop on the task's session
POST   /api/tasks/{id}/pause            pause a running task
POST   /api/tasks/{id}/resume           resume a paused task
POST   /api/tasks/{id}/complete         mark a task complete
POST   /api/tasks/{id}/archive          archive a task
DELETE /api/tasks/{id}                  delete an archived task

GET    /api/sessions                    list sessions (monitor snapshot)
GET    /api/sessions/{id}/messages      full transcript
GET    /api/sessions/{id}/diff          accumulated diff
GET    /api/permissions                 list pending permissions
POST   /api/sessions/{id}/permissions/{pid}   respond to a permission
POST   /api/sessions/{id}/archive       archive a session
POST   /api/sessions/{id}/delete        hard-delete a session

GET    /api/state                       composite dashboard state
GET    /api/events                      server-sent event stream
GET    /api/recent-workspaces           recently used workspaces
DELETE /api/recent-workspaces           remove from recent
GET    /api/browse                      directory listing for the path picker
POST   /api/pick-folder                 native OS folder picker

Webhook payload format and event types: docs/notifications.md.

Architecture (short)

core/      Harness, store, event bus, protocols
runtime/   OpenCode HTTP client, prompts
levels/
  notify/  WebhookSink + NotifyConfig
  server/  SessionMonitor, WebSink, ConsoleSink, serve
server/    FastAPI app + routes + static UI ([ui] extra)
docs/      Detailed guides (configuration, notifications, architecture)
tests/     contracts/ holds the architecture-enforcement tests

State changes flow: store write → event emit → sinks. API routes only read the store; the event bus pushes notifications, not data. Details: docs/architecture.md.

Development

uv sync
uv run pytest
uv run ruff check src/ tests/
uv run mypy src/openloom

# Rebuild frontend into the package (before release)
cd frontend && npm install && npm run build
cd .. && uv build

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openloom-0.16.2.tar.gz (675.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openloom-0.16.2-py3-none-any.whl (665.9 kB view details)

Uploaded Python 3

File details

Details for the file openloom-0.16.2.tar.gz.

File metadata

  • Download URL: openloom-0.16.2.tar.gz
  • Upload date:
  • Size: 675.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openloom-0.16.2.tar.gz
Algorithm Hash digest
SHA256 619355260075479da36325b59f9cee33f0adee6960fcc663ac7dbfe26e18d134
MD5 6b31a6cf2361cc69a5f13823e86ee303
BLAKE2b-256 9c25472cf546aa2233048ee97b288081bdc74f41bc7707ab5974e5f15e16a4a1

See more details on using hashes here.

Provenance

The following attestation bundles were made for openloom-0.16.2.tar.gz:

Publisher: release.yml on atbeta/openloom

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openloom-0.16.2-py3-none-any.whl.

File metadata

  • Download URL: openloom-0.16.2-py3-none-any.whl
  • Upload date:
  • Size: 665.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openloom-0.16.2-py3-none-any.whl
Algorithm Hash digest
SHA256 f9999ceecc6761fc587a12ac9569d8b5451a462288781912fab78cd2ed86c5fe
MD5 e06970088fe43c9e8d401f1d4fa9a6b2
BLAKE2b-256 dc2242e80343b46b058856d760a97730d77194b26fe473eef803aa6e4a4f03a1

See more details on using hashes here.

Provenance

The following attestation bundles were made for openloom-0.16.2-py3-none-any.whl:

Publisher: release.yml on atbeta/openloom

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page