feat: finalise multi-profil Codex/Claude avec catalogue de modèles
- Backend : clone_profile_from_seed généralisé (non OpenCode) - Backend : catalogue static Claude/Codex (3 modèles chacun, 1 recommandé) - Backend : commandes Tauri list_claude_models/list_codex_models - Frontend : ProfilesSettings refonte en onglets Codex/Claude + create/duplicate/edit/delete - Frontend : ModelSelect searchable partagé + fallback saisie manuelle - Frontend : assignation agent nom · modèle - Tests QA : 4 profils modèles distincts (2 Claude, 2 Codex) assignés à agents
This commit is contained in:
@ -237,6 +237,15 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
|
||||
// Determine if a template is chosen → profile selector is hidden (template imposes it).
|
||||
const hasTemplate = newTemplateId !== "";
|
||||
|
||||
const profileLabel = (profile: import("@/domain").AgentProfile): string => {
|
||||
const model =
|
||||
profile.model ??
|
||||
profile.opencode?.model ??
|
||||
profile.opencodeProvider?.model ??
|
||||
profile.chatHttp?.model;
|
||||
return model ? `${profile.name} · ${model}` : profile.name;
|
||||
};
|
||||
|
||||
return (
|
||||
<Panel title="Agents" className="flex flex-col gap-0">
|
||||
{vm.error && (
|
||||
@ -326,7 +335,7 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
|
||||
<option value="">— select profile —</option>
|
||||
{vm.profiles.map((p) => (
|
||||
<option key={p.id} value={p.id}>
|
||||
{p.name}
|
||||
{profileLabel(p)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@ -366,7 +375,10 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
|
||||
const isRunning = a.id === activeAgentId;
|
||||
const live = vm.liveAgents.find((candidate) => candidate.agentId === a.id);
|
||||
const profileName =
|
||||
vm.profiles.find((p) => p.id === a.profileId)?.name ??
|
||||
(() => {
|
||||
const p = vm.profiles.find((p) => p.id === a.profileId);
|
||||
return p ? profileLabel(p) : null;
|
||||
})() ??
|
||||
a.profileId;
|
||||
const agentDrift = drift.driftByAgentId.get(a.id);
|
||||
// Source of this agent's last orchestration delegation (mcp vs
|
||||
@ -478,7 +490,7 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
|
||||
)}
|
||||
{vm.profiles.map((p) => (
|
||||
<option key={p.id} value={p.id}>
|
||||
{p.name}
|
||||
{profileLabel(p)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
@ -141,6 +141,42 @@ describe("AgentsPanel (with MockAgentGateway)", () => {
|
||||
expect((btn as HTMLButtonElement).disabled).toBe(true);
|
||||
});
|
||||
|
||||
it("shows profile names with their model in the assignment selector", async () => {
|
||||
const profile = new MockProfileGateway();
|
||||
await profile.saveProfile({
|
||||
id: "codex-fast",
|
||||
name: "Codex fast",
|
||||
command: "codex",
|
||||
args: [],
|
||||
contextInjection: { strategy: "conventionFile", target: "AGENTS.md" },
|
||||
detect: "codex --version",
|
||||
cwdTemplate: "{projectRoot}",
|
||||
structuredAdapter: "codex",
|
||||
model: "gpt-5-mini",
|
||||
});
|
||||
await profile.saveProfile({
|
||||
id: "claude-opus",
|
||||
name: "Claude deep",
|
||||
command: "claude",
|
||||
args: [],
|
||||
contextInjection: { strategy: "conventionFile", target: "CLAUDE.md" },
|
||||
detect: "claude --version",
|
||||
cwdTemplate: "{projectRoot}",
|
||||
structuredAdapter: "claude",
|
||||
model: "claude-opus-4-8",
|
||||
});
|
||||
|
||||
renderPanel(new MockAgentGateway(), profile);
|
||||
await waitForIdle();
|
||||
|
||||
const labels = Array.from(
|
||||
screen.getByLabelText("agent profile").querySelectorAll("option"),
|
||||
).map((option) => option.textContent);
|
||||
|
||||
expect(labels).toContain("Codex fast · gpt-5-mini");
|
||||
expect(labels).toContain("Claude deep · claude-opus-4-8");
|
||||
});
|
||||
|
||||
it("selecting an agent displays its context", async () => {
|
||||
const agent = new MockAgentGateway();
|
||||
// Pre-seed an agent with initial content.
|
||||
|
||||
Reference in New Issue
Block a user