Skip to content

feat(agent-consoles): registry editor — point "Edit config" at agents.toml - #69

Merged
brettchien merged 2 commits into
mainfrom
feat/adr49-registry-editor
Aug 17, 2026
Merged

feat(agent-consoles): registry editor — point "Edit config" at agents.toml#69
brettchien merged 2 commits into
mainfrom
feat/adr49-registry-editor

Conversation

@brettchien

Copy link
Copy Markdown
Contributor

What

The remote panel's Edit config button edited the deprecated remote.toml. But slice 1 (#66) made agents.toml the source of truth — when it exists, remote.toml is ignored — so once you have a multi-agent registry, that button edits a file the app no longer reads. This closes that gap: the editor now edits the registry (agents.toml), so multi-agent setups are manageable from the UI instead of by hand-editing files (the "Studio is the control plane" gap Brett flagged).

Independent of #68 (fs read path); branched on fresh main.

Backend (studio-desktop)

  • remote.rsread_registry_text returns agents.toml; when absent/empty it seeds the editor with the adopted legacy remote.toml (rendered via AgentRegistry::to_toml), so opening the editor migrates a single-endpoint setup to the multi-agent format on first save. write_registry_text parses + runs AgentRegistry::validate (unique non-empty names, ≤1 management) before writing — a bad edit never lands. Per-endpoint url/token completeness stays a dial-time check (a half-filled entry is savable, matching remote.toml).
  • lib.rsregistry_config / registry_config_write commands returning {path, text}, registered in the invoke handler. The file carries tokens, so this text is editor-only and never mixed into the panel/selector views (those stay token-free).
  • Reject-path unit tests (bad TOML / two managements / dup names) — they short-circuit before any filesystem access.

Frontend (console)

  • RegistryConfig + registryConfig / writeRegistryConfig on Source (Tauri → the new commands; Mock → a canned agents.toml fixture).
  • EditorTarget is now fleet | registry; openEditor loads agents.toml lazily (titled edit agents.toml) on each open; on save it refreshes both the management panel and the agent-console selector, since the registry drives both.
  • remoteConfig still feeds the panel's url+status; writeRemoteConfig / remote_config_write remain for the legacy path but are no longer wired to a button.

Verification

  • console: tsc --noEmit clean · 75 vitest tests · vite build OK.
  • acp-tunnel: 34 tests (incl. AgentRegistry parse/validate/round-trip).
  • studio-desktop (out-of-workspace, GTK) compiles + runs the new reject-path tests in the bundle-macos CI job.

🤖 Generated with Claude Code

brettchien and others added 2 commits August 17, 2026 11:31
…ation

Backend for the registry editor (agent-consoles follow-up): the config editor
still wrote the deprecated `remote.toml`, even though slice 1 made `agents.toml`
the source of truth. Add the read/write path for the registry itself.

- remote.rs: `read_registry_text` returns `agents.toml`; when it is absent or
  empty it seeds the editor with the *adopted* registry (the legacy `remote.toml`
  rendered via `AgentRegistry::to_toml`), so opening the editor migrates a
  single-endpoint setup into the multi-agent format on first save.
  `write_registry_text` parses + runs `AgentRegistry::validate` (unique non-empty
  names, ≤1 `management`) before writing — a bad edit never lands. Per-endpoint
  url/token completeness stays a dial-time check, matching `remote.toml`.
- lib.rs: `registry_config` / `registry_config_write` commands ({path,text}),
  registered in the invoke handler. Tokens live in this text, so it is
  editor-only and never mixed into the panel/selector views (which stay
  token-free).
- Unit tests for the reject paths (bad TOML, two managements, dup names) — these
  short-circuit before any filesystem access.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The remote panel's "Edit config" opened the deprecated `remote.toml`, which
`agents.toml` shadows once it exists — so edits silently did nothing. Repoint the
editor at the registry.

- source.ts / types.ts / fixtures.ts: `RegistryConfig` + `registryConfig` /
  `writeRegistryConfig` on `Source` (Tauri → `registry_config[_write]`; Mock →
  a canned `agents.toml`). `remoteConfig` keeps serving the panel's url+status;
  `writeRemoteConfig` stays for the legacy path but is no longer wired to a button.
