refactor: remove seeded boss console conversation
This commit is contained in:
@@ -203,7 +203,7 @@ public class MasterAgentMemoryActivity extends BossScreenActivity {
|
||||
|
||||
final EditText titleInput = BossUi.buildInput(this, "记忆标题", false);
|
||||
final EditText contentInput = BossUi.buildInput(this, "记忆内容", true);
|
||||
final EditText projectIdInput = BossUi.buildInput(this, "例如:boss-console", false);
|
||||
final EditText projectIdInput = BossUi.buildInput(this, "例如:wenshenapp", false);
|
||||
final EditText tagsInput = BossUi.buildInput(this, "标签,逗号分隔", false);
|
||||
contentInput.setMinLines(6);
|
||||
|
||||
|
||||
@@ -1880,7 +1880,7 @@ export function DeviceEnrollmentBuilder() {
|
||||
const [name, setName] = useState("Mac Mini");
|
||||
const [avatar, setAvatar] = useState("M");
|
||||
const [account, setAccount] = useState("17600003315");
|
||||
const [projects, setProjects] = useState("Boss 移动控制台");
|
||||
const [projects, setProjects] = useState("");
|
||||
const [endpoint, setEndpoint] = useState("mac://new-device.local");
|
||||
const [note, setNote] = useState("新设备待绑定");
|
||||
const [result, setResult] = useState<{
|
||||
|
||||
@@ -549,7 +549,7 @@ export function MasterAgentPromptMemoryClient({
|
||||
label="projectId"
|
||||
value={newMemory.projectId}
|
||||
onChange={(value) => setNewMemory((current) => ({ ...current, projectId: value }))}
|
||||
placeholder="例如 boss-console"
|
||||
placeholder="例如 wenshenapp"
|
||||
/>
|
||||
) : null}
|
||||
<Field
|
||||
|
||||
@@ -1038,7 +1038,7 @@ const initialState: BossState = {
|
||||
account: PRIMARY_ADMIN_ACCOUNT,
|
||||
source: "production",
|
||||
status: "online",
|
||||
projects: ["Boss 移动控制台", "硬件审计协作"],
|
||||
projects: ["硬件审计协作"],
|
||||
quota5h: 68,
|
||||
quota7d: 81,
|
||||
lastSeenAt: "2026-03-25T11:52:00+08:00",
|
||||
@@ -1053,7 +1053,7 @@ const initialState: BossState = {
|
||||
account: "kris.plus.gpu",
|
||||
source: "demo",
|
||||
status: "abnormal",
|
||||
projects: ["Boss 移动控制台", "硬件审计协作"],
|
||||
projects: ["硬件审计协作"],
|
||||
quota5h: 31,
|
||||
quota7d: 46,
|
||||
lastSeenAt: "2026-03-25T11:40:00+08:00",
|
||||
@@ -1068,7 +1068,7 @@ const initialState: BossState = {
|
||||
account: "kris.plus.backup",
|
||||
source: "demo",
|
||||
status: "offline",
|
||||
projects: ["Boss 移动控制台"],
|
||||
projects: [],
|
||||
quota5h: 92,
|
||||
quota7d: 95,
|
||||
lastSeenAt: "2026-03-25T08:15:00+08:00",
|
||||
@@ -1084,7 +1084,7 @@ const initialState: BossState = {
|
||||
pinned: true,
|
||||
systemPinned: true,
|
||||
deviceIds: ["mac-studio"],
|
||||
preview: "已汇总 3 个项目,优先收尾 Boss 移动控制台里 must_finish_before_compaction 的线程。",
|
||||
preview: "已汇总当前活跃项目,优先收尾 must_finish_before_compaction 的线程并同步最新风险。",
|
||||
updatedAt: "2026-03-25T12:06:00+08:00",
|
||||
lastMessageAt: "2026-03-25T12:06:00+08:00",
|
||||
isGroup: false,
|
||||
@@ -1111,7 +1111,7 @@ const initialState: BossState = {
|
||||
id: "master-summary",
|
||||
sender: "master",
|
||||
senderLabel: "主 Agent",
|
||||
body: "Boss 移动控制台存在 urgent 线程待交接,硬件审计协作还有 1 条摄像头证据待复核。",
|
||||
body: "当前存在 urgent 线程待交接,硬件审计协作还有 1 条摄像头证据待复核。",
|
||||
sentAt: "2026-03-25T12:06:00+08:00",
|
||||
kind: "text",
|
||||
},
|
||||
@@ -1119,91 +1119,6 @@ const initialState: BossState = {
|
||||
goals: [],
|
||||
versions: [],
|
||||
},
|
||||
{
|
||||
id: "boss-console",
|
||||
name: "Boss 移动控制台",
|
||||
pinned: false,
|
||||
deviceIds: ["mac-studio"],
|
||||
preview: "登录、设备页、线程预算与设备绑定链路正在收口到 v13。",
|
||||
updatedAt: "2026-03-25T11:52:00+08:00",
|
||||
lastMessageAt: "2026-03-25T11:52:00+08:00",
|
||||
isGroup: false,
|
||||
threadMeta: {
|
||||
projectId: "boss-console",
|
||||
threadId: "thread-boss-ui",
|
||||
threadDisplayName: "北区试产线回归",
|
||||
folderName: "归档确认",
|
||||
activityIconCount: 1,
|
||||
updatedAt: "2026-03-25T11:52:00+08:00",
|
||||
codexThreadRef: "thread-boss-ui",
|
||||
codexFolderRef: "boss-console",
|
||||
},
|
||||
groupMembers: [],
|
||||
createdByAgent: true,
|
||||
collaborationMode: "development",
|
||||
approvalState: "not_required",
|
||||
unreadCount: 2,
|
||||
riskLevel: "medium",
|
||||
contextBudgetPct: 62,
|
||||
contextBudgetLabel: "62%",
|
||||
messages: [
|
||||
{
|
||||
id: "p1",
|
||||
sender: "master",
|
||||
senderLabel: "主 Agent",
|
||||
body: "项目目标页已切成可完成、可编辑、可记录完成时间的结构。",
|
||||
sentAt: "2026-03-25T11:40:00+08:00",
|
||||
kind: "text",
|
||||
},
|
||||
{
|
||||
id: "p2",
|
||||
sender: "device",
|
||||
senderLabel: "Mac Studio / Codex",
|
||||
body: "登录、注册、忘记密码页已经补齐,并带验证码发送状态回显。",
|
||||
sentAt: "2026-03-25T11:48:00+08:00",
|
||||
kind: "text",
|
||||
},
|
||||
],
|
||||
goals: [
|
||||
{
|
||||
id: "goal-1",
|
||||
text: "完成北区试产线全链路回归,覆盖串口、视觉、OTA 和容灾切换。",
|
||||
state: "completed",
|
||||
note: "已完成 · 09:12 由主 Agent 复核",
|
||||
completedAt: "2026-03-25T09:12:00+08:00",
|
||||
completedBy: "主 Agent",
|
||||
},
|
||||
{
|
||||
id: "goal-2",
|
||||
text: "所有关键步骤必须留下可交接证据,禁止仅口头确认。",
|
||||
state: "pending",
|
||||
note: "进行中 · 允许用户编辑,主 Agent 会同步重排任务",
|
||||
},
|
||||
{
|
||||
id: "goal-3",
|
||||
text: "当线程上下文余量进入 urgent 前,必须完成阶段摘要与 handoff。",
|
||||
state: "pending",
|
||||
note: "待处理 · 主 Agent 会优先把压缩前必须收尾的任务推到前面",
|
||||
},
|
||||
],
|
||||
versions: [
|
||||
{
|
||||
version: "v1.3.0",
|
||||
summary: "登录页改为账号密码 / 验证码双模式,并新增 OTA 版本中心与本机最高管理员绑定。",
|
||||
createdAt: "2026-03-26T09:20:00+08:00",
|
||||
},
|
||||
{
|
||||
version: "v1.2.8",
|
||||
summary: "补齐认证页、线程预算接口、设备绑定草稿与运维审计摘要。",
|
||||
createdAt: "2026-03-25T11:30:00+08:00",
|
||||
},
|
||||
{
|
||||
version: "v1.2.7",
|
||||
summary: "会话页、设备页、我的页切到微信式一级导航。",
|
||||
createdAt: "2026-03-25T09:15:00+08:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "audit-collab",
|
||||
name: "硬件审计协作",
|
||||
@@ -1418,56 +1333,6 @@ const initialState: BossState = {
|
||||
checklist: baseThreadChecklist(["持续刷新阶段摘要", "复核风险排序", "回写交接文档"]),
|
||||
capturedAt: "2026-03-25T12:06:00+08:00",
|
||||
},
|
||||
{
|
||||
snapshotId: "snapshot-boss-ui",
|
||||
projectId: "boss-console",
|
||||
taskId: "task-ui-refine",
|
||||
threadId: "thread-boss-ui",
|
||||
title: "Boss UI 收口线程",
|
||||
summary: "会话页、设备页和我的页已稳定,剩设备绑定与运维摘要联调。",
|
||||
nodeId: "mac-studio",
|
||||
workerId: "worker-mac-ui",
|
||||
sourceKind: "worker_estimator",
|
||||
status: "running",
|
||||
contextBudgetRemainingPct: 62,
|
||||
contextBudgetLevel: "watch",
|
||||
compactionExpectedAt: "2026-03-25T14:05:00+08:00",
|
||||
mustFinishBeforeCompaction: false,
|
||||
estimatedRemainingTurns: 10,
|
||||
estimatedRemainingLargeMessages: 4,
|
||||
lastCompactionAt: "2026-03-25T10:28:00+08:00",
|
||||
compactionCount: 1,
|
||||
patchPending: true,
|
||||
testsPending: true,
|
||||
evidencePending: false,
|
||||
checklist: baseThreadChecklist(["设备绑定页接 API", "回归 lint/build", "回写 README"]),
|
||||
capturedAt: "2026-03-25T11:52:00+08:00",
|
||||
},
|
||||
{
|
||||
snapshotId: "snapshot-boss-auth",
|
||||
projectId: "boss-console",
|
||||
taskId: "task-auth-delivery",
|
||||
threadId: "thread-boss-auth",
|
||||
title: "认证链路交接线程",
|
||||
summary: "登录/注册/忘记密码已完成,但验证码回显和文档收口必须先固化。",
|
||||
nodeId: "mac-studio",
|
||||
workerId: "worker-mac-auth",
|
||||
sourceKind: "worker_estimator",
|
||||
status: "handoff_pending",
|
||||
contextBudgetRemainingPct: 34,
|
||||
contextBudgetLevel: "urgent",
|
||||
compactionExpectedAt: "2026-03-25T12:24:00+08:00",
|
||||
mustFinishBeforeCompaction: true,
|
||||
estimatedRemainingTurns: 4,
|
||||
estimatedRemainingLargeMessages: 1,
|
||||
lastCompactionAt: "2026-03-25T11:14:00+08:00",
|
||||
compactionCount: 2,
|
||||
patchPending: true,
|
||||
testsPending: true,
|
||||
evidencePending: false,
|
||||
checklist: baseThreadChecklist(["固化 patch", "记录测试结果", "生成 handoff package"]),
|
||||
capturedAt: "2026-03-25T11:54:00+08:00",
|
||||
},
|
||||
{
|
||||
snapshotId: "snapshot-audit-chief",
|
||||
projectId: "audit-collab",
|
||||
@@ -1519,36 +1384,8 @@ const initialState: BossState = {
|
||||
capturedAt: "2026-03-25T11:58:00+08:00",
|
||||
},
|
||||
],
|
||||
threadHandoffPackages: [
|
||||
{
|
||||
handoffPackageId: "handoff-boss-auth",
|
||||
projectId: "boss-console",
|
||||
taskId: "task-auth-delivery",
|
||||
fromThreadId: "thread-boss-auth",
|
||||
toThreadId: "thread-boss-auth-followup",
|
||||
packageStatus: "ready",
|
||||
summaryText: "认证页功能已齐,剩余文档、回归和部署验证待在新线程继续。",
|
||||
openQuestions: ["验证码直回显是否继续保留在公网环境", "是否把帮助页挂到登录页右上角"],
|
||||
criticalFiles: ["src/app/auth", "src/app/api/auth", "src/components/app-ui.tsx"],
|
||||
criticalCommands: ["npm run lint", "npm run build", "curl -sS http://127.0.0.1:3000/api/health"],
|
||||
criticalTests: ["登录验证码发送", "注册后登录", "忘记密码重置"],
|
||||
criticalArtifacts: ["docs/architecture/api_and_service_inventory_cn.md"],
|
||||
decisionLinks: ["boss-console:auth-mvp"],
|
||||
createdAt: "2026-03-25T11:49:00+08:00",
|
||||
readyAt: "2026-03-25T11:54:00+08:00",
|
||||
},
|
||||
],
|
||||
threadHandoffPackages: [],
|
||||
threadContextAlerts: [
|
||||
{
|
||||
alertId: "alert-boss-auth",
|
||||
threadId: "thread-boss-auth",
|
||||
projectId: "boss-console",
|
||||
alertType: "context_urgent",
|
||||
alertStatus: "opened",
|
||||
openedAt: "2026-03-25T11:54:00+08:00",
|
||||
summary: "认证链路线程已进入 urgent,必须优先固化 patch、测试结论和 handoff。",
|
||||
masterActions: ["prepare_handoff", "avoid_large_context_append", "finalize_artifacts"],
|
||||
},
|
||||
{
|
||||
alertId: "alert-audit-hw",
|
||||
threadId: "thread-audit-hardware",
|
||||
@@ -1591,23 +1428,6 @@ const initialState: BossState = {
|
||||
suggestedNextAction: "优先重试本地 agent 证据上传,再做审计复验。",
|
||||
autoRepairable: true,
|
||||
},
|
||||
{
|
||||
faultId: "fault-context-auth",
|
||||
faultKey: "THREAD.CONTEXT.HANDOFF.REQUIRED",
|
||||
severity: "critical",
|
||||
status: "opened",
|
||||
nodeId: "mac-studio",
|
||||
serviceName: "boss-web",
|
||||
projectId: "boss-console",
|
||||
threadRef: "thread-boss-auth",
|
||||
traceId: "trace-boss-auth-002",
|
||||
runbookId: "runbook-thread-handoff",
|
||||
firstSeenAt: "2026-03-25T11:50:00+08:00",
|
||||
lastSeenAt: "2026-03-25T11:54:00+08:00",
|
||||
summary: "认证线程预算降到 urgent,未完成 handoff 前不应继续追加长上下文。",
|
||||
suggestedNextAction: "执行压缩前收尾,写明关键文件、命令和测试结果。",
|
||||
autoRepairable: false,
|
||||
},
|
||||
],
|
||||
opsRepairTickets: [
|
||||
{
|
||||
@@ -7794,7 +7614,7 @@ function pruneStaleAutoImportedProjectsForDevice(
|
||||
selectedCandidates: DeviceImportCandidate[],
|
||||
) {
|
||||
const activeSignatures = new Set(selectedCandidates.map((candidate) => candidateThreadSignature(candidate)));
|
||||
const reservedProjectIds = new Set(["master-agent", "boss-console", "audit-collab"]);
|
||||
const reservedProjectIds = new Set(["master-agent", "audit-collab"]);
|
||||
|
||||
state.projects = state.projects.filter((project) => {
|
||||
if (reservedProjectIds.has(project.id)) return true;
|
||||
|
||||
@@ -233,3 +233,16 @@ test("conversation items prefer latest observed codex activity over stale last m
|
||||
assert.equal(thread?.latestReplyAt, "2026-04-04T11:48:00+08:00");
|
||||
assert.equal(thread?.latestReplyLabel, formatTimestampLabel("2026-04-04T11:48:00+08:00"));
|
||||
});
|
||||
|
||||
test("default seeded conversations no longer expose Boss 移动控制台", async () => {
|
||||
await setup();
|
||||
const state = await readState();
|
||||
|
||||
const items = getConversationHomeItems(state);
|
||||
|
||||
assert.ok(items.some((item) => item.projectId === "master-agent"), "expected master-agent to remain available");
|
||||
assert.equal(
|
||||
items.some((item) => item.projectId === "boss-console" || item.threadTitle === "Boss 移动控制台"),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -66,6 +66,58 @@ test.beforeEach(async () => {
|
||||
await writeState(structuredClone(baseState));
|
||||
});
|
||||
|
||||
function buildDispatchableThreadProject({
|
||||
id,
|
||||
projectName,
|
||||
threadDisplayName,
|
||||
body,
|
||||
}: {
|
||||
id: string;
|
||||
projectName: string;
|
||||
threadDisplayName: string;
|
||||
body: string;
|
||||
}) {
|
||||
return {
|
||||
id,
|
||||
name: projectName,
|
||||
pinned: false,
|
||||
systemPinned: false,
|
||||
deviceIds: ["mac-studio"],
|
||||
preview: body,
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
lastMessageAt: "2026-03-30T10:00:00+08:00",
|
||||
isGroup: false,
|
||||
threadMeta: {
|
||||
projectId: id,
|
||||
threadId: `thread-${id}`,
|
||||
threadDisplayName,
|
||||
folderName: "阻塞梳理",
|
||||
activityIconCount: 0,
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
codexThreadRef: `thread-${id}`,
|
||||
codexFolderRef: id,
|
||||
},
|
||||
groupMembers: [],
|
||||
createdByAgent: true,
|
||||
collaborationMode: "development" as const,
|
||||
approvalState: "not_required" as const,
|
||||
unreadCount: 0,
|
||||
riskLevel: "low" as const,
|
||||
messages: [
|
||||
{
|
||||
id: `msg-${id}`,
|
||||
sender: "device" as const,
|
||||
senderLabel: "Win GPU / Codex",
|
||||
body,
|
||||
sentAt: "2026-03-30T10:00:00+08:00",
|
||||
kind: "text" as const,
|
||||
},
|
||||
],
|
||||
goals: [],
|
||||
versions: [],
|
||||
};
|
||||
}
|
||||
|
||||
async function createAuthedRequest(url: string, method: "GET" | "POST" | "PATCH", body?: unknown) {
|
||||
const session = await createAuthSession({
|
||||
account: "17600003315",
|
||||
@@ -91,44 +143,24 @@ async function ensureTwoSingleThreadProjects() {
|
||||
return singles;
|
||||
}
|
||||
|
||||
assert.ok(singles[0], "expected at least one seeded single-thread project");
|
||||
const seed = singles[0];
|
||||
const clonedProject = {
|
||||
...seed,
|
||||
id: "boss-console-clone",
|
||||
name: "Boss 移动控制台副线程",
|
||||
deviceIds: [...seed.deviceIds],
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
lastMessageAt: "2026-03-30T10:00:00+08:00",
|
||||
preview: "副线程等待主 Agent 汇总阻塞点。",
|
||||
threadMeta: {
|
||||
...seed.threadMeta,
|
||||
projectId: "boss-console-clone",
|
||||
threadId: "thread-boss-ui-clone",
|
||||
const freshProjects = [
|
||||
buildDispatchableThreadProject({
|
||||
id: "dispatch-confirm-a",
|
||||
projectName: "北区试产线主线程",
|
||||
threadDisplayName: "北区试产线回归",
|
||||
body: "这里还在等待主 Agent 汇总阻塞点。",
|
||||
}),
|
||||
buildDispatchableThreadProject({
|
||||
id: "dispatch-confirm-b",
|
||||
projectName: "南区试产线主线程",
|
||||
threadDisplayName: "南区试产线回归",
|
||||
folderName: "阻塞梳理",
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
codexThreadRef: "thread-boss-ui-clone",
|
||||
codexFolderRef: "boss-console-clone",
|
||||
},
|
||||
groupMembers: [],
|
||||
messages: [
|
||||
{
|
||||
id: "msg-boss-console-clone",
|
||||
sender: "device" as const,
|
||||
senderLabel: "Win GPU / Codex",
|
||||
body: "这里还在等待视觉链路复核。",
|
||||
sentAt: "2026-03-30T10:00:00+08:00",
|
||||
kind: "text" as const,
|
||||
},
|
||||
],
|
||||
goals: [],
|
||||
versions: [],
|
||||
};
|
||||
body: "这里还在等待视觉链路复核。",
|
||||
}),
|
||||
];
|
||||
|
||||
await writeState({
|
||||
...state,
|
||||
projects: [...state.projects, clonedProject],
|
||||
projects: state.projects.concat(freshProjects),
|
||||
});
|
||||
|
||||
const nextState = await readState();
|
||||
|
||||
@@ -49,6 +49,58 @@ test.beforeEach(async () => {
|
||||
await writeState(structuredClone(baseState));
|
||||
});
|
||||
|
||||
function buildDispatchableThreadProject({
|
||||
id,
|
||||
projectName,
|
||||
threadDisplayName,
|
||||
body,
|
||||
}: {
|
||||
id: string;
|
||||
projectName: string;
|
||||
threadDisplayName: string;
|
||||
body: string;
|
||||
}) {
|
||||
return {
|
||||
id,
|
||||
name: projectName,
|
||||
pinned: false,
|
||||
systemPinned: false,
|
||||
deviceIds: ["mac-studio"],
|
||||
preview: body,
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
lastMessageAt: "2026-03-30T10:00:00+08:00",
|
||||
isGroup: false,
|
||||
threadMeta: {
|
||||
projectId: id,
|
||||
threadId: `thread-${id}`,
|
||||
threadDisplayName,
|
||||
folderName: "阻塞梳理",
|
||||
activityIconCount: 0,
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
codexThreadRef: `thread-${id}`,
|
||||
codexFolderRef: `/Users/kris/code/${id}`,
|
||||
},
|
||||
groupMembers: [],
|
||||
createdByAgent: true,
|
||||
collaborationMode: "development" as const,
|
||||
approvalState: "not_required" as const,
|
||||
unreadCount: 0,
|
||||
riskLevel: "low" as const,
|
||||
messages: [
|
||||
{
|
||||
id: `msg-${id}`,
|
||||
sender: "device" as const,
|
||||
senderLabel: "Mac Studio / Codex",
|
||||
body,
|
||||
sentAt: "2026-03-30T10:00:00+08:00",
|
||||
kind: "text" as const,
|
||||
},
|
||||
],
|
||||
goals: [],
|
||||
versions: [],
|
||||
};
|
||||
}
|
||||
|
||||
async function createAuthedRequest(projectId: string, body: { body: string; kind?: string }) {
|
||||
const session = await createAuthSession({
|
||||
account: "17600003315",
|
||||
@@ -69,73 +121,22 @@ async function createAuthedRequest(projectId: string, body: { body: string; kind
|
||||
|
||||
async function ensureTwoSingleThreadProjects() {
|
||||
const state = await readState();
|
||||
const seed = state.projects.find((project) => project.id !== "master-agent" && !project.isGroup);
|
||||
assert.ok(seed, "expected at least one seeded single-thread project");
|
||||
|
||||
const primaryProject = {
|
||||
...seed,
|
||||
id: "dispatch-thread-a",
|
||||
name: "Boss 移动控制台主线程",
|
||||
deviceIds: ["mac-studio"],
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
lastMessageAt: "2026-03-30T10:00:00+08:00",
|
||||
preview: "主线程正在等待汇总今天的联调阻塞点。",
|
||||
threadMeta: {
|
||||
...seed.threadMeta,
|
||||
projectId: "dispatch-thread-a",
|
||||
threadId: "thread-dispatch-a",
|
||||
...buildDispatchableThreadProject({
|
||||
id: "dispatch-thread-a",
|
||||
projectName: "北区试产线主线程",
|
||||
threadDisplayName: "北区试产线回归",
|
||||
folderName: "阻塞梳理",
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
codexThreadRef: "thread-dispatch-a",
|
||||
codexFolderRef: "/Users/kris/code/boss",
|
||||
},
|
||||
groupMembers: [],
|
||||
messages: [
|
||||
{
|
||||
id: "msg-dispatch-a",
|
||||
sender: "device" as const,
|
||||
senderLabel: "Mac Studio / Codex",
|
||||
body: "主线程还在等待主 Agent 汇总阻塞点。",
|
||||
sentAt: "2026-03-30T10:00:00+08:00",
|
||||
kind: "text" as const,
|
||||
},
|
||||
],
|
||||
goals: [],
|
||||
versions: [],
|
||||
body: "主线程还在等待主 Agent 汇总阻塞点。",
|
||||
}),
|
||||
};
|
||||
|
||||
const secondaryProject = {
|
||||
...seed,
|
||||
id: "dispatch-thread-b",
|
||||
name: "Boss 移动控制台副线程",
|
||||
deviceIds: ["mac-studio"],
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
lastMessageAt: "2026-03-30T10:00:00+08:00",
|
||||
preview: "副线程等待主 Agent 汇总阻塞点。",
|
||||
threadMeta: {
|
||||
...seed.threadMeta,
|
||||
projectId: "dispatch-thread-b",
|
||||
threadId: "thread-dispatch-b",
|
||||
...buildDispatchableThreadProject({
|
||||
id: "dispatch-thread-b",
|
||||
projectName: "南区试产线主线程",
|
||||
threadDisplayName: "南区试产线回归",
|
||||
folderName: "阻塞梳理",
|
||||
updatedAt: "2026-03-30T10:00:00+08:00",
|
||||
codexThreadRef: "thread-dispatch-b",
|
||||
codexFolderRef: "/Users/kris/code/boss",
|
||||
},
|
||||
groupMembers: [],
|
||||
messages: [
|
||||
{
|
||||
id: "msg-dispatch-b",
|
||||
sender: "device" as const,
|
||||
senderLabel: "Mac Studio / Codex",
|
||||
body: "副线程还在等待视觉链路复核。",
|
||||
sentAt: "2026-03-30T10:00:00+08:00",
|
||||
kind: "text" as const,
|
||||
},
|
||||
],
|
||||
goals: [],
|
||||
versions: [],
|
||||
body: "副线程还在等待视觉链路复核。",
|
||||
}),
|
||||
};
|
||||
|
||||
await writeState({
|
||||
@@ -202,11 +203,8 @@ test("POST /api/v1/projects/[projectId]/messages returns a dispatch plan for gro
|
||||
|
||||
test("POST /api/v1/projects/[projectId]/messages keeps dispatchPlan null for single-thread projects", async () => {
|
||||
await setup();
|
||||
const state = await readState();
|
||||
const singleProject = state.projects.find(
|
||||
(project) => project.id !== "master-agent" && !project.isGroup,
|
||||
);
|
||||
assert.ok(singleProject, "expected a seeded single-thread project");
|
||||
const [singleProject] = await ensureTwoSingleThreadProjects();
|
||||
assert.ok(singleProject, "expected a synthetic single-thread project");
|
||||
|
||||
const response = await POST(await createAuthedRequest(singleProject.id, { body: "单线程消息" }), {
|
||||
params: Promise.resolve({ projectId: singleProject.id }),
|
||||
@@ -547,10 +545,7 @@ test("POST /api/v1/projects/[projectId]/messages excludes master-agent from grou
|
||||
|
||||
test("createIndependentGroupChat rejects non-thread members like master-agent", async () => {
|
||||
await setup();
|
||||
const state = await readState();
|
||||
const realThread = state.projects.find(
|
||||
(project) => project.id !== "master-agent" && !project.isGroup && Boolean(project.threadMeta.codexThreadRef),
|
||||
);
|
||||
const [realThread] = await ensureTwoSingleThreadProjects();
|
||||
assert.ok(realThread, "expected a real thread-backed project");
|
||||
|
||||
await assert.rejects(
|
||||
|
||||
@@ -63,6 +63,46 @@ test.beforeEach(async () => {
|
||||
await resetMasterAgentControls();
|
||||
});
|
||||
|
||||
async function ensureOrdinaryProject(projectId = "ordinary-project") {
|
||||
await setup();
|
||||
const state = await readState();
|
||||
if (state.projects.some((item) => item.id === projectId)) {
|
||||
return projectId;
|
||||
}
|
||||
state.projects.push({
|
||||
id: projectId,
|
||||
name: "普通项目线程",
|
||||
pinned: false,
|
||||
systemPinned: false,
|
||||
deviceIds: ["mac-studio"],
|
||||
preview: "普通项目测试线程。",
|
||||
updatedAt: "2026-04-04T12:00:00+08:00",
|
||||
lastMessageAt: "2026-04-04T12:00:00+08:00",
|
||||
isGroup: false,
|
||||
threadMeta: {
|
||||
projectId,
|
||||
threadId: `thread-${projectId}`,
|
||||
threadDisplayName: "普通项目线程",
|
||||
folderName: "普通项目",
|
||||
activityIconCount: 0,
|
||||
updatedAt: "2026-04-04T12:00:00+08:00",
|
||||
codexThreadRef: `thread-${projectId}`,
|
||||
codexFolderRef: projectId,
|
||||
},
|
||||
groupMembers: [],
|
||||
createdByAgent: true,
|
||||
collaborationMode: "development",
|
||||
approvalState: "not_required",
|
||||
unreadCount: 0,
|
||||
riskLevel: "low",
|
||||
messages: [],
|
||||
goals: [],
|
||||
versions: [],
|
||||
});
|
||||
await writeState(state);
|
||||
return projectId;
|
||||
}
|
||||
|
||||
test.after(async () => {
|
||||
if (runtimeRoot) {
|
||||
await rm(runtimeRoot, { recursive: true, force: true });
|
||||
@@ -368,6 +408,7 @@ test("master-agent 对话控制 POST 清空后仍稳定回传 controls null", as
|
||||
|
||||
test("非 master-agent 项目详情不应回传 agentControls 字段", async () => {
|
||||
await setup();
|
||||
const ordinaryProjectId = await ensureOrdinaryProject();
|
||||
|
||||
const session = await createAuthSession({
|
||||
account: "17600003315",
|
||||
@@ -377,13 +418,13 @@ test("非 master-agent 项目详情不应回传 agentControls 字段", async ()
|
||||
});
|
||||
|
||||
const response = await getProjectRoute(
|
||||
new NextRequest("http://127.0.0.1:3000/api/v1/projects/boss-console", {
|
||||
new NextRequest(`http://127.0.0.1:3000/api/v1/projects/${ordinaryProjectId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`,
|
||||
},
|
||||
}),
|
||||
{ params: Promise.resolve({ projectId: "boss-console" }) },
|
||||
{ params: Promise.resolve({ projectId: ordinaryProjectId }) },
|
||||
);
|
||||
|
||||
assert.equal(response.status, 200);
|
||||
@@ -841,6 +882,7 @@ test("GET /agent-controls 在未显式设置 BOSS_STATE_FILE 时仍可正常读
|
||||
|
||||
test("GET /agent-controls rejects ordinary projects", async () => {
|
||||
await setup();
|
||||
const ordinaryProjectId = await ensureOrdinaryProject();
|
||||
|
||||
const session = await createAuthSession({
|
||||
account: "17600003315",
|
||||
@@ -850,13 +892,13 @@ test("GET /agent-controls rejects ordinary projects", async () => {
|
||||
});
|
||||
|
||||
const response = await getAgentControlsRoute(
|
||||
new NextRequest("http://127.0.0.1:3000/api/v1/projects/boss-console/agent-controls", {
|
||||
new NextRequest(`http://127.0.0.1:3000/api/v1/projects/${ordinaryProjectId}/agent-controls`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`,
|
||||
},
|
||||
}),
|
||||
{ params: Promise.resolve({ projectId: "boss-console" }) },
|
||||
{ params: Promise.resolve({ projectId: ordinaryProjectId }) },
|
||||
);
|
||||
|
||||
assert.equal(response.status, 404);
|
||||
@@ -940,10 +982,11 @@ test("master-agent 对话控制 POST 会稳定拒绝非法 backendOverride", asy
|
||||
|
||||
test("master-agent controls helper 不会写入普通项目", async () => {
|
||||
await setup();
|
||||
const ordinaryProjectId = await ensureOrdinaryProject();
|
||||
|
||||
await assert.rejects(
|
||||
() =>
|
||||
updateProjectAgentControls("boss-console", {
|
||||
updateProjectAgentControls(ordinaryProjectId, {
|
||||
modelOverride: "gpt-5.4",
|
||||
reasoningEffortOverride: "low",
|
||||
}),
|
||||
@@ -951,7 +994,7 @@ test("master-agent controls helper 不会写入普通项目", async () => {
|
||||
);
|
||||
|
||||
const state = await readState();
|
||||
const project = state.projects.find((item) => item.id === "boss-console");
|
||||
const project = state.projects.find((item) => item.id === ordinaryProjectId);
|
||||
assert.equal(project?.agentControls, undefined);
|
||||
assert.equal(await getProjectAgentControls("boss-console"), null);
|
||||
assert.equal(await getProjectAgentControls(ordinaryProjectId), null);
|
||||
});
|
||||
|
||||
@@ -44,6 +44,39 @@ test.beforeEach(async () => {
|
||||
await writeState(structuredClone(baseState));
|
||||
});
|
||||
|
||||
function buildSingleThreadProject(projectId: string) {
|
||||
return {
|
||||
id: projectId,
|
||||
name: "测试线程",
|
||||
pinned: false,
|
||||
systemPinned: false,
|
||||
deviceIds: ["mac-studio"],
|
||||
preview: "测试线程等待继续处理。",
|
||||
updatedAt: "2026-04-04T11:30:00+08:00",
|
||||
lastMessageAt: "2026-04-04T11:30:00+08:00",
|
||||
isGroup: false,
|
||||
threadMeta: {
|
||||
projectId,
|
||||
threadId: "thread-preflight",
|
||||
threadDisplayName: "测试线程",
|
||||
folderName: "测试项目",
|
||||
activityIconCount: 0,
|
||||
updatedAt: "2026-04-04T11:30:00+08:00",
|
||||
codexThreadRef: "thread-preflight",
|
||||
codexFolderRef: "preflight-project",
|
||||
},
|
||||
groupMembers: [],
|
||||
createdByAgent: true,
|
||||
collaborationMode: "development" as const,
|
||||
approvalState: "not_required" as const,
|
||||
unreadCount: 0,
|
||||
riskLevel: "low" as const,
|
||||
messages: [],
|
||||
goals: [],
|
||||
versions: [],
|
||||
};
|
||||
}
|
||||
|
||||
async function createAuthedRequest(projectId: string, body: { body: string }) {
|
||||
const session = await createAuthSession({
|
||||
account: "17600003315",
|
||||
@@ -65,14 +98,17 @@ async function createAuthedRequest(projectId: string, body: { body: string }) {
|
||||
test("single-thread message rejects projects without a real codex thread binding", async () => {
|
||||
await setup();
|
||||
const state = await readState();
|
||||
const singleProject = state.projects.find(
|
||||
(project) => project.id !== "master-agent" && !project.isGroup,
|
||||
);
|
||||
assert.ok(singleProject, "expected a seeded single-thread project");
|
||||
const singleProject = buildSingleThreadProject("preflight-thread");
|
||||
|
||||
await writeState({
|
||||
...state,
|
||||
projects: state.projects.map((project) =>
|
||||
projects: state.projects.concat(singleProject),
|
||||
});
|
||||
|
||||
const nextState = await readState();
|
||||
await writeState({
|
||||
...nextState,
|
||||
projects: nextState.projects.map((project) =>
|
||||
project.id === singleProject.id
|
||||
? {
|
||||
...project,
|
||||
@@ -96,8 +132,8 @@ test("single-thread message rejects projects without a real codex thread binding
|
||||
assert.equal(payload.code, "THREAD_BINDING_REQUIRED");
|
||||
assert.equal(payload.message, "当前线程还没有绑定真实 Codex 线程,请先重新导入该线程后再试。");
|
||||
|
||||
const nextState = await readState();
|
||||
const queuedTask = nextState.masterAgentTasks.find(
|
||||
const finalState = await readState();
|
||||
const queuedTask = finalState.masterAgentTasks.find(
|
||||
(task) => task.projectId === singleProject.id && task.taskType === "conversation_reply",
|
||||
);
|
||||
assert.equal(queuedTask, undefined);
|
||||
|
||||
Reference in New Issue
Block a user