151 lines
5.0 KiB
TypeScript
151 lines
5.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
|
|
classifyAgentMessage,
|
|
|
|
buildFocus,
|
|
|
|
buildSkillCatalog,
|
|
|
|
buildToolTrace,
|
|
|
|
inferLifecycleStage,
|
|
|
|
canEnterPlay,
|
|
|
|
} from "../src/server/agent-view.js";
|
|
|
|
import { createSession } from "../src/runtime/phase-machine.js";
|
|
|
|
|
|
|
|
describe("classifyAgentMessage", () => {
|
|
|
|
it("parses orchestrator decisions", () => {
|
|
|
|
const m = classifyAgentMessage("[总管] run_worker: 生成规则");
|
|
|
|
expect(m.kind).toBe("orchestrator_decision");
|
|
|
|
expect(m.body).toBe("生成规则");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("parses agent tool calls", () => {
|
|
|
|
const m = classifyAgentMessage("[总管 tool] invoke_worker: write-rules");
|
|
|
|
expect(m.kind).toBe("agent_tool");
|
|
|
|
expect(m.title).toContain("invoke_worker");
|
|
|
|
expect(m.body).toBe("write-rules");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("parses worker output", () => {
|
|
|
|
const m = classifyAgentMessage("[Worker] write-rules 已完成\n\n### rules\n\n1. foo");
|
|
|
|
expect(m.kind).toBe("worker_output");
|
|
|
|
expect(m.actor).toBe("write-rules");
|
|
|
|
expect(m.body).toContain("1. foo");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("parses worker questions", () => {
|
|
|
|
const m = classifyAgentMessage(
|
|
|
|
"[Worker] setup-scenario 提问:\n- 请提供玩家人数",
|
|
|
|
);
|
|
|
|
expect(m.kind).toBe("worker_questions");
|
|
|
|
expect(m.actor).toBe("setup-scenario");
|
|
|
|
expect(m.body).toContain("请提供玩家人数");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("falls back when worker question body is empty", () => {
|
|
|
|
const m = classifyAgentMessage("Worker 提问:\n");
|
|
|
|
expect(m.kind).toBe("worker_questions");
|
|
|
|
expect(m.body).toContain("请补充当前步骤");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("lifecycle", () => {
|
|
|
|
it("infers design before startup completed", () => {
|
|
|
|
const session = createSession();
|
|
|
|
expect(inferLifecycleStage(session)).toBe("design");
|
|
|
|
expect(canEnterPlay(session)).toBe(false);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("infers play after startup completed", () => {
|
|
|
|
const session = {
|
|
|
|
...createSession(),
|
|
|
|
slots: { ...createSession().slots, startupCompleted: true },
|
|
|
|
};
|
|
|
|
expect(inferLifecycleStage(session)).toBe("play");
|
|
|
|
expect(canEnterPlay(session)).toBe(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("respects uiLifecycleStage override", () => {
|
|
|
|
const session = {
|
|
|
|
...createSession(),
|
|
|
|
slots: { ...createSession().slots, uiLifecycleStage: "play" },
|
|
|
|
};
|
|
|
|
expect(inferLifecycleStage(session)).toBe("play");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("buildSkillCatalog", () => {
|
|
|
|
it("lists design skills for world-simulator", () => {
|
|
|
|
const session = createSession();
|
|
|
|
const catalog = buildSkillCatalog(session, "world-simulator", "design");
|
|
|
|
expect(catalog.length).toBeGreaterThan(0);
|
|
|
|
expect(catalog.every((s) => s.stage === "design")).toBe(true);
|
|
|
|
expect(catalog.some((s) => s.id === "interaction-paradigm")).toBe(true);
|
|
|
|
expect(catalog[0].purpose.length).toBeGreaterThan(0);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("marks intake active during intake wait", () => {
|
|
|
|
const session = {
|
|
|
|
...createSession(),
|
|
|
|
waitingReason: { kind: "intake" as const, prompt: "请填写" },
|
|
|
|
};
|
|
|
|
const catalog = buildSkillCatalog(session, "basic", "design");
|
|
|
|
const intake = catalog.find((s) => s.id === "intake");
|
|
|
|
expect(intake?.status).toBe("active");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("buildToolTrace", () => {
|
|
|
|
it("collects agent_tool messages since last user input", () => {
|
|
|
|
const trace = buildToolTrace([
|
|
|
|
{
|
|
|
|
kind: "user_input",
|
|
|
|
text: "hello",
|
|
|
|
createdAt: "2026-01-01T00:00:00Z",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
kind: "agent_tool",
|
|
|
|
text: "[总管 tool] read_blackboard: tags=3",
|
|
|
|
createdAt: "2026-01-01T00:00:01Z",
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
expect(trace).toHaveLength(1);
|
|
|
|
expect(trace[0].name).toBe("read_blackboard");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("buildFocus", () => {
|
|
|
|
it("focuses user on input wait", () => {
|
|
|
|
const session = {
|
|
|
|
...createSession(),
|
|
|
|
phase: "waiting_user" as const,
|
|
|
|
waitingReason: { kind: "input" as const, message: "请描述场景" },
|
|
|
|
};
|
|
|
|
const focus = buildFocus(session, session.waitingReason);
|
|
|
|
expect(focus.actorType).toBe("user");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("focuses user on worker questions", () => {
|
|
|
|
const session = {
|
|
|
|
...createSession(),
|
|
|
|
phase: "waiting_user" as const,
|
|
|
|
waitingReason: {
|
|
|
|
kind: "worker_questions" as const,
|
|
|
|
workerId: "setup-scenario",
|
|
|
|
questions: ["请提供玩家人数"],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const focus = buildFocus(session, session.waitingReason);
|
|
|
|
expect(focus.actorType).toBe("user");
|
|
|
|
expect(focus.action).toContain("setup-scenario");
|
|
|
|
expect(focus.detail).toContain("请提供玩家人数");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("shows design burst when running in design lifecycle", () => {
|
|
|
|
const session = {
|
|
|
|
...createSession(),
|
|
|
|
phase: "running" as const,
|
|
|
|
};
|
|
|
|
const focus = buildFocus(session, undefined, undefined, "design");
|
|
|
|
expect(focus.actorLabel).toBe("Agent");
|
|
|
|
expect(focus.action).toContain("设计");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|