Skip to content

Releases: Hocsman/Relayer

v0.8.19

Choose a tag to compare

@github-actions github-actions released this 09 Oct 15:02

Patch release that shows an operator the shell command a Claude Code prompt asks to run, in the desktop application and in the web gateway: since v0.8.18 the prompt had an honest title and no command, which had to be read in the terminal. The command is redacted line by line, keeping what follows a masked value, bounded, and never sent to a viewer. A shell-command prompt no longer carries a tool-call badge, which could have carried the rest of the command to viewers, and the modal's terminal tail loses its escape sequences. The command is read from the agent's screen and the redaction knows common credential forms only, so check it against the terminal before you allow anything.

Added

  • The decision modal shows the command a prompt asks about. For a shell command Claude Code asks to run, the title was honest since v0.8.18 but the command itself reached no screen: the prompt's summary is a constant, and the command the adapter read was kept in the event and shown nowhere. The core's prompt view now carries it, in the desktop application and in the web gateway, and the modal draws it in its own block under the summary. Only the Claude Code shell-command prompt has one: the generic adapter's command, a quoted fragment of the question such as a file name or the word yes, is read by the policy and is not shown. Each line is redacted as the journal redacts values and keeps the text after a masked value, so TOKEN=x curl ... | sh reads TOKEN=[REDACTED] curl ... | sh rather than stopping at the mask; the line breaks are kept, and the command is cut at eight lines of 200 characters, with a mark where it was cut. A bidi override or a zero-width character shows as a replacement character instead of reordering or hiding what is read. A prompt that is a secret (marked sensitive, or a credential) carries none. The redaction knows common credential forms only: a flag whose argument is the secret (mysql -pSECRET), a password piped to a command and a bare key with no known prefix are shown. The command is read from the agent's screen by rules that fit the layouts seen, so it is advisory: check it against the terminal before you allow anything. The TUI does not show it. The command is never written to the audit journal.

  • A viewer is never sent the command. The gateway removes it from every frame and every state a viewer receives, as it removes the host's version and the journal's path. With the terminals shown, a viewer can still read the command where the agent printed it; --viewer-terminals hidden closes that. An operator's copy is left as it was.

Fixed

  • A shell command Claude Code asked about could become a tool-call badge that every client was shown. The badge is read from the same screen text as the prompt, so a command that names mcp__a__b followed by host=... produced a badge whose parameters held the rest of the command, redacted but otherwise as the agent typed it, in the prompt card of every client, viewers included. A Claude Code shell-command prompt asks about a command, not a tool call, and no longer carries a badge. A genuine MCP tool-call prompt keeps its badge, with its redacted, bounded parameters, for viewers too, as the web gateway documents.

  • The gateway sent the unmasked frame to any role but a viewer. There are only two roles, so nothing reached a client it should not have; the choice now fails closed, and an operator alone is sent the frame with the command in it.

  • The last lines of output in the decision modal were the terminal's raw text. For a Claude Code shell command they carried colour codes, cursor movements and window-title strings, which the modal printed as stray characters or let split a word into two. They are now taken out before the lines are cut and redacted, so a secret that an escape sequence split in two is one text again when it is redacted. A cursor move to the right becomes spaces, since Claude Code spaces its words that way; the other moves are dropped, so a screen the agent painted by positioning the cursor reads as the text it wrote, not as the grid a terminal shows. A line is cut at a character, not through an emoji, and the redaction is run on at most 1,024 characters of a line: the URL pattern is quadratic in a long run of letters and digits, and 256 KiB of them an agent prints would have frozen the window.

Full commit list: v0.8.18...v0.8.19

v0.8.18

Choose a tag to compare

@github-actions github-actions released this 09 Oct 11:23

Patch release built with Go 1.26.9, which fixes the standard library advisories in net/http, crypto/tls, net/textproto and mime/multipart that every earlier release carried. A high-risk prompt, such as a shell command Claude Code asks about, is no longer masked as confidential, and a URL that does not parse no longer prints its credentials, a leak the move to Go 1.26 exposed.

