Commit Graph
127 Commits
Author SHA1 Message Date
AskaEth d47185feb8 docs(contract): v0.97 voice WS frames + TTS autolaunch + upstream error safety
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- voice 进 message_persisted/channel_persisted 首帧与握手回放(M44 fb55769,
  INSERT→写列→notify;两条实现前提进契约)
- emotionTTS 自启默认关(M47,voice-tts-launch.json,probeUrl 单向兜底)
- UpstreamError 构造即安全:code/type 固定令牌守卫、message 默认缺席
  (M49/M50,一处行为变更如实标注)
- U6 双凭据门明示(Bearer JWT 或 X-API-Key,既有能力非新增)
- 改正 14 处「WS 帧不带 voice」假话 + 旧横幅加注;尾注 v0.82→v0.97
2026-10-26 21:44:44 +08:00
AskaEth af61b8ccfe docs(contract): v0.96 语音发音人绑生效人格(sessionId/两个错误码)+ 管理面脱敏投影与 props.omitted
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-26 14:10:40 +08:00
AskaEth 240b59708d docs(contract): 矩阵补 B28-B31 与计数改正(14+31+40=85,纠正 104 的重复计入)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-26 12:48:53 +08:00
AskaEth 4c79728553 docs(contract): v0.94 MCP 管理面与工具 origin + v0.95 渠道静默窗口,含写门不对称与呈现隔离口径
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-26 12:46:25 +08:00
AskaEth bd49080bd1 tower: snapshot of uncommitted base checkout changes (worktree wt-32) 2026-10-24 21:51:07 +08:00
AskaEth 0c2322bb92 docs(api): 契约 v0.93 — 审查预算的唯一真源(MEMORY_REVIEW_TIMEOUT_SEC 默认 20→90)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-13 23:28:39 +08:00
AskaEth 80096c8bd1 docs(api): 契约 v0.92 — 记忆整理调优键读写端点(B26/B27,切片 M4)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-13 21:38:47 +08:00
AskaEth e32d85afc3 docs(api): 契约 v0.91 —— recallMode 如实上报(新增 mixed/空态语义)
起因:M6 诊断发现旧实现把「向量路径实际失败、回退词项」静默报成 embedding,面板看不出降级。M9 改为上报实际选用的召回路径,取值域扩 mixed(部分分区降级)与 ""(无候选可比)。同步:版本行 + §6 v0.91 条目 + §4.12 的 recallMode 值表;并记录「客户端 5s 超时未改、改的是批上限推演」这条对客户端透明但重要的取舍。
2026-10-13 20:37:17 +08:00
AskaEth ae68bb0241 docs(api): 契约 v0.90 —— 渠道连接态三态(可选 pending/ready/reason + connected 语义收紧)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
登记 POST /admin/channels/open|close 响应与 GET /admin/channels 平台行/账号行新增的三个可选键,并写明 connected 由「动作被受理」纠正为「真实连接状态」这一行为变更(老客户端若只看 2xx 会继续骗人)。新增 §2.4(d) 字段级说明;C19/C20 两行标注;计数不变(14 + 25 + 40 = 79)。
2026-10-13 19:41:14 +08:00
AskaEth da1d23dd23 docs(契约): 补两条口径 —— 非 chat 模式(单卡正文)与 br 不分隔的位置
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 非 chat 模式(工作/代码/文档/首页/视觉/语音/智能体/设置):br 是真换行、
  不切分;行内白名单照常出样式;白名单外逐字原样;不许出现字面标签。
  各模式既有较强排版约定优先(工作模式按行切清单、代码模式 pre 块)
- br 只在普通文本层分隔:角色注记内部与白名单行内元素(尤其 code)内部
  都不分隔 —— 前者会把注记正文漏成聊天气泡,后者会破坏成对元素平衡、
  触发整段降级反而露出裸标签
2026-10-07 18:45:08 +08:00
AskaEth 1107b2a717 docs(契约): 行内白名单定为六项(加回 u,s 仍不在内)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-07 18:37:42 +08:00
AskaEth 6ac214366e docs(契约): br 改为「气泡/消息分隔符」(主人口径),行内白名单收窄到五项
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人 2026-10-07:「对消息内的 <br> 始终处理为多条消息隔断」。

- br 语义与 p 同级:两边文本成为两条独立消息(IM)/ 两个独立气泡(Web),
  而不是同一气泡内的换行;<br> / <br/> / </br> 三种写法等价
- 行内白名单据此收窄为五项(b/strong/i/em/code),br 移出白名单
- 明确影响面:含 br 的回复在 IM 上会变成多条消息(受既有气泡间隔节流)
- 标注「实现跟进中」:Web 端 2dae469 当时把 br 当行内换行、IM 出站当时
  连行内标签都还没剥,两侧都在改;以契约为准,不以当日实现为准
- IM 出口那节补一句:br 不按行内标签处理,按切分走
2026-10-07 17:20:47 +08:00
AskaEth d27c1d4e33 docs(契约): v0.89 修订 —— 白名单收窄六项 + 边界行为降为实现细节 + 补 IM 出口口径
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
与安卓(客户端)会话对齐后的三处修订:

- 白名单八项 -> 六项(去掉 u / s):它们在她 100 条输出里 0 命中,
  不值得让各端为它们返工;各端可额外容错,但契约只承诺六项
- 结构不平衡的粒度(整段降级 vs 只坏那一对保持字面)降为实现细节:
  契约只承诺「不产生行内渲染 + 字符一个不丢」;这类输入极罕见,
  为它让任一端返工不划算
- 新增 IM 出口口径:成对且平衡的白名单标签 -> 剥标签留文本;
  br -> 换行;白名单外与不平衡 -> 原样保留(逐字不丢)。
  与 action / narration 的「整体剥除」语义不同(那是角色扮演标记)

大小写口径(Web 不敏感 / 安卓只认小写)留待各端自定,已与安卓协商。
2026-10-07 17:19:48 +08:00
AskaEth b989f0bb0c docs(契约): v0.89 登记助手消息的行内标签白名单
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
依据:主人 2026-10-07 要求前端支持这类标签的基本渲染(Web 端实现已推库
2dae469,真机产物已一致)。

