summaryrefslogtreecommitdiffhomepage
path: root/packages/api/src/app.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-21 19:52:35 +0900
committerAdam Malczewski <[email protected]>2026-05-21 19:52:35 +0900
commit4ba2673557c7cfd0bc31b03e2c35ab5e1efe60e7 (patch)
treedddae8fc37a12157f4c8e840475e9e5d6203464a /packages/api/src/app.ts
parent55633c90c0d96e62153a4b6655f3f833a3b46ad4 (diff)
downloaddispatch-4ba2673557c7cfd0bc31b03e2c35ab5e1efe60e7.tar.gz
dispatch-4ba2673557c7cfd0bc31b03e2c35ab5e1efe60e7.zip
feat: tab system with per-tab agents, DB persistence, and DaisyUI tabs-lift UI
- Add tabs, messages, and settings tables to SQLite database - Backend: refactor AgentManager to manage per-tab Agent instances via Map<tabId, TabAgent> - Backend: WebSocket events tagged with tabId for multiplexing - Backend: tab CRUD routes (create, list, update, archive, messages) - Backend: persist user and assistant messages to DB during chat - Frontend: new tabStore replaces single chatStore with multi-tab reactive state - Frontend: TabBar component using DaisyUI tabs-lift style with status dots - Frontend: Settings sidebar panel for title generation model selection - Frontend: wire ChatPanel, ChatInput, Header to use tabStore - Fix HMR listener accumulation via wsClient.clearCallbacks() - Delete old single-chat chatStore (chat.svelte.ts)
Diffstat (limited to 'packages/api/src/app.ts')
-rw-r--r--packages/api/src/app.ts19
1 files changed, 13 insertions, 6 deletions
diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts
index ce26aaa..f5588a6 100644
--- a/packages/api/src/app.ts
+++ b/packages/api/src/app.ts
@@ -5,6 +5,7 @@ import { PermissionManager } from "./permission-manager.js";
import { configRoutes } from "./routes/config.js";
import { skillsRoutes } from "./routes/skills.js";
import { modelsRoutes, startWakeScheduler } from "./routes/models.js";
+import { tabsRoutes } from "./routes/tabs.js";
export const permissionManager = new PermissionManager();
export const agentManager = new AgentManager(permissionManager);
@@ -17,7 +18,7 @@ app.use(
origin: "http://localhost:5173",
credentials: true,
allowHeaders: ["Content-Type", "Authorization"],
- allowMethods: ["GET", "POST", "OPTIONS"],
+ allowMethods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"],
}),
);
@@ -29,19 +30,24 @@ app.get("/status", (c) => {
return c.json({
status: agentManager.getStatus(),
messageCount: agentManager.getMessageCount(),
+ statuses: agentManager.getAllStatuses(),
});
});
app.post("/chat", async (c) => {
- const body = await c.req.json<{ message?: unknown; keyId?: unknown; modelId?: unknown; reasoningEffort?: unknown }>();
- const message = body.message;
+ const body = await c.req.json<{ tabId?: unknown; message?: unknown; keyId?: unknown; modelId?: unknown; reasoningEffort?: unknown }>();
+ const { tabId, message } = body;
+
+ if (typeof tabId !== "string" || tabId.trim() === "") {
+ return c.json({ error: "tabId must be a non-empty string" }, 400);
+ }
if (typeof message !== "string" || message.trim() === "") {
return c.json({ error: "message must be a non-empty string" }, 400);
}
- if (agentManager.getStatus() === "running") {
- return c.json({ error: "agent is already running" }, 409);
+ if (agentManager.getTabStatus(tabId) === "running") {
+ return c.json({ error: "agent is already running for this tab" }, 409);
}
const keyId = typeof body.keyId === "string" ? body.keyId : undefined;
@@ -52,7 +58,7 @@ app.post("/chat", async (c) => {
: undefined;
// Non-blocking — let the agent run in the background
- agentManager.processMessage(message, keyId, modelId, reasoningEffort).catch(console.error);
+ agentManager.processMessage(tabId, message, keyId, modelId, reasoningEffort).catch(console.error);
return c.json({ status: "ok" });
});
@@ -60,6 +66,7 @@ app.post("/chat", async (c) => {
app.route("/config", configRoutes);
app.route("/skills", skillsRoutes);
app.route("/models", modelsRoutes);
+app.route("/tabs", tabsRoutes);
// Start the wake scheduler on boot (restores persisted schedule)
startWakeScheduler();