AskaEth
|
eddf921229
|
feat(memory): 记忆整理调优面板读写(三态 + 热生效 + 面板表单)
主人 2026-10-07 两条口径:「.env 只做兜底用,后端自动创建独立的面板配置文件
之后就走面板配置文件,这些键优先从面板配置上取」+「频率必须调,可以调到 1 小时
或者三小时,或者动态分配(白天短、静默期长)」。
此前 $DATA_DIR/config/memory-tidy.json 只有读路径(装配期读一次 + 启动播种):
主人要改频率只能改文件 + 重启后端。本切片补上「读回来 → 改 → 落盘 → 当场生效」。
后端(读写端点,契约待塔登记 v0.92):
- GET /api/v1/admin/memories/tidy/tuning 读盘上 7 键值 + 取值规则 specs +
缺位时 .env 档的进程生效值(与 /admin/config-facts 同源)
- PUT 同路径:三态补丁(键缺位 = 不改动 / 合法值 = 采用 / 非法值 = 400 且盘上一
个字节都不动)+ null 清除哨兵(删键 ⇒ 回到下一档)+ writeAuth + 审计
memory.tidy.tuning;值一个都没变时不落盘,文件解不开时拒绝覆盖(409)
- 落盘走 runtimeconfig.PatchMemoryTidyTuning(原子写 temp+rename,与
SeedMemoryTidy/SaveWebSearch 同族同手法);补丁语义保留下受管的其它键
(t1Judge/t1PerWindow/autoApplyPerRound 原样保留)
- 热生效:保存后按「面板 > .env > 代码默认」重算并叠到运行期对象;整理档会
重排心跳(否则新周期要等旧周期那一觉睡完),审查档当场改 ingestGate/tempTtlSec。
未接线/失败时如实回 hot:false + hotError,绝不把「落盘了」说成「生效了」
前端(Miuix):
- 记忆管理页新增「记忆整理调优」卡:7 键表单(中文标签、单位、留空⇒跟随谁、
env 档当前值、坏值就地报错并拦住保存、保存后回显从磁盘读回的真值)
- 试算卡的 recallMode 按契约 v0.91 如实渲染:embedding / lexical(已降级)/
mixed(部分分区降级)/ ""(无候选可比)——空串不再渲染成空标签;并把
error.code=tidy_recall_unavailable 单列一句(「尝试过但向量源不可用」,与
「没有候选可比」显式区分)
验证:go test ./internal/{services/gateway,runtime/memory,config/runtimeconfig}
全绿;三态/清除/坏文件拒写/同值不重写/热生效接线各有用例,并逐条做过变异自证;
tsc --noEmit + vite build 通过;两个 esbuild 前端测试 39 + 34 + 41 checks 通过。
|
2026-10-13 21:59:18 +08:00 |
|
AskaEth
|
625ae33c1e
|
fix(admin): 主人账号编辑器草稿改存无损形态 ——「+ 添加账号 ID」不再点了没点
根因:`emit` 把每次改动都写成 `flattenAccounts(groups)`(写盘形态,空行被丢掉),而
草稿本身就是界面手势的往返载体 ⇒ 刚点出来的占位空行当场蒸发,界面照旧(主人报的
「配置指定适配器主人账号的卡片内的添加账号ID按钮似乎没用」)。`+ 添加平台` 走同一条
通路(空平台组同样被丢),实测同样失效。
改法:分清两种扁平形态 ——
· 草稿 `draftAccounts`(编辑期,无损:空行/未选平台的组都留着;不 trim 正在输入的字)
· 写盘形态 `accountsForSave`(保存期,只留平台与账号 ID 都非空的条目)
后端契约与 `configs/users/owner.yaml` 格式一个字不改。保存路径写盘前必须收形:半成品
条目(有 id 没平台)会被后端 `Validate()` 直接 400(internal/user/load.go)。
测试:`OwnerAccountsView` 从编辑器里拆出来(无 hook),测试可直接调用它、点它自己的
按钮、把 onChange 收到的真草稿再喂回去重渲染 —— 走的是「手势 → 草稿 → 界面」真往返
(本项目没有 jsdom)。修前该测试红在「点『+ 添加账号 ID』⇒ 界面上真的多出一行 —
got 1, want 2」。
|
2026-10-13 20:00:43 +08:00 |
|
AskaEth
|
b01e2ddb1a
|
refactor(admin): 主人账号编辑器拆出无 hook 的 OwnerAccountsView
界面本体与「分组/选项 memo」分层:OwnerAccountsEditor 只做 memo 组装,OwnerAccountsView 无 hook。本项目没有 jsdom,拆开后测试可以**直接调用**视图、点它自己的按钮,从而钉住「手势 → 草稿 → 重渲染」这条往返(见下一个提交)。行为不变:emit 仍用 flattenAccounts;账号行加 data-account-row 定位标记。
|
2026-10-13 19:56:32 +08:00 |
|
AskaEth
|
b4d55f412e
|
fix(channel): 连接动作不再乐观 —— 适配器自报连接态三态 + 面板如实呈现
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
根因:adminChannelOpen 在 ch.Connect 返回 nil 后硬编码 connected=true。而 Connect 返回 nil 只说明「这次调用没有致命失败」—— ws+client 首连失败刻意非致命(OneBot 实现可能比 Cyrene 后启动),ws+server 则只需监听就绪,两种情况 IsConnected() 都是 false。前端又只按「请求成功」写死「已连接」,于是管理面把「后端受理了」说成「渠道连上了」。
改法:三态口径落在适配器自己身上(connected/pending/ready,internal/channel/adapter 的纯函数 ConnectStateOf 判定 + ConnectSignals 结构化入参,避免两个 bool 接反),qq_obv11 实现 ConnectOutcome 并记住脱敏后的失败原因;网关经类型断言探测(与 accountStatusAware/rescanAware 同套路,核心 sdk.Channel 契约刻意不扩展),只做映射不猜 transport/mode。
线上投影:open/close 响应与 GET /admin/channels 的平台行/账号行各新增 pending/ready/reason 三个可选键(零值即整键缺席,同 NeedsRescan/LastError 口径);既有四键逐字不变,未实现该状态面的平台输出逐字节不变。close 报真实 IsConnected(),断开也不乐观。
凭据边界:reason 三层脱敏(形状级 → 已知明文替换 → 剥 access_token= 查询参数),日志同样只用脱敏串。
验收:主会话亲自复跑 go build/vet 全绿、全树 go test 无 FAIL、目标三包 test 全绿、前端 tsc --noEmit 干净、esbuild 逻辑测试 34+46 checks 全过;主会话亲自变异摘掉第 3 层脱敏实现,发现原两条脱敏用例其实钉不住它(值恰好等于已知明文/被形状级顺带吃掉)⇒ 补一条钉住它独有窗口的用例(短值+紧邻空白),变异下必红、还原后绿。
|
2026-10-13 19:38:56 +08:00 |
|
AskaEth
|
811e463cea
|
refactor(web): chat.tsx 551 行按职责拆成 5 片(守 500 行铁律,零行为变化)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- chat/index.tsx 272 行(编排层:SplitChatBubble + MessageBubble + 各气泡)
- chat/toolviz.tsx 112(工具卡 / 思维链可视件)
- chat/labels.tsx 70(元数据小字)
- chat/chrome.tsx 60(外壳判定与注记)
- chat/split.ts 51(纯函数:br 分隔与段落切分,最易测)
- 旧 chat.tsx 删除;调用方一行未动(沿用既有目录索引解析,与 messages/ 同一模式)
纯搬家证据:把旧文件与新 5 文件做行多重集 diff,差异只有 export 前缀、
一行 re-export(ThinkingBlock / ToolCard)与 import 重写 —— 没有一行
JSX / 样式类名 / 逻辑变化;测试文件一字未改。
对外导出名与形状逐字不变(仍是 ThinkingBlock / ToolCard / MessageBubble / SplitChatBubble)。
验收:tsc 0、render 189 checks、bubble 27 checks、四个邻接套件全绿、
vite build 294 模块;变异(拆后去掉 br 空段过滤 => ⑦ 红)证明测试仍在真测那些逻辑。
|
2026-10-07 18:56:30 +08:00 |
|
AskaEth
|
f01fd7db20
|
feat(web): u 加回白名单 + 空气泡丢弃 + 非 chat 模式不再显示字面标签
主人 2026-10-07 三点裁定:「下划线确实可以保留」「多余的空气泡确实可以丢掉」
「非聊天模式 按照应当有的专业卡片和格式来显示」。
- 白名单回到六项(u 加回,s 仍在外)
- 空 p 块也丢(与 br 碎片同一口径);纯注记回复 ⇒ 零气泡
- 非 chat 模式(8 个单卡正文)给它自己的渲染口径 CARD_RENDER:
br ⇒ 真换行元素(那些容器没有 whitespace-pre-wrap,文本换行会被 CSS 折叠)、
行内白名单正常出样式;WorkMessageBody 的按行切清单、CodeMessageBody 的 pre
等既有较强约定一律保留
- 🔴 发现并修了一个会炸的下游损失:空气泡原本是承载头像/思维链/工具卡/心情/
「生成中…」的那一行,丢气泡会把这些用户可见内容一起丢 ⇒ 加 hasBubbleChrome,
补一行无正文外壳
- 测试 99 -> 189 checks;6 个变异(含「外壳行摘掉 ⇒ 工具卡丢失」)
- 改掉一条既有断言:三个 p 中首段被注记剥空 ⇒ 气泡数由 3 改为 2(并补按行数断言)
|
2026-10-07 18:44:45 +08:00 |
|
AskaEth
|
a19d12b228
|
feat(markup): br 改为气泡/消息分隔符 + 行内白名单收窄到五项(Web + IM 两侧)
主人 2026-10-07:「对消息内的 <br> 始终处理为多条消息隔断」。
Web 侧(frontend/web):
- chat.tsx 的 splitParagraphs(SplitChatBubble 的切分层)新增 BR_SEPARATOR_RE
与 splitBubbleBreaks:<br>/<br/>/</br> 等价、属性不解析、<brow>/<br2> 挡掉、
流式半截 <br 不切;不含 <br> 的段逐字返回原样(既有 p 口径一字节不动)
- inlineMarkup.ts 删掉 break 节点(br 不再是行内元素),白名单收窄为
b/strong/i/em/code 五项;renderContent.tsx 同步;测试 76 -> 99 checks
- 40 条基线逐字节对照:28 IDENTICAL,12 个 DIFF 恰是含 br/u/s/<BR> 的样例
IM 侧(internal/services/channelbridge):
- 新增 inline_tags.go:成对平衡的白名单行内标签剥标签留文本、白名单外与
结构不平衡整段原样保留;br 作为第二级分隔符交给 splitBubbles
- 两级切分:p 块为第一级,块内按 br 再切为第二级;空气泡丢弃
- br 切分放在角色注记摘除之后:否则 action 里的 br 会把注记正文漏成聊天气泡
(变异专门钉住这条顺序)
两路各 4 个自带变异均「改坏 -> 编译过 -> 变红 -> 还原 -> 复绿」。
|
2026-10-07 17:32:05 +08:00 |
|
AskaEth
|
2dae4693be
|
feat(web): 消息渲染支持行内标签白名单(修 <b> 等标签裸露显示)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人 2026-10-07:「她在聊天消息中插入了 html/md 标签,能否增加网页前端
对此类标签的基本渲染支持?」
实测现场(最近 12 条 assistant 消息):<p> 96 次、<action> 34 次
(这两个前端已支持),<b> 6 次(未支持 => 裸标签直接显示给用户);
Markdown 记号 0 个 => 她不用 Markdown,所以不引 Markdown 引擎。
- 新增 inlineMarkup.ts(纯逻辑,111 行):把文本切成「文本 / br / 行内元素」
的节点树;渲染与解析分离,不拼 HTML
- renderContent.tsx 沿用既有出入口,把节点树映射成 React 元素;
既有 <p> / <action> 机制原样不动
- 白名单:b / strong / i / em / code / u / s(成对)+ br(void),
标签名大小写不敏感;不做块级、不做 a(独立安全面)、不做 Markdown
- 安全:全程无 dangerouslySetInnerHTML(文本走 React 子节点,默认转义);
属性一律丢弃(节点类型里没有属性位);白名单外 => 纯文本字面量;
结构不平衡(未闭合/交错)=> 整段降级为原文,字符一个不丢
- 测试 76 条断言(esbuild + node,零新依赖);含 3 条安全性质
- 无回归:改动前后 11 个消息样例过真实 SplitChatBubble 链路逐字节对照,
7 个 SAME,4 个 DIFF 恰是含白名单标签的样例
|
2026-10-07 17:11:37 +08:00 |
|
AskaEth
|
c46053e276
|
feat(admin): 搜索提供商下拉/多选/表格显示中文名(值仍是 ASCII id)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人报障:「为什么中文搜索接口……下拉菜单里全给的英文名?你甚至不愿意带个括号
把中文名写上去」。根因不是漏写文案,而是整条链路没有「显示名」的位置:字段
声明只有 optionsFrom,候选动作回的就是 KnownNames() 的纯 id 数组。
- 后端:候选改为对象数组 {value,label};label 表与接入位表同源(键用
NameSearXNG 等常量),未知 id 原样回。value 逐字等于 KnownNames()。
- 前端:下拉/多选「显示 label、写 value」;表格单元格按 value→label 索引显示,
查不到回落显示原值。多选从裸输入框改成「候选开关 + 落盘值输入框」。
解码器本就兼容裸字符串/{value,label}/{id} 三形状 ⇒ 其他插件零影响。
中文名逐字:SearXNG 本地兜底(searxng)/ 智谱 BigModel(zhipu)/
阿里云 IQS 信息查询(aliyun_iqs)/ 博查 Bocha(bocha)/
火山引擎·豆包搜索(volcengine)/ 百度千帆 AI 搜索(baidu_qianfan)。
验收(主会话复跑 + 亲自变异):go build ./... + vet 绿;search 包测试绿;
tsc --noEmit 0 错;新渲染测试 26 checks + 既有 plugin panel 55 / data 90 /
schema values 40 / channel config 70 全绿。我用 sed 把 values.ts 的
option.value 全换成 option.label ⇒ 渲染测试红(「点选把候选项的 id 追加到
降级链末尾」got="bocha, 智谱 BigModel(zhipu)")—— 这正是会把配置文件写坏的
错法;还原后 md5 一致且绿。
⚠️ 契约面变更(我在 Docs/api-client.md 登记):plugin.providers 候选响应形状
字符串数组 → 对象数组;label 仅供显示、value 才是落盘值。
⚠️ 已知边界:viewer 角色看不到 form 区块 ⇒ 表格对 viewer 仍显示裸 id(未做)。
⚠️ 真机浏览器点击未跑(项目无 jsdom)⇒ 建议刷新管理页点一次确认观感。
|
2026-10-07 10:08:19 +08:00 |
|
AskaEth
|
c512609c63
|
feat(admin): 记忆整理候选的应用/回滚操作面(切片 TIDY-FE)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人报障「面板上点不到『应用』」→ 后端 TIDY-1 的五条端点(契约 v0.83 B20–B24)
此前只能 curl 驱动。本片补齐界面:候选表(动作/状态/摘要/理由/时间 + 行内
应用/回滚,回滚仅在 status=applied 时可点)、status 筛选、批量应用全部待定、
手动真跑一轮(两个写动作都走二次确认)。
三条契约硬口径落到界面:
- 幂等不是错误:skipped 用中性色,绝不渲染成失败;
- 超限必须如实:truncated=true 时黄牌「还有 N 条待定未处理」,且该分支绝不
渲染「已全部处理」;未处理量只由后端权威字段算得(total - 已处理三项);
- 权威键只读:状态与计数全部取自响应,不做「应该成功了」的推断。
一个刻意的偏离(有测试钉住):截断提示不用共用 Notice tone=warn —— 那个组件
的固定前缀是「待实现」,贴在「还有 N 条未处理」上会被读成「功能还没做」。
验收(主会话复跑 + 亲自变异):tsc --noEmit 0 错;vite build 通过;
渲染测试 43 checks、传输层测试 28 checks 全绿;我把 truncated 判断取反 ⇒
渲染测试红在「截断时必须显式点明命中数大于本轮处理数」,还原后 md5 一致且绿。
⚠️ 未跑真机浏览器交互(需 owner 凭据 + 真后端),证据止于渲染层与传输层。
|
2026-10-07 09:58:21 +08:00 |
|
AskaEth
|
57d0e8c543
|
feat(admin): 时间轴只读权威键 + 自排筛选轴 + browser 面板图标(Y2+Y3+Y4 前端一并落)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 时间轴:页首「已过保留期」的**唯一**判据改为 retentionBoundaryReached === true(truncated 只出
「未显示」);缺席(老后端)一句话都不说;删掉旧的 truncated+retentionEnabled+翻到底 启发式
- Backthink:筛选轴接上服务端 ?trigger=(客户端筛选保留作兜底)+ 自排闸用量行(三态文案)
- plugin/nav.ts 加 browser 图标
- 真机(隔离实例,46/46):翻页不重不漏、三种「必须 false」的权威键语义、**重启后 round 不重复**
⚠️ 未验:真机上没构造出权威键 true(需真有更早轮次被清扫)——别读成「真机验过 true」
|
2026-10-06 21:48:52 +08:00 |
|
AskaEth
|
2d211c2d16
|
feat(backthink,browser): 自排工具面 C1-3 + 浏览器两处偏离收口(X2+X3 同文件一并落)
X2 C1-3(自排工具面与提示词):
- request_think(thinktool.go,照 transcode/tool.go 范式)+ reason 必填/urgency 闭集 + 双守门
(安全闸 + owner 判定:群聊/陌生人一律拒)
- **成对**进两张唤醒态允许集(quiet + awake;CAPACITY_TOOL_SETS 是整体替换)
- 提示词抽共用段落 + 自排前缀优先(不枚举工具清单);面板加 self_wake 标签与筛选轴
- 4 个变异自检(拿掉 owner 判定/只加一张表/放宽 urgency/前端标签恒等)
X3 收口(与 X2 共用 wire_services.go ⇒ 一并落):
- 删掉第三份 SSRF 实现(guard.go),改由 io.ValidateRequestURL 唯一判定 + 薄门面(保两个错误身份)
- 浏览器会话挂进 a.close(真机:真 Chrome 被关、无残留、生产未扰动)
- a.wireBrowser() 归位到 wire_runtime.go,删掉 ioToolOptions 的临时挂点
- 5 个变异自检
顺带补提交 C1-2 漏掉的 internal/config/runtimeconfig/backthink_test.go(ed50053 漏 add)。
|
2026-10-06 21:16:23 +08:00 |
|
AskaEth
|
745365c7df
|
fix(admin): 时间轴分页与保留期文案按契约收紧(V2)
- 类型补上后端真在返回的 source/nextBefore/hasMore/retentionEnabled(此前类型里没有)
- 新增纯函数 pageHasOlder(hasMore 权威,老后端退满页启发式)/timelineCursorOf/retentionClaimable/
timelineHints;取数体抽成可执行导出函数 ⇒ effect 本体首次被真跑(真 fetch + 真状态机 + 真 SSR)
- 🔴 修一处成对契约缺陷:后端 truncated = hasMore || !storeWired,而契约/设计要求 truncated ⇒
「已过保留期」⇒ 旧前端会对主人说假话。现改为:只有「已翻到底 + 保留期启用 + 非 live-only」
才说「已过保留期」,其余说「更早的轮次未显示」
- 3 个变异自检(去掉 canLoadMore 短路/恒可翻页/忽略 hasMore)
⚠️ 未验(如实):鉴权后的真机 HTTP 往返拿不到 owner 凭据;前端产物未部署(面板仍是旧文案)
|
2026-10-06 20:25:06 +08:00 |
|
AskaEth
|
5ee5f9126f
|
feat(admin): Flow 页时间轴视图(W2)
- 视图切换:最近一轮(既有 DOM 逐字节未变,7/7 SSR 对照 IDENTICAL)| 时间轴(新)
- 时间轴:竖轴=时间(默认最新在上)、每回合一个可折叠块、块内复用 LaneTracks 泳道、
块间不画任何连线(不臆造因果);before 游标分页;truncated ⇒ 显式「更早的轮次已过保留期」
- 降级诚实:只拿到最近一轮(老后端)⇒ 明确提示「当前只支持最近一轮」
- 4 个变异自检(排序方向/块间插线/truncated 静默/游标方向);老视图等价性实测
|
2026-10-06 15:59:11 +08:00 |
|
AskaEth
|
9e63af9063
|
feat(admin): 前端可接入点 —— 折叠分组 + 动态插件页 + schema 渲染(PLG-3)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- sections.tsx 的分组升级为可折叠(CollapsibleGroup);新增 plugins 分组由
GET /api/v1/admin/panels 动态填充(navModel.ts 纯函数可单测)
- 插件页走动态段 /admin/plugin/<pluginId>/<panelId>,不进 router.ts 的静态白名单
- 把既有三段式(字段声明表 + 纯函数规则 + 渲染器,含「留空=不改」掩码语义)泛化成
schema → 控件(src/admin/schema/),复用它渲染插件表单/表格/事实面/日志
- 可测判据:新增插件页不得改动 sections.tsx / router.ts / AdminApp.tsx 的静态清单
- when 不满足 ⇒ 入口不出现;viewer 只看到只读区块
我的复跑:npx tsc --noEmit 通过;npx vite build 通过(3.67s);50 个前端脚本式测试全部
打印 checks passed(含本片新增 pluginData 90 / PluginPanel.render 55 / navModel 62 / schema values 40)。
|
2026-10-06 09:04:51 +08:00 |
|
AskaEth
|
89434eadef
|
fix(flow): graph_update 按路分桶——修真并行两路的图互相覆盖(FV2)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
【取证(症状成立,且比登记描述更糟)】两路真并行时两路最终落盘**同一个 turn_id**,
其中一路的图彻底消失,且 A 路的工具被画在 B 路的图上。逐层定位:发布器 graphs 只按
sessionID 建键(后起的路整张顶掉前者)· 帧载荷基线的键集被既有测试精确断言为
{id,nodes,edges,sessionId,startedAt,endedAt}(无任何区分路的键)· 前端每会话单桶 ·
落盘 (session_id,turn_id) 唯一键两路相撞 · 冷启动 LastGraph 只有一张。
该 bug 早已公开登记(design/15 §2.5.10-1「本轮未解决」、契约 §6-19「仍**未修**」)。
【修法】
- 归属桶 {会话}[#{路}](loop.FlowOwnerKey,与 checkpoint 键同字符口径);路只来自
ParallelLegFrom(ctx) ⇒ 单路逐字不变(桶 = 会话 id),既有调用点语义零改动
- 图 id 的第二分量改用**建图序号**(每次 +1、永不复用):同一轮里可以有并行路 +
队首续跑的串行路,用轮次号会让两条串行路撞同一个 web-1
- 轮次号只在上一轮的路全部收尾后推进,并丢掉更早轮次的桶(否则内存随路数无界增长)
- 新增只读出口 LatestLanes ⇒ GET /admin/flow/graph 的 lanes[](最近一轮每一路),
graph 仍是「最新一路」= 向后兼容
- 同源洞一并修:子代理归属原取 AgentSpec.ParentSession(会话、不含路)⇒ 并行路里派的
子代理 span 被静默丢弃;新增 AgentSpec.FlowOwner 承载「会话#路」,ParentSession 继续
只承载会话(总线载荷 subagent_result.sessionId 不受影响)
- 前端:store 增 flowLanes(按 turnId 分格、按轮淘汰、硬上限 4)+ LaneTracks 泳道卡;
单路/未给 lanes 时渲染路径与改前逐字相同
【无迁移】turn_id 是自由文本、每路 id 唯一 ⇒ 0022 的唯一索引天然分两行(0023 仍空着)
【我的验收】① 隔离 worktree(30 文件)build/vet/linux/darwin 全过、69 包全绿 0 FAIL;
② 我亲手打红变异:让 FlowOwnerKey 忽略路 ⇒ TestFlowPublisher_ParallelLegsDoNotOverwriteEachOther
FAIL;③ 前端独立复跑 npx tsc --noEmit + npm run build 通过(4.12s)
⚠️ 契约 v0.77 的登记在紧随其后的 docs(contract) 提交里(同一个工作时段内)。
|
2026-10-06 08:16:03 +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
|
8e56163418
|
feat(admin): Flow 页会话选择器 + 修「泳道 0」文案(纯前端)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 新增 flow/flowSession.ts(白名单判定与后端 flowSessionAllowed 逐条对齐 + 选项构造 + 选中回落 web + 取数接线 flowPageData)+ FlowSessionPicker.tsx
- Flow.tsx:图不再读兼容字段 s.flowGraph / activeSessionId,改由 flowPageData 从 flowGraphs[选中会话] 取;选择器只在给了 onSelectSession 时渲染(既有调用形态不变)
- 修文案:0 泳道时统计卡显示「—」+「本回合未派发子代理」(原先 0 紧挨「泳道」会被读成「泳道 0」)
- 标签单一出处:FlowSnapshotHint 改用 flowSessionLabel(避免与选择器漂移)
- 测试 33 + 21 checks;变异:取图改回主会话桶 / 选项去掉 sess-backthink / 泳道卡还原 / 放行白名单外会话 ⇒ 全红
- 如实登记覆盖缺口:组件调用点那一行无法自动化(无 jsdom,zustand SSR 快照取不到真实 state)⇒ 有一条变异打不红,靠「取图路径唯一 + 注释」缓解
- ⚠️ 行为变化(任务书要求):默认选中固定为 web,不再随 activeSessionId 漂移(原先跟着激活会话走)——一行可改
|
2026-10-05 20:00:01 +08:00 |
|
AskaEth
|
14e76b6480
|
feat(admin): 流程图方向——Flow 页自下而上(最新在上),Backthink/Agents 图块最新在左
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
主人要求:① 全链路流程页从下往上渲染(时序自下而上 ⇒ 最新一定在最上)+ 容器高度自动拓高;② 主动思考页保持原渲染方式但最新放最左(容器默认露最左);③ 追加:改布局后多路渲染仍要合适。
- 共享布局唯一入口 flow/layout.ts:新增枚举 FlowOrientation(newest-top|newest-left) + layoutFlow(graph, orientation);排布拆成「抽象网格→像素」两步,方向开关只在 toPixelNode/toPixelBand;泳道成道逻辑零改动
- 边几何抽到新文件 flow/edges.ts:只按方向选锚点,带回 fromId/toId(不重排图数据),悬空边如实丢弃
- FlowGraphView 的 orientation 是**必填** prop(漏传=编译错误);画布不限高、无 overflow-y(不内部裁切,纵向随图生长);图例加方向小字
- 三处调用同一套布局:Flow=newest-top、Backthink=newest-left、Agents=newest-left(后者是我按同一档裁定,一行可改)
- 顺带修复:画布最大列现在把「刚开出、还没跑出步骤的泳道」最小宽度算进去(否则泳道框被裁到画布外)
- 测试 593 checks(orientation 逻辑 131 + 渲染 338 + lanes 40 + 既有回归);变异 7 条全红,含「边端点连到错节点」「容器限高裁切」「空泳道被裁」
- 多路:lanes.test 期望随新方向镜像更新,泳道仍成道;并行路数据互相覆盖的已知问题归后续分桶切片
|
2026-10-05 19:51:45 +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
|
a3482ed06e
|
fix(admin): 日志类列表统一「最新在上」+ 顶部前插不顶跑阅读位置
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 审计 15 个日志/事件面后统一口径:改 3 个(渠道总日志 / 每账号消息日志弹窗 / 适配器专属页实时日志),7 个本已合规未动,聊天页**刻意保持正序**(本质是读对话,正序+自动滚底才自然)
- 排序口径唯一出处 channelLogOrder.ts(历史+WS 追帧按 id 去重、时间倒序)+ 展示层不倒序(与 channelEventViews 同分工);三面共用 useChannelMessageLog 取数
- 新增 logScroll.ts:贴顶跟着最新走、已下读按新增高度补偿(**新条目不把正在看的内容顶跑**);窗口化容器的滚动锚定一并接上
- 后端无需改(三处消息日志读取本就是 created_at DESC,已核)
- 诚实边界:滚动锚定的 DOM 那半只有纯函数有自动化证据(项目无 jsdom/Playwright,渲染测试跑 react-dom/server、effect 不执行)⇒ 已如实标注
- 变异实测:排序改回旧→新 / 取消补偿 / 渲染层再倒一次 / 改坏共享文案 —— 四处均打红
- ⚠️ 两处未改(在别的切片文件面内,已转交):flow/NodeDetail 节点实时日志 → 归 F2;Agents 任务日志 → 归 H1
|
2026-10-05 15:37:04 +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
|
a057a0f61f
|
feat(channels): 流控可配 —— 陌生人私聊额度走账号配置 directMsgPerMin(裁决 13)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 后端:账号顶层 directMsgPerMin(缺失/0 = 保护性默认 6 条/分;正值 = 该值;负值 = 关闭并留带 source 的 WARN)
- 日志如实报出**实际生效**额度(adapter.(*FlowGate).DirectPerMin()),告警文案不再写死 6
- 面板:渠道配置表单加字段(含帮助文案)+ 回显/写回链路(LIMIT_KEYS 四键)+ 账号卡片摘要
- 文案纠错:AdapterConfigSummary 的 msgPerMin 由「私聊每分钟上限」改为「非主人消息每分钟总上限」(原描述是误导),并列补 directMsgPerMin 行
- 变异实测:缺键丢默认 / 负值当 1 条每分 / 前端吞负值 —— 三个都精确打红并还原
- ⚠️ 已知缺口(下一片补,gateway 面):GET/PUT /admin/channels 尚未下发/解码该键 ⇒ 面板暂时回显空、保存会把 -1 重置为 0
|
2026-10-05 13:50:29 +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
|
012e9f3ad4
|
feat(web): 前端 P0 收尾(多路忙态收口 / 握手 history 落 store / 清理幽灵键)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
|
2026-10-05 11:29:17 +08:00 |
|
AskaEth
|
eaada3af5d
|
feat(web): 前端与后端同步一批(帧壳 turnId / 并行六态与两级分桶 / documents 渲染 / System 键名 / 主人档案分组)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 帧壳 `turnId` 在 socket 层不再被丢(`lib/ws.ts` 的 dispatchFrame 补 turnId/seq)—— 按路分桶的前提
- 并行帧组接入:`turn_begin` / `steer_ack` / `pending_ack` / `pending_drain` / `pending_full` /
`chat_duplicate` ⇒ 六态 + 灰态 + `(sessionId, turnId)` 两级分桶;`appendChunk` 不再永远写尾气泡
- 多路忙态:`session_interrupted` 不再无条件当空闲(看 mode/activeTurns);`stream_end` 不再只按会话过滤
- `documents` 渲染(含 status/chars/truncated 与**逐字中文 reason**);`turnId` 历史行分组小标
- `System.tsx` 键名改正(真键 HOST/PORT/ADMIN_USERNAME/ADMIN_PASSWORD,避免照抄静默失效)
- 主人档案**按平台分组** + 平台下拉 + 账号候选;**写盘仍展平数组**(后端契约不变)
- 验证:tsc --noEmit ✅、npm run build ✅、22 个前端测试文件全通过(本次新增 3 文件 108 条断言)
- 未做(如实):握手 history handler 与断线补拉、上行 interrupt 按钮、proactive_message/subagent_result
等帧落点、文档上传入口与图片预检、voice 管线、System 的 env 读端点
|
2026-10-05 11:09:24 +08:00 |
|
AskaEth
|
42279b7124
|
feat(channels): 按账号连接/断开 + 可删空 id 条目(修主人真机踩的两个缺口)
缺口①:「连接」是按平台连、且恒打第一条账号(channelActionRequest 只有 name)⇒ 主人有两条
qq-obv11 时点到的是 mode=server 的历史条目,报 server mode requires listenAddr,而正确的
client 条目轮不到。缺口②:空 id 条目既删不掉(DELETE 要求 id 非空)也改不了(UpsertChannel
空 id = 新建),越改越多。
后端:
- POST /admin/channels/open|close 请求体新增可选 accountId(缺省逐字兼容,老客户端零影响);
按该账号驱动;未知 id ⇒ 404 且零连接(不静默回落第一条);已配置未注册 ⇒ 409
- DELETE /admin/channels/{platform}/config 新增显式 empty=1(id 与 empty=1 互斥 ⇒ 400;
无可删 ⇒ 404);语义严格 —— 只删空 id 条目,绝不把「id 为空」默认解释成删空 id
- 响应新增:open/close 的 resolved(实际驱动的适配器名)与 accountId;delete 的 removed/empty;
GET /admin/channels 的 accounts[].connected(可选 bool,缺省 = 未知)
- 审计动作名不变(channel.open/close/delete),日志行新增 accountId 字段
前端:每条账号一行「连接/断开」(带 accountId);按钮旁明确「会连哪一条」;空 id 行显著标注
+ 删除入口;channelActionBody 携带 accountId;新增 action 测试 33 checks + 空 id 行渲染 31 checks。
测试:后端 14 个用例(真实 HTTP 路由 + JWT + 真注册表 + spy 适配器,断言「哪一条被驱动、
另一条一次都没被碰」)。
变异 6 条(accountId 被忽略 / id 为空当删空 id / 找不到静默回落 / 前端丢 accountId /
删空 id 退化成 ?id= / 空 id 行按普通行渲染)。
|
2026-10-05 10:37:56 +08:00 |
|
AskaEth
|
c4ea02a887
|
feat(web): 渠道配置面板重做(按模式动态显隐 + 下拉 + 真鉴权键 + 掩码不回填)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
新增:channelFieldRules.ts(纯函数规则层:选择器归一化 / 按 transport×mode 显隐与必填 /
写回补丁 / 模式提示文案)、channelConnFields.tsx(「连接配置」区:只渲染当前组合生效的字段,
逐字段「谁连谁」说明 + 必填/可选标记 + 掩码清除开关)、channelAccountId.tsx(账号 id 与
「覆盖/新建」状态 Tag)、channelPersonaField.tsx(从表单抽出,逻辑逐字保留)。
qq-obv11:transport / mode 改下拉;remote_url 仅 ws+client 显示且必填;listen_addr 按
server / http+client 条件显示;api_base_url 仅 http(http+client 必填);report_path 仅 http;
access_token 掩码;self_id 仅记录。每种组合顶部一句 modeHints;保存前拦截必填缺失并红框点名。
⚠️ 修掉一处数据毁伤:后端把 access_token 掩码成 ****abcd 下发,旧代码回填进表单 ⇒
「打开就保存」会把掩码写成真 token。现在掩码不回填、只记 configuredKeys,并提示
「已配置,留空 = 不改动」;要清空必须显式打开「清除已配置的 access_token」开关
(顺带给掩码秘密补上了以前没有的「清除」能力)。可见字段清空发 ""、隐藏字段不进补丁(缺位 = 保留)。
Token 误导修掉:PLATFORM_GUIDE.authKey 声明平台真正读取的键 —— qq-obv11 不再渲染 bot_token
输入框、不再标必填(改指向 access_token);渠道列表与只读配置卡的凭据行同步改用真实 authKey。
空 id:结论是后端契约(UpsertChannel 空 id = 新建),前端不碰后端修好:记住 savedId
(留空 = 覆盖当前条目)+ 改新 id 时内联二次确认 + 保存后用返回的 accountId 刷新 + 状态 Tag。
测试:新增真组件 SSR 渲染测试(68 checks)+ 规则单测(58 checks);17 个前端测试文件逐跑全绿;
tsc --noEmit 与 npm run build 通过(产物 index-CK_2RXKo.js)。
|
2026-10-05 10:15:19 +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
|
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 |
|
AskaEth
|
c4e09afd38
|
feat(admin): 面板「存储」卡片接真数据(含 PG 增强状态)+ 契约 v0.60
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 现状:overviewMetrics.ts:188 的「存储」卡是写死的 value:"SQLite",后端也没有对应读端点(/admin/system/* 只有 revoke-keys/rotate-jwt/backup/restore 四个动作端点)。
- 新增只读端点 GET /api/v1/admin/system/storage(ownerAuth):main{kind,path,fileBytes,walBytes,shmBytes,pageSize,pageCount,freelistPages,freeBytes} + documents{count,bytes} + vector{kind,enabled,ready};只读(SELECT/PRAGMA + os.Stat)、复用活连接不开新池、**绝不含凭据**(只回 kind,不回 host/DSN)、失败一律 500 storage_unavailable(不回全 0 假快照;表缺失也 500 而不是静默 0)。
- vector 真源 = 组合根 app.vectorState()(Configured = DB_PG_URL 非空;Live = 向量索引类型断言成功),零新探测 ⇒ 面板现在能区分「未启用(未配置 DB_PG_URL)/ 启用(pgvector·ready)/ 已配置但未就绪(连接失败,检索降级)」,正好解答主人对「面板只写 SQLite」的困惑。
- 前端:卡片显示真实体积 + WAL/页数/空闲页 + documents 件数与占用 + PG 那一行;取数走与已接线卡片同一条加载/错误态(失败显示 '—' + 端点报错,不静默显示 0)。
- 契约 v0.60(项目红线):api-client(版本段 + 端点表 + 存储状态契约小节 + changelog)与 api-reference(版本号 + 端点表 + 详情小节)同步;字段名三方(代码/Docs/前端)逐字对齐。
- 主会话独立验收:隔离 worktree(ST1 的 14 文件,排除 B1 在途的 chat_documents.go/documents_pdf_test.go)build/vet 绿、全树 59 包 0 FAIL、存储端点 7 用例 PASS、双平台交叉编译绿。
- ⚠️ 该切片自首一起事故:为通过 cmd/cyrene 类型检查跑了 go build ./cmd/cyrene/(单 main 包)⇒ 覆盖了仓库根 cyrene.exe(49,675,264 B / 19:32:19,脏树未 strip);上一个权威产物仍在 cyrene.exe~(39,507,968 B / 19:25:13)。运行中的 PID 477964 未受影响;主会话将在整树干净后重跑 ctrl.bat build 恢复权威产物。
|
2026-10-04 19:44:53 +08:00 |
|
AskaEth
|
47e56ca13c
|
feat(admin): 面板暴露 contextWindow(可见可改)+ DocumentRef 语义注释同步
- 面板读写面与 maxTokens 逐处对齐:读 DTO 增 contextWindow(omitempty)、写请求 ContextWindow *int(缺失=保留、显式 0=未声明同义、负数 400)、回写 models.json;负数红线仍由 runtimeconfig.Group.Validate 唯一裁决,网关守卫只做「更早失败 + 面板可读字段名」。前端:类型 + 表单字段(提交前 /^\d+$/ 校验)+ 分组行角标 + Notice + 目录候选用早已解析却没人用的 contextLimit 预填。文案写明「重启后生效」(推导只在进程启动时算一次)。
- 顺手修既有红线违反:ProviderFields 87→76(抽 ApiKeyAndBudgetField)、LlmProviderForm 113→74(buildRequest 提为模块级纯函数)——本次碰过的文件里已无 >80 的函数,行为/文案逐字未变。
- DocumentRef 的 chars/truncated 注释从「抽取侧」改为「本回合注入侧」(全文长度只在 document-read 的 totalChars、extractTruncated 专指撞 8 MiB 安全上限);机械校验为空 ⇒ 代码零改动。
- 主会话独立验收:隔离 worktree Go 侧 build/vet + gateway/history 两包绿;主树 npx tsc --noEmit exit 0、npm run build 成功并 copy 到 internal/webui/dist;注释机械校验为空;变异由子代理复现(写路径不认该键 ⇒ 3 红;去掉网关负数守卫 ⇒ 400 变 500)。
|
2026-10-04 18:11:43 +08:00 |
|
AskaEth
|
c96a7db7f1
|
feat(gateway,config,web,docs): backthink 实时帧显示打扰原因 + 图片上限可配 + api-reference 端点表补齐
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 前端:`lib/wsParse.ts` 的 `parseBackthinkItem` 现在也读原始 `PushResult` 的 route/detail/bypassed(兼容
camelCase),与历史 DTO 路径**同名同义**(空串 → undefined、未报告的 bool 不编成 false);
Backthink 页 Notice 改为如实描述。证据=用后端两条路径的**真实序列化**喂同一个 PushResult 做逐字段比对
- 图片上限可配:`CHAT_IMAGE_MAX_BYTES`(默认 5242880 = 与 /upload 同口径;**未配置/0/负 一律用默认**,
不存在「禁用上限」);删掉重复的字符常量,换算只留一份 `chatImageCharsFor`;补 3 条用例
(默认值==upload 预算、配 1 MiB 真生效且只有合法那条驱动回合、非正值回落默认)
- `Docs/api-reference.md`:§1 端点表按 `routes.go` **补齐**(auth refresh/logout、upload、files、三支 audio、
/admin/plugins/installed、/admin/llm/catalog、observability/logs、notifications、owner-profile、reminders),
既有编号一律未动(保交叉引用);两张 `historyMessage` 字段表补 `personaId`(+ `clientMsgId`);
修正 §8.1「主人档案保存仍 501」与实现不符(已真实实现)
- 复核:build/vet 绿 + gateway/config 包测试绿 + 前端 tsc 0 错 + vite build 成功
- 登记:§1 各表的编号跨表重复(不重编号以保交叉引用);`admin_plugins.go:217` 注释写错鉴权档(未改);
§6.4 的 WS 事件帧表仍未补全
|
2026-10-04 16:24:36 +08:00 |
|
AskaEth
|
decf59c1b4
|
feat(gateway,admin-web): Skill 运行时安装 + backthink 打扰原因 + 建提醒入口(P0-19/P0-20/P0-22,审计 §4.3)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- P0-19:POST /admin/plugins/install 支持 `kind:"skill"`(此前一律 400)——yaml 或 path 二选一、
name 一致性校验、显式错误码(bad_request/name_mismatch/skill_parse_failed/name_conflict)、
装后以 kind:"skill" 出现在 GET /admin/plugins;**文件来源解析错误绝不回显文件内容**(变异验证实测确认
该泄露向量真实存在:yaml 解码器会带出出错值前几字符 ⇒ 只回显路径 + 通用原因,细节进日志)
- P0-20:AdminBackthinkItem 补 route/detail/bypassed(DB 早已存,零迁移),面板 Backthink 展示
- P0-22:新增 POST /admin/automations/reminders(owner-only + 审计),at/message 必填、RFC3339 校验、
409 already_exists;过去时间不作 400(下次轮询立即触发)
- 顺手修真缺陷:新建 id 原用 time.Now().UnixNano(),Windows 时钟粒度粗到毫秒 ⇒ 并行创建撞 id 得假 409;
改用 crypto/rand 128 位 hex(仓库既有 skills/store.go 范式)
- 复核:gateway 包 build/vet/测试绿 + 前端 tsc 0 错;新增 12 用例(含 8 条非法输入显式码);
**4 条变异反证**均已回滚
- 未做(登记):WS 帧侧的 route/detail 未在前端展示(`lib/wsParse.ts` 3 行,需放行);skill 未落
skills.Store(重启后消失,注入点在 cmd/cyrene);提醒无删除入口(CancelReminder 已存在、无路由)
|
2026-10-04 14:21:41 +08:00 |
|
AskaEth
|
852c327360
|
refactor(web): 随路径式入口清理失效的管理链接(设计:用户端不提供管理入口)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- TopBar / Nav 的 hash 形式管理入口(#/admin、#/admin/system)**删除**(design/07 §0.2:用户端不提供任何
管理入口、bundle 不含该路径串);面板只经 ADMIN_PATH 路径直入
- AdminApp 三处 window.location.hash = "" 改调 router 导出的 navigateUserShell()
- 复核:tsc 0 错 + vite build 成功;用户端源码 grep "#/admin" 0 命中(仅在 channels/useAdapterNav.ts
尚有模块内闭环的 hash 字面量,属另一决策,仅报告未改)
- 局限:无 playwright/puppeteer 且不许加依赖 ⇒ 「布局未塌」为结构+静态样式依赖推断(无子元素数量相关样式)
|
2026-10-04 14:01:43 +08:00 |
|
AskaEth
|
a398dc9b92
|
feat(config,gateway,web): 路径式安全入口 ADMIN_PATH + 数据目录 platform 哨兵(裁决 1.6/16.4.7/12.4.2 + 1.5/12.4.3 核对)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 1.6 + 16.4.7:安全入口=路径式 `/<ADMIN_PATH>`(env `ADMIN_PATH`,默认 `admin`;`xxx/admin` 亦可,
非法值回退 `/admin`)。命中前缀 → 送注入了 `<base href="/">` + `window.__CYRENE_ADMIN_ENTRY__` 的面板入口页
(深链直达);**形如面板入口但前缀不符 → 404**(不回落用户端聊天壳);`/api/*`、`/ws`、webhook 先放行
⇒ **API 不藏**。前端路由改**路径式**(`router.ts` 只读注入,无硬编兜底;移除 hash 入口)
- 1.5 / 12.4.3 核对:暴露面默认 `0.0.0.0` 与裁决一致,无需改代码(仅补注释)
- 12.4.2:`DATA_DIR` 支持 `platform` 哨兵(大小写不敏感)→ 平台标准目录;默认 `./data` 与显式空值行为不变
(显式空值保持原兜底,避免正在运行实例突然换库)
- 复核:build/vet 绿 + config/gateway 包测试绿 + linux/darwin 交叉编译绿;新增 7 条用例,
并用真实构建产物(`webui.Assets()`)跑通「浏览器直开 /admin 能启动面板」(`<base>` 必须在资源引用之前)
- 变异 2 条(关掉错前缀 404 / 前端默认前缀兜底)已还原
- ⚠️ 运行时序:前端已生效而后端 hatch 未生效期间**面板打不开** ⇒ 需紧接一次「build 到位 + restart」
|
2026-10-04 14:00:25 +08:00 |
|
AskaEth
|
0e1f163ef9
|
feat(gateway,admin-web): 可观测面从轮询改为 WS 帧推送(裁决 05.4.3 + 16.4.3-WS)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 新增三个下行帧(纯新增,既有帧名/形状零改动):observability_log / observability_trace /
observability_metrics,各带 ≤1s 节拍、无新数据不发帧、单帧上限(日志 200 / span 100)
- 帧壳 {type,data} 不带 sessionId;**不进 frameReachesAll 白名单** ⇒ fail-closed 只发 owner+admin
(变异验证两次:加进白名单 → viewer 用例必红)
- 推送开启条件=owner/admin 凭据「读过一次」对应 REST(引导数据仍由该次读取返回),
连接全断即退订并停循环(无常驻 goroutine);既有 REST 端点路径/RBAC/形状完全不变
- 前端:删除 logs/metrics/trace 三处 useAutoReload 轮询;新增 observabilityStream(引导+去重合并+窗口)
与 observabilityViews(三个面板 + WS 徽标),长列表统一走已落地的 VirtualList
- 复核:build/vet 绿 + gateway 全包测试绿(含真 bus→真 /ws/chat 三角色帧断言)+ `-race`;
前端 tsc 0 错 + vite build 成功
- 待登记契约:api-client 需新增「可观测实时推送」一节
|
2026-10-04 13:58:03 +08:00 |
|
AskaEth
|
8d70f6d331
|
feat(web): 双档 UI 模式(性能档 = 现状 / Smooth Corner 华丽档)+ 开关挂载(裁决 16.4.4)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- design/uiMode.ts:档位纯逻辑(localStorage `cyrene.ui-mode` 持久化、跨标签页同步、未知值回落默认档),
只改 <html> 类名与 data 属性 ⇒ 不碰 React 树、不重挂载、几何尺寸不变
- 性能档=**现状**:tokens.css / styles/index.css 的改动**全为新增(0 删除)**,且全部限定在
`:root.ui-mode-fancy` 下;75 元素签名比对证明性能档渲染与改动前逐元素一致
- 华丽档对齐六大核心:连续曲率 corner-shape / 多层 Surface 环境光 / 弹簧动效 / 深色优先(浅色单列补偿)/
毛玻璃升级 / Liquid Glass(4 折射 + RGB 色差 + 鼠标跟踪 + 双层动态边框)
- 开关挂载:main.tsx 在 createRoot 之前 initUiMode(零闪烁);TopBar/Nav/AdminTopBar 三处挂载,
窄屏(<640px)按既有约定收起(实测否则 375px 角色块被压到 27px、面板溢出 73px),移动端由侧栏抽屉覆盖
- 复核:tsc 0 错 + vite build 成功;uiMode 38 项 node 测试 + headless Chrome 真渲染 30 项(真点击/真持久化/
真指针事件);变异反证(去掉读回 → 持久化往返必红)
|
2026-10-04 13:41:09 +08:00 |
|
AskaEth
|
c1a98d7c01
|
perf(admin-web): 长列表虚拟滚动 + 专属页只读配置卡(裁决 16.4.3/16.4.6)+ 修限流读写成对的数据丢失
- 16.4.3(效率最高):AdminTable 超阈值走窗口化(DOM 行数与数据量解耦,上下占位行撑滚动条);
新增可复用 VirtualList;适配器消息日志换成窗口化版
- 16.4.6(操作更便利):保留列表页内联编辑,同时把「专属页只读展示该平台完整字段」真正实现
(AdapterConfigSummary,此前专属页一个配置字段都不显示)
- 🔴 顺带修一条静默数据丢失:列表页限流三项原从 acct.config.* 读,而后端把它们存**账号顶层**
⇒ 表单永远回显空、保存时 undefined→0 会把已设限流清空。现改从顶层读、留空则不发该键、非法输入报错中止;
并顺手改正用户可见的错误文案(0 = 保护性默认 6条/分,负值才是关闭)
- 复核:tsc 0 错 + vite build 成功;channel limits 27 / admin virtual scroll 8023 项断言;3 条变异反证
|
2026-10-04 13:41:08 +08:00 |
|
AskaEth
|
b480b04dd0
|
feat(web,ws): 会话级消息缓存 + 图桶 + 忙态按会话过滤(裁决 16.4.5/16.4.2 + 修 16.4.1 的帧缺口)
- 16.4.5:非激活会话的帧**不再丢弃**,按自己的 sessionId 落进该会话桶(新增 store/sessionCache.ts 纯函数 +
useAppStore.sessionMessages/flowGraphs);切会话改为「存回/取出」而非清空;流式增量 O(1) 定位桶、
只延长已存在的助手气泡(不凭一帧 delta 凭空造消息)
- 16.4.2:图按会话入桶(上限 12 个),Backthink 页取 sess-backthink 的图而不再被用户会话图顶替;
空态区分「实时通道未连接」与「尚未产生链路图」;wire_runtime 的图源白名单注释按裁决口径重写(零行为改动)
- 修 16.4.1 的缺口:handlers/session.ts 补 `session_idle` / `session_interrupted`(此前落到未知帧兜底 ⇒
sessionBusy 可能卡 true ⇒ 预约永不落地);忙态**置忙与置闲两侧**统一按 frameIsActive 过滤(只过滤置闲
会让后台会话的忙帧点亮徽标而它自己的 idle 被丢 ⇒ 永久卡忙)
- 清理:删除 lib/streamScope.ts(口径已被 16.4.5 推翻)与只剩测试在用的 messageEventApplies
- 复核:tsc 0 错 + vite build 成功;messageSync 69 / session 19 项逻辑测试;4 条变异反证
|
2026-10-04 13:41:02 +08:00 |
|
AskaEth
|
f454273c5a
|
feat(web): 静默/活动态切换 + 预约生效(裁决 16.4.1)
- 唯一逻辑入口 components/modeSwitch.ts(纯函数判定 + 预约态 + 落地/撤销)+ ModeSelector 组件
- 活动态:她自己切(下行 mode_switch)直接生效,用户此时切换只登记**预约**、不偷偷改、不上行,
且不清除预约(两层不打架)
- 静默态:用户可即时切 + 上行 mode_switch
- **用户提前切 → 下次静默态生效**:`active` 由 true→false 时落地;徽章写「已预约 · X(她静默后生效)」,
不会让人误以为立刻生效
- 「活动/静默」判据=她是否在忙(sessionBusy || 屏上 streaming || sending);若口径要改成 capacity 三态,
只需改 modeSwitch.ts 的一个谓词(已登记)
- 复核:tsc 0 错 + vite build 成功 + node 逻辑测试 28 项;2 条变异反证(去掉活动态分叉 / 去掉 active 守卫)
|
2026-10-04 13:41:00 +08:00 |
|
AskaEth
|
1ac02bd77e
|
feat(gateway,admin-web): 面板可编辑 provider 推理形状 / 角色级路由 / multimodal 真语义(裁决 03.4.2/03.4.1)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 后端:AdminLlmProvider 暴露 reasoning{kind}(恒下发,""=未声明=auto)、personaModels、personaProviders;
顶层新增 reasoningKinds / personaProviderOptions 两个**取值域**(唯一来源= runtimeconfig 导出枚举,
校验错误文案也由枚举拼接 ⇒ 不可能漂移)
- 读写共用同一映射(admin_llm_provider.go),避免「读侧少一个键 ⇒ 面板一保存就抹掉」
- 写侧语义:缺失=保留、给出=整块/整表替换;非法值一律 runtimeconfig.Group.Validate() ⇒ 400 bad_request
(实测非法 kind 返回 400 且 models.json 完全不动,而非 500 静默失败)
- 前端:新增 LlmProviderRouting(推理形状下拉 + multimodal 开关 + 按角色路由编辑器);
目录能力位改三态(true/false/未标注),「未标注」不再谎称「不支持」;目录/角色清单不可用时
**不阻塞编辑**且与「字段为空」是不同状态;绝无 mock/sample 回退
- 复核:build/vet 绿 + gateway/config 包测试绿 + tsc 0 错 + vite build 成功;2 条变异反证(删 reasoning
补丁 → 2 例红;删 Group.Validate → 该 400 用例红)已还原
- 注意(主会话已知):npm run build 把新 SPA 写进 frontend/web/dist,而运行中的二进制优先从磁盘读该目录
⇒ :10942 目前是「新面板 + 旧后端」,等阶段 5 全绿后按红线「先 build 到位、再一次 restart」切换
|
2026-10-04 13:06:54 +08:00 |
|
AskaEth
|
3972ea4007
|
refactor(admin-web): 模型目录面板改走既有 API 层(adminGetLlmCatalog)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 新增 lib/api/admin.ts 的 adminGetLlmCatalog({refresh}) + 目录响应类型(lib/types/admin.ts),并由 lib/api.ts 再导出
- LlmCatalogPanel 不再直连 lib/api/core(它曾是全仓唯一这样做的 admin 模块),行为不变:
五种状态判定、gzip 响应、离线粘贴导入均保持
- 复核:grep lib/api/core 全仓前端 0 命中;npx tsc --noEmit 0 错;npm run build 成功
|
2026-10-04 12:45:36 +08:00 |
|
AskaEth
|
63e5b70e3d
|
feat(gateway,admin-web): 模型目录自动化 —— GET /admin/llm/catalog(裁决 03.4.2 次件)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
- 新增 GET /api/v1/admin/llm/catalog(owner|admin,?refresh=1 强制刷新):抓 models.dev api.json 并裁剪
(226 供应商 / 8392 模型 → 1.31 MB),写落盘快照 data/llm_catalog.json(无凭据,TTL 24h,gitignore)
- 降级诚实:快照优先 → 在线刷新 → stale 快照兜底(标 stale+warning)→ 都不行回 503 catalog_unavailable,
绝不编造;抓取只发生在该端点内,**不进运行时热路径**
- 前端:新增 llm/LlmCatalogPanel(在线目录 + 粘贴导入落同一份候选 → 一键带出 baseURL/model id/multimodal/
maxTokens);Llm.tsx 挂载面板、删掉硬编码的「主→备→本地」假状态、表单按 id#seq 重挂
- 诚实性:面板把「加载中 / 端点未实现 / 抓取失败或离线 / 响应结构不认识 / 目录为空」分成五种独立状态,
全程无 mock/sample 回退
- 复核:新增 9 条零外网用例全绿;**变异**(Stale:true→false / TS 只认 camelCase)各自红后已还原;
Go 真产出与 TS 解析器双向实测 8392 模型能力位零差异
- 越界说明:routes.go 仅 +3 行(注册该路由);llm/LlmCatalogPanel.tsx 为拆出以守住 Llm.tsx ≤500 行
|
2026-10-04 12:43:04 +08:00 |
|