- 白名单:b/strong、i/em、code、u、s(成对)+ br(void),大小写不敏感
- 三条硬口径:白名单外按纯文本原样显示(字符一个不丢)/ 属性一律不解析 /
  结构不平衡(未闭合、交错)整段降级为原文
- 安全红线:助手文本是不可信内容,任何端都不许整体当 HTML 注入;
  正确姿势是解析成结构化节点、交给各自的安全渲染原语
- 范围克制:不做块级(段落仍走 §4.16 的 p 机制)、不做 a、不做 Markdown
- 服务端零变更:无新端点 / 帧 / 字段,计数仍 14 + 25 + 40 = 79
2026-10-07 17:15:32 +08:00
AskaEth a4efbffe60 docs(架构): 插件开端口的四条护栏转为「已确认采纳」
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人 2026-10-07:「其他几个选项按你的最优解来」⇒ 四条护栏正式采纳,
去掉 AGENTS.md §9 与契约 v0.88 里的「待主人逐条确认」字样。

护栏内容不变:显式配置 / 默认绑 127.0.0.1 / 日志与审计 / 不得绕过身份判定。
2026-10-07 16:16:29 +08:00
AskaEth 5b8c7a3ef3 docs(架构): 澄清单端口红线只约束框架自身,扩展插件可开端口
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人 2026-10-07 口径补充:原先把这条红线读成「任何组件都不许开端口」过宽了。

- AGENTS.md §9:红线加限定语(只约束 Cyrene 框架自身);qq-obv11 那条已登记
  例外升格为一般规则的实例;新增插件开端口的四条护栏(显式配置 / 默认绑
  127.0.0.1 / 日志与审计 / 不得绕过身份判定)—— 护栏由主代理拟,待主人逐条确认
- Docs/api-client.md v0.88:澄清「插件监听端口不在契约范围内」;零破坏性变更,
  无新端点 / 无新帧 / 无字段形状变化,路由计数不变(14 + 25 + 40 = 79)
- README.md:用户侧表述同步

DevDocs 侧同步(不进 git):design/13 §0、design/09 §5、design/08 §4、恢复提示词红线节。
2026-10-07 14:09:01 +08:00
AskaEth ce9af42945 feat(search): 新增腾讯云·元宝搜索与秘塔搜索两家 provider
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
接入位表六家扩到八家,端点形状与错误码一字未改(契约 v0.87,仅两处名单)。

- tencent_wsa:POST {base}/SearchPro + Authorization Bearer,零签名。
  两条官方口径决定实现:HTTP 状态码恒 200(判失败只能读 Response.Error.Code);
  Pages 是「JSON 字符串数组」需二次解析。错误码按官方表分类
  (UnauthorizedOperation/AuthFailure.* = 凭据、ResourceUnavailable = 欠费、
  RequestLimitExceeded* = 限流),文案只带 code 不带上游 Message。
- metaso:POST {base}/api/v1/search,响应 schema 仅第三方来源且互相打架,
  实现走宽松解析(数组候选与字段名回退),逐处标「未实测」。
- 零新 env 键:两家只走 $DATA_DIR/config/plugins/web-search.json,
  .env / .env.example 未动;组合根 websearch_providers.go 零功能改动。
- 契约 v0.87:values.provider 合法闭集与 plugin.providers 候选各加两条,
  计数不变(14 + 25 + 40 = 79)。
2026-10-07 13:42:41 +08:00
AskaEth 74e171bc04 docs(contract): v0.86 —— 搜索条数上限可在面板配(字段 + values 键 + 回显键;无新端点)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-07 11:11:24 +08:00
AskaEth 8325d1bb8c docs(contract): v0.85 —— B25 按动作批量拒绝候选 + 轮末自动落地(行为变更)+ link 候选停产
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-07 11:08:07 +08:00
AskaEth 23dc9a3e5e docs(contract): 修回 §5 开头被吃掉的 blockquote 首行(v0.84 登记时的编辑失误)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-07 10:29:49 +08:00
AskaEth 77dbf9b1f7 docs(contract): v0.84 —— 显示名形状变更 + 段落标签硬约束 + 浏览器降级档登记
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
① plugin.providers 候选:裸字符串数组 → 对象数组 {value,label};两条硬口径
   (label 仅供显示、value 才是落盘值;前端三形状兼容 ⇒ 其他插件零影响)。
② 新增 §4.16:助手文本不得出现未配对段落标签(一个 <p> = 一段、段落不嵌套),
   含实测根因两条 + 修后处置口径 + 「客户端仍应保留容错」与护栏清单。
③ 模型面登记:web-fetch 浏览器降级档的 fetchedBy(direct|browser)如实标注。
④ provider 配置面去 env 化(对客户端零形状影响)一并登记。
⑤ 计数不变:无新端点 ⇒ 14 + 24 + 40 = 78。

顺带修一处我自己的失误:上一版登记 §4.15 时把 `## 5. env 与模型配置` 标题
误删,已补回(§5 表格内容未受影响)。

诚实边界:web-search 真机工具路径仍有一处未部署的修复(linkless)已登记;
显示名真机点击未跑 + dist 是 go:embed(前端改动必须重新 build);
viewer 角色表格仍显示裸 id;腾讯/秘塔适配未实现。
2026-10-07 10:29:39 +08:00
AskaEth ef8fc66e54 docs(contract): v0.83 —— 记忆整理应用面五条端点 + 读侧 status 闸 + 两处过时口径改正
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
新增 §2.2 B20–B24(候选列表 / 单条应用 / 批量应用 / 单条回滚 / 手动真跑)、
§4.15 详解(三条硬约束:幂等不是错误 / 超限必须如实 / 软动作永不物理删除)、
错误码表补 404 tidy_run_not_found、409 tidy_disabled、500 六个 tidy_*、503 tidy_timeout;
计数 writeAuth 19 → 24、总数 73 → 78(按 routes.go 核:writeAuth 挂载 admin 26 条,
减去历史口径的 channels/events 与 counters ⇒ 24)。

改正两处已成假话的 §7.2 条目:
- 「整理候选 apply/revert/列表/运行均未实现」→ 已实现五条,仍缺 reject / 运行历史读口 / 按 run 筛候选
- 「面板记忆页无法过滤」→ 补一句:v0.83 起服务端已按 status 过闸,字段面仍未补齐

