import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import test from "node:test"; const root = path.resolve(import.meta.dirname, ".."); const read = (...parts) => fs.readFileSync(path.join(root, ...parts), "utf8"); const sessionSource = read("client", "src", "sections", "superAgent", "utils", "session.js"); const sidebarHook = read("client", "src", "sections", "superAgent", "hooks", "useSuperAgentSidebar.js"); const chatHook = read("client", "src", "sections", "superAgent", "hooks", "useSuperAgentChat.js"); const actions = read("client", "src", "sections", "superAgent", "actions", "api.js"); const router = read("client", "src", "sections", "superAgent", "router.js"); const constants = read("client", "src", "sections", "superAgent", "constants.js"); function loadSessionUtils() { const transformed = sessionSource .replace(/^import[\s\S]*?from\s+"[^"]+";\s*/gm, "") .replace(/export const ([A-Za-z0-9_]+) =/g, "const $1 =") + "\nconst DEFAULT_TASK_FOLDER_ID = '__default__';\nconst DEFAULT_TASK_FOLDER_NAME = '默认任务集';\nreturn { buildSidebarTreeFromAgentResources, normalizeSidebarSession, normalizeTaskRealmForDisplay };"; return new Function(transformed)(); } const { buildSidebarTreeFromAgentResources, normalizeSidebarSession, normalizeTaskRealmForDisplay, } = loadSessionUtils(); test("chat route remains /chat", () => { assert.match(router, /path: "\/chat"/); }); test("superAgent storage keys are versioned and legacy ids are cleared", () => { assert.match(constants, /superAgent_v2_active_conversation_id/); assert.match(constants, /LEGACY_SUPER_AGENT_STORAGE_KEYS/); assert.match(sessionSource, /ensureVersionedStorage/); assert.match(sessionSource, /removeItem\(key\)/); }); test("sidebar session keeps conversation id and thread id separate", () => { const session = normalizeSidebarSession({ id: "11111111-1111-4111-8111-111111111111", name: "采购任务", type: "task", taskset_id: "22222222-2222-4222-8222-222222222222", thread_id: "33333333-3333-4333-8333-333333333333", }, "task"); assert.equal(session.conversationId, "11111111-1111-4111-8111-111111111111"); assert.equal(session.threadId, "33333333-3333-4333-8333-333333333333"); assert.equal(session.tasksetId, "22222222-2222-4222-8222-222222222222"); }); test("tasksets become folders and conversations stay flat", () => { const tree = buildSidebarTreeFromAgentResources( [{ id: "ts-1", name: "2026 采购" }], [ { id: "c-1", name: "任务A", type: "task", taskset_id: "ts-1", thread_id: "th-1" }, { id: "c-2", name: "闲聊", type: "conversation", taskset_id: null, thread_id: "th-2" }, ], ); const taskRealm = tree.realms.find((item) => item.realm === "task"); const chatRealm = tree.realms.find((item) => item.realm === "conversation"); assert.equal(taskRealm.folders[0].name, "2026 采购"); assert.equal(taskRealm.folders[0].sessions[0].id, "c-1"); assert.equal(chatRealm.sessions[0].id, "c-2"); assert.equal(chatRealm.folders.length, 0); }); test("sidebar and CRUD use new resource paths and ids", () => { assert.match(sidebarHook, /listAllTasksets/); assert.match(sidebarHook, /listAllConversations/); assert.match(actions, /params: \{ tasksetId: folderId \}/); assert.match(actions, /params: \{ conversationId: sessionId \}/); assert.match(chatHook, /thread_id/); assert.match(chatHook, /taskset_id/); assert.match(chatHook, /activeConversationId/); }); test("first real default taskset binds to the fixed sidebar slot", () => { const tree = buildSidebarTreeFromAgentResources( [{ id: "ts-default", name: "默认任务集" }], [{ id: "c-1", name: "任务A", type: "task", taskset_id: "ts-default", thread_id: "th-1" }], ); const displayed = normalizeTaskRealmForDisplay(tree.realms.find((item) => item.realm === "task")); assert.equal(displayed.folders.length, 1); assert.equal(displayed.folders[0].name, "默认任务集"); assert.equal(displayed.folders[0].id, "ts-default"); assert.equal(displayed.folders[0].isVirtualDefault, true); assert.equal(displayed.folders[0].sessions[0].id, "c-1"); }); test("empty tasksets still show one placeholder default taskset", () => { const tree = buildSidebarTreeFromAgentResources([], []); const displayed = normalizeTaskRealmForDisplay(tree.realms.find((item) => item.realm === "task")); assert.equal(displayed.folders.length, 1); assert.equal(displayed.folders[0].id, "__default__"); assert.equal(displayed.folders[0].name, "默认任务集"); assert.equal(displayed.folders[0].isVirtualDefault, true); }); test("custom tasksets stay beside the bound default taskset", () => { const tree = buildSidebarTreeFromAgentResources( [ { id: "ts-default", name: "默认任务集" }, { id: "ts-2", name: "2026 采购" }, ], [ { id: "c-1", name: "任务A", type: "task", taskset_id: "ts-default", thread_id: "th-1" }, { id: "c-2", name: "任务B", type: "task", taskset_id: "ts-2", thread_id: "th-2" }, ], ); const displayed = normalizeTaskRealmForDisplay(tree.realms.find((item) => item.realm === "task")); assert.equal(displayed.folders.map((folder) => folder.name).join(","), "默认任务集,2026 采购"); assert.equal(displayed.folders[0].id, "ts-default"); assert.equal(displayed.folders[1].id, "ts-2"); });