Hugpy Harness
hugpy-harness has two roles:
- A terminal client. It provides a full-screen TUI (
hugpy-harness) and a plain chat REPL (hugpy-chat) for a standalone Hugpy wrapper or a Hugpy fleet core such ashttps://dev.hugpy.ai. It talks HTTP only. It needs no background service, engine, worker enrollment or toolserver. - The agent-loop core library. Since 0.1.7, hugpy-agent is built on this package. Its loop, gateway, adapter, journal, policy, memory and tool registry are the harness modules, and hugpy-agent adds its fleet extras through hooks. The harness never imports hugpy-agent.
The full reference, covering hooks, chat-loop behavior and the agent layer,
is in docs/agent-and-harness.md.
Status (2026-10-18)
| where | version |
|---|---|
this source (pyproject.toml) |
0.1.10, prepared for release |
installed on ae (deb, /opt/hugpy-harness/venv) |
0.1.9 with local source fixes |
| PyPI | 0.1.9; the 0.1.10 endpoint/TUI changes are not published yet |
Install
From the apt repo, on any Debian or Ubuntu box:
curl -fsSL https://dev.hugpy.ai/apt/setup.sh | sudo bash
sudo apt install hugpy-harness # hugpy-wrapper is Recommended, not required
sudo apt install --no-install-recommends hugpy-harness # client only (remote wrapper/fleet)
From locally built debs, without an apt repo:
bash packaging/debian/build.sh # -> build/debian/: harness .deb, wrapper .deb if built, install.sh
cd build/debian
./install.sh # missing local wrapper: prompt [Y/n]
./install.sh --yes # default yes, no prompts
./install.sh --without-wrapper # harness only (remote wrapper or fleet)
./install.sh --dry-run # show the APT plan only
From source with pip:
pip install ../hugpy_tools . # needs hugpy-tools>=0.2.7
How the deb is put together:
- The deb (Architecture
all) carries the harness wheel and thehugpy-toolswheel in/usr/share/hugpy-harness/. The postinst installs both offline into/opt/hugpy-harness/venv. /usr/bin/hugpy-harnessand/usr/bin/hugpy-chatexec that venv.Recommends: hugpy-wrapper (>= 0.1.62). The wrapper keeps its own runtime and service, and neither package embeds the other.apt remove hugpy-harnessleaves chat history, the wrapper, models and hugpy-agent untouched. A purge removes only this package's runtime.
Connect
If you do not pass --base or a connection --config, the CLI reads the local
wrapper endpoint and a fleet or core endpoint from the user config directory
(XDG_CONFIG_HOME/hugpy) and /etc/hugpy. It checks the wrapper's /health
identity against the core's online worker list. A matching worker URL selects
the core, so model choice and resource monitoring cover the fleet. An absent
or unreachable core leaves the standalone wrapper selected. If no wrapper is
installed, a configured fleet or core endpoint is selected directly.
HUGPY_WRAPPER_URL and HUGPY_FLEET_URL explicitly select their respective
endpoints. --base and --config override detection. A user-level fleet
setting also selects that fleet directly. View or change it with:
hugpy-harness endpoint
hugpy-harness endpoint set-fleet https://dev.hugpy.ai
hugpy-harness endpoint clear-fleet
The setting is stored in ~/.config/hugpy/fleet.endpoint.json (or under
XDG_CONFIG_HOME). The URL may be a fleet core or its HTTPS proxy.
If nothing is found, the CLI tells you to pass --base URL or set
HUGPY_FLEET_URL.
Key. The CLI sends HUGPY_WRAPPER_API_KEY to a wrapper and
HUGPY_FLEET_API_KEY to a core.
The fleet key is a hugpy-core keys create --name <box> --scope use key, and
you need it only from outside the LAN once the core requires keys for outside
callers. That core key policy is being set up. As of 2026-10-15, the fleet's
/v1 answered without a key.
URLs. A bare host gets https://, and {base}/v1 is probed first.
--config FILE reads a JSON connection file that may hold only base
and model (both strings). The option's help text says the file comes from
fitevict setup, but the installed wrapper CLI (0.1.122) has no setup
command, so write the file yourself.
To connect a remote box to the fleet:
hugpy-harness endpoint set-fleet https://dev.hugpy.ai
hugpy-chat --list-models
hugpy-harness --model <id from the list>
With 0.1.7, which is what apt serves until 0.1.8 is published, pass
--base https://dev.hugpy.ai instead. 0.1.7 does not read the fleet
endpoint files or HUGPY_FLEET_API_KEY.
Launch
hugpy-harness # full-screen TUI (same as `launch` / `tui`)
hugpy-harness launch --workspace /path/to/project --model MODEL_ID
hugpy-harness --base http://HOST:PORT --workspace /path/to/project
hugpy-harness chat --model MODEL_ID # plain REPL (= hugpy-chat)
hugpy-chat --message "one question" --model MODEL_ID # one turn, exit code 0 on success
TUI options: --config, --base, --workspace, --model, --session,
--policy {ask,readonly,auto} (default ask), --optional-tools and
--list-models. The model defaults to HUGPY_WRAPPER_MODEL.
hugpy-chat options: the same as the TUI, plus
--tools-mode {prompted,constrained,native,auto}, --max-steps (12),
--max-tokens (1024), --message and --resume RUN_ID.
In the TUI:
/modelopens the model picker. Every picker filters as you type./resourcestoggles telemetry, and/resources fullopens the compute monitor.- In the monitor,
eor Delete evicts a model through the wrapper's operator action. A fleet core has no eviction route, so there the TUI says so instead of failing. - The display shows fleet resource snapshots per worker, including stale or offline state.
Sessions are journaled in <workspace>/.hugpy_wrapper/chat/journal.db.
Reopen a session with the same --workspace and /session, or use
hugpy-chat --resume RUN_ID. Conversations stay in the same workspace
journal when you change endpoints.
Serve (named sessions over loopback HTTP)
hugpy-harness serve --workspace ~/work # 127.0.0.1:9126 by default
hugpy-harness serve --port 9127 --base https://dev.hugpy.ai
HUGPY_SERVE_TOKEN=... hugpy-harness serve --host 0.0.0.0 # a token is required beyond loopback
The harness is a client; this is its one inbound surface and it is local by
default. It wraps the same session client the TUI uses, so a session created
over HTTP is the same journal row the TUI shows, with the same per-session
model, destination and generation settings. No per-user venv or console is
scaffolded: install the harness and run serve in a workspace.
The routes are the ones serve_client.hugpy_serve.HugpyServeClient already
speaks (written for the agent session service on :9126), so hugpy-agent tui --serve http://127.0.0.1:9126 and any adapter that fronted that service work
unchanged against this server:
| route | purpose |
|---|---|
GET /api/state |
service, workspace, endpoint, sessions, profiles (the model catalog) |
GET /api/console/models · GET /api/tasks |
picker rows · the media task table |
GET / POST /api/sessions |
list · create {name?, profile?, settings?, directive?, init_prompt?}; with a model set, the init prompt runs as the first turn |
GET /api/sessions/<sid>?after=N |
session view (status, settings, pending question, queue, cursor) plus {id, ts, kind, data} events after cursor N |
GET /api/sessions/<sid>/events?after=N · GET /api/sessions/<sid>/models |
the same view, and the catalog, under sub-paths a per-session proxy (Core's /api/sessions/<sid>/*) can reach |
POST /api/sessions/<sid>/messages |
{text, by?}; runs the turn in the background, or queues it while a turn is open (queued: true) |
GET / POST /api/sessions/<sid>/queue |
what is waiting · {action: add|update|remove|clear, id?, text?} |
POST /api/sessions/<sid>/init |
(re)send the session's init_prompt as a turn |
POST /api/sessions/<sid>/tasks |
{task, model, text, files?, params?}; files must be inside the workspace |
POST /api/sessions/<sid>/{answer,stop,clear,name,profile,settings} |
answer a question, interrupt, clear context, rename, select model, set destination/controls |
<sid> accepts the id, the session:<id> route or an unambiguous name.
Per-session direction is a user setting, not a role: directive is appended
to the system prompt, which the loop re-renders every turn, so a changed
directive applies from the next turn without touching the transcript;
init_prompt is the first message a host sends when it creates the session (a
station's init brief). Messages that arrive mid-turn queue in the journal
and the thread that ran the turn drains them into one collated next turn — a
keeper's nudges and board notes are never refused. (The wrapper's own queue
underneath is compute admission for model calls; this one is conversation
ordering.) A media task's result is also recorded in the conversation (as an
operator-shared result), so the model can reason about the output on the next
turn; before, it reached only the display transcript.
Registering a session with a fleet core so it has a route from the fleet's routing is hugpy-agent's job (the inbound, central-facing arm); this server never does it.
Chat behavior
Chat runs the agent loop in conversation mode:
- A plain-text reply is a complete answer. Tools are optional.
- Each user message gets its own step budget.
- An empty reply or an engine error ends the turn with that error. The loop never demands a tool call.
<tool_call>and<tool_response>markup never shows up as reply text.- A
final_answercall that has no answer gets one repair prompt.
Model tools
The harness imports the standalone hugpy_tools catalog, so file, search,
text, data, hash, time and web tools work in both the TUI and plain chat.
Without optional tools, a loop has 36 tools, including fs_read, fs_write,
fs_glob, shell, ask_operator and final_answer.
- Tools execute on the harness machine, jailed to
--workspace, even when the model runs on a remote wrapper or fleet. - Writes, network access and host operations go through
--policy. --optional-toolsadds document, media, rendering, image, desktop and VM tools. Their backends load only when called; install the matchinghugpy-toolsextras, for examplepip install 'hugpy-tools[images,files,render]'. Rendering also needs Playwright Chromium.- Generated images and screenshots go to
.hugpy_wrapper/tool-artifactsin the workspace.
Using the core as a library
from hugpy_harness.config import Config
from hugpy_harness.loop import AgentLoop
loop = AgentLoop(Config(base="http://127.0.0.1:52017", model="MODEL", workspace="."),
extra_tool_providers=[my_provider]) # provider(ctx) -> [ToolSpec]
print(loop.run("task"))
The hooks are:
| hook | purpose |
|---|---|
AgentLoop(rag_factory=, comms_factory=, catalog_provider=, extra_tool_providers=) |
constructor hooks |
AgentLoop.turn_ctx(run_id) |
spans one drive |
Gateway.request_ctx(chat_url, key) |
per-request headers and cancel |
Config.state_dir and config.state_path() |
per-workspace state; default .hugpy_wrapper/chat |
the hugpy_harness.tools entry-point group |
each entry is a provider(ctx) -> [ToolSpec] |
imports.constants.SERVICE_NAME |
the harness's name (declared, not yet read anywhere) |
the imports/ layer |
constants, functions, classes, init_imports |
Details and caveats are in
docs/agent-and-harness.md.
Development
python -m pytest -q tests # 20 tests, offline
bash packaging/debian/build.sh # runs plain `python3`; BUILD_PYTHON is not read (pure-Python wheels)
Build, release and test conventions for the whole tree are in
docs/development.md.
Changes
0.1.23 (2026-10-20; local deb build from staging; 0.1.20–0.1.22 were earlier cuts of the same day)
-
hugpy-harness tui --serve URL: the TUI as a client of a harness session server (the console's Station tab runs exactly this). -
hugpy-harness serve(hugpy_harness.serve, console scripthugpy-harness-serve): the harness's named sessions over loopback HTTP, speaking theHugpyServeClientroute vocabulary. Bearer token beyond loopback; loopback Host/same-origin only without one. -
WrapperClient.raw_eventsand the pending question's text/options are exposed for remote clients. -
A media task's result is appended to the session's conversation, not only to the display transcript.
-
hugpy_harness.toolserver_clientexists; the TUI's toolserver status line and loci registry discovery imported it but it was never shipped, so both silently degraded. -
AbstractServeClient.roll_modeposted keyword arguments to anHttp.post(path, body)helper and raised.
0.1.8 (2026-10-15; installed on ae, not yet in the apt pool)
- Fleet connection. Endpoint resolution falls back to
HUGPY_FLEET_URL, then~/.config/hugpy/fleet.endpoint.json, then/etc/hugpy/fleet.endpoint.json. The key falls back toHUGPY_FLEET_API_KEY. When no endpoint is found, the error now names--baseandHUGPY_FLEET_URL. - Evicting a model on a fleet core (no
/v1/operator/actions) reports "this endpoint has no eviction" instead of a raw HTTP error.
0.1.7 (2026-10-15; apt, bundled in hugpy-agent 0.1.118)
- The harness became the core library for hugpy-agent. 0.1.7 added
rag_factory,comms_factory,catalog_providerandextra_tool_providersonAgentLoop, theturn_ctxmethod,Gateway.request_ctx,Config.state_dirandstate_path(), thehugpy_harness.toolsentry-point group, and theimports/layer, which includesSERVICE_NAME.
0.1.6 (2026-10-14; PyPI)
- The right-hand resources pane shows one box per loaded model, and
eor Delete unloads a model. - The sessions pane lists the loaded models each session used.
- A model is strictly per session.
- Tool markup (
<tool_call>and<tool_response>) is gated out of reply text.
0.1.5 (2026-10-14)
- Pickers search as you type.
/resources fullis the compute panel in the terminal, with eviction.- A local model's reply is no longer shown twice.
Local Harness controls:
/modelsoffers an engine filter;/models gguf,/models transformers, or/models allselects it directly. Each model shows its size and engine/runner./settingschooses the inference endpoint: Fleet or a specific wrapper./observeselects resource telemetry independently of inference routing./rename NAMEnames a session./pinkeeps it near the top of the session list. The session retains its model, endpoint and controls across restarts./session-settingsshows controls; appendmax_tokens 1024,temperature 0.2,no_think false, oralloc_mode auto|gpu-only|ram-only|max-gpu|max-ramto set one. Current controls also appear in the sidebar./clearremoves this session's transcript and model context./clear-modelkeeps the transcript and resets only model context.- Model reasoning is retained in a collapsed thinking card after the reply; Enter/Space/click expands it. The activity line follows the transcript and reports observed load stages; estimates without evidence remain unknown.
Local agents receive the actual registered toolset in their init prompt. When Core is available they get fleet_list_models, fleet_list_workers and fleet_call_model (chat, embeddings, image generation). The shared toolserver client exposes toolserver_list_categories, toolserver_list_tools and toolserver_call_tool through Core's /ts/* relay. Core uses its existing management access gate for this relay; inference-only keys do not grant toolserver operator access. Toolserver calls retain target-specific policy checks and the shared privileged-tool allowlist. HUGPY_AGENT_TOOLSERVER=0 disables the bridge. The shared MCP catalog gives frontier clients the same explicit toolserver names and argument fields; older call names remain accepted as compatibility aliases.
User-defined persistent sessions
See Named sessions for arbitrary names, persistent model destinations, stable routes and delegation shared with Agent.
Pending human input (0.1.17): structured model questions and provider approvals join an inline queue beside the composer. Arrival never opens a modal, takes keyboard focus or replaces a prompt draft. Press Ctrl-A (or click the pending notice, or use /questions) to answer when ready. Tab / Shift-Tab cycles requests; Up / Down or 1–9 selects a choice; Enter sends it. Questions also accept a custom answer in a separate editor. Esc returns to the exact prompt draft/cursor; answers and selection survive switching queued requests. Answering one request does not automatically focus the next. A request resolved elsewhere stays on a resolved notice until you explicitly leave, preventing late keystrokes from answering another question or sending your draft. Requests and answers remain scoped to their originating session, including reused provider request IDs. Provider wait timeouts still apply. Plain numbered prose remains ordinary text; selectable inputs use structured question events such as ask_operator.
Task inputs are available with /task: choose a task, filter compatible models,
and browse local files for image-to-image, inpainting, image-to-video or audio
transcription. Type the prompt in the main input pane and press Enter to run;
/task-params {"steps":20,"size":"512x512"} sets task options, and /task cancel
returns to chat. Inputs are uploaded as bytes to the selected endpoint. Results
are saved in hugpy-outputs under the workspace, with a JSON result record.
The chat target and its model context stay separate from task execution.
Local agents expose basic file and shell tools directly. Additional utilities
are nested under local_list_categories, local_list_tools and
local_call_tool. The dispatcher preserves the target's permission policy,
workspace confinement and durable execution context. Frontier configurations
retain their existing tool exposure.
Ctrl-R focuses resources. Up/Down and page keys scroll that column; Tab selects
an entry, [/] cycles wrappers, t/T cycles types, y copies the full error
or process details, and Enter expands errors. Drag selection stays within its
starting column. Ctrl-S saves unsent input; Ctrl-B or /drafts restores it
without sending. Task drafts retain their task, model and input-file choices.
Downloads and feature gallery
Get the Debian packages, architecture details, SHA-256 checksums and installation commands from Hugpy Downloads. The feature gallery shows the console, Agent, Station, Media and Video interfaces. Screenshots reflect the versions captured; the current interface may differ.
Metadata
Release files for hugpy-harness 0.1.16
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 | |
|---|---|---|---|---|
| hugpy_harness-0.1.16.tar.gz | 228.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hugpy_harness-0.1.16-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 454.7 kB
Release files / hugpy_harness-0.1.16.tar.gz
| Download URL | hugpy_harness-0.1.16.tar.gz |
|---|---|
| Size | 228.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d6e5a20cd79239071dfd8a94932f8b469367ac2984514c10fa1157cef40964bc
|
|
BLAKE2b-256 checksum How to use checksums |
bc752b286c5006ae19a53eafd0f896c64279e07eefd0ba8bbe1459fa5f79c246
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / hugpy_harness-0.1.16-py3-none-any.whl
| Download URL | hugpy_harness-0.1.16-py3-none-any.whl |
|---|---|
| Size | 226.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9e20bdf9e228ebc156301fa1a1d5446257c2aa0b175b686bb548c6e8a22be733
|
|
BLAKE2b-256 checksum How to use checksums |
7ff3264fe253583fcb6a335ce1b6e1495ad559d142a3ac2ebea010948e5e2004
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for hugpy-harness 0.1.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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.