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.15.5.tar.gz (675.0 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.15.5-py3-none-any.whl (665.0 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for openloom-0.15.5.tar.gz
Algorithm Hash digest
SHA256 5be247d18696dde33291a8266c8f4ccf87b71983ab39fa939c76a734c65ad120
MD5 c5a5de4064f1ad8ca68f98ef38c657b9
BLAKE2b-256 b24a1111ed3ba6a386c1b4f53938cdffc136fc3f4b5b7a15889f3b2c538dee5f

See more details on using hashes here.

Provenance

The following attestation bundles were made for openloom-0.15.5.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.15.5-py3-none-any.whl.

File metadata

  • Download URL: openloom-0.15.5-py3-none-any.whl
  • Upload date:
  • Size: 665.0 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.15.5-py3-none-any.whl
Algorithm Hash digest
SHA256 66b7f2cf17488b1aca1698dd47592455caca1ca2526ef298e4b581b0f24f050b
MD5 c4e2953e0a306202b58754631ab697b4
BLAKE2b-256 7e9af28a3b6dc0bfbb7e430ef0da150d586e269b2ea550a29e0e27ef5f98ee6d

See more details on using hashes here.

Provenance

The following attestation bundles were made for openloom-0.15.5-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