feat(desktop): add env editor to agent settings

LA
3h ago
+576-14

What & why

Fixes #9309.

Settings → Agents has no way to edit an agent's env (host_agent_configs.env_json), even though the host service already stores, reads and applies it. So to run an agent on a second Claude account, users write the command as env CLAUDE_CONFIG_DIR=… claude …. In that form folder-trust seeding fails without any message, because resolveTrustFamily only reads the first word of the command (env). Every resume of an automation run then shows the "trust this folder" dialog.

This PR adds the env editor the issue proposes, so env can be set without wrapping the command. The approach is open to maintainer input. CONTRIBUTING asks for agreement on features first, so I'm happy to change the UI.

Trust-through-env-wrappers fix moved to #9394.

Env editor on the agent settings page (AgentDetail)

  • New Environment Variables section under Launch: key/value rows with add and remove. It uses the existing Input/Button primitives and the same save-on-blur pattern as the other fields on the page.
  • An invalid or duplicate variable name (not [A-Za-z_][A-Za-z0-9_]*, or already used by an earlier row) is marked aria-invalid and not saved. If that row was saved before, its last saved entry is kept, so a typo never drops a variable or blocks saving the other rows. Removing a row always saves. New rows with an empty name are drafts until filled in.
  • Env saves are serialized: one is in flight at a time, the latest edit is queued behind it, and each save compares against the env last sent. While a save is pending or a row has unsaved edits, the rows are the source of truth; the editor adopts the saved env only when it is idle and clean. Restore defaults remounts the editor. Text typed while a save is in flight is kept.
  • Saving the page no longer drops env. The Command field now shows only argv; env lives in its own section, and the env editor is the only thing that writes env. A Command save sends only command and args; an inline VAR=value prefix typed into Command is moved into the env editor and saved there. Before, a command containing shell syntax (&&, |, …) was saved with env: {}, which deleted whatever env was set.

New user-facing strings ("Add variable", "Remove variable", "Variable name", "Variable value" and the hint line) are translated in every locale. "Environment Variables" and "No variables set yet." reuse existing catalog entries.

Not in this PR (possible follow-up, glad to open it separately):

  • The new custom agent form doesn't have the Env editor yet. Env can be set right after the agent is created.

How I tested it

Unit tests: the seed-agent-trust.test.ts tests moved to #9394 with the trust fix.

Checks: bun run lint is clean (no warnings), bun run typecheck passes (42/42 tasks), and bun run check:i18n passes with the regenerated catalogs committed.

bun run test: everything this PR touches passes. A few suites fail locally (re-run after the review fixes) on my machine, and they fail the same way on an untouched upstream/main checkout: cli checkPlugin (4), host-service getGitStatusPartial › a sequence of batches converges…, shared recurrence timezone math › walks a BYHOUR list…. Some timeout-based tests also flaked under full-suite load and pass when run alone (agentJobs router, RendererRouter, FsWatcherManager.refreshIgnores).

End-to-end in the dev desktop app, driven over CDP from this worktree (renderer on its own DESKTOP_VITE_PORT, signed in as the seeded dev account), using real clicks and typing:

  1. Settings → Agent commands → Add agent → Custom agent…, created Claude (work) with command claude --dangerously-skip-permissions.
  2. Add variable → typed CLAUDE_CONFIG_DIR / /Users/me/.claude-work, tabbed out. Switched to another agent and back: the row is still there.
  3. Edited the Command field to claude --dangerously-skip-permissions --verbose and tabbed out. Env was kept (the case that used to be lost).
  4. Typed an invalid name 1BAD-KEY: the field is marked invalid and nothing is saved. Removing that row, then removing CLAUDE_CONFIG_DIR, saved {}.
  5. After the review fixes, each case checked against host.db:
    • typing a value right after the name save keeps the typed value;
    • a second CLAUDE_CONFIG_DIR row is flagged and doesn't overwrite;
    • renaming a saved DISABLE_AUTOUPDATER to 1BAD keeps DISABLE_AUTOUPDATER=1 saved;
    • removing another variable while invalid rows exist still saves;
    • a plain Command edit leaves env unchanged;
    • a Command save with an inline INLINE_VAR=1 prefix moves it into the editor and clears it from the field, and removing it in the editor stays removed;
    • a new valid row wins over an invalid row's saved fallback with the same name.
  6. Checked host.db after these steps: command=claude, args_json=["--dangerously-skip-permissions","--verbose"], env_json={"CLAUDE_CONFIG_DIR":"/Users/me/.claude-work"} (later {} after the removals).

Screenshots

env-editor-light env-editor-dark env-editor-validation-dark env-editor-empty-dark-full

Checklist

  • PR title follows conventional commits (type(scope): subject)
  • bun run lint and bun run typecheck pass (CI fails on lint warnings too)
  • "Allow edits from maintainers" is checked on fork PRs
  • I have signed the CLA (the bot asks on your first PR)

🤖 Generated with Claude Code

Summary by CodeRabbit

Summary

  • New Features
    • Configure environment variables for each agent, with controls to add, edit, and remove entries.
    • Inline environment variables in launch commands are moved into the agent’s environment settings when the command is edited.
    • Editing a command without inline variables preserves the configured environment.
    • Claude and Codex agents are recognized when launched through environment wrappers; trust settings reflect wrapper options that clear or unset variables.
  • Localization
    • Added translations for the environment variable editor and its guidance across supported languages.