dsh-sessions-manager 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.js", "../src/zstd-frame.js"],
4
+ "sourcesContent": ["// dsh-sessions-manager \u2014 host half.\n//\n// Serves /archived-sessions/* JSON routes (list / restore / restore-many /\n// delete / delete-many / sessions / workspaces / move) over the host\n// `webServer`. The browser Settings sections (\"\u5F52\u6863\u4F1A\u8BDD\" & \"\u79FB\u52A8\u4F1A\u8BDD\") talk to\n// these. Reads/writes the durable workspace archive set\n// (workspaceRegistry + storageDomain), folds titles/dates/workspace tags from\n// session persistence, physically removes a session's log file on delete, and\n// relocates a conversation (session) between workspaces on move.\nimport { mkdir, realpath, rename, stat, unlink, writeFile } from 'node:fs/promises'\nimport { basename, dirname, isAbsolute, join } from 'node:path'\nimport { readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { rewriteFrame0Cwd } from './zstd-frame.js'\n\nexport const name = 'dsh-sessions-manager'\nexport const inject = ['webServer', 'workspaceRegistry', 'sessionPersistence', 'sessionQuery', 'storageDomain']\n\nconst MAX_TITLE = 80\n// Recycle bin (\u56DE\u6536\u7AD9): normal deletes land here instead of being erased.\nconst TRASH_DIR = process.env.DSH_SESSIONS_MANAGER_TRASH_DIR || join(homedir(), '.dsh', 'sessions-manager-trash')\nconst TRASH_INDEX = join(TRASH_DIR, 'index.json')\nconst TRASH_SCHEMA_VERSION = 2\nconst DEFAULT_TRASH_SETTINGS = Object.freeze({ retentionDays: 0 })\n// -- per-session detail aggregation (v2.0: \u53D6 Zephyr-vibe buildDetails \u7CBE\u534E) --\n// \u8BC6\u522B\u201C\u641C\u7D22/\u6293\u53D6\u201D\u7C7B\u5DE5\u5177\uFF0C\u7528\u6765\u6536\u96C6 fetch \u8BB0\u5F55\u3002\nconst FETCH_TOOL_RE = /search|fetch|download|browse/i\nconst MAX_FETCHES = 12 // fetch \u8BB0\u5F55\u4E0A\u9650\uFF08\u9632\u54CD\u5E94\u8FC7\u5927\uFF09\nconst MAX_FILES = 20 // write/edit \u6587\u4EF6\u5217\u8868\u4E0A\u9650\n\nfunction json(res, value, status = 200) {\n res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' })\n res.end(JSON.stringify(value))\n}\n\nfunction errorStatus(error) {\n return error && Number.isInteger(error.status) ? error.status : 500\n}\n\nasync function readJsonBody(req) {\n const chunks = []\n let total = 0\n for await (const chunk of req) {\n chunks.push(chunk)\n total += chunk.length\n if (total > 1 << 20) return null\n }\n try {\n return JSON.parse(Buffer.concat(chunks).toString('utf8'))\n } catch {\n return null\n }\n}\n\nfunction parseIds(body) {\n const raw = body && body.sessionIds\n if (!Array.isArray(raw)) return null\n const ids = []\n for (const v of raw) if (typeof v === 'string' && isSafeSessionId(v)) ids.push(v)\n return ids\n}\n\nfunction isSafeSessionId(value) {\n return typeof value === 'string' && value.length > 0 && value.length <= 200 && !/[\\\\/\\0]/.test(value) && value !== '.' && value !== '..'\n}\n\nfunction requireSessionId(value) {\n if (!isSafeSessionId(value)) {\n const error = new Error('\u65E0\u6548\u7684 sessionId')\n error.status = 400\n throw error\n }\n return value\n}\n\n// Best-effort: figure out which conversation is the host's *currently active*\n// one. DSH's in-memory session store (ctx.sessions) keeps EVERY instantiated\n// session alive even after you switch away in the UI, so \"is it in\n// ctx.sessions\" is NOT the same as \"is it the active conversation\". We probe a\n// few known accessors for the active id; if none is available we return null\n// and callers should treat the session as movable (the move path is\n// crash-safe via backup+rollback and re-syncs the live object afterwards).\nfunction getActiveSessionId(context) {\n try {\n const a = context.get('activeSession')\n if (a != null) return (a && a.id != null) ? a.id : (typeof a === 'string' ? a : null)\n } catch (e) { /* no such key */ }\n try {\n const c = context.get('currentSession')\n if (c != null) return (c && c.id != null) ? c.id : (typeof c === 'string' ? c : null)\n } catch (e) { /* no such key */ }\n try {\n const store = context.get('sessions')\n if (store && store.active && store.active.id != null) return store.active.id\n } catch (e) { /* no such key */ }\n return null\n}\n\nfunction foldTitle(events) {\n let found = null\n let firstUser = null\n for (const ev of events) {\n if (ev.type === 'session/title' && ev.data && typeof ev.data.title === 'string' && ev.data.title.length) {\n found = ev.data.title\n }\n if (firstUser === null && ev.type === 'user/message' && ev.data && Array.isArray(ev.data.content)) {\n const txt = ev.data.content.filter((b) => b && b.type === 'text').map((b) => b.text).filter(Boolean).join(' ').trim()\n if (txt) firstUser = txt\n }\n }\n return found || firstUser || null\n}\n\nexport function apply(ctx) {\n const w = ctx.workspaceRegistry\n const sp = ctx.sessionPersistence\n const sq = ctx.sessionQuery\n const dom = () => ctx.storageDomain.get('workspace')\n const authorityTitleCache = new Map()\n let authorityTitlesLoaded = false\n\n async function archivedState() {\n const d = dom()\n if (!d) throw new Error('workspace domain is not open')\n return d.global.get()\n }\n\n async function writeArchived(nextIds) {\n const d = dom()\n if (!d) throw new Error('workspace domain is not open')\n const cur = d.global.get()\n const next = Object.assign({}, cur, { archivedSessionIds: nextIds })\n await d.global.set(next)\n // Keep the registry's in-memory cache in sync so the live sidebar refreshes.\n if (w && 'state' in w) { try { w.state = next } catch (e) { /* best-effort */ } }\n return next\n }\n\n let archiveMutation = Promise.resolve()\n function mutateArchived(mutator) {\n const operation = archiveMutation.then(async () => {\n const state = await archivedState()\n const list = (state.archivedSessionIds || []).map(String)\n const result = await mutator(list)\n if (result.next) await writeArchived(result.next)\n return result.value\n })\n archiveMutation = operation.catch(() => {})\n return operation\n }\n\n let wsByPath = {}\n\n async function resolveOne(id) {\n let title = null, createdAt = null, cwd = null\n try {\n const o = await sq.readTitleSnapshot(id)\n if (o) {\n if (o.title && o.title.title) title = String(o.title.title)\n if (o.session) { cwd = o.session.cwd || null; createdAt = o.session.createdAt || null }\n }\n } catch (e) { /* fall back to raw log */ }\n if (!title || !cwd) {\n try {\n const r = await sp.readFrom(id, 0)\n if (r.meta) {\n if (!cwd) cwd = r.meta.cwd || null\n if (!createdAt) createdAt = r.meta.createdAt || null\n }\n if (!title && Array.isArray(r.events)) title = foldTitle(r.events)\n } catch (e2) { /* keep what we have */ }\n }\n const ws = cwd ? wsByPath[cwd] : undefined\n const workspaceGone = !!(cwd && !ws)\n const display = title ? (String(title).length > MAX_TITLE ? String(title).slice(0, MAX_TITLE) + '\u2026' : String(title)) : null\n return {\n sessionId: id,\n title: display,\n createdAt: createdAt || null,\n workspacePath: cwd || null,\n workspaceTitle: (ws && ws.title) ? ws.title : null,\n workspaceGone: workspaceGone ? true : false,\n hasWorkspace: !!cwd,\n }\n }\n\n // Restore (unarchive) one session; throws on failure.\n async function restoreOne(sid) {\n requireSessionId(sid)\n return mutateArchived((list) => list.includes(sid)\n ? { next: list.filter((x) => x !== sid), value: { ok: true, restored: true } }\n : { next: null, value: { ok: true, restored: false } })\n }\n\n // ---- Recycle bin (\u56DE\u6536\u7AD9) helpers ----------------------------------------\n let trashMutation = Promise.resolve()\n function normalizeTrashStore(raw) {\n if (Array.isArray(raw)) return { schemaVersion: TRASH_SCHEMA_VERSION, settings: { ...DEFAULT_TRASH_SETTINGS }, items: raw, purgedSessionIds: [] }\n const settings = raw && typeof raw.settings === 'object' ? raw.settings : {}\n const retentionDays = Number.isInteger(settings.retentionDays) && settings.retentionDays >= 0 ? settings.retentionDays : 0\n return {\n schemaVersion: TRASH_SCHEMA_VERSION,\n settings: { retentionDays },\n items: raw && Array.isArray(raw.items) ? raw.items : [],\n purgedSessionIds: raw && Array.isArray(raw.purgedSessionIds) ? [...new Set(raw.purgedSessionIds.filter(isSafeSessionId).map(String))] : [],\n }\n }\n async function readTrashStore() {\n try { return normalizeTrashStore(JSON.parse(readFileSync(TRASH_INDEX, 'utf8'))) } catch (e) { return normalizeTrashStore(null) }\n }\n async function readTrash() { return (await readTrashStore()).items }\n async function writeTrashStore(store) {\n await mkdir(TRASH_DIR, { recursive: true })\n const tmp = join(TRASH_DIR, `.index-${process.pid}-${Date.now()}.tmp`)\n await writeFile(tmp, JSON.stringify(normalizeTrashStore(store), null, 2), { encoding: 'utf8', mode: 0o600 })\n await rename(tmp, TRASH_INDEX)\n }\n function mutateTrash(mutator) {\n const operation = trashMutation.then(async () => {\n const store = await readTrashStore()\n const result = await mutator(store)\n await writeTrashStore(store)\n return result\n })\n trashMutation = operation.catch(() => {})\n return operation\n }\n\n // Soft-delete one session: record it in the recycle-bin index but KEEP its\n // log in the original workspace directory. Moving the file out (and detaching\n // it from the workspace) orphaned the session into DSH's \"\u672A\u5206\u7EC4\" group and\n // made restore land in \u672A\u5206\u7EC4 instead of the original workspace \u2014 so we leave\n // the file where it is and let the sidebar DOM shim hide the row instead.\n async function deleteOne(sid) {\n requireSessionId(sid)\n // Soft-delete is always allowed \u2014 including the currently-active conversation.\n // The log file stays in its original workspace dir (recorded in the \u56DE\u6536\u7AD9\n // index below), so the live session is unaffected and the entry stays\n // recoverable from \u56DE\u6536\u7AD9. (Move, by contrast, physically relocates the file\n // and still guards the active session in moveTargetWorkspace.)\n let header = null\n let cwd = null\n let title = null\n let removedPath = null\n try {\n const headers = await sp.list()\n header = headers.find((h) => String(h.id) === sid) || null\n if (header) {\n const loc = sp.locate(header)\n if (loc && typeof loc.path === 'string') removedPath = loc.path\n cwd = header.cwd || null\n title = header.title || (header.meta && header.meta.title) || null\n }\n if (!title) {\n const r = await sp.readFrom(sid, 0)\n if (r && r.meta) { if (!cwd) cwd = r.meta.cwd; title = foldTitle(r.events) }\n }\n } catch (e) { /* best-effort */ }\n // Record in the trash index only \u2014 the log stays in its workspace dir.\n if (!header && !removedPath) {\n const error = new Error('\u627E\u4E0D\u5230\u8BE5\u4F1A\u8BDD')\n error.status = 404\n throw error\n }\n const archived = await mutateArchived((list) => ({ next: null, value: list.includes(sid) })).catch(() => false)\n await mutateTrash((store) => {\n const entry = {\n sessionId: sid, title: title || cwd || sid, cwd: cwd || null,\n header: header || null, originalPath: removedPath || null,\n sizeBytes: header && typeof header.size === 'number' ? header.size : null,\n wasArchived: archived, deletedAt: Date.now(),\n }\n const at = store.items.findIndex((t) => String(t.sessionId) === sid)\n if (at >= 0) store.items[at] = entry\n else store.items.push(entry)\n store.purgedSessionIds = store.purgedSessionIds.filter((id) => id !== sid)\n })\n return { ok: true, trashed: true }\n }\n\n // Restore a trashed session: the log never left its original workspace dir,\n // so we just drop it from the recycle-bin index and the sidebar reveals it in\n // its original workspace (no move / no re-attach needed).\n async function restoreFromTrash(sid) {\n requireSessionId(sid)\n await mutateTrash(async (store) => {\n const entry = store.items.find((t) => String(t.sessionId) === sid)\n if (!entry) { const error = new Error('\u56DE\u6536\u7AD9\u4E2D\u627E\u4E0D\u5230\u8BE5\u4F1A\u8BDD'); error.status = 404; throw error }\n // Restore the pre-delete archive state before removing the durable trash\n // entry. If this fails, mutateTrash does not write and the item remains\n // recoverable instead of disappearing into an inconsistent state.\n if (entry.wasArchived === false) await restoreOne(sid)\n store.items = store.items.filter((t) => String(t.sessionId) !== sid)\n store.purgedSessionIds = store.purgedSessionIds.filter((id) => id !== sid)\n })\n return { ok: true, restored: true }\n }\n\n // Permanently erase a trashed session: physically delete its log (still in\n // the original workspace dir) and detach it from any workspace so DSH drops it.\n async function purgeFromTrash(sid) {\n requireSessionId(sid)\n let purged = false\n await mutateTrash(async (store) => {\n const entry = store.items.find((t) => String(t.sessionId) === sid)\n if (!entry) { const error = new Error('\u56DE\u6536\u7AD9\u4E2D\u627E\u4E0D\u5230\u8BE5\u4F1A\u8BDD'); error.status = 404; throw error }\n let target = null\n try {\n const headers = await sp.list()\n const current = headers.find((h) => String(h.id) === sid)\n const located = current && sp.locate(current)\n if (located && typeof located.path === 'string') target = located.path\n } catch (e) {}\n if (!target && typeof entry.originalPath === 'string') target = entry.originalPath\n // JSONL persistence stores logs as\n // .../<sessionId>/session.jsonl.zstd\n // Older backends may instead include the id in the filename itself.\n // Accept both layouts, but reject every unrelated path before unlink.\n const targetOwnsSession = target && (basename(dirname(target)) === sid || basename(target).includes(sid))\n if (target && !targetOwnsSession) {\n const error = new Error('\u65E5\u5FD7\u8DEF\u5F84\u4E0E\u4F1A\u8BDD ID \u4E0D\u5339\u914D\uFF0C\u5DF2\u505C\u6B62\u6C38\u4E45\u5220\u9664')\n error.status = 409\n throw error\n }\n // Persist the tombstone before any irreversible work. A crash after this\n // point may leave the trash item retryable, but can never resurrect the\n // session in a later list baseline.\n if (!store.purgedSessionIds.includes(sid)) store.purgedSessionIds.push(sid)\n await writeTrashStore(store)\n // A freshly-created or recently-opened Session can remain resident after\n // its file is unlinked. Flush once, then use SessionStore's entered-record\n // detach capability so DSH emits host/session-removed and the client list\n // drops the row instead of resurrecting it from live memory.\n try {\n const sessions = ctx.get('sessions')\n const liveSession = sessions && sessions.get && sessions.get(sid)\n if (liveSession && typeof sessions.flush === 'function') await sessions.flush(liveSession)\n const entered = sessions && sessions.store && sessions.store.get && sessions.store.get(sid)\n if (liveSession && (!entered || typeof entered.detach !== 'function')) throw new Error('\u5BBF\u4E3B\u672A\u63D0\u4F9B live Session detach \u80FD\u529B')\n if (entered && typeof entered.detach === 'function') entered.detach()\n // session/disposed starts an asynchronous persistence retirement. Wait\n // for it before unlinking, otherwise its final drain can race the file\n // deletion and briefly (or permanently) republish an orphan that the\n // official sidebar groups under \u201C\u672A\u5206\u7EC4\u201D.\n const retirement = sp && sp.retirements && sp.retirements.get && sp.retirements.get(sid)\n if (retirement && typeof retirement.then === 'function') await retirement\n } catch (e) {\n const error = new Error('\u65E0\u6CD5\u4ECE\u5BBF\u4E3B\u5185\u5B58\u79FB\u9664\u4F1A\u8BDD\uFF0C\u5DF2\u505C\u6B62\u6C38\u4E45\u5220\u9664\uFF1A' + String((e && e.message) || e))\n error.status = 409\n throw error\n }\n if (target) {\n try { await unlink(target) } catch (e) { if (e && e.code !== 'ENOENT') throw new Error('\u5220\u9664\u6587\u4EF6\u5931\u8D25\uFF1A' + String((e && e.message) || e)) }\n }\n try { for (const ent of w.list()) { if (ent.sessionIds.includes(sid)) { try { await ent.detachSession(sid) } catch (e) {} } } } catch (e) {}\n try { if (w.sessionPaths && w.sessionPaths.delete) w.sessionPaths.delete(sid) } catch (e) {}\n try { if (w.headers && w.headers.delete) w.headers.delete(sid) } catch (e) {}\n await restoreOne(sid)\n // Rebuild from the post-unlink disk baseline before reporting success.\n // Merely deleting the two Maps above does not notify/rebuild Workspace\n // entities, leaving the client with an orphaned \u201C\u672A\u5206\u7EC4\u201D snapshot.\n try { await reindexRegistry() } catch (e) { /* tombstone still prevents resurrection */ }\n store.items = store.items.filter((t) => String(t.sessionId) !== sid)\n purged = true\n })\n if (!purged) throw new Error('\u5F7B\u5E95\u5220\u9664\u5931\u8D25')\n return { ok: true, purged: true }\n }\n\n async function trashSettings(next) {\n if (next === undefined) return (await readTrashStore()).settings\n const days = Number(next.retentionDays)\n if (!Number.isInteger(days) || ![0, 7, 30, 90].includes(days)) {\n const error = new Error('retentionDays \u4EC5\u652F\u6301 0\u30017\u300130\u300190')\n error.status = 400\n throw error\n }\n await mutateTrash((store) => { store.settings = { retentionDays: days } })\n return (await readTrashStore()).settings\n }\n\n async function cleanupExpiredTrash() {\n const store = await readTrashStore()\n const days = store.settings.retentionDays\n if (!days) return 0\n const cutoff = Date.now() - days * 86400000\n const ids = store.items.filter((item) => Number(item.deletedAt) > 0 && Number(item.deletedAt) < cutoff).map((item) => String(item.sessionId))\n let count = 0\n for (const sid of ids) { try { await purgeFromTrash(sid); count++ } catch (e) {} }\n return count\n }\n\n // ---- \"move conversation between workspaces\" helper -----------------------\n // DSH binds a conversation to the workspace whose canonical directory path\n // equals the session's stored cwd. Moving it therefore means: (1) adopt the\n // target path as a workspace (create if needed), (2) durably relocate the\n // session's log so its header carries the new cwd, and (3) reassign the\n // workspace membership (detach everywhere, attach to target). The log\n // relocation goes through the persistence service's own encoder (handles the\n // zstd artifact encoding) with a backup + rollback so a failure never leaves\n // the session half-moved.\n\n async function moveTargetWorkspace(rawPath) {\n if (typeof rawPath !== 'string' || !rawPath.trim()) throw new Error('\u7F3A\u5C11\u76EE\u6807\u5DE5\u4F5C\u533A\u8DEF\u5F84')\n let p = String(rawPath).trim()\n if (p.startsWith('~/')) p = join(homedir(), p.slice(2))\n if (!isAbsolute(p)) p = join(homedir(), p)\n let canonical = null\n try { canonical = await realpath(p) } catch (e) { canonical = null }\n if (canonical === null) {\n await mkdir(p, { recursive: true })\n canonical = await realpath(p)\n }\n return { canonical, entity: await w.create(canonical, basename(canonical) || 'workspace') }\n }\n\n async function moveOne(sid, targetPath) {\n // Only block the *active* conversation. ctx.sessions keeps instantiated\n // sessions alive after you switch away, so the old check (sessions.get(sid))\n // wrongly rejected every opened session \u2014 you could never move one you'd\n // merely looked at. When the host exposes no active-session accessor we\n // can't prove activeness, so we allow the move; the relocation below is\n // crash-safe (backup + rollback) and re-syncs the live object.\n const activeId = getActiveSessionId(ctx)\n if (activeId != null && String(activeId) === String(sid)) {\n throw new Error('\u8BE5\u4F1A\u8BDD\u5F53\u524D\u5904\u4E8E\u6253\u5F00\u72B6\u6001\uFF0C\u8BF7\u5148\u5207\u6362\u5230\u522B\u7684\u4F1A\u8BDD\u518D\u79FB\u52A8\u3002')\n }\n const r = await sp.readFrom(sid, 0)\n if (!r || !r.meta) throw new Error('\u65E0\u6CD5\u8BFB\u53D6\u8BE5\u4F1A\u8BDD\u7684\u65E5\u5FD7')\n const meta = r.meta\n const events = r.events\n const oldCwd = meta.cwd || null\n\n const { canonical, entity: target } = await moveTargetWorkspace(targetPath)\n\n if (oldCwd) {\n let oldCanon = null\n try { oldCanon = await realpath(oldCwd) } catch (e) { oldCanon = null }\n if (oldCanon === canonical) {\n return { ok: true, already: true, workspaceId: target.id, workspaceTitle: target.title }\n }\n }\n\n const newHeader = Object.assign({}, meta, { cwd: canonical })\n\n // 1) Decide relocation strategy. `sessionPersistence.create()` rejects\n // (\"already exists in this backend\") for ANY session the host has\n // instantiated into its in-memory `states` \u2014 and DSH instantiates *every*\n // session it can find on disk at startup, including ARCHIVED ones. So a\n // supposedly \"closed\" archived session is NOT safe for the create()+append()\n // path; create() will throw. The only universally safe move is to physically\n // relocate the on-disk log (rewriting frame0's cwd) and redirect the live\n // object + persistence state. We still attempt create()+append() as the\n // fast path for genuinely-virgin session ids, but on an already-exists\n // collision we fall back to the relocate path. That covers live, archived,\n // and restored sessions alike.\n const live = ctx.get('sessions')\n const liveObj = live && live.get && live.get(sid)\n const isOpen = !!liveObj\n\n const ALREADY_EXISTS_RE = /already exists in this backend/i\n\n // Physically relocate a session's on-disk log to `newHeader`'s cwd,\n // rewriting frame0's cwd so sp.list()/reindex attribute it correctly.\n // Returns true if a relocation actually happened.\n const relocateLog = async (header, newHeaderObj) => {\n const oldPath = locatePath(header)\n const newPath = locatePath(newHeaderObj)\n if (!oldPath || !newPath || oldPath === newPath) return false\n const backupPath = `${oldPath}.move-backup-${Date.now()}`\n try {\n // Ensure the destination project directory exists (rename does not\n // create it). Without this, the rename silently no-ops on ENOENT and\n // the log stays put while workspace.json is wrongly updated.\n await mkdir(dirname(newPath), { recursive: true })\n await rename(oldPath, backupPath) // current log to safety\n await rewriteFrame0Cwd(backupPath, canonical) // frame0 cwd -> newPath\n await rename(backupPath, newPath) // relocate to the new workspace dir\n } catch (e) {\n try { await rename(backupPath, oldPath) } catch (_) {}\n if (e && e.code !== 'ENOENT') throw e\n return false\n }\n return true\n }\n\n const locatePath = (header) => {\n let fn = null\n try { if (typeof sp.locate === 'function') fn = sp.locate.bind(sp) } catch (e) {}\n if (!fn && sp.backend && typeof sp.backend.locate === 'function') fn = sp.backend.locate.bind(sp.backend)\n if (!fn) return null\n try {\n const loc = fn(header)\n if (loc && typeof loc.path === 'string') return loc.path\n if (typeof loc === 'string') return loc\n } catch (e) {}\n return null\n }\n\n // Rewriting frame0's cwd now lives in src/zstd-frame.js so it can be\n // regression-tested directly. See that module for why frame boundaries are\n // validated by decompression and why a non-session frame0 is rejected\n // instead of rewritten.\n\n if (isOpen) {\n // Live session: relocate the on-disk log (rewriting frame0's cwd to the\n // new path) and redirect the live object + persistence state. We must\n // rewrite frame0, not just rename: sp.list() reads frame0's cwd from\n // disk, and WorkspaceEntity.sessionIds filters by that exact cwd. A bare\n // rename would leave frame0 pointing at the old workspace, so reindex /\n // restart would keep attributing the session to the wrong workspace.\n await relocateLog(meta, newHeader)\n // Redirect the persistence state's cwd so future appends land in newPath.\n try {\n const st = sp.states && sp.states.get && sp.states.get(sid)\n if (st && st.meta) st.meta = Object.assign({}, st.meta, { cwd: canonical })\n } catch (e) { /* best-effort */ }\n } else {\n // Closed session: try the fast create()+append() path first. But DSH\n // instantiates *all* on-disk sessions (including archived ones) into its\n // in-memory states at startup, so create() usually throws\n // \"already exists in this backend\". On that collision we fall back to a\n // physical relocate of the existing log (rewriting frame0's cwd), which\n // is safe and needs no create().\n let oldPath = null\n try {\n const loc = locatePath(meta)\n if (loc && typeof loc === 'string') oldPath = loc\n else if (loc && loc.path) oldPath = loc.path\n } catch (e) { oldPath = null }\n\n if (typeof sp.create !== 'function' || typeof sp.append !== 'function') {\n // No create primitive: must relocate the existing log directly.\n await relocateLog(meta, newHeader)\n } else {\n const backupPath = oldPath ? `${oldPath}.move-backup-${Date.now()}` : null\n if (backupPath) { try { await rename(oldPath, backupPath) } catch (e) { if (e && e.code !== 'ENOENT') throw new Error('\u79FB\u52A8\u5931\u8D25\uFF1A\u65E0\u6CD5\u5907\u4EFD\u65E7\u7684\u4F1A\u8BDD\u65E5\u5FD7') } }\n const restore = async () => { if (backupPath) { try { await rename(backupPath, oldPath) } catch (_) {} } }\n try {\n await sp.create(newHeader)\n await sp.append(sid, events)\n const check = await sp.readFrom(sid, 0)\n if (!check || !check.meta || check.meta.cwd !== canonical) {\n throw new Error('\u79FB\u52A8\u540E\u6821\u9A8C\u5931\u8D25\uFF1A\u4F1A\u8BDD\u5DE5\u4F5C\u76EE\u5F55\u672A\u6B63\u786E\u66F4\u65B0')\n }\n if (backupPath) { try { await unlink(backupPath) } catch (e) {} }\n } catch (e) {\n if (ALREADY_EXISTS_RE.test(String((e && e.message) || e))) {\n // Collision: the session is already materialized in states (archived\n // or previously opened). Fall back to physically relocating the log.\n await restore()\n await relocateLog(meta, newHeader)\n } else {\n await restore()\n throw new Error('\u79FB\u52A8\u4F1A\u8BDD\u65E5\u5FD7\u5931\u8D25\uFF1A' + String((e && e.message) || e))\n }\n }\n }\n }\n\n // Keep the live (in-memory) session object consistent with the relocated\n // log so the host doesn't keep appending to the old path. This MUST happen\n // before attachSession(): WorkspaceEntity.attachSession() validates the\n // session by reading live.header first, and if it still carries the old cwd\n // the realpath check will fail on the old (now missing) directory.\n try {\n if (liveObj) {\n if ('header' in liveObj) liveObj.header = newHeader\n if ('cwd' in liveObj) liveObj.cwd = canonical\n if ('meta' in liveObj) liveObj.meta = newHeader\n }\n } catch (e) { /* best-effort */ }\n\n // 2) Reassign workspace membership (durable records + in-memory index).\n for (const ent of w.list()) {\n try { await ent.detachSession(sid) } catch (e) { /* ignore */ }\n }\n if (w.headers && typeof w.headers.set === 'function') w.headers.set(sid, newHeader)\n if (w.sessionPaths && typeof w.sessionPaths.set === 'function') w.sessionPaths.set(sid, canonical)\n await target.attachSession(sid)\n\n // Verify the membership actually landed on the target workspace. DSH's\n // WorkspaceEntity.attachSession persists asynchronously; if it silently\n // no-ops (e.g. the session's durable cwd still points elsewhere) the UI\n // would show \"moved\" while the sidebar keeps the old grouping. Fail loud\n // instead of returning a fake success.\n const verified = (() => {\n try { return target.sessionIds.includes(sid) } catch (e) { return false }\n })()\n if (!verified) {\n throw new Error('\u79FB\u52A8\u540E\u6821\u9A8C\u5931\u8D25\uFF1A\u4F1A\u8BDD\u672A\u51FA\u73B0\u5728\u76EE\u6807\u5DE5\u4F5C\u533A\uFF0C\u8BF7\u91CD\u8BD5\u6216\u91CD\u542F DSH\u3002')\n }\n\n return {\n ok: true,\n moved: true,\n workspaceId: target.id,\n workspaceTitle: target.title,\n workspacePath: canonical,\n }\n }\n\n // Force the host's WorkspaceRegistry to rebuild its in-memory sessionPath\n // index from the durable persistence headers. DSH's WorkspaceEntity.sessionIds\n // is a *getter* that filters record.sessionIds by `host.sessionPath(id) ===\n // record.path`; that sessionPath Map is only repopulated at startup (bootstrap\n // + indexHeaders). So even after a successful move writes the durable cwd,\n // the running process keeps attributing the session to its OLD workspace until\n // a restart \u2014 unless we reindex here. Calling this right after move makes the\n // sidebar reflect the new grouping with NO restart required.\n async function reindexRegistry() {\n const reg = w\n if (!reg || typeof reg.replaceHeaderIndex !== 'function') return false\n let headers = null\n try { headers = await sp.list() } catch (e) { headers = null }\n if (!headers || !Array.isArray(headers)) return false\n await reg.replaceHeaderIndex(headers)\n if (typeof reg.rebuildEntities === 'function') reg.rebuildEntities()\n return true\n }\n\n async function listWorkspaces() {\n const out = []\n try {\n for (const ent of w.list()) out.push({ workspaceId: ent.id, title: ent.title, path: ent.path })\n } catch (e) { /* ignore */ }\n return out\n }\n\n // Archive (hide) one session: adds its id to the durable archive set so it\n // is dropped out of the sidebar. DSH requires the session to exist (live or\n // persisted) \u2014 a genuine miss surfaces as an error.\n async function archiveOne(sid) {\n requireSessionId(sid)\n return mutateArchived(async (list) => {\n if (list.includes(sid)) return { next: null, value: { ok: true, archived: false } }\n await w.archiveSession(sid)\n // archiveSession owns the durable write; keep this operation serialized\n // with restoreOne so two requests cannot overwrite each other's state.\n return { next: null, value: { ok: true, archived: true } }\n })\n }\n\n async function allSessionItems() {\n let materialized = new Set()\n let live = ctx.get('sessions')\n try {\n const headers = await sp.list()\n materialized = new Set(headers.map((h) => String(h.id)))\n } catch (e) { /* best-effort */ }\n const ids = []\n try { for (const header of await sp.list()) ids.push(String(header.id)) } catch (e) { /* ignore */ }\n if (live) { try { live.list().forEach((s) => { if (!ids.includes(String(s.id))) ids.push(String(s.id)) }) } catch (e) { /* ignore */ } }\n // Exclude sessions already moved to the recycle bin (\u8F6F\u5220\u9664): they live in\n // \u56DE\u6536\u7AD9, not in \u4F1A\u8BDD\u7BA1\u7406, so the panel won't re-list them after a delete.\n let hiddenIds = new Set()\n try {\n const store = await readTrashStore()\n hiddenIds = new Set([...store.items.map((t) => String(t.sessionId)), ...store.purgedSessionIds.map(String)])\n } catch (e) {}\n const visibleIds = ids.filter((id) => !hiddenIds.has(id))\n wsByPath = {}\n try { for (const ent of w.list()) wsByPath[ent.path] = ent } catch (e) { wsByPath = {} }\n const currentArchived = new Set((await archivedState().catch(() => ({ archivedSessionIds: [] }))).archivedSessionIds || [])\n const items = []\n const CHUNK = 6\n for (let i = 0; i < visibleIds.length; i += CHUNK) {\n const res2 = await Promise.all(visibleIds.slice(i, i + CHUNK).map(resolveOne))\n for (const it of res2) items.push({ ...it, archived: currentArchived.has(it.sessionId) })\n }\n return items\n }\n\n async function sidebarAuthority() {\n const ids = []\n try { for (const header of await sp.list()) ids.push(String(header.id)) } catch (e) {}\n const sessions = ctx.get('sessions')\n try { if (sessions) sessions.list().forEach((session) => { if (!ids.includes(String(session.id))) ids.push(String(session.id)) }) } catch (e) {}\n const store = await readTrashStore()\n if (!authorityTitlesLoaded && ids.length && typeof sq.readTitleSnapshots === 'function') {\n const results = await sq.readTitleSnapshots(ids)\n results.forEach((result, index) => {\n if (result && result.status === 'fulfilled' && result.value && result.value.title && typeof result.value.title.title === 'string') {\n authorityTitleCache.set(ids[index], result.value.title.title)\n }\n })\n authorityTitlesLoaded = true\n } else if (!authorityTitlesLoaded) {\n for (const sid of ids) {\n try {\n const snapshot = await sq.readTitleSnapshot(sid)\n if (snapshot && snapshot.title && typeof snapshot.title.title === 'string') authorityTitleCache.set(sid, snapshot.title.title)\n } catch (e) {}\n }\n authorityTitlesLoaded = true\n }\n return {\n titles: Object.fromEntries(authorityTitleCache),\n trashedSessionIds: store.items.map((item) => String(item.sessionId)),\n purgedSessionIds: store.purgedSessionIds.map(String),\n }\n }\n\n // \u805A\u5408\u4E00\u6761\u4F1A\u8BDD\u7684\u8BE6\u60C5\uFF08\u78C1\u76D8\u5360\u7528 / \u8F6E\u6B21\u00B7\u6B65\u6570\u00B7\u6D88\u606F\u6570 / \u5DE5\u5177\u7EDF\u8BA1 / fetch /\n // write/edit \u6587\u4EF6 / \u8840\u7EDF parent/children/subagents\uFF09\u3002live \u4E0E\u6301\u4E45\u5316\u4F1A\u8BDD\u90FD\u53EF\u8BFB\u3002\n // \u6240\u6709\u7EDF\u8BA1\u5BF9\u672A\u77E5\u4E8B\u4EF6\u7C7B\u578B\u5BB9\u9519\uFF1Bfetch \u4E0E files \u505A\u4E0A\u9650\u622A\u65AD\uFF0Cfiles \u7528 stat \u8FC7\u6EE4\n // \u78C1\u76D8\u4E0A\u5DF2\u4E0D\u5B58\u5728\u7684\u8DEF\u5F84\uFF0C\u907F\u514D\u8BE6\u60C5\u9762\u677F\u5217\u51FA\u5DF2\u5220\u9664\u6587\u4EF6\u3002\n async function buildDetails(sid) {\n const sessions = ctx.get('sessions')\n const live = sessions && sessions.get(sid)\n let meta = null\n let events = []\n if (live !== void 0) {\n meta = (live && live.header) || null\n try { events = Array.isArray(live.events) ? [...live.events] : [] } catch (e) { events = [] }\n } else {\n const r = await sp.readFrom(sid, 0)\n if (!r || !r.meta) throw new Error('\u627E\u4E0D\u5230\u8BE5\u4F1A\u8BDD\u7684\u8BB0\u5F55\uFF08\u4F1A\u8BDD\u4E0D\u5B58\u5728\uFF09')\n meta = r.meta\n events = Array.isArray(r.events) ? r.events : []\n }\n let sizeBytes = null\n try {\n // rc.8 \u7684 sessionPersistence \u540E\u7AEF\u6CA1\u6709 artifactInfo\uFF1B\u7528 locate(meta) \u62FF\u65E5\u5FD7\n // \u6587\u4EF6\u771F\u5B9E\u8DEF\u5F84\u540E stat \u51FA\u5B57\u8282\u6570\uFF08\u78C1\u76D8\u5360\u7528\uFF09\u3002\n const loc = sp.locate(meta)\n if (loc && typeof loc.path === 'string' && loc.path) {\n const st = await stat(loc.path)\n if (st && typeof st.size === 'number') sizeBytes = st.size\n }\n } catch (e) { sizeBytes = null }\n let lastTime = typeof meta && typeof meta.createdAt === 'number' ? meta.createdAt : 0\n const fileSet = new Map()\n const stats = {\n turns: 0, steps: 0, userMessages: 0, assistantMessages: 0,\n toolCalls: 0, attachments: 0, toolCounts: {}, fetches: [],\n }\n const turnSeen = new Set()\n const stepSeen = new Set()\n for (const ev of events) {\n if (ev && typeof ev.time === 'number' && ev.time > lastTime) lastTime = ev.time\n const d = (ev && ev.data && typeof ev.data === 'object') ? ev.data : {}\n const type = ev && ev.type\n switch (type) {\n case 'turn/start':\n if (typeof d.turn === 'number') turnSeen.add(d.turn)\n break\n case 'step/start':\n if (typeof d.step === 'number') stepSeen.add(d.step)\n break\n case 'user/message':\n stats.userMessages++\n if (Array.isArray(d.content)) for (const b of d.content) if (b && b.type === 'image') stats.attachments++\n break\n case 'assistant/message':\n stats.assistantMessages++\n break\n case 'tool/call': {\n stats.toolCalls++\n const tn = typeof d.name === 'string' && d.name ? d.name : 'tool'\n stats.toolCounts[tn] = (stats.toolCounts[tn] || 0) + 1\n if (FETCH_TOOL_RE.test(tn)) {\n let query\n try {\n const a = typeof d.arguments === 'string' ? JSON.parse(d.arguments) : d.arguments\n query = typeof a?.query === 'string' ? a.query : typeof a?.url === 'string' ? a.url : typeof a?.q === 'string' ? a.q : undefined\n } catch (e) { query = undefined }\n stats.fetches.push({ tool: tn, ...(query && query !== '' ? { query } : {}) })\n }\n if (tn === 'write' || tn === 'edit') {\n let argsJ\n try { argsJ = typeof d.arguments === 'string' ? JSON.parse(d.arguments) : d.arguments } catch (e) { break }\n const fp = argsJ && typeof argsJ.file_path === 'string' && argsJ.file_path ? argsJ.file_path : undefined\n if (fp !== undefined && !fileSet.has(fp)) fileSet.set(fp, tn)\n }\n break\n }\n }\n }\n stats.turns = turnSeen.size\n stats.steps = stepSeen.size\n if (stats.fetches.length > MAX_FETCHES) stats.fetches = stats.fetches.slice(0, MAX_FETCHES)\n const fileEntries = [...fileSet.entries()].slice(0, MAX_FILES * 2)\n const exists = await Promise.all(fileEntries.map(([p]) => stat(p).then(() => true).catch(() => false)))\n const files = fileEntries\n .filter((_, i) => exists[i])\n .map(([path, tool]) => ({ path, tool }))\n .slice(0, MAX_FILES)\n // lineage\uFF1A\u5206\u53C9\u5B50\u4F1A\u8BDD\uFF08\u975E subagent\uFF09\u4E0E\u5B50\u4EE3\u7406\uFF08origin==='subagent'\uFF09\uFF0Csource \u53BB\u91CD\u3002\n const lineage = {\n parentSessionId: (meta && typeof meta.parentSession === 'string') ? meta.parentSession : null,\n children: [],\n subagents: [],\n }\n const childrenSet = new Set()\n const subagentSet = new Set()\n try {\n if (typeof sp.list === 'function') {\n for (const h of await sp.list()) {\n if (String(h.parentSession) !== String(sid)) continue\n if (h.origin === 'subagent') subagentSet.add(h.id); else childrenSet.add(h.id)\n }\n }\n } catch (e) { /* best-effort */ }\n if (sessions) {\n try {\n sessions.list().forEach((s) => {\n if (String(s.header.parentSession) !== String(sid)) return\n if (s.header.origin === 'subagent') subagentSet.add(s.id); else childrenSet.add(s.id)\n })\n } catch (e) { /* best-effort */ }\n }\n lineage.children = [...childrenSet]\n lineage.subagents = [...subagentSet]\n return {\n sessionId: sid,\n sizeBytes,\n createdAt: (meta && typeof meta.createdAt === 'number') ? meta.createdAt : null,\n updatedAt: lastTime || null,\n files,\n stats,\n lineage,\n }\n }\n\n ctx.effect(() => {\n const disposers = []\n\n if (typeof ctx.on === 'function') disposers.push(ctx.on('session/event', (session, event) => {\n if (event && event.type === 'session/title' && event.data && typeof event.data.title === 'string') {\n authorityTitleCache.set(String(session.id), event.data.title)\n }\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/list',\n handler: async (req, res) => {\n try {\n const state = await archivedState()\n const ids = state.archivedSessionIds || []\n // Only surface archived ids that still exist (materialized log or live).\n // Deleted sessions keep a hidden archive id but no log, so they drop out here.\n let materialized = new Set()\n let live = ctx.get('sessions')\n try {\n const headers = await sp.list()\n materialized = new Set(headers.map((h) => String(h.id)))\n } catch (e) { /* best-effort */ }\n const trashStore = await readTrashStore()\n const hidden = new Set([...trashStore.items.map((item) => String(item.sessionId)), ...trashStore.purgedSessionIds.map(String)])\n const idStrs = ids.map(String).filter((id) => !hidden.has(id) && (materialized.has(id) || (live && live.get(id))))\n wsByPath = {}\n try { for (const ent of w.list()) wsByPath[ent.path] = ent } catch (e) { wsByPath = {} }\n const items = []\n const CHUNK = 6\n for (let i = 0; i < idStrs.length; i += CHUNK) {\n const res2 = await Promise.all(idStrs.slice(i, i + CHUNK).map(resolveOne))\n items.push.apply(items, res2)\n }\n json(res, { items })\n } catch (e) {\n json(res, { error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/restore',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const sid = body && typeof body.sessionId === 'string' ? body.sessionId : null\n if (!sid) return json(res, { ok: false, error: 'missing sessionId' }, 400)\n json(res, await restoreOne(sid))\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, errorStatus(e))\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/restore-many',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const ids = parseIds(body)\n if (!ids || ids.length === 0) return json(res, { ok: false, error: 'missing sessionIds' }, 400)\n const results = []\n for (const sid of ids) {\n try { results.push({ sessionId: sid, ok: true, ...(await restoreOne(sid)) }) }\n catch (e) { results.push({ sessionId: sid, ok: false, error: String((e && e.message) || e) }) }\n }\n json(res, { ok: true, restored: results.filter((r) => r.ok).length, results })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/delete',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const sid = body && typeof body.sessionId === 'string' ? body.sessionId : null\n if (!sid) return json(res, { ok: false, error: 'missing sessionId' }, 400)\n json(res, await deleteOne(sid))\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, errorStatus(e))\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/delete-many',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const ids = parseIds(body)\n if (!ids || ids.length === 0) return json(res, { ok: false, error: 'missing sessionIds' }, 400)\n const results = []\n for (const sid of ids) {\n try { results.push({ sessionId: sid, ok: true, ...(await deleteOne(sid)) }) }\n catch (e) { results.push({ sessionId: sid, ok: false, error: String((e && e.message) || e) }) }\n }\n json(res, { ok: true, deleted: results.filter((r) => r.ok).length, results })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n // ---- Recycle bin (\u56DE\u6536\u7AD9) routes ----------------------------------------\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/trash/list',\n handler: async (req, res) => {\n try {\n await cleanupExpiredTrash()\n const list = await readTrash()\n list.sort((a, b) => (b.deletedAt || 0) - (a.deletedAt || 0))\n const store = await readTrashStore()\n json(res, { schemaVersion: store.schemaVersion, settings: store.settings, purgedSessionIds: store.purgedSessionIds, items: list })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, errorStatus(e))\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/trash/settings',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const settings = body && Object.prototype.hasOwnProperty.call(body, 'retentionDays')\n ? await trashSettings({ retentionDays: body.retentionDays })\n : await trashSettings()\n json(res, { ok: true, settings })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, errorStatus(e))\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/trash/verify',\n handler: async (req, res) => {\n try {\n const items = await readTrash()\n const results = await Promise.all(items.map(async (item) => {\n let exists = false\n if (typeof item.originalPath === 'string') exists = await stat(item.originalPath).then(() => true).catch(() => false)\n return { sessionId: item.sessionId, status: exists ? 'ok' : 'missing', originalPath: item.originalPath || null }\n }))\n json(res, { ok: true, healthy: results.filter((r) => r.status === 'ok').length, missing: results.filter((r) => r.status === 'missing').length, results })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, errorStatus(e))\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/trash/restore',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const sid = body && typeof body.sessionId === 'string' ? body.sessionId : null\n if (!sid) return json(res, { ok: false, error: 'missing sessionId' }, 400)\n json(res, await restoreFromTrash(sid))\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/trash/purge',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const sid = body && typeof body.sessionId === 'string' ? body.sessionId : null\n if (!sid) return json(res, { ok: false, error: 'missing sessionId' }, 400)\n json(res, await purgeFromTrash(sid))\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/trash/purge-many',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const ids = parseIds(body)\n if (!ids || ids.length === 0) return json(res, { ok: false, error: 'missing sessionIds' }, 400)\n const results = []\n for (const sid of ids) {\n try { results.push({ sessionId: sid, ok: true, ...(await purgeFromTrash(sid)) }) }\n catch (e) { results.push({ sessionId: sid, ok: false, error: String((e && e.message) || e) }) }\n }\n json(res, { ok: true, purged: results.filter((r) => r.ok).length, results })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n // All conversations (for the \"\u79FB\u52A8\u4F1A\u8BDD\" panel).\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/sessions',\n handler: async (req, res) => {\n try {\n json(res, { items: await allSessionItems() })\n } catch (e) {\n json(res, { error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/sidebar-state',\n handler: async (req, res) => {\n try {\n json(res, await sidebarAuthority())\n } catch (e) {\n json(res, { error: String((e && e.message) || e) }, errorStatus(e))\n }\n },\n }))\n\n // Available target workspaces (for the move picker).\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/workspaces',\n handler: async (req, res) => {\n try {\n json(res, { items: await listWorkspaces() })\n } catch (e) {\n json(res, { error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n // Move one conversation to a target workspace (existing path or a new one).\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/move',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const sid = body && typeof body.sessionId === 'string' ? body.sessionId : null\n const target = body && typeof body.targetPath === 'string' ? body.targetPath : null\n if (!sid) return json(res, { ok: false, error: 'missing sessionId' }, 400)\n if (!target) return json(res, { ok: false, error: 'missing targetPath' }, 400)\n const moved = await moveOne(sid, target)\n // Reindex the host's in-memory sessionPath index so the sidebar\n // reflects the new grouping immediately (no DSH restart needed).\n // Do this before replying: drag/drop and menu clients treat a 2xx\n // response as the commit point and must never announce success while\n // the sidebar still holds the old workspace index.\n try { await reindexRegistry() } catch (e) { /* best-effort */ }\n json(res, { sessionId: sid, ...moved })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n // Archive (hide) one session.\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/archive',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const sid = body && typeof body.sessionId === 'string' ? body.sessionId : null\n if (!sid) return json(res, { ok: false, error: 'missing sessionId' }, 400)\n json(res, { sessionId: sid, ...(await archiveOne(sid)) })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n // Archive (hide) many sessions.\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/archive-many',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const ids = parseIds(body)\n if (!ids || ids.length === 0) return json(res, { ok: false, error: 'missing sessionIds' }, 400)\n const results = []\n for (const sid of ids) {\n try { results.push({ sessionId: sid, ok: true, ...(await archiveOne(sid)) }) }\n catch (e) { results.push({ sessionId: sid, ok: false, error: String((e && e.message) || e) }) }\n }\n json(res, { ok: true, archived: results.filter((r) => r.ok).length, results })\n } catch (e) {\n json(res, { ok: false, error: String((e && e.message) || e) }, 500)\n }\n },\n }))\n\n // Per-session details (v2.0): disk usage, turn/step/message counts, tool\n // usage, fetch records, write/edit files, and lineage.\n disposers.push(ctx.webServer.register({\n kind: 'exact',\n path: '/archived-sessions/details',\n handler: async (req, res) => {\n try {\n const body = await readJsonBody(req)\n const sid = body && typeof body.sessionId === 'string' ? body.sessionId : null\n if (!sid) return json(res, { ok: false, error: 'missing sessionId' }, 400)\n json(res, await buildDetails(sid))\n } catch (e) {\n json(res, { error: String((e && e.message) || e) }, (e && e.status) ? e.status : 500)\n }\n },\n }))\n\n return () => { for (const d of disposers) d() }\n }, 'dsh-sessions-manager: routes')\n}\n", "// dsh-sessions-manager \u2014 zstd frame helpers.\n//\n// DSH persists session logs as a sequence of concatenated zstd frames. The\n// FIRST frame must be exactly one line: the session header JSON (type\n// 'session'). The persistence layer enforces this on startup\n// (assertZstdHeaderFrame), so any corruption of frame0 takes down the whole\n// web profile.\n//\n// Moving a session between workspaces requires rewriting frame0's `cwd`\n// without re-encoding the rest of the log. That rewrite is where a bad frame\n// boundary can silently destroy a session \u2014 hence the defensive checks here.\n\nimport zlib from 'node:zlib'\nimport { readFileSync, writeFileSync } from 'node:fs'\n\n// zstd magic bytes are 28 B5 2F FD; read as a little-endian uint32 that is\n// 0xFD2FB528 (4247762216).\nexport const ZSTD_MAGIC = 0xFD2FB528\n\nconst CHECKSUM_OPTS = { params: { [zlib.constants.ZSTD_c_checksumFlag]: 1 } }\n\n/**\n * Locate real zstd frame boundaries in a concatenated-frame buffer.\n *\n * Scanning for the 4-byte magic alone produces FALSE POSITIVES: the same byte\n * sequence can occur inside compressed data. Every candidate is therefore\n * validated by attempting decompression; only offsets that decode are kept.\n *\n * @param {Buffer} buf\n * @returns {number[]} ascending offsets of real frame starts\n */\nexport function findZstdFrameStarts(buf) {\n const starts = []\n for (let i = 0; i + 4 <= buf.length; i++) {\n if (buf.readUInt32LE(i) !== ZSTD_MAGIC) continue\n try {\n // Two checks are needed, not just one:\n // - a magic inside compressed data fails to decode and throws\n // - a BARE 4-byte magic at the very end of the buffer decodes to an\n // EMPTY result without throwing, so non-empty output is required too\n // Every real frame carries at least one JSON line, so neither case can\n // be a genuine frame start.\n const out = zlib.zstdDecompressSync(buf.subarray(i, i + Math.min(buf.length - i, 1000000)))\n if (out.length > 0) starts.push(i)\n } catch (_) {\n // Not a real frame boundary \u2014 the magic bytes occurred inside compressed data.\n }\n }\n return starts\n}\n\n/**\n * Rewrite the `cwd` field of a session log's first frame, leaving all\n * subsequent frames byte-identical.\n *\n * Refuses to write anything unless frame0 is a session header. A corrupted\n * frame0 (e.g. an `agent/inbox/spliced` event) is reported as an error rather\n * than being re-serialized back to disk \u2014 rewriting it would bake the\n * corruption in permanently and make the file unrecoverable.\n *\n * @param {string} filePath path to session.jsonl.zstd\n * @param {string} newCwd workspace path to write into frame0\n * @throws {Error} when the log has no zstd frame or frame0 is not a session header\n */\nexport function rewriteFrame0Cwd(filePath, newCwd) {\n const buf = readFileSync(filePath)\n const starts = findZstdFrameStarts(buf)\n if (starts.length === 0) throw new Error('\u4F1A\u8BDD\u65E5\u5FD7\u683C\u5F0F\u5F02\u5E38\uFF08\u65E0 zstd \u5E27\uFF09')\n const end0 = starts.length > 1 ? starts[1] : buf.length\n const frame0 = buf.subarray(starts[0], end0)\n const text = zlib.zstdDecompressSync(frame0).toString('utf8')\n const nl = text.indexOf('\\n')\n const line = nl >= 0 ? text.slice(0, nl) : text\n const obj = JSON.parse(line)\n if (obj.type !== 'session') {\n throw new Error(`\u4F1A\u8BDD\u65E5\u5FD7\u683C\u5F0F\u5F02\u5E38\uFF08\u5E270 \u4E0D\u662F session header\uFF0C\u5B9E\u9645 type=${obj.type}\uFF09`)\n }\n if (obj.cwd === newCwd) return // already correct, no rewrite needed\n obj.cwd = newCwd\n const newFrame0 = zlib.zstdCompressSync(JSON.stringify(obj) + '\\n', CHECKSUM_OPTS)\n const rest = buf.subarray(end0)\n writeFileSync(filePath, Buffer.concat([newFrame0, rest]))\n}\n\n/**\n * Non-destructive variant of rewriteFrame0Cwd: returns the rewritten buffer\n * instead of touching the file on disk. Used by tests.\n *\n * @param {Buffer} buf\n * @param {string} newCwd\n * @returns {Buffer} rewritten log\n */\nexport function rewriteFrame0CwdInMemory(buf, newCwd) {\n const starts = findZstdFrameStarts(buf)\n if (starts.length === 0) throw new Error('\u4F1A\u8BDD\u65E5\u5FD7\u683C\u5F0F\u5F02\u5E38\uFF08\u65E0 zstd \u5E27\uFF09')\n const end0 = starts.length > 1 ? starts[1] : buf.length\n const frame0 = buf.subarray(starts[0], end0)\n const text = zlib.zstdDecompressSync(frame0).toString('utf8')\n const nl = text.indexOf('\\n')\n const line = nl >= 0 ? text.slice(0, nl) : text\n const obj = JSON.parse(line)\n if (obj.type !== 'session') {\n throw new Error(`\u4F1A\u8BDD\u65E5\u5FD7\u683C\u5F0F\u5F02\u5E38\uFF08\u5E270 \u4E0D\u662F session header\uFF0C\u5B9E\u9645 type=${obj.type}\uFF09`)\n }\n obj.cwd = newCwd\n const newFrame0 = zlib.zstdCompressSync(JSON.stringify(obj) + '\\n', CHECKSUM_OPTS)\n const rest = buf.subarray(end0)\n return Buffer.concat([newFrame0, rest])\n}\n\n/**\n * Build a multi-frame session log buffer (header frame + event frames),\n * matching the layout DSH's persistence layer writes. Used by tests.\n *\n * @param {object} header session header (must have type: 'session')\n * @param {object[]} events subsequent records, one zstd frame each\n * @returns {Buffer}\n */\nexport function buildSessionLog(header, events = []) {\n const frames = [JSON.stringify(header) + '\\n', ...events.map((e) => JSON.stringify(e) + '\\n')]\n return Buffer.concat(frames.map((f) => zlib.zstdCompressSync(Buffer.from(f, 'utf8'), CHECKSUM_OPTS)))\n}\n\n/**\n * Read frame0 of a session log and return the parsed header line.\n *\n * @param {Buffer} buf\n * @returns {{obj: object, lineCount: number}}\n */\nexport function readFrame0(buf) {\n const starts = findZstdFrameStarts(buf)\n if (starts.length === 0) throw new Error('\u4F1A\u8BDD\u65E5\u5FD7\u683C\u5F0F\u5F02\u5E38\uFF08\u65E0 zstd \u5E27\uFF09')\n const end0 = starts.length > 1 ? starts[1] : buf.length\n const text = zlib.zstdDecompressSync(buf.subarray(starts[0], end0)).toString('utf8')\n const lines = text.split('\\n').filter((l) => l.length > 0)\n return { obj: JSON.parse(lines[0]), lineCount: lines.length }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["readFileSync", "readFileSync"]
7
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "dsh-sessions-manager",
3
+ "description": "DSH 设置面板会话管理器:归档 / 恢复 / 彻底删除 / 移动到其他工作区,带工作区标签与会话日期;统一「会话管理」面板。Session manager for the DeepSeek Harness settings panel — archive / restore / permanently delete / move sessions across workspaces, with workspace tags & session dates in one unified panel.",
4
+ "version": "3.2.1",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./client": "./lib/client.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "lib",
14
+ "src",
15
+ "cordis.patch.yml",
16
+ "README.md",
17
+ "README.en.md",
18
+ "LICENSE",
19
+ "assets"
20
+ ],
21
+ "keywords": [
22
+ "deepseek-harness",
23
+ "dsh-plugin",
24
+ "dsh",
25
+ "session-manager",
26
+ "archived-sessions",
27
+ "session"
28
+ ],
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/TOBYCAI/dsh-sessions-manager.git"
33
+ },
34
+ "scripts": {
35
+ "build": "node build.mjs",
36
+ "test": "node --test tests/*.test.js",
37
+ "check:dist": "npm run build && git diff --exit-code -- lib",
38
+ "check:sessions": "node scripts/check-session-health.mjs",
39
+ "prepack": "node build.mjs"
40
+ },
41
+ "devDependencies": {
42
+ "esbuild": "^0.25.0"
43
+ },
44
+ "dsh": {
45
+ "bundle": {
46
+ "patch": "./cordis.patch.yml"
47
+ },
48
+ "client": {
49
+ "inject": [
50
+ "@deepseek-ai/dsh-client-runtime",
51
+ "@deepseek-ai/dsh-client-ui-slots",
52
+ "@deepseek-ai/dsh-client-ui-settings",
53
+ "@deepseek-ai/dsh-client-locale"
54
+ ],
55
+ "platform": "web"
56
+ }
57
+ },
58
+ "peerDependencies": {
59
+ "@deepseek-ai/cordis": "^4.0.1",
60
+ "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6",
61
+ "@deepseek-ai/dsh-workspace": "^0.1.0-rc.6",
62
+ "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.0-rc.6",
63
+ "@deepseek-ai/dsh-session-query": "^0.1.0-rc.6",
64
+ "@deepseek-ai/dsh-storage-domain": "^0.1.0-rc.6",
65
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
66
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
67
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6",
68
+ "react": "^18.2.0"
69
+ },
70
+ "allowScripts": {
71
+ "esbuild@0.25.0": true
72
+ }
73
+ }