feat(server,web): DB-served session list, CLI-session visibility toggle, and sidebar group paging (#158)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Yaowei Zheng
2026-08-03 13:51:14 +08:00
committed by GitHub
parent e6a58ac131
commit aeffc8a02f
16 changed files with 359 additions and 42 deletions
+32 -4
View File
@@ -194,7 +194,7 @@ describe("session-index", () => {
sessionMeta(junkMeta),
userText("junk"),
]);
const list = (await (await api.get(base())).json()) as SessionsResponse;
const list = (await (await api.get(`${base()}?cli=1`)).json()) as SessionsResponse;
expect(list.sessions.find((s) => s.sessionId === adopted)?.source).toBe("schedule");
expect(list.sessions.find((s) => s.sessionId === junk)?.source).toBeUndefined();
});
@@ -395,7 +395,13 @@ describe("session-index", () => {
userText("cli session"),
]);
const list = (await (await api.get(base())).json()) as SessionsResponse;
// The default list is DB-only (web rows): an unmanaged CLI Trace is neither listed
// nor adopted by it (#139 — no Trace-directory scanning on the default path).
const webOnly = (await (await api.get(base())).json()) as SessionsResponse;
expect(webOnly.sessions.find((s) => s.sessionId === discovered)).toBeUndefined();
expect((await api.get(`/api/sessions/${discovered}`)).status).toBe(404);
const list = (await (await api.get(`${base()}?cli=1`)).json()) as SessionsResponse;
const found = list.sessions.find((s) => s.sessionId === discovered);
expect(found).toBeDefined();
expect(found!.modelId).toBe("cli-model");
@@ -404,11 +410,33 @@ describe("session-index", () => {
expect(found!.hasTrace).toBe(true);
expect(found!.createdAt).toBe(sessionIdCreatedAt(discovered));
// Already indexed: visible via the single-lookup endpoint.
// Adopted as client "cli": the default list still excludes it afterwards, and counts
// follow the same filter…
const after = (await (await api.get(`${base()}?counts=1`)).json()) as SessionsResponse;
expect(after.sessions.find((s) => s.sessionId === discovered)).toBeUndefined();
expect(after.counts!.active).toBe(0);
// …but the adopted row makes the Session individually reachable (deep links work).
const single = await api.get(`/api/sessions/${discovered}`);
expect(single.status).toBe(200);
});
it("legacy rows without a client marker stay visible by default (grandfathered as web)", async () => {
const legacy = "session-2026-07-02-09-00-00-0abc0001";
t.deps.sessionsRepo.insert({
sessionId: legacy,
projectId,
agentId: "default_agent",
provider: "custom",
modelId: "m-legacy",
workspace: "/tmp/w-legacy",
approvalMode: "allow-all",
title: null,
createdAt: "2026-07-02T09:00:00.000Z",
});
const list = (await (await api.get(base())).json()) as SessionsResponse;
expect(list.sessions.find((s) => s.sessionId === legacy)).toBeDefined();
});
it("DELETE Session: clears the index row and every Trace shard; the list doesn't resurrect it; re-delete 404", async () => {
await configureModels();
const { session } = (await (await api.post(base(), {})).json()) as SessionCreateResponse;
@@ -484,7 +512,7 @@ describe("session-index", () => {
}),
]);
const created = (await (await api.post(base(), {})).json()) as SessionCreateResponse;
const list = (await (await api.get(base())).json()) as SessionsResponse;
const list = (await (await api.get(`${base()}?cli=1`)).json()) as SessionsResponse;
expect(list.sessions[0]!.sessionId).toBe(created.session.sessionId);
expect(list.sessions[list.sessions.length - 1]!.sessionId).toBe(older);
});