诚实边界写进契约:生产库零动作 / 面板真机未跑 / 旧候选 run_id 为空串 /
run_id 列只在存储包前滚 / 语义召回不可复现。
2026-10-07 09:51:57 +08:00
AskaEth 20f82c9679 docs(contract): v0.82 —— resultSummary(失败也带)+ 自排成本可见面 + browser 面板(Z4)
- tool_execution.end 的 resultSummary(含失败口径、≤500 rune、空 ⇒ 缺席)
- GET /admin/backthink 加 ?trigger=(闭集 + 未知/空 400 invalid_trigger + 下推 SQL)+ 新端点
  GET /api/v1/admin/backthink/self-wake(writeAuth、三态、gate 快照、*IsDefault 语义)
- GET /admin/panels 新增 browser/settings + 六个动作(3 读 3 写)+ 错误码 + turnAttachment
- request_think 可达性改正(已上线);计数 72→73(writeAuth 18→19)
2026-10-07 09:34:46 +08:00
AskaEth a820d9968e docs(contract): v0.81 —— 时间轴 retentionBoundaryReached + round 跨重启单调 + 自排可见面登记(X5)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 新增响应键 retentionBoundaryReached(与 truncated 分意,判据三条 + 三个盲区如实登记)
- round 跨重启不再重复(行为修复,客户端可放心按 round 分组)
- 登记 trigger=self_wake 与 4 条 backthink.self_wake.* 审计动词,并标明入口工具 request_think
  **未接线 ⇒ 生产里目前不会出现**
- §7.1 两行状态;计数不变(无新端点 ⇒ 14+18+40=72)
⚠️ 该键取自 X1 工作树实现 ⇒ 已在文档 4 处标明「提交后按 git show 逐字复核」(本提交即该实现)
2026-10-06 21:16:25 +08:00
AskaEth ea150ceea9 docs(contract): §2.0 端点计数回补(14+18+40=72)+ S6 未确证项收口(V4)
- writeAuth 17→18、总数 71→72(并写明自 v0.76 起 −1 的滞后已修正;历史版本条目保留原写法)
- S6 的未确证项逐条核对:能验的给代码级证据,验不了的写明「为什么验不了」(无凭据/禁重启)
- 契约版本仍 v0.80(同版内记账回补,零形状变更)
2026-10-06 20:25:14 +08:00
AskaEth ea43f07bce docs(contract): v0.80 —— 时间轴出口 + provider 热重载 + AMR 落副本
三批登记(S6 按代码逐条核对,30 条 文件:行 证据;我复核版本行与唯一当前版本):
① W1 GET /api/v1/admin/flow/timeline(时间正序/游标/retentionHours/truncated,计数 16→17、70→71)
② W3 热重载 + primary/fallback:新增错误码 primary_not_configured(400)/config_unstartable(409),
   并改正 3 处已变假话的旧口径(『不热替换、需重启』『无主 provider 字段』)
③ W4 VoiceTranscoder 已注入 ⇒ AMR 落 wav 副本;6 处已变假话处改正
   (S6 主动纠正一处口径:微信 iLink 语音刻意不下载字节,不算进『已修』)

⚠️ S6 发现既有记账错误:§2.0 条数自 v0.76(cb232e7) 起少 1(实测应为 14+18+40=72),
已在文档内如实标注请裁决,未擅自回补。
2026-10-06 18:46:26 +08:00
AskaEth 78e99d9f4c docs(contract): v0.79 —— 面板动作真可用 + 记忆审查/语音可见面 + 回合标识全覆盖
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
四批一并登记(DOC-3 按代码逐条核对,含 文件:行 证据;我复核版本行/唯一当前版本/端点面):
① PLG-2 动作真可用:SetExecutor 已删、生产执行器接线(wire_frontend.go:205 →
   wire_plugin_actions.go:61-89);错误码按代码重写 —— action_not_implemented(501) 只剩
   「已声明未登记处理器」,兜底是 502 action_failed;并纠正一处输入说法:网关层非法 JSON
   回 bad_request(非 invalid_payload),两个码都登记
② CODE-TC-2 回合标识全覆盖:StartRoundWithTurnID 是唯一生产建图点;graph.id 对单路也追加
   #{回合标识} ⇒ 契约明写「一律用响应里的 id,不要自拼」
③ L45:新增 B16 GET /admin/memories/reviews + B17 POST /admin/memories/{id}/revert(writeAuth)
   + U19 GET /api/v1/media/{name} 与消息 DTO voice(覆盖率按实际填充面写,帧/握手回放标注不带)
④ L2/SK-5 零契约变更(已在报告说明)
计数:writeAuth 14→16、总数 68→70。

⚠️ DOC-3 指出的易误用点(已在文档区分):turns[].turnId(=graph.id) 与 lanes[].turnId(=权威 turn-N) 不同源。
未确证:documents.SaveAudio / UpdateMessageVoice / RevertMerged 本体未逐行核(标「转述自 L45」);真机未跑。
2026-10-06 14:20:08 +08:00
AskaEth a150211468 docs(contract): v0.78 登记 16 路已落地的六批形状(含 5 处如实的覆盖率/未实现标注)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
六批:① 消息读取面 toolCalls[] 已实现(迁移 0023 按路键;**覆盖率只到真并行路**)
② 管理面 panels + 动作端点 + panels_changed 帧(**动作执行器无生产实现 ⇒ 现全回 501**)
③ 搜索 provider 可变源(config/plugins/web-search.json;不新增 env 键)
④ 账号上下线落事件 + channel_event 帧(**只覆盖账号状态这一路**)
⑤ iLink 账号三态(-14=被拒非过期;**accounts[] 每账号三态未做**)
⑥ 语音:**只登记落库面已实现**,用户可见面(DTO voice + /media/{name})仍标未实现

我复核并改掉一处 overclaim:v0.78 总述原写「六条全部已实现」与 ⑥ 自述矛盾 ⇒
改为「①~⑤ 已实现;⑥ 只到落库面」。

