重构世界模拟器为模块化配方架构,完善创作编排、会话运行时与 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

@@ -0,0 +1,57 @@
import type { SkillIndexEntry, SkillStartupMode, ActiveSkillSnapshot } from "../types/runtime.js";
/** 新建作品时自动加载的能力库(用户不再选 skill 包) */
export const DEFAULT_ORCHESTRATOR_ID = "world-simulator";
/**
* 首屏引导兜底(包内 orchestrator.md `uiPrompt` 优先)。
* 词汇与 design-intake A1/A2 对齐:站位 + 系统扮演/输出/交互。
*/
export const DEFAULT_UI_PROMPT = `请用你自己的话描述想做什么——没有必填项,下面只是帮你找思路的提示。
【你扮演什么】(可对照,也可不按表)
· 单角代入:我就是一个固定角色
· 代理操控:我有角色,但常发 () 指令指挥
· 旁观/实验:我不扮演谁,看或记录推演
· 写手/统筹:我定方向,要成稿或助手式分段(长文 / 爽文也走这条)
· 多角切换:我轮流扮演不同身份
【系统要给你什么】(输出与交互,不是文风问卷)
· 回合对话:你一句,系统回一段可见结果
· 助手分段:先大纲/细纲,你再填表或改设定,再按章/段写正文
· 只要事实摘要 / 要可读叙事 / 要状态表…
【输入约定】可选用括号区分:
· () 圆括号:用户指令/要求,不可写成角色对白
· "" 双引号:角色在世界内说的话
· 【】方括号:角色在世界内的行动
未加标记时默认可视为世界内输入;语义明显是元话语按指令处理。
【核心体验】若愿意可带一句:你最想反复感到的是什么——没有也没关系,我会从描述里察觉。
示例丧尸世界但我不会被感染1v1 网恋;都市爽文先写大纲再按章开写;坠机求生;思想实验旁观三方选择……`;
export function resolveDefaultOrchestratorId(
available: SkillIndexEntry[],
): string {
if (available.some((s) => s.name === DEFAULT_ORCHESTRATOR_ID)) {
return DEFAULT_ORCHESTRATOR_ID;
}
if (available.length === 0) {
throw new Error("registry 中没有可用 orchestrator");
}
return available[0].name;
}
/** 兼容旧快照world-simulator 默认 agent-firstUI 引导 → 用户输入 → Agent 调 Skill */
export function effectiveStartupMode(
skill: Pick<ActiveSkillSnapshot, "name" | "startupMode">,
): SkillStartupMode {
if (skill.startupMode === "agent-first" || skill.startupMode === "intake") {
return skill.startupMode;
}
// 旧 frontmatter design-intake bootstrap 已废弃,等同 agent-first
if (skill.startupMode === "design-intake") return "agent-first";
if (skill.name === DEFAULT_ORCHESTRATOR_ID) return "agent-first";
return "intake";
}

View File

@@ -8,9 +8,15 @@ export type AppSettings = {
version: 1;
activeProfileId: string | null;
activePresetId: string | null;
/**
* 每个会话保留带全量上下文痕迹的消息条数(最新 N 条)。
* 更早的消息仍保留,但去掉 contextTrace 正文。默认 50 表示不存痕迹。
*/
contextTraceKeepLatest?: number;
};
const FILE_NAME = "settings.json";
const DEFAULT_CONTEXT_TRACE_KEEP = 5;
function settingsPath(): string {
return path.join(getUserDataDir(), FILE_NAME);
@@ -21,9 +27,16 @@ function defaultSettings(): AppSettings {
version: 1,
activeProfileId: null,
activePresetId: null,
contextTraceKeepLatest: DEFAULT_CONTEXT_TRACE_KEEP,
};
}
export function normalizeContextTraceKeepLatest(raw: unknown): number {
const n = typeof raw === "number" ? raw : Number(raw);
if (!Number.isFinite(n) || n < 0) return DEFAULT_CONTEXT_TRACE_KEEP;
return Math.min(50, Math.floor(n));
}
export function loadAppSettings(): AppSettings {
ensureUserDataDirs();
try {
@@ -34,6 +47,9 @@ export function loadAppSettings(): AppSettings {
version: 1,
activeProfileId: parsed.activeProfileId ?? null,
activePresetId: parsed.activePresetId ?? null,
contextTraceKeepLatest: normalizeContextTraceKeepLatest(
parsed.contextTraceKeepLatest ?? DEFAULT_CONTEXT_TRACE_KEEP,
),
};
} catch {
return defaultSettings();