Files

17 KiB
Raw Permalink Blame History

ccweb 消息回传统一影响面映射

结论

本任务的核心影响面集中在 3 条链路:

  1. 公开工具契约:lib/ccweb-mcp-server.js 的 TOOLS 当前同时公开 ccweb_send_message 与 ccweb_request_reply,server.js 的 Codex App fallback dynamic tools 又复制了一份定义。
  2. 内部兼容分发:server.js 的 callInternalMcpTool() 已经把两个工具名分别 分发到 sendCrossConversationMessage() 和 requestCrossConversationReply(); 旧别名兼容应保留在这里。
  3. 自动回传状态机:sendCrossConversationMessage() 通过 expectReply 创建 pending reply;目标完成后由 completeCrossConversationReply()、deliverCrossConversationReply() 写回来源并触发来源自动续跑。

ccweb_request_reply 不能简单从 TOOLS 删除后收工,因为当前正式 MCP 的 tools/call 白名单也复用 TOOLS。如果公开列表不再包含旧名,就必须把 “公开 tools/list” 与 “tools/call 内部兼容旧名” 分离。

代码索引状态

  • codebase-memory-mcp 项目:home-cc-web
  • 索引状态:ready
  • 代码图节点/边:5941 / 13063

文件影响清单

文件 当前职责 本任务影响
lib/ccweb-mcp-server.js 正式 MCP stdio 服务器、正式 TOOLS 定义、tools/list 和 stdio tools/call 白名单 新增/调整共享契约导出;ccweb_send_message 必填 replyMode;公开列表移除 ccweb_request_reply;stdio tools/call 仍需兼容旧名
server.js 导入 CCWEB_MCP_TOOLS、共享 HTTP MCP、内部 dispatcher、跨对话发送/回传、Codex App fallback dynamic tools、pending 持久化 复用共享定义;ccweb_send_message 按 replyMode 归一到 expectReply;旧 ccweb_request_reply 兼容;目标/来源提示增加模式和原始请求;pending 状态新增 originalRequest
README.md MCP 工具用户文档 公开工具表只推荐 ccweb_send_message,说明 replyMode=one_way/return_and_continue;旧 ccweb_request_reply 只标内部兼容别名
scripts/regression.js 端到端回归覆盖 MCP 发送、回传、busy source 队列、Codex App running target 现有 ccweb_request_reply 测试改为兼容测试;新增 replyMode 双模式、公开列表、正式 MCP/dynamic tools 定义一致性、提示内容断言
public/app.js 跨对话 reply 展示、折叠状态、ready count 前端消费 本任务通常不改;但回归会间接受到 returned reply metadata 影响

正式 MCP 定义链路

当前位置

  • lib/ccweb-mcp-server.js:15 定义 const TOOLS = [...]。
  • lib/ccweb-mcp-server.js:110 定义公开 ccweb_send_message。
  • lib/ccweb-mcp-server.js:159 定义公开 ccweb_request_reply。
  • lib/ccweb-mcp-server.js:445 在 stdio tools/list 返回 { tools: TOOLS }。
  • lib/ccweb-mcp-server.js:450 在 stdio tools/call 用 TOOLS.some(...) 做白名单。
  • lib/ccweb-mcp-server.js:503 导出 { TOOLS, prepareImagePayload, runStdioServer }。

需要调整

  • 新建或拆出无副作用共享契约:
    • reply mode 常量:one_way、return_and_continue。
    • 校验集合或归一函数。
    • ccweb_send_message 的 description 与 inputSchema。
  • ccweb_send_message.inputSchema.required 必须包含:
    • targetConversationId
    • content
    • replyMode
  • replyMode schema 必须是 enum:
    • one_way
    • return_and_continue
  • ccweb_request_reply 不再出现在正式 tools/list。
  • tools/call 兼容旧名时不要再依赖公开 TOOLS 作为唯一白名单。可选策略:
    • PUBLIC_TOOLS 用于 tools/list。
    • CALLABLE_TOOL_NAMES 或 isCallableMcpTool(name) 用于 tools/call,包含旧 ccweb_request_reply。

Codex App fallback dynamic tools 链路

当前位置

  • server.js:15 导入 TOOLS: CCWEB_MCP_TOOLS。
  • server.js:10221 定义 codexAppCommunicationDynamicTools()。
  • server.js:10273 复制定义 ccweb_send_message,当前无 replyMode。
  • server.js:10358 复制定义 ccweb_request_reply。
  • server.js:10391 定义 handleCodexAppDynamicToolCall()。
  • server.js:10395-10402 Codex App dynamic tool 白名单仍包含 ccweb_request_reply。
  • server.js:10407 调用 callInternalMcpTool()。

