# Codex 对话消息操作：前端实现与后端交接

日期：2026-10-23。范围：Arkme 插件内已同步的 Codex 任务对话，不是 DSH 原生会话。

更新说明：本文的复制链接、Codex 私人标记和结构化来源仍需后端。发给自己后续已升级为共享发送弹窗及本机持久化引用视觉；云端结构协议以《选中文字发给自己-跨来源引用云端交接-20261023.md》为准，避免另建重复接口。

## 当前实现

- 消息右键使用统一菜单，支持复制、多选。多选计数和复制文本、复制链接、转发、退出多选全部位于底部；最多 100 条，保持消息顺序。轮询更新不清空未变化的选项，正文变化后取消对应选择，切换账号或任务关闭选区和弹窗。
- 部分文字选择复用现有浮框：复制、全选、标记、查找、发给自己。查找仅覆盖当前已加载的正文，加载更早消息后自动补充；不包含页头、时间及复制按钮。
- 标记复用背景高亮和取消标记交互，保存于 Host 的稳定 SQLite 账号存储，按环境、账号、Codex 来源、任务和消息隔离，不依赖网页端口。当前为“仅本机”，尚无跨设备同步。
- 发给自己弹窗展示任务标题、原作者、原时间及选中文字，用户补充内容后，通过现有 `sources.self-target` / `source.send-text` 创建一条快记。新增内容与引用在同一条记录内。失败保留输入，重试使用相同记录 ID；发送结果未确认时不显示成功。
- 转发复用统一目标选择器、每个目标的幂等重试和现有发送链路。Host 先重新检查任务可读权限，再交付选择快照。私聊/群聊使用 `/api/v1/chats/records/forward` 的通用 agent 快照；快记目标使用现有 forward_records。显示来源为 Codex，包含任务、作者和原时间，不伪造 DSH 会话或原始 Chat 记录 ID。
- 当前转发保留的是用户选择的正文快照，不是服务端签名的 Codex 原文证明；后续结构化来源协议应明确这一区别。

## 1. 必需：Codex 多选复制链接

现有插件复制链接合同及本地分享页实现只覆盖已有 record、agent、dsh_native / dsh_question 等来源，没有可直接使用的 Codex 类型；这是本次源码核对结果，不代表已探测所有线上部署。不能把 Codex 数据当作 `dsh_native` 上送，否则会错误关联会话并显示 DeepSeek 身份。

前端已保留复制链接入口，当前点击明确提示：**“Codex 复制链接：后端接口开发中，开发完成后此功能完整可用。”** 不生成假链接、不创建隐形快记，也不绕开公共审查流程。

建议扩展已有 `/api/v1/chats/messages/copy-link/get-or-create` 的来源联合类型，具体字段名由后端确认。建议源对象包含：

| 字段 | 用途 |
| --- | --- |
| kind = codex_message | 独立来源类型 |
| team_id / source_id / task_id | 识别团队、采集端、任务；本地任务同样需要稳定来源 ID |
| event_id（或 turn_id + kind + sequence） | 消息稳定身份；不使用列表下标 |
| role、display_name、task_title | 正确显示来源，不能强制改为 DeepSeek Harness |
| occurred_at | 原消息时间，建议 Unix 毫秒，明确时区展示规则 |
| text_content、text_format | 选择时冻结的正文，保留 Markdown；不包含 UI 元数据 |
| content_digest / revision | 校验快照版本，避免同一事件更新后静默替换已分享内容 |

要求沿用普通消息的敏感内容审查、安全检查和分享页生命周期。审查未通过时不能返回可访问链接。复用现有幂等生成机制：同一顺序、版本和权限范围的选择应得到同一分享结果；取消、超时、失败均保留当前选择。

权限需区分“能看任务”与“能向团队外分享”，服务端校验当前账号和来源访问权。来源已删除/撤权时应明确失败，不能信任客户端自己声明的成员、角色或原文。对于仅存在采集端的历史数据，请明确允许分享快照的授权方式，不能假装云端已保存原文。

