feat(gateway): D3 审查读侧/revert 端点 + 语音用户可见面(L45)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

(a) 记忆入库前审查的两条 admin 端点(主人判「有必要就做」):
  GET /api/v1/admin/memories/reviews?limit= (留痕读侧,不含记忆正文)
  POST /api/v1/admin/memories/{id}/revert (回滚软合并,落审计 memory.review.revert)
  两条 writeAuth(裁定 DECISIONS #10:留痕属私有面,不给 viewer)
(b) 语音用户可见面:DTO voice(含服务端派生 url)+ GET /api/v1/media/{name}
  (200/206/304/416/HEAD、Accept-Ranges、非法名/穿越统一 404 media_not_found)
  + documents.Store.SaveAudio(内容寻址、$DATA_DIR 0600)+ history voice 列读写与引用计数差动

缺口(如实):message_persisted/握手回放仍不带 voice(文件面外,需给 history.Message 加字段);
AMR 需一行 VoiceTranscoder 注入(DECISIONS #11 已排)。4 个变异自检(含一条自认的等价变异)。
This commit is contained in:
AskaEth committed 2026-10-06 14:12:27 +08:00
1 parent 4a937bd269
commit 1420e8aaa6
15 files changed
+2202 -5

No files matched your search

+9 -4
View File
@@ -177,10 +177,15 @@ func (s *Server) handlePatchMessage(w http.ResponseWriter, r *http.Request) {
// 气泡(message_persisted 实时同步契约,D3 编辑
// 路径)。thinking 会从帧中剥掉,以匹配持久化/重放
// 契约;REST 回复保留它,因为消息路由仅 owner/admin。
edited := toHistoryMessage(msg)
edited.Thinking = ""
s.hub.broadcastMessageEdited(edited)
writeJSON(w, http.StatusOK, toHistoryMessage(msg))
//
// 语音面(契约 v0.67 §4.4;切片 L45):响应与广播帧**都要**带 voice(编辑不改变
// 语音事实,但读取面必须一致)。两条 DTO 共用**一次**批量读列 —— attachVoice 按
// id 去重,同一条消息只查一次。
pair := []historyMessage{toHistoryMessage(msg), toHistoryMessage(msg)}
pair[0].Thinking = ""
s.attachVoice(r.Context(), pair)
s.hub.broadcastMessageEdited(pair[0])
writeJSON(w, http.StatusOK, pair[1])
}
// handleDeleteMessage 移除单条消息(D3)。未知 id 返回 404,