Files
writing-agent/tests/worker-set-view.test.ts

163 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "vitest";
import { parseWorkerSetYaml } from "../src/skills/worker-set-parse.js";
import { formatWorkerSetForUser } from "../src/skills/worker-set-view.js";
const FULL_SAMPLE = `
version: 1
form_summary: 西幻升级交互,带状态跟踪
play_morphology: action_reaction_loop
workers:
- ref: world-simulator
duty: 世界推进与裁决
when: 每轮用户输入后
context:
static:
- 设计.worker集
- 世界.蓝图.确认稿
dynamic:
- 用户.最新输入
- 运行.事件流
outputs:
- 运行.本轮.裁决
- 运行.事件流
- ref: narrator
duty: 组装用户可见回复
when: 中间产物齐后
presentation:
tone: 西幻
avoid: [空泛公告]
context:
static:
- 叙事.指南.确认稿
dynamic:
- 运行.事件流
outputs:
- 输出.用户展示
instantiate_hints:
invoke: [opening-generator]
tag_flow:
- "用户.最新输入 → 运行.本轮.裁决"
- "运行.本轮.裁决 → 输出.用户展示"
`;
describe("formatWorkerSetForUser", () => {
it("builds Chinese worker cards with context order", () => {
const parsed = parseWorkerSetYaml(FULL_SAMPLE)!;
const view = formatWorkerSetForUser(parsed)!;
expect(view.headline).toContain("西幻");
expect(view.playModeLabel).toBe("行动–反应循环");
expect(view.workers).toHaveLength(2);
expect(view.workers[0].displayName).toBe("世界模拟");
expect(view.creationUnits?.map((u) => u.id)).toEqual(
expect.arrayContaining([
"fixed:aesthetics", // sample 含 narrator.presentation
"worker:world-simulator",
"worker:narrator",
]),
);
// 未写入的示例话题不出现(不是填空表)
expect(view.creationUnits?.some((u) => u.id === "fixed:input_protocol")).toBe(
false,
);
expect(view.workers[0].context.staticTags[0].tag).toBe("设计.worker集");
expect(view.workers[0].writes).toEqual(["运行.本轮.裁决", "运行.事件流"]);
expect(view.workers[0].context.explicit).toBe(true);
expect(view.workers[1].presentation?.[0].label).toBe("语气");
expect(view.tagFlow).toHaveLength(2);
expect(view.designTasks).toHaveLength(1);
expect(view.designTasks[0].id).toBe("opening-generator");
});
it("exposes contextTags as standalone cards with mount targets", () => {
const parsed = parseWorkerSetYaml(`
narrative_guide: 残酷、不有求必应
resident_context:
- id: tone
content: 文风克制
mount: [narrator]
workers:
- ref: world-simulator
duty: 裁决
- ref: narrator
duty: 转述
presentation:
tone: 冷峻
`)!;
const view = formatWorkerSetForUser(parsed)!;
expect(view.contextTags?.length).toBeGreaterThanOrEqual(2);
const guide = view.contextTags?.find((c) => c.id === "fixed:narrative_guide");
expect(guide?.mountSummary).toBe("全部 Worker");
expect(guide?.mounts.map((m) => m.workerId).sort()).toEqual([
"narrator",
"world-simulator",
]);
const tone = view.contextTags?.find((c) => c.id === "resident:tone");
expect(tone?.mounts).toHaveLength(1);
expect(tone?.mounts[0].workerId).toBe("narrator");
expect(tone?.mountSummary).toBe(tone!.mounts[0].workerName);
const aesthetics = view.contextTags?.find((c) =>
c.id.startsWith("fixed:aesthetics"),
);
expect(aesthetics?.mounts[0].workerId).toBe("narrator");
expect(view.workers[1].readsSummary).toMatch(/美学|叙事|常驻/);
});
it("surfaces interaction paradigm, core worker, and rationale", () => {
const parsed = parseWorkerSetYaml(`
form_summary: 囚徒困境旁观
interaction_paradigm: 旁观多角(推断)
core_worker: world-simulator
reasoning: 三个角色需信息隔绝,各用 role-decide陈述用 Markdown不必文学转述。
workers:
- ref: world-simulator
role: core
rationale: 裁决组合结果,兼任结构化陈述。
- ref: role-decide
role: auxiliary
rationale: 仅 3 个参与者,隔绝决策上下文。
merge_considered: 不为每个 NPC 各开一个 worker。
`)!;
const view = formatWorkerSetForUser(parsed)!;
expect(view.interactionParadigm).toContain("旁观");
expect(view.coreWorker).toBe("world-simulator");
expect(view.reasoning).toContain("信息隔绝");
expect(view.workers[0].roleLabel).toBe("核心");
expect(view.workers[0].rationale).toContain("裁决");
expect(view.workers[1].mergeConsidered).toContain("NPC");
});
it("merges default context when explicit context omitted", () => {
const parsed = parseWorkerSetYaml(`
workers:
- ref: variable-update
duty: 跟踪变量
when: 世界机之后
`)!;
const view = formatWorkerSetForUser(parsed)!;
expect(view.workers[0].context.staticTags.some((t) => t.tag.includes("变量.目录"))).toBe(
true,
);
expect(view.workers[0].context.explicit).toBe(false);
expect(view.workers[0].writes).toContain("变量.当前");
});
it("marks design tasks filled when blackboard has tags", () => {
const parsed = parseWorkerSetYaml(`
workers:
- ref: narrator
instantiate_hints:
invoke: [opening-generator]
`)!;
const view = formatWorkerSetForUser(parsed, {
filledTags: ["运行.初始变量", "输出.开场白"],
})!;
expect(view.designTasks[0].status).toBe("filled");
});
it("parses context and outputs fields", () => {
const parsed = parseWorkerSetYaml(FULL_SAMPLE)!;
expect(parsed.workers[0].context?.static).toContain("世界.蓝图.确认稿");
expect(parsed.workers[0].outputs).toContain("运行.事件流");
});
});