引入上下文片段、固定槽与投影排序,并完善机遇裁定与游玩期 UI。
把创作产物收敛为可挂载片段与 play_slots/context_order,同步修订世界模拟器模块与运行时拼装。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -27,7 +27,8 @@ const WORKER_LABELS: Record<string, string> = {
|
||||
"agent-burst": "编排器调度",
|
||||
narrator: "叙事转述",
|
||||
"role-decide": "角色决策",
|
||||
"world-simulator": "世界推演",
|
||||
"world-simulator": "主世界层",
|
||||
chance: "机遇裁定",
|
||||
"round-present": "回合呈现",
|
||||
outline: "大纲 / 细纲",
|
||||
"chapter-writer": "章节正文",
|
||||
@@ -36,7 +37,7 @@ const WORKER_LABELS: Record<string, string> = {
|
||||
const FIXED_TOPIC_LABELS: Record<string, string> = {
|
||||
"aesthetics-interaction": "美学纲领与交互范式",
|
||||
interaction: "交互范式",
|
||||
narrative_guide: "叙事指南",
|
||||
narrative_guide: "叙事指南与故事推进",
|
||||
input_protocol: "输入协议",
|
||||
core_premise: "核心前提",
|
||||
aesthetics: "美学纲领",
|
||||
|
||||
@@ -67,6 +67,15 @@ import {
|
||||
formatWorkerSetForUser,
|
||||
type WorkerSetUserView,
|
||||
} from "../skills/worker-set-view.js";
|
||||
import {
|
||||
CONTEXT_ORDER_TAG,
|
||||
applyContextOrderEdit,
|
||||
mergeContextOrderIntoWorkerSetJson,
|
||||
parseContextOrder,
|
||||
serializeContextOrder,
|
||||
synthesizeContextOrderFromWorkers,
|
||||
type ContextOrderEdit,
|
||||
} from "../skills/context-order.js";
|
||||
import {
|
||||
CREATION_FLOW_TAG,
|
||||
CREATION_SELECTED_RECIPE_TAG,
|
||||
@@ -292,6 +301,80 @@ export class SessionManager {
|
||||
return this.toView(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编排上下文投影排序:写入 设计.worker集.context_order(若有规格),
|
||||
* 并同步 设计.上下文投影排序。下次声明驱动拼装即按新序。
|
||||
*/
|
||||
patchContextOrder(sessionId: string, edit: ContextOrderEdit): SessionView {
|
||||
const s = this.require(sessionId);
|
||||
const bb = s.runtime.getBlackboard();
|
||||
const workerSetRaw =
|
||||
bb.getContentByTag("设计.worker集")?.trim() ||
|
||||
bb.getContentByTag("设计.worker集.草稿")?.trim() ||
|
||||
"";
|
||||
const orderTagRaw = bb.getContentByTag(CONTEXT_ORDER_TAG)?.trim() || "";
|
||||
|
||||
let current = parseContextOrder(
|
||||
workerSetRaw
|
||||
? parseWorkerSetYaml(workerSetRaw).context_order
|
||||
: undefined,
|
||||
);
|
||||
if (!current) current = parseContextOrder(orderTagRaw);
|
||||
if (!current && workerSetRaw) {
|
||||
const parsed = parseWorkerSetYaml(workerSetRaw);
|
||||
// 与检查器合成逻辑一致:缺 context 时用包内默认契约(视图侧也会合成)
|
||||
current = synthesizeContextOrderFromWorkers(
|
||||
parsed.workers.map((w) => ({
|
||||
ref: w.ref,
|
||||
name: w.name,
|
||||
context: w.context,
|
||||
})),
|
||||
parsed.play_slots,
|
||||
);
|
||||
}
|
||||
if (!current && edit.action !== "replace") {
|
||||
throw new Error("尚无上下文投影排序可编辑;请先完成游玩拓扑/细化终稿,或提交完整 replace");
|
||||
}
|
||||
if (!current && edit.action === "replace") {
|
||||
current = parseContextOrder(edit.context_order);
|
||||
if (!current) throw new Error("context_order 无法解析");
|
||||
}
|
||||
|
||||
const next = applyContextOrderEdit(current!, edit);
|
||||
const serialized = serializeContextOrder(next);
|
||||
|
||||
bb.write({
|
||||
tag: CONTEXT_ORDER_TAG,
|
||||
content: serialized,
|
||||
source: "user",
|
||||
});
|
||||
|
||||
if (workerSetRaw) {
|
||||
const targetTag = bb.getContentByTag("设计.worker集")?.trim()
|
||||
? "设计.worker集"
|
||||
: bb.getContentByTag("设计.worker集.草稿")?.trim()
|
||||
? "设计.worker集.草稿"
|
||||
: null;
|
||||
if (targetTag) {
|
||||
bb.write({
|
||||
tag: targetTag,
|
||||
content: mergeContextOrderIntoWorkerSetJson(workerSetRaw, next),
|
||||
source: "user",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 尚无规格:至少落下排序表,供细化终稿合并
|
||||
bb.write({
|
||||
tag: CONTEXT_ORDER_TAG,
|
||||
content: serialized,
|
||||
source: "user",
|
||||
});
|
||||
}
|
||||
|
||||
if (s.bookId) this.persist(s);
|
||||
return this.toView(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开 Book:优先恢复磁盘快照;无快照则新建 Session。
|
||||
* 若该 Book 已在内存中,直接返回当前视图。
|
||||
|
||||
@@ -291,6 +291,96 @@ const server = createServer(async (req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "POST" && sub === "/context-order") {
|
||||
const body = JSON.parse(await readBody(req)) as {
|
||||
action?: string;
|
||||
slotRef?: string;
|
||||
index?: number;
|
||||
delta?: number;
|
||||
anchor?: string;
|
||||
projection?: string;
|
||||
context_order?: unknown;
|
||||
};
|
||||
try {
|
||||
const action = body.action?.trim();
|
||||
if (action === "replace") {
|
||||
const view = sessionManager.patchContextOrder(sessionId, {
|
||||
action: "replace",
|
||||
context_order: body.context_order,
|
||||
});
|
||||
json(res, 200, view);
|
||||
return;
|
||||
}
|
||||
if (action === "move") {
|
||||
const delta = body.delta === 1 || body.delta === -1 ? body.delta : 0;
|
||||
if (!body.slotRef?.trim() || typeof body.index !== "number" || !delta) {
|
||||
json(res, 400, { error: "move 需要 slotRef、index、delta(±1)" });
|
||||
return;
|
||||
}
|
||||
const view = sessionManager.patchContextOrder(sessionId, {
|
||||
action: "move",
|
||||
slotRef: body.slotRef.trim(),
|
||||
index: body.index,
|
||||
delta,
|
||||
});
|
||||
json(res, 200, view);
|
||||
return;
|
||||
}
|
||||
if (action === "set_anchor") {
|
||||
if (
|
||||
!body.slotRef?.trim() ||
|
||||
typeof body.index !== "number" ||
|
||||
(body.anchor !== "pre_history" && body.anchor !== "post_history")
|
||||
) {
|
||||
json(res, 400, {
|
||||
error: "set_anchor 需要 slotRef、index、anchor(pre_history|post_history)",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const view = sessionManager.patchContextOrder(sessionId, {
|
||||
action: "set_anchor",
|
||||
slotRef: body.slotRef.trim(),
|
||||
index: body.index,
|
||||
anchor: body.anchor,
|
||||
});
|
||||
json(res, 200, view);
|
||||
return;
|
||||
}
|
||||
if (action === "set_projection") {
|
||||
const proj = body.projection;
|
||||
if (
|
||||
!body.slotRef?.trim() ||
|
||||
typeof body.index !== "number" ||
|
||||
(proj !== "fixed" &&
|
||||
proj !== "full" &&
|
||||
proj !== "summary" &&
|
||||
proj !== "fields")
|
||||
) {
|
||||
json(res, 400, {
|
||||
error: "set_projection 需要 slotRef、index、projection",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const view = sessionManager.patchContextOrder(sessionId, {
|
||||
action: "set_projection",
|
||||
slotRef: body.slotRef.trim(),
|
||||
index: body.index,
|
||||
projection: proj,
|
||||
});
|
||||
json(res, 200, view);
|
||||
return;
|
||||
}
|
||||
json(res, 400, {
|
||||
error: "action 须为 move | set_anchor | set_projection | replace",
|
||||
});
|
||||
} 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);
|
||||
|
||||
Reference in New Issue
Block a user