feat(h5-session): Session Broker、run SSE replay 与 Finish 竞态修复
落地 H5 Session 架构 Patch 1–5(Broker 收口、Router decision、SSE taxonomy、goosed 边界检查), 并新增可选 MEMIND_RUN_STREAM_REPLAY run 事件回放与 H5 假交付 guard;修复 Finish 先于 agent-run gate 导致 UI 永久 loading 的竞态,接入 verify:h5-session-patches 回归脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { buildPageEditAgentPolicy } from './capabilities.mjs';
|
||||
import { buildPageEditSubAgentInstructions } from './mindspace-chat-context.mjs';
|
||||
import { buildMindSpacePageSaveInstructions } from './mindspace-page-patch.mjs';
|
||||
import { reconcileAgentSession } from './session-reconcile.mjs';
|
||||
import { resolveSessionAccess } from './session-broker.mjs';
|
||||
|
||||
const insecureDispatcher = new Agent({
|
||||
connect: { rejectUnauthorized: false },
|
||||
@@ -93,10 +94,12 @@ export function createPageEditSessionService({
|
||||
apiTarget,
|
||||
apiSecret,
|
||||
userAuth,
|
||||
sessionAccess = null,
|
||||
pageService,
|
||||
pageLiveEdit,
|
||||
llmProviderService,
|
||||
}) {
|
||||
const sessionStore = resolveSessionAccess({ userAuth, sessionAccess });
|
||||
const apiFetch = createApiFetch(apiTarget, apiSecret);
|
||||
|
||||
const applySessionLlmProvider = async (sessionId) => {
|
||||
@@ -114,7 +117,7 @@ export function createPageEditSessionService({
|
||||
throw Object.assign(new Error('缺少 fork 参数'), { code: 'invalid_request' });
|
||||
}
|
||||
|
||||
const ownsParent = await userAuth.ownsSession(userId, parentSessionId);
|
||||
const ownsParent = await sessionStore.validateOwnership(userId, parentSessionId);
|
||||
if (!ownsParent) {
|
||||
throw Object.assign(new Error('无权使用父会话'), { code: 'forbidden' });
|
||||
}
|
||||
@@ -152,7 +155,7 @@ export function createPageEditSessionService({
|
||||
throw Object.assign(new Error('Agent 会话启动失败'), { code: 'worker_unavailable' });
|
||||
}
|
||||
|
||||
await userAuth.registerAgentSession(userId, sessionId);
|
||||
await sessionStore.registerAgentSession(userId, sessionId);
|
||||
|
||||
if (sessionPolicy.gooseMode) {
|
||||
await readJson(
|
||||
@@ -229,7 +232,7 @@ export function createPageEditSessionService({
|
||||
throw Object.assign(new Error('缺少 close 参数'), { code: 'invalid_request' });
|
||||
}
|
||||
|
||||
const owns = await userAuth.ownsSession(userId, sessionId);
|
||||
const owns = await sessionStore.validateOwnership(userId, sessionId);
|
||||
if (!owns) {
|
||||
throw Object.assign(new Error('无权关闭该子会话'), { code: 'forbidden' });
|
||||
}
|
||||
@@ -244,7 +247,7 @@ export function createPageEditSessionService({
|
||||
const trimmedSummary = String(summary ?? '').trim();
|
||||
const parentId = String(parentSessionId ?? binding?.parentSessionId ?? '').trim();
|
||||
if (trimmedSummary && parentId) {
|
||||
const ownsParent = await userAuth.ownsSession(userId, parentId);
|
||||
const ownsParent = await sessionStore.validateOwnership(userId, parentId);
|
||||
if (ownsParent) {
|
||||
const page = await pageService.getPage(userId, pageId).catch(() => null);
|
||||
await apiFetch('/agent/harness_remember', {
|
||||
|
||||
Reference in New Issue
Block a user