值得记的一笔:DOC-2 拒绝照抄我任务书里「把语音改成已实现」的要求,回代码核到三条机器证据
(无 /media/ 路由、DTO 无 voice 字段、转码器无注入点)⇒ 没实现的没写成可用。这是对的。

未确证(文档内已标):六批都没在运行中的 :10942 上验过(本批禁换产物/重启);
lane_turn_id == chat_messages.turn_id 仍是代码级推论。
2026-10-06 09:11:48 +08:00
AskaEth 180128c207 docs(contract): v0.77 登记 —— graph_update 按路分桶 + MT-A 事实/异步口径 + MT-B 旋钮说明
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
三批一并登记(都由 DOC-1 文档代理按代码逐处核对,含 文件:行 证据;我复核了四个核查点):
① FV2:graph_update 帧加 turnId/round(omitempty)、图按 {会话}#{路} 分桶(单路逐字不变)、
   GET /admin/flow/graph 加 lanes[](恒非 null,graph 仍是「最新一路」⇒ 老读取方零改动)、
   新增 §4.11(d) 专写增量键与缺席语义;§7.4-19「两路互顶未修」→ 已在 v0.77 修复;
   §7.4-23 改写为 4 条(明写「toolCalls[] 前提已具备但本身仍未实现,别读成已实现」);
   新增 §7.4-24(真并行未做真机端到端 + 已知窄竞态)
② MT-A:事实面 12→13(MEMORY_TIDY_COMPARE_LIMIT)、候选默认 20→200、truncated 第 4 档 (budget)、
   异步实现口径(并发复用 runId / 同一 3 分钟 / result 逐字节同形 / running 时双缺席 /
   重启 404 tidy_run_not_found)
③ MT-B:4 个旋钮写清「不进 .env(.env 已冻结)⇒ 有效行为=代码默认值,调优入口待接线」;
   它建议的两条 admin 端点只进「未实现」清单(机器证据:代码与前端 zero 命中)

顺手清掉我早前一次编辑在版本行留下的重复文本(DOC-1 指出但按边界没动)。
2026-10-06 08:23:49 +08:00
AskaEth fe4c702f87 feat(gateway): 未读面 unreadCount 进 message_persisted 帧(契约 v0.76 ②)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- broadcastPersisted 是 Web 与 IM 两条路的**共用**函数 ⇒ 两处口径天然一致,不存在分叉
- 口径 = 绝对未读 + 主人单一已读游标(chat_sessions.read_at)⇒ 多端共享
- 可选键 omitempty:只在 >0 且确实算得出来时出现;0 与「算不出来」都不顶位
  (0 只能从「会话有游标」推出,否则那个 0 就是编的)⇒ 缺席 = 老行为
- Docs/api-client.md:v0.76 ① 按实测改标「未实现且无实现路径」(见下),② 已实现,
  并更正「GET /sessions 的 unreadCount 自 D4 起已在」、新增 §4.13 与 §7.4 第 23 条

⚠️ ① toolCalls[] 本版未落地(取证结论,非推断):chat_messages.turn_id(编排器随机路标识
turn-<16hex>)与 flow_graphs.turn_id(trace.FlowGraph.ID = <session>-<轮次序号>,进程内递增)
不同源,真实库中 66 条消息行 vs 21 条图行字面交集为空 ⇒ 无法 join;按时间窗硬凑属于猜,不做。
补齐路径:链路图带回合标识(新键 + 迁移 0023 起)+ 落盘列,之后 toolCalls[] 是一条批量 SQL。

我亲自复跑的变异:去掉帧里的 n > 0 守卫 ⇒ TestBroadcastPersistedUnreadCountAbsentWhenUnknown
与 TestBroadcastPersistedCarriesUnreadCount 双双 FAIL(断言「0 不该顶位」)⇒ 测试是真护栏。
2026-10-06 07:58:42 +08:00
AskaEth cb232e7360 docs(contract): v0.76 登记三面(工具回看 toolCalls[] / 未读 unreadCount / 记忆整理试算加性异步 B15)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- ① 消息读取面 toolCalls[]:数据源 = 已落盘轮次链路图(flow_graphs/0022,168h),
  不从正文反解;图取不到 ⇒ 字段整体缺席(绝不回空数组冒充「没调过工具」)
- ② message_persisted.data.unreadCount + GET /sessions 列表项同键;
  口径 = 绝对未读 + 主人单一已读游标(chat_sessions.read_at)⇒ 多端共享(与安卓已对齐)
- ③ 记忆整理试算加性异步:?async=1 ⇒ 202 + runId,新增 B15 轮询端点;
  不带 async 行为逐字不变;结果仅进程内存(TTL 30min / 并发 1 / 重启即失效 ⇒ 如实 404)