分享页需支持 Codex 标识、任务标题、真实消息角色与时间，按选中消息原顺序展示，支持移动端；不显示 DeepSeek 专属头像、默认作者或无效跳转。链接过期、撤销、重复请求和旧客户端兼容沿用公共分享协议。

## 2. 必需：Codex 标记跨设备同步

建议复用统一 annotations 能力，扩展独立 Codex 源类型。不要把 Codex task_id 放入 DSH session_id 冒充同源。

- 锚点采用现有 `arkme-text-v1` 可见正文投影，包含 start/end、text、prefix/suffix、sourceDigest；坐标使用 UTF-16 code unit，与当前前端一致。Markdown 的工具按钮、代码标签、日期和头像不进入投影。
- 当前本机身份为 `[环境, 当前账号, codex, [teamRef, sourceId/installationId, taskId], eventId]`。其中 teamRef 是插件内部引用；对外协议需由 Host 解析成真实身份，不能直接把它作为云端权威主键。
- 提供能力查询、批量读取、添加、删除接口；operationId 幂等，返回最终 annotation_id、版本和保存状态。取消标记需要删除版本或 tombstone，防止离线设备重新恢复已取消标记。
- 默认个人标记，同团队能看到原文不代表能看到其他成员的私人标记。正文版本不匹配应返回失配状态，禁止首个同文字匹配或随意迁移偏移。
- 接通后才能显示“已同步”。迁移本机标记必须核验来源和摘要，保留失败项、支持重试，不能将网络失败当作空列表覆盖本机标记。

## 3. 增强：转发和引用的结构化来源

当前通用转发快照可保留可读的来源信息，但没有 Codex 专属结构化来源和可靠跳回原任务能力；需要后端给出来源字段、权限判断和接收端降级展示合同。

发给自己当前是“新增正文 + 引用来源 + 引用文字”的同一条文本快记，没有使用 Chat 专属的延展关系 ID。如果需要点击引用返回指定 Codex 任务和消息，应扩展跨来源引用协议，定义 source type、稳定 task/event 身份、冻结选区、正文版本及无权限/原文删除状态。无需新增一个仅用于保存纯文本快记的接口。

## 联调验收

1. 本机任务和远程团队任务各选 1 条、多条；相同 eventId 在不同采集端不得混淆。
2. 用户提问、AI 回答、代码、表格、重复词、emoji、跨行选区都正确引用；已截断或来源变化的整条快照不能转发。
3. 分享走公共审查；拒绝、超时、撤权、重试、过期和撤销可验证。分享页正确展示 Codex 来源。
4. 标记两设备同步、取消标记、离线重连、正文变化、账号切换和旧本机迁移无串号、无复活。
5. 转发和发给自己的发送幂等：网络结果不明后重试不重复创建；部分目标失败只重试失败目标。

代码入口：`CodexMessageInteractions.tsx`、`SelectedTextSelfNote.tsx`、`codex-message-actions.ts`、`message-action-service.ts`、`message-action-infrastructure.ts`、`native-local-marks.ts`。

本次验证边界：交互在 3102 实际页面核验；发送、重试、存储隔离及请求结构通过自动化测试核验。未向真实账号提交测试快记、未向联系人发送测试转发，未发布到 dev/master。

## 额外观察：云端任务来源偶发不可用

3102 验证中观察到已有任务列表轮询偶发显示“部分来源暂不可用”，随后清空当前任务，导致阅读器、选区和引用弹窗一起卸载。`readPersonalCodexState` 将失败来源排除，`TeamCodexActivity.refresh` 用该结果替换列表，选中项缺失后自动切换或清空。它与本次未变化正文的 DOM 节点稳定性问题是两条不同链路。

当前还不能仅根据 UI 判断失败属于网络、服务异常还是权限撤销。本次没有修改这条既有权限降级路径；需要保留后端错误分类，先取得真实失败响应再修复。推荐仅对明确的临时网络/服务故障保留已有阅读和用户输入、显示更新失败并重试；明确撤权、退出团队或账号切换仍立即清理。不要把所有失败都当作临时错误继续显示历史敏感内容。