Fixed

  • A high-risk prompt was displayed as a confidential one. The display mask covered every high-risk event, so a shell command Claude Code asks about showed the title "Confidential input required", a masked answer field and no command — the command had to be read in the terminal. Masking now covers only what is a secret: an event the adapter marked sensitive, or a credential prompt. A high-risk prompt keeps an honest label and takes a normal, visible answer, in the decision modal and in the TUI, whose notifications now carry the summary the prompt is shown with rather than the adapter's raw text. The decision modal also shows the last lines of the agent's output, redacted and cut; for a Claude Code shell command they are the terminal's raw text, escape sequences included. The command itself is still not in the prompt's summary, which for Claude Code is a constant: read it on the agent's card. A prompt whose text contains token, secret, password or otp, even inside a longer word such as footprint, is still marked sensitive and still masked. The audit journal is unchanged: a high-risk entry is still recorded sensitive, with the constant sensitive_event summary.

  • A URL that does not parse no longer prints its credentials. The redaction that every summary, notification and journal entry goes through returned a URL it could not parse unchanged. Go 1.25 parsed https://bot:secret@host:443:443/repo.git and masked its user and password; Go 1.26, which Relayer is now built with, refuses a host with an extra colon, so the password stayed in clear. The text up to the last @ of a URL that does not parse is now masked, whatever the URL would have meant.

Security

  • Relayer is built with Go 1.26.9. The Go standard library has advisories in net/http, crypto/tls, net/textproto and mime/multipart (GO-2026-6603, GO-2026-6607, GO-2026-6608, GO-2026-6613 and GO-2026-6617 among them; govulncheck reported at least seven) that are fixed in Go 1.26.9 and in Go 1.27.2. Go 1.27.0 and 1.27.1 are affected. The 1.25 series, which every release was built with (1.25.13 from v0.1.1-alpha to v0.8.17, 1.25.8 for v0.1.0-alpha), is no longer supported and gets no fix. The web gateway serves net/http, and the desktop's update check and the webhooks speak HTTP and TLS as clients. Building from source needs Go 1.26.9, or 1.27.2 or newer. Moving the go line to 1.26 also changes the defaults of three GODEBUG settings: TLS clients offer two more hybrid ML-KEM key exchange groups, and url.Parse refuses a host with an extra colon (see the fix above).

Full commit list: v0.8.17...v0.8.18

v0.8.17

Choose a tag to compare

@github-actions github-actions released this 08 Oct 14:11

Patch release that lets Relayer see the prompts Claude Code asks before it runs a shell command or creates a file (2.1.285 and 2.1.286) or edits one (2.1.285), which an agent waited at unseen until now, and that makes a panic while reading one agent's output end that agent's session instead of the whole process. A person answers those prompts: Allow and Deny are not offered for Claude, since no answer to them has been checked against a real Claude Code, and the rules rest on 2.1.285 cases of unconfirmed provenance and on 2.1.286 test strings. The desktop application and the gateway also flag an agent whose version they were not verified against; the probe that finds the version runs only the agent's own binary, inside its deadline and outside the gateway's state lock.

