重构世界模拟器为模块化配方架构,完善创作编排、会话运行时与 Web UI,并清理过时技能。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-30 00:39:32 +08:00
parent 2b74c30d36
commit e670a5129c
167 changed files with 22955 additions and 5659 deletions

View File

@@ -65,6 +65,8 @@ const server = createServer(async (req, res) => {
"GET /api/presets",
"GET /api/books",
"GET /api/skills",
"GET /api/directors",
"GET /api/modules",
"GET /api/stats/tokens",
],
});
@@ -130,6 +132,104 @@ const server = createServer(async (req, res) => {
return;
}
if (req.method === "POST" && sub === "/answers") {
const body = JSON.parse(await readBody(req)) as {
answers?: Array<{
questionId?: string;
optionId?: string;
text?: string;
}>;
note?: string;
};
const answers = (body.answers ?? [])
.filter(
(a) =>
typeof a?.questionId === "string" &&
a.questionId.trim() &&
typeof a?.text === "string" &&
a.text.trim(),
)
.map((a) => ({
questionId: a.questionId!.trim(),
optionId:
typeof a.optionId === "string" && a.optionId.trim()
? a.optionId.trim()
: undefined,
text: a.text!.trim(),
}));
if (!answers.length) {
json(res, 400, { error: "answers 不能为空" });
return;
}
const note =
typeof body.note === "string" && body.note.trim()
? body.note.trim()
: undefined;
try {
const view = await sessionManager.answerQuestions(
sessionId,
answers,
note,
);
json(res, 200, view);
} catch (err) {
json(res, 400, {
error: err instanceof Error ? err.message : "提交失败",
});
}
return;
}
const messageActionMatch = sub.match(
/^\/messages\/([^/]+)\/(edit|refresh|variant|delete)$/,
);
if (req.method === "POST" && messageActionMatch) {
const messageId = decodeURIComponent(messageActionMatch[1]);
const action = messageActionMatch[2];
const body = JSON.parse(await readBody(req).catch(() => "{}")) as {
text?: string;
direction?: string;
};
let view;
try {
if (action === "edit") {
if (!body.text?.trim()) {
json(res, 400, { error: "text 不能为空" });
return;
}
view = await sessionManager.editMessage(
sessionId,
messageId,
body.text.trim(),
);
} else if (action === "refresh") {
view = await sessionManager.refreshMessage(sessionId, messageId);
} else if (action === "variant") {
if (body.direction !== "prev" && body.direction !== "next") {
json(res, 400, { error: "direction 须为 prev 或 next" });
return;
}
view = await sessionManager.switchMessageVariant(
sessionId,
messageId,
body.direction,
);
} else if (action === "delete") {
view = await sessionManager.deleteMessage(sessionId, messageId);
} else {
json(res, 400, { error: "未知 action" });
return;
}
} catch (err) {
json(res, 400, {
error: err instanceof Error ? err.message : "操作失败",
});
return;
}
json(res, 200, view);
return;
}
if (req.method === "POST" && sub === "/lifecycle") {
const body = JSON.parse(await readBody(req)) as { stage?: string };
if (body.stage !== "design" && body.stage !== "play") {
@@ -147,6 +247,74 @@ const server = createServer(async (req, res) => {
return;
}
if (req.method === "POST" && sub === "/recipe") {
const body = JSON.parse(await readBody(req)) as { recipeId?: string };
if (!body.recipeId?.trim()) {
json(res, 400, { error: "缺少 recipeId" });
return;
}
try {
const view = await sessionManager.setSelectedRecipe(
sessionId,
body.recipeId.trim(),
);
json(res, 200, view);
} catch (err) {
json(res, 400, {
error: err instanceof Error ? err.message : "选定配方失败",
});
}
return;
}
if (req.method === "POST" && sub === "/board") {
const body = JSON.parse(await readBody(req)) as {
tag?: string;
content?: string;
};
if (!body.tag?.trim()) {
json(res, 400, { error: "缺少 tag" });
return;
}
try {
const view = sessionManager.writeUserBoardTag(
sessionId,
body.tag,
body.content ?? "",
);
json(res, 200, view);
} catch (err) {
json(res, 400, {
error: err instanceof Error ? err.message : "写入失败",
});
}
return;
}
if (req.method === "POST" && sub === "/context-traces/prune") {
try {
const result = sessionManager.pruneSessionContextTraces(sessionId);
json(res, 200, { ...result, session: sessionManager.get(sessionId) });
} catch (err) {
json(res, 400, {
error: err instanceof Error ? err.message : "修剪失败",
});
}
return;
}
if (req.method === "POST" && sub === "/context-traces/clear") {
try {
const result = sessionManager.clearSessionContextTraces(sessionId);
json(res, 200, { ...result, session: sessionManager.get(sessionId) });
} catch (err) {
json(res, 400, {
error: err instanceof Error ? err.message : "清除失败",
});
}
return;
}
if (req.method === "POST" && sub === "/actions") {
const body = JSON.parse(await readBody(req)) as { action?: string };
let view;
@@ -160,6 +328,9 @@ const server = createServer(async (req, res) => {
case "accept":
view = await sessionManager.accept(sessionId);
break;
case "skip_questions":
view = await sessionManager.skipQuestions(sessionId);
break;
case "reject":
view = await sessionManager.reject(sessionId);
break;