diff --git a/2-Message-Server.md b/2-Message-Server.md index 75c02c2..6f70e1e 100644 --- a/2-Message-Server.md +++ b/2-Message-Server.md @@ -226,11 +226,27 @@ primary out of herdr — see [Deployment model](#deployment-model).)* > `workspace.list`/`pane.list` to enumerate/re-attach). Crucially, herdr 0.7.0 exposes a > **native `agent.*` namespace** — `agent.start`, `agent.send`, `agent.read`, `agent.list`, > `agent.get`, `agent.focus`, plus `server.agent_manifests` and `pane.report_agent` — so herdr -> already models "agents", not just panes. **Open opportunity:** `bridged`'s south side may use -> `agent.start`/`agent.send`/`agent.list` directly instead of the "create pane + `send_text` a -> launch line" workaround below. Spike this in **Stage 1 (CB-102)** and prefer it if it carries -> env/model cleanly; the pane-based path stays the documented fallback. (Also available: -> `worktree.*` for git-isolated workers.) +> already models "agents", not just panes. (Also available: `worktree.*` for git-isolated workers.) +> +> **CB-102 — decided: `bridged` drives the native `agent.*` path** (the pane + `send_text` +> workaround below is the documented fallback only). The spike proved against the live daemon that +> `agent.start` accepts a **first-class `env` map** that reaches the process environment — so a +> worker's `ANTHROPIC_BASE_URL` is injected cleanly and guard-checked, with no shell-prefix +> parsing and `bridged`'s own env untouched. herdr also tracks each worker's **Claude session +> UUID** (`agent_session.value`), grounding the ID contract in herdr's own identity. Observed +> `agent.*` schema: +> +> | Call | Params | Returns | +> |---|---|---| +> | `agent.start` | `{name, argv:[...], env:{...}}` | `{agent:{terminal_id, pane_id, workspace_id, agent_status}}` | +> | `agent.send` | `{target, text}` | ack | +> | `agent.read` | `{target, source}` — `source ∈ visible\|recent\|recent_unwrapped\|detection` | `{read:{text}}` | +> | `agent.get` | `{target}` | `{agent:{…, agent_session:{value:}, agent_status}}` | +> | `agent.list` | `{}` | `{agents:[…]}` — discovery, each with its session UUID | +> +> `target` is the `terminal_id`. There is **no `agent.stop`** — tear a worker down with +> `pane.close {pane_id}`. `agent_status ∈ idle\|working\|blocked\|unknown` drives the +> status-gated injector (inject only when idle/blocked). > > **Two wire facts the Stage-1 client (CB-101) pinned by contract test — both bit the first > build:** (1) the request `id` **must be a JSON string** — an integer id is rejected with @@ -239,7 +255,8 @@ primary out of herdr — see [Deployment model](#deployment-model).)* > (open → one frame → one line → close), which as a bonus needs no locking. A long-lived socket is > only for the streaming `events.subscribe` path, not request/response. -The pane-based control path (the fallback, and what the examples below use): +The pane-based control path — the **fallback** now that CB-102 chose `agent.*`; shown here for +reference and for herdr builds without the `agent.*` namespace: ```jsonc // spawn: create a pane, then launch the worker in its shell (env stays worker-only) diff --git a/8-Roadmap.md b/8-Roadmap.md index d93cef1..2cf893c 100644 --- a/8-Roadmap.md +++ b/8-Roadmap.md @@ -122,11 +122,22 @@ Compact scope; expand into detailed tickets when a stage starts (as Stage 1 is b ## Stage 1 — detailed tickets **Goal:** Opus, from its own subscription session, mounts `bridged` and gets a code review -back from a real `ccs gx00-vllm claude` worker — same host, one hardcoded profile, reply via a -pane scrape (envelope comes in Stage 2). This is the thinnest end-to-end vertical slice. +back from a real worker spawned under the `ccs ltms-local` profile (routing to the gx00 vLLM at +`http://gx00.gw:8000`) — same host, one hardcoded profile, reply via a pane/agent read (envelope +comes in Stage 2). This is the thinnest end-to-end vertical slice. **Definition of done for the stage:** `CB-107` demo passes. +> **Build status (in `bridged/`, Maven · Java 25 · 25 tests green).** +> ✅ **CB-101** herdr client — connection-per-call, contract-tested vs live 0.7.0. +> ✅ **CB-102** worker spawn — native `agent.*` with env injection, guard-checked, live-verified. +> ✅ **CB-106** config + logging — Jackson YAML + Logback. +> 🟡 **CB-104** REST core — `GET /healthz`, `/sessions`, `/agents`, `POST/DELETE /workers` done; +> the **blocking `POST /sessions/{id}/message` + reply capture** is the remaining piece. +> ⬜ **CB-103** status-gated injector · **CB-105** MCP adapter · **CB-107** e2e demo gate. +> Two herdr wire facts pinned by contract test: **string ids**, and **one request per +> connection**. `agent.start` env is the subscription-safe injection point (no shell prefix). + --- ### CB-101 — herdr socket client @@ -141,14 +152,20 @@ and one event; a **contract test vs the real herdr** asserts `ping.protocol == 1 `workspace.list`/`pane.list` shapes. **Deps.** none. -### CB-102 — spawn a worker via ccs profile -**Scope.** First **spike herdr's native `agent.start`/`agent.send`/`agent.list`** (0.7.0 has -them) — if they carry the `ccs claude` command + env/model cleanly, use them. Else -fall back to the documented path: open a herdr pane and `send_text` `ccs claude` -(+ workspace `cwd`), detecting **Ready** via `pane.wait_for_output` / `pane.read -{source:"detection"}` (prompt-ready), not an absent status. -**Acceptance.** Against a fake `ccs`+`claude` stub in a real herdr pane, the worker reaches -`Ready`; the resolved command is exactly `ccs gx00-vllm claude` (asserted from a spawn log). +### CB-102 — spawn a worker (native `agent.*`) ✅ +**Decided (spike done).** herdr's `agent.start {name, argv, env}` carries the worker command and +a **first-class `env` map that reaches the process** (proven via `agent.read`), so `bridged` +injects `ANTHROPIC_BASE_URL` there — guard-checked before the call — with no shell prefix and its +own env untouched. Chosen over the pane + `send_text` fallback. herdr tracks each worker's Claude +**session UUID** (`agent.list`/`agent.get`), which grounds the ID contract. Teardown is +`pane.close` (no `agent.stop`). +**Built.** `AgentControl` (start/send/read/get/status/list/close) + `WorkerService` (builds env +from config, `assertWorker` **before** any herdr call) + REST `POST /workers`, `GET /agents`, +`DELETE /workers/{paneId}`. +**Acceptance (met).** Unit: `POST /workers` with an off-allowlist base_url → **403 and herdr is +never touched**; a good base_url → `agent.start` env carries `ANTHROPIC_BASE_URL`. Contract: a +live probe spawn proves env propagation and cleans up its pane. (Real `ccs ltms-local claude` +worker spawn is exercised by the CB-107 demo.) **Deps.** CB-101. ### CB-103 — status-gated injector