Added

  • The Claude adapter recognizes the prompts Claude Code asks before it runs a shell command, creates a file and edits a file. The adapter knew only the workspace-trust and environment-key prompts of 2.1.59, so an agent stopped at any of these waited for an answer nobody was asked for. The shell-command and create-file prompts of 2.1.285 and 2.1.286 and the edit-file prompt of 2.1.285 are now raised as events: the shell command as a high-risk permission carrying a best-effort reading of the command Claude Code displays, the other two as confirmations of unknown risk. A person answers all three at the terminal. The desktop and web decision modal take a typed answer too, but for the shell command it shows the title "Confidential input required", the agent, adapter, risk and rule, and a masked field, but not the command, the terminal output or the options, so read the command in the terminal. Create and edit are unknown risk, not low, so no policy allows them by itself: what Claude Code is asked to write decides what runs next (.github/workflows, .claude/settings.json, a package.json script), and the guardrails only partly cover that. A deny a policy would give is handed back to a person, since the adapter has no verified deny byte. Allow and Deny buttons are not offered in the modal; the TUI still lists F2 and F3, which write nothing and bring the prompt back. No answer to these prompts is backed by a stored capture of its effect: Enter is expected to take the highlighted option, which Relayer does not read, so answer at the terminal. The 2.1.285 cases have unconfirmed provenance and the 2.1.286 layouts are test strings, not fixtures; the 2.1.59 workspace-trust and API-key rules also recognize the unnumbered menu Claude Code draws in 2.1.286, from test strings only. Other prompts Claude Code asks (overwriting an existing file, a PowerShell command, a web fetch, an MCP tool) have no rule and no stored capture: one is raised only if its text happens to match one of the five rules or an intercept_pattern, and otherwise the agent waits unseen.

  • The desktop application and the gateway flag an agent whose version they were not verified against. When a run starts, each vendor agent's own binary is run as --version, and an agent whose version is not one its adapter's patterns were captured against is marked "unverified version" on its card and in the decision modal, since its prompts and the answers it expects may differ from what the adapter knows. It is informational and stays that way deliberately: it changes no policy, an unverified version does not refuse an automatic decision, and nothing is written to the audit journal. The TUI and relayer doctor do not run it. See the security model.

  • RELAYER_NO_VERSION_CHECK=1 turns the version probe off for every user of a machine, as RELAYER_NO_UPDATE_CHECK turns off the desktop's release check: nothing is executed and no badge is shown. It is an environment variable rather than a configuration key because the probe runs a child process on the host — a machine-level concern, an endpoint agent that flags process spawns or a network home directory — while config.yaml is per-project, strict-schema and travels through version control. A probe's answer is now cached per executable path until that file's modification time or size changes, so restarting a run does not run every vendor tool again; an upgrade changes the file and asks the new binary.

Fixed

  • A panic in the goroutine that reads an agent's output ends that agent's session, not Relayer. Nothing recovered a panic there, so a bug in an adapter, or output an agent chose to make it trip, ended the whole process and with it the supervision of every other agent, in the TUI, the desktop application and the gateway. The reader now returns the panic as the error that ends that session, like any other processing failure, with the runtime error's message (numbers and type names, never text from the agent) or, for any other panic value, only its type, and the place it came from. A PTY agent's terminal is closed with its reader, as after any reader failure; with tmux the pane keeps running with nobody reading it, so stop it from the interface. A panic in the same adapter code reached when a policy answers a prompt, or when a gateway call delivers an answer, is still not recovered and ends the whole process. In the TUI, Bubble Tea catches such a panic (a policy answer, a tmux re-synchronization), prints it, restores the terminal and ends the TUI, and with it the supervision of every agent.

  • A very small browser terminal could blank a Windows agent's screen. A terminal pane that had not been laid out yet, or a phone-sized one, sent a resize of a few columns, and the Windows pseudo-console could blank the screen it kept. The gateway now raises any resize to at least 40 columns by 15 rows, the browser terminal reports no size under 20 by 8, and the terminal pane has a minimum height of 250 px, so a narrow pane gets a wider PTY than it shows. The desktop application's own resize call is not clamped.

  • The version probe replayed the command line it was told to run. Starting a run executes each vendor agent's own binary as --version, to compare its version with the ones its adapter's patterns were captured against. For sh -c and cmd /c it rejoined the configured arguments, appended --version and ran the whole script again, so a configured command's side effects happened twice per start — a marker file the script wrote was written once by the probe and once by the agent. A launcher (npx, node, python, docker) reported its own version as the agent's, and an agent configured with shell: and no argv probed whatever claude the PATH happened to resolve. The probe now runs argv[0] alone, with none of the configured arguments, and only when its base name — without a Windows .exe suffix — is exactly the adapter's own executable (claude, codex, aider, goose, interpreter); anything else is not probed, and shows no version and no badge. On Windows, sh.exe and cmd.exe fail that test by name.

  • The probe's 2.5s timeout did not bound the probe. Killing the process left the wait for its output pipes, which a grandchild that inherited stdout — a daemon the tool spawned, a pager, a hung child — held open for as long as it lived. Every agent was probed in turn, and on relayer serve that ran under the gateway's state lock, so one such tool stalled getState for every connected operator and viewer. The wait is now bounded 500ms past the process, a run's probes run in parallel within a 4s budget, and the gateway runs them before taking its state lock. What a probe reads is bounded at 64 KiB, and is stdout alone, on the line that names the product: a banner or a launcher's own version no longer becomes "the agent's version" that every client saw.

  • A viewer received the host's tool version. installedVersion and the reason text that quotes it crossed to viewers with the rest of the state, as the startup notices carrying the same reason did not. A viewer now receives the unverified flag alone — that an agent's version is not one this build was verified against is supervision information, and a viewer watching a prompt should have it — and neither the version nor the reason: which build of a tool a machine runs is host tooling state, like the notices and the journal's path that getState already hid.

  • An agent whose adapter the configuration left empty was never warned. The probe tested the configured adapter, so command: [claude] with no adapter: — which the run resolves to the Claude adapter — showed nothing whatever its version, while an adapter the registry resolved away to generic was probed on the configured name alone. The check now uses the adapter the run resolved.

  • The version warnings were in French, while every other string a run shows an operator is English: the reason a version is unverified, the startup log's Avertissement, and the desktop badge's version non vérifiée.

