UH — Universal Harness
A provider-agnostic agent harness in Python. UH merges the ideas that worked in four prior projects:
| Source | What UH takes from it |
|---|---|
| Harness Maxxing | web UX, organised sessions |
| OpenClaw | the harness contract (toolsets, fail-closed modes) |
| Hermes Agent | the synchronous agent loop (run_turn) |
| UHB | deterministic routing, fail-closed modes |
The CLI is universal. UH makes no assumption about a specific vendor: it
must drive any local or free provider (Ollama on 127.0.0.1:11434, a local
free router such as OmniRouter/Nemotron) and must never require an API key on
first launch. A paid provider is an opt-in, never a prerequisite.
The web UI will be copied and adapted from Harness Maxxing, not written from scratch and not vendored as a dependency.
- Requires Python ≥ 3.10 (
tomliis installed automatically on 3.10) - Single runtime dependency:
httpx - Installable on its own: no dependency on
hermes-home-visionoruhb
Install (development)
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
uh --version
uh doctor
Roadmap — five milestones
- Jalon 0 — Skeleton. PyPI-installable package (
uh-harness),uhCLI surface,uh --version,uh doctor(python / httpx / git / ollama probe), README, MIT license. No agent loop, no storage, no UI. - Jalon 1 — Agent core. Synchronous
run_turnloop, toolsets (file,terminal,memory), in-memory message history,uh chat. No persistence yet: a session lives only as long as the process. - Jalon 2 — Local model layer. Streamed chat (
--no-streamto disable), realuh model pullwith progress, provider registry (ollama,omnirouter), traced fallback chain,uh providers list. - Jalon 3 — Sessions and configuration. Persisted sessions backed by a
SQLite FTS5 store (
uh sessions list|show|search|delete|resume), layered configuration withuh config show|get|set|path,uh memory migrate|show, extendeduh doctor. - Jalon 4 — Console experience.
uhwith no argument opens a chat console: banner, status bar, slash commands (docs/SLASH_COMMANDS.md), zero-configuration onboarding (local model, offer to pull one sized to the RAM, local router, or a guided free key),uh providers add|removewith keys in$UH_HOME/keys.toml(0600), UTF-8 stdio on Windows, CI matrix. - Jalon 5 — Web UI.
uh webserves a local interface (sessions column, streaming chat, collapsible tool cards, context gauge, per-session model, full-text search, command palette, stop button, light/dark themes) from three static files with no build step and no third-party JavaScript.
Jalons 0 to 5 are done. The interface is an original design: it borrows the feature list of Harness Maxxing (public README) and none of its code, which is under a Fair Source Licence. UH stays MIT.
CLI surface (Jalon 4)
uh # interactive console (piped: help, unchanged)
uh --version # print the version
uh doctor # environment report (python, httpx, git, ollama, store, disk, config, keys)
uh model list # list models served by Ollama, exit 1 if Ollama is down
uh model pull <name> # download a model with progress on stderr
uh chat [prompt] # agent loop with file/terminal/memory tools (streamed, saved)
uh sessions list # recent sessions (id, date, provider, messages, title)
uh sessions show <id> # one session and its messages
uh sessions search <q> # full-text search across every message
uh sessions resume <id> # continue a session (--fork copies it into a new one)
uh config show|get|set # layered configuration
uh memory migrate|show # one-shot import of the flat memory file
uh providers list # registered providers and their state
uh providers add <name> # store an OpenAI-compatible provider (key never printed)
uh providers remove <name>
uh chat reads stdin or takes a prompt argument; --once (or any non-TTY
stdin) answers one line and exits. Answers stream by default, --no-stream
waits for the whole message. Conversations are persisted unless --no-save;
--session <id> resumes (full id, unique prefix, or the short form shown by
uh sessions list). Tool activity goes to stderr, the answer to stdout.
Web interface (optional)
pip install "uh-harness[web]"
uh web # http://127.0.0.1:8765/?token=...
uh web --port 9000 --no-open
uh web --allow-terminal # enable the shell toolset in the browser (off by default)
uh web without the extra prints the exact install command and exits 1. The
core package stays on httpx; fastapi/uvicorn only exist in [web].
Security, because a local server is still a server:
- binds
127.0.0.1; any other--hostprints a warning before starting; - a 43-character token is generated at startup, accepted once as
?token=and then kept in anHttpOnly; SameSite=Strictcookie; every/apiroute answers 401 without it (curl:Authorization: Bearer <token>); HostandOriginare checked (DNS-rebinding / CSRF), no CORS is enabled;- strict CSP without
unsafe-inline, so no inline script or style exists in the HTML;X-Frame-Options: DENY; - the terminal toolset is disabled unless
--allow-terminal; file tools stay inside--workspace; - no provider key is ever returned by the API.
Design notes and the feature-by-feature rationale live in docs/WEB_DESIGN.md.
Console and slash commands
uh on a terminal starts the console; piped, it still prints help (scriptable
unchanged). The status bar shows provider · model · session · messages · context estimate. /help lists everything; the full inventory and the reason
each Hermes command was kept or dropped lives in
docs/SLASH_COMMANDS.md.
/help, /model, /provider, /tools, /clear, /new, /sessions,
/resume, /history, /retry, /title, /status, /config, /doctor,
/memory, /version, /exit (aliases /h, /?, /reset, /quit, /q,
/v). An unknown command is never sent to the model: it is refused with the
closest match suggested.
First run with nothing configured, in order: an installed Ollama model, else an
offer to pull a small model sized to the detected RAM (size shown, never
downloaded without a yes), else a local OmniRouter gateway, else a provider you
already added, else a guided setup that explains where to get a free key and
stores it in $UH_HOME/keys.toml (0600). UH ships no key: a published key
is a stolen key.
The console can use rich when installed
(pip install "uh-harness[ui]"); without it, or off a TTY, everything falls
back to plain text with the same output contract.
echo "list the python files here" | uh chat --once --model qwen3:8b --workspace .
uh chat --session 1829-9474 "et ensuite ?" # resume
uh sessions search clavier # FTS5 across all messages
uh memory migrate # MEMORY.md -> one session, once
Sessions, store and configuration
- Store:
$UH_HOME/uh.db(~/.uh/uh.db), schema v1, WAL, foreign keys ON. Deleting a session cascades to its messages and to the FTS index. - Two tables (
sessions,messages) plus an external-content FTS5 index kept in sync by triggers;searchreturns a snippet and abm25rank. - A database written by a newer build is refused (
StoreError), never downgraded silently. No FTS5 in the SQLite build means no store at all. - Configuration, lowest to highest: defaults,
~/.uh/config.toml,./uh.toml,UH_*environment, CLI flags.uh config showprints the source of every value. Unknown keys warn and are ignored; the writer handles flat keys only (booleans, integers, strings) and refuses anything else. uh memory migratecopiesMEMORY.mdinto akind=memorysession after writing a timestamped.bak; it is idempotent and leaves the flat file in place (the memory toolset still reads and writes it).
Providers and fallback
| Provider | Transport | Endpoint | Streaming |
|---|---|---|---|
ollama |
httpx, NDJSON | POST /api/chat, GET /api/tags, POST /api/pull |
yes |
omnirouter |
httpx, SSE | POST /v1/chat/completions (OpenAI-compatible) |
yes |
Without --provider, uh chat builds the chain ollama then omnirouter
(when configured). Every switch is printed on stderr, never silent:
fallback: ollama -> omnirouter (reason: cannot reach Ollama at ...)
--provider <kind> disables the fallback: one provider, its errors surface as
they are. Model namespaces are not shared, so the backup uses
UH_OMNIROUTER_MODEL (default auto/best-free) rather than the Ollama tag.
OmniRouter discovery (measured, not guessed)
uh doctor and uh providers list find a local OmniRoute gateway from these
sources, first hit wins:
UH_OMNIROUTER_URL(explicit)OMNIROUTE_BASE_URL(the OmniRoute CLI's own variable)BASE_URL=in~/.omniroute/.envhttp://{HOST}:{PORT}from that dotenv (PORTdefaults to 20128)- otherwise: "not configured" - nothing is invented
API key: UH_OMNIROUTER_API_KEY, then OMNIROUTE_API_KEY, then the same
dotenv. The key is only ever sent as an Authorization header, never printed.
What the installed gateway actually looks like (OmniRoute 3.8.52):
- data dir
~/.omniroute, configuration is a dotenv file plus a SQLite database - no YAML/TOML, so UH needs no parser dependency; - server on
http://127.0.0.1:20128,GET /v1/modelsandPOST /v1/chat/completionsanswer with aBearerkey (1632 model ids on the machine this was measured on, includingauto/*combos); - streaming is SSE (
data: {...}chunks,data: [DONE]terminator).
License
MIT — see LICENSE.
Metadata
Release files for uh-harness 0.7.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| uh_harness-0.7.2.tar.gz | 96.4 kB | With provenance | Details for uh_harness-0.7.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| uh_harness-0.7.2-py3-none-any.whl | Python 3 | none | any | Details for uh_harness-0.7.2-py3-none-any.whl |
Total release size: 176.9 kB
Release files / uh_harness-0.7.2.tar.gz
| Download URL | uh_harness-0.7.2.tar.gz |
|---|---|
| Size | 96.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2deaef9209b841eb10b8cea8bf3fa8cc3f138727427777d40e7f9af983746f2
|
|
BLAKE2b-256 checksum How to use checksums |
72272d7c665410e71738b83e965f835a7e6a26497aec2d454e7c6fc6d31e4445
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logRelease files / uh_harness-0.7.2-py3-none-any.whl
| Download URL | uh_harness-0.7.2-py3-none-any.whl |
|---|---|
| Size | 80.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2456e7a87c93dfbafdd406237eef3c3f96f2e6c23e82674eab5da7a77cb0420c
|
|
BLAKE2b-256 checksum How to use checksums |
9b4ef8e6c5a99595d3b4f43ee7308e5baaf7420183713aecffdc55b6163e690f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logSecurity for uh-harness 0.7.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for uh_harness-0.7.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.