需要调整

  • codexAppCommunicationDynamicTools() 不应再复制 send 的 description/schema。
  • 建议从共享正式定义派生 Codex App fallback 定义:
    • 保留 namespace: 'ccweb'。
    • 复用同一 description 和 inputSchema。
    • 不包含 ccweb_request_reply。
  • handleCodexAppDynamicToolCall() 要区分“公开 fallback tool”与“旧线程动态调用兼容”:
    • 新公开列表不含 ccweb_request_reply。
    • 已加载旧 dynamic tool 的线程调用 ccweb_request_reply 时,内部仍可接受并映射到 return_and_continue。

Composer MCP 候选链路

当前位置

  • server.js:2536 定义 listComposerMcpItems()。
  • server.js:2555 遍历 CCWEB_MCP_TOOLS 生成 / 里的 mcp:ccweb/<tool> 候选。

需要调整

  • 如果 CCWEB_MCP_TOOLS 改为公开 tools/list,则 Composer 会自动不再展示 mcp:ccweb/ccweb_request_reply。
  • 必须确认 / 候选里 mcp:ccweb/ccweb_send_message 的描述与 schema 语义一致。
  • 不要从内部兼容白名单反推 Composer 候选,否则会重新暴露旧工具。

内部 dispatcher 链路

当前位置

  • server.js:6084 定义 callInternalMcpTool(tool, args, sourceSessionId, sourceHopCount)。
  • server.js:6094-6095:ccweb_send_message 直接调用 sendCrossConversationMessage(args, sourceSessionId, sourceHopCount)。
  • server.js:6100-6101:ccweb_request_reply 调用 requestCrossConversationReply(args, sourceSessionId, sourceHopCount)。
  • server.js:5915-5917:requestCrossConversationReply() 只是 sendCrossConversationMessage(..., { expectReply: true })。

需要调整

  • callInternalMcpTool() 是兼容旧工具名的最佳位置。
  • 新逻辑建议:
    • ccweb_request_reply 固定归一为 replyMode=return_and_continue。
    • ccweb_send_message 读取 args.replyMode。
    • 缺少 replyMode 的旧 ccweb_send_message 内部按 one_way 处理。
    • 非法 replyMode 返回 mcpToolError('invalid_reply_mode', ...)。
  • 保持返回字段兼容:
    • one_way 返回现有 messageId、deliveryStatus 等。
    • return_and_continue 继续返回 requestId、status: waiting、 replyDelivery、sourceAutoRun。

正式 MCP HTTP 与 stdio 入口

共享 HTTP MCP

  • server.js:6145 定义 handleMcpJsonRpcMessage()。
  • server.js:6161 在 tools/list 返回 CCWEB_MCP_TOOLS。
  • server.js:6165 在 tools/call 用 CCWEB_MCP_TOOLS.some(...) 做白名单。
  • server.js:6168-6174 调用 callInternalMcpTool()。
  • server.js:6188-6227 定义 handleSharedMcpHttpApi(),从 URL query 读取 sourceSessionId、sourceHopCount 后调用 handleMcpJsonRpcMessage()。

旧内部 HTTP API

  • server.js:6230 定义 handleInternalMcpApi()。
  • server.js:6243-6247 从 body 读取 tool/args/sourceSessionId/sourceHopCount 并直接调用 callInternalMcpTool()。
  • scripts/regression.js 当前大量通过该内部 API 调用工具。

stdio MCP 桥

  • lib/ccweb-mcp-server.js:327 定义 callCcweb(tool, args)。
  • lib/ccweb-mcp-server.js:352-357 将 tool/args/sourceSessionId/sourceHopCount POST 到内部 API。
  • lib/ccweb-mcp-server.js:425 定义 stdio handleRequest()。
  • lib/ccweb-mcp-server.js:445 返回 TOOLS。
  • lib/ccweb-mcp-server.js:450 当前用 TOOLS 做 stdio call 白名单。

关键风险

正式 tools/list 删除旧工具后:

  • handleMcpJsonRpcMessage() 的 tools/call 会拒绝旧 ccweb_request_reply。
  • stdio handleRequest() 的 tools/call 也会拒绝旧 ccweb_request_reply。

因此兼容旧工具名必须覆盖两个 tools/call 白名单,而不是只改 callInternalMcpTool()。

发送与回传状态机

单向发送 / 创建 pending

  • server.js:5808 定义 sendCrossConversationMessage()。
  • server.js:5814-5815 由 options.expectReply 推导 expectReply/sourceAutoRun。
  • server.js:5849 expectReply 为真时生成 requestId。
  • server.js:5857-5874 写入目标 user message 的 crossConversation metadata, 并通过 setPendingCrossConversationReply() 创建 pending。
  • server.js:5883-5887 调用 handleMessage(),目标 runtimeText 来自 buildCrossConversationRuntimeText()。
  • server.js:5906-5911 仅在 requestId 存在时返回 status/replyDelivery/sourceAutoRun。

