Skip to content

Commit e720f96

Browse files
committed
fix: treat profiles as built-in agents
1 parent ff9e9d1 commit e720f96

9 files changed

Lines changed: 204 additions & 209 deletions

File tree

app.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,11 @@ func (a *App) ConnectConfiguredAgent(agentID string) error {
310310
if err != nil {
311311
return err
312312
}
313-
for _, endpoint := range cfg.Agents {
313+
endpoints, err := miyaconfig.AgentEndpoints(cfg)
314+
if err != nil {
315+
return err
316+
}
317+
for _, endpoint := range endpoints {
314318
if endpoint.ID == agentID {
315319
if !endpoint.IsEnabled() {
316320
return fmt.Errorf("agent %q is disabled", agentID)
@@ -382,7 +386,11 @@ func (a *App) ListAgentSessions() ([]agent.Session, error) {
382386
}
383387

384388
var sessions []agent.Session
385-
for _, endpoint := range cfg.Agents {
389+
endpoints, err := miyaconfig.AgentEndpoints(cfg)
390+
if err != nil {
391+
return nil, err
392+
}
393+
for _, endpoint := range endpoints {
386394
if !endpoint.IsEnabled() {
387395
continue
388396
}

app_agent_sessions_test.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package main
22

33
import (
4+
"context"
45
"path/filepath"
56
"testing"
67

78
agentsconfig "github.com/lsongdev/miya-agents/config"
89
agentsession "github.com/lsongdev/miya-agents/session"
10+
desktopagent "wails-app/internal/agent"
911
miyaconfig "wails-app/internal/config"
1012
)
1113

@@ -19,13 +21,9 @@ func TestListAgentSessionsGroupsBuiltinSessionsByBoundAgent(t *testing.T) {
1921
agentsconfig.ConfigFile = previousFile
2022
})
2123

22-
enabled := true
2324
service := miyaconfig.NewService()
2425
err := service.Save(&miyaconfig.Config{
25-
Agents: []miyaconfig.ACPAgentConfig{
26-
{ID: "miya-default", Name: "Miya Default", Enabled: &enabled, Type: "builtin", Profile: "default"},
27-
{ID: "miya-coding", Name: "Miya Coding", Enabled: &enabled, Type: "builtin", Profile: "coding"},
28-
},
26+
Agents: []miyaconfig.ACPAgentConfig{},
2927
Profiles: map[string]*miyaconfig.ProfileConfig{
3028
"default": {Provider: "openai", ModelName: "gpt-5"},
3129
"coding": {Provider: "openai", ModelName: "gpt-5-codex"},
@@ -57,10 +55,30 @@ func TestListAgentSessionsGroupsBuiltinSessionsByBoundAgent(t *testing.T) {
5755
for _, session := range sessions {
5856
byID[session.ID] = session.AgentID
5957
}
60-
if got := byID[defaultSession.ID]; got != "miya-default" {
58+
if got := byID[defaultSession.ID]; got != "default" {
6159
t.Fatalf("default session agent = %q", got)
6260
}
63-
if got := byID[codingSession.ID]; got != "miya-coding" {
61+
if got := byID[codingSession.ID]; got != "coding" {
6462
t.Fatalf("coding session agent = %q", got)
6563
}
64+
65+
app.manager = desktopagent.New(context.Background(), service.Load, nil)
66+
if err := app.ConnectConfiguredAgent("coding"); err != nil {
67+
t.Fatalf("ConnectConfiguredAgent: %v", err)
68+
}
69+
t.Cleanup(func() { _ = app.manager.Disconnect() })
70+
if _, err := app.InitializeAgent("test", "test"); err != nil {
71+
t.Fatalf("InitializeAgent: %v", err)
72+
}
73+
created, err := app.CreateSession(agentsconfig.ConfigPath)
74+
if err != nil {
75+
t.Fatalf("CreateSession: %v", err)
76+
}
77+
persisted, err := agentsession.Load(created.ID)
78+
if err != nil {
79+
t.Fatalf("load created session: %v", err)
80+
}
81+
if persisted.AgentName != "coding" {
82+
t.Fatalf("created session profile = %q, want coding", persisted.AgentName)
83+
}
6684
}

frontend/bindings/github.com/lsongdev/miya-agents/config/models.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export class ACPAgentConfig {
2424
* builtin, stdio (default), http, or sse
2525
*/
2626
"type"?: string;
27-
"profile"?: string;
2827
"command"?: string;
2928
"args"?: string[];
3029
"url"?: string;
@@ -43,14 +42,14 @@ export class ACPAgentConfig {
4342
* Creates a new ACPAgentConfig instance from a string or object.
4443
*/
4544
static createFrom($$source: any = {}): ACPAgentConfig {
46-
const $$createField6_0 = $$createType0;
47-
const $$createField8_0 = $$createType1;
45+
const $$createField5_0 = $$createType0;
46+
const $$createField7_0 = $$createType1;
4847
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
4948
if ("args" in $$parsedSource) {
50-
$$parsedSource["args"] = $$createField6_0($$parsedSource["args"]);
49+
$$parsedSource["args"] = $$createField5_0($$parsedSource["args"]);
5150
}
5251
if ("headers" in $$parsedSource) {
53-
$$parsedSource["headers"] = $$createField8_0($$parsedSource["headers"]);
52+
$$parsedSource["headers"] = $$createField7_0($$parsedSource["headers"]);
5453
}
5554
return new ACPAgentConfig($$parsedSource as Partial<ACPAgentConfig>);
5655
}

frontend/src/context/AgentContext.jsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,24 @@ export function AgentProvider({ children }) {
3939
const [error, setError] = useState(null)
4040

4141
const agents = useMemo(() => {
42-
const configured = Array.isArray(config.agents) ? config.agents : []
43-
return configured
44-
.filter((agent) => agent.enabled !== false)
45-
.map((agent) => ({
46-
...normalizeAgent(agent),
47-
model: agent.type === 'builtin' ? config.profiles?.[agent.profile]?.model || '' : '',
48-
}))
49-
.filter((agent) => agent.id && (agent.type === 'builtin' || agent.command))
42+
const profiles = Object.entries(config.profiles || {}).sort(([left], [right]) => {
43+
if (left === 'default') return -1
44+
if (right === 'default') return 1
45+
return left.localeCompare(right)
46+
})
47+
const builtinAgents = profiles.map(([id, profile]) => normalizeAgent({
48+
id,
49+
name: id,
50+
type: 'builtin',
51+
profile: id,
52+
model: profile?.model || '',
53+
}))
54+
const profileIDs = new Set(profiles.map(([id]) => id))
55+
const externalAgents = (Array.isArray(config.agents) ? config.agents : [])
56+
.filter((agent) => agent.enabled !== false && agent.type !== 'builtin')
57+
.map(normalizeAgent)
58+
.filter((agent) => agent.id && agent.command && !profileIDs.has(agent.id))
59+
return [...builtinAgents, ...externalAgents]
5060
}, [config.agents, config.profiles])
5161

5262
const selectedAgent = agents.find((a) => a.id === selectedAgentId) || agents[0] || null

frontend/src/pages/Settings.jsx

Lines changed: 11 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ function GeneralSettings() {
248248

249249
function AgentsSettings() {
250250
const { config, saveConfig, saving, error } = useMiyaConfig()
251-
const agents = Array.isArray(config.agents) ? config.agents : []
251+
const agents = (Array.isArray(config.agents) ? config.agents : []).filter((agent) => agent.type !== 'builtin')
252252
const [editing, setEditing] = useState(null)
253253
const [adding, setAdding] = useState(false)
254254
const [form, setForm] = useState({ name: '', enabled: true, command: '', args: '' })
@@ -269,7 +269,8 @@ function AgentsSettings() {
269269
const handleSave = async () => {
270270
if (!form.command.trim()) return
271271
await saveConfig((prev) => {
272-
const id = uniqueAgentId(form, prev.agents || [], editing)
272+
const reserved = Object.keys(prev.profiles || {}).map((id) => ({ id }))
273+
const id = uniqueAgentId(form, [...(prev.agents || []), ...reserved], editing)
273274
const agents = (prev.agents || []).filter((agent) => agent.id !== id)
274275
agents.push({
275276
id,
@@ -312,8 +313,8 @@ function AgentsSettings() {
312313
return (
313314
<div className="space-y-4">
314315
<SectionHeader
315-
title="Agents"
316-
description="Manage built-in profile agents and external ACP agents."
316+
title="External Agents"
317+
description="Manage ACP agents such as OpenCode. Profiles appear automatically in Chat."
317318
action={(
318319
<Button size="sm" onClick={startAdd} disabled={adding || editing !== null}>
319320
<Plus className="size-3.5 mr-1" /> Add Agent
@@ -328,21 +329,14 @@ function AgentsSettings() {
328329
<div className="min-w-0">
329330
<div className="flex items-center gap-2">
330331
<p className="font-medium text-sm">{agent.name || agent.id}</p>
331-
{agent.type === 'builtin' && <span className="rounded border px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-muted-foreground">Built in</span>}
332332
</div>
333333
<p className="text-xs text-muted-foreground font-mono truncate">
334-
{agent.type === 'builtin'
335-
? `${agent.profile || 'no profile'} / ${config.profiles?.[agent.profile]?.model || 'no model'}`
336-
: `${agent.type || 'stdio'} / ${commandFromAgent(agent)}`}
334+
{agent.type || 'stdio'} / {commandFromAgent(agent)}
337335
</p>
338336
</div>
339337
<div className="flex items-center gap-2 shrink-0 ml-2">
340-
{agent.type !== 'builtin' && (
341-
<>
342-
<Button variant="ghost" size="icon-xs" onClick={() => startEdit(agent)}><Pencil className="size-3" /></Button>
343-
<Button variant="ghost" size="icon-xs" onClick={() => handleDelete(agent.id)}><Trash2 className="size-3" /></Button>
344-
</>
345-
)}
338+
<Button variant="ghost" size="icon-xs" onClick={() => startEdit(agent)}><Pencil className="size-3" /></Button>
339+
<Button variant="ghost" size="icon-xs" onClick={() => handleDelete(agent.id)}><Trash2 className="size-3" /></Button>
346340
<Switch
347341
checked={agent.enabled !== false}
348342
onChange={(enabled) => handleToggleEnabled(agent.id, enabled)}
@@ -354,7 +348,7 @@ function AgentsSettings() {
354348
)}
355349
</div>
356350
))}
357-
{agents.length === 0 && <div className="px-4 py-8 text-center text-xs text-muted-foreground">No agents configured</div>}
351+
{agents.length === 0 && <div className="px-4 py-8 text-center text-xs text-muted-foreground">No external agents configured</div>}
358352
{adding && <div className="px-4 py-3">{editor}</div>}
359353
</div>
360354
<ConfigError error={error} />
@@ -433,50 +427,15 @@ function ProfilesSettings({ onSelectItem }) {
433427
contextWindowTokens: Number(form.contextWindowTokens) || 0,
434428
contextWarnRatio: Number(form.contextWarnRatio) || 0,
435429
}
436-
const agents = Array.isArray(prev.agents) ? [...prev.agents] : []
437-
if (editing) {
438-
const previousGeneratedName = editing === 'default' ? 'Miya Default' : editing
439-
for (let index = 0; index < agents.length; index += 1) {
440-
if (agents[index].type === 'builtin' && agents[index].profile === editing) {
441-
agents[index] = {
442-
...agents[index],
443-
profile: id,
444-
name: agents[index].name === previousGeneratedName
445-
? (id === 'default' ? 'Miya Default' : id)
446-
: agents[index].name,
447-
}
448-
}
449-
}
450-
}
451-
if (!agents.some((agent) => agent.type === 'builtin' && agent.profile === id)) {
452-
const baseId = `miya-${id}`
453-
let agentId = baseId
454-
let suffix = 2
455-
const used = new Set(agents.map((agent) => agent.id))
456-
while (used.has(agentId)) {
457-
agentId = `${baseId}-${suffix}`
458-
suffix += 1
459-
}
460-
agents.push({
461-
id: agentId,
462-
name: id === 'default' ? 'Miya Default' : id,
463-
enabled: true,
464-
type: 'builtin',
465-
profile: id,
466-
command: 'miya-agent',
467-
args: ['acp'],
468-
})
469-
}
470-
return { ...prev, profiles, agents }
430+
return { ...prev, profiles }
471431
})
472432
handleCancel()
473433
}
474434
const handleDelete = async (id) => {
475435
await saveConfig((prev) => {
476436
const profiles = { ...(prev.profiles || {}) }
477437
delete profiles[id]
478-
const agents = (prev.agents || []).filter((agent) => !(agent.type === 'builtin' && agent.profile === id))
479-
return { ...prev, profiles, agents }
438+
return { ...prev, profiles }
480439
})
481440
}
482441

0 commit comments

Comments
 (0)