6ee6fd64dd
Introduce page edit sessions with draft preview and patch API, chat skill picker, user memory profile, h5ApiBase resolution, voice WAV transport, and scripts for 105/g2 deployment and Plaza local dev. Co-authored-by: Cursor <cursoragent@cursor.com>
105 lines
3.5 KiB
JavaScript
105 lines
3.5 KiB
JavaScript
import { mergeMindSpacePagePatch, normalizeMindSpacePagePatchInput } from './mindspace-page-patch.mjs';
|
|
|
|
export function createPageLiveEditService({ pageService, resolveUserIdForAgentSession }) {
|
|
/** @type {Map<string, { userId: string, pageId: string, boundAt: number }>} */
|
|
const sessionBindings = new Map();
|
|
/** @type {Map<string, number>} */
|
|
const liveRevisions = new Map();
|
|
|
|
const bumpRevision = (pageId) => {
|
|
liveRevisions.set(pageId, (liveRevisions.get(pageId) ?? 0) + 1);
|
|
};
|
|
|
|
return {
|
|
bindSession({ userId, sessionId, pageId, parentSessionId = null }) {
|
|
if (!userId || !sessionId || !pageId) {
|
|
throw Object.assign(new Error('缺少绑定参数'), { code: 'invalid_request' });
|
|
}
|
|
sessionBindings.set(String(sessionId), {
|
|
userId: String(userId),
|
|
pageId: String(pageId),
|
|
parentSessionId: parentSessionId ? String(parentSessionId) : null,
|
|
boundAt: Date.now(),
|
|
});
|
|
return {
|
|
sessionId: String(sessionId),
|
|
pageId: String(pageId),
|
|
...(parentSessionId ? { parentSessionId: String(parentSessionId) } : {}),
|
|
};
|
|
},
|
|
|
|
unbindSession(sessionId) {
|
|
sessionBindings.delete(String(sessionId));
|
|
},
|
|
|
|
getBinding(sessionId) {
|
|
return sessionBindings.get(String(sessionId)) ?? null;
|
|
},
|
|
|
|
getLiveRevision(pageId) {
|
|
return liveRevisions.get(String(pageId)) ?? 0;
|
|
},
|
|
|
|
async applyAgentPatch(input) {
|
|
const sessionId = String(input?.sessionId ?? input?.session_id ?? '').trim();
|
|
const pageId = String(input?.pageId ?? input?.page_id ?? '').trim();
|
|
if (!sessionId || !pageId) {
|
|
throw Object.assign(new Error('缺少 session_id 或 page_id'), { code: 'invalid_request' });
|
|
}
|
|
|
|
const userId = await resolveUserIdForAgentSession(sessionId);
|
|
if (!userId) {
|
|
throw Object.assign(new Error('无效的 Agent 会话'), { code: 'forbidden' });
|
|
}
|
|
|
|
const binding = sessionBindings.get(sessionId);
|
|
if (binding && binding.pageId !== pageId) {
|
|
throw Object.assign(new Error('当前会话未绑定该页面'), { code: 'page_binding_mismatch' });
|
|
}
|
|
if (binding && binding.userId !== userId) {
|
|
throw Object.assign(new Error('会话与用户不匹配'), { code: 'forbidden' });
|
|
}
|
|
|
|
const patch = normalizeMindSpacePagePatchInput(input);
|
|
if (!patch) {
|
|
throw Object.assign(new Error('缺少 title / summary / content 变更'), { code: 'invalid_request' });
|
|
}
|
|
|
|
const page = await pageService.getPage(userId, pageId);
|
|
const merged = mergeMindSpacePagePatch(
|
|
{
|
|
title: page.title,
|
|
summary: page.summary ?? '',
|
|
content: page.content ?? '',
|
|
},
|
|
patch,
|
|
);
|
|
|
|
const updated = await pageService.updatePage(userId, pageId, {
|
|
expectedVersion: input?.expectedVersion ?? input?.expected_version ?? page.versionNo,
|
|
title: merged.title,
|
|
summary: merged.summary,
|
|
content: merged.content,
|
|
templateId: page.templateId,
|
|
changeNote: String(input?.changeNote ?? input?.change_note ?? 'Agent 对话修改').slice(0, 255),
|
|
});
|
|
|
|
bumpRevision(pageId);
|
|
return {
|
|
page: updated,
|
|
liveRevision: liveRevisions.get(pageId) ?? 0,
|
|
};
|
|
},
|
|
|
|
async getRevisionSnapshot(userId, pageId) {
|
|
const page = await pageService.getPage(userId, pageId);
|
|
return {
|
|
pageId: page.id,
|
|
versionNo: page.versionNo,
|
|
updatedAt: page.updatedAt,
|
|
liveRevision: liveRevisions.get(pageId) ?? 0,
|
|
};
|
|
},
|
|
};
|
|
}
|