Add concurrency caps, the two-pane web UI, AGENTS.md, and plan status
This commit is contained in:
+5
-1
@@ -10,7 +10,7 @@
|
||||
- `internal/agents`: the agent session manager. It owns the running Claude processes and records their activity as events.
|
||||
- `internal/claude`: the Claude Code stream-json client (see `docs/claude-protocol.md`).
|
||||
- `internal/report`: the `report` MCP tool and the Stop hook that requires it each turn.
|
||||
- `web/`: the SPA, embedded into the binary. `byre serve --dev` serves `./web` from disk instead (run from the repo root).
|
||||
- `web/`: the SPA, frameworkless TypeScript compiled file-by-file to ES modules (no bundler). `web/src/*.ts` is the source; `web/src/api.gen.ts` holds types generated from `api/openapi.yaml`, and `web/src/api.ts` is the typed client over them. `go generate ./web` runs `npm ci`, regenerates the types and compiles `web/dist/`, which is committed so `go install` needs no Node. The binary embeds `index.html`, `style.css`, `dist/` and `src/` (for source maps). `byre serve --dev` serves `./web` from disk instead (run from the repo root); pair it with `npm --prefix web exec tsc -- --watch`.
|
||||
|
||||
## Data
|
||||
|
||||
@@ -49,6 +49,10 @@ curl -H 'Tailscale-User-Login: you@example.com' -H 'Tailscale-User-Name: You' ht
|
||||
|
||||
byre chooses the Claude session ID (`vendor_session_id`) up front, so every session can be resumed. Processes start on demand: the first message after a restart resumes the session with `--resume`. Sessions are `running` from the moment a turn is sent until its `result` arrives; on startup, every session still marked `running` was cut off mid-turn, and byre resumes it and sends a message telling the agent to re-check the working tree. Stopping the server kills all agent processes. Completing a task kills its sessions and marks them `completed`.
|
||||
|
||||
Running turns are capped globally and per vendor (`DefaultLimits` in `internal/agents/manager.go`). A turn that would exceed a cap leaves its session `queued`, with the message held in the `turn_queue` table; whenever a turn ends, a process exits, or a task completes, the oldest queued turns whose vendor has room start. Interrupting a `queued` session cancels its queued turn. Idle sessions with a live process don't count against the caps. Whether a turn resumes the Claude conversation is decided by whether the session has ever sent one.
|
||||
|
||||
When Claude Code reports a rate-limit status of `rejected` and the turn then fails, the inbox gets a `rate_limited` item carrying the vendor's status (including when the limit resets) instead of a generic error. `GET /api/admin/status` reports running and queued turns and the latest rate-limit status each vendor sent.
|
||||
|
||||
Agent activity becomes events with `agent_session_id` set: `user.message`, `agent.message`, `agent.thinking`, `agent.tool_call`, `agent.tool_result`, `agent.report`, `turn.ended`, and `session.state`. Anything else Claude Code emits (rate-limit status, task progress, state changes) is recorded unchanged as `agent.vendor`.
|
||||
|
||||
## Inbox
|
||||
|
||||
+23
-21
@@ -1,6 +1,4 @@
|
||||
# Agent Manager — Plan
|
||||
|
||||
Working name: `agentmgr` (placeholder; final name TBD).
|
||||
# byre — Plan
|
||||
|
||||
A self-hosted Go server on a home Mac Studio that manages third-party coding agents (Claude Code, Codex) across projects and tasks, exposed to a SPA over Tailscale. Built for its author first; multi-user and multi-device from the start.
|
||||
|
||||
@@ -8,6 +6,8 @@ A self-hosted Go server on a home Mac Studio that manages third-party coding age
|
||||
|
||||
This plan reflects decisions already agreed. Do not deviate from it without asking first. If an approach turns out not to work, a decision needs revisiting, or something requires reverting work or a destructive action, stop and ask rather than choosing an alternative. Items under "Open items" are not yet decided; raise them when they become blocking.
|
||||
|
||||
`AGENTS.md` holds the standing rules for working on byre (deploying before handoff, UI layout and look, web code). `docs/operations.md` describes how the built system works and runs. "Build sequence" below says what is built.
|
||||
|
||||
## Goals
|
||||
|
||||
- Manage many concurrent agent sessions across projects (repos) and tasks (worktrees).
|
||||
@@ -39,7 +39,7 @@ This plan reflects decisions already agreed. Do not deviate from it without aski
|
||||
### Server
|
||||
|
||||
- Go, single binary, run under launchd on the Mac Studio.
|
||||
- Binds to `127.0.0.1` only. Fronted by `tailscale serve`, which provides HTTPS with a valid `*.ts.net` certificate.
|
||||
- Binds to `127.0.0.1` only. Fronted by `tailscale serve` on its own HTTPS port of the machine's `*.ts.net` name (not a path prefix, which breaks the SPA's absolute paths), with a valid certificate. Clients get HTTP/2 and SSE passes through unbuffered.
|
||||
- SPA served from `embed.FS` in release builds and `os.DirFS` behind a dev flag. Same handler either way.
|
||||
- Storage: SQLite via `modernc.org/sqlite` (pure Go, no CGO), WAL mode.
|
||||
|
||||
@@ -47,11 +47,11 @@ This plan reflects decisions already agreed. Do not deviate from it without aski
|
||||
|
||||
- Identity comes from Tailscale's injected headers (`Tailscale-User-Login`, `Tailscale-User-Name`). Trust them only on requests arriving from loopback via the serve proxy.
|
||||
- Multi-user is a real requirement. Every mutating action records the acting user.
|
||||
- Config maps each Tailscale login to a git name and email, used for commit authorship and co-author trailers.
|
||||
- Users are created on first request. Each sets the git name and email used for commit authorship and co-author trailers through the API (`PUT /api/me`); agents can't start until it is set.
|
||||
|
||||
### API
|
||||
|
||||
- REST, OpenAPI spec-first. Generate the TypeScript client for the SPA from the spec; a Swift client can be generated later for the macOS app.
|
||||
- REST, OpenAPI spec-first (`api/openapi.yaml`). Go server types and routing are generated with `oapi-codegen`; the SPA's TypeScript types are generated with `openapi-typescript` and used through a small typed client. A Swift client can be generated later for the macOS app.
|
||||
- Real-time state: one multiplexed SSE stream per client. Every event carries a monotonic sequence number; clients resume with `Last-Event-ID`.
|
||||
- Terminals are the only WebSocket use: one WebSocket per open terminal.
|
||||
|
||||
@@ -71,8 +71,8 @@ This plan reflects decisions already agreed. Do not deviate from it without aski
|
||||
|
||||
### Concurrency and usage
|
||||
|
||||
- Global and per-vendor caps on concurrently running agent sessions; excess work queues.
|
||||
- Usage visibility per task, workflow stage, and vendor.
|
||||
- Global and per-vendor caps on concurrently running agent turns (idle sessions don't count); excess turns queue.
|
||||
- Usage (cost, duration, turns) is recorded per turn in the event log and available through the API per task, workflow stage, and vendor. The UI never shows it.
|
||||
- Vendor rate-limit errors surface as their own inbox state rather than as generic failures.
|
||||
|
||||
---
|
||||
@@ -133,7 +133,7 @@ Validation: reject malformed calls with a corrective message (for example, an `a
|
||||
|
||||
1. Claude: a Stop hook blocks stopping until `report` has been called this turn, injecting an instruction to call it.
|
||||
2. Codex (or any vendor without a stop gate): on turn completion with no report, the server automatically sends a follow-up turn asking for one.
|
||||
3. After N failed nudges, the review agent synthesizes a report from the transcript and diff. The UI marks it as synthesized, not self-reported.
|
||||
3. After the nudge limit (`maxNudges` in `internal/report/gate.go`), the review agent synthesizes a report from the transcript and diff. The UI marks it as synthesized, not self-reported.
|
||||
|
||||
The same mechanism bounces questions asked in plain prose (a final message that asks something without an `ask_human` call). Expect to tune strictness; overly strict validation produces nudge loops, overly lenient produces filler reports.
|
||||
|
||||
@@ -320,7 +320,7 @@ Diff and stats are computed between the resulting tree and the merge-base with t
|
||||
### Snapshots
|
||||
|
||||
- Built with the same temp-index technique, then `git commit-tree` on the resulting tree.
|
||||
- Stored under hidden refs: `refs/agentmgr/tasks/<task-id>/snap/<n>`. Git deduplicates blobs, so frequent snapshots are cheap.
|
||||
- Stored under hidden refs: `refs/byre/tasks/<task-id>/snap/<n>`. Git deduplicates blobs, so frequent snapshots are cheap.
|
||||
- A snapshot pairs the code commit with the conversation position (`event_seq`).
|
||||
- Triggers: after every tool call (PostToolUse hook where available; shell commands can change files, so file-editing tools alone are not enough), at turn end, on `ask_human`, and on a timer as a backstop. Skip if the tree sha is unchanged.
|
||||
- Retention: keep everything until task completion.
|
||||
@@ -356,7 +356,7 @@ An explicit human action. Removes the worktree, the task branch, and the task's
|
||||
|
||||
## History
|
||||
|
||||
- On each squash merge, attach a git note under `refs/agentmgr/notes` (or `refs/notes/agentmgr`) containing: the plan, the handoff bundle, review summaries, eval results, and resolved inbox decisions. Full transcripts are referenced by task ID.
|
||||
- On each squash merge, attach a git note under `refs/notes/byre` containing: the plan, the handoff bundle, review summaries, eval results, and resolved inbox decisions. Full transcripts are referenced by task ID.
|
||||
- Notes don't push or fetch by default; the merge cycle pushes the notes ref explicitly, and setup configures fetching it.
|
||||
- Archived tasks are searchable across transcripts, artifacts, and decisions.
|
||||
|
||||
@@ -439,9 +439,10 @@ Read-only, scoped to project and worktree roots.
|
||||
|
||||
### UI
|
||||
|
||||
- Split-screen layout.
|
||||
- Fast project and task switching (command palette).
|
||||
- SPA framework not yet chosen.
|
||||
- Two panes, always side by side at exactly half the width each, under one top navbar. Each pane has tabs and can show any view; new views (diff, files, terminal, preview) become tabs, never columns. `AGENTS.md` holds the binding layout and look rules.
|
||||
- Fast task switching: a ⌘K switcher in the navbar, plus a Tasks view.
|
||||
- Frameworkless: TypeScript compiled file-by-file to ES modules, no bundler.
|
||||
- Atkinson Hyperlegible Mono throughout, self-hosted; high-contrast text on a quiet palette.
|
||||
|
||||
---
|
||||
|
||||
@@ -462,6 +463,8 @@ Issues identified in review that implementation must account for:
|
||||
|
||||
### Spike (first)
|
||||
|
||||
**Status: done.** `internal/claude` is the client; `docs/claude-protocol.md` records the protocol; `internal/claude/session_test.go` exercises each capability against the real CLI and records transcripts into `internal/claude/testdata/transcripts/`, with the account email scrubbed. Fork is wired in but untested.
|
||||
|
||||
A Go client for the Claude Code CLI's stream-json control protocol that can:
|
||||
|
||||
- Start a session, stream events, send a message.
|
||||
@@ -475,8 +478,11 @@ Record transcripts from the spike as the seed of the contract test suite.
|
||||
### Milestones
|
||||
|
||||
- **M0 — Skeleton.** launchd service, tailscale serve auth, SQLite schema, OpenAPI spec, projects, tasks, worktree create/delete, event log, SSE stream with resume, embedded SPA shell, basic server observability.
|
||||
- **Status: done**, and running as a service on the Mac Studio. Observability is exposed through `GET /api/admin/status` only; the SPA has no admin view yet.
|
||||
- **M1 — One agent end to end (Claude).** Sessions, streaming, messaging, full-interface rendering for Claude, MCP tools (`ask_human`, `report`), report enforcement, inbox with all views and response types, web push notifications, restart-and-resume, concurrency caps.
|
||||
- **M2 — Seeing the work.** Temp-index diffs and stats, snapshots, timeline scrubbing, terminals, file browser, split screen, previews.
|
||||
- **Done:** sessions, streaming, messaging, interrupt; `ask_human` and `report` with validation; Stop-hook report enforcement; inbox with global, project, and task views and every response type, answered as one batch and delivered to the agent as one message; restart-and-resume; global and per-vendor caps with queueing; rate limits as their own inbox state; the web UI (two panes, task switcher, transcript, inbox).
|
||||
- **Not done:** full-interface rendering beyond messages, tool calls, and interrupt (slash commands, plan mode, model switching, compaction, todo lists, subagent views, image input, `@`-file mentions, Markdown and Mermaid rendering); web push notifications; bouncing questions asked in plain prose; the synthesized-report fallback (needs the M3 review agent); the session instructions from "Session configuration" (no pushing, no other branches, no git maintenance, no self-attribution; only the attribution setting is applied); pinning the CLI version (auto-update is disabled, the version isn't pinned).
|
||||
- **M2 — Seeing the work.** Temp-index diffs and stats, snapshots, timeline scrubbing, terminals, file browser, previews.
|
||||
- **M3 — Second vendor and review.** Codex adapter with full-interface rendering, review profiles, review triggers, visual review output, cross-vendor review.
|
||||
- **M4 — Merging and history.** Full merge cycle with squash, authorship and co-author trailers, drafted commit messages, merge-resolution stage, git notes, task completion and archive, archive search, GitHub Issues integration.
|
||||
- **M5 — Workflows.** Workflow definitions and storage, stages, gates, bounded loops, plan artifact, eval stages, task intake, mid-task swapping.
|
||||
@@ -487,15 +493,11 @@ Record transcripts from the spike as the seed of the contract test suite.
|
||||
|
||||
## Open items
|
||||
|
||||
- Raised in critique, not decided: build the MCP server and inbox first against manually launched agents, to validate the structured-question loop before investing in orchestration.
|
||||
- Verify Codex app-server support for forking threads.
|
||||
- Confirm the exact Claude Code setting for disabling co-author attribution in the CLI version we pin.
|
||||
- Confirm `tailscale serve` behavior for HTTP/2 and for exposing per-task preview ports.
|
||||
- Confirm `tailscale serve` behavior for exposing per-task preview ports.
|
||||
- Terminal persistence across server restarts: accept loss, or back terminals with tmux?
|
||||
- Format of per-project setup config (setup script, port allocation, run/capture config for reviews).
|
||||
- Simulator allocation strategy for parallel tasks and review agents.
|
||||
- Periodic review thresholds (defaults for time and diff size).
|
||||
- Report-enforcement nudge limit N.
|
||||
- Default limits for just-in-time fan-out (depth, concurrency, budget).
|
||||
- SPA framework.
|
||||
- Final app name.
|
||||
- Task navigation: keep both the ⌘K switcher and the Tasks view, or only one.
|
||||
|
||||
Reference in New Issue
Block a user