80 lines
8.5 KiB
Markdown
80 lines
8.5 KiB
Markdown
# 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 与线程级来源上下文约定
|