- 三批均标注「契约先行 · 尚未落地」,实现落地后回改并去掉标注
2026-10-06 07:42:19 +08:00
AskaEth 236b202fda feat(backthink): 静默时段心跳降频(A)—— THINK_QUIET_INTERVAL_SEC 默认 3600
主人裁定「先做 A(静默降频)再转向 C」;背景实测:静默期**思考频率不变**(恒定 900s 心跳),只拦打扰 ⇒ 成本照付。
- 新键 THINK_QUIET_INTERVAL_SEC(默认 3600):窗口内用该间隔、窗外仍用 THINK_INTERVAL_SEC;**≤0 = 关闭降频**(不许出现「半夜完全不思考」暗坑);窗口关闭/钟点非法 ⇒ 不降频
- 心跳**每拍按时刻选间隔**(放到循环外就永远不生效——变异实测能打红)
- 🔴 不变量:**事件驱动不被降频影响**(TestBackthink_EventTrigger_NotSlowedByQuietInterval 钉住)
- 事实卡 9→12 条(+思考总开关/普通间隔/静默间隔,标「改需重启」);契约形状未变、未升版
- ⚠️ 如实登记:ENABLE_BACKGROUND_THINKING 是**死配置**(全仓无消费者),已在 env/面板标签/契约三处标注,**是否接线待主人拍**
- 变异 5 条全红;C 的缺口清单(唤醒面 7 处)已回给主会话排期
2026-10-05 22:38:17 +08:00
AskaEth d6ed9cfa2b feat(memory): 记忆整理「试算(dry-run)」入口 + 面板卡(契约 v0.75)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人要「现在就想看到在 736 条上它会建议合并哪些」,而不是等 23:00 的低峰首跑。
- 后端:Tidier.DryRun + TidyService.DryRun,**复用同一条扫描→召回→判定→构造候选路径**(抽出 tidyRoundPolicy,生产轮与试算轮只差「候选出口」与「召回降级策略」两处;禁止第二套实现)
- 🔴 零副作用:不写 memory_tidy_candidates / memory_tidy_runs,绝不改 memory_items 任何列;机械证明 = 跑前跑后 cyrene.db 与 -wal 的 SHA256 一致(测试断言「表行数不变」)
- 失败码诚实:500 tidy_unavailable / 409 tidy_disabled / 500 tidy_recall_unavailable(**embedding 不可用不降级成 0 条**)/ 503 tidy_timeout / 500 tidy_dry_run_failed;**空数组唯一含义 = 算过了没建议**
- 端点 POST /api/v1/admin/memories/tidy/dry-run(writeAuth);面板「记忆」页加试算卡(点了才跑、无轮询、三态分开、明写「仅试算,不会改动任何记忆」)
- 真机 e2e(741 条活库副本):76 ms / 15 分区 / 53 候选 / 41 skipped / 1 vetoed(low_confidence);样例 merge reason=fact_subset score=1.0000、link reason=similar_but_not_contained score=0.9248
- 变异:dry-run 顺手写库 ⇒ 两条测试红(行数 0→2);自造阈值 ⇒ 红;失败回 200 空数组 ⇒ 红
- 如实登记:Compared=688 被 400/分区护栏截断、候选上限 20/分区被打满 ⇒ 护栏值需在 741 条量级重定(非 bug)
2026-10-05 22:06:54 +08:00
AskaEth f4adf0b526 feat(notify): IM 路由的主动消息也在非前台补浏览器通知(契约 v0.74)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人裁定 C 的必然推论:她该不该提醒我,与后端走了哪条路由无关。P0 如实报回的缺口 = 通知只挂在 proactive_message 帧上,而 auto 提示在有已连接 IM 渠道时优先走 IM ⇒ 最常发生的路径恰恰不弹。

判据(先查现状、不猜):行字段**区分不出**主动推送与普通回复(已用真库证据排除 platform/adapterId/turnId 反推)⇒ 新增**结构化的正向声明**:帧顶 data.proactive === true,只由主动推送那条通道打(gateway.BroadcastProactivePersisted);普通路径永远传 false。
- 后端:channelbridge 抽出唯一写入口径 persistMain + 新增 proactive_notify 接缝;gateway 抽出 broadcastPersisted(msg, proactive) 两入口共用
- 前端:handlers/proactive-notice.ts 两条帧路共用同一套提醒运行时(有界去重 + 降级说明 + DOM/Notification 取值);message.ts 读到判据才走提醒
- 对照断言(必须有):普通 IM 回复/主人提问/显式 proactive:false ⇒ **0 通知、3 气泡**(不误伤)
- 变异实测:去掉非主动保护 / 跨两路去重失效 / 前台也弹 / IM 不发判据帧 / 普通广播带判据 ⇒ 5 条全部打红
- 契约 v0.74(纯增量:message_persisted 新增可选 proactive:true)
- 边界(如实):判据**不落库**(翻历史不弹提醒,也不得从行字段反推);落库失败时不发判据帧(无权威 id 的帧前端整帧丢弃)
2026-10-05 19:47:54 +08:00
AskaEth a6cb44b8a8 feat(flow): 链路图本地落盘 + 多轮历史回放(design/19 §P3,契约 v0.73)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人要求「链路流程应该有本地数据,不然每次重载之后页面就没数据了」:
- 落盘:迁移 0022_flow_graphs(session_id/turn_id/graph_json/时间戳 + 唯一键 upsert)+ internal/runtime/flowgraph(RecordRound/Graph/Latest/Turns/Turn + 保留期 GC)
- 写入:loop.EndRound **每轮一条**(锁外深拷贝后写;失败 WARN 且不打印图内容);只落发布会话白名单(复用 flowSessionAllowed)
- 读回:端点加 turnId + source(live/persisted/none) + turns[](最新在前,空为 []);**内存优先、miss 才落盘**(最新一轮永远最新)
- 前端:flowGraphs 纳入 persistOptions.partialize(**刷新即刻有图**,version 3→4 + migrate)+ 历史轮选择器 + 诚实文案;顺带收 L1 转交的 flow/NodeDetail.tsx 节点日志倒序
- 新 env 键:FLOW_GRAPH_RETENTION=168h / FLOW_GRAPH_GC_INTERVAL=1h
- 变异实测:每 span 一行 / 保留期守卫失效 / partialize 去掉 flowGraphs / 去掉内存优先 / 白名单失效 ⇒ 全部打红
- 3 个禁动面文件的纯追加改动(Flow.tsx 挂选择器、FlowSnapshotHint 纠已成假话的文案、其渲染断言随之更新)由主会话裁定认可
- 登记存疑:图桶持久化无体积上限;useFlowTurns 的 effect 无自动化证据(项目无 jsdom)
2026-10-05 16:19:32 +08:00
AskaEth 84c124f5a9 fix(backthink): 她主动找你终于看得见 —— 主动消息落主会话 + 前端消费 + 非前台浏览器通知
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
修主人报的硬故障「后台思考的主动推送一个都没有推出来」(根因两条,缺一不可):
- 后端 gateway/proactive.go 只广播 proactive_message 帧、**从不写会话历史** ⇒ 刷新补不回来
- 前端**零消费点** ⇒ 帧被静默丢弃(既无气泡也无通知)

后端:新增 proactive_persist.go(**先落库、再发帧**;web/assistant/mode=chat;未接线时空操作且不谎称已落库)
  载荷新增 id/createdAt(落库行权威 id);PushResult.Detail 三态如实分开报告「投递到几个连接」与「是否落库」
