Replace the example Tailscale IP with <host> so no server address ships
in the repo. Tailnet-only (CGNAT) so low severity, but no reason to leak it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Integrate the laptop feature branch (multi-language capability benchmark,
model picker, and ESA-style pseudonymous file attribution) onto the current
church main (native AI harness, podman/vbox sandboxes, † theme). Only conflict
was the help/status line — kept the newer podman grammar + /help, spliced in
/export-signed, and aligned attribution strings to the † sigil.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The headless bench stubbed _send_sbx_input to a no-op, so run_shell
commands were staged to /tmp/.hh_*.cmd but the typed wrapper never ran —
the rc/out poll timed out and any shell-dependent task FAILed (mkdir+list
burned ~248s then failed). Make the stub run typed input in the bench
process (CWD == workdir), faithfully standing in for the room PTY, so
_run_shell_in_pty (staging/poll/cleanup) is exercised end-to-end.
Result: 3/3 pass. Surfaced a separate model-side argument leak in
script+run (tool JSON-schema dict passed as the command) — fixed next.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
First functional bench run crashed 0/3 on two latent bench-only bugs:
(1) the Phase-4 token-count change made complete_with_tools return
(text, calls, usage), but the bench preflight still unpacked 2 values;
(2) make_bridge never stubbed _send_sbx_input, so the PTY-mirror
visibility feature hit ws=None and raised AttributeError on every tool
call. Stub it to capture mirrored lines for --verbose.
After the fix: 2/3 pass (write+read, script+run). mkdir+list still
fails — local-backend run_shell CWD divergence + 3B model churn, not a
working-memory regression.
Sprint: native-harness-working-memory (bench) — see also 46e5620, dc6317f, c83abbe
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add bench-lang.py + bench/ package: a third benchmark axis answering
"which open-source model is best for my workflow?" across Python,
JavaScript, Go, Rust and Bash.
- MultiPL-E (Go/Rust/JS/Bash) + original HumanEval (Python), loaded via
the HF datasets-server REST API with on-disk cache — no datasets/
pyarrow dependency.
- Completions go straight to Ollama /api/generate with raw=True so
instruct models continue the code instead of replying with prose.
- Code runs in rootless, network-less podman (safe default) with a
host-toolchain fallback; pass@1/pass@k via the HumanEval estimator.
- run/score separation: results persist to a scorecard JSON, then
`pick --workflow ops` re-ranks without re-running any model.
- Extensible: a new language is one Lang entry; a new workflow is one
block in workflows.json.
Also fix a --runs grant-persistence bug in bench-sandbox.py: the grant
leaked across runs, invalidating the L0-nogrant refusal test on runs 2+.
Each run now revokes the ACL and starts ungranted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bench-ai.py drives the /ai chat path end-to-end (SRP -> Fernet -> WebSocket
-> provider), measuring TTFT/total/tok-s per model, with a --direct provider
mode that isolates raw model throughput from event-loop contention.
bench-sandbox.py benchmarks the /ai <agent> !<task> sandbox code path by
playing the room owner on the zero-knowledge relay: it grants drive, sends
graded tasks (L0 no-grant refusal, file/script/logic/multistep, destructive
gating+confirm, blast-radius cap), captures the agent's injected _sbx:input
frames, and grades correctness behind the same destructive guard. Includes
REPL-aware exec replay (folds python3 sessions into heredocs), prompt-prefix
stripping, model-fail vs replay-limit tagging, --runs N averaging with
per-step timing, and auto-bumped timeouts for reasoning models.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Display-mirror hybrid (docs/plan-harness-visibility.md §2): native tool
calls now show up in the shared sandbox terminal again via inert `# `-
prefixed comment lines (comment-prefix = anti-double-run/anti-escape),
mirroring only each command. Chat de-flooded to opener + final summary.
write_file mkdir -p parent dir so relative/absolute paths both work
(fixes the regression where script creation silently failed). ui.rs
fmt_line returns Vec<Line> splitting on \n so multi-line agent output
renders as an indented block instead of one garbled row.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the Goose agentic harness across the codebase per
docs/spec-native-harness.md §3. Goose made N sequential model calls inside the
sandbox (slow on CPU-only hardware) and forced an in-container→host Ollama
gateway that tripped the rootless-Podman slirp4netns loopback bug.
- bridge.py: delete _run_goose/_goose_argv/_goose_present + GOOSE_* consts and
the present-cache; __init__ now takes harness="simple"/max_turns=5; granted
!task runs _run_simple until the native loop lands (Phase 2).
- __main__.py: --harness {native,simple} (was {goose,simple}); drop
--goose-max-turns, add --max-turns; default harness simple.
- app.rs: /ai start accepts native|simple (plain aliases simple) instead of a
bare plain flag; refresh harness comments.
- sbx.rs: remove the in-container Ollama gateway (Docker host-gateway / Podman
slirp4netns host-loopback) and the dk_bootstrap OLLAMA_HOST env — kills the
slirp4netns loopback bug; drop Goose comments.
- bootstrap.sh: drop goose from the prereq probe.
- bootstrap-ai.sh: remove the entire Goose install block, --no-goose flag,
GOOSE_INSTALLER_URL, host config writer, and goose_bin helper.
- sandbox-bootstrap.sh: remove the in-sandbox Goose binary install + config.
- spec-goose-harness.md: banner — harness portion superseded; Podman stays.
cargo check + py_compile clean. No Goose refs remain (headroom/ untouched).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add an installable-VM catalog that ships pointers only (no multi-GB images
in the repo). When a VM is chosen it is BUILT LOCALLY on the caller's own
machine — nothing is relayed to the room.
- scripts/vbox-library.json: 7-entry manifest (Windows 11, macOS Sonoma,
Kali, Parrot, Ubuntu 24.04, Fedora 41, Debian 12) with download
pointers, ostype, cpu/mem/disk, and build kind.
- scripts/vbox-library.sh: --list / --info / --plan / --install. Build
kinds: iso (download/--iso + createvm + boot installer; EFI+TPM for
Win11), cloudimg (delegate to vbox-new.sh, unattended), ova (import),
manual (pointer-only, e.g. macOS per Apple licensing). Detect-first:
--plan changes nothing, install refuses to clobber and rolls back
half-built VMs, direct URLs fall back to the page + --iso <path>.
- sbx.rs: vbox_library()/library_vm()/vbox_library_install() loaders +
running_vms() for live-state markers.
- app.rs: /sbx vmlib (catalog ✓installed/↓available), /sbx vmlib <id>
(pointer/notes), /sbx vmlib <id> install [--iso path] (local build);
/sbx vms now flags running VMs (▶). Registered in SBX_SUBCOMMANDS +
usage.
- ui.rs: help entry for the VM library.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The docker/podman headless-container desktop (XFCE + TigerVNC + websockify
+ noVNC published on host 127.0.0.1:6080) is removed. It was unreliable: the
desktop stack installed asynchronously during provisioning with every step
wrapped in `|| true` (silent failures), while the host port mapping existed
the moment the container ran — so opening the browser before websockify bound
6080 inside the container reset the connection (ERR_CONNECTION_RESET), with no
readiness signal and no loud failure. Containers also can't be rendered as a
real desktop by VirtualBox (no framebuffer/display), so the only desktop path
that stays is the native VirtualBox VM GUI.
Removed:
- sbx.rs: GUI_PORT const; PortHolder/port_holder()/kill_port_holder() (the
port-consent gate existed only for the noVNC publish); the `gui` param +
`-p 127.0.0.1:6080:6080` block in prepare(); HH_SBX_GUI env in dk_bootstrap();
the `gui` param on provision().
- app.rs: PendingGuiLaunch + PortPrompt structs; the port_prompt App field; the
`gui` field on PendingSudoLaunch; the port-consent modal + the sudo-submit
port check; want_gui/gui parsing + the container-GUI launch message; `gui`
threaded through spawn_launch and all call sites.
- sandbox-bootstrap.sh: the entire HH_SBX_GUI=1 desktop block.
- ui.rs / usage strings: dropped "[gui]"/"noVNC desktop" from the docker/podman
help and the /sbx usage line.
Kept (unchanged): the VirtualBox VM GUI — gui_launch() (VBoxManage startvm
--type gui), launch_vbox_gui, `/sbx vbox gui <vm>` and the `/sbx gui` alias. The
`gui` keyword is still stripped from positionals so `/sbx vbox gui <vm>` parses.
cargo check passes clean (no warnings).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GUI launch (docker/podman) now pre-flights the noVNC port (6080): if it's
already bound, a modal names the holding pid and asks before killing it and
proceeding — never a blind `bind: address already in use` collision.
Sudo capture is now uniform across every backend installer. Previously only
Docker fed the masked-modal password through `--stdin-pass`/`sudo -S`; the
captured password was silently dropped for Podman/Multipass and never wired
for VirtualBox, and those scripts used bare `sudo` (which hangs/corrupts a
raw-mode tty). Now:
- shared `run_ensure()` feeds the password to any ensure-*.sh via stdin
- podman/multipass/vbox scripts gain the docker sudo ladder
(interactive `sudo` / `--yes` `sudo -n` / `--stdin-pass` `sudo -S -p ''`)
- podman's apt path wrapped in `sh -c` so one sudo covers update+install
- vbox GUI path preflights `sudo_ready()` with actionable guidance, falling
back to fail-fast `sudo -n` instead of a tty hang
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Podman as a rootless/daemonless sandbox backend alongside Docker,
Multipass and Local, and wire Goose in as the default agentic harness
for the granted `!task` path (bridge execs `<engine> exec <name> goose
run` and streams output to chat; auto-degrades to the simple one-shot
injector when goose is absent).
Add an optional GUI sandbox track (XFCE + TigerVNC + websockify/noVNC on
:6080) summoned via `/sbx <engine> gui`, plus container-side provisioning
in sandbox-bootstrap.sh and a host-side ensure-podman.sh prereq helper.
Refresh the in-app command help to the backend-led `/sbx <engine> [gui]`
grammar and minor ui tweaks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make the message-input bar part of the F5 resize cycle so its height is
adjustable without a sandbox (multi-line/wrapped inputs are now readable),
and let chat/clergy borrow height from the compose box when no terminal is
present. Add Option C masked sudo prompt that feeds `sudo -S` over stdin to
install/start Docker — the password never reaches chat, the PTY, or outbound
frames, and Docker Desktop on Linux is detected so no sudo is requested.
Fix a freeze where clicking the compose box entered layout-edit mode and
silently swallowed every keystroke: clicking the input bar no longer enters
edit mode, and typing any printable char now drops out of edit mode and types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add a -h/--help guard (prints the usage header) to the five scripts
that lacked one: smoke.sh, smoke-e2e.sh, test-features.sh,
demo-save-load.sh, sandbox-bootstrap.sh. All 15 hh/scripts/*.sh now
respond to --help.
- README: new "Scripts" section detailing every script (setup, hosting,
sandbox provisioning, tests) and noting each takes --help; plus an
archive note for the film-* recording scripts.
- README: new "Window layout" section documenting live pane resizing
(F4 fullscreen, click/F5 to select + arrows to resize) and the
/layout save/load/list/rm/reset presets, with matching keybinding-table
rows.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Trim newcomer-facing clutter in hh/scripts/ without changing the running
product or CI.
- Move the one-off demo-recording scripts (film-save-load.sh,
film-virtualbox.sh) to scripts/archive/ — they depend on external
personal tooling (asciinema, video-toolkit, edge-tts, xdotool) and were
never wired into the README or CI.
- Delete join.sh: it duplicated connect.sh's join with fewer options
(hardcoded port, plaintext-only). Its only unique behaviour — a
pre-join git pull (gitea→origin, ff-only) + rebuild — is now an opt-in
`--sync` flag on connect.sh, leaving one canonical joiner.
No scripts or docs referenced the removed/moved files, so nothing else
needed updating.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, launching a sandbox on a machine without the backend binary
died with a raw "not installed" error and no path forward. Now the
missing backend can be installed in-line, gated on explicit consent, so
a fresh checkout can go from zero to a running sandbox without leaving
the TUI.
Folded into the existing `/sbx launch` verb rather than a new command
(menu is already dense): `/sbx launch docker|multipass [image] install`.
The `install` token opts in; without it the user gets an actionable
error naming the exact retry. The token is filtered out of positional
image parsing so it never shadows a custom image.
The install runs off-thread inside the existing spawn_launch task,
before provisioning, so the TUI never blocks and the *launching guard is
cleared via BrokerMsg on failure. A fresh Docker install also leaves its
daemon up, so launch proceeds straight through.
Scripts (detect-first, never silent, --plan dry-run, idempotent if
already present):
- ensure-multipass.sh (new): Linux→snap (clear failure if snapd absent),
macOS→brew cask, Windows→winget.
- ensure-docker.sh: new --install mode using Docker's OFFICIAL,
GPG-verified apt repo (docker-ce) on Debian/Ubuntu, with dnf
(Fedora/RHEL) and pacman (Arch) fallbacks. Deliberately avoids piping
get.docker.com into a root shell. Existing daemon-start path intact.
sbx.rs: docker_installed()/multipass_installed() detectors and
ensure_docker_install()/ensure_multipass_install() wrappers; ENSURE_MULTIPASS const.
ui.rs: help text documents the [install] option on both backends.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The existing vbox paths only open pre-made images; there was no way to
build one from scratch. Add `/sbx launch vbox new [name]` backed by
scripts/vbox-new.sh: download an Ubuntu cloud image, convert it to a VDI,
build a cloud-init NoCloud seed that creates a sudo login user and installs
the sandbox-tools.json toolchain on first boot, then create + boot the VM.
VirtualBox guests can't be exec'd into like docker/multipass, so cloud-init
is the provisioning channel. Generates a one-time login password (or takes
--pass) and authorizes a local SSH key if present. Runs off-thread; rolls
back a half-built VM on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reorganize the help menu into one VIRTUAL MACHINES cluster covering all
backends, and bring docker/multipass/vbox to save+load parity:
- Launch-time dev toolchain: sandbox-bootstrap.sh + editable
sandbox-tools.json (vim/curl guaranteed), installed in docker AND
multipass sandboxes at provision time.
- Vbox load: vm_restore + `/sbx vmload <vm> [label]` (restore snapshot
then boot the GUI).
- Multipass load: `/sbx load` is now backend-aware (locate_snapshot +
SnapKind), mp_restore re-attaches the shared shell; teardown stops
(not purges) an instance that still has snapshots so they survive
`/sbx stop`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- app.rs: F1 now closes the help overlay (toggle), not just opens it
- host-house.sh: add --user/--name flag to override the $USER default seat
- connect.sh: incremental cargo build before launch so the UI is never a
stale release binary; --no-build escape hatch for toolchain-less joiners
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a BRANCH guard (default: main) so launching the house never demos a
stale checkout. ensure_branch() switches to $BRANCH before the cargo build,
refuses when the tree is dirty (won't clobber uncommitted work), and no-ops
when already on-branch or when BRANCH= is passed to keep the current checkout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- host-house.sh: one tmux session hosting a room (server window) plus your own
GUI client window — builds, frees the port, waits for /health, then attaches.
- host-room.sh: free_port() kills any stale LISTENer squatting on the port so
the bind can't fail with "address already in use".
- join.sh: cap each remote pull with `timeout` + GIT_TERMINAL_PROMPT=0 so an
unreachable remote fails fast instead of hanging.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the 14 hack-house scripts (bootstrap, lets-hack, host-room, smoke,
demo/film harnesses, connect/join, ensure-docker/vbox) into hh/scripts/
and fix their path resolution for the deeper location. Update cross-refs:
sbx.rs script consts, app.rs hints, CI, direnv .envrc, README, and docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>