feat(context): verify product session scope

This commit is contained in:
2026-09-22 10:43:36 +08:00
parent 88b41ba04c
commit 9faed59f23
12 changed files with 358 additions and 56 deletions
+17 -1
View File
@@ -53,6 +53,9 @@ export class SessionMetadataStore {
this.filePath(sessionId),
);
if (existing) {
if (!matchesContext(existing, input)) {
throw new SessionContextMismatchError();
}
return { created: false, record: existing };
}
@@ -80,9 +83,10 @@ export class SessionMetadataStore {
if (!normalizedSessionId) {
return null;
}
return await readJsonFile<SessionRecord>(
const record = await readJsonFile<SessionRecord>(
this.filePath(normalizedSessionId),
);
return record && matchesContext(record, context) ? record : null;
}
async touch(
@@ -132,6 +136,18 @@ const normalizeSessionId = (value?: string) => {
return normalized ? normalized.slice(0, 128) : undefined;
};
export class SessionContextMismatchError extends Error {
constructor() {
super("session not found");
}
}
const matchesContext = (record: SessionRecord, context: SessionMetadataContext) =>
record.actorKey === context.actorKey &&
record.projectKey === context.projectKey &&
(context.userId === undefined || record.ownerUserId === context.userId) &&
(context.projectId === undefined || record.projectId === context.projectId);
const normalizeSessionUpdates = (
updates: Partial<Pick<SessionRecord, "title" | "status">>,
) => {