Merge forge/main into atlas/5034-s4-remove-hive-model
This commit is contained in:
commit
baefb32bd5
22 changed files with 921 additions and 248 deletions
|
|
@ -209,6 +209,11 @@ Writes are best-effort: `append_stdout` / `append_stderr` / `finish`
|
|||
log a warning on sqlite error and let the build continue. A failed
|
||||
log row never blocks a rebuild.
|
||||
|
||||
A row is open (`finished_at IS NULL`) only while its child runs. At startup
|
||||
`Coordinator::open` closes every row still open as `fail` and appends a
|
||||
line saying hive-c0re stopped: the previous process died mid-build, and
|
||||
the retention sweep never deletes open rows.
|
||||
|
||||
### `/harness/hyperhive-model` (per agent)
|
||||
|
||||
Single-line text file holding a claude model name. The harness only
|
||||
|
|
|
|||
|
|
@ -166,6 +166,16 @@ from how the child exited. `continue` clears it and hands the session a
|
|||
fresh turn allowance, which is what makes an interrupt a pause you can
|
||||
undo rather than a session you have to abandon.
|
||||
|
||||
On `claude`, the signal is always SIGINT, and it goes to claude's whole
|
||||
process group. The signal doesn't reach a command that moved itself out of
|
||||
the group (`setsid`), and claude's Bash tool starts every command that way:
|
||||
claude stops those commands itself when it shuts down on SIGINT. A SIGKILL
|
||||
would leave them running, so nothing escalates to one. `interrupt` returns
|
||||
at once, and the name stays claimed until claude has exited: `status` says
|
||||
**stopping**, and the daemon refuses `start` and `continue` on that name
|
||||
until then. A claude that ignores the SIGINT stays stopping until someone kills
|
||||
it by hand or the unit stops.
|
||||
|
||||
When the session knows where its report goes — `start`'s `report_file`,
|
||||
or the path the subagent names when it signals — the daemon appends the
|
||||
stop reason to that file as well, so the artifact you were going to read
|
||||
|
|
@ -338,7 +348,7 @@ page applies as written.
|
|||
On `acp`:
|
||||
|
||||
- each run spawns its own copy of the parent's ACP agent. The end of the
|
||||
run kills it and every command it started. The unit loads
|
||||
run sends SIGINT to it and every command in its process group. The unit loads
|
||||
`backendEnvironmentFile` for it. On the `opencode` preset, the daemon
|
||||
reads a provider key that file leaves unset from the swarm secret store
|
||||
at `swarm/agents/<agent>/acp-provider`, as the harness does.
|
||||
|
|
@ -347,9 +357,10 @@ On `acp`:
|
|||
agent process, and a `continue` with none recorded fails straight away.
|
||||
- a role goes in front of the session's first prompt, since ACP has no
|
||||
system prompt.
|
||||
- `interrupt` sends `session/cancel`, and `force` changes nothing. The
|
||||
runtime kills an agent that ignores the cancel for 10s, with every
|
||||
command it started.
|
||||
- `interrupt` sends `session/cancel`. The runtime sends SIGINT to the
|
||||
process group of an agent that ignores the cancel for 10s, and when the
|
||||
run ends. Nothing escalates past that SIGINT. The name is free as soon as
|
||||
`interrupt` sends the cancel.
|
||||
- the runtime sets `model` and `effort` on the session when the agent
|
||||
offers that value for its `model` or `thought_level` config option. Any
|
||||
other value does nothing (`hive_runtime::AcpRuntime`).
|
||||
|
|
|
|||
|
|
@ -142,8 +142,8 @@ else a `TurnError`) drives the post-claude branch:
|
|||
| `Err(RateLimited)` | sleep `HIVE_RATE_LIMIT_SLEEP_SECS` (default 300), requeue inflight, status back to `online` |
|
||||
| `Err(AuthFailed)` | emit `needs_login_idle` sentinel, requeue inflight, park in `wait_for_login` |
|
||||
| `Err(SessionNotFound)` | resume + create self-heal both missed ("shouldn't happen"); requeue inflight so the next turn creates fresh — no status park, message not dropped |
|
||||
| `Err(ApiStall)` | idle watchdog killed claude after `HIVE_TURN_IDLE_SECS` (default 600) of output silence; sleep `HIVE_STALL_SLEEP_SECS` (default 60), requeue inflight, status back to `online` |
|
||||
| `Err(AgentStall(note))` | the same, for an ACP agent: after `HIVE_TURN_IDLE_SECS` with no `session/update` the harness sent the agent `session/cancel`, and killed it if it ignored that; `note` says which |
|
||||
| `Err(ApiStall)` | idle watchdog sent claude SIGINT after `HIVE_TURN_IDLE_SECS` (default 600) of output silence and claude exited; sleep `HIVE_STALL_SLEEP_SECS` (default 60), requeue inflight, status back to `online` |
|
||||
| `Err(AgentStall(note))` | the same, for an ACP agent: after `HIVE_TURN_IDLE_SECS` with no `session/update` the harness sent the agent `session/cancel`, and SIGINT if it ignored that; `note` says which |
|
||||
| `Err(Failed(err))` | route `[system] \`<qualified-label>\` claude turn failed:\n<err>` to `operator` via `send_to_operator` |
|
||||
|
||||
`ApiStall` catches an Anthropic API stall — a multi-retry connection storm where
|
||||
|
|
@ -152,7 +152,11 @@ the stream goes silent for minutes. On `claude` the idle watchdog lives in
|
|||
resets on every stdout line, so a large but still-streaming turn is never cut —
|
||||
only complete output silence for the window trips it. The harness sets the
|
||||
window from `HIVE_TURN_IDLE_SECS` (`0` disables) and maps the driver's
|
||||
`Error::IdleTimeout` onto `TurnError::ApiStall`.
|
||||
`Error::IdleTimeout` onto `TurnError::ApiStall`. The watchdog sends claude's
|
||||
process group SIGINT, so claude stops the commands it started, and then
|
||||
waits for claude to exit. Nothing escalates to SIGKILL: a claude that
|
||||
ignores the SIGINT keeps the turn open until someone kills it or the unit
|
||||
stops.
|
||||
|
||||
`hive-runtime` applies the same window to an ACP agent's turns. That's
|
||||
also how a provider HTTP 429 ends an ACP turn when the agent retries it without
|
||||
|
|
|
|||
|
|
@ -229,16 +229,17 @@ Slash commands today:
|
|||
|
||||
- `/help` — list commands locally.
|
||||
- `/clear` — wipe the local terminal view (server history kept).
|
||||
- `/cancel` — `POST /api/cancel` → host shellouts `pkill -INT
|
||||
claude`, emits a Note. On an ACP agent it sends the agent
|
||||
`session/cancel` instead, and kills it if the turn hasn't ended 10s
|
||||
later. Also surfaces as a `■ cancel turn` button in the state row
|
||||
while state=thinking.
|
||||
- `/cancel` — `POST /api/cancel` → SIGINT to the turn's claude and every
|
||||
command it started (its process group), emits a Note. On an ACP agent
|
||||
it sends the agent `session/cancel` instead, and SIGINT if the turn
|
||||
hasn't ended 10s later. Also surfaces as a `■ cancel turn` button in
|
||||
the state row while state=thinking.
|
||||
- `/compact` — `POST /api/compact` → sets the deferred
|
||||
`Bus::request_compact()` flag and returns immediately. The harness
|
||||
runs the `/compact` at the next turn boundary (end of the in-flight
|
||||
turn, or `turn::run_pending_compact` when idle); output streams into
|
||||
the live panel. Works mid-turn, not only when idle.
|
||||
the live panel. Works mid-turn, not only when idle. Either way the live
|
||||
panel ends with a `/compact done` or `/compact failed: …` note.
|
||||
- `/new-session` — `POST /api/new-session` (confirms first).
|
||||
Arms a one-shot on the Bus; next turn runs without
|
||||
`--continue`, dropping the resume session entirely.
|
||||
|
|
@ -266,8 +267,8 @@ shaped).
|
|||
- `POST /login/{start,code,cancel}` — claude OAuth login flow.
|
||||
Start/cancel emit `LiveEvent::StatusChanged` to flip the
|
||||
badge to/from `needs_login_in_progress`.
|
||||
- `POST /api/cancel` — SIGINT the in-flight claude turn. Emits a
|
||||
`LiveEvent::Note`.
|
||||
- `POST /api/cancel` — SIGINT the in-flight claude turn and the
|
||||
commands it started. Emits a `LiveEvent::Note`.
|
||||
- `POST /api/compact` — run `/compact` on the persistent session
|
||||
(same MCP config + system prompt + allowed tools as a normal
|
||||
turn — only the stdin payload differs). Flips state to
|
||||
|
|
@ -363,7 +364,8 @@ shaped).
|
|||
`{"<name>": "<upstream_url>"}`). Each entry mounts a transparent
|
||||
reverse-proxy at `/extra/<name>/` that forwards every request (method,
|
||||
headers, body) to the configured upstream, strips hop-by-hop headers
|
||||
on both sides, and buffers the full response body (MVP — SSE
|
||||
on both sides (repeated response headers such as two `Set-Cookie` lines
|
||||
are all forwarded), and buffers the full response body (MVP — SSE
|
||||
connections will appear as one large response rather than streaming).
|
||||
The `/extra/` namespace ensures user-declared proxies can never
|
||||
conflict with native agent endpoints. Upstream values are either an
|
||||
|
|
|
|||
Loading…
Reference in a new issue