chore: checkpoint Boss app v2.5.11
This commit is contained in:
@@ -1592,6 +1592,123 @@ test("POST /api/v1/master-agent/tasks/[taskId]/complete folds thread commentary
|
||||
assert.equal(updatedProject?.unreadCount, 1);
|
||||
});
|
||||
|
||||
test("POST /api/v1/master-agent/tasks/[taskId]/complete strips already mirrored process text from aggregate thread replies", async () => {
|
||||
await setup();
|
||||
const singleProject = await ensureSingleThreadProject();
|
||||
assert.ok(singleProject, "expected a seeded single-thread project");
|
||||
await resetThreadExecutionState(singleProject.id);
|
||||
await setProjectTakeover(singleProject.id, false);
|
||||
|
||||
const sendResponse = await postMessageRoute(
|
||||
await createAuthedRequest(
|
||||
`http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`,
|
||||
"POST",
|
||||
{ body: "只要不对原有项目有任何影响。你按最推荐的方式做。" },
|
||||
),
|
||||
{ params: Promise.resolve({ projectId: singleProject.id }) },
|
||||
);
|
||||
const sendPayload = (await sendResponse.json()) as { task?: { taskId: string } };
|
||||
|
||||
const queuedState = await readState();
|
||||
const task = queuedState.masterAgentTasks.find(
|
||||
(item) => item.taskId === sendPayload.task?.taskId,
|
||||
);
|
||||
assert.ok(task, "expected a queued conversation_reply task");
|
||||
|
||||
const processOne = "我先按非侵入方式收口:不碰原项目代码,只把这次 APP 端问题整理成可复现、可提交的诊断文档。";
|
||||
const processTwo = "我在按调试流程收证据,但会收在文档里,不会动任何现有项目文件。";
|
||||
const processThree = "文档已经落下来了,我再做一次范围确认,确保只有独立报告被新增。";
|
||||
const finalText = "我按非侵入方式处理了,没有碰任何原有项目代码,只新增了一份独立排查文档。";
|
||||
const aggregateReply = `${processOne}${processTwo}${processThree}${finalText}`;
|
||||
const requestedAtMs = Date.parse(task.requestedAt);
|
||||
assert.ok(Number.isFinite(requestedAtMs), "expected task requestedAt to be parseable");
|
||||
const taskRelativeTime = (offsetMs: number) => new Date(requestedAtMs + offsetMs).toISOString();
|
||||
|
||||
const mirroredState = await readState();
|
||||
const project = mirroredState.projects.find((item) => item.id === singleProject.id);
|
||||
assert.ok(project, "expected the single-thread project to exist");
|
||||
project.messages = project.messages.filter(
|
||||
(message) =>
|
||||
message.id === task.requestMessageId ||
|
||||
message.executionProgress?.taskId === task.taskId,
|
||||
);
|
||||
project.messages.push(
|
||||
{
|
||||
id: "msg-process-one",
|
||||
sender: "device",
|
||||
senderLabel: project.threadMeta.threadDisplayName,
|
||||
body: processOne,
|
||||
sentAt: taskRelativeTime(1_000),
|
||||
kind: "thread_process",
|
||||
},
|
||||
{
|
||||
id: "msg-process-two",
|
||||
sender: "device",
|
||||
senderLabel: project.threadMeta.threadDisplayName,
|
||||
body: processTwo,
|
||||
sentAt: taskRelativeTime(2_000),
|
||||
kind: "thread_process",
|
||||
},
|
||||
{
|
||||
id: "msg-process-three",
|
||||
sender: "device",
|
||||
senderLabel: project.threadMeta.threadDisplayName,
|
||||
body: processThree,
|
||||
sentAt: taskRelativeTime(3_000),
|
||||
kind: "thread_process",
|
||||
},
|
||||
{
|
||||
id: "msg-final-mirrored",
|
||||
sender: "device",
|
||||
senderLabel: project.threadMeta.threadDisplayName,
|
||||
body: finalText,
|
||||
sentAt: taskRelativeTime(4_000),
|
||||
kind: "text",
|
||||
},
|
||||
);
|
||||
project.preview = finalText;
|
||||
project.lastMessageAt = taskRelativeTime(4_000);
|
||||
project.unreadCount = 1;
|
||||
await writeState(mirroredState);
|
||||
|
||||
const response = await completeMasterTaskRoute(
|
||||
await createAuthedRequest(
|
||||
`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/complete`,
|
||||
"POST",
|
||||
{
|
||||
deviceId: task.deviceId,
|
||||
status: "completed",
|
||||
targetProjectId: singleProject.id,
|
||||
targetThreadId: singleProject.threadMeta.threadId,
|
||||
replyBody: aggregateReply,
|
||||
},
|
||||
),
|
||||
{ params: Promise.resolve({ taskId: task.taskId }) },
|
||||
);
|
||||
assert.equal(response.status, 200);
|
||||
|
||||
const nextState = await readState();
|
||||
const updatedProject = nextState.projects.find((item) => item.id === singleProject.id);
|
||||
const aggregateMessages =
|
||||
updatedProject?.messages.filter((message) => message.body === aggregateReply) ?? [];
|
||||
const finalMessages = updatedProject?.messages.filter((message) => message.body === finalText) ?? [];
|
||||
|
||||
assert.equal(aggregateMessages.length, 0, "aggregate process+final reply should not be displayed");
|
||||
assert.equal(finalMessages.length, 1, "already mirrored final result should not be duplicated");
|
||||
assert.equal(updatedProject?.preview, finalText);
|
||||
assert.equal(updatedProject?.unreadCount, 1);
|
||||
|
||||
const cleanupState = await readState();
|
||||
const cleanupProject = cleanupState.projects.find((item) => item.id === singleProject.id);
|
||||
if (cleanupProject) {
|
||||
cleanupProject.messages = [];
|
||||
cleanupProject.preview = "测试线程等待继续处理。";
|
||||
cleanupProject.lastMessageAt = "2026-04-04T11:30:00+08:00";
|
||||
cleanupProject.unreadCount = 0;
|
||||
}
|
||||
await writeState(cleanupState);
|
||||
});
|
||||
|
||||
test("POST /api/v1/master-agent/tasks/[taskId]/complete keeps compact numbered progress updates folded", async () => {
|
||||
await setup();
|
||||
const singleProject = await ensureSingleThreadProject();
|
||||
@@ -1649,7 +1766,7 @@ test("POST /api/v1/master-agent/tasks/[taskId]/complete keeps compact numbered p
|
||||
assert.equal(updatedProject?.unreadCount, 0);
|
||||
});
|
||||
|
||||
test("device heartbeat keeps conversation preview on the latest non-process message", async () => {
|
||||
test("device heartbeat activity does not overwrite conversation preview with desktop process text", async () => {
|
||||
await setup();
|
||||
const singleProject = await ensureSingleThreadProject();
|
||||
assert.ok(singleProject, "expected a seeded single-thread project");
|
||||
@@ -1713,12 +1830,14 @@ test("device heartbeat keeps conversation preview on the latest non-process mess
|
||||
(message) => message.externalMessageId === "codex-thread:preview-keep:2026-04-24T05:41:14.246Z:p1",
|
||||
);
|
||||
|
||||
assert.equal(processMessage?.kind, "thread_process");
|
||||
assert.equal(processMessage, undefined);
|
||||
assert.equal(updatedProject?.messages.length, 1);
|
||||
assert.equal(updatedProject?.preview, "这是上一轮最终结果。");
|
||||
assert.equal(updatedProject?.unreadCount, 0);
|
||||
assert.equal(updatedProject?.threadMeta.lastObservedCodexActivityAt, "2026-04-24T05:41:14.246Z");
|
||||
});
|
||||
|
||||
test("device heartbeat keeps conversation preview blank when only process messages are mirrored", async () => {
|
||||
test("device heartbeat activity clears stale process preview without appending desktop process text", async () => {
|
||||
await setup();
|
||||
const singleProject = await ensureSingleThreadProject();
|
||||
assert.ok(singleProject, "expected a seeded single-thread project");
|
||||
@@ -1773,9 +1892,11 @@ test("device heartbeat keeps conversation preview blank when only process messag
|
||||
(message) => message.externalMessageId === "codex-thread:preview-empty:2026-04-24T05:41:14.246Z:p1",
|
||||
);
|
||||
|
||||
assert.equal(processMessage?.kind, "thread_process");
|
||||
assert.equal(processMessage, undefined);
|
||||
assert.equal(updatedProject?.messages.length, 0);
|
||||
assert.equal(updatedProject?.preview, "");
|
||||
assert.equal(updatedProject?.unreadCount, 0);
|
||||
assert.equal(updatedProject?.threadMeta.lastObservedCodexActivityAt, "2026-04-24T05:41:14.246Z");
|
||||
});
|
||||
|
||||
test("legacy device process text is reclassified and no longer pollutes preview or unread", async () => {
|
||||
|
||||
Reference in New Issue
Block a user