feat: session source in session_meta and an Automated sidebar folder (#40)
Co-authored-by: Alice <alice@prismshadow.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ import { sseEndpoint } from "../sse.js";
|
||||
import {
|
||||
badRequest,
|
||||
optionalEnum,
|
||||
optionalPagingQuery,
|
||||
optionalString,
|
||||
paginationQuery,
|
||||
pathParam,
|
||||
@@ -96,7 +97,14 @@ export function agentSessionsRoutes(deps: AppDeps): Hono<AppEnv> {
|
||||
const agentId = requireValidId(c, "agentId");
|
||||
deps.projectService.requireProjectAccess(c.var.user.userId, projectId);
|
||||
await deps.agentConfigService.requireExists(projectId, agentId);
|
||||
const sessions = await deps.sessionService.listSessions(projectId, agentId);
|
||||
// Optional paging (absent = full list, the pre-paging contract): the sidebar requests
|
||||
// limit+1 and shows limit, detecting "has more" without a response-envelope change.
|
||||
const paging = optionalPagingQuery(c);
|
||||
const sessions = await deps.sessionService.listSessions(
|
||||
projectId,
|
||||
agentId,
|
||||
...(paging ? [paging] : []),
|
||||
);
|
||||
return c.json({ sessions } satisfies SessionsResponse);
|
||||
});
|
||||
|
||||
@@ -167,7 +175,9 @@ export function sessionsRoutes(deps: AppDeps): Hono<AppEnv> {
|
||||
app.get("/:sessionId", async (c) => {
|
||||
const row = resolveSession(c);
|
||||
const hasTrace = await deps.sessionService.hasTrace(row);
|
||||
return c.json({ session: deps.sessionService.toInfo(row, hasTrace) } satisfies SessionResponse);
|
||||
return c.json({
|
||||
session: await deps.sessionService.toInfo(row, hasTrace),
|
||||
} satisfies SessionResponse);
|
||||
});
|
||||
|
||||
app.patch("/:sessionId", async (c) => {
|
||||
@@ -216,7 +226,7 @@ export function sessionsRoutes(deps: AppDeps): Hono<AppEnv> {
|
||||
}
|
||||
const hasTrace = await deps.sessionService.hasTrace(updated);
|
||||
return c.json({
|
||||
session: deps.sessionService.toInfo(updated, hasTrace),
|
||||
session: await deps.sessionService.toInfo(updated, hasTrace),
|
||||
} satisfies SessionResponse);
|
||||
});
|
||||
|
||||
@@ -244,6 +254,9 @@ export function sessionsRoutes(deps: AppDeps): Hono<AppEnv> {
|
||||
{ recursive: true, force: true },
|
||||
);
|
||||
deps.sessionsRepo.deleteById(row.sessionId);
|
||||
// Drop the derived-origin entry along with the Session (bulk Agent/Project deletion
|
||||
// may leave stale entries; session ids are never reused, so they are never matched).
|
||||
deps.sessionSources.delete(row.sessionId);
|
||||
} finally {
|
||||
deps.manager.endSessionDeletion(row.sessionId);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,28 @@ export function paginationQuery(c: Context): { offset: number; limit: number } {
|
||||
return { offset, limit };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse OPTIONAL list paging query params: both absent = null (caller returns the full
|
||||
* list — the pre-paging behavior stays intact for existing callers). When paging, `limit`
|
||||
* is required (1-1000) and `offset` optional (>= 0, default 0) — an offset alone would
|
||||
* silently return the full list shifted, which no caller ever means.
|
||||
*/
|
||||
export function optionalPagingQuery(c: Context): { offset: number; limit: number } | null {
|
||||
const rawLimit = c.req.query("limit");
|
||||
const rawOffset = c.req.query("offset");
|
||||
if (rawLimit === undefined && rawOffset === undefined) return null;
|
||||
if (rawLimit === undefined) throw badRequest("offset requires limit.");
|
||||
const limit = Number.parseInt(rawLimit, 10);
|
||||
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
|
||||
throw badRequest("limit must be an integer between 1 and 1000.");
|
||||
}
|
||||
const offset = Number.parseInt(rawOffset ?? "0", 10);
|
||||
if (!Number.isInteger(offset) || offset < 0) {
|
||||
throw badRequest("offset must be a non-negative integer.");
|
||||
}
|
||||
return { offset, limit };
|
||||
}
|
||||
|
||||
/** Read the JSON request body (parse failure / non-object -> 400). */
|
||||
export async function readJson(c: Context): Promise<Record<string, unknown>> {
|
||||
let body: unknown;
|
||||
|
||||
Reference in New Issue
Block a user