前端:handlers/proactive.ts(气泡**恒落** + 按落库 id 去重 + **非前台才补通知** + 拒绝授权/畸形载荷可见降级)
  + lib/notify/proactive.ts(纯逻辑 isBackground/decideProactiveNotice/有界去重表)
  + handlers/task.ts(另三个被丢的帧 task_done/automation_result/subagent_result)
  + ws/resync.ts + useWsResync.ts(**重连重跑本页引导读取**,首次连接不跑)
双份核查(带三层证据):现状不双份 —— 路由分叉「一次 Push 至多一个 sink 成功」,且 IM 路由本来就把主动消息镜像回主会话恰好一次
契约 v0.72;变异实测:只发帧不落库 / 写成 user 角色 / 去重失效 / 前台也弹 / IM 成功再打活跃 ⇒ 全部打红
2026-10-05 16:02:36 +08:00
AskaEth 1bf85a955b feat(gateway): 链路图冷启动快照(v0.71)+ 面板来源说明条 —— 修「轮次结束后打开页面一片空白」
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 断点已证 = **时序层**:graph_update 只走实时帧从不重放、flowGraphs 不持久化、补拉通路此前根本不存在(EndRound 注释承诺过「刷新能重放」,代码里从来没有)
  铁证:15:16:38 页面重载抹掉 15:09 那轮图,而下一轮心跳要等 ~15:24 ⇒ 打开后台思考页时桶必然为空
- 新增只读端点 GET /api/v1/admin/flow/graph?sessionId=(owner|admin;省略=主会话;graph 与 graph_update 帧的 data 逐字同形)
  无图 ⇒ 200 + graph:null(不画假图);图源未接线 ⇒ 500 flow_unavailable(不伪装成「没有图」);不审计、不发布
- loop.FlowPublisher.LastGraph:只读快照(不发布/不改图/不动轮次计数),FlowGraph.Clone 深拷贝避免锁外编码撕裂
- 前端 useFlowSnapshot:只在「本会话还没有图」时补拉(避免每行一个请求);Flow/Agents/Backthink 三处接入 + 来源说明条(明确「最近一轮」与「多轮历史回放尚未实现」)
- 变异实测:LastGraph 恒 nil ⇒ 4 条测试红;白名单去掉 sess-backthink ⇒ 对应测试红(首次变异因未使用 import 编译失败 ⇒ 已作废重做)
- ⚠️ 诚实边界:每会话只回**最近一轮**(多轮回放属 F2);白名单外会话(IM 群聊/陌生人)永远没有图;**后端重启后未跑新轮次前端点如实返 null(图是内存资产,不落库)**—— 主人要的「本地数据」正是 F2 要补的
2026-10-05 15:38:53 +08:00
AskaEth c8053b1f36 feat(gateway): 渠道事件查询面 + 面板「渠道事件」页(design/26 E6)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 两个只读端点:GET /admin/channels/events(列表,8 个过滤条件 + 分页钳制)与 .../events/counters(逐类计数,恒带 approx:true 如实标「近似」)
- 诚实边界:>90 天窗口 ⇒ 400(不静默裁剪);未知 platform/档位 ⇒ 400;**未接线 ⇒ 500 channel_events_unavailable**(绝不回空 200 假装「没有事件」);微信平台的空如实说明「该渠道不提供非消息事件」而不借别平台顶替
- 面板:新 section「渠道事件」(计数卡 + 事件行 + 过滤条 + 30s 静默刷新),空/错误态分开且失败显示错误不显示 0
- 组合根接线(主会话补):wire_frontend.go 的 Deps 注入 EventStore: a.channelEventStore()(未接线时为真 nil ⇒ 保留诚实 500)
- 契约不升版(查询面 v0.67 已登记):就地改「未实现 → 已实现 + 端点路径」,并补登记 5 处原先留白的落地值(camelCase / 通用 bad_request / RBAC owner|admin / data.approx / total 语义)
- 顺带把 §5 三个 CHANNEL_EVENT_* env 键由「尚未实现」改「已接线」(E4)
- 变异实测:去掉 90 天上限 ⇒ status 200 want 400;未接线回空 200 ⇒ status 200 want 500
- 债务登记:events.Filter 无 AccountID 列 ⇒ 服务端暂不能按账号筛(列表仍如实下发 accountId),要加需先改 internal/runtime/events
2026-10-05 14:38:37 +08:00
AskaEth a0abf0199a feat(gateway)!: 后台思考档位精确化(+ 面板三态 + 契约 v0.69/v0.70)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- gateway:优先读落库的 rec.Urgency(精确档位,与安静时段窗口无关);仅空串才回落到既有下限推导,并打 urgencyLowerBound 标记(**只在推导值上为 true**)
- 面板三态:精确(例常/紧急(高)/紧急·安全域)|下限(一律加 ≥ 前缀)|未知;下限悬停说明写清「为什么旧行只能是下限」
- 实时帧零协议改动:Record 的 JSON 键就是 urgency ⇒ 帧自然带上;wsParse 早已防御性读取(只加一处 urgencyLowerBound 防御读)
- 契约:v0.69(directMsgPerMin 透传修复,逐字照抄附录 A)与 v0.70(精确化 + 更正 §4.10(b)「实时帧不带这两键」)一起登记
- 变异实测:旧行不标下限 ⇒ 3 条测试红(结构体对比);前端去掉 ≥ ⇒ 渲染测试红
- ⚠️ 诚实边界(§7.3 已登记):实时旧记录刷新前显示「档位未知」、刷新后显示「≥ 例常」——同一条记录两路下的已知差异
2026-10-05 14:15:55 +08:00
AskaEth 34cef95e17 feat(gateway): 配置只读事实面 + 后台思考行「档位/穿透」(契约 v0.68)
- 新增 GET /api/v1/admin/config-facts:记忆整理 7 键 + 安静时段 2 键的**进程生效值** + 声明默认值(反射读 default 标签,不读 .env)
- 面板:记忆整理卡 + 安静时段卡(Miuix 只读),文案按新语义「只服务主动搭话,不静默回复」
- 后台思考行加 urgency(**只能是下限**,存档不可分 high/critical)+ quietHoursBypassed(与既有 bypassed 是两件事)
- 红线实测:往响应里塞 JWT ⇒ TestAdminConfigFacts_NeverLeaksSecretValues 打红(连断言消息都把整个响应打出来了)
- 前端:configFacts 24 checks / ConfigFactsCards 29 / BackthinkUrgencyTags 19 全过
- 契约 v0.68(纯增量:B12 新端点 + B8 两字段 + §4.10 形状与语义 + §7.4-17 下限限定)
2026-10-05 13:54:56 +08:00
AskaEth 52dac4575f feat(gateway): 账号在线三态(后端 AccountStatus 可选接口 + 面板第二枚标签 + 契约账目更正)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- known=false ⇒ accountOnline 键**缺席**(未知 ≠ 离线),与既有 connected 语义并列不合并
- 面板:账号在线/账号离线/账号状态未知 三态 + 中性灰 tone + 「这不等于离线」悬停说明
- 测试:gateway 跨层锁「键缺席」(用真 qq_obv11.Adapter)+ 面板 61/40 断言
- 变异:known 当离线 ⇒ 两条测试同时红(已复跑确认)
- Docs/api-client.md:§7.2 那行由未实现改已实现(留表,注明下版移入 §7.1)
2026-10-05 13:29:53 +08:00
AskaEth be6e5f5780 docs(api): 契约升 v0.67(契约先行登记:media 播放端点 + voice DTO + 渠道事件查询面 + accountOnline;三批未实现已如实标注)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
2026-10-05 12:53:35 +08:00
AskaEth 1e0f2c9ec8 docs(api): v0.66 补充说明——chat.image 体积上限按形态写清(描述性更正,不升版本号) 2026-10-05 11:22:26 +08:00
AskaEth 0cbc9ff886 docs(api): 契约升 v0.66(帧层 + turn_id + 渠道配置 + 修正未实现清单 + 思考流口径更正)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 登记 turn_begin / steer_ack 帧壳 turnId / interrupt.turnId(仅 scope=current)/ 帧序按实现
  (session_busy → message_persisted(user) → turn_begin)/ activeTurns / cancelledTurns /
  pending_ack.reason=parallel_limit / session_idle 并行时收紧
