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

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-30 00:39:32 +08:00
co-authored by Cursor
parent 2b74c30d36
commit e670a5129c
167 changed files with 22955 additions and 5659 deletions
+235
View File
@@ -0,0 +1,235 @@
import type { Blackboard } from "../blackboard/blackboard.js";
/** 下一 worker / 总管可读的定稿摘要 tag */
export const CONTEXT_BRIEF_TAG = "上下文.定稿摘要";
export type CompressWorkerResult = {
archivedTags: string[];
finals: Array<{ tag: string; chars: number; preview: string }>;
briefText: string;
};
const ALWAYS_ARCHIVE_AFTER_ACCEPT = ["用户.worker答复", "用户.修订说明"];
/**
* Worker 产物经用户验收(或自动完工)后:
* - 终产物打 final
* - 过程/草稿 tag 归档(不再进入后续 worker 取数)
* - 写入定稿摘要,供下一阶段「指点」用
*/
export function compressAfterWorkerAccept(params: {
blackboard: Blackboard;
workerId: string;
outputTags: string[];
summary?: string;
/**
* final = 终稿验收(归档草稿、写定稿摘要)
* unit = 创作单位验收(只归档过程答复,保留 设计.worker集.草稿)
*/
mode?: "final" | "unit";
}): CompressWorkerResult {
const { blackboard, workerId, outputTags, summary } = params;
const mode = params.mode ?? "final";
const finals: CompressWorkerResult["finals"] = [];
const archivedTags: string[] = [];
const uniqueOutputs = [...new Set(outputTags.map((t) => t.trim()).filter(Boolean))];
if (mode === "final") {
for (const tag of uniqueOutputs) {
const content = blackboard.getContentByTag(tag);
if (content == null) continue;
blackboard.write({
tag,
content,
source: workerId,
metadata: {
role: "final",
acceptedAt: new Date().toISOString(),
workerId,
},
});
finals.push({
tag,
chars: content.length,
preview: previewText(content, 120),
});
}
} else {
// 单位验收:草稿保持活跃,终产物列表仅作面板提示
for (const tag of uniqueOutputs) {
if (tag === "用户.需求" || tag === "创作.当前单位") continue;
const content = blackboard.getContentByTag(tag);
if (content == null) continue;
finals.push({
tag,
chars: content.length,
preview: previewText(content, 120),
});
}
}
const toArchive = new Set<string>(ALWAYS_ARCHIVE_AFTER_ACCEPT);
if (mode === "final") {
if (blackboard.getContentByTag("设计.worker集")?.trim()) {
toArchive.add("设计.worker集.草稿");
}
for (const tag of uniqueOutputs) {
if (tag.endsWith(".草稿")) continue;
const draft = `${tag}.草稿`;
if (blackboard.getContentByTag(draft) != null) toArchive.add(draft);
}
}
for (const tag of toArchive) {
if (uniqueOutputs.includes(tag) && mode === "final") continue;
if (mode === "unit" && tag.endsWith(".草稿")) continue;
const content = blackboard.getContentByTag(tag);
if (content == null) continue;
blackboard.write({
tag,
content: `(已压缩归档)原过程内容已折叠。定稿见:${
finals.map((f) => f.tag).join("、") || "(无)"
}\n\n---\n${previewText(content, 400)}`,
source: "compress",
metadata: {
role: "archived",
archivedAt: new Date().toISOString(),
fromWorker: workerId,
compressMode: mode,
},
});
archivedTags.push(tag);
}
const briefText =
mode === "unit"
? buildUnitBriefText({ workerId, summary, finals, archivedTags })
: buildBriefText({
workerId,
summary,
finals,
archivedTags,
});
if (mode === "final") {
blackboard.write({
tag: CONTEXT_BRIEF_TAG,
content: briefText,
source: "compress",
metadata: { role: "final", workerId },
});
}
return { archivedTags, finals, briefText };
}
function buildUnitBriefText(params: {
workerId: string;
summary?: string;
finals: CompressWorkerResult["finals"];
archivedTags: string[];
}): string {
return [
`## 创作单位已验收(${params.workerId})`,
"",
params.summary?.trim() ? `摘要:${params.summary.trim()}` : "摘要:(无)",
"",
"草稿仍保留在 `设计.worker集.草稿`;下一单位继续增量,勿依赖已删的过程对话。",
].join("\n");
}
function buildBriefText(params: {
workerId: string;
summary?: string;
finals: CompressWorkerResult["finals"];
archivedTags: string[];
}): string {
const lines = [
`## 上一阶段定稿(${params.workerId})`,
"",
params.summary?.trim()
? `摘要:${params.summary.trim()}`
: "摘要:(无)",
"",
"### 保留的终产物 tag",
];
if (params.finals.length === 0) {
lines.push("- (无)");
} else {
for (const f of params.finals) {
lines.push(`- \`${f.tag}\`(${f.chars} 字)`);
lines.push(` ${f.preview}`);
}
}
if (params.archivedTags.length) {
lines.push("", "### 已压缩的过程 tag");
for (const t of params.archivedTags) {
lines.push(`- \`${t}\`(归档,后续 worker 默认不读)`);
}
}
lines.push(
"",
"下一 worker 应以以上终产物为准继续;勿依赖已归档的过程讨论。",
);
return lines.join("\n");
}
export function previewText(content: string, max: number): string {
const t = content.replace(/\s+/g, " ").trim();
if (t.length <= max) return t;
return `${t.slice(0, max)}…`;
}
/** 供 UI:黑板可读条目(默认隐藏 archived 全文,只给索引) */
export function buildBoardPanel(blackboard: Blackboard): {
finals: Array<{ tag: string; source: string; preview: string; updatedAt: string }>;
active: Array<{ tag: string; source: string; preview: string; updatedAt: string }>;
archivedCount: number;
brief?: string;
} {
const items = latestItemsByTag(blackboard.exportItems());
const finals: Array<{
tag: string;
source: string;
preview: string;
updatedAt: string;
}> = [];
const active: typeof finals = [];
let archivedCount = 0;
let brief: string | undefined;
for (const item of items.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))) {
const role = String(item.metadata?.role ?? "");
if (item.tag === CONTEXT_BRIEF_TAG) {
brief = item.content;
continue;
}
if (role === "archived") {
archivedCount += 1;
continue;
}
const row = {
tag: item.tag,
source: item.source,
preview: previewText(item.content, 160),
updatedAt: item.updatedAt,
};
if (role === "final") finals.push(row);
else active.push(row);
}
return { finals, active, archivedCount, brief };
}
function latestItemsByTag(
items: import("../types/blackboard.js").BlackboardItem[],
): import("../types/blackboard.js").BlackboardItem[] {
const byTag = new Map<string, (typeof items)[0]>();
for (const item of items) {
const prev = byTag.get(item.tag);
if (!prev || item.updatedAt > prev.updatedAt) byTag.set(item.tag, item);
}
return [...byTag.values()];
}