Files
cc-web/.planning/user-input-history-mcp/findings.md

80 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Findings & Decisions: 用户输入历史 MCP
## Requirements
- 新增一个专用 ccweb MCP,只读返回用户输入内容列表。
- 可指定目标对话;未指定时默认当前来源对话。
- 默认返回最近 15 次用户输入。
- 所有返回输入正文合计最多 1000 个 Unicode 字符,并明确标记截断或仍有更早内容。
- 引导表单提交后插入为普通用户消息的内容必须包含在内。
- 结果供 LLM 回看用户要求、作为实现验收参考。
## Research Findings
- 当前暴露的 `ccweb_list_conversations` 只返回会话元数据,明确不返回对话正文。
- 当前 ccweb MCP 工具清单没有读取会话用户输入历史的工具。
- 仓库已有其他活跃计划,当前任务使用 `.planning/user-input-history-mcp/` 隔离,避免覆盖根目录或其他任务计划。
- Trellis 开发者身份已初始化为 `shiyue`;共享 `.trellis/.current-task` 指向其他任务,因此本任务不覆盖该共享指针。
- codebase-memory 项目 `home-cc-web` 索引状态为 `ready`(6308 nodes / 14349 edges),可直接用于函数级定位。
- 第二次独立计划审查已通过,契约足以进入代码研究与实施。
- MCP 公共工具定义集中在 `lib/ccweb-mcp-server.js` 的 `TOOLS`;现有 `ccweb_list_conversations`、`ccweb_prompt_user` 均在此声明 schema。
- 内部 MCP 分派入口是 `server.js` 的 `callInternalMcpTool(tool, args, sourceSessionId, sourceHopCount)`,现有列表工具转到 `listConversationSummaries`。
- `listConversationSummaries` 已提供来源会话清洗、limit clamp、会话文件枚举和运行状态等可复用模式;正文读取应另建只读函数,避免扩大摘要接口职责。
- 引导表单链路关键函数为 `createCcwebPromptUser`、`handleCcwebPromptUserResponse`、`buildCcwebPromptUserResponseText`;需读取精确源码确认提交内容最终是否由普通 `handleMessage` 持久化为 `role=user`。
- 已确认引导表单提交链路:`handleCcwebPromptUserResponse` 用 `buildCcwebPromptUserResponseText` 生成问题/选择/答案正文,再调用普通 `handleMessage(..., { emitUserMessage: true })`;因此提交结果会成为普通 `role=user` 消息,新工具只按角色筛选即可自然纳入。
- 待填写的引导卡本身是 `role=assistant` 且携带 `ccwebPrompt`,必须排除;只有用户提交后的普通消息才应返回。
- `loadSession(id)` 已是统一、安全的会话 JSON 读取入口,包含 ID 清洗、大小限制、会话规范化和运行时线程索引更新;新查询不应自行读文件。
- 公共 MCP `tools/list` 读取 `lib/ccweb-mcp-server.js` 的 `TOOLS`,共享 HTTP/JSON-RPC 调用经 `handleMcpJsonRpcMessage` 与 `isCallableToolName` 进入 `callInternalMcpTool`。
- `isCallableToolName` 会自动接受所有 `TOOLS` 中的公开工具;新增工具无需维护额外 allowlist。`CODEX_APP_COMMUNICATION_TOOL_NAMES` 只控制旧 dynamic-tool 兼容集,是否纳入需结合兼容路径调用点判断。
- 现有回归函数 `assertCcwebListConversationsScopeContract` 同时断言正式 MCP schema、兼容工具定义和内部分派源码;新工具应补同等级 schema/route 契约,并通过内部 MCP HTTP 做数据行为验证。
- `server.js` 的 composer 候选和共享 MCP `tools/list` 都直接展开 `CCWEB_MCP_TOOLS`,因此加入 `TOOLS` 后会自动出现在 `/` MCP mention 与正式 MCP 列表中。
- 旧 Codex App dynamic-tool 兼容路径由 `CODEX_APP_COMMUNICATION_TOOL_NAMES` 精确筛选,并最终仍调用同一个 `callInternalMcpTool`。新工具与 `ccweb_list_conversations` 同属只读会话通信能力,应纳入该兼容集合;正式 MCP 仍是主路径。
- 测试编写阶段检查工作区时,仅本任务计划/Trellis 文件与测试代理临时清单为新增文件,尚无生产代码改动或与用户既有改动冲突。
- 新增聚焦测试 `scripts/ccweb-list-user-inputs-unit.js`,覆盖正式 schema、JSON-RPC/旧内部路由、默认/指定会话、15 条、角色筛选、引导提交、Unicode 总预算、错误码与只读性。
- 首次执行按预期失败:公开 `TOOLS` 尚无新工具,调用返回 `unknown_tool`;只读 fixture hash/mtime 断言已独立通过,说明红灯指向缺失实现而非测试环境破坏。
- 主审阅发现初版测试有两处需修正:禁止覆盖 `HOME`;提交表单正文会合法包含表单标题,排除未提交卡应按角色/消息 ID 断言。
- codebase-memory 一次搜索误报项目未索引,但随后的 `list_projects`/`index_status` 显示 `home-cc-web` 仍为 ready(6387 nodes / 14484 edges),属于瞬时查询异常,无需重建索引。
- codebase-memory 收敛重试又返回 `Transport closed`,已按降级规则停用本轮该查询并改用本地 `rg/sed`;定位范围仅限 `sanitizeId`、`truncateTextValue`、`normalizeSession`。
- 测试代理已移除 HOME 覆盖、改用消息 ID 排除未提交引导卡,并增加临时目录清理;复跑仍只因 `unknown_tool`/缺失 schema 失败。
- 生产实现新增 `listUserInputHistory`,使用 `loadSession` 只读筛选 `role=user`,从最新向前分配 Unicode code point 总预算,再按旧到新返回。
- 正式 `TOOLS`、Codex App 旧兼容集合和 `callInternalMcpTool` 已统一注册 `ccweb_list_user_inputs`,没有第二套实现。
- 主线程与实现代理在交接边界发生一次 apply_patch 上下文冲突;补丁工具安全拒绝覆盖,最终 diff 仅包含代理的目标改动,无丢失或重复代码。
- 语法检查和聚焦测试全部通过,包含 fixture 文件 hash/mtime 不变断言。
- 既有 `ccweb-message-reply-unit` 通过,说明新增 fallback 工具未破坏统一发送/旧别名契约。
- 完整 `npm run regression` 在约 37 秒内通过;`git diff --check` 无空白或补丁格式问题。
- 独立 Trellis 质量审查已通过,无阻断问题;补强了空会话、预算恰好用满(有/无更早输入)和运行时 clamp 测试,复跑通过。
- 运行态检查发现除当前对话外,`63b18af6-1b9a-4ad0-8627-f2b5ba22fc39` 仍为 running;按仓库运维规则不得重启 cc-web,因此当前进程尚未加载新 MCP,需待其他对话空闲后重启。
## Finalized Query Semantics
- `conversationId`:显式传入则读取该会话;缺省才回退到来源会话。显式无效 ID 不得静默回退。
- `limit`:默认 15,允许 1–50;先从全部非空 `role=user` 文本中取最近 N 条。
- `maxChars`:默认 1000,允许 1–1000;以 JavaScript Unicode code point(`Array.from`)计数,约束所有返回 `content` 合计。
- 预算分配:从最新候选向更早候选分配;遇到剩余预算不足时保留该消息开头并标记 `truncated=true`,更早候选不再返回;最后按旧到新排序。
- `hasMore=true`:存在 limit 之外的更早用户输入,或字数预算截断/省略了候选。
- 空白/空正文 user 消息不作为“内容”返回;引导表单提交正文是非空普通 user 消息,因此自然包含。
- 缺少来源会话返回 `missing_source_conversation`;显式无效 ID 返回 `invalid_conversation_id`;会话不存在返回 `conversation_not_found`。
- 返回项包含 `messageId|null`、`createdAt|null`、`content`、`contentChars`、`originalChars`、`truncated`;顶层包含会话 ID、limit/maxChars、总字符数、返回数与 `hasMore`。
- Trellis backend 规范目前大多是占位内容;本改动不涉及“部分 JSON 预览”专项规则。实现应沿用现有 `loadSession`、`mcpToolError` 和 `scripts/regression.js` 的实际项目惯例。
- 已创建隔离 Trellis 任务 `.trellis/tasks/08-17-user-input-history-mcp/` 并写入完整 PRD;因共享 current task 属于其他工作,本任务不改写 `.trellis/.current-task`。
## Technical Decisions
| Decision | Rationale |
|----------|-----------|
| 工具采用只读查询 | 不应改变会话、消息或运行状态 |
| 当前会话默认值由 MCP 服务的来源会话上下文解析 | 与已有跨会话工具的线程级来源语义保持一致 |
| 返回最近 N 条后再按旧到新输出 | 查询高效,同时方便 LLM 顺序理解需求演进 |
| 1000 字作为整体硬预算,从最新输入向前填充 | 限制最坏上下文体积并优先保留最新验收要求 |
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| “最多 1000 字”存在逐条或整体预算歧义 | 独立计划审查后选择更保守的整体硬预算,并增加截断/更多标记 |
## Resources
- `.trellis/workflow.md`
- `AGENTS.md` 中 ccweb MCP 与线程级来源上下文约定