- chat_messages.turn_id:历史 DTO、PATCH 响应、admin 渠道日志、message_persisted /
  channel_persisted / message_edited、握手回放均增可选 turnId(空则键省略)
- 渠道配置:open|close 可选 accountId + resolved/accountId;DELETE config 显式 empty=1 + removed/empty;
  GET /admin/channels 的 accounts[].connected
- 语音:VOICE_STT_TIMEOUT_SEC;IM 入站附件引用落库(行为变更)
- 修正 v0.65 的「未实现」清单(那 5 项都已实现并上线)
- 思考流口径更正:历史 thinking 恒缺席(全仓无写入者),思维链唯一出口是实时 thinking.* 帧
2026-10-05 11:15:25 +08:00
AskaEth e5cc9c6054 docs(api): 重写 api-client.md(61 KB / 701 行,比归档版缩小 85%)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
以代码为唯一真源重写:概览认证 / 用户侧 REST 表 / 管理侧表+权限矩阵 / WS 协议(握手+回放+上行+下行+角色门)/
契约细节(错误码·游标·幂等·消息 DTO·文档图片语音·忙时并行·interrupt)/ env 与模型配置 /
变更记录(只留 v0.60 起,更早指向 Docs/archive/)。

相对归档 v0.65 的修正(以 routes.go 等代码为准):
1. 管理侧权限矩阵计数错(归档 61 且自相矛盾)⇒ 实测 12 viewer + 11 owner/admin + 40 owner-only = 63
2. WS 握手实际也接受 ?token=<JWT>(仅不接受 X-API-Key)
3. limit>200 是钳到 200,不是 400(非数字/<1 才 400)
4. voice_output 的 sessionId 在 data 里(帧壳没有)
5. WS_MAX_CONNECTIONS 疑似解析未接线(标未确证)
6. 未确证清单 6 条已写入 §7.4
2026-10-05 09:34:08 +08:00
AskaEth 2ba1baf6d0 docs(api): 归档 v0.65 旧版 api-client / api-reference(准备以代码为真源重写干净版)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
旧版积了 v0.28→v0.65 的历史条目、「契约版本」标题并列、双变更记录、mtime 落后等结构性问题;
归档保留完整历史(含 §5 变更记录),新文件将从零重写,只留最近若干版并指向本归档。
2026-10-05 09:29:33 +08:00
AskaEth 879669cc59 docs(api): 契约 v0.65(忙时并行基础管线 + 六条登记)+ 修三条过期陈述
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
升 v0.65(依据 design/15 §2.5 与 commit 89e13cf),一并登记 679cee0(IM 入站附件引用落库)
/ 59a1ac4(llm_usage.session_id 接线)/ a918819(IM 各渠道语音可被读到),
cb564fe / 15181e7 / 7d2ff74 为纯内部修(无契约变化)。
三档:纯增量 = chat.parallel / activeTurns / cancelledTurns / pending_ack.reason /
两条 REST 应答形状 / VOICE_STT_TIMEOUT_SEC;行为变更 = IM 附件引用落库(无新字段)/
IM 语音进上下文 / session_idle 时点收紧(仅并行时);破坏性 = 无。
修正(客户端抓到的三条,属实):① session_id 「恒为 NULL」→ 已接线(端点仍不按会话过滤/分组)
② 「契约版本」标题并列三处 → v0.65 唯一,其余降为「上一版」
③ 按提交逐个核对补齐(mtime 落后问题)。
未实现项写明:turn_begin / steer_ack 帧壳 turnId / interrupt.turnId / chat_messages.turn_id /
graph_update 按路分桶 / 写类工具文件级互斥(tool/legfile.go 仅定死形状,无实现无接线)/ 第二路 system 摘要。
env CHAT_MAX_PARALLEL_TURNS 如实标注「未接线,生产恒为默认 2」。
2026-10-05 09:23:46 +08:00
AskaEth be38651ba4 feat(llm): 用量落库 + 只读汇总端点 + 面板用量小卡(契约 v0.64)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- model 层新增可选 UsageSink(nil ⇒ 逐字不变,既有用例零改动)
- 组合根注入 UsageWriter:有界队列 1024 + 单写者 + 批量事务(128/批、2s 兜底),
  队列满丢弃 + 限流 WARN,绝不阻塞模型调用;sink 挂 RuntimeLLM(热重载不断)
