OpenLoom: a lightweight agent task harness — schedule, monitor, and verify AI coding tasks.
Project description
OpenLoom
Don't trust the agent's word — trust the file system.
OpenLoom is a lightweight harness and observer for OpenCode. It schedules tasks, watches sessions, verifies completion with file-system checks instead of model self-reports, and keeps you notified from anywhere — even when a long-running task gets stuck on a hung child process.
OpenLoom does not replace OpenCode. It fills the gaps in OpenCode's HTTP API: session monitoring, task plans, periodic checks, a web dashboard, an inbox for remote dispatch, and webhook / file notifications.
What's in 0.10
- Inbox dispatch — drop a markdown file (or POST to a webhook) to enqueue a task from anywhere. Bind it to an existing session, or spin up a new one.
- Abort & resume — send a markdown with
abort: trueandsession: <id>to take over a stuck session from your phone. - Notifications — webhook + file sinks for every harness event, including a new
SESSION_STALE_BUSYalert that fires when a session has been busy with no progress for N consecutive checks. - Config summary card — the web dashboard now shows at a glance which channels are live (inbox watcher, webhooks, file sinks).
- UI refresh — JetBrains Mono, less AI-app feel, Windows font parity.
- Architecture hardening —
core.protocols(typed injection),runtime.factory.build_harness()(single assembly), connection-pool reuse on the OpenCode client.
Full notes per feature: docs/inbox.md, docs/notifications.md, docs/architecture.md.
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, openloom watch --ui) |
server |
Same as ui (team server mode alias) |
openspec |
OpenSpec checkbox completion checks (cold-detected) |
github |
GitHub integration |
validate |
Pre-archive validation hooks (your project's pytest/mypy) |
Quick start
0. 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 watch exits with setup hints; openloom serve starts the dashboard but session features stay offline until OpenCode is up.
1. 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
Windows (PowerShell):
$env:OPENLOOM_OPENCODE_URL = "http://127.0.0.1:4096"
$env:OPENLOOM_OPENCODE_USERNAME = "opencode"
$env:OPENLOOM_OPENCODE_PASSWORD = "your-password"
Full env-var reference: docs/configuration.md.
2. CLI — watch a task spec
openloom init # writes openloom.yaml in cwd
openloom watch # run harness from openloom.yaml
openloom watch --ui # same + local web UI (needs [ui])
openloom watch --verbose # debug-level logs
openloom status
openloom log <task-id-prefix>
openloom serve --host 127.0.0.1 --port 55413
Example spec (openloom.yaml):
name: Fix SSE reconnect
workspace: /path/to/project
check_interval_minutes: 5 # minimum 5; all tasks are harness-watched
goal: |
Fix SSE reconnect after network drop.
steps:
- Investigate current SSE implementation
- Implement reconnect with backoff
- Add regression coverage
3. Web dashboard — multi-task server
pip install "openloom[ui]"
openloom serve
Open http://127.0.0.1:55413 for:
- Dashboard — session token usage, by-model breakdown, period summaries (today / week / month / total)
- Activity — tasks, archived tasks, sessions by workspace, config summary card showing live inbox / webhook / file-sink status, stuck-session pill when any session has been busy without progress
- New Task — plan with goal / steps / acceptance, attach to a workspace or an existing session
- Permissions — pending tool approvals with Once / Always / Deny (optional auto-accept, OpenCode Desktop–compatible)
- Session drawer — messages, usage, diff, metadata per session
The UI static assets are pre-built inside the wheel — no Node.js required at install time.
Remote dispatch — the inbox
The inbox turns any device that can write a file (or POST an HTTP request) into a dispatch surface.
# 1. Point OpenLoom at a directory on the host
export OPENLOOM_INBOX_DIR=/path/to/inbox
openloom serve # or openloom watch
Drop a markdown into /path/to/inbox/task.md:
# Implement retry policy
workspace: /Users/you/project
## goal
Add exponential backoff to the SDK's HTTP client.
OpenLoom polls every 30 s (configurable), consumes the file, creates a task, and renames the file to processed-<id>. From the UI or the CLI, you can also POST to /api/inbox/trigger instead of writing a file.
Bind to an existing session so a follow-up turns append to the same transcript:
# Continue the type check
session: ses_abc
workspace: /Users/you/project
## goal
Pick up from where you left off.
Take over a stuck session from anywhere — when you receive a SESSION_STALE_BUSY alert, drop a file like this:
# Resume the long task
session: ses_abc
abort: true
workspace: /Users/you/project
## goal
Stop the hung npm install and finish the type check.
abort: true is opt-in. OpenLoom calls POST /session/ses_abc/abort (releasing any in-flight tool) and then prompt_async with your new goal, so the agent picks up the new task immediately. Full schema and examples: docs/inbox.md.
Notifications
Configure webhook or file sinks to receive harness events anywhere:
# Webhook — POST a JSON event to your endpoint
export OPENLOOM_NOTIFY_WEBHOOK_URLS='https://hooks.example.com/openloom'
# File sink — one JSON file per event written to the directory
export OPENLOOM_NOTIFY_FILE_DIRS=/var/log/openloom-events
export OPENLOOM_NOTIFY_FILE_PREFIX=openloom
Built-in event types:
| Event | When |
|---|---|
TASK_CREATED |
A new task was added to the store |
TASK_STARTED |
The harness started the first turn of a task |
TASK_UPDATED |
Progress, status, or step change |
TASK_COMPLETED |
The agent reported completion (or auto-archive) |
TASK_FAILED |
Budget exceeded, session lost, etc. |
LOG_LINE |
Notable log line (e.g. abort failure) |
SESSION_STALE_BUSY |
A session has been busy for OPENLOOM_STALE_BUSY_CHECKS consecutive checks (default 10) with no new completed message — typically a hung child process |
SESSION_STALE_BUSY is one-shot per stuck episode — the latch releases when the session recovers. The alert carries the session id, title, workspace, and stuck duration, so a webhook handler can deep-link to the session drawer.
Full payload schema and per-event filtering: docs/notifications.md.
Architecture (short)
core/ Harness, store, event bus, Source / Checker / Sink ABCs (lean)
runtime/ OpenCode HTTP client, session status, prompts
levels/ Progressive capabilities (manual, config, openspec, ui, inbox, notify, server, …)
server/ FastAPI app + routes + static UI ([ui] extra)
docs/ Detailed guides (inbox, notifications, configuration, architecture)
State changes flow: store write → event emit. API routes read the store; the event bus pushes notifications only. See docs/architecture.md for the contracts that keep this honest.
Development
uv sync
uv run pytest
uv run ruff check src/ tests/
# Rebuild frontend into the package (before release)
cd frontend && npm install && npm run build
cd .. && uv build
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openloom-0.10.0.tar.gz.
File metadata
- Download URL: openloom-0.10.0.tar.gz
- Upload date:
- Size: 677.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b6f18210533dcf8064a5f94af02bd57ed43e095959d3d7938149b79cc67f01a
|
|
| MD5 |
5ad8563619b8715dc82250654633e3df
|
|
| BLAKE2b-256 |
7abf77efd1fb7b3b9188215fa87ee4d6bfa559699bbcef9f70e8896a26e2773e
|
Provenance
The following attestation bundles were made for openloom-0.10.0.tar.gz:
Publisher:
release.yml on atbeta/openloom
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openloom-0.10.0.tar.gz -
Subject digest:
1b6f18210533dcf8064a5f94af02bd57ed43e095959d3d7938149b79cc67f01a - Sigstore transparency entry: 1819126372
- Sigstore integration time:
-
Permalink:
atbeta/openloom@2b23f4f21591f5d7b85daf796f95d37157dbc212 -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/atbeta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2b23f4f21591f5d7b85daf796f95d37157dbc212 -
Trigger Event:
push
-
Statement type:
File details
Details for the file openloom-0.10.0-py3-none-any.whl.
File metadata
- Download URL: openloom-0.10.0-py3-none-any.whl
- Upload date:
- Size: 662.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eaac96f30e84f604a26fdd35122bc3998b9b43cda016f400308ef65dfac3a74a
|
|
| MD5 |
e02e0c16c6cb065f5841a7d8714f0ef7
|
|
| BLAKE2b-256 |
f63b46ce3515d365d92dc8fb0e8d7ba1cd7abc78475f200f492129586aefb184
|
Provenance
The following attestation bundles were made for openloom-0.10.0-py3-none-any.whl:
Publisher:
release.yml on atbeta/openloom
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openloom-0.10.0-py3-none-any.whl -
Subject digest:
eaac96f30e84f604a26fdd35122bc3998b9b43cda016f400308ef65dfac3a74a - Sigstore transparency entry: 1819126482
- Sigstore integration time:
-
Permalink:
atbeta/openloom@2b23f4f21591f5d7b85daf796f95d37157dbc212 -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/atbeta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2b23f4f21591f5d7b85daf796f95d37157dbc212 -
Trigger Event:
push
-
Statement type: