Repository navigation
Named agent profiles: save a provider+model+effort+permission combo and apply it in one click - #283
Conversation
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>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Diff noise, not part of this PR: the local The 14 files that are NOT this work: Pushing |
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:
Claude Code · Haiku 4.5 · Low · Plan mode).Why it is built this way
No new IPC channel and no new per-thread state.
settings:get/settings:setalready carry the wholeAppSettingspatch, 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.
setProviderclears 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:setthrows on the first bad field, matching howmcpServersand 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
profileSummary, one showing a raw model id and the other theModelInfolabel, so the same profile described itself two different ways in two places. Collapsed to one exported, tested function.buildBestOfNEntriesnever read itscurrentProviderIdargument: the guard it fed,id === currentProviderId || installed.has(id), sat behind aninstalled.has(id)check that already made it always true. Removing it leftbuildBestOfNPlana wrapper with no caller insrc/, kept alive only by its own tests — deleted, with its coverage moved ontobuildBestOfNEntriesplus 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
setPermissionModeandsetReasoningEffortinuseCoder.tstake an optionalthreadId(thesetArchivedprecedent) 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 onlysetDetail. That is a small behaviour change on the existing path:setError(null)now also runs when selection has moved.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 onmainand 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 totest/bestOfN.test.tsandtest/composer.test.tsx.🤖 Generated with Claude Code