- main.ts: `EditorTarget` is now `fleet | registry`; `openEditor` loads
  `agents.toml` lazily (titled "edit agents.toml") on each open; on save it
  refreshes both the management panel and the agent-console selector, since the
  registry drives both. The backend rejects a structurally-invalid registry
  (dup names / two managements) without writing, surfaced as the editor error.

75 frontend tests + tsc + build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@brettchien
brettchien merged commit 4fc01fb into main Aug 17, 2026
2 checks passed
@brettchien
brettchien deleted the feat/adr49-registry-editor branch August 17, 2026 03:45
brettchien added a commit that referenced this pull request Aug 17, 2026
…ote.toml (#72)

Since #69 the REMOTE panel's "Edit config" opens the registry (`agents.toml`,
the source of truth), but the panel still *displayed* the `remote.toml` path
(from `remoteConfig.path`) with a "configure the remote connection" tooltip —
so the shown file didn't match the file the button opens.

Label the registry path instead. It lives in a different view-model
(`RegistryConfig`) than the connection view, so `remoteHtml`/`renderRemote`
take it as a param; `refreshRemote` loads it best-effort (a registry read
error never blanks the connection panel) and re-uses the cache a save
refreshes. When the registry path isn't known yet the label is omitted —
better than mislabelling `remote.toml`.

- render.ts: `remoteHtml(view, registryPath?)` / `renderRemote(el, view,
  registryPath?)` — show the registry path, updated tooltip + comment.
- main.ts: `refreshRemote` loads `registryConfig` best-effort; both render
  sites pass `registryConfig?.path`.
- render.test.ts: +2 cases (labels agents.toml not remote.toml; omits the
  label when no registry path is known).

Verification: `tsc --noEmit` clean · 86 vitest (2 new) · `vite build` OK.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
brettchien added a commit that referenced this pull request Aug 18, 2026
The management connection logged "oab tools published" the moment Studio got
a sessionId back from session/new — i.e. when Studio *declared* the `oab`
server, not when the agent consumed it. So an operator saw "published" while
the agent reported no oab tools, with nothing in the Activity log to explain
the gap.

The tools only appear once the agent (via the gateway) connects back to the
declared server over the reverse-MCP tunnel and lists them. Make that
observable and fix the wording:

- Declaration log: "oab tools published/republished" → "oab server
  (re-)declared (awaiting agent connect)". Declared ≠ consumed.
- Inbound::Connect: log when the agent opens the reverse-MCP tunnel to the
  oab server — the proof the declaration was consumed.
- Inbound::Message: log the reverse-MCP call; for `tools/list`, log the
  served tool count — the definitive "the agent pulled N oab tools" signal.

Now the Activity log distinguishes the two failure modes: no Connect / no
tools/list after "declared" ⇒ the gateway/agent runtime isn't tunnelling the
reverse direction (upstream); a tools/list served with 0 (or an error) ⇒ a
Studio-side issue. Behaviour is unchanged — logging only.

No local src-tauri build (its dep tree OOMs this box, as with #69); CI
build-test covers the compile. Change is additive logging using patterns
already in the file.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
brettchien added a commit that referenced this pull request Aug 18, 2026
…ng (#75)

Follow-up to #74. Fills the silent gaps a reviewer hits when the Activity log
can't explain a reverse-MCP or turn problem. Logging only, no behaviour change.

- A. tools/call outcome: log the tool name + ok / tool-error / failed(+msg),
  instead of only "a tools/call happened". A failing tool call is the usual
  troubleshooting case and was invisible.
- B. tools/list failure: detect a sidecar error reply and log its message
  instead of mislabelling it "unexpected shape" (that branch now means a
  genuinely odd shape, not an error).
- C. Inbound::Disconnect: log when the agent tears down the reverse-MCP tunnel
  to the oab server — symmetric with #74's Connect log; explains "tools
  vanished" with no error.
- D. Unsupported reverse-MCP method: log when the agent calls a method the oab
  server doesn't serve (replied -32601) — surfaces a capability mismatch.
- E. OutMsg::Cancel: log the operator Stop so Activity distinguishes an
  operator cancel from a gateway cancel or an error drop.

The tunnel-message observability is restructured into one match on the inner
method (list/call/initialize/other) carrying (level, message); `initialize`
stays unlogged (Connect already proves the agent engaged).

No local src-tauri build (dep tree OOMs this box, as with #69/#74); CI
build-test covers the compile. Additive logging using patterns already in the
file (`app.emit` + `json!`, `and_then(Value::as_*)`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant