From fb7eb3105e1fbbdefdad9348f9132815b5bbe52d Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Thu, 20 Aug 2026 19:36:12 +0900 Subject: feat(named-agents): sidebar view, synthetic provider, auto-save, snapshot semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the frontend of the Named Agents feature (plan §5-§9): - New features/named-agents module: pure logic (types re-exported from @dispatch/wire + transport-contract, validation, provider/model options, snapshot helpers, network normalizers, auto-save status model) + an injected auto-save controller (name debounce, immediate provider/model save, monotonic stale-response guard) + the collapsible sidebar card view. - Store: /named-agents CRUD + boot seed; selectModel(model, snapshot?) persists the raw model WITH the namedAgent snapshot; draft promotion carries it; refreshModel restores ModelResponse.namedAgent. - Chat store setNamedAgent (sends namedAgent on chat.send); Tab.snapshot + setSnapshot (retained across rename/remove, persisted with tabs). - Model selector: synthetic Named Agents provider ALWAYS first; selecting one resolves to the raw model + records a snapshot; a retained snapshot of a renamed/removed agent still displays as unavailable (plan §7). - Composition root wires the view (viewKinds + loaded-modules manifest) + feeds the selector. Tests: logic + controller + selector + app-store CRUD (1246 pass), typecheck 0, biome clean (only pre-existing config infos pending a future migrate), build OK. --- src/app/App.svelte | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'src/app/App.svelte') diff --git a/src/app/App.svelte b/src/app/App.svelte index e50648a..9436648 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -44,6 +44,12 @@ import TodoList from "../features/surface-host/ui/TodoList.svelte"; import { manifest as tabsManifest, TabList } from "../features/tabs"; import { manifest as viewsManifest, ViewSidebar } from "../features/views"; + import { + manifest as namedAgentsManifest, + NamedAgentsView, + type NamedAgentInput, + type NamedAgentSelectionSnapshot, + } from "../features/named-agents"; import { CwdField, type CwdSaveResult, @@ -119,6 +125,7 @@ const viewKinds = [ { id: "tabs", label: "Tabs" }, { id: "model", label: "Model" }, + { id: "named-agents", label: "Named Agents" }, { id: "lsp", label: "Language Servers" }, { id: "mcp", label: "MCP Servers" }, { id: "extensions", label: "Extensions" }, @@ -167,6 +174,7 @@ heartbeatManifest, concurrencyManifest, visionManifest, + namedAgentsManifest, ].map((m) => [m.name, m.description] as const); // Smart-scroll: keep the transcript pinned to the bottom while it streams, @@ -310,6 +318,21 @@ store.selectModel(model); } + // A Named Agent selected in the model selector: resolve it to its raw model + // AND record the snapshot on the active conversation (plan §6/§7). The store + // persists the raw model to `/conversations/:id/model` + retains the snapshot + // on the tab. + function handleSelectNamedAgent(snapshot: NamedAgentSelectionSnapshot) { + store.selectModel(snapshot.model, snapshot); + } + + // Adapt the store's Named Agent CRUD to the named-agents feature's ports. + const namedAgentPorts = { + create: (input: NamedAgentInput) => store.createNamedAgent(input), + update: (id: string, input: NamedAgentInput) => store.updateNamedAgent(id, input), + remove: (id: string) => store.deleteNamedAgent(id), + }; + // Adapt the store's WarmResult to the cache-warming feature's WarmNow port. async function warmNow(): Promise { const result = await store.warmNow(); @@ -754,6 +777,9 @@ selected={store.activeModel} onSelect={handleSelectModel} modelInfo={store.modelInfo} + namedAgents={store.namedAgents} + namedAgentSnapshot={store.namedAgentSnapshot} + onSelectNamedAgent={handleSelectNamedAgent} /> + {:else if kind === "lsp"} {#key store.currentConversationId} -- cgit v1.2.3