mirror of
https://github.com/0xWheatyz/handler.git
synced 2026-08-30 07:16:24 +00:00
eeb8259ba4
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
8.7 KiB
8.7 KiB
Changelog
All notable changes to handler are documented here. The format follows
Keep a Changelog; versions are the v* tags
the image workflows publish (plus latest from every push to main).
[Unreleased]
Added — user accounts: email sign-in, invites, resets, per-user separation
- Email + password accounts replace "know the API key" for humans. First run shows a setup form and the first account created is the admin; every later account is invited by an admin through a one-shot set-password link. Passwords are scrypt (stdlib, self-describing hashes); sessions are opaque bearer tokens stored only as SHA-256 with a configurable TTL.
- Password reset by email (
POST /auth/forgot→ short-lived link, silent about account existence) via plain SMTP (SMTP_*settings). Email is optional: with SMTP unset, invite/reset links are shown to the admin in the dashboard to hand over out-of-band. Spending a link revokes the account's existing sessions. - Per-user separation of projects, skills, and tools. Projects, skills, MCP connectors, plugins, and model backends gain an owner; users see shared + their own (foreign resources 404 — existence isn't leaked), owners operate their own projects end-to-end without admin, shared (unowned) rows stay admin-managed and visible to all. Launches materialize only the project owner's skills/connectors, and private model backends can't be picked for someone else's spawns or schedules. Deleting a user reassigns their resources to shared; admins can reassign owners.
- Users page in the dashboard (admin-only): invite, admin/disable toggles, reset
links, delete. Sign-in page gains first-run setup, forgot-password, and a raw
API-token fallback;
/resetis the public landing page for invite/reset links. - Admin safety rails: the last active admin can't be demoted/disabled/deleted; no
self-deletion.
AUTH_TOKEN/ADMIN_TOKEN/SHARED_CONTEXT_WRITE_TOKENkeep their exact historical semantics for scripts/CI and break-glass. - 24 new tests (auth flows + separation matrix; 397 total).
Database (user accounts)
- Migration
0016_user_accounts: newusers,auth_sessions,auth_tokenstables plus a nullableowner_user_idonprojects,claude_skills,claude_connectors,claude_plugins,claude_models. Purely additive; existing rows have no owner (= shared) so an upgraded deployment behaves exactly as before until accounts are created.
Deployment notes (user accounts rollout)
- Apply migrations as usual (the API container runs them on start).
- Optionally set
SMTP_HOST/SMTP_PORT/SMTP_USERNAME/SMTP_PASSWORD/SMTP_FROM(+SMTP_STARTTLS/SMTP_SSL) andPUBLIC_BASE_URLfor emailed links; without them, invite/reset links appear in the dashboard instead. - Open the dashboard and create the first account — it becomes the admin. Existing
AUTH_TOKEN-based scripts keep working unchanged; the token can be rotated or dropped once accounts exist (keep one as break-glass if you like). - New TTL knobs (optional):
SESSION_TTL_DAYS=30,RESET_TOKEN_TTL_HOURS=2,INVITE_TOKEN_TTL_HOURS=168.
Added — the pi harness for local models (#29)
harnesson model backends (claude|pi, defaultclaude). A backend row can now run its agents through the lightweight pi coding agent instead of theclaudebinary. pi speaks the OpenAI Completions API natively, so a bare local endpoint (vLLM, llama.cpp, Ollama) works without a LiteLLM / claude-code-router translation proxy — and the loop is far lighter for slow local token throughput. Selectable in the dashboard's Claude → Models form and viaPOST /claude/models.- Full gate parity on pi via a bundled bridge extension (
pi_bridge.ts, generated into a per-agentPI_CODING_AGENT_DIRunder~/.handler-pi/, outside the repo tree). All gate logic stays in the same tested Python hooks:- Stop/completion gate (tests green + committed + pushed) re-prompts pi with blockers;
git pushruns the test → image-build → protected-branch approval chain and denies on failure;forge merge/mise run deployhit the approval gate;- questions defer through a new
ask_operatortool into the normal answer/resume flow; - memory recall injects at session start;
memory_search/get/save/linkare registered as native pi tools (pi has no MCP) throughpython -m handler.mcpserver --call.
- Web tools for agents:
web_searchandweb_fetch(handler.webtool), registered on pi-harness agents. Fetch is provider-free (HTML stripped to readable text, size-capped). Search resolvesSEARXNG_URL→BRAVE_SEARCH_API_KEY→ a zero-config DuckDuckGo fallback. - Full built-in tool surface on pi:
read,write,edit,bashplusgrep,find,ls(off by default in stock pi) — 14 tools total including the handler set. - Skills + prompts on pi: pi discovers the same web-managed
~/.claude/skillssync and the repo's committed.claude/skills(forge role skills); handler conventions are appended to pi's system prompt;AGENTS.md/CLAUDE.mdare read natively. - Cross-worker resume for pi sessions: single-JSONL transcripts pre-assigned by
handler, archived/materialized through the existing
session_archivesflow. PI_BINbinary override;SEARXNG_URL/BRAVE_SEARCH_API_KEYsettings; afake_pitest binary and 22 new tests (370 total).
Changed
- Control image: Node bumped from NodeSource 20 to 22 (pi requires ≥ 22.19; Claude
Code needs ≥ 18, unaffected) and
@earendil-works/pi-coding-agentis baked in alongside the Claude Code CLI. control.modelsrefactored:resolve_model()returns the row + decrypted key andharness_of()/claude_env()split harness selection from env building.resolve_model_env()keeps its signature (claude rows unchanged; pi rows return{}).- Dashboard Models form gained the harness selector and a
pi harnessbadge; docs (docs/local-models.md, README) describe both harnesses.
Database
- Migration
0015_model_harness: addsclaude_models.harness(NOT NULL DEFAULT 'claude'). Purely additive — every existing backend row keeps its current behavior. Applied automatically by the API container on start (RUN_MIGRATIONSstaysfalseon control, as before).
Deployment notes (for this release's rollout)
Merging to main publishes both images (docker.yml → ghcr.io/0xwheatyz/handler,
docker-control.yml → ghcr.io/0xwheatyz/handler/control). To roll out:
- Pull both images and restart API before control (compose already orders this):
the API applies
0015_model_harnesson boot; the control worker only needs the new column to exist when a pi backend is first selected. - The control image must be the new build before spawning any pi-harness agent —
it carries the
pibinary and Node 22. Older control containers refuse cleanly (launch fails loudly, no silent fallback to the subscription). - No env changes required. Optional:
SEARXNG_URLorBRAVE_SEARCH_API_KEYon the control container for a realweb_searchprovider (unset = DuckDuckGo fallback);PI_BINonly if pi lives off PATH. - Existing agents are untouched: subscription and claude-harness agents launch exactly as before; running agents and their resumes are unaffected by the migration.
- Rollback: reverting the images is safe — the
harnesscolumn is ignored by old code. Only agents already pinned to a pi backend would fail to resume until the new control image returns (alembic downgradewould drop the column; not needed for an image-level rollback). - Volume/layout invariants unchanged: same
PROJECTS_ROOT, sameHANDLER_SECRET_KEYeverywhere, no new shared filesystem. pi state lives under the worker's$HOME(~/.handler-pi/) and sessions ride the existing DB archive flow.
Verification
- 370 tests green (SQLite, real
alembic upgrade headper test), including the newfake_pirunner suite and mocked web-tool provider tests. - The bridge was validated live against pi 0.84.1 with a stub OpenAI endpoint: memory
injection, push-gate denial (including protected-branch approval), the stop-gate
block loop,
ask_operatorpause/resume, and the 14-tool surface all ran end to end through the real hooks and database.
Earlier work (pre-changelog)
Phases 1–2 plus the web dashboard, headless runner, credential store, schedules, model
backends, skill install-from-prompt, and the agent memory layer predate this changelog;
see docs/PLAN.md and the merged PR history for their details.