Skip to content

Named agent profiles: save a provider+model+effort+permission combo and apply it in one click - #283

Merged
currentbits merged 11 commits into
mainfrom
coder/named-agent-profiles-provider-model-effo-8e360e
Aug 17, 2026
Merged

currentbits merged 11 commits into
mainfrom
coder/named-agent-profiles-provider-model-effo-8e360e

Conversation

@currentbits

Copy link
Copy Markdown
Owner

Closes #190.

What this adds

A named agent profile is a saved (provider, model, reasoning effort, permission mode) combination — "cheap scout" (haiku + read-only) versus "deep worker" (opus + accept-edits). Today those four settings are set one at a time, per thread, every time. This makes the combination a thing you save once and pick.

Three places use them:

  • Settings → Agent profiles — add, edit, delete, reorder. Each row shows what it resolves to (Claude Code · Haiku 4.5 · Low · Plan mode).
  • Composer model picker — profiles appear above the provider list and apply in one click. Keyboard reachable; hidden entirely when you have none, so the picker is unchanged for anyone who never makes one.
  • Best of N — fan out across profiles. Each profile forks a thread with its provider and model, then gets its effort and permission set before its run starts.

Why it is built this way

No new IPC channel and no new per-thread state. settings:get/settings:set already carry the whole AppSettings patch, and applying a profile is exactly the three calls the composer already made. A profile stores a combination; it does not become a fourth kind of thread state that everything else has to learn about.

Call order is load-bearing. setProvider clears reasoning effort on a harness switch, so effort must be set after it and permission after that. Both the picker (pickProfile) and the Best of N fan-out follow that order, and both say why in a comment — this is silent when you get it wrong.

Validation is strict on the way in, lenient on the way out. settings:set throws on the first bad field, matching how mcpServers and the other settings keys behave. Reading a corrupt store instead drops the bad entries and keeps loading, so one malformed profile on disk cannot make the app unopenable.

A profile on a missing CLI stays visible. It renders disabled with a "not installed" reason in both the picker and Best of N, and is dropped from a fan-out plan rather than firing a switch to a CLI that is not there. It stays editable in Settings — uninstalling a CLI should not silently eat your configuration.

Cleanups this surfaced

  • The Settings list and the picker rows had each grown their own profileSummary, one showing a raw model id and the other the ModelInfo label, so the same profile described itself two different ways in two places. Collapsed to one exported, tested function.
  • buildBestOfNEntries never read its currentProviderId argument: the guard it fed, id === currentProviderId || installed.has(id), sat behind an installed.has(id) check that already made it always true. Removing it left buildBestOfNPlan a wrapper with no caller in src/, kept alive only by its own tests — deleted, with its coverage moved onto buildBestOfNEntries plus a regression guard that a plain provider row still carries no profile fields and keeps inheriting model and permission from the source thread.

Reviewer notes

  • setPermissionMode and setReasoningEffort in useCoder.ts take an optional threadId (the setArchived precedent) so they can target a fork. They previously returned early when selection moved mid-await, which would have skipped the thread-list update for a fork that is never selected; they now always update the list and guard only setDetail. That is a small behaviour change on the existing path: setError(null) now also runs when selection has moved.
  • The picker inserts profile rows above a provider list whose keyboard navigation is index-based. The offset is applied at all six sites — detail pane, open-reset, arrow keys, Enter, mouse-enter, and backing out of a drill-down.

Testing

core 38/38, renderer 776/776, electron 891/892, memory 122/122.

The single electron failure is wireClient × webBridge over a real socket (reconnect backoff), which already fails on main and is tracked as #233.

New coverage: electron/test/agent-profiles.test.js (round-trip, healing, every rejection case), test/profileRows.test.ts, test/bestOfNComposer.test.tsx, test/setterThreadId.test.tsx, and extensions to test/bestOfN.test.ts and test/composer.test.tsx.

🤖 Generated with Claude Code

  • PR created by the Claude Code agent

Willem and others added 11 commits August 17, 2026 14:47
Shared contract for issue #190 so the store, Settings UI and composer can be
built against one definition. devCoder's settings mock stores/echoes it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… Usage and cost analytics view (per provider/model, 7/30/90-d
Normalize heals junk entry-by-entry on load; setSettings validates
strictly (same idiom as mcpServers). No new IPC channel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Issue #190. Settings now lists saved AgentProfile rows (provider, model,
effort, permission mode) and persists the list through onSaveSettings.
App passes the providers catalogue so the form can offer modelInfo and
per-provider efforts without filtering uninstalled CLIs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…into coder/named-agent-profiles-provider-model-effo-8e360e
Saved profiles (provider + model + effort + permission) show at the
top of the picker and apply with the existing three thread calls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…into coder/named-agent-profiles-provider-model-effo-8e360e
Both workers wrote their own. The picker's showed a raw model id, Settings'
showed the ModelInfo label — the same profile described itself two ways in two
places. Keep the exported, tested one, teach it the provider registry, and
delete the copy in SettingsModal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…into coder/fork-named-agent-profiles-provider-model-387629
Profile rows sit above the existing provider checkboxes and stay hidden
when none are saved. A profile fork uses provider+model, then sets
effort and permission on that fork before the run. Provider rows still
inherit model/permission from the source thread.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
buildBestOfNEntries never read currentProviderId — the guard it fed
(id === current || installed.has(id)) sat behind an installed check that
already made it always true. Removing it left buildBestOfNPlan as a wrapper
with no caller in src/, kept alive only by its own tests, so that goes too.
Its coverage moves onto buildBestOfNEntries, including a regression guard
that a provider row still carries no profile fields.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@currentbits

Copy link
Copy Markdown
Owner Author

Diff noise, not part of this PR: the local main is one commit ahead of origin/main — 669c529, the usage-and-cost-analytics worktree merge, which was merged locally but never pushed. This branch picked it up as an ancestor, so GitHub currently shows it as part of this PR: 34 files instead of 21.

The 14 files that are NOT this work: src/usage.ts, src/components/UsageView.tsx, src/components/UsageView.module.css, src/components/Sidebar.tsx, src/wireClient.ts, electron/ipc.js, electron/preload.js, electron/runner.js, electron/workflow.js, docs/ARCHITECTURE.md, test/usage.test.ts, test/usageView.test.tsx, test/appWiring.test.tsx, test/support/fakeCoder.ts.

Pushing main makes this correct itself — GitHub recomputes the diff and it drops to the 21 files described above. Until then, git diff main...HEAD locally shows the real scope.

@currentbits
currentbits merged commit e899375 into main Aug 17, 2026
1 check failed
@currentbits
currentbits deleted the coder/named-agent-profiles-provider-model-effo-8e360e branch October 3, 2026 16:07
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.

Named agent profiles (provider + model + effort + permission presets)

1 participant