diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c89c67fe..0c7a2705 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -74,7 +74,7 @@ Backend selection uses a verified protocol allowlist, not browser inference. [Compatibility](./DEPLOYMENT.md#herdr-compatibility) defines supported versions, legacy fallback, and clipboard limitations. -Herdr 0.9.0 endpoints require generation 1 and exact codecs +Herdr 0.9.0 and 0.9.4 endpoints require generation 1 and exact codecs `shell.snapshot.v1`, `shell.surface.v1`, `shell.input.semantic.v1`, and `shell.blob.v1`. Unknown generations/codecs fail closed. Attachment waits for the initial snapshot; each viewer crops its pane from the shared tab surface. @@ -700,8 +700,10 @@ do not multiply alerts. Finished/needs-attention map to completed/blocked; pane-less custom alerts use their sound (`request` = blocked). The runtime relays each one to Web Push and to browsers as `roamgate.task_notification`; the bridge hello advertises `herdr_task_notifications` so pages disable their own status -tracker. The shell reconnects with capped backoff. Servers without endpoint -support (older Herdr, or endpoints disabled) fall back to the status tracker +tracker. Herdr 0.9.4's Antigravity/Grok foreground-idle policy stays authoritative; +Roamgate does not synthesize extra alerts for their background tasks. The shell +reconnects with capped backoff. Servers without endpoint support (older Herdr, +or endpoints disabled) fall back to the status tracker below, relayed the same way; a temporarily disconnected endpoint server does not. The `status` source tracks agent transitions through per-pane subscriptions and diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index b5d7741c..65ec3525 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -22,15 +22,15 @@ use the remote host's shell and tools. ### Herdr compatibility This source build supports verified legacy protocols 14–20 (Herdr 0.7.0–0.8.2) -and **tagged Herdr 0.9.0 / protocol 22**. Protocol 21 and unknown versions are -rejected at control/binary probes. Use a compatible Roamgate build or separate +and **tagged Herdr 0.9.0 and 0.9.4 / protocol 22**. Protocol 21 and unknown versions +are rejected at control/binary probes. Use a compatible Roamgate build or separate server; **do not downgrade a live server**. Published binaries follow their [release notes](https://github.com/powerfooI/roamgate/releases). The [plugin](#herdr-plugin) separately requires Herdr 0.7.2+. Tab reordering requires Herdr 0.7.2+; Herdr 0.7.0 and 0.7.1 retain their fixed tab order. The bridge confirms support before enabling dragging. -Herdr 0.9.0 uses stable endpoint generation 1, distinct from protocol 22. +Herdr 0.9.0 and 0.9.4 use stable endpoint generation 1, distinct from protocol 22. `ROAMGATE_DISABLE_ENDPOINT=1` explicitly selects legacy direct-terminal fallback. | Area | Behavior / limitation | @@ -47,7 +47,7 @@ Herdr supplies no producing-pane/input identity: a delayed write from A after B becomes foreground can reach B's recent input owner. This is not source-PTY or original-browser isolation. Detach/replacement/disposal invalidates ownership. Reads are disabled; permission failures show copy-retry UI. Ordinary copy/paste -is unchanged. OSC 52 is unavailable on the **0.9.0 legacy fallback**; older +is unchanged. OSC 52 is unavailable on the **protocol 22 legacy fallback**; older servers retain their existing relay. Closing one workspace never implicitly closes its linked group. If Herdr requires @@ -100,14 +100,18 @@ roamgate herdr setup roamgate herdr status ``` -If absent, setup downloads **Herdr 0.9.0**, verifies build-pinned SHA-256 hashes, +If absent, setup downloads **Herdr 0.9.4**, verifies build-pinned SHA-256 hashes, and installs to `~/.local/bin/herdr` on Unix or -`%APPDATA%\roamgate\herdr\0.9.0` on Windows. It never replaces a binary it did not +`%APPDATA%\roamgate\herdr\0.9.4` on Windows. It never replaces a binary it did not install. If already installed, it uses the first binary on PATH or in those locations, leaving it in place. Unix's standard location supports `herdr update`; Windows uses Roamgate's private versioned directory, not the official junctioned store, and needs a newer verified Roamgate install for replacement. +The verified Windows asset is x64 only; managed setup has no native Windows +ARM64 Herdr asset. Windows ARM64 SSH hosts can run Herdr's x64 build under +emulation on a best-effort basis. + Setup installs/starts `herdr server` as a user service: `roamgate-herdr.service` (Linux), `dev.roamgate.herdr` (macOS), or a per-user Windows scheduled task. Definitions carry a Roamgate marker; unrelated existing definitions are untouched. @@ -604,9 +608,11 @@ notifications (Herdr 0.9+), so Roamgate alerts match Herdr's toasts, including runtime for this; Herdr counts it as a connected client for `notification show`, but it never takes focus or tab geometry. `status` (or older Herdr servers) instead observes `working -> blocked` and `working -> done/idle`; startup snapshots are -silent. Delivery is best-effort: OS/Focus settings, outages, expired -subscriptions, or stopped runtimes can prevent it. Messages expire after five -minutes, with no durable replay; HTTP 404/410 removes expired subscriptions. +silent. Herdr 0.9.4 treats Antigravity and Grok as idle once they return to the +foreground prompt, even while background tasks or subagents continue; Roamgate +preserves that policy. Delivery is best-effort: OS/Focus settings, outages, +expired subscriptions, or stopped runtimes can prevent it. Messages expire after +five minutes, with no durable replay; HTTP 404/410 removes expired subscriptions. Turning notifications off revokes on the server before browser unsubscribe. If revocation fails, reconnect/retry or revoke OS/browser permission immediately; diff --git a/server/src/bridge/protocol-compat.ts b/server/src/bridge/protocol-compat.ts index b9b35c16..402fee43 100644 --- a/server/src/bridge/protocol-compat.ts +++ b/server/src/bridge/protocol-compat.ts @@ -3,7 +3,7 @@ export const MAXIMUM_HERDR_PROTOCOL = 22; export class HerdrCompatibilityError extends Error {} -// Retain the verified legacy codecs (14-20) and add only tagged v0.9.0 (22). +// Retain legacy codecs (14-20); tagged Herdr 0.9.0 and 0.9.4 both use 22. // Protocol 21 and future versions must never be echoed through either codec. export function isSupportedHerdrProtocol( protocol: unknown, diff --git a/server/src/herdr/cli.test.ts b/server/src/herdr/cli.test.ts index 301eef66..f17fe0ca 100644 --- a/server/src/herdr/cli.test.ts +++ b/server/src/herdr/cli.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from "bun:test"; import { runHerdrCommand } from "./cli"; +import { VERIFIED_HERDR_VERSION } from "./release"; function fakeConfig() { return { @@ -46,11 +47,17 @@ describe("runHerdrCommand", () => { ...io, loadConfig: () => fakeConfig() as never, detect: () => - Promise.resolve({ state: "running", version: "0.9.0", protocol: 22 }), + Promise.resolve({ + state: "running", + version: VERIFIED_HERDR_VERSION, + protocol: 22, + }), serviceStatus: () => ({ installed: true, active: true }), }); expect(code).toBe(0); - expect(io.out.join("\n")).toContain("running (version 0.9.0"); + expect(io.out.join("\n")).toContain( + `running (version ${VERIFIED_HERDR_VERSION}`, + ); expect(io.out.join("\n")).toContain("installed, active"); }); @@ -76,12 +83,14 @@ describe("runHerdrCommand", () => { Promise.resolve({ outcome: "installed-and-started", binaryPath: "/managed/herdr", - version: "0.9.0", + version: VERIFIED_HERDR_VERSION, protocol: 22, }), }); expect(code).toBe(0); - expect(io.out.join("\n")).toContain("Installed Herdr 0.9.0"); + expect(io.out.join("\n")).toContain( + `Installed Herdr ${VERIFIED_HERDR_VERSION}`, + ); expect(io.out.join("\n")).toContain("/managed/herdr"); }); diff --git a/server/src/herdr/release.test.ts b/server/src/herdr/release.test.ts index 4127a510..8808888a 100644 --- a/server/src/herdr/release.test.ts +++ b/server/src/herdr/release.test.ts @@ -151,6 +151,14 @@ describe("installVerifiedHerdr", () => { test("extracts the Windows zip layout via PowerShell", async () => { const homeDir = scratch(); const appDataDir = scratch(); + const previous = herdrManagedBinaryPath( + homeDir, + appDataDir, + "win32", + "0.9.0", + ); + mkdirSync(dirname(previous), { recursive: true }); + writeFileSync(previous, "previous-herdr"); const content = "fake-zip"; const expectedSha256 = createHash("sha256").update(content).digest("hex"); const installDir = dirname( @@ -187,6 +195,10 @@ describe("installVerifiedHerdr", () => { expect(commands).toHaveLength(1); expect(commands[0].join(" ")).toContain("Expand-Archive"); expect(result.installed).toBe(true); + expect(result.binaryPath).toBe( + join(appDataDir, "roamgate", "herdr", "0.9.4", "herdr.exe"), + ); + expect(await Bun.file(previous).text()).toBe("previous-herdr"); expect( existsSync(join(result.binaryPath, "..", "conpty", "conpty.dll")), ).toBe(true); diff --git a/server/src/herdr/release.ts b/server/src/herdr/release.ts index 52f91146..96f6dcf0 100644 --- a/server/src/herdr/release.ts +++ b/server/src/herdr/release.ts @@ -11,7 +11,7 @@ import { join } from "node:path"; * Herdr release against Roamgate's supported protocol range * (server/src/bridge/protocol-compat.ts). */ -export const VERIFIED_HERDR_VERSION = "0.9.0"; +export const VERIFIED_HERDR_VERSION = "0.9.4"; export const VERIFIED_HERDR_PROTOCOL = 22; export type HerdrReleaseTarget = @@ -23,15 +23,15 @@ export type HerdrReleaseTarget = export const VERIFIED_HERDR_SHA256: Record = { "linux-x86_64": - "4fa1a01158dd8043da92d31b270780b0dcc10603038d9b61cac4d81ab63fb71f", + "493304fbefb0ed0ea0455f373b47f48d24e8774bc7974f4c916ad2e937675b23", "linux-aarch64": - "9c8db20fb7e7427b138d5367113f1621ffd319f2f65d6f009e2594029115f0d2", + "9511cb70982c1ef115f232058e7ed7cf9640aa5a2c0ca78934cefda3aeb093fd", "macos-x86_64": - "d0c920b2a126a74809fa1491411c9a097a44786cac9c2ca51b818a995581cf16", + "2015ca1de3e93b84cdcccbfd94fbbd65dd56338cea4d3e57a8660e977a3bd700", "macos-aarch64": - "32b53df09872628059c789a69f02a6b8e29e14ddf26711421f3463f70c1aef17", + "ea20bfff9cb8c83a996081c926e6ff9370de8e543baa859bca48d19175d895ff", "windows-x86_64": - "b4508c445de1c1a68c760a01735da2aba2fa214b2aafd4b07f732e49b2a64b11", + "0e4ccb5cd7b6ca464a69637cda66d3c93334ebc057c50c5878b8e89e1a20afee", }; export function resolveHerdrReleaseTarget(