pending 持久化

  • server.js:222 pending 文件是 config/cross-conversation-replies.json。
  • server.js:4376-4402 normalizeCrossConversationReplyState() 归一状态,当前字段包括: requestId/messageId/sourceConversationId/sourceTitle/targetConversationId/targetTitle/status/createdAt/hopCount/sourceAutoRun/replyText/completedAt/returnedAt/replyMessageId/lastError。
  • server.js:4419-4422 saveCrossConversationReplies() 写入文件。
  • server.js:4430-4439 loadCrossConversationReplies() 启动加载未 returned 的 pending。
  • server.js:4447-4452 setPendingCrossConversationReply() 创建 pending。
  • server.js:4455-4468 updatePendingCrossConversationReply() 更新 pending。
  • server.js:4471-4475 deletePendingCrossConversationReply() 删除 pending。

需要新增字段

  • PRD 要求 pending reply 状态保留原始请求文本,建议字段名使用 originalRequest。
  • 写入点:sendCrossConversationMessage() 创建 pending 的对象。
  • 归一点:normalizeCrossConversationReplyState(),旧状态缺失时填 '' 或明确占位。
  • 输出点:
    • crossConversationReplySummary()
    • getPendingCrossConversationReply()
    • buildCrossConversationReplyAutoRunText()

目标完成与来源写回

  • server.js:9257 普通 Codex 运行 entry 记录 crossConversationReplyRequestId。
  • server.js:6796-6799 普通 Codex 完成后调用 completeCrossConversationReply(),再 flush 来源 pending。
  • server.js:11158 Codex App 运行 entry 记录 crossConversationReplyRequestId。
  • server.js:11322-11326 Codex App 完成后调用 completeCrossConversationReply(),再 flush 来源 pending。
  • server.js:6060-6081 completeCrossConversationReply() 提取目标输出,更新 pending 为 ready,随后调用 deliverCrossConversationReply()。
  • server.js:5969-6048 deliverCrossConversationReply():
    • 来源不存在或目标不存在时改为 failed。
    • 来源仍 running 时保持 ready,稍后 flush。
    • 已处理过则标记 returned 并删除 pending。
    • 追加 ccwebDisplayOnly assistant 消息到来源。
    • metadata 写入 replyToRequestId/processed/autoRun。
    • sourceAutoRun 为真时调用 startCrossConversationReplyAutoRun()。
  • server.js:6050-6057 flushPendingCrossConversationReplies() 在来源空闲后投递 ready reply。

目标运行提示

当前位置

  • server.js:5733-5737 buildCrossConversationRuntimeText(sourceSession, content)。
  • 当前只包含来源标题、来源 ID、消息正文。
  • server.js:5886 sendCrossConversationMessage() 发送目标运行时使用该提示。

需要调整

buildCrossConversationRuntimeText() 需要知道模式,建议参数扩展为:

  • sourceSession
  • content
  • replyMode 或 { replyMode, requestId }

提示必须覆盖:

  • one_way:明确本轮输出不会自动回传来源。
  • return_and_continue:
    • 系统会自动回传本轮最终输出。
    • 目标应在真正完成或明确阻塞后给出完整交付。
    • 不要为回复本请求而手工调用跨对话发送工具,避免重复。
  • 继续保留来源标题、来源 ID、原消息正文。

来源自动续跑提示

当前位置

  • server.js:5744-5748 buildCrossConversationReplyAutoRunText(targetSession, replyText)。
  • server.js:5750-5777 startCrossConversationReplyAutoRun() 使用该 runtimeText 触发来源隐藏续跑。
  • 当前提示只包含目标标题和返回正文。

需要调整

buildCrossConversationReplyAutoRunText() 需要额外输入 pending 或关联上下文,至少包含:

  • requestId
  • 目标对话标题/ID
  • 原始请求 originalRequest
  • 返回正文 replyText
  • 明确要求来源先判断返回是否完整满足原始请求,不能把“已返回”直接当作“已完成”。

建议让 deliverCrossConversationReply() 把 pending 或 { requestId, originalRequest } 传给 startCrossConversationReplyAutoRun(),再传给 buildCrossConversationReplyAutoRunText()。

ccweb_create_conversation 间接受影响

当前位置

  • server.js:5646 定义 createMcpConversation()。
  • server.js:5664 兼容 args.requestReply === true || args.waitForReply === true。
  • server.js:5694-5697 创建后首条消息调用 sendCrossConversationMessage(..., { expectReply: requestReply })。
  • server.js:5723-5728 如果有 requestId,返回 replyStatus/replyDelivery/sourceAutoRun。
  • lib/ccweb-mcp-server.js:101 正式 ccweb_create_conversation schema 公开 requestReply。
  • server.js:10349 Codex App fallback schema 也公开 requestReply。

