Skip to content

feat(desktop): GUI for agent respond-to gate (owner-only / anyone / allowlist)#557

Merged
tlongwell-block merged 2 commits into
mainfrom
gui-respond-to
May 13, 2026
Merged

feat(desktop): GUI for agent respond-to gate (owner-only / anyone / allowlist)#557
tlongwell-block merged 2 commits into
mainfrom
gui-respond-to

Conversation

@tlongwell-block
Copy link
Copy Markdown
Collaborator

Summary

Adds a "Who can talk to this agent" dropdown to the Create / Edit agent dialogs in the desktop app, exposing sprout-acp's --respond-to flag to end users so they can share agents with teammates without dropping to the CLI.

Three modes:

  • Owner only (default — matches existing behaviour)
  • Anyone (fully open bot)
  • Allowlist (owner + a list of 64-char hex pubkeys)

When Allowlist is selected, a user-picker appears: search by name / NIP-05 with chip-style selection (same UX pattern as ChannelMemberInviteCard), plus a collapsible "paste pubkeys" panel for exact entry.

dropdown placement

Why

sprout-acp already supports this gate via SPROUT_ACP_RESPOND_TO / SPROUT_ACP_RESPOND_TO_ALLOWLIST env vars. Before this PR, the only way to configure it was hand-editing the agent record JSON. Now it's a first-class GUI knob.

What's in here

Commit 1 — backend + TS API plumbing (634ee16)

  • RespondTo enum + validate_respond_to_allowlist (mirrors sprout-acp/src/config.rs::validate_allowlist — 64-char hex, dedupe, lowercase, error on invalid).
  • ManagedAgentRecord / Summary and the Create/Update request structs gain respond_to + respond_to_allowlist. #[serde(default)] keeps legacy records on disk deserialising cleanly.
  • The update path merges-then-validates, so a single update can switch mode AND supply pubkeys atomically.
  • runtime.rs builds the env vars via a small helper; non-allowlist modes explicitly remove SPROUT_ACP_RESPOND_TO_ALLOWLIST so stale state can't leak across restarts.
  • build_deploy_payload forwards the fields to remote providers.
  • TS: RespondToMode type + new fields on ManagedAgent / CreateManagedAgentInput / UpdateManagedAgentInput.
  • Pure helpers parsePubkeyInput / mergeAllowlist for inline UI feedback (Rust validator remains authoritative).

Commit 2 — UI (c12e0f8)

  • New <CreateAgentRespondToField/> component used by both Create and Edit dialogs.
  • Search input uses useUserSearchQuery, chips for selected entries, collapsible paste textarea with invalid-entry feedback.
  • Create + Edit both block submit when mode = allowlist with an empty list, so the user sees a disabled button instead of a server-side config error.
  • Edit uses tri-state diff: only sends fields when they actually changed. The local allowlist is preserved across mode toggles, so users can flip away from allowlist and back without losing entries.

Validation

  • Rust: 262 cargo test pass, including new tests for validate_respond_to_allowlist, the legacy-record default, and the per-mode env builder.
  • TS unit: 9 new node tests for parsePubkeyInput + mergeAllowlist (cases: empty, whitespace, mixed-case dedupe, invalid hex, npub rejection, wrong-length rejection, ordering).
  • TS typecheck + biome check + file-size guard: all green.
  • Pre-push hooks: all green (desktop, mobile, rust, web).

