106 Commits

Author SHA1 Message Date
0xWheatyz 52167db085 Re-assert 0600 on deploy key files at every materialize
O_CREAT's mode only applies to brand-new files, so a key whose permissions
were widened externally (e.g. a k8s fsGroup remount chmodding volume files
to group-rw) stayed 0660 forever and ssh refused it. fchmod on each rewrite
makes materialize_private_key self-heal.
2026-08-17 11:22:24 -04:00
Wyatt 2041d84c67 Merge pull request #34 from 0xWheatyz/claude/mobile-app-feature-parity-run9dz
Root checkout is a ref store: worktree spawns never touch it
2026-08-14 15:05:04 -04:00
Claude 24e6cf5ca8 Skip UI-serving tests when the web export is absent
The Next.js export is a generated artifact and deliberately untracked
(82183d9 stopped tracking it; the Docker image builds it in its own
node stage), but three tests still asserted its presence — failing on
every fresh clone. They now skip with a clear reason when
src/handler/api/static/ was never built, and keep guarding any
environment that has it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-14 19:04:39 +00:00
Claude 042e27dd15 Root checkout is a ref store: worktree spawns never touch it
Worktree spawns now fetch only. The agent's branch is cut from
origin/* and the root checkout - which may hold the operator's own
work - is never fast-forwarded, merged, or re-parked by a spawn. A
failed fetch on a worktree spawn is fatal instead of a silent note:
the contract is 'starts at the remote's latest push', and cutting a
branch from stale refs would break it quietly.

Hardening around the same contract: the branch start falls back
origin/HEAD -> origin/main -> origin/master when the head pin is
missing, and a stale agent/<name> branch left behind by a deleted
agent is reset to the remote tip (-B) instead of silently shadowing
it - while -B's refusal to move a branch checked out elsewhere keeps
in-flight agents protected. Root/subdir placements (schedule firings,
mise-init) keep the fast-forward behavior for their shared tree, as
does the explicit sync command.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-14 18:54:53 +00:00
Wyatt 2a8cac7895 Merge pull request #33 from 0xWheatyz/claude/mobile-app-feature-parity-run9dz
Fix stale spawns: default to worktrees from origin/HEAD + tappable fleet stat cards
2026-08-14 07:42:20 -04:00
Claude b4ad63c687 Default operator spawns to a fresh worktree cut from origin/HEAD
A spawn with no explicit placement ran the agent in the shared
project-root checkout. The root only fast-forwards while it is parked
on the default branch, so the moment one agent left it on a feature
branch, every later no-placement spawn — the mobile app always, the web
form whenever the branch field was blank — started from a stale tree:
the operator's freshly pushed commit was fetched into origin/* but
invisible to the agent.

Operator spawns on a git root now default to a worktree on
agent/<name>, cut from origin/HEAD like any explicit worktree spawn, so
a new agent always starts at the remote's latest push and gets the
per-agent isolation the README promises. Schedule firings opt out
(auto_worktree=False): their continuity convention is a state file
living in the root tree across runs. The mise-init bootstrap and
non-git roots keep root placement unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 19:30:10 +00:00
Claude e7d08ef3de feat(app): tappable fleet stat cards open a filtered agent list
The Running / Waiting / Done cards now navigate to a new agent-list
screen pre-filtered to that bucket, using exactly the same grouping as
the counts so the number tapped is the number listed. The list shows
every agent row the API knows — an agent that hasn't dropped a
checkmark yet is visible the moment it spawns, with a status badge, its
age, and a live last-output line while it works. Rows open the agent
detail screen, whose back button now returns to wherever the detail was
opened from (fleet or the list).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 18:44:33 +00:00
Wyatt fc47f02c44 Merge pull request #32 from 0xWheatyz/claude/mobile-app-feature-parity-run9dz
Fix untrusted-workspace wedge on headless runs + mobile Activity screen
2026-08-13 13:38:32 -04:00
Claude eaa23611ac feat(app): Activity screen - the command queue on mobile
Settings → Manage → Activity lists every control command with status
filter chips, when/where it ran (project, agent, claiming worker or
'unclaimed'), and tap-to-expand result/error text; a Sweep CI button
enqueues the global poll. The list auto-refreshes every 5s while open,
so a login_start or spawn can be watched to completion — this was the
one web dashboard page missing from the phone, and exactly the view
needed to diagnose a stuck command.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 17:23:50 +00:00
Claude a93cd27ead Fix untrusted-workspace wedge: re-seed claude trust at every launch
Phase 4 (1517e4d) deleted the tmux launch path and with it the only
caller of claude_config.ensure_onboarded, so agent working dirs -
every fresh worktree is a brand-new path - were never pre-trusted in
~/.claude.json. Headless 'claude -p' runs then wedge or refuse on the
workspace-trust dialog with nobody at a TTY to accept it.

Spawn and resume now mark onboarding complete and trust the working
dir right before launch, next to the settings/claude_gen
materialization. Resume matters independently: a cross-worker resume
can land in a container whose ~/.claude.json has never seen the dir.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 16:58:32 +00:00
Wyatt e007440612 Merge pull request #31 from 0xWheatyz/claude/mobile-app-feature-parity-run9dz
Mobile app full feature parity: run events, schedules, memory, management surface, user accounts
2026-08-13 11:02:31 -04:00
Claude 79163e09e7 Add handler-quiet-output to the built-in skills
Agents narrate far more than anyone reads: the transcript is not the
deliverable, and prose there is spent tokens burying information where
no one looks. The new skill routes each kind of output to its store —
work happens through tool calls; a minimized NOTES.md ledger (one
bullet per action, committed with the work) records what happened and
how; problems and causes go to memory; status goes to the final
checkpoint-sized message the Stop hook captures onto the checkmark; and
questions go through the question tool, which reaches the operator as a
push notification and an answer prompt in the web and mobile apps
instead of stalling silently in the transcript.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 14:55:47 +00:00
Claude 44d1387007 Ignore uv.lock: a test-run byproduct, not a consumed lock file
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 14:44:58 +00:00
Claude b1cdc3d55d Ship built-in operator skills, seeded on API startup
Seven skills now travel with Handler itself and are seeded into the
managed skill store when the API boots: gate recovery, the test
authorship standard, checkpoint quality, memory discipline, mise-task
rules, scheduled-run continuity, and secrets hygiene. They cover the
judgment layer the hard gates cannot enforce — the gates check that
tests pass, not that an agent responded to a blocked gate sensibly or
kept credentials out of logs.

Seeding is idempotent by name: existing rows are never touched, so
operator edits and enable/disable choices survive every upgrade;
deleting a built-in restores it as shipped on the next start (disable
is the supported off-switch). Rows are created shared and enabled, so
they sync to every worker like any managed skill and remain
admin-editable from the dashboard or the mobile app. Seeding failures
log and never block the API from serving.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 14:43:30 +00:00
Claude 8463da45d2 docs: document the mobile management surface + user accounts on mobile
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:55:47 +00:00
Claude c3e3d1627e feat(app): full management surface + email sign-in
Settings → Manage now opens the entire admin control surface on the
phone, mirroring the web dashboard page for page:

- Models: full CRUD for model backends incl. write-only API keys
  (set/clear), harness selection (claude/pi), enable toggles.
- Skills: create, toggle, delete, expand to read SKILL.md, and
  install-from-prompt driven through the command queue.
- Connectors: stdio/http/sse MCP servers with args/env/header parsing.
- Plugins: marketplace plugins pinned to their repo.
- Permissions: default mode + allow/deny/ask rules over the read-only
  env baseline.
- Repositories: register in git-server or manual mode (incl. the
  mise-init bootstrap), sync, delete.
- Git servers: forge hosts with encrypted tokens and generated deploy
  keys (public half selectable for copying).
- Approvals: record operator approve/reject verdicts per branch.
- Shared context: browse and set the cross-agent key/value store.
- Users: invite (with shareable invite links), promote/disable, mint
  reset links, delete — the user-accounts feature that just landed.
- Claude login: drive the worker's claude /login flow from the phone.
- Account (Settings): who you're signed in as, change password,
  sign out with server-side session revocation.

The connect screen gains the matching gate: email sign-in via
/auth/login (session token stored like the legacy env token),
first-run setup when the server has zero accounts, forgot-password,
and the API-token method as fallback (auto-selected for pre-accounts
servers). Memory gains note authoring + deletion via a new
reloadMemory hook.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:55:18 +00:00
Claude 804baadddd feat(app): management foundation - client exposure, manage hub, shared shell
Groundwork for the full admin surface on mobile: the API client gains
the user-account and Claude-management types (skills, connectors,
plugins, permissions, users/auth, owner fields) plus an unauthenticated
authApi helper; AppState exposes the client and the new management
screen names; ManageShell/Field/ErrorNotice and a useResource hook give
the subscreens one shared page/fetch pattern; Settings gains Manage and
Account rows leading to the new hub.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:48:50 +00:00
Claude 25cf6c9f5b Merge origin/main (user accounts) into mobile feature-parity branch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48

# Conflicts:
#	CHANGELOG.md
2026-08-13 13:45:28 +00:00
Wyatt c85fe83343 Merge pull request #30 from 0xWheatyz/claude/user-authentication-system-oeorfo 2026-08-13 09:43:35 -04:00
Claude a5f1fa2a07 docs: note the mobile app's new screens in README + CHANGELOG
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:14:21 +00:00
Claude deb17c95e4 feat(app): memory screen - browse the agent note graph on mobile
A new Memory tab loads /memory/graph when opened and lists the
distilled notes newest-first with kind filter chips
(fact/decision/gotcha/runbook); tapping a note expands its body, tags,
and both directions of its links, resolved to note titles. Read-only by
design — authoring stays with the web dashboard's admin surface and the
agents' own MCP server.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:13:50 +00:00
Claude 31de11c07c feat(app): schedules screen - recurring agent spawns on mobile
A new Schedules tab lists every schedule across projects with its
interval, prompt, next/last run, and role/model badges; the operator
can create one (project, name prefix, interval, role, model backend,
prompt), pause/resume with a switch, and delete with confirmation.
Mirrors the web dashboard's Schedules page, including the model picker
added there. New clock/brain icons join the Lucide subset and the tab
bar grows to four tabs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:12:26 +00:00
Claude 41915b1a33 feat(app): headless run event stream on the agent detail screen
Add an Events tab between Checkmark and Log: while the detail (or
answer) screen is open the store polls the cursor-paged
/agents/{name}/events endpoint every 3s and renders the stream-json
events the way the web dashboard does — assistant text as prose, tool
calls as badges, results as a turns/cost footer, worker notices as
danger callouts, raw lines verbatim. The meta card now also shows the
agent's model backend and supervising worker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:10:09 +00:00
Claude 6319bf0fd4 feat(app): model backend picker on the spawn screen
Fetch the registered model backends (/claude/models) alongside the
fleet poll and offer the enabled ones in a Model select on the spawn
form, defaulting to the Claude subscription — the mobile counterpart of
the web dashboard's per-spawn dropdown. The select is hidden when no
backends are registered, and Select itself now takes value/label pairs
as well as bare strings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:08:44 +00:00
Claude dbf8f46df8 feat(app): sync API client types with the current Handler API
Port the schema additions the backend gained since the last mobile
release: headless-runner fields on Agent (model_id, session_id,
worker_id) plus the AgentEvent row, model_id on Schedule, the
ClaudeModel backend registry row, and the agent-memory note/link/graph
shapes. Mirrors frontend/lib/api.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
2026-08-13 13:07:28 +00:00
Claude eeb8259ba4 Document user accounts: README section, config table, changelog, .env.example
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
2026-08-12 19:38:32 +00:00
Claude 722a2f344c Frontend: email sign-in, first-run setup, reset links, Users admin page
- AuthGate replaces the raw token prompt: first-run setup form (creates
  the admin) when no accounts exist, email/password sign-in with a
  forgot-password flow, and a collapsible raw-API-token fallback for
  legacy/script setups.
- /reset is a public page where invite and password-reset links land;
  success stores the fresh session and enters the dashboard.
- Users section (admin-only nav): invite by email (link always shown,
  emailed when SMTP is configured), admin/disable toggles, reset links,
  and delete with the shared-resources handoff spelled out.
- Sidebar shows who is signed in; sign-out revokes the session
  server-side.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
2026-08-12 19:37:08 +00:00
Claude 110772580a Add auth and per-user separation test suites
24 new tests: setup/login/session lifecycle, invites, resets (with and
without SMTP), admin guards and the last-admin lockout guard, resource
reassignment on user deletion, cross-user 404s on projects/skills/
connectors/plugins/models/schedules/commands/memory, private model
backends rejected at spawn/schedule time, and claude_gen materializing
only shared + owner rows at launch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
2026-08-12 19:30:16 +00:00
Claude 68c24f3a4b Apply per-user skills/connectors at launch; stamp installed skills' owner
claude_gen.apply now takes the launching project's owner and materializes
only shared rows plus that user's own; spawn and resume pass it through.
skill_install stamps imported rows with the requesting user from the
command payload (reinstalls keep the existing owner).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
2026-08-12 19:26:15 +00:00
Claude 6555f1ad79 Scope API resources per user (shared + owned visibility, owner-or-admin edits)
Projects, agents, interaction, approvals, schedules, memory, commands,
and the Claude page resources (skills/connectors/plugins/models) now
resolve through the Actor's ownership rules: users see shared rows plus
their own, creates stamp the creating user as owner, mutations require
the owner or an admin, and invisible resources 404. Legacy env tokens
keep their exact historical semantics (all-access; admin token for the
old admin-gated routes). Commands from a user carry a user:<id> audit
label so they can track their own non-project commands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
2026-08-12 19:24:22 +00:00
Claude c227faa76d Add email/password auth: actor-based deps and /auth routes
- deps: every request resolves to an Actor — a signed-in user (session
  token, hash-stored) or a legacy env token with its historical
  semantics. require_admin/require_shared_write now accept admin users.
- /auth: status probe, first-run setup (first account = admin), login,
  logout, me, change-password, forgot/reset (one-shot links), and
  admin user management with invite links + a last-admin lockout guard.
- Invite/reset links are emailed when SMTP is configured and always
  returned to the admin; forgot never reveals account existence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
2026-08-12 19:16:27 +00:00
Claude deb67c17cf Add auth helpers, SMTP emailer, and user/session repository layer
- handler.authn: stdlib scrypt password hashing + opaque token helpers
  (tokens stored only as SHA-256).
- handler.emailer: plain-SMTP delivery for invites/resets; unconfigured
  SMTP degrades to returning links instead of mailing them.
- config: SMTP_*, PUBLIC_BASE_URL, session/reset/invite TTLs.
- repository: user/session/one-shot-token CRUD, lockout-guard counts, and
  shared-plus-mine visibility filters (VISIBLE_ALL sentinel) on projects,
  skills, connectors, plugins, models, commands, and memory notes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
2026-08-12 19:13:33 +00:00
Claude a414a18fde Add user account schema: users, sessions, one-shot tokens, ownership columns
users/auth_sessions/auth_tokens tables plus a nullable owner_user_id on
projects, claude_skills, claude_connectors, claude_plugins, and
claude_models (null = shared/legacy, so upgrades keep behaving as before).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
2026-08-12 19:10:31 +00:00
Wyatt 59696d8e93 Merge pull request #29 from 0xWheatyz/claude/lightweight-harness-pi-xp4kl8 2026-08-12 14:59:47 -04:00
Claude 5d3414db3b Add CHANGELOG.md with release + deployment notes for the pi harness
Keep-a-Changelog format, seeded with this PR's release: the pi harness
feature set, the control-image changes (Node 22, pi baked in), the additive
0015_model_harness migration, and a numbered rollout checklist (image order,
env, rollback, volume invariants) so the deployment strategy can be verified
against it after merge. README links to it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdGv3u3DfTsP1S188KDhVH
2026-08-12 18:45:15 +00:00
Claude e5c8ebfd34 Add web_search / web_fetch tools for pi-harness agents
pi ships no web tools and claude's WebSearch/WebFetch are Anthropic-server-side
(absent on local endpoints), so these are handler-owned: a new handler.webtool
module (httpx, already a dependency) exposed through the bridge extension the
same way as the memory tools. web_fetch is provider-free — GET, HTML stripped
to readable text, size-capped. web_search resolves SEARXNG_URL, then
BRAVE_SEARCH_API_KEY, then falls back to DuckDuckGo's HTML endpoint with zero
config. Verified live: a pi agent now advertises all 14 tools (7 built-ins +
ask_operator + 4 memory + 2 web).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdGv3u3DfTsP1S188KDhVH
2026-08-12 18:41:41 +00:00
Claude 887576bf22 pi bridge: activate the full built-in tool set (grep/find/ls)
pi ships seven built-in tools but activates only read/write/edit/bash by
default. The --tools flag can't fix this (it is a strict allowlist that drops
extension tools — verified against a live endpoint), so the bridge calls
setActiveTools with everything registered at session start: all seven built-ins
plus ask_operator and the four memory tools. Verified live: the model now
receives all twelve tool definitions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdGv3u3DfTsP1S188KDhVH
2026-08-12 18:27:17 +00:00
Claude a3a5c272a2 Add the pi harness: lightweight local-model agents with full gate parity
Model backend rows gain a harness column (claude | pi). A pi-harness row runs
the agent through the pi coding agent instead of the claude binary — pi speaks
the OpenAI Completions API natively, so a bare vLLM/llama.cpp/Ollama endpoint
needs no LiteLLM/claude-code-router translation proxy, and the loop is far
lighter for slow local token throughput. The Claude subscription and existing
claude-harness backends are untouched.

Parity comes from generated per-agent artifacts under ~/.handler-pi (outside
the repo tree, so the clean-tree gate never trips): models.json + settings.json
render the row as a pi provider pinned as the default model; a bundled bridge
extension (pi_bridge.ts) adapts pi's events to the exact stdin/stdout contract
of `python -m handler.hooks` — the Stop/completion gate re-prompts pi with
blockers via a follow-up message, git push runs the test/build/approval gates
and denies on failure, questions defer through an ask_operator tool into the
normal answer/resume flow, and memory recall is injected at session start. The
memory tools are registered natively (pi has no MCP), shelling to a new
`python -m handler.mcpserver --call <tool>` seam that reuses the MCP server's
implementations. Skills reuse the same ~/.claude/skills sync (pi implements the
same SKILL.md standard) plus the repo's committed .claude/skills.

Sessions are single JSONL files pre-assigned via --session, so cross-worker
resume archives/materializes exactly like claude's; the prompt travels on stdin
(pi has no -- separator). The supervisor normalizes pi's event stream on the
fly: assistant message_end feeds last_output, the final agent_end becomes the
run result. The whole chain was validated live against pi 0.84.1 with a stub
OpenAI endpoint: memory injection, push-gate denial (including the protected-
branch approval gate), stop-gate block loop, and ask_operator pause all ran
end to end through the real hooks and DB.

Also: harness selector in the dashboard Models form, pi baked into the control
image (NodeSource 22 for pi's node >= 22.19 floor), PI_BIN override, docs in
docs/local-models.md, fake_pi fixture + 12 tests (361 total green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdGv3u3DfTsP1S188KDhVH
2026-08-12 18:21:35 +00:00
Wyatt 356fa276a2 Merge pull request #28 from 0xWheatyz/claude/tencentdb-agent-memory-hgqjsy 2026-08-05 13:06:34 -04:00
Claude 82183d9bca Stop tracking the built web UI; build it in the Docker image instead
src/handler/api/static/ was a committed build artifact: Next's content-hashed
chunk names churn on every build, so any two branches touching frontend/ were
guaranteed merge conflicts there, PR diffs drowned in generated churn, and a
forgotten `npm run export` could silently ship a UI older than its source.

- gitignore the export (plus frontend/out and .next were already covered) and
  remove the 52 tracked files.
- Dockerfile grows a `ui` stage (npm ci + npm run build) whose output is copied
  into the packaged tree before pip install, so the image published by docker.yml
  always carries a UI built from exactly that commit's source — the frontend
  build is now effectively part of CI with no new workflow.
- .dockerignore excludes frontend artifacts and any stale local export: COPY
  into src/handler/api/static merges, so a checkout copy must never leak in.
- pyproject: hatchling skips VCS-ignored files, so `artifacts` re-includes the
  export when present; absent it, the wheel builds fine and the API just runs
  headless (it only mounts static/ when the directory exists).
- README documents the two build paths (Docker stage vs `npm run export` for
  source installs) and the headless fallback.

Verified: wheel with the export present ships all 52 files (memory page
included); wheel without it builds clean and create_app() skips the UI mount.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WYqkoYPX8NAo1V2KyXr1pk
2026-08-05 15:43:19 +00:00
Wyatt 810fb80217 Merge pull request #27 from 0xWheatyz/claude/tencentdb-agent-memory-hgqjsy 2026-08-05 11:33:11 -04:00
Claude dc80e7bc28 Add agent memory: linked note store, MCP server, recall hook, and graph UI
The distilled knowledge layer over the raw log/transcript history, inspired by
TencentDB-Agent-Memory's memory-hub model, adapted to handler's invariants
(state lives only in the database; workers stay stateless and disposable):

- memory_notes + memory_links tables (migration 0014) with a portable DAL:
  scoped listing/search (project + global), idempotent linking, and a one-read
  graph. Deleting an agent nulls attribution but keeps its notes; deleting a
  project removes its notes and edges, leaving global knowledge intact.
- /memory API routes: reads on the normal token, note/link authoring on the
  admin token, plus GET /memory/graph for the dashboard.
- Bundled handler-memory MCP server (python -m handler.mcpserver), a
  dependency-free stdio JSON-RPC implementation injected into every launch's
  --mcp-config ahead of the DB connectors and allowlisted in generated
  settings, exposing memory_search / memory_get / memory_save / memory_link.
  Identity and DATABASE_URL arrive via the spawn env, same as hooks.
- SessionStart recall hook: injects the most recent notes in scope as
  additional context at session start, best-effort, never blocking.
- Memory page in the web UI: a hand-rolled force-directed SVG graph of the
  note web (colored by kind, hover highlights, click-through details), plus
  note/link authoring — the app's first visualization, no chart dependency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WYqkoYPX8NAo1V2KyXr1pk
2026-08-04 13:49:47 +00:00
Wyatt 682ea20ffc Merge pull request #26 from 0xWheatyz/claude/local-model-tool-calling-pt0xcq 2026-07-30 13:35:04 -04:00
Claude 44632ea771 Extend the model picker to schedules
schedules.model_id (migration 0013) pins every fired run of a recurring spawn
to a registered model backend, exactly like a hand-spawned agent: the worker
copies it into each firing's spawn payload, the launched agent records the pin,
and resumes stay on the same backend. The Schedules form gets the same Model
dropdown as the spawn form (Claude subscription by default), with a badge in
the schedules table. Create/update routes fail fast on a missing or disabled
backend so a stale selection bounces immediately instead of every firing
failing asynchronously in Activity; a backend deleted later still fails each
firing visibly rather than silently falling back to the subscription.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DzDofD7gP63WpeLG8vEdZu
2026-07-29 18:54:31 +00:00
Claude 2d5c0e34d7 Add local model backends: per-spawn dropdown pointing claude at alternative endpoints
Operators can register Anthropic-API-compatible endpoints (a local Qwen/Llama
behind LiteLLM or claude-code-router, an LLM gateway) on the dashboard's
Claude -> Models tab and pick one from a Model dropdown when spawning an agent.
The agent still launches as the same claude binary with the same hooks, skills,
connectors, plugins, and gates — only its ANTHROPIC_BASE_URL / ANTHROPIC_MODEL /
ANTHROPIC_AUTH_TOKEN env differs — and it stays pinned to its backend across
resumes. No selection keeps the worker's Claude subscription untouched.

- claude_models table (+ agents.model_id pin), migration 0012
- control.models resolves a row into the launch env (API keys Fernet-encrypted
  at rest, decrypted only in the control container; placeholder key when none is
  stored so the subscription OAuth token never reaches a local endpoint)
- /claude/models CRUD (admin-gated writes, key never returned), spawn route +
  worker + CLI (--model) pass the selection through, fail-fast on missing or
  disabled backends
- dashboard: Models tab, spawn-form dropdown, model badge in the agents table
- docs/local-models.md: why bare OpenAI-compatible servers break tool calling
  with Qwen-Coder, and working vLLM/LiteLLM/llama.cpp stacks

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DzDofD7gP63WpeLG8vEdZu
2026-07-29 18:36:16 +00:00
Wyatt c4e6ae0faa Merge pull request #25 from 0xWheatyz/claude/expo-go-mise-setup-c2rsyv 2026-07-23 13:46:49 -04:00
Claude 962a613cc6 Add the Expo Go dev-server task to .mise.toml
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCxHDoWDBHJ9GN3djVFkpw
2026-07-23 17:45:25 +00:00
Claude 4f74b8313f Revert "Add mise task to start the Expo Go dev server behind a reverse proxy"
This reverts commit 02e43f833f.
2026-07-23 17:45:13 +00:00
Claude 02e43f833f Add mise task to start the Expo Go dev server behind a reverse proxy
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCxHDoWDBHJ9GN3djVFkpw
2026-07-23 17:42:42 +00:00
Wyatt 4ef524ab05 Merge pull request #24 from 0xWheatyz/claude/claude-management-page-y7tzmz 2026-07-23 12:36:03 -04:00