import type { Router } from "express"; import { z } from "zod"; import { getLocalAgentContext } from "../context/localContext.js"; import { readPurpose, type SessionMetadataStore } from "../sessions/metadataStore.js"; const nameSchema = z.object({ name: z.string().trim().min(1).max(80) }).strict(); const membershipSchema = z.object({ topic_id: z.string().uuid().nullable() }).strict(); export function registerChatTopicRoutes(router: Router, sessions: SessionMetadataStore) { router.get("/topics", async (req, res) => { res.json({ topics: await sessions.topics.list(getLocalAgentContext(req)) }); }); router.post("/topics", async (req, res) => { const parsed = nameSchema.safeParse(req.body); if (!parsed.success) { res.status(400).json({ message: "主题名称须为 1 至 80 个字符" }); return; } res.status(201).json(await sessions.topics.create(getLocalAgentContext(req), parsed.data.name)); }); router.patch("/topics/:topic_id", async (req, res) => { const parsed = nameSchema.safeParse(req.body); if (!parsed.success) { res.status(400).json({ message: "主题名称须为 1 至 80 个字符" }); return; } const topic = await sessions.topics.rename(getLocalAgentContext(req), req.params.topic_id, parsed.data.name); if (!topic) { res.status(404).json({ message: "主题不存在" }); return; } res.json(topic); }); router.delete("/topics/:topic_id", async (req, res) => { // Removing a grouping never deletes conversations. Old IDs resolve as unclassified. const removed = await sessions.topics.remove(getLocalAgentContext(req), req.params.topic_id); if (!removed) { res.status(404).json({ message: "主题不存在" }); return; } res.status(204).end(); }); router.patch("/session/:session_id/topic", async (req, res) => { const parsed = membershipSchema.safeParse(req.body); if (!parsed.success) { res.status(400).json({ message: "invalid topic membership" }); return; } const context = getLocalAgentContext(req); const record = await sessions.get(context, req.params.session_id); if (!record || readPurpose(record) === "task_execution") { res.status(404).json({ message: "session not found" }); return; } if (parsed.data.topic_id && !await sessions.topics.get(context, parsed.data.topic_id)) { res.status(404).json({ message: "主题不存在" }); return; } await sessions.touch(record, { topicId: parsed.data.topic_id }); res.json({ session_id: record.sessionId, topic_id: parsed.data.topic_id }); }); }