Design notes / things I'd flag

  • nobody mode is intentionally not surfaced in the dropdown. It's a heartbeat-only setup with no meaningful GUI use case; we can add it later if a real one emerges.
  • npub decoding isn't supported in the paste textarea — the harness CLI takes hex only, so this matches its contract. We could decode client-side later; for now invalid entries are surfaced as a count with a clear message.
  • Owner is always implicit. The harness auto-includes the agent's owner in allowlist mode, so we don't add it to the chip list and the picker has a small note explaining this.
  • The allowlist is preserved across mode toggles in local UI state (so the user doesn't lose entries when experimenting with the dropdown), but only sent on the wire when the saved mode needs it.
  • File-size budgets: bumped runtime.rs (780→990) and types.rs (added override at 700) — both grew by the new RespondTo plumbing; flagged in the override comments as required by check-file-sizes.mjs.

Out of scope

  • Per-channel overrides (the harness has one global gate).
  • Surfacing the owner pubkey in the Edit dialog's "owner is implicit" note (would require threading owner_hex through ManagedAgent to the frontend — useful but separate).

npub1c2zwvnst5vlca8x97dn5ftfzkz0lnlnd89wqearkktedclwyq7gsvjdkte added 2 commits May 12, 2026 19:24
Adds the 'who can talk to this agent' surface to the managed-agent
backend, mirroring sprout-acp's --respond-to flag:

- owner-only (default — preserves existing behaviour)
- anyone
- allowlist (with a list of 64-char hex pubkeys; owner is implicit)

Rust:
- RespondTo enum + validate_respond_to_allowlist (mirrors
  sprout-acp/src/config.rs::validate_allowlist).
- ManagedAgentRecord/Summary + Create/Update request structs gain
  respond_to / respond_to_allowlist (#[serde(default)] for legacy
  records). The update path merges-then-validates so a single update
  can switch to allowlist AND supply pubkeys atomically.
- runtime.rs builds SPROUT_ACP_RESPOND_TO / SPROUT_ACP_RESPOND_TO_ALLOWLIST
  env vars via a new helper; non-allowlist modes remove the allowlist
  var so stale state can't leak across restarts.
- All call sites threaded with owner_hex.
- build_deploy_payload forwards the fields to remote providers.
- 262 cargo tests pass.

TS API:
- RespondToMode type + respondTo/respondToAllowlist fields on
  ManagedAgent, CreateManagedAgentInput, UpdateManagedAgentInput.
- tauri.ts forwards camelCase fields (matches Rust serde renames).
- Pure helpers (parsePubkeyInput, mergeAllowlist) for inline UI
  feedback only — the Rust validator is authoritative. 9 unit tests.
- e2eBridge mock updated to model the same field surface.

No UI changes here; the dialogs come in the next commit.

Signed-off-by: npub1c2zwvnst5vlca8x97dn5ftfzkz0lnlnd89wqearkktedclwyq7gsvjdkte <c284e64e0ba33f8e9cc5f36744ad22b09ff9fe6d395c0cf476b2f2dc7dc40791@sprout.up.railway.app>
Adds a "Who can talk to this agent" section to both Create and Edit
agent dialogs. Three modes:

- Owner only (default) — matches the harness default; no change for
  existing agents.
- Anyone — fully open bot.
- Allowlist — owner + a list of 64-char hex pubkeys.

When Allowlist is selected, a user-picker appears:
- Search by name / NIP-05 (uses useUserSearchQuery, same pattern as
  ChannelMemberInviteCard).
- Chips with X-to-remove.
- Collapsible "paste pubkeys" textarea for exact entry. Validation
  feedback is inline; invalid entries are surfaced as a count, never
  silently dropped. Hex-only — npub decoding deferred.

Submit/validation:
- Create + Edit both block submit when mode == allowlist with an empty
  list, so the user sees a disabled button instead of a server-side
  config error.
- Edit uses tri-state diff semantics consistent with the rest of the
  dialog: only sends respondTo/respondToAllowlist when they actually
  changed. The local allowlist is preserved across mode toggles so the
  user can flip away and back without losing entries.
- Create only sends the allowlist when the chosen mode needs it.

The Rust validator (validate_respond_to_allowlist) remains the source
of truth — the TS helpers exist solely for inline UX feedback.

Signed-off-by: npub1c2zwvnst5vlca8x97dn5ftfzkz0lnlnd89wqearkktedclwyq7gsvjdkte <c284e64e0ba33f8e9cc5f36744ad22b09ff9fe6d395c0cf476b2f2dc7dc40791@sprout.up.railway.app>
@tlongwell-block tlongwell-block merged commit 9c258e4 into main May 13, 2026
15 checks passed
@tlongwell-block tlongwell-block deleted the gui-respond-to branch May 13, 2026 16:48
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.

2 participants