Full commit list: v0.8.16...v0.8.17

v0.8.16

Choose a tag to compare

@github-actions github-actions released this 29 Sep 10:22

Patch release whose Aider, Open Interpreter and Goose adapters read the questions those agents actually ask, captured from real sessions, and answer with bytes observed to do what they say. Goose's deny, n and Enter, would have allowed the tool call on Goose's real menu; it now picks Deny. A web gateway can keep the agents' terminals from its viewers with --viewer-terminals hidden, and a keystroke to an agent that reads nothing now gives up after five seconds on macOS, the BSDs and Windows as it did on Linux.

Fixed

  • The Aider adapter read the questions Aider asks, not questions it was guessed to ask. Its patterns were written by hand from documentation and never checked against Aider. Captured from Aider 0.86.2 in a PTY, against a stand-in model and a disposable repository, half of them turned out to be questions Aider never asks ("Apply changes?", "Commit changes?", "Push to remote?", "Add files to git?"), while "Add .aider* to .gitignore (recommended)?" went undetected and "Add command output to the chat?" and "Allow edits to file that has not been added to the chat?" were both reported as adding a file to the chat, the second one a file edit shown as a low-risk question. The adapter now reads the six questions captured, each answered with y and Enter and with n and Enter and its side effect checked, and nothing else: an Aider question not among them falls back to the configured intercept_patterns. The fixtures are in internal/adapters/testdata/aider.
  • The Open Interpreter adapter read the questions Open Interpreter asks. Its patterns were written by hand from documentation. Captured from Open Interpreter 0.4.3 in a PTY, against a stand-in model, it asks two questions, "Would you like to run this code? (y/n)" and, under --safe_mode ask, "Would you like to scan this code? (y/n)", and nothing before installing a package or saving a file; the hand-written "run the following code" and "run this command" wordings were never printed. It also prints the question, then two line breaks and the indentation it reads the answer on: written in one piece, the question was two lines above the line the adapter looked at, and was seen only when a repaint happened to precede it. The adapter now reads the two captured questions, each answered with y and Enter and with n and Enter and its side effect checked, on the line above the answer line too. The fixtures are in internal/adapters/testdata/interpreter.
  • The Goose adapter's deny would have allowed the tool call. Its patterns were written by hand from documentation, as (y/n) questions answered with y or n and Enter. Captured from Goose 1.52.0 in a PTY, against a stand-in model, Goose asks no such question: it asks before a tool call with a menu (Allow, Always Allow, Deny, Cancel), where y and n do nothing and Enter picks the highlighted option, Allow. n and Enter was observed to run the command. None of the patterns matched Goose's output, so the adapter never sent it, and no Goose question was ever detected either. The adapter now reads the two menus Goose draws, "Goose would like to call the above tool, do you allow?" and "Do you allow this tool call?" (drawn when a check attached a notice, as for extension management), in Unicode and ASCII, and answers with the keys that pick Allow or Deny wherever the highlight is: k to the top, j down, Enter. Each answer was typed into Goose and its effect checked. Manual input names allow, deny or cancel. The fixtures are in internal/adapters/testdata/goose.
  • A question drawn over several lines could be lost when a read ended inside a line break or a character. Before the screen took over detection, a read that ended between the \r and the \n of a line break erased the line it ended, and one that ended inside a UTF-8 character had its bytes replaced. Goose's menu read a byte at a time left nothing of itself to detect. The detection window now holds either back for the next read.
  • A web keystroke to an agent that reads nothing could block for good on macOS, the BSDs and Windows. Once such an agent's terminal input buffer is full, a write waits for room. On Linux the gateway's keystrokes gave up after five seconds; elsewhere the write blocked until the agent read again or exited, and held the session's write slot, and the run's end, with it. On macOS and the BSDs the terminal is now written through the runtime's poller, as on Linux, which the five-second bound and a Stop both end; on Windows a timer cancels a console write still blocked at its deadline. TestAKeystrokeWriteToAnAgentThatReadsNothingEndsWithItsContext now runs on macOS, and TestAKeystrokeWriteToAWindowsAgentThatReadsNothingEndsWithItsContext on Windows.