- 迁移 0017:表 llm_usage(13 列,session_id 可空)+ idx_created_at + idx_session_id
- 新端点 GET /api/v1/admin/llm/usage(owner|admin 读;缺省近 24h、上限 90 天、
  超限 400 不静默裁剪;hitRate = cacheHit/(cacheHit+cacheMiss),分母 0 ⇒ 0;
  绝不含凭据与正文;未接线/查库失败 ⇒ 500 usage_unavailable)
- LLM_USAGE_RETENTION(默认 720h,≤0 = 关闭清理):启动清一次 + 每小时
- 面板 LLM 页「用量(近 24h)」小卡 + Docs 两份同步 v0.64
- 已知缺口(如实登记):session_id 恒 NULL,需 loop 侧在请求上打标,留列后接
2026-10-04 22:16:26 +08:00
AskaEth 902b6eecfd feat(api): documents[].reason + messages 对不存在会话回 404(安卓回执九两条;契约 v0.63)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- ① documents[] 元素增可选 reason(omitempty):装配期在给出 status 的**同一处**填充,取的**就是注入模型清单里那份原因文案**(同一字符串两处用,服务端不为客户端另编一句),只在 unreadable 时下发;随 S3.5 的回合后回写落进 document 列 ⇒ 历史接口 / 握手回放都有。文案只含原因、不含落盘路径/存储名以外内容/凭据(有独特目录名反证断言)。

- ② GET /sessions/{id}/messages:不存在/已删除 ⇒ 404 session_not_found;**存在但无消息仍 200 {data:[]}**(空会话绝不能被答成 404);参数形状校验仍先于 404;存在性判定复用既有窄视图(history.Store.SessionPersona,未知 id 报 ErrSessionNotFound),**未新增 SQL/存储方法**;判不出来(未接该视图)保持旧语义(判不出 ≠ 不存在),判定本身失败回 500 不静默。

- 契约 v0.63(三档:reason 纯增量 / messages 404 行为变更 / 无破坏性):api-client §1 行 6 + §1.2④ 的 10 条逐字文案 + §4.4 错误码 + changelog + banner;api-reference §1.2 行 6 + §3.4 + §3.8 DTO + §8.1 错误码表。

- 主会话独立核对的副作用(隔离会话 sess-* 现在也 404):查活库 chat_sessions 里**根本没有 sess-* 行**(只有 web 与 web:<uuid>),sess-backthink 消息数 0,前端唯一提到 sess-backthink 的是 graph_update 的内存桶测试 ⇒ **没有客户端按 id 读隔离会话的 messages**;且这与 POST /sessions/{id}/read(v0.37 起即 404)一致 ⇒ 接受不加 sess- 例外,并在回执里问安卓确认。

- 主会话独立验收:隔离 worktree(S9 的 13 文件)build/vet 绿、整树连跑 2 次 59 ok / 0 FAIL、reason/404 用例 8 PASS、双平台交叉编译绿;**变异亲自复现**(在调用点短路 404 守卫 ⇒ UnknownSessionIs404 与 DeletedSessionIs404 双红:status = 200, want 404)。

- 如实登记:验收过程中出现过**一次不可复现的子集 FAIL**(restore 之后那次 Session|Reason 子集跑;未记到用例名)—— 随后同一子集、那三条用例 ×5、以及整树 ×2 全绿,判为瞬时;已记入账本,后续留意。
2026-10-04 21:50:08 +08:00
AskaEth c1fa1e9b9a feat(admin): 面板「重置为 env 默认」动作(materialize 的反方向)+ 契约 v0.62
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 语义:只把 .env **确实声明过**的键覆盖回 env 值;env 未声明的键(fallback/multimodal/maxTokens/reasoning/personaModels)原样保留;**组集合不增减**(不复活被面板删的组、不删面板新增的组);无键可改时不写盘。成功即热重载 LLM 栈。返回 {reset:[{group,keys}], skipped:[{group,reason}], path},**只回键名、不回显凭据值**。

- 护栏:必须 confirm:true(缺失/false/非法 body ⇒ 400,且**先于任何读写**);**owner-only**(与同页 POST /admin/llm、/admin/llm/test 同门 —— 这是破坏性写,门不比同族更宽);审计动作 llm.resetFromEnv(camelCase,全仓多词动作的既有约定)。失败路径明确报错(env_defaults_unreadable / models_unreadable / llm_unavailable),不假成功。

- 组合根注入 Deps.EnvPath(网关不 fopen .env):请求时按既有解析入口重读,语义 =「退回 .env 现在声明的默认值」;.env 层里的 provider 是同义反复,不计入「env 为该组声明了什么」的判定(有专门用例)。

- 前端:LLM 页「重置为 env 默认」卡片(中文二次确认 → 提交 → 按 reset/skipped 渲染 → 成功后重拉配置),标签 owner 专属(与 System.tsx 同款)。

- 契约 v0.62:api-client(权限矩阵改回 12+10+38=60、端点表 9b、重置契约小节、写操作契约、changelog、banner)与 api-reference(端点表 #70、§5.13a 详情含 curl、§8.2/§8.3/§8.4)同步;字段名三方逐字对齐。

- 主会话独立验收:隔离 worktree(R1 的 19 文件)build/vet 绿、gateway+config 三包绿、R1 用例 10 PASS、reset_from_env 残留 0;**变异亲自复现**(去掉 confirm 校验 ⇒ RequiresConfirm 红:status = 200, want 400,且响应体显示面板配置真的被改)。

- 子代理自报并已处理:测试文件触 500 行上限 ⇒ 拆成语义侧 + 护栏侧两份;门改回 owner-only 后前端标签会误导 admin ⇒ 改「owner 专属」并重建 bundle。任务书本身仍是旧口径(owner|admin / snake_case)—— 已由主会话登记修正说明,避免压缩后有人按任务书回退。

- 未做(诚实边界):不支持按组重置;不纳入 channels.json/plugins.json;不自动重启(contextWindow 的预算推导仍只在启动时算一次)。新端点需下一次 build+restart 才在 :10942 生效。
2026-10-04 21:33:11 +08:00