重构世界模拟器为模块化配方架构,完善创作编排、会话运行时与 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
+146 -25
View File
@@ -1,46 +1,167 @@
import type { PresetPackage, PresetPromptRole } from "../types/preset.js";
import type {
PresetPackage,
PresetPromptEntry,
PresetPromptRole,
} from "../types/preset.js";
import { savePreset } from "./store.js";
export type PresetEnabledEntryView = {
export type PresetEntryView = {
orderIndex: number;
id: string;
name: string;
role: PresetPromptRole;
marker: boolean;
content: string;
/** 实际会注入 LLM 请求(有非空 content) */
/** prompt_order 与条目自身均启用 */
enabled: boolean;
orderEnabled: boolean;
entryEnabled: boolean;
/** 启用且有非空 content → 会注入 LLM */
willInject: boolean;
};
/** 按 prompt_order 列出所有启用条目及其内容 */
export function listEnabledPresetEntries(
preset: PresetPackage,
): PresetEnabledEntryView[] {
/** @deprecated 名称保留:现为全部条目;过滤启用请用 filter */
export type PresetEnabledEntryView = PresetEntryView;
function sortOrder(preset: PresetPackage) {
return [...preset.promptOrder].sort((a, b) => a.orderIndex - b.orderIndex);
}
function toView(
orderIndex: number,
entry: PresetPromptEntry,
orderEnabled: boolean,
): PresetEntryView {
const content = entry.content ?? "";
const enabled = orderEnabled && entry.enabled;
const trimmed = content.trim();
return {
orderIndex,
id: entry.id,
name: entry.name,
role: entry.role,
marker: entry.marker,
content,
enabled,
orderEnabled,
entryEnabled: entry.enabled,
willInject: enabled && trimmed.length > 0,
};
}
/** 按 prompt_order 列出全部条目(含未启用),便于设置页编辑 */
export function listAllPresetEntries(preset: PresetPackage): PresetEntryView[] {
const promptById = new Map(preset.prompts.map((p) => [p.id, p]));
const ordered = [...preset.promptOrder].sort(
(a, b) => a.orderIndex - b.orderIndex,
);
const entries: PresetEnabledEntryView[] = [];
const seen = new Set<string>();
const entries: PresetEntryView[] = [];
for (const orderItem of ordered) {
if (!orderItem.enabled) continue;
for (const orderItem of sortOrder(preset)) {
const entry = promptById.get(orderItem.promptId);
if (!entry || !entry.enabled) continue;
if (!entry) continue;
seen.add(entry.id);
entries.push(toView(orderItem.orderIndex, entry, orderItem.enabled));
}
const content = entry.content.trim();
entries.push({
orderIndex: orderItem.orderIndex,
id: entry.id,
name: entry.name,
role: entry.role,
marker: entry.marker,
content,
willInject: content.length > 0,
});
// 未进 order 的条目附在末尾
let nextIndex =
entries.reduce((m, e) => Math.max(m, e.orderIndex), -1) + 1;
for (const entry of preset.prompts) {
if (seen.has(entry.id)) continue;
entries.push(toView(nextIndex++, entry, false));
}
return entries;
}
export function countInjectingEntries(entries: PresetEnabledEntryView[]): number {
/** 仅启用中的条目(装配 / 旧 API 兼容) */
export function listEnabledPresetEntries(
preset: PresetPackage,
): PresetEntryView[] {
return listAllPresetEntries(preset).filter((e) => e.enabled);
}
export function countInjectingEntries(
entries: Array<{ willInject: boolean }>,
): number {
return entries.filter((e) => e.willInject).length;
}
export function countEnabledEntries(
entries: Array<{ enabled: boolean }>,
): number {
return entries.filter((e) => e.enabled).length;
}
export type PresetEntryPatch = {
id: string;
enabled?: boolean;
content?: string;
name?: string;
role?: PresetPromptRole;
};
/**
* 纯函数更新(不写盘)。启用状态同步 order + entry。
*/
export function applyPresetEntryPatches(
preset: PresetPackage,
patches: PresetEntryPatch[],
): PresetPackage {
if (!patches.length) return preset;
const prompts = preset.prompts.map((p) => ({ ...p }));
const promptById = new Map(prompts.map((p) => [p.id, p]));
let order = preset.promptOrder.map((o) => ({ ...o }));
for (const patch of patches) {
const entry = promptById.get(patch.id);
if (!entry) {
throw new Error(`条目不存在: ${patch.id}`);
}
if (typeof patch.content === "string") {
entry.content = patch.content;
if (entry.content.trim()) entry.marker = false;
}
if (typeof patch.name === "string" && patch.name.trim()) {
entry.name = patch.name.trim();
}
if (
patch.role === "system" ||
patch.role === "user" ||
patch.role === "assistant"
) {
entry.role = patch.role;
}
if (typeof patch.enabled === "boolean") {
entry.enabled = patch.enabled;
const orderItem = order.find((o) => o.promptId === patch.id);
if (orderItem) {
orderItem.enabled = patch.enabled;
} else if (patch.enabled) {
const orderIndex =
order.reduce((m, o) => Math.max(m, o.orderIndex), -1) + 1;
order.push({
promptId: patch.id,
enabled: true,
orderIndex,
});
}
}
}
return {
...preset,
prompts,
promptOrder: order,
};
}
/**
* 更新一条或多条并落盘。
*/
export function patchPresetEntries(
preset: PresetPackage,
patches: PresetEntryPatch[],
): PresetPackage {
return savePreset(applyPresetEntryPatches(preset, patches));
}