diff options
| author | Adam Malczewski <[email protected]> | 2026-08-20 19:36:12 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-08-20 19:36:12 +0900 |
| commit | fb7eb3105e1fbbdefdad9348f9132815b5bbe52d (patch) | |
| tree | deb95e97a917424ea67470b168b91349a41d1c08 /src/app/App.svelte | |
| parent | 63588c3c69b32d180dc78a2cc187cc8aa22ad7e0 (diff) | |
| download | dispatch-web-fb7eb3105e1fbbdefdad9348f9132815b5bbe52d.tar.gz dispatch-web-fb7eb3105e1fbbdefdad9348f9132815b5bbe52d.zip | |
feat(named-agents): sidebar view, synthetic provider, auto-save, snapshot semantics
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.
Diffstat (limited to 'src/app/App.svelte')
| -rw-r--r-- | src/app/App.svelte | 35 |
1 files changed, 35 insertions, 0 deletions
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 @@ -45,6 +45,12 @@ 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, LspStatusView, @@ -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<WarmFeedback | null> { 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} /> <!-- Keyed on the workspace conversation (active tab OR draft) so the inputs re-mount per conversation — incl. switching between drafts — and can't @@ -775,6 +801,15 @@ /> {/key} </div> + {:else if kind === "named-agents"} + <!-- Named Agents: a global, collapsible sidebar view of curated presets. + The store owns the list (authoritative for the model selector + agent + tooling); the view drives CRUD via injected ports (auto-save). --> + <NamedAgentsView + agents={store.namedAgents} + models={store.models} + ports={namedAgentPorts} + /> {:else if kind === "lsp"} <!-- Re-mount per conversation (incl. draft) so the loaded server list is isolated. --> {#key store.currentConversationId} |
