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.14.0.tar.gz (662.5 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.14.0-py3-none-any.whl (650.4 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for openloom-0.14.0.tar.gz
Algorithm Hash digest
SHA256 71ef1e3d04bb5bb9557c542ba1b9edadc454a029df509b898c05dbaaa09cdb98
MD5 0a8f352d8d9911bcb617a9119e05dec5
BLAKE2b-256 c0f4d677500f18680b7ce127359b2011a1fd26a7bfb129ca7dd196ef0fe6a0ad

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: openloom-0.14.0-py3-none-any.whl
  • Upload date:
  • Size: 650.4 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.14.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1617ff6ac0f93e9ef0e54255b6f0e2c3bd5bd011e7567b3d1133708226fbe74c
MD5 1ff21365c3dabe481ee56b1fd7849537
BLAKE2b-256 bc5e1e44bc263f66aadea9423c3eae1854e13770dc5519cefe894f58bd8ee1bf

See more details on using hashes here.

Provenance

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