Added

  • relayer serve --viewer-terminals hidden keeps the agents' terminals from viewers. A viewer token was a token to read every terminal: the snapshots carry each screen verbatim, so a secret an agent echoed reached every viewer, and the only choice was not to hand viewer tokens out. With the option, a viewer's state and snapshots carry each agent's status, exit code and prompt card but no output, a recording's contents are refused to it, and its interface says why in place of each terminal. Operators are unaffected, and the default, shown, keeps the behaviour as it was.

Full commit list: v0.8.15...v0.8.16

v0.8.15

Choose a tag to compare

@github-actions github-actions released this 29 Sep 08:32

Patch release whose desktop application says when a new version is out. At launch it asks GitHub for the latest release and offers its download in a banner; the check is on by default and turned off in the settings. The desktop binaries now carry their version, so this release is the first that can tell a newer one apart: the banner appears from the next release on. As PID 1 in a container without an init, Relayer also reaps the orphans a stopped agent leaves behind.

Added

  • The desktop application tells you when a new version is out. At launch it asks GitHub for the repository's latest release and, when it is newer than the running one, shows a banner with a Download button: on Windows it downloads the new installer, elsewhere it opens the release page. Not now sets that release aside until the next. The request carries nothing but the running version; the check is turned off in Settings → Notifications, or for every user of a machine with RELAYER_NO_UPDATE_CHECK=1. A link only ever leads to a release of this repository, whatever the response names, and Relayer installs nothing by itself. Until now a user had to watch the Releases page. The desktop binaries of a release now carry its version, which the check compares; they said dev. The README's privacy section says so.

Fixed

  • Relayer running as PID 1 left every stopped agent's orphans in the process table. In a container started without an init, Relayer adopts every orphan, and nothing but Relayer can reap them. v0.8.14 stopped counting those zombies when it confirmed a stop, but left them behind, so each Stop of an agent whose shell had started a child added one for good. When a stop leaves an agent's process group with nothing but zombies, Relayer now waits for those that are its own children. It waits only for members of that group and never blocks, so it cannot take the exit status of a process it waits for elsewhere; a process that left the group, or anything else in the container, is still left to an init, which docs/web-gateway.md still asks for. TestStopIsConfirmedWhenAnOrphanIsLeftAsAZombie now also fails against v0.8.14.

Full commit list: v0.8.14...v0.8.15

v0.8.14

Choose a tag to compare

@github-actions github-actions released this 28 Sep 18:24

Patch release that stops a zombie from blocking an agent. Where orphans are reaped late or not at all (Relayer as PID 1 in a container without an init, the Kubernetes default, or an init that reaps on a timer), stopping an agent whose shell had started a child was reported as unconfirmed, which blocked its next run. On Linux, Relayer now confirms a stop whose process group is left only with zombies; relayer-capture had the same fault and is fixed too.

