# Boss 执行底座抽象层重构 Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 在不改变现有生产行为的前提下,为 Boss 建立稳定的执行底座抽象层,把当前主 Agent、普通线程回复、群聊分发的底层执行职责从散落逻辑中抽出,为后续接入 `ClawBackendAdapter` 和 `OmxTeamBackendAdapter` 做准备。 **Architecture:** 先新增 `src/lib/execution/` 下的接口与默认实现,再把 `boss-master-agent.ts`、`local-agent/server.mjs` 和关键 API route 逐步映射到这些抽象。整个过程坚持“先平移、不改行为”,所有已有主链继续沿用 `boss-state.json + master-agent queue + local-agent + codex exec resume`。 **Tech Stack:** Next.js App Router、TypeScript、Node.js、文件型状态 `data/boss-state.json`、原生 `local-agent`、现有测试栈 `tsx --test`、Android/前台不改行为。 --- ### Task 1: 定义执行底座类型与默认 contract **Files:** - Create: `/Users/kris/code/boss/src/lib/execution/types.ts` - Create: `/Users/kris/code/boss/src/lib/execution/execution-backend.ts` - Create: `/Users/kris/code/boss/src/lib/execution/orchestration-backend.ts` - Test: `/Users/kris/code/boss/tests/execution-foundation-contracts.test.ts` - [ ] **Step 1: 写失败测试,定义执行底座 contract 期望** ```ts import assert from "node:assert/strict"; import test from "node:test"; import { createExecutionRequest, isQueuedExecutionResult, isImmediateExecutionResult, } from "@/lib/execution/types"; test("ExecutionRequest 工厂会生成稳定默认字段", () => { const request = createExecutionRequest({ kind: "master_agent_reply", projectId: "master-agent", requestMessageId: "msg-1", body: "你好", }); assert.equal(request.kind, "master_agent_reply"); assert.equal(request.projectId, "master-agent"); assert.equal(request.requestMessageId, "msg-1"); assert.equal(request.body, "你好"); assert.equal(request.targetProjectId, undefined); assert.equal(request.targetThreadId, undefined); }); test("ExecutionResult 类型守卫能区分 queued 与 immediate", () => { const queued = { status: "queued", taskId: "task-1", backendId: "master-codex-node", }; const completed = { status: "completed", backendId: "openai-api", output: "done", }; assert.equal(isQueuedExecutionResult(queued), true); assert.equal(isImmediateExecutionResult(queued), false); assert.equal(isQueuedExecutionResult(completed), false); assert.equal(isImmediateExecutionResult(completed), true); }); ``` - [ ] **Step 2: 运行测试,确认当前失败** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/execution-foundation-contracts.test.ts ``` Expected: ```text ERR_MODULE_NOT_FOUND ``` - [ ] **Step 3: 写最小 contract 类型与工厂函数** ```ts // /Users/kris/code/boss/src/lib/execution/types.ts import type { ReasoningEffort } from "@/lib/boss-data"; export type ExecutionRequestKind = | "master_agent_reply" | "thread_reply" | "dispatch_execution" | "attachment_analysis"; export interface ExecutionRequest { kind: ExecutionRequestKind; projectId: string; requestMessageId: string; body: string; requestedByAccount?: string; requestedByLabel?: string; taskId?: string; targetThreadId?: string; targetProjectId?: string; modelOverride?: string; reasoningEffortOverride?: ReasoningEffort; } export interface ExecutionQueuedResult { status: "queued" | "running"; taskId: string; backendId: string; sessionId?: string; } export interface ExecutionImmediateResult { status: "completed" | "failed"; backendId: string; output?: string; error?: string; } export interface ExecutionBackendDescription { backendId: string; label: string; mode: "local" | "remote" | "api"; } export function createExecutionRequest(input: ExecutionRequest): ExecutionRequest { return { ...input }; } export function isQueuedExecutionResult( value: ExecutionQueuedResult | ExecutionImmediateResult, ): value is ExecutionQueuedResult { return value.status === "queued" || value.status === "running"; } export function isImmediateExecutionResult( value: ExecutionQueuedResult | ExecutionImmediateResult, ): value is ExecutionImmediateResult { return value.status === "completed" || value.status === "failed"; } ``` ```ts // /Users/kris/code/boss/src/lib/execution/execution-backend.ts import type { ExecutionBackendDescription, ExecutionImmediateResult, ExecutionQueuedResult, ExecutionRequest, } from "@/lib/execution/types"; export interface ExecutionBackend { backendId: string; canHandle(input: ExecutionRequest): Promise | boolean; execute(input: ExecutionRequest): Promise; describe(input: ExecutionRequest): Promise; } ``` ```ts // /Users/kris/code/boss/src/lib/execution/orchestration-backend.ts export interface OrchestrationBackend { backendId: string; describe(): Promise<{ backendId: string; label: string }>; } ``` - [ ] **Step 4: 再跑测试,确认 contract 生效** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/execution-foundation-contracts.test.ts ``` Expected: ```text # pass 2 ``` - [ ] **Step 5: 提交** ```bash cd /Users/kris/code/boss git add tests/execution-foundation-contracts.test.ts src/lib/execution/types.ts src/lib/execution/execution-backend.ts src/lib/execution/orchestration-backend.ts git commit -m "feat: add execution foundation contracts" ``` ### Task 2: 抽离 PromptAssembler 与 MemoryResolver **Files:** - Create: `/Users/kris/code/boss/src/lib/execution/prompt-assembler.ts` - Create: `/Users/kris/code/boss/src/lib/execution/memory-resolver.ts` - Modify: `/Users/kris/code/boss/src/lib/boss-master-agent.ts` - Test: `/Users/kris/code/boss/tests/execution-prompt-assembler.test.ts` - Test: `/Users/kris/code/boss/tests/execution-memory-resolver.test.ts` - Test: `/Users/kris/code/boss/tests/master-agent-config-resolution.test.ts` - [ ] **Step 1: 写失败测试,固定记忆筛选与 prompt 拼装顺序** ```ts import assert from "node:assert/strict"; import test from "node:test"; import { buildExecutionPromptForTesting, resolveRelevantMemoriesForTesting, } from "@/lib/execution/prompt-assembler"; test("PromptAssembler 会按固定顺序拼管理员提示词、用户提示词、对话提示词和记忆", () => { const prompt = buildExecutionPromptForTesting({ globalPrompt: "GLOBAL", userPrompt: "USER", conversationPrompt: "CONVERSATION", projectMemories: [{ title: "项目记忆", content: "PROJECT", projectId: "boss-console", tags: [] }], userMemories: [{ title: "用户记忆", content: "USER_MEMORY", tags: [] }], requestText: "继续", }); assert.match(prompt, /GLOBAL/); assert.match(prompt, /USER/); assert.match(prompt, /CONVERSATION/); assert.match(prompt, /PROJECT/); assert.match(prompt, /USER_MEMORY/); assert.ok(prompt.indexOf("GLOBAL") < prompt.indexOf("USER")); assert.ok(prompt.indexOf("USER") < prompt.indexOf("CONVERSATION")); }); test("MemoryResolver 在 master-agent 会话下优先挑当前请求命中的项目记忆", () => { const resolved = resolveRelevantMemoriesForTesting({ projectId: "master-agent", requestText: "boss-console 的审批流", memories: [ { memoryId: "m1", scope: "project", projectId: "boss-console", title: "审批流", content: "boss-console approval", tags: ["approval"] }, { memoryId: "m2", scope: "project", projectId: "wenshenapp", title: "UI", content: "wechat ui", tags: ["ui"] }, ], }); assert.equal(resolved.projectMemories.length, 1); assert.equal(resolved.projectMemories[0]?.projectId, "boss-console"); }); ``` - [ ] **Step 2: 运行测试,确认当前失败** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/execution-prompt-assembler.test.ts tests/execution-memory-resolver.test.ts ``` Expected: ```text ERR_MODULE_NOT_FOUND ``` - [ ] **Step 3: 写最小实现,并让 boss-master-agent 改用新模块** ```ts // /Users/kris/code/boss/src/lib/execution/memory-resolver.ts export function resolveRelevantMemories(input: { projectId: string; requestText?: string; memories: Array<{ memoryId: string; scope: "global" | "project"; projectId?: string; title: string; content: string; tags?: string[]; }>; }) { const lowered = input.requestText?.toLowerCase() ?? ""; const projectMemories = input.memories.filter((memory) => { if (memory.scope !== "project") return false; if (input.projectId !== "master-agent") { return memory.projectId === input.projectId; } if (!lowered) return true; return [memory.projectId, memory.title, memory.content, ...(memory.tags ?? [])] .filter(Boolean) .some((value) => value!.toLowerCase().includes(lowered) || lowered.includes(value!.toLowerCase())); }); const userMemories = input.memories.filter((memory) => memory.scope === "global"); return { projectMemories: projectMemories.slice(0, 6), userMemories: userMemories.slice(0, 8), }; } export const resolveRelevantMemoriesForTesting = resolveRelevantMemories; ``` 为避免 `master-agent` 当前生产行为回归,允许在同一个 `/Users/kris/code/boss/src/lib/execution/memory-resolver.ts` 中额外提供一个 **runtime-safe helper**(例如 `resolveRuntimeRelevantMemories(...)`),专门给 `boss-master-agent.ts` 使用。约束如下: - `resolveRelevantMemories(...)` 仍保持上面的最小 contract,供基础测试与后续 contract 使用。 - runtime-safe helper 只负责把当前已存在的主 Agent 运行时保护内聚回执行模块,例如: - `workflow_rule / user_preference` 优先; - `master-agent` 非空请求但无 lexical 命中时,回退到前 6 个项目记忆。 - 不允许把这些运行时保护继续散落在 `boss-master-agent.ts` 中;如果需要保留生产行为,优先放进同一个 execution 模块。 ```ts // /Users/kris/code/boss/src/lib/execution/prompt-assembler.ts import { resolveRelevantMemories } from "@/lib/execution/memory-resolver"; export function buildExecutionPrompt(input: { globalPrompt?: string | null; userPrompt?: string | null; conversationPrompt?: string | null; projectMemories: Array<{ title: string; content: string; projectId?: string; tags?: string[] }>; userMemories: Array<{ title: string; content: string; tags?: string[] }>; requestText: string; }) { return [ input.globalPrompt?.trim() ? `管理员全局主提示词:\n${input.globalPrompt.trim()}` : null, input.userPrompt?.trim() ? `用户私有主提示词:\n${input.userPrompt.trim()}` : null, input.conversationPrompt?.trim() ? `当前对话附加提示词:\n${input.conversationPrompt.trim()}` : null, input.projectMemories.length ? `项目记忆:\n${input.projectMemories.map((item) => `- [${item.projectId ?? "unknown"}] ${item.title}: ${item.content}`).join("\n")}` : null, input.userMemories.length ? `用户通用记忆:\n${input.userMemories.map((item) => `- ${item.title}: ${item.content}`).join("\n")}` : null, `当前消息:\n${input.requestText}`, ].filter(Boolean).join("\n\n"); } export const buildExecutionPromptForTesting = buildExecutionPrompt; export { resolveRelevantMemoriesForTesting } from "@/lib/execution/memory-resolver"; ``` 在 `/Users/kris/code/boss/src/lib/boss-master-agent.ts` 中,把当前内联的 memory selection 与 prompt assembly 改成调用: ```ts import { buildExecutionPrompt } from "@/lib/execution/prompt-assembler"; import { resolveRelevantMemories } from "@/lib/execution/memory-resolver"; ``` 并在 `resolveMasterAgentExecutionConfig(...)` 中改成: ```ts const memoryScope = listUserMasterMemoriesView(state, resolvedAccountId, { includeArchived: false }); const { projectMemories, userMemories } = resolveRelevantMemories({ projectId, requestText, memories: memoryScope, }); const executionPrompt = buildExecutionPrompt({ globalPrompt: promptPolicy?.globalPrompt ?? null, userPrompt: userPrompt?.content ?? null, conversationPrompt: scopedAgentControls?.promptOverride ?? null, projectMemories, userMemories, requestText: requestText ?? "", }); ``` - [ ] **Step 4: 跑新测试和既有主 Agent 测试** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/execution-prompt-assembler.test.ts tests/execution-memory-resolver.test.ts tests/master-agent-config-resolution.test.ts ``` Expected: ```text # pass ``` - [ ] **Step 5: 提交** ```bash cd /Users/kris/code/boss git add tests/execution-prompt-assembler.test.ts tests/execution-memory-resolver.test.ts tests/master-agent-config-resolution.test.ts src/lib/execution/prompt-assembler.ts src/lib/execution/memory-resolver.ts src/lib/boss-master-agent.ts git commit -m "refactor: extract execution prompt assembly" ``` ### Task 3: 抽离 PermissionPolicy 与 ToolRegistry **Files:** - Create: `/Users/kris/code/boss/src/lib/execution/permission-policy.ts` - Create: `/Users/kris/code/boss/src/lib/execution/tool-registry.ts` - Modify: `/Users/kris/code/boss/src/app/api/v1/projects/[projectId]/messages/route.ts` - Modify: `/Users/kris/code/boss/src/lib/boss-data.ts` - Test: `/Users/kris/code/boss/tests/execution-permission-policy.test.ts` - Test: `/Users/kris/code/boss/tests/group-message-dispatch-plan.test.ts` - Test: `/Users/kris/code/boss/tests/dispatch-plan-confirmation.test.ts` - [ ] **Step 1: 写失败测试,固定 approval_required 和 tool policy 语义** ```ts import assert from "node:assert/strict"; import test from "node:test"; import { evaluatePermissionPolicyForTesting } from "@/lib/execution/permission-policy"; test("approval_required 群聊在已有待确认推荐时拒绝继续直接执行", () => { const result = evaluatePermissionPolicyForTesting({ project: { id: "group-1", isGroup: true, collaborationMode: "approval_required", approvalState: "pending_user", }, hasPendingDispatchPlan: true, }); assert.equal(result.allowed, false); assert.equal(result.requiresApproval, true); assert.match(result.reason ?? "", /等待确认/); }); ``` - [ ] **Step 2: 运行测试确认失败** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/execution-permission-policy.test.ts ``` Expected: ```text ERR_MODULE_NOT_FOUND ``` - [ ] **Step 3: 写权限策略与工具注册最小实现,并在消息 route 中复用** ```ts // /Users/kris/code/boss/src/lib/execution/permission-policy.ts export function evaluatePermissionPolicy(input: { project?: { id: string; isGroup: boolean; collaborationMode: "development" | "approval_required"; approvalState: "not_required" | "pending_agent" | "pending_user" | "approved" | "rejected"; }; hasPendingDispatchPlan?: boolean; }) { const project = input.project; if (!project) { return { allowed: true, requiresApproval: false, toolPolicy: { allowedTools: [], deniedTools: [] }, collaborationPolicy: { mode: "development" as const, canDispatchDirectly: true, canCrossThreadTalk: true, }, }; } if (project.isGroup && project.collaborationMode === "approval_required" && input.hasPendingDispatchPlan) { return { allowed: false, requiresApproval: true, reason: "当前还有一条主 Agent 推荐等待确认,请先确认或拒绝后再继续发送新指令。", toolPolicy: { allowedTools: [], deniedTools: ["dispatch_execution"] }, collaborationPolicy: { mode: "approval_required" as const, canDispatchDirectly: false, canCrossThreadTalk: false, }, }; } return { allowed: true, requiresApproval: project.isGroup && project.collaborationMode === "approval_required", toolPolicy: { allowedTools: ["conversation_reply", "dispatch_execution"], deniedTools: [] }, collaborationPolicy: { mode: project.collaborationMode, canDispatchDirectly: project.collaborationMode === "development", canCrossThreadTalk: project.collaborationMode === "development", }, }; } export const evaluatePermissionPolicyForTesting = evaluatePermissionPolicy; ``` ```ts // /Users/kris/code/boss/src/lib/execution/tool-registry.ts export function listExecutionTools() { return [ { name: "conversation_reply", kind: "execution" }, { name: "dispatch_execution", kind: "execution" }, { name: "attachment_analysis", kind: "analysis" }, ] as const; } ``` 如果在落地时发现当前生产审批流需要把“先生成推荐、再等待确认”的动作显式建模,允许在同一个 `tool-registry.ts` 中补一个 `group_dispatch_plan` 工具定义,并同步把 `PermissionPolicy` 和测试调整为**以真实运行时语义为准**。约束如下: - `approval_required` 群聊不能再暴露与 `canDispatchDirectly=false` 矛盾的 `dispatch_execution` 直接权限; - `toolPolicy` 必须能清楚表达“当前允许先生成推荐,但不允许直接跨线程执行”的状态; - 任何新增工具定义都必须只服务于现有审批主链,不提前引入 Task 4+ 的执行后端能力。 在 `/Users/kris/code/boss/src/app/api/v1/projects/[projectId]/messages/route.ts` 中,把 `approval_required + pending plan` 判断替换成: ```ts const permission = evaluatePermissionPolicy({ project, hasPendingDispatchPlan: Boolean(pendingPlan), }); if (!permission.allowed) { return NextResponse.json( { ok: false, message: permission.reason, pendingPlan, collaborationGate: buildCollaborationGate(project), }, { status: 409 }, ); } ``` - [ ] **Step 4: 跑权限测试与现有 dispatch 测试** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/execution-permission-policy.test.ts tests/group-message-dispatch-plan.test.ts tests/dispatch-plan-confirmation.test.ts ``` Expected: ```text # pass ``` - [ ] **Step 5: 提交** ```bash cd /Users/kris/code/boss git add tests/execution-permission-policy.test.ts tests/group-message-dispatch-plan.test.ts tests/dispatch-plan-confirmation.test.ts src/lib/execution/permission-policy.ts src/lib/execution/tool-registry.ts src/app/api/v1/projects/[projectId]/messages/route.ts git commit -m "refactor: extract execution permission policy" ``` ### Task 4: 抽离 ExecutionBackendSelector 与默认执行后端 **Files:** - Create: `/Users/kris/code/boss/src/lib/execution/backend-selector.ts` - Create: `/Users/kris/code/boss/src/lib/execution/backends/master-codex-node-backend.ts` - Create: `/Users/kris/code/boss/src/lib/execution/backends/openai-backend.ts` - Create: `/Users/kris/code/boss/src/lib/execution/backends/aliyun-qwen-backend.ts` - Modify: `/Users/kris/code/boss/src/lib/boss-master-agent.ts` - Test: `/Users/kris/code/boss/tests/execution-backend-selector.test.ts` - Test: `/Users/kris/code/boss/tests/master-agent-openai-fallback.test.ts` - Test: `/Users/kris/code/boss/tests/master-agent-message-queue.test.ts` - [ ] **Step 1: 写失败测试,固定后端选择优先级** ```ts import assert from "node:assert/strict"; import test from "node:test"; import { selectExecutionBackendForTesting } from "@/lib/execution/backend-selector"; test("主控可用时优先选择当前主控 backend", async () => { const backend = await selectExecutionBackendForTesting({ primary: { provider: "master_codex_node", status: "ready" }, backups: [{ provider: "aliyun_qwen_api", status: "ready" }], }); assert.equal(backend.backendId, "master-codex-node"); }); test("主控 degraded 时回退到可用阿里备用 backend", async () => { const backend = await selectExecutionBackendForTesting({ primary: { provider: "master_codex_node", status: "degraded" }, backups: [{ provider: "aliyun_qwen_api", status: "ready" }], }); assert.equal(backend.backendId, "aliyun-qwen"); }); ``` - [ ] **Step 2: 运行测试确认失败** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/execution-backend-selector.test.ts ``` Expected: ```text ERR_MODULE_NOT_FOUND ``` - [ ] **Step 3: 写 selector 和默认 backend 壳,并让 boss-master-agent 通过 selector 取 backend** ```ts // /Users/kris/code/boss/src/lib/execution/backend-selector.ts export async function selectExecutionBackend(input: { primary: { provider: string; status: string }; backups: Array<{ provider: string; status: string }>; }) { const primaryBackend = resolveBackendByProvider(input.primary.provider); if (input.primary.status === "ready") { return primaryBackend; } const qwen = input.backups.find((item) => item.provider === "aliyun_qwen_api" && item.status === "ready"); if (qwen) { return { backendId: "aliyun-qwen" }; } const openai = input.backups.find((item) => item.provider === "openai_api" && item.status === "ready"); if (openai) { return { backendId: "openai-api" }; } const master = input.backups.find((item) => item.provider === "master_codex_node" && item.status === "ready"); if (master) { return { backendId: "master-codex-node" }; } return primaryBackend; } export const selectExecutionBackendForTesting = selectExecutionBackend; ``` 补充说明: - selector 的运行时语义以“`ready primary` 优先,否则按 `aliyun_qwen -> openai -> master_codex_node` 顺序回退,最后再回 primary 兜底”为准。 - 如果同一 provider 存在多个账号,只要其中任何一个是 `ready`,该 backend 就视为可选。 在 `/Users/kris/code/boss/src/lib/boss-master-agent.ts` 中,把 provider fallback 的选择逻辑收进一个单独调用: ```ts const selectedBackend = await selectExecutionBackend({ primary: { provider: runtime.account.provider, status: runtime.account.status }, backups: state.aiAccounts .filter((item) => item.accountId !== runtime.account.accountId) .map((item) => ({ provider: item.provider, status: item.status })), }); ``` 并让后续 provider-specific 分支改成按 `selectedBackend.backendId` 分派,而不是继续直接拼条件。 - [ ] **Step 4: 跑 selector 测试和既有 fallback 测试** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/execution-backend-selector.test.ts tests/master-agent-openai-fallback.test.ts tests/master-agent-message-queue.test.ts ``` Expected: ```text # pass ``` - [ ] **Step 5: 提交** ```bash cd /Users/kris/code/boss git add tests/execution-backend-selector.test.ts tests/master-agent-openai-fallback.test.ts tests/master-agent-message-queue.test.ts src/lib/execution/backend-selector.ts src/lib/execution/backends/master-codex-node-backend.ts src/lib/execution/backends/openai-backend.ts src/lib/execution/backends/aliyun-qwen-backend.ts src/lib/boss-master-agent.ts git commit -m "refactor: add execution backend selector" ``` ### Task 5: 抽离 RemoteRuntimeAdapter 与 OrchestrationBackend **Files:** - Create: `/Users/kris/code/boss/src/lib/execution/remote-runtime-adapter.ts` - Create: `/Users/kris/code/boss/src/lib/execution/backends/boss-native-orchestrator.ts` - Modify: `/Users/kris/code/boss/local-agent/server.mjs` - Modify: `/Users/kris/code/boss/src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts` - Test: `/Users/kris/code/boss/tests/remote-runtime-adapter.test.ts` - Test: `/Users/kris/code/boss/tests/dispatch-execution-result.test.ts` - Test: `/Users/kris/code/boss/tests/local-agent-codex-task-runner.test.mjs` - [ ] **Step 1: 写失败测试,固定远程执行结果和编排分层** ```ts import assert from "node:assert/strict"; import test from "node:test"; import { normalizeRemoteExecutionResultForTesting } from "@/lib/execution/remote-runtime-adapter"; test("RemoteRuntimeAdapter 会把 local-agent 回写标准化成统一结果", () => { const normalized = normalizeRemoteExecutionResultForTesting({ status: "completed", dispatchExecutionId: "dx-1", targetProjectId: "project-1", targetThreadId: "thread-1", rawThreadReply: "链路正常", }); assert.equal(normalized.status, "completed"); assert.equal(normalized.targetProjectId, "project-1"); assert.equal(normalized.rawThreadReply, "链路正常"); }); ``` - [ ] **Step 2: 运行测试确认失败** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/remote-runtime-adapter.test.ts ``` Expected: ```text ERR_MODULE_NOT_FOUND ``` - [ ] **Step 3: 写最小 adapter,并让 complete route 和 local-agent 对齐这个结构** ```ts // /Users/kris/code/boss/src/lib/execution/remote-runtime-adapter.ts export function normalizeRemoteExecutionResult(input: { status: "completed" | "failed"; dispatchExecutionId?: string; targetProjectId?: string; targetThreadId?: string; rawThreadReply?: string; replyBody?: string; errorMessage?: string; }) { return { status: input.status, dispatchExecutionId: input.dispatchExecutionId, targetProjectId: input.targetProjectId, targetThreadId: input.targetThreadId, rawThreadReply: input.rawThreadReply, replyBody: input.replyBody, errorMessage: input.errorMessage, }; } export const normalizeRemoteExecutionResultForTesting = normalizeRemoteExecutionResult; ``` 在 `/Users/kris/code/boss/src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts` 中增加: ```ts import { normalizeRemoteExecutionResult } from "@/lib/execution/remote-runtime-adapter"; ``` 并把 body 传入 `completeMasterAgentTask` 前先标准化: ```ts const normalized = normalizeRemoteExecutionResult(body); ``` 在 `/Users/kris/code/boss/local-agent/server.mjs` 中,把 `conversation_reply / dispatch_execution` 结果拼装收进一个小 helper,输出结构与 `normalizeRemoteExecutionResult` 对齐。 - [ ] **Step 4: 跑 adapter 测试和既有 dispatch/local-agent 测试** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test tests/remote-runtime-adapter.test.ts tests/dispatch-execution-result.test.ts node --test tests/local-agent-codex-task-runner.test.mjs ``` Expected: ```text # pass ``` - [ ] **Step 5: 提交** ```bash cd /Users/kris/code/boss git add tests/remote-runtime-adapter.test.ts tests/dispatch-execution-result.test.ts tests/local-agent-codex-task-runner.test.mjs src/lib/execution/remote-runtime-adapter.ts src/lib/execution/backends/boss-native-orchestrator.ts src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts local-agent/server.mjs git commit -m "refactor: add remote runtime adapter" ``` ### Task 6: 跑整体验证并补文档 **Files:** - Modify: `/Users/kris/code/boss/README.md` - Modify: `/Users/kris/code/boss/docs/architecture/current_runtime_and_deploy_status_cn.md` - Modify: `/Users/kris/code/boss/docs/architecture/api_and_service_inventory_cn.md` - [ ] **Step 1: 跑本轮完整验证矩阵** Run: ```bash cd /Users/kris/code/boss npx --yes tsx --test \ tests/execution-foundation-contracts.test.ts \ tests/execution-prompt-assembler.test.ts \ tests/execution-memory-resolver.test.ts \ tests/execution-permission-policy.test.ts \ tests/execution-backend-selector.test.ts \ tests/remote-runtime-adapter.test.ts \ tests/master-agent-config-resolution.test.ts \ tests/master-agent-openai-fallback.test.ts \ tests/master-agent-message-queue.test.ts \ tests/group-message-dispatch-plan.test.ts \ tests/dispatch-plan-confirmation.test.ts \ tests/dispatch-execution-result.test.ts node --test tests/local-agent-codex-task-runner.test.mjs npm run lint npm run build ``` Expected: ```text All tests pass Next.js build succeeds ``` - [ ] **Step 2: 更新文档,说明执行底座抽象层已落地,但不改变生产主链** 在文档中至少补下面这些事实: ```md - 当前 Boss 已新增 `src/lib/execution/` 执行底座抽象层 - 当前生产主链仍然沿用 `local-agent -> codex exec resume` - 当前已完成 `ExecutionBackend / PromptAssembler / PermissionPolicy / RemoteRuntimeAdapter / OrchestrationBackend` 的默认实现 - 当前 `claw-code` 与 `oh-my-codex` 尚未正式接入生产链,只是 contract ready ``` - [ ] **Step 3: 再跑一次 lint/build 确认文档与代码最终状态一致** Run: ```bash cd /Users/kris/code/boss npm run lint npm run build ``` Expected: ```text No new errors ``` - [ ] **Step 4: 提交** ```bash cd /Users/kris/code/boss git add README.md docs/architecture/current_runtime_and_deploy_status_cn.md docs/architecture/api_and_service_inventory_cn.md git commit -m "docs: record execution foundation refactor" ``` ## Self-Review ### Spec coverage - 已覆盖执行底座抽象层总目标 - 已覆盖 `ExecutionBackend / Selector / SessionRuntime / PermissionPolicy / ToolRegistry / PromptAssembler / MemoryResolver / RemoteRuntimeAdapter / OrchestrationBackend` - 已覆盖现有主链不变的约束 - 已覆盖后续接 `claw-code / oh-my-codex` 前的 contract-ready 目标 ### Placeholder scan - 无 `TODO / TBD / implement later` - 每个任务都给了明确文件、测试、命令和 commit 边界 ### Type consistency - `ExecutionRequest / ExecutionQueuedResult / ExecutionImmediateResult / PermissionCheckResult` 在各任务中保持一致 - `ExecutionBackend` 与 `OrchestrationBackend` 已分层,没有混用 ## Execution Handoff Plan complete and saved to `/Users/kris/code/boss/docs/superpowers/plans/2026-04-02-boss-execution-foundation.md`. Two execution options: **1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration **2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints **Which approach?**