需要保持

PRD 不要求修改 ccweb_create_conversation 的参数语义。实现 replyMode 时必须确保:

  • requestReply=true 仍创建 pending reply。
  • 返回字段仍兼容现有断言。
  • 目标提示也应按 return_and_continue 模式生成,因为底层仍是跨对话发送。

前端展示影响

前端不参与工具契约选择,但会消费返回消息 metadata:

  • public/app.js:1481-1489 getCrossConversationReplyCollapseKey() 使用 replyToRequestId/messageId 作为折叠 key。
  • public/app.js:7135-7144 收到 session_message 时,如果是 replyToRequestId,会减少 ready reply count。
  • public/app.js:7968-7974 createMsgElement() 用 reply/replyToRequestId 标记跨对话回复气泡。

本任务如果只新增 originalRequest/replyMode metadata,前端无需改;如果改变 replyToRequestId/processed/ccwebDisplayOnly 字段,则会影响展示和 ready count。

回归测试影响面

现有相关断言

  • scripts/regression.js:6348-6386 覆盖 ccweb_create_conversation + requestReply=true 自动回传。
  • scripts/regression.js:6392-6415 覆盖 ccweb_send_message 单向发送与目标 runtime prompt。
  • scripts/regression.js:6417-6434 覆盖跨对话 hop count。
  • scripts/regression.js:6440-6490 覆盖旧 ccweb_request_reply 自动回传。
  • scripts/regression.js:6503-6578 覆盖来源 busy 时 ready reply 排队、pending list/detail、 来源空闲后 flush 和 auto-run。
  • scripts/regression.js:7348-7357 覆盖目标 Codex App running 时拒绝发送。

需要新增/调整断言

  • tools/list 中:
    • 存在 ccweb_send_message。
    • ccweb_send_message.inputSchema.required 包含 replyMode。
    • replyMode.enum 为 ['one_way', 'return_and_continue']。
    • 不存在公开 ccweb_request_reply。
  • 正式 MCP 与 Codex App fallback:
    • send definition 共用同一 schema/description。
    • Codex App fallback 不公开 ccweb_request_reply。
  • ccweb_send_message(replyMode='one_way'):
    • 不返回 requestId。
    • 不创建 pending。
    • 目标 runtime prompt 明确不会自动回传。
  • ccweb_send_message(replyMode='return_and_continue'):
    • 返回 requestId/status/replyDelivery/sourceAutoRun。
    • 目标消息 metadata 有 expectsReply/replyRequestId。
    • pending 文件有 originalRequest。
    • 目标 runtime prompt 明确会自动回传并禁止手工重复发送。
    • 来源 auto-run prompt 包含 requestId、目标标题/ID、原始请求、返回正文、完整性判断指令。
  • 兼容:
    • 内部 ccweb_request_reply 仍成功并等价于 return_and_continue。
    • 旧 ccweb_send_message 缺少 replyMode 仍按 one_way 成功。
    • stdio/shared HTTP tools/call 对旧 ccweb_request_reply 仍可执行,即使 tools/list 不再公开它。

建议实现顺序

  1. 抽出共享契约,定义 reply modes 和公开 ccweb_send_message definition。
  2. 让正式 MCP tools/list 和 Codex App fallback 复用共享 send definition。
  3. 分离公开工具列表与内部可调用工具白名单,保证旧 ccweb_request_reply 可 call 不可 list。
  4. 在 dispatcher 或发送函数中归一 replyMode -> expectReply。
  5. pending 增加 originalRequest,归一、保存、查询和旧数据兼容。
  6. 改目标 runtime prompt 和来源 auto-run prompt。
  7. 更新 README。
  8. 更新回归断言,覆盖正式 MCP、dynamic fallback、旧兼容和 busy source 队列。

主要风险

  • 如果只删除 TOOLS 中的 ccweb_request_reply,旧线程的正式 MCP 调用会被 tools/call 白名单挡住,达不到 PRD 的内部兼容要求。
  • 如果只改 lib/ccweb-mcp-server.js,Codex App fallback dynamic tools 仍会暴露旧工具。
  • 如果 replyMode 在 schema 必填但内部没有兼容缺省,已加载旧 schema 的 ccweb_send_message 调用可能被拒绝。
  • 如果来源 auto-run prompt 仍只包含“已返回”,模型可能把不完整返回误判为任务完成。
  • 如果 pending 删除过早,get_pending_reply 对 returned 历史查询会依赖来源消息里的 replyToRequestId,因此不能破坏 processed/replyToRequestId/ccwebDisplayOnly。