Files
ttstd-infinite-canvas/web/test/agent-interjection.test.ts
ddCat 6892732b06 feat(agent): Agent 工作台 - 计划插话记忆、统一模型选择器并修复摘要与视频入库
将计划清单、运行中插话和个人记忆接入云端 Agent;全站模型选择器统一为左右二级菜单。画布摘要按预算截断不再整轮拒绝,首页视频未知宽高可入库。README 补充贡献者 morrow。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 20:21:25 +08:00

51 lines
3.1 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 { expect, test } from "bun:test";
// 自研(2026-09-17):Agent 运行中插话。
//
// 这条功能的核心是**取消一个此前刻意加上的限制**:运行时输入框本来是禁用的
// (旧代码 `disabled={busy || running || …}`),策略提示词也据此写着「运行中用户无法打字」。
// 所以这里的断言要盯住两件事:入口真的打开了,以及旧前提没有残留在代码里。
test("运行中不再锁死输入框,发送走插话而不是新建轮次", async () => {
const panel = await Bun.file(new URL("../src/components/canvas/canvas-cloud-agent-panel.tsx", import.meta.url)).text();
// 运行中走插话分支(不建轮次、不走那套扣费幂等记账)
expect(panel).toContain("await interject(value)");
expect(panel).toContain("sendAgentInterjection(activeRun.id, { text: value, messageId })");
// 乐观落进时间线时带插话标记,与「开新一轮」的消息在界面上区分开
expect(panel).toContain('interjection: "sent"');
// 输入框的禁用条件里不能再有 running —— 只保留「连不上事件流」「历史/待确认未水合」这类真不能发的状态
expect(panel).toContain('disabled={Boolean(run && connectionStatus !== "connected")');
expect(panel).not.toContain("disabled={busy || running ||");
// 停止按钮由 running 驱动,发送按钮由我们自己的请求驱动,两者不再互斥
expect(panel).toContain("sending={busy}");
expect(panel).toContain("running={running}");
// 两个 SSE 事件都要处理:回显(多标签页)与未送达退回
expect(panel).toContain('event.type === "user_interjection"');
expect(panel).toContain('event.type === "user_interjection_dropped"');
// 退回时要让文案能被再次发送
expect(panel).toContain("setPrompt?.((current) => (current.trim() ? current : text))");
});
test("输入框运行中同时给出「发送(插话)」与「停止」", async () => {
const composer = await Bun.file(new URL("../src/components/canvas/canvas-cloud-agent-chat-ui.tsx", import.meta.url)).text();
// 关键改动:canStop 不再等于 sending —— 否则运行中两个动作只能二选一
expect(composer).toContain("const canStop = Boolean(running && onStop);");
expect(composer).toContain("const canSubmit = !disabled && !sending && Boolean(prompt.trim() || attachments.length);");
expect(composer).not.toContain("const canStop = Boolean(sending && onStop);");
expect(composer).toContain('aria-label="停止本轮"');
// 插话/未送达要有可见标记
expect(composer).toContain('interjection?: "sent" | "undelivered";');
expect(composer).toContain('item.interjection === "undelivered" ? "插话未送达" : "插话"');
});
test("插话走独立接口,不碰轮次接口", async () => {
const api = await Bun.file(new URL("../src/services/api/agent.ts", import.meta.url)).text();
expect(api).toContain("export function sendAgentInterjection(");
expect(api).toContain("/interjections");
expect(api).toContain("messageId: string");
});