Fixed

  • A Stop was reported as unconfirmed when the agent's shell had started a child, wherever orphans are reaped late or not at all: in a container where Relayer is PID 1 without an init, the Kubernetes default, and under an init that reaps on a timer, as some sandboxes do. The shell dies without waiting for its child, which is killed with the group and stays in it as a zombie until whoever adopted it reaps it; Relayer probed the group by number, found the zombie, and reported every such Stop as unconfirmed, which blocks the agent's next run. A zombie runs nothing and holds no descriptor, the terminal included, so on Linux the check that follows the forced kill now reads the group's members in /proc and confirms a group left only with zombies. A process counts as a zombie only once it is down to its last thread. The group counts as running whenever /proc might not show all of it: mounted from another PID namespace, or with hidepid. Other systems check as before. relayer-capture waited for the same zombies after killing a capture's process group, and failed the capture when that wait ran out, after about two seconds, or six with tmux, which waited twice; it now counts them as the session does. TestStopIsConfirmedWhenAnOrphanIsLeftAsAZombie, and TestACaptureEndsWhenAnOrphanIsLeftAsAZombie with its timed-out and tmux counterparts, fail against v0.8.13. docs/web-gateway.md still asks for docker run --init: Relayer does not reap the zombies, it only no longer counts them.

Full commit list: v0.8.13...v0.8.14

v0.8.13

Choose a tag to compare

@github-actions github-actions released this 27 Sep 14:19

Patch release that gives Windows an installer. relayer-desktop_0.8.13_windows_amd64_setup.exe installs Relayer for the current user, without administrator rights, with a shortcut on the desktop and in the Start menu; the zip remains for those who prefer it. The Windows executable now carries the release's version.

Added

  • A Windows installer, relayer-desktop_<version>_windows_amd64_setup.exe, next to the zip. It installs Relayer for the current user in %LOCALAPPDATA%\Programs\Relayer, without administrator rights, with a shortcut on the desktop and in the Start menu, installs the WebView2 runtime when Windows lacks it, offers to start Relayer when it ends, and registers an uninstaller in Settings → Apps that leaves the configuration in %APPDATA%\relayer in place. Until now Windows users extracted a zip and had to make their own shortcut. The installer is built on every change by the GUI workflow, whose run keeps it as an artifact, and published with the release, included in the desktop checksums, their signature and the provenance attestation. The executable and the installer now carry the release's version; they said 1.0.0. The installer is not code-signed, so SmartScreen may ask to confirm it.

Full commit list: v0.8.12...v0.8.13

v0.8.12

Choose a tag to compare

@github-actions github-actions released this 26 Sep 16:58

Patch release that makes the desktop app usable again: since v0.8.9 at least, it opened on an empty window. An idle application sent its agents to the interface as null, and the interface stopped before drawing anything. The web gateway was not affected. Release pages now show this CHANGELOG's section for the tag rather than the raw commit list.

Fixed

  • The desktop app opened on an empty window, on every launch, in v0.8.9, v0.8.10 and v0.8.11 at least. An application that has just started is idle and has no agents; the copy of its state that GetState returns turned that empty list into a nil slice, which reaches the interface as "agents": null, and the interface read it as a list before its first render, so nothing was drawn but the window's background. The state's lists are now always lists, and the interface takes a null list as empty. TestAnIdleApplicationSendsItsListsAsLists and a relayerState test fail against v0.8.11. The web gateway builds its lists another way and was not affected. Found by the v0.8.11 trial on Windows.

Full commit list: v0.8.11...v0.8.12

v0.8.11

Choose a tag to compare

@github-actions github-actions released this 26 Sep 12:53

Changelog

  • d26244c Revert "chore(release): fold the unreleased fixes into v0.8.10"
  • 4a4a5d8 build(web): rebuild the embedded interface
  • c2a60e2 chore(release): fold the unreleased fixes into v0.8.10
  • 843f8e6 chore(release): prepare v0.8.11
  • ef014c4 fix(config): a notifications save keeps the block as written
  • 9482b1a fix(config): a replaced webhook is a new entry, a blank severity is rewritten
  • 4e52959 fix(config): switching from strict to permissive keeps the root relative
  • 4d7897d fix(gui): a click on a settings tab switches the tab
  • aa4e15d test(settings): the Windows trial in sequence, on both front ends

