refactor: keep imported project understanding sync automatic

This commit is contained in:
kris
2026-04-04 08:43:53 +08:00
parent 432cf97541
commit cf57b5058f
9 changed files with 25 additions and 502 deletions

View File

@@ -13,7 +13,6 @@ let selectImportDraftRoute: (typeof import("../src/app/api/v1/devices/[deviceId]
let reviewImportDraftRoute: (typeof import("../src/app/api/v1/devices/[deviceId]/import-draft/review/route"))["POST"];
let completeMasterTaskRoute: (typeof import("../src/app/api/v1/master-agent/tasks/[taskId]/complete/route"))["POST"];
let applyImportDraftRoute: (typeof import("../src/app/api/v1/devices/[deviceId]/import-draft/apply/route"))["POST"];
let syncDeviceProjectUnderstandingRoute: (typeof import("../src/app/api/v1/devices/[deviceId]/project-understanding-sync/route"))["POST"];
let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"];
let readState: (typeof import("../src/lib/boss-data"))["readState"];
let AUTH_SESSION_COOKIE = "";
@@ -25,7 +24,7 @@ async function setup() {
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const [enrollmentModule, heartbeatModule, importDraftModule, selectModule, reviewModule, completeModule, applyModule, syncModule, data, auth] =
const [enrollmentModule, heartbeatModule, importDraftModule, selectModule, reviewModule, completeModule, applyModule, data, auth] =
await Promise.all([
import("../src/app/api/v1/devices/enrollments/route.ts"),
import("../src/app/api/device-heartbeat/route.ts"),
@@ -34,7 +33,6 @@ async function setup() {
import("../src/app/api/v1/devices/[deviceId]/import-draft/review/route.ts"),
import("../src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts"),
import("../src/app/api/v1/devices/[deviceId]/import-draft/apply/route.ts"),
import("../src/app/api/v1/devices/[deviceId]/project-understanding-sync/route.ts"),
import("../src/lib/boss-data.ts"),
import("../src/lib/boss-auth.ts"),
]);
@@ -46,7 +44,6 @@ async function setup() {
reviewImportDraftRoute = reviewModule.POST;
completeMasterTaskRoute = completeModule.POST;
applyImportDraftRoute = applyModule.POST;
syncDeviceProjectUnderstandingRoute = syncModule.POST;
createAuthSession = data.createAuthSession;
readState = data.readState;
AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE;
@@ -614,220 +611,6 @@ test("imported thread projects queue hidden understanding sync tasks on newer ac
);
});
test("existing imported devices can manually trigger project understanding sync from the device route", async () => {
await setup();
const enrollmentResponse = await createEnrollmentRoute(
await createAuthedRequest("http://127.0.0.1:3000/api/v1/devices/enrollments", "POST", {
name: "Mac Studio Existing",
avatar: "E",
account: "17600003315",
endpoint: "mac://existing.local",
note: "manual project sync",
}),
);
assert.equal(enrollmentResponse.status, 200);
const enrollmentPayload = (await enrollmentResponse.json()) as {
enrollment: { pairingCode: string };
device: { id: string };
};
assert.equal(
(
await deviceHeartbeatRoute(
new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
deviceId: enrollmentPayload.device.id,
pairingCode: enrollmentPayload.enrollment.pairingCode,
name: "Mac Studio Existing",
avatar: "E",
account: "17600003315",
status: "online",
quota5h: 69,
quota7d: 86,
projects: [],
endpoint: "mac://existing.local",
projectCandidates: [
{
folderName: "视觉控制台",
folderRef: "vision-console",
threadId: "thread-vision-console",
threadDisplayName: "视觉控制台主线程",
codexFolderRef: "vision-console",
codexThreadRef: "thread-vision-console",
lastActiveAt: "2026-03-30T12:00:00+08:00",
suggestedImport: true,
},
],
}),
})
)
).status,
200,
);
const draftResponse = await getImportDraftRoute(
await createAuthedRequest(
`http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft`,
"GET",
),
{ params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) },
);
const draftPayload = (await draftResponse.json()) as {
draft: { candidates: Array<{ candidateId: string }> };
};
const selectedCandidateIds = draftPayload.draft.candidates.map((candidate) => candidate.candidateId);
assert.equal(
(
await selectImportDraftRoute(
await createAuthedRequest(
`http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/select`,
"POST",
{ selectedCandidateIds },
),
{ params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) },
)
).status,
200,
);
assert.equal(
(
await reviewImportDraftRoute(
await createAuthedRequest(
`http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/review`,
"POST",
{},
),
{ params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) },
)
).status,
200,
);
let currentState = await readState();
const reviewTask = currentState.masterAgentTasks.find(
(task) =>
task.taskType === "device_import_resolution" &&
task.deviceImportDraftId &&
task.status === "queued",
);
const understandingTask = currentState.masterAgentTasks.find(
(task) =>
task.taskType === "conversation_reply" &&
task.deviceImportDraftId &&
task.deviceImportCandidateId &&
task.status === "queued",
);
assert.ok(reviewTask);
assert.ok(understandingTask);
const reviewCompleteResponse = await completeMasterTaskRoute(
await createAuthedRequest(
`http://127.0.0.1:3000/api/v1/master-agent/tasks/${reviewTask?.taskId}/complete`,
"POST",
{
deviceId: reviewTask?.deviceId,
status: "completed",
replyBody: JSON.stringify(
{
summary: "Mac Studio Existing 导入建议:将视觉控制台主线程导入为独立会话。",
items: selectedCandidateIds.map((candidateId) => ({
candidateId,
action: "create_thread_conversation",
reason: "需要保留独立上下文,建议新建会话。",
})),
},
null,
2,
),
},
),
{ params: Promise.resolve({ taskId: reviewTask?.taskId ?? "" }) },
);
const reviewCompleteRaw = await reviewCompleteResponse.text();
if (reviewCompleteResponse.status !== 200) {
assert.fail(reviewCompleteRaw);
}
assert.equal(
(
await completeMasterTaskRoute(
await createAuthedRequest(
`http://127.0.0.1:3000/api/v1/master-agent/tasks/${understandingTask?.taskId}/complete`,
"POST",
{
deviceId: enrollmentPayload.device.id,
status: "completed",
replyBody: JSON.stringify(
{
projectGoal: "完成视觉控制台与设备实时状态打通。",
currentProgress: "已完成导入,当前项目处于持续联调阶段。",
technicalArchitecture: "Android 前端通过 Boss Web 与 local-agent 对接活跃 Codex 线程。",
currentBlockers: "视觉状态回流还不够实时。",
recommendedNextStep: "继续压缩状态同步延迟。",
},
null,
2,
),
},
),
{ params: Promise.resolve({ taskId: understandingTask?.taskId ?? "" }) },
)
).status,
200,
);
assert.equal(
(
await applyImportDraftRoute(
await createAuthedRequest(
`http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/apply`,
"POST",
{},
),
{ params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) },
)
).status,
200,
);
const syncResponse = await syncDeviceProjectUnderstandingRoute(
await createAuthedRequest(
`http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/project-understanding-sync`,
"POST",
{},
),
{ params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) },
);
const syncRaw = await syncResponse.text();
if (syncResponse.status !== 200) {
assert.fail(syncRaw);
}
const syncPayload = JSON.parse(syncRaw) as {
queuedTasks?: Array<{ projectId: string; threadDisplayName: string }>;
};
assert.equal(syncPayload.queuedTasks?.length, 1);
assert.equal(syncPayload.queuedTasks?.[0]?.threadDisplayName, "视觉控制台主线程");
currentState = await readState();
const importedProject = currentState.projects.find(
(project) => project.threadMeta.codexThreadRef === "thread-vision-console",
);
const manualSyncTask = currentState.masterAgentTasks.find(
(task) =>
task.taskType === "conversation_reply" &&
task.projectId === "master-agent" &&
task.projectUnderstandingTargetProjectId === importedProject?.id &&
task.projectUnderstandingReason === "manual_device_sync" &&
task.status === "queued",
);
assert.ok(manualSyncTask, "expected manual device sync route to queue a hidden understanding task");
});
test("heartbeat candidates no longer auto-create chat windows from legacy projects when import draft is present", async () => {
await setup();