summaryrefslogtreecommitdiffhomepage
path: root/src/app/App.svelte
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-08-20 19:36:12 +0900
committerAdam Malczewski <[email protected]>2026-08-20 19:36:12 +0900
commitfb7eb3105e1fbbdefdad9348f9132815b5bbe52d (patch)
treedeb95e97a917424ea67470b168b91349a41d1c08 /src/app/App.svelte
parent63588c3c69b32d180dc78a2cc187cc8aa22ad7e0 (diff)
downloaddispatch-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.svelte35
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}