v0.8.10

Choose a tag to compare

@github-actions github-actions released this 25 Sep 12:39

Patch release that stops a settings save from losing configuration. A save from the web interface rebuilt every agent from the form alone, so it dropped every agent's environment variables and shell script, and "Restart the agents" did so even when nothing was edited, restarting the agents without their environment; on both front ends a save reformatted agents it had not changed, and the security tab dropped settings it did not show. A save now writes only what was edited, and a save that changes nothing leaves the file as it was.

Fixed

  • A save from the web interface lost every agent's environment variables and shell script, and has since relayer serve was introduced in v0.5.0. The gateway rebuilt each agent from the form alone, and the form carries neither: every save of the Agents tab wrote each agent back without its env, API keys included, and turned a shell: agent into command: [<id>], a program named after the agent. It also wrote the catalogue's adapter over a blank adapter. "Restart the agents" sends the agents even when nothing was edited, so a plain restart did the same, and the agents came back without their environment. The gateway now shows and saves agents through the desktop's resolver, now shared (internal/agentprofile), as docs/configuration.md always said it did:

    • an agent with environment variables, a shell script or an adapter the form does not know is read-only in the editor, and every save copies it exactly as the file has it. A save that leaves it out, or sends its ID as a new agent or with a command, is refused and writes nothing;
    • an existing agent keeps its command and adapter; the form changes its name, working directory and backend, and replaces its command only as a whole;
    • an agent removed and added again under the same ID is a new agent and inherits nothing from the one it replaces, its environment included;
    • the interface sends preserve, which the gateway read as preserveOnSave and so never saw;
    • a configuration written by hand often could not be saved from the web interface at all: the gateway named no catalogue entry for a custom command or a shell agent and showed a blank adapter as blank, which the form refuses, so Save and Restart stayed disabled. Every profile now names a catalogue entry and the adapter the agent runs with.

    TestAWebRestartKeepsEachAgentsEnvironment, TestAWebSaveKeepsEveryAgentItDidNotChange, TestAWebSaveCannotDropOrReplaceAReadOnlyAgent and TestTheWebProfileViewShowsNothingItCannotRoundTrip fail against v0.8.9. A file edited while the editor is open is refused as stale on both front ends, so a save can neither drop an environment variable added in the YAML nor bring back one removed there (TestAnEnvironmentEditedWhileTheWebEditorWasOpenIsNeitherLostNorRevived and its desktop counterpart). Found by the v0.8.9 acceptance pass; v0.8.8 did the same.

  • Saving the agents rewrote every agent in the file, on the desktop and the web gateway alike, since the agent editor was introduced in v0.1.0-alpha: a desktop save that changed one agent, and on the gateway every save of the Agents tab and every "Restart the agents", rebuilt every entry. Every agent lost its comments, flow sequences became block lists, a shell script's quoting changed, and every agent that inherited the file's backend was pinned to it with backend: pty, so a later change of the file's backend no longer reached it. A relative cwd came back as an absolute path naming the user's home directory for an agent added again under a new ID. An agent a save did not change now keeps its entry as written, its values, comments, flow sequences and quoting; a changed agent keeps every field it did not change where it was, and a changed field keeps its comment (TestAReplacedCommandKeepsItsComment); a new agent in a directory another entry already names gets that entry's relative text; the file keeps its Windows line endings and byte-order mark, where a rename in a CRLF file changed every line (TestASaveKeepsTheFilesLineEndingsAndByteOrderMark); and a save that changes nothing writes nothing: the revision stays, and no open editor has to reload. A save that writes still re-encodes the whole file, so blank lines between entries, folded and multi-line plain scalars, escape sequences, the spacing before a comment and the %YAML and --- markers are normalized wherever they are; docs/configuration.md lists them. Every value loads back the same. Both front ends also issued a new revision token after every save, written or not, so after one tab saved without a change, or restarted the agents, another tab's next save was refused as stale and the interface replaced that tab's unsaved edits with the file; a token is now issued only when a save writes the file. The writer also refuses to publish a file that would not load back as the agents requested. TestSavingOneAgentLeavesTheOthersAsWritten, TestRenamingOneAgentOnTheDesktopLeavesEverythingElseAsWritten, TestAWebRenameLeavesEverythingElseAsWritten, TestAWebRestartWithNothingEditedWritesNothing, TestASaveThatChangesNothingLeavesEveryOpenEditorCurrent and TestADesktopSaveThatChangesNothingLeavesTheRevisionAsItWas fail against v0.8.9.

  • A security save rewrote the whole policies block, on the desktop and the web gateway alike, since the settings editor was introduced in v0.3.0: the block was rebuilt from the effective policy, which drops what that rebuild does not write. A save that only toggled dry-run, or changed nothing, removed profile: custom and every guardrail set to false, and lost the block's comments and flow lists; since v0.8.6, which resolves a relative workspace_root when loading, it also wrote workspace_root: ./workspace back as an absolute path naming the user's home directory. The block is now edited: a field whose value the file already gives keeps its text, a changed field is written where it is, a profile the file names stays as the base of the fields it does not set, and a save that changes nothing writes nothing. An absolute or rooted workspace_root keeps its text too: the loader now cleans it as the save does, so a save no longer rewrites /srv/ws as \srv\ws on Windows, which Linux does not read as that directory, so that with a configuration shared with Linux every path an agent named there was outside the workspace, or drops a trailing separator (TestASecuritySaveKeepsAnAbsoluteWorkspaceRootAsWritten). Turning the workspace guardrail on in a file that names no root writes only the guardrail: the save also wrote the root the guardrail defaults to, the configuration's directory, as an absolute workspace_root, which then no longer followed the file when it moved (TestTurningTheWorkspaceGuardrailOnWritesOnlyTheGuardrail). When the editor switches to a preset and the file names a profile, the file names that preset, also when a field was adjusted after the preset was chosen: the writer recognized only a preset the policy matched exactly, so choosing strict and then setting the rate limit dropped the profile line and its comments and wrote every field of strict out (TestAPresetChosenThenAdjustedIsNamedWhereTheFileNamesAProfile, TestAWebPresetChosenThenAdjustedIsNamedInTheFile, TestADesktopPresetChosenThenAdjustedIsNamedInTheFile). The writer refuses to publish a file whose policy would not load back as requested. TestASecuritySaveChangesOnlyWhatItChanged, TestAWebSecuritySaveKeepsThePolicyAsWritten and TestADesktopSecuritySaveKeepsThePolicyAsWritten fail against v0.8.9. Found by the v0.8.9 acceptance pass; v0.8.8 did the same.

  • Turning the workspace guardrail on from the web interface guarded a directory that does not exist when relayer serve was given a relative --config, since v0.8.6. With the root field left empty, the root is the configuration's directory; the gateway passes that directory as its command line gave it, cfg, and the save resolved it against itself, writing workspace_root: <cwd>\cfg\cfg, so every path an agent named was outside the workspace. The default root is now resolved once, against the configuration's directory. The desktop passes an absolute directory and was not affected. TestAWebGuardrailOnARelativelyAddressedConfigurationGuardsItsDirectory and TestTheDefaultWorkspaceRootOfARelativelyAddressedConfigurationIsItsDirectory fail against v0.8.9.

Changed

  • The web gateway no longer sends an agent's existing command line to operators, as the desktop never has: an argument can be a credential, and every operator token received it. The Agents tab shows each agent's executable label and argument count, and Replace the command starts a new argv from the catalogue's default. A client that saves agents through the gateway's API must send an existing agent back with preserve and no argv, and a new one with its catalogue presetID and an adapter that profile supports, as the interface does. A read-only agent sent back with preserve keeps the file's name, working directory and backend; any sent with it are ignored.

Full commit list: v0.8.9...v0.8.10