Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
## 0.0.520 (2026-11-11)

### 后端
- **钉钉进度卡正文上限从 1500 提到 6000**:状态行、回复和本轮工具列表仍拼在同一个 `body` 里,整段超过 6000 字符才截断,与飞书进度卡正文上限一致。实时进度卡和定时任务结果卡都走这一段。
- **修复钉钉会话的定时任务跑完后结果不推回钉钉**:定时任务收尾此前只把最终回复推回微信和飞书。在钉钉私聊或群里创建的任务会照常执行,回复只留在会话里,钉钉对话看不到。现与飞书对齐:直跑、「立即执行」,以及没有进度卡的失败会话恢复,成功后都把最终回复推回原钉钉会话。机器人配了进度卡模板时发一张「处理完成」卡(不写入执行中的进度卡映射,避免崩溃恢复把这张通知刷成「正在恢复」);没有模板或卡片投放失败时改发 markdown。引用标记在推送前剥离。失败和取消不推。

### 桌面 Electron
- **修复 LOCAL 任务粘贴超长文本后发送报「写入失败」并取消发送**:粘贴 ≥2000 字会转成 `pasted-text-N.txt`,发送时由主进程写入 `~/.mao/attachments/`。写盘本身成功,但 IPC 回的是工具协议 `{ success, path }`,渲染层只认与普通附件拷贝相同的 `{ ok, absolutePath }`,于是把成功当成失败(提示「文件 pasted-text-N.txt 写入失败」),附件全部失败后整单取消。现将该入口的返回改回 `{ ok, absolutePath }` / `{ ok: false, error }`;`dump_user_messages` 委托仍走工具协议,不受影响。同名粘贴仍追加 `-1` 副本,不覆盖已有附件。

### 管理后台
- **用量分析「用户用量明细」去掉账号、消息、最后登录三列**:表格和 CSV 都不再带这三列。用户名仍在「用户」列(无显示名时回落账号);消息数仍用于上方「用户活跃排行」。
- **用量分析「用户用量明细」操作列两个链接拉开间距**:「调用流水」和「用量详情」原先紧贴成一整段,现中间留 12px,列宽同步加宽以免折行。
- **容器管理列表支持筛选和分页**:可按关键词(用户 / Agent / 容器名,纯数字按用户 ID 或 Agent ID 精确匹配)、运行状态、在册与否、是否有任务筛选,每页 10 / 20 / 50 / 100 条,默认 20 条、第一页。筛选和翻页都在已取回的列表上完成。有筛选时按钮变为「重建筛选结果」,处理的是筛选后的全部容器,而不是只处理当前页。
- **容器管理列表的用户、Agent 列改为名称**:用户列优先显示「显示名称」,否则显示用户名;Agent 列显示 Agent 名称。用户或 Agent 已删除、或名称字段为空时回落 `#ID`。单行重建 / 删除确认,以及「重建全部」的失败明细,同步改用名称。
- **用户详情「用量」Tab 拉开卡片的垂直间距**:总账卡、趋势图、按模型 / 场景 / Agent 三列这几块上下原先贴在一起,现改为 16px;左右间距仍为 12px。窄屏折行时,总账卡行与三列卡片的行距同样为 16px。
- **用户详情「用量」总 Token 卡改用 K / M / B 缩写**:总 Token 及输入、输出、缓存改为与用量分析相同的 `formatTokens`,固定两位小数(如 2,713,315,304 → 2.71B)。

## 0.0.520 (2026-11-11)

### 管理后台
- **修复在线设备页设备 ID 点不了复制**:设备 ID 列开了表格溢出提示,列宽不够时 ID 和「复制」被收进提示里的纯文本(悬停能看见「复制」,但那不是按钮,点不到;真正的按钮被裁在单元格外面)。现改为 ID 自身省略、悬停只展示完整 ID,「复制」按钮固定留在单元格内;点击 ID 或「复制」都会写入剪贴板。

## 0.0.519 (2026-11-11)

### 前端(桌面 / Web / 安卓)
- **修复部署重启续跑后任务停在半截、看不到最终回复(刷新页面才恢复)**:蓝绿切换时旧执行被新执行顶掉,旧流式气泡被打上「中断」标记;终态重拉历史时它被当成服务端未落库的唯一副本保留,并统一追加在 REST 消息之后。轮次渲染取最后一条 assistant 作为最终回复,于是界面停在部署前那半截内容,真正的最终回复反而被折叠进「已执行 N 个步骤」。现区分「用户中止」(服务端不落库,气泡是唯一副本,必须保留)与「被新执行顶掉」(内容会由续跑重新产出),后者在终态重拉时不再保留;边路任务 / 子代理面板在终态也不再生效「保留本地流式气泡」的判定。

### 后端
- **修复钉钉进度卡执行中不显示「取消任务」按钮**:按钮可见性原本写成「字符串变量 `status` `isEqual` `running`」,`editorData` 里条件完整、卡片平台也照原样保留,但平台生成的 `widgetInfo`(客户端真正渲染的那份)把条件抹成了空串 `@triple{'' , 'visible', 'gone'}`,条件位恒假,按钮永远是 `gone`。官方样例模板证实:卡片平台只把「boolean 变量 + isTrue/isFalse」渲染成可用的 `@data{...}` 条件。现改为由 `progressCardParams` 下发 `canCancel` / `canRetry` 两个 boolean 变量(`running` → `canCancel=true`,`failed` → `canRetry=true`,其余状态均为 `false`),模板按钮可见性相应改为 `isTrue(canCancel)` / `isTrue(canRetry)`。`status` 变量保留作语义占位。新增回归用例固化四种状态与两个 boolean 的对应关系。

### mao-cli
- 钉钉进度卡模板同步改用 boolean 变量控制按钮显隐(`canCancel` / `canRetry` + `isTrue`),并在说明里写明「字符串 `isEqual` 比较会被平台抹成空条件」这一约束与完整踩坑过程;自检脚本新增对应检查(可见性必须是 `isTrue(<boolean 变量>)`、变量表必须声明该 boolean),已用修复前模板反向验证能报出全部问题。

## 0.0.518 (2026-11-11)

### mao-cli
- **修复钉钉进度卡「取消任务」按钮点了没反应、重试按钮点不动**:两个按钮各有缺陷,且都不在按钮可见性条件上——取消按钮的 `actionId` 是空的 `dynamicString`,点击后不上报 actionId,后端 `parseCardCallback` 取不到值、整个卡片回调分支不会触发,表现为「按钮看得见但点了没反应」;重试按钮的 `status` 是 `disabled`,渲染时置灰且不响应点击,表现为「重试按钮完全点不动」。现已把取消/重试的 `actionId` 补为字面量 `cancel` / `retry`,`status` 统一改为 `normal`。自检脚本新增这两类检查(`actionId` 空值、`status=disabled`),并同步订正模板说明:`widgetInfo` 只是平台导出时生成的渲染快照,导入后客户端渲染以 `editorData` 的组件树与变量条件为准(`useCustomWidgetInfo=false`),手改 XML 不生效。

### 后端
- **修复定时任务 Spring cron 在「日 + 周」同时受限时按 OR 语义触发**:croner 默认 `legacyMode: true` 是 POSIX 的「日或周任一命中即触发」,与产品接受的 Spring/Quartz 方言(两者都满足才触发)相反。例如 `0 0 12 1 1 1` 本意是「1 月 1 日且为周一」(约四年一次),修复前会把 1 月每个周一都排进档期。现 5 处 Cron 构造统一使用同一组解析选项(`legacyMode: false`,时区不变),预览、下次档期计算、落库校验、补跑与调度器装载口径一致;`0 0 10 ? * MON-FRI` 等单方受限表达式行为不变。

### agent-cli
- **修复无 ripgrep 时 glob/grep 回退的路径匹配语义**:`**/` 现按「零个或多个目录」编译,`src/**/*.ts` 能命中 `src/a.ts`、`**/*.ts` 能命中根目录文件;同时去掉目录遍历里把带目录前缀 pattern 退化成全仓 basename 匹配的兜底,`src/**/*.ts` 不再命中 `docs/b.ts` 等越界文件。匹配前统一把相对路径转成 `/` 分隔,Windows 回退路径不再因反斜杠失配;结果输出仍保留平台路径形态。

### 前端(桌面 / Web / 安卓)
- **修复富文本粘贴转 Markdown 时表格单元格内的竖线撑破列结构**:turndown-plugin-gfm 原样输出 cell 文本,`a|b` 里的 `|` 会被 GFM 当成列分隔符,2 列表格渲染成 3 列、后续行整体串列。现覆盖表格 cell 规则,把单元格文本中的 `|` 转义为 `\|`,表头与表体一致处理。

## 0.0.517 (2026-11-11)

### 前端(桌面 / Web / 安卓)
- **修复暗色主题下「设置 → 服务器」状态卡变成一块白卡片**:该卡片底色误写成未定义令牌 `--aw-bg` 配亮色回退值 `#fbfbfc`,回退值恒生效,于是暗色主题下这块仍是近乎纯白的亮底,配暗色文字既刺眼又读不清(亮色主题下回退值恰好接近底色所以没暴露)。现改为随主题切换的 `--aw-canvas-parchment`(亮 `#f5f5f7` / 暗 `#222226`),在卡片内比卡面深/浅一档,边界清晰。配套新增 `ServerSettingsView.theme.test.ts` 源码守卫:组件引用的 `--aw-*` 令牌必须都已在 `style.css` 定义,避免再用「未定义令牌 + 亮色回退值」制造只在暗色档暴露的回归。

## 0.0.516 (2026-11-11)

### mao-cli
- **修复钉钉进度卡执行中不显示「取消任务」按钮,并删掉冗余的卡片标题**:按钮整个消失(连取消任务都没有)是因为 ButtonBlock 缺了区块级 `visible` 字段——同级 Markdown / DividerBlock 都有、唯独它没有,导致整个按钮区块不渲染,而非单个按钮的条件问题;现已按同级组件同样的结构补齐恒显示配置,单个按钮的显隐仍由各自的 `status` 条件决定。另外卡片顶部那行「正在处理」来自 `Card.cardName`(`${title}`),与正文首行「**状态:正在处理** · 第 1 轮」是同一信息出现两次,且 Grid 里还另有一个纯 `${title}` 的 BaseText;现已把 cardName 置空并移除该 Grid,标题信息只保留正文状态行一处。自检脚本同步补齐这两类检查(`${title}` 残留、ButtonBlock 缺 visible),已用修复前的模板验证能报出全部问题。

## 0.0.515 (2026-11-11)

### 前端(桌面 / Web / 安卓)
- **左右侧栏滚动条与对话区一致隐藏**:任务列表,以及右侧任务、文件、Git 页,内容仍可滚动,滚动条不再占位显示。

## 0.0.514 (2026-11-11)

### 后端
- **修复 4 处跨平台生产缺陷**(前后端全量测试在 Windows 开发机跑查发现,Linux 部署行为不变):①electron 更新清单缓存排除正则只认 `/` 分隔符——`fastifyStatic` 的 setHeaders 拿到平台本地路径,Windows 部署下 `latest*.yml`/`releases/*.yml` 错误走 7 天强缓存(自动更新可能拿到陈旧清单),改为 `/` 与 `\` 双兼容;②容器运行时闭包 manifest 的文件键从 `path.relative` 改为强制 POSIX 分隔符——buildId 是容器代码漂移检测的事实源,Windows 构建机与 Linux 部署此前会产出不同 manifest;③`toWorkspaceRelative` 的工作区相对路径展示统一为 `/` 分隔(IM 图片保存提示等面向用户与 Agent 的文案,Windows 开发机不再出现反斜杠);④信号脚本探针超时击杀在 Windows(无负 pid 进程组语义)改为直接终止 leader——此前超时后探针进程无法回收、整条链路挂起 5s 超时。

### agent-cli
- **rg 路径归一修正**:Windows 下 rg 输出带 `.\` 前缀,grep/glob 结果路径剥掉该前缀后按平台形态透传(各消费方按平台 join 构造),Linux 输出恒等透传。测试基建:临时目录清理在 Windows 上遇句柄占用(git 子进程残留)降级为告警不淹没断言失败;若干 POSIX-only 语义用例(执行位恢复、0600 文件模式、bash 回退目录、进程组击杀)在 Windows 跳过。

## 0.0.513 (2026-11-11)

### 前端(桌面 / Web)
- **输入效率包:富文本粘贴转 Markdown 与截图轻标注**(方案 docs/plan/2026-11-10-rich-paste-annotate-technical-design.md,提案批次 28):①粘贴分流新增 text/html 分支——Word / 网页 / 飞书文档 / Excel 复制的富文本转为**等义 Markdown 源文本**插入输入框(标题/列表/粗斜体/链接/行内代码/代码块;Excel 区域含合并单元格按左上值展开成规整 GFM 表格、首行作表头),渲染层 MarkdownContent 正常渲染;turndown + turndown-plugin-gfm 动态加载(chunk 门禁加库内部字面量防进首屏包),converter 在 ChatInput setup 预热、粘贴时未就绪 / 超 512KB / 转换空一律回落现状纯文本兜底(无损降级);三条既有分流契约(图片文件化 / ≥2000 字符长文本文件化 / 快捷命令标记)保序不动。②图片轻标注——待发图片缩略卡新增「标注」按钮,全屏 canvas 编辑器(画笔 / 矩形 / 箭头 / 文字 / 马赛克 / 撤销,固定红色单线宽),操作栈重放实现撤销、底图只读零损伤,确认后替换该待发附件为 PNG(原图驻内存保留,可反复重标);图片发送通道(上传/LOCAL)零改动。零后端改动、零迁移;标注按钮在全部执行模式可用(图片发送通道无模式分支)。

## 0.0.512 (2026-11-11)

### 前端(桌面 / Web)
- **工具结果表格化视图:列表类结果一键转表格**(方案 docs/plan/2026-11-10-tool-result-table-view-technical-design.md,提案批次 28):`task_list` / `list_scheduled_tasks` / `list_signal_watchers` / `dump_user_messages` 及自定义 HTTP / MCP 工具返回的「列表形 JSON」结果,卡片结果区出现「表格 / 原文」切换(默认原文,与现状逐字节一致;按钮仅命中时出现)。判定从严:顶层数组或唯一数组字段包裹对象、元素全为扁平键值对象(值仅 string/number/boolean/null)才转表,嵌套值 / 多数组字段 / 空数组 / 超限一律保持原文——不设键名白名单(目标工具数组键 todos/tasks/watchers/files 与任意自定义包裹键同规则命中,误判防护靠唯一性 + 扁平性双闸)。表格复用中心 `TableGrid`(首行表头、数值列右对齐、200 行虚拟滚动),规模上限 201 行 × 30 列 / 1MB 文本;「复制为 CSV」按 RFC 4180 转义,粘贴进 Excel 行列同构;视图偏好按工具名存 localStorage(`aw-tool-result-view.*`,设备本地口径)。零后端改动、零迁移、零新依赖;解析输入为结果全文(后端摘要链不改写结果本体),表格态可呈现超过原文态 4000 字截断显示量的完整数据。

## 0.0.511 (2026-11-11)

### 后端
- **管理后台新增「用户用量」视图接口**(方案 docs/plan/2026-11-10-admin-user-usage-product-design.md):新增 `GET /v1/admin/users/:id/usage/summary`(周期总账 + 按模型 / 场景 / Agent 分布)与 `GET /v1/admin/users/:id/usage/trends`(按天 / 小时分桶、后端补零),复用桌面端「我的用量」同一套服务方法(`summaryForUser` / `trendsForUser`),零新表零迁移、聚合走既有 `idx_llm_call_user_created` 索引。权限 `analytics:read`(与用量分析同语义,方案 D1);目标用户不存在 / 参数非法返回业务码 `USER_NOT_FOUND` / `PARAM_INVALID`(HTTP 200 Result 口径),自定义区间跨度上限 365 天(对齐桌面回溯口径,方案 D4)。连通性自检行(`connectivity_test`)`user_id` 恒为 NULL,单用户聚合结构上天然不含——与桌面 me/* 口径天然一致,无需排除参数(方案 §6.3)。周期与自定义区间解析沿用 me/* 语义(非法回落 last30、起止倒置交换),`summary/trends` 响应新增解析后的 `window`(起止日历日)供前端深链调用流水直接取用。用量契约(周期枚举 / 聚合行 / 响应壳)下沉 `@mao/contracts`(usage.ts),后端 re-export 保持既有 import 路径。

### admin
- **用户详情抽屉新增「用量」Tab**(方案 §5):位于「会话」之后,权限 `analytics:read` 门控(无权限整 Tab 不渲染);周期下拉(今日 / 昨日 / 本周 / 上周 / 本月 / 上月 / 近 7 天 / 近 30 天 / 自定义,默认近 30 天)+ 四张统计卡(总 Token / 总成本 / 调用次数与成功率 / 平均解码速度)+ 趋势双轴图(Token 左轴、调用次数右轴)+ 按模型 / 场景 / Agent 三块分布表;Tab 懒加载(首次激活才请求,不加重抽屉首开)。深链出口按钮按目标页权限二次门控:「查看调用流水」(`llm-call:read`,带周期日期直达)与「查看会话」(`session:read`)。用量分析页用户明细行新增「用量详情」反链(`/users?detail=&tab=usage`),用户列表解析深链打开抽屉并定位用量 Tab;场景名复用 `llmCallLabels` 汉化表。

### 前端(桌面 / Web)
- **用量类型契约收口**:桌面端手写的用量聚合类型(`UsageTotals` / `UsageModelRow` / `UsageSceneRow` / `UsageAgentRow` / `UsageTrendBucket` / `MyUsageSummary` / `MyUsageTrends` / 周期枚举等,api/index.ts 879-976 行)删除,改从 `@mao/contracts` 消费(`UsageTrendBucket` → `LlmCallTrendRow`、`UsageAgentRow` → `LlmCallAgentAggRow` 改名由编译期兜底);字段零变化、行为零变化。

## 0.0.510 (2026-11-10)

### 后端
- **管理后台「容器管理」纳入 Docker 未在册残留容器**:此前列表只含当前后端进程在册的容器(注册表纯内存),后端重启 / 崩溃后遗留的 Docker 容器(优雅重启产生的 exited、异常残骸的 running)完全不可见,「重建全部」也覆盖不到,只能靠 `container-gc.sh` 定时静默清理。现列表合并 `docker ps -a`(按 `mao.runtime=user` label 过滤):未在册容器标记 `tracked=false` 一并展示(含 Docker 上报的存在时长);docker 扫描失败时降级为仅在册列表,不影响接口可用。`POST /v1/users/:id/container/recycle` 语义扩展:对未在册残留容器执行 `docker rm -f` 并返回新增的 `removed` 状态(按容器名 + 运行时 label 精确匹配,绝不误删外部容器),下次使用时按最新配置自动重建(挂载数据在宿主机目录,不受影响);整用户聚合回收同时清扫在册容器与该用户的残留容器(聚合优先级 failed > skipped-busy > rebuilt > removed)。孤儿删除与并发 `ensureRunning` 经同键串行锁互斥,锁内复核防止误删刚被纳管的新容器。旧命名 `mao-user-{纯数字}` 孤儿容器仍由 `container-gc.sh` 清理。容器列表 / 回收结果契约类型下沉 `@mao/contracts`。
- **管理后台新增「在线设备」实时查询接口**:新增 `GET /v1/statistics/online-devices`(权限 `analytics:read`),返回当前 WS 在线连接实时快照——总数(连接数 / 去重在线用户数 / 去重设备数)、按客户端类型(桌面客户端 / 安卓 App / 终端 CLI / 浏览器 Web / 页面嵌入)分组计数与按用户的连接列表(设备名 / 平台 / 设备 ID / 连接时长 / 认证到期 / 认证来源)。数据源为 WS 连接注册表纯内存快照(`StreamingWsRegistry.listConnections()`,register 时新增 connectedAt 记录),零新增表零迁移;「在线」= WS 连接存活,断线即离线,无历史留存。去重设备口径:electron / cli 按 `(userId, deviceId)` 去重(同机桌面多窗口与 mao-agent CLI 共用 device.json 算一台),安卓壳逐条计数,browser / embed 无设备标识不计入设备数。响应契约类型下沉 `@mao/contracts`(statistics.ts)。只读快照不触发过期连接的惰性关闭(无 close 副作用)。

### admin
- **容器管理面板支持查看与清理 Docker 残留容器**:未在册容器在状态列显示「未在册」标签(悬停说明来源与处置方式),启动时间列显示 Docker 上报的存在时长,操作列提供「删除」(确认弹窗说明「下次使用时自动重建」语义);「重建全部」覆盖未在册残留容器,确认与结果汇总区分重建 / 删除计数。用户详情抽屉「重建容器」适配新增的 `removed` 结果(残留删除成功不再误报「重建失败」)。列表与回收结果类型改从 `@mao/contracts` 消费,不再本地手写。
- **新增「在线设备」菜单页**:与「用量分析」同级(overview 组,权限 `analytics:read`)。统计卡展示在线连接 / 在线用户 / 去重设备三个数字与五类客户端分组计数;表格按用户列出每条在线连接(客户端类型标签、设备名 / 平台 / 设备 ID 一键复制、连接时长、认证到期、认证来源)。15s 自动轮询 + 手动刷新 + 「更新于」时间戳;页面切走自动停轮询(keepAlive onActivated/onDeactivated),轮询失败静默保留旧数据、靠「更新于」暴露陈旧,避免每 15s 弹红条。客户端类型中文名走 `Record<WsClientType, string>` 穷尽汉化表(联合扩张漏项编译期即失败)。


## 0.0.510 (2026-11-11)

### 前端(桌面 / Web / 安卓)
- **修复暗色主题下「设置 → 服务器」状态卡变成一块白卡片**:该卡片底色误写成未定义令牌 `--aw-bg` 配亮色回退值 `#fbfbfc`,回退值恒生效,于是暗色主题下这块仍是近乎纯白的亮底,配暗色文字既刺眼又读不清(亮色主题下回退值恰好接近底色所以没暴露)。现改为随主题切换的 `--aw-canvas-parchment`(亮 `#f5f5f7` / 暗 `#222226`),在卡片内比卡面深/浅一档,边界清晰。配套新增 `ServerSettingsView.theme.test.ts` 源码守卫:组件引用的 `--aw-*` 令牌必须都已在 `style.css` 定义,避免再用「未定义令牌 + 亮色回退值」制造只在暗色档暴露的回归。

## 0.0.511 (2026-11-11)

### mao-cli
- **修复内置钉钉进度卡模板的两处按钮缺陷**:①「请求成功判定条件」为空——钉钉明确该配置是 Toast 的总开关,不配则平台告警且按钮的成功/失败 Toast 文案都不生效;现按后端回调契约补齐,取消按钮绑 `status == 'cancelled'`、重试按钮绑 `status == 'running'`(后端取消/重试成功时会用 `cardParamMap` 把 `status` 更新为对应值,失败路径只回 `tips`、不改 `status`,判定因此能精确区分成败)。②**重试按钮完全失效**——它的点击事件类型是「打开链接」且链接为空,点击只会打开一个空页面,根本进不了后端的重试分支,「失败态显示重试」形同虚设;现改为「回传请求」。配套新增 `template/check-template.js` 自检脚本,把变量声明、Markdown 绑定、actionId、actionType、可见性与判定条件固化成可执行检查(已用带缺陷的旧模板验证能报出全部 6 处问题),README 的改模板注意点同步补充。

## 0.0.509 (2026-11-10)

### mao-cli
- **mao-cli 技能内置钉钉进度卡模板**:`skills/mao-cli/template/dingtalk-progress-card.json` 是 Mao 钉钉机器人进度卡在钉钉开放平台卡片编辑器的导入文件,排版与飞书进度卡同构(状态行 + 完整回复正文 + 本轮工具列表),终态后只保留结果、隐藏「取消任务」按钮,失败态显示「重试」。此前新增机器人必须自己在卡片平台从零画一套模板,现在可直接导入本文件、保存、发布,再把发布后的模板 ID 填到管理后台「钉钉机器人 → 进度卡模板 ID」。模板用法、变量契约与改模板注意点见 `skills/mao-cli/template/README.md`,已在 `SKILL.md` 知识库路由表与 `reference/dingtalk-bot.md` 交叉登记。

## 0.0.508 (2026-11-10)

### 后端
- **钉钉进度卡改为「状态行 + 完整正文」,与飞书卡片同构**:此前完成卡只有一行「共 N 轮 · 耗时 X 秒」,Agent 的回复全文看不到——因为 `runtime.ts` 只在 failed/cancelled 时才把正文透传给卡片,running/completed 都丢掉了。现在正文全状态透传,卡片正文按 `**状态:处理完成** · 共 3 轮 · 耗时 44 秒` + 空行 + 回复全文 + `**本轮工具**` 列表拼接,执行中则实时显示流式累积的 assistant 文本与在途工具耗时。配套新增 `card-body.spec.ts` 锁住拼接格式、执行中不带耗时、超长截断等行为。


## 0.0.507 (2026-11-10)

### 后端
- **修复钉钉任务完成后重复回复**:进度卡终态更新成功后,还会再发一条独立的文本消息,同一条结果出现两遍(卡片一份 + 消息一份)。现与飞书对齐:卡片终态(完成 / 取消 / 失败)更新成功就只由卡片呈现结果,不再另发消息;仅当卡片更新失败(网络抖动、模板问题)才回落发文本,保证用户至少收到一遍。


## 0.0.506 (2026-11-10)

### 后端
- **修复钉钉进度卡/排队卡始终投放不出去**:钉钉 `createAndDeliverCard` 接口要求场域模型(`imRobotOpenSpaceModel` / `imGroupOpenSpaceModel`)与投放模型(`imRobotOpenDeliverModel` / `imGroupOpenDeliverModel`)**成对**传入,只传前者报 `param.openDeliverModelError`、只传后者在 HTTP 200 里回 `spaces of card is empty`,两者齐备才会真正进入场域投放。此前只传了前者,导致卡片从未创建成功,用户看不到进度卡,只能等 Agent 执行完收到一条纯文本回复。群聊投放此前还缺 `imGroupOpenDeliverModel.robotCode`(钉钉据此定位机器人,缺失报 `chatbot not exist`),现已从机器人配置带入。另外该接口 HTTP 200 不代表投放成功,场域级失败只体现在 `deliverResults` 里,现已解析该字段并抛错,避免"接口通了但用户什么都没收到"被静默放过。


## 0.0.505 (2026-11-10)

### 后端
- **本地工具执行不再因执行端通道失效长时间卡住**:此前会话绑定设备后,只要 `isConnected` 判定在线,服务端就开等执行端回报,最坏要干等满总超时(默认 900 秒)才返回 `Local tool execution timed out`——桌面端闪断、或 mao-agent 与桌面端共用同一 `deviceId` 时执行连接被抢走(半死连接:socket 活着但执行端没有该会话上下文,收到帧不回报),都会出现这种情况。现在:
  - 设备定向投递零成功时(该设备当前没有任何可写执行连接),立即回「本地执行端未连接,请确保桌面客户端正在运行」,同时收敛服务端 pending,不再留下无人认领的等待。
  - 无审批请求下发后,若执行端在 60 秒内既没回 `tool_approval` 空表态也没回结果,即判通道半死并快速失败,提示用户确认桌面客户端在运行、未被后台挂起后重试。该窗口与总超时定时器同起跑并取更早触发者,只会让结果更早,绝不会把请求拖得比总超时更久。
  - `sendToDevice` 在主执行连接不可写(readyState 非 OPEN / 未授权)时,回退到同一 deviceId 的另一条存活连接(同机多窗口 / 多 CLI 进程场景),避免整个设备被误判离线。

### 前端(桌面 / Web / 安卓)
- **侧边栏钉钉分组改用钉钉品牌图标**:钉钉私聊 / 群聊分组此前用通用聊天气泡图标,与飞书、微信的品牌 logo 不一致,看不出这条分组来自哪个 IM。现在换成钉钉官方图形(`#1296DB` 品牌蓝),与既有飞书 / 微信图标同尺寸、同渲染方式;分组判定逻辑不变,仅替换图标资源。


## 0.0.504 (2026-11-10)

### 后端
- **钉钉 OAuth 登录配置迁入管理后台**:绑定用登录应用的 Client ID / Client Secret / 回调地址此前只能改 `.env` 再重启后端,现在在「系统设置 → 登录认证 → 钉钉 OAuth 登录」维护,保存后即时生效。配置改由 `system_setting` 承载(`dingtalk.oauth.clientId` / `clientSecret` / `redirectUri`,Secret 加密存储、列表返回掩码),绑定短链、授权跳转、回调换 token 全部改为实时读取,不再依赖启动时快照。做法与飞书 OAuth 登录(`auth.feishu.*`)一致:`DINGTALK_OAUTH_*` 环境变量不再生效,升级时首次启动由 SettingsBootstrap 自动导入 DB(管理员在后台保存过的行不被覆盖),导入后即可从 `.env` 删除。`DINGTALK_BOT_ENABLED` 与 `APP_DINGTALK_BOT_SECRET` 仍是环境变量(前者改后需重启,后者是加密主密钥,放进同一张表等于用同一把钥匙锁门和锁钥匙)。

### 管理后台
- **系统设置新增「钉钉 OAuth 登录」组**:与「飞书 OAuth 登录」并列在登录认证分组下,含 Client ID、Client Secret(密码框 + 已设置提示 + 清空)、回调地址(格式校验),复用集成配置的保存与测试链路。

## 0.0.503 (2026-11-10)

### 后端
- **Docker 模式的 Shell 脚本哨兵改在属主容器内执行**:此前脚本探针一律由后端进程在宿主机执行,即使用户运行环境已切到 Docker,脚本仍能以后端同一系统用户访问宿主机文件和网络。现在按哨兵属主的有效运行模式分流(用户级覆盖优先,与会话 Shell 相同):Docker 模式在该用户、该 Agent 的运行时容器里执行,可见范围与容器内 Shell 一致;host 模式仍在后端宿主执行。容器起不来、运行时版本尚未对齐或通道断开时,本轮记一次失败并顺延到下个轮询间隔,不计入连续五次自动停用,也不改回宿主机执行。有活跃脚本哨兵时,对应容器会被定期唤醒。

## 0.0.502 (2026-11-10)

### 前端(桌面 / Web)
- **工具卡片的审批与截断改为图标**:状态区不再平铺「AI 已批准 / 低风险放行 / 规则放行 / AI 已拒绝 / 输出已截断」。依次对应头像、魔棒、文档、红色圆圈叉、琥珀色剪刀。悬停或点按仍显示原文;审批图标附带理由。

## 0.0.501 (2026-11-10)

### 前端(桌面 / Web)
- **深色主题压低长文亮度**:对话正文、标题和导航主文字由近白 `#f5f5f7` 改为 `#d0d0d4`(卡片上约 10:1),说明文字再低一档。页面底停在炭黑,控件边框保持深灰。浅色主题不变。

## 0.0.500 (2026-11-10)

### 后端
- **失败会话批量恢复不再限于 402/403**:`GET /v1/admin/sessions/recover-failed/preview` 改为列出时间窗内全部 `phase=FAILED` 的主会话(额度、权限、工具错误、超时等),错误摘要优先用终态 `executionError`。`POST /v1/admin/sessions/recover-failed/run` 按勾选 id 续跑。历史参数 `scope`(`QUOTA` / `ALL_402_403`)忽略,不再据此跳过。LOCAL、子代理/边路、定时任务待重试、执行中仍会跳过。清单最多 200 条,超出时 `truncated=true`。

### 管理后台
- **「恢复失败会话」改为勾选后执行**:会话管理页入口不再只扫 402/403。弹窗列出失败会话(可按标题、用户、错误筛选),管理员勾选后发起恢复,单批仍最多 20 个;不再默认全选。本地执行、定时任务待重试、正在执行的行不可勾选。

### 终端 CLI(mao-cli)
- **`mao admin-session recover-failed`**:`preview [--window-hours]` 列出时间窗内失败主会话;`run --session-ids 431,473` 按 ID 发起恢复(单批最多 20,需 `session:write`)。重复 ID 会合并,`--window-hours` 只接受 1–168。

## 0.0.499 (2026-11-10)

### 后端
- **API Token 支持永久删除**:`DELETE /v1/open/tokens/:id` 仍是吊销(记录保留、立即失效)。新增 `DELETE /v1/open/tokens/:id/permanent`,属主可删掉有效或已吊销的 Token;删除后鉴权变为查无,历史调用流水保留,管理端按原 Token 重发会拒绝。

### 前端(桌面 / Web)
- **开放接口 Token 卡片可删除**:每条 Token(含已吊销)增加删除按钮,确认后永久移除。吊销改为停止图标,与删除区分。

### 终端 CLI(mao-cli)
- **`mao open token purge --id <id>`**:永久删除 API Token。`open token delete` 仍是吊销。

## 0.0.498 (2026-11-10)

### 终端 CLI(mao-cli)
- **信号哨兵 CLI 支持 Shell 脚本探针**:`mao signal-watcher` 此前只有 HTTP 探针参数(`--url` 为硬编码必填),脚本哨兵只能走桌面端表单或 REST 创建。现新增 `--probe-type HTTP|SCRIPT`、`--script-path`、`--timeout-seconds`:
  - 必填项按类型分流:HTTP 必填 `--url`,SCRIPT 必填 `--script-path`(缺哪个报哪个,不再一律报缺 `--url`)。
  - 两侧字段互斥:显式传 `--probe-type` 时非生效侧字段(SCRIPT 下的 `--url`/`--method`/`--headers`/`--body`/`--json-path`,HTTP 下的 `--script-path`/`--timeout-seconds`)在本地丢弃并打 stderr 提示,不静默;未传 `--probe-type` 时全部下发,由后端按库内类型忽略。
  - 本地校验与后端同口径:`--script-path` 非空、非绝对路径、不含 `..` 段;`--interval` 对 SCRIPT 下限 5 分钟;`--compare-mode STATUS_CODE` 对 SCRIPT 拒绝;`--timeout-seconds` 5~300 整数。脚本文件的存在性 / ≤256KB / 属主边界仍由服务端校验。
  - `update` 可用 `--probe-type` + 对侧字段把已有哨兵切成脚本探针(会重置判定基线);HELP 与 `reference/signal-watcher.md` 同步补充两种 create 形态与脚本侧报错清单。

### 前端(桌面 / Web)
- **哨兵探针类型说明换到选项下方**:新建/编辑哨兵时,探针类型旁的说明不再跟在单选后面挤成一行,改为显示在选项下方。
- **哨兵「选择脚本」按钮与路径输入框齐高**:脚本路径行右侧按钮此前用对话框 24px 按钮,比 32px 输入框矮一截,现与输入框同高、同圆角。

## 0.0.497 (2026-11-10)

### Embed SDK
- **产物改为提交 git,部署不再重复构建**:`/embed/mao-chat.js` 此前由每次 desktop 构建的 `prebuild` 现构 `sdk/embed` 生成,服务器上常留下「构建产物未提交」的脏改动,且线上产物与仓库可能不一致。现 SDK 产物(`desktop/public/embed/mao-chat.js` 与版本锁定副本 `mao-chat.v{version}.js`)**入 git**,改 SDK 源码后在 `sdk/embed` 跑一次 `npm run build` 并提交产物即可;desktop 构建与部署只消费已提交产物。CI `embed-sdk` job 新增产物一致性校验(重新构建并比对 sha256),**改了 SDK 源码却漏提交产物会在 CI 直接挂掉**。本次同时补上 0.3.3 产物。

## 0.0.496 (2026-11-10)

### Embed SDK
- **执行过程渲染重设计,与客户端对话页同构**:浮窗里 Agent 的执行过程(思考/工具调用/中间说明)此前是「思考过程 details + N 个工具调用」两段粗折叠,与桌面端观感差距大。现按桌面端对话页的形态重写:
  - **轮次折叠**:一轮执行完成后,过程自动折叠为「已执行 N 个步骤,任务耗时 X」胶囊(耗时从用户消息发出到本轮最后一条消息结束;历史回放取 REST 消息时间戳),最终回复在胶囊下方常显,点击展开全过程、再点收起,与桌面端历史轮次一致;无工具的纯问答轮不产生折叠条。
  - **思考行**:「思考完成」行 + 图标 + 展开箭头,执行中显示「思考中…」转圈并在头部空白区滚动预览最新思考片段,展开看原文(对齐桌面端 ThinkingBlock)。
  - **工具行/组**:工具名全部中文化(与桌面端同一映射口径);单调用渲染一张带图标卡片行,多调用折叠为「2次执行命令」式中文汇总组头,组头带组内执行耗时与转圈。行头显示后端一句话摘要(缺省回退「中文名 · 入参摘要」),右侧为秒表(执行中满 1s 起表)、转圈、成功√/失败×或「结果未记录」。
  - **工具卡富视图**(对齐桌面端 ToolCallCard):shell 终端会话块($ 命令 + 输出 + exit 徽标)、edit_file 行级 diff(± 着色、上下文折叠、替换摘要)、read_file/write_file 行号内容、glob/grep 命中列表、web_search 结果卡片、open_web_page 正文;底栏给结果摘要与「查看原始输入输出」开关;无富视图的工具回退输入/输出 JSON 块,超长输出折叠可展开完整;页面截图缩略图点击看大图(沿用灯箱)。
  - 数据层配套:WS `tool_call_result` 的 result(富视图数据源)与 summary(行头摘要)分字段保存(此前 summary 覆盖 result 导致富视图解析不到 JSON);工具调用记录起表时刻(订阅重放按 `elapsed_ms` 反推,刷新/重连后秒表不归零);历史消息携带 `createdAt`/`updatedAt` 供折叠条算耗时。SDK 产物版本 0.3.2 → 0.3.3。

## 0.0.495 (2026-11-10)

### 后端
- **修复一次性定时任务排队结算丢失用户改档**:会话忙入队后、消费完成前,用户把 cron 换成另一个一次性日期,排队结算曾无条件把任务标完结并清空档期——新档期永不触发、任务被误标 finished。现与直跑收尾同口径:改档(队列行 fireTime 不再是当前 cron 的触发点)且任务启用时按最新 cron 重开档期,未改档行为不变。
- **修复流式 tool_call 合并拆分**:部分网关返回的带 id 参数分片不带 index,与无 id 首片的占位调用对不上号时,同一工具调用被拆成两条重复派发(前半参数截断、后半无名),用户命令实际未被完整执行。现 byId/byIndex 都落空且恰有一条占位调用时归并到它。
- **修复出站投递抛出型失败永不终态**:投递过程抛异常(如 webhook 密钥解密持续失败)此前只记日志,行停在 SENDING 被恢复器反复复位重试、无重试上限、不落终态与审计。现与结果型失败同口径:按 1/5/25 分钟退避共 3 次,耗尽后终态 FAILED 并落审计。
- **`message_queue.content` 列宽与消息门禁对齐**(V190 迁移随启动自动执行):TEXT(65535 字节)装不下开放调用允许的 32000 字中文消息(96000 字节),忙会话入队被 MySQL 严格模式拒绝、排队消息丢失。现拓宽为 MEDIUMTEXT,与 message.content 一致;32000 字门禁行为不变。

### 前端(桌面 / Web)
- **修复图片注入行剥离对纯文本消息的尾随空行改写**:气泡/编辑回填/复制口径对不含注入行的内容现保持恒等,非桌面来源消息编辑重发不再被静默裁掉尾随空行;回声去重比对口径不变(仍双侧收敛判等)。
- **修复压缩包点文件与无点文件无法文本预览**:`.gitignore`/`.editorconfig`/`.env`/`Dockerfile`/`Makefile` 此前一律走下载分支,现正确进内存文本预览。
- **修复工具卡片预览截断产生乱码字符**:emoji 等宽字符恰落在截断边界时被从代理对中间切开渲染成 U+FFFD,现按 Unicode 码点完整截断(工具参数预览与后台子代理完成通知摘要)。

## 0.0.494 (2026-11-10)

### 后端
- **信号哨兵支持 Shell 脚本探针**:哨兵探针类型二选一(HTTP / SCRIPT)。脚本哨兵以 `bash <脚本>` 在后端宿主执行本人项目工作区内的脚本(不要求可执行位、stdin 关闭、独立进程组),stdout 作为判定值进入既有判定管线(HASH / JSON_FIELD),exit code 0=成功、非 0 计一次失败(连续 5 次自动停用兜底不变),超时 5~300 秒(默认 60)、stdout 上限 1MB;脚本路径保存与执行双侧做属主与软链逃逸校验;执行环境为白名单变量(PATH/HOME/TZ/MAO_WATCHER_ID/NAME),不透传后端进程全量 env。调度、退避、自动停用、触发(openRun)、轮询记录、清理链路全部复用;`signal_watcher` 加 `probe_type`/`script_path`/`script_timeout_seconds`、`signal_watcher_check` 加 `exit_code`(V189 迁移随启动自动执行,存量哨兵默认 HTTP 零行为变化)。新增 Agent 内置工具 `list_signal_watchers` / `create_signal_watcher`:会话中可让 Agent 写好脚本(项目工作区)后直接创建哨兵,实现对话闭环。

### 前端(桌面 / Web)
- **信号哨兵表单支持脚本探针**:探针类型切换分流——脚本态展示脚本路径(工作区文件选择器 + 手输)、执行超时,隐藏 URL/方法/请求头/请求体;判定模式对脚本哨兵隐藏状态码选项;轮询间隔下限按类型切换(脚本 5 分钟);列表卡显示探针类型徽标、脚本路径与超时;「立即测试」结果展示脚本退出码与输出样本;轮询记录状态码列对脚本探针显示退出码。

### 管理后台
- **信号哨兵只读清单与详情展示探针类型**:列表加「类型」列,探测地址列对脚本哨兵显示脚本路径,配置详情展示探针类型与执行超时。

## 0.0.493 (2026-11-10)

### 前端(桌面 / Web)
- **外观个性化:强调色五款与界面字号三档**:个人信息页顶部外观卡在主题色之外新增「强调色」(经典蓝 / 紫 / 绿 / 橙 / 石墨五款预设,应用于按钮、链接与选中态,亮暗主题各自适配)与「界面字号」(小 / 标准 / 大三档,立即生效)两行设置;偏好存本机 localStorage(与主题色同口径,不随登出清除),刷新后保留。
- **默认观感零变化**:不选择新偏好时全部取值与旧版逐字节相同(经典蓝即默认强调色、标准即默认字号);所有个性化均为显式 opt-in,登录页与空态的营销字号不随字号档变化。

## 0.0.492 (2026-11-09)

### 后端
- **`/opt/mao-data/share` 按 Agent 隔离(形态 C)**:容器内由「整根 rw 挂载」改为两条子路径 rw——`agent-experience/<agentId>/`(本 Agent 团队经验库)与 `agents/<agentId>/`(本 Agent 私有区:投放品/脚本/产物);其他 Agent 目录在容器文件系统层面不复存在(含经验库跨 Agent 零可见,此前为"软链 + 提示词收口"的接受项)。配套调整:
  - 挂载计划 `assertSafe` 对两条子路径做 purpose 绑定的精确校验(整根 / 其他 Agent / 形似路径一律拒绝);
  - 容器内沙箱允许根同步收窄为上述两条子路径(与挂载面严格一致,share 根不再放行);
  - `runtime-agent` 硬依赖 `MAO_CONTAINER_AGENT_ID`(缺失/非法即退出,与 `MAO_CONTAINER_USER_ID` 同口径);
  - 管理员公共投放区取消:投放改为落到 `agents/<目标AgentId>/`(目录级挂载即时生效,多 Agent 需同一文件时逐个复制);
  - 存量容器随挂载指纹漂移一次性自动重建;`agent-experience/` 路径本身不变(工作区 `团队经验/` 软链不受影响)。
- **存量数据迁移**:由 `scripts/migrate-share-per-agent.sh` 完成(分发 settings/scripts 至各 Agent 目录、DB 引用更新、旧残留清理;含 DB 前像与分阶段校验)。提示词同步为新文案("本 Agent 的文件放 `agents/<agentId>/`;无跨 Agent 公共共享目录;旧路径已迁移,勿自行重建")。

## 0.0.491 (2026-11-09)

### Embed SDK
- **浮窗入口不再出现「先品牌图标、后换头像」的闪动**:0.0.490 预取后,入口按钮在拿到 Agent 详情前仍会先渲染品牌图标、随后才换成配置头像。现在入口图标分两步处理:①每次成功获取 Agent 详情后,把头像与名称缓存到站点 localStorage(按 Mao 服务地址 + Agent 维度),下次页面加载首屏直接显示上次结果、不等网络;②首次加载(无缓存)期间入口按钮暂不渲染(不出现任何默认图标),解析完成后一次性显示最终图标(配置头像或品牌图标);超时 1.2 秒按无头像兜底、拿到结果后仍会更新。预取失败静默,首次打开浮窗仍会重试。SDK 产物版本 0.3.1 → 0.3.2。

## 0.0.490 (2026-11-09)

### Embed SDK
- **浮窗入口在页面加载时就显示 Agent 头像**:此前入口按钮在首次打开前只显示品牌图标,用户点开浮窗后才加载 Agent 详情、换成管理后台配置的头像。现在 SDK 初始化即预取 Agent 详情(与首次打开共用同一请求,打开后不重复拉取):入口按钮与浮窗标题在页面加载时就使用配置的头像与 Agent 名称;无头像或未配置时仍回退品牌图标与「Mao 助手」。预取失败静默回退(不写浮窗错误横幅),首次打开仍按原流程重试;`launcher.visible: false` 不预取。注意:公司 SSO 模式下预取会在页面加载时触发一次换票(不再等用户点开浮窗),不打开浮窗的页面同样会换票并按既有 TTL 续期;WS 连接仍为懒连接。SDK 产物版本 0.3.0 → 0.3.1。

## 0.0.489 (2026-11-09)

### 后端
- **公司 SSO 宿主 Origin 支持 HTTP、localhost 与 IP**:宿主 Origin 白名单的精确项由「仅 HTTPS」放宽为 HTTP/HTTPS 均可,支持 localhost、IPv4/IPv6 与非默认端口(如 `http://localhost:2222`);换票 CORS 预检与 POST 校验共用同一规则同步放行。子域模式仍仅限 `https://*.example.com`,校验域名白名单、checkUrl 与换票入口 HTTPS(`requireHttps`)要求不变。

### 管理后台
- **公司 SSO 宿主 Origin 表单支持 HTTP、localhost 与 IP**:校验与后端保持一致,输入提示补充 `http://localhost:2222`、`http://127.0.0.1:8080` 等示例与默认端口说明;子域模式仍仅 HTTPS,HTTP 来源仅建议本地开发或内网联调使用。

## 0.0.488 (2026-11-09)

### 后端
- **非白名单图片格式(TIFF 等)强制转码后再进 prompt**:用户上传的图片若实际是 TIFF/BMP 等非 webp/png/jpeg/gif 格式(例如文件名伪装成 `.jpg` 的 TIFF 截图),此前图片准备链路只按尺寸/体积决定是否重编码——尺寸合规的 TIFF 会被原样转成 `data:image/tiff;base64,…` 发给模型,上游(如 kuaishou 渠道)不认该格式直接 400 `unsupported image`;且失败会话历史里这张图每轮都会重发,用户重发新图也无法自救。现在 `PromptImageResizer` 统一走图片格式白名单(png/jpeg/gif/webp),白名单外的格式即使尺寸/体积合规也强制转码为 PNG(压不小则 JPEG),三条协议(OpenAI 兼容 / Anthropic / Responses)与远程图片缓存路径同时生效。

## 0.0.487 (2026-11-09)

### 终端 CLI(mao-agent)
- **常驻本地执行端 `mao-agent local --serve`**:不加入任何会话,凭本机设备身份替全部 LOCAL 会话执行工具。桌面客户端退出后,安卓 / 浏览器上已绑定该设备的本地任务仍能跑。`--workspace` 是信任根(默认当前目录,须已信任,不会静默信任)。需审批时默认等桌面 / 安卓 / Web 任一端投票,超时(默认 300 秒,`--approval-timeout-ms`)后拒绝;附着形态 `mao-agent --local --on-approval=remote` 使用同一个超时。`--yolo` 可全自动。与桌面客户端同时在线时,谁最后连接谁执行:桌面端后启动会夺回,关闭桌面端后执行权回到仍在线的 daemon。信任根外的技能 / MCP 同步不回包,避免失败回包把同机桌面端的成功判负;仅 daemon 在线且工作区在根外时,技能同步会在约 60 秒后失败,MCP 同步超时后该轮工具不可用。无人值守建议加 `--yolo` 或把会话权限设为 `FULL`。守护进程等于把本机 shell 开放给自己账号的会话,只在可信机器上运行。

### 后端
- **执行端类型随 `device_status` 下发**:在线帧带当前执行连接的 `clientType`(`electron` / `cli`)。设备仍在线、只是执行连接从桌面换成 mao-agent(或反过来)时会再推一帧,同类型互顶不推。会话详情和列表增加可选 `executorClientType`,刷新后不用等下一次上下线。无新表、无迁移。

### 前端(桌面 / Web / 安卓)
- **本地任务横幅区分谁在执行**:在别的设备上看,在线且执行连接是桌面客户端时显示「桌面客户端在执行」,是 mao-agent 时显示「mao-agent 在执行」。字段还没有时仍是「该任务在 {设备名} 上执行」。当前这台机器就是绑定设备时横幅不出现。离线文案和发送拦截不变。
- **后端重启 / 蓝绿部署后不再出现重复且一直转圈的工具调用**:会话恢复执行时,末尾那条被中断的工具调用此前会在界面上一式两份(同一条命令两个条目),旧的那份持续转圈直到任务结束或刷新页面才消失。现在后端换新执行重跑时就地终结旧气泡里未终结的调用(已执行完的轮次与已产出的文字原样保留),恢复后的调用是唯一在跑的条目,结果正常落到它身上。

### 终端 CLI(mao-cli)
- 手册补充「常驻本地执行端」:适用场景、命令、信任根、审批口径、与桌面端共存和无人值守注意事项。

## 0.0.486 (2026-11-09)

### 管理后台
- **移除用量分析「运行轨迹」Tab**:最慢的轮、最贵的轮、工具失败率三个榜不再展示。旧链接 `?tab=run-trace` 会打开总览。桌面端任务里按会话查看的运行轨迹仍在。

### 后端
- **移除 `GET /v1/admin/analytics/run-trace`**:该聚合只给上述 Tab 使用,一并删除。
- **飞书多文件消息(一次发送多个文件)落地支持**:飞书把「一次发多个文件」编码成 `post` 富文本消息、文件挂在 content 顶层 `files` 数组(`{"content":[[]],"files":[{"file_key":"file_v3_…","file_name":"x.pptx","is_folder":false}]}`),此前入站链路完全不识别该形态——正文提取为空、消息退化为 `[post msg=om_xxx]` 占位(Agent 看不到文件名和内容),`feishu_download_file` 兜底也报「未找到包含文件/图片的群聊消息」。现在与单文件消息对齐:
  - 入站解析识别 `files` 数组,生成 `[文件:名字 msg=om_xxx file_key=file_v3_xxx]` 逐文件占位(带 file_key 供精确下载);文件夹项(`is_folder=true`)不可下载、保留 `[文件夹:名字]` 占位(不再退化为 `[post msg=…]`);
  - 只有文件、没有正文的 post 与单文件消息同语义:入站即下载落盘、只写入会话不单独触发任务(带正文的 post 正常触发并注入文件路径);
  - 私聊直传、群聊预下载(日志行回填 `@{路径}@`)均支持多文件,同一消息内重名文件自动避让;
  - `feishu_download_file` 新增可选 `file_key` 参数:多文件消息按占位符逐一下载,不传则下载该消息全部文件(返回 `paths` 数组,单个失败不影响其余文件);含图+文件的混合 post 中该工具只负责文件资源,图片仍由入站直传 / 群预下载覆盖;
  - 带内嵌文件的多文件 post 与图片/文件同类,不并入纯文本防抖合并窗口,到达即单独触发。

### 终端 CLI(mao-cli)
- **移除 `mao analytics run-trace`**:与管理后台该 Tab 同一份数据,Tab 下线后命令不再提供。

### Embed SDK
- **浮窗入口按钮放大为原来的 75%**:0.0.424 曾缩到 56px 的 50%(28px),实际偏小;现调整为 75%——桌面 42px、窄屏(≤480px)39px。品牌图标 / Agent 头像满铺按钮随之同比缩放,内侧描边环、未读红点、运行环与拖动避让同步适配,点击打开对话的行为不变。
- **页面工具改为读 / 写分离,`page.enabled` 只控制写/操作类**:读类工具(`page_inspect` / `page_observe` / `page_screenshot` / `page_wait`)不受开关控制、始终提供——宿主零配置,Agent 默认就能查看当前页面(元素快照与遮罩截图);写/操作类(`page_scroll` / `page_focus` / `page_fill` / `page_select` / `page_check` / `page_uncheck` / `page_click` / `page_keyboard` / `page_actions`)缺省关闭,显式 `page: { enabled: true }` 才开启,不再有「一行 `init` 就默认拿到操作宿主页面能力」的隐式授权。后端按连接级声明过滤写类工具,SDK 侧写请求双保险直接返回 `page_operation_disabled`;写能力关闭时截图强制遮罩敏感区域(不弹确认卡)、浮窗不显示授权下拉 / 确认卡片 / 动作日志、注入模型的提示词换用只读变体(明确告知只能查看、不能代操作)。读能力需要页面快照,SDK 因此始终创建页面引擎(patch 宿主 `history.pushState` 并挂导航监听,非破坏性)——这是「读始终提供」的固有成本,暂无「连读也全关」的开关。管理后台自家宿主已显式 `page: { enabled: true }`,浮窗助手读写能力不变;**外部存量宿主**若依赖原缺省开启,补 `page: { enabled: true }` 即可恢复。

## 0.0.485 (2026-11-09)

### 管理后台
- **会话管理「活跃时间」新增「最近 1 天」「最近 3 天」**:筛选下拉原有「最近 7 天」起步,查当天或近几天的活跃会话只能手选自定义区间;新增两档后点选即查,口径与既有预设一致(含今天,按北京时间日历日切分,`--range 1d/3d` 同效)。默认档仍是最近 7 天,地址栏 `range` 参数同步支持两档。

## 0.0.484 (2026-11-09)

### 后端
- **非流式 LLM 调用补记 token 用量与成本**:连通性测试、飞书群聊溢出摘要等走非流式客户端的调用,此前落库 `usage: null`,入/出 Token 与成本恒为 0。现在三个协议(OpenAI 兼容 / Anthropic Messages / Responses)的非流式响应都会归一出 `usage`(Anthropic 含缓存读/写分项),`llm_call` 记录真实 token 与按真实 token 计的成本;飞书群聊溢出摘要(`feishu_summarize`)同时补上调用归属人——本次触发的发言者(缺省回退会话归属人),管理后台「调用流水」的「用户」列不再为空。

## 0.0.483 (2026-11-09)

### 后端
- **本地任务执行端离线提示中文化 + 在线态实时推送**:
  - 执行端未连接的拒绝文案统一为中文「本地执行端未连接,请确保桌面客户端正在运行」:覆盖发送 / 编辑重发 / 重试 / 边路任务创建 / 定时与开放接口执行(WS 错误帧),以及工具执行失败回执(本地端中途断线时的工具卡片)——此前这些路径是英文,弹窗与工具卡片会直接露出英文句子。
  - 新增 `device_status` 用户级广播:桌面客户端退出(设备最后一条执行连接断开)时向该用户的其它连接推送离线,桌面/mao-agent 连入时推送上线;同设备多窗口覆盖、连接交接不重复广播。`targetDeviceOnline` 原本只是会话详情的请求时快照,其他端(安卓/浏览器)此前只能靠刷新纠正。

### 前端(桌面 / Web / 安卓)
- **本地任务横幅实时跟随执行端在线态**:桌面客户端退出后,「该任务在 xxx 上执行」细条数秒内由绿转橙(「执行端 xxx 不在线,发送前请在该设备上打开 Mao」),桌面重新打开后自动转回在线态,不再只有刷新页面才纠正;边路任务面板的执行端提示同样实时。横幅转橙后浏览器/安卓端继续发送会被既有中文拦截挡住。
- **发送失败不再卡「正在保存…」60 秒**:本地端未连接等「消息尚未落库即被服务端拒绝」的场景,此前只回错误帧、不发保存确认,输入框一直转「正在保存…」直到 60 秒超时,且实际未发出的消息会挂在列表里像已发出。现在拒绝即解锁输入框(原文保留可重试)、回滚该条乐观消息,错误横幅中文展示;任务被占用、预算超限等同类落库前拒绝同样受益。

## 0.0.482 (2026-11-09)

### 后端
- **飞书进度卡片:首次思考占位带耗时刷新**:「正在思考…」满 1 秒后附耗时(`正在思考…(1 分 30 秒)`),思考期间按「在途工具耗时」同一间隔(默认 3 秒,`FEISHU_CARD_TOOL_TICK_MS` 可调)重推卡片——首次 LLM 调用长时间无产出时,用户能看到时间在走,不再只有一行静止文案。口径与工具耗时一致:亚秒不显示;首个非空白增量(首 token)/工具开始/轮次收尾即停思考计时。后续轮仍不重刷占位(0.0.481 语义不变)。

## 0.0.481 (2026-11-09)

### 后端
- **飞书进度卡片:「正在思考…」仅在首次思考显示,后续轮次不再重刷**:0.0.476 起每轮开始都会把卡片重刷成「正在思考…」,已执行到第 36 轮的任务正文与工具被抹掉、看起来像进度重置。现改为仅首个轮次推送该占位,后续轮保持已展示的进展(轮次与工具更新仍由工具开始 / 轮次结束推进);失败卡重试与崩溃恢复续跑同理。钉钉共用同一监听器,行为同步。

## 0.0.480 (2026-11-09)

### 前端(桌面 / Web)
- **信号哨兵请求头凭据提示不再被输入框截断**:`{{MAO_TOKEN}}` / `{{ECP_TOKEN}}` 占位符说明从值输入框的 placeholder 挪到请求头编辑区下方单独一行(可完整折行显示),值输入框 placeholder 改为简短的「Header 值」(编辑态仍显示「留空不修改」)。

## 0.0.479 (2026-11-08)

### 前端(桌面 / Web)
- **聊天消息长代码块默认折叠**:超过 30 行的代码块默认只显示首屏约 8 行并带渐隐遮罩,块头新增行数徽标(如「500 行」)与「展开全部 / 收起」按钮,长代码不再把对话撑出十几屏;展开状态在会话内记住(切换主题、流式结束、翻页重渲后不折回),刷新页面后恢复默认折叠。折叠是视觉裁剪,代码完整渲染,复制、划选不受影响。
- **代码块换行开关**:折叠块块头新增「换行」按钮,切换长行折行 / 横向滚动;偏好全局记忆(localStorage),刷新后沿用,会话内全部代码块(含短块长行)一致生效。
- 分享页随消息正文同步生效;文件预览、运行轨迹、技能页、OpenApi 文档等其它宿主的代码块展示不变。Mermaid 图不折叠;超过 64KB 的 mermaid 源码按普通代码块折叠(既有行为)。

## 0.0.478 (2026-11-08)

### 前端(桌面 / Web)
- **中心页签效率补完**(技术方案 `docs/plan/2026-11-08-center-tab-efficiency-technical-design.md`):
  - **中键关闭页签**:鼠标中键点击页签即关闭(主会话页签除外,`×` 按钮上的中键同样生效),并抑制浏览器中键自动滚动指示器。
  - **页签拖拽排序**:非主会话页签可拖拽重排,悬停页签显示左右插入指示线(按中点判定),拖到 Tab 栏空白处即「移到最后」;主会话页签恒在首位、不可拖拽,其上无插入线且落点为禁止光标。顺序为会话生命周期内行为,刷新后按既有重建口径,不持久化。与 0.0.476 工作区文件拖入 Tab 栏的落点互不干扰(文件拖拽行为不变)。
  - **右键菜单新增「关闭右侧文件」**:关闭被右键页签右侧的全部文件类页签;右侧的执行中边路任务 / 子代理页签不受影响(与「关闭其他文件」同口径),副作用(关闭标记、激活回退)完整保留。
  - **键盘循环切换页签**:`Ctrl/Cmd+Alt+←/→` 在中心页签(含主会话)间循环切换、端点回绕;输入框 / 编辑器聚焦时不触发,长按不连跳。Web 端部分平台(Linux GNOME 工作区切换、macOS Chrome 切换标签页等)该组合键可能被系统或浏览器占用拦截,Electron 桌面端不受影响。

## 0.0.477 (2026-11-08)

### 前端(桌面 / Web)
- **顶栏隐藏命令面板功能图标**:顶栏 ✨ 图标不再展示(功能打磨中,暂不面向用户开放),命令面板本体与 `Ctrl/Cmd+Shift+P` 快捷键、`?` 速查表均不受影响。

### 后端
- **飞书进度卡片:在途工具显示执行中耗时**(技术方案 `docs/plan/2026-11-08-feishu-card-tool-elapsed-technical-design.md`):「本轮工具」列表对执行中的工具行追加耗时(`shell:`npm test`(执行中 · 1 分 30 秒)`),有工具在跑期间每 3 秒重推一次卡片,长耗时工具执行期间不再完全静止。口径与桌面端 0.0.463–465 对齐:亚秒(< 1s)不显示、已完成工具摘要行不带耗时、并行工具各自计时、终态卡不带执行中耗时;工具结束 / 轮次结束 / 终态即停刷新(模型思考阶段卡片照旧静止)。新增环境变量 `FEISHU_CARD_TOOL_TICK_MS`(默认 3000,0 关闭周期刷新),可降频规避 im.v1.message.patch 限额,无需发版。钉钉共用同一监听器,进度卡同样带耗时刷新(钉钉群用户可见的行为同源变化)。

## 0.0.476 (2026-11-08)

### 后端
- **飞书进度卡片:首次思考不再停留在「任务已接收,正在准备执行。」**:此前卡片只在首个工具调用开始或轮次结束才 PATCH,AgentLoop 发起每轮 LLM 请求(`onRoundStart`)时只记轮次、不刷新卡片——首次调用常要几十秒才产出内容,这段时间卡片一直停在首卡文案,用户容易误以为任务卡住。现改为每轮开始即刷新卡片:尚无正文时显示「正在思考…」,状态行同步显示「第 N 轮」;失败卡重试与崩溃恢复续跑同样生效(轮次按已完成的助手轮接续)。钉钉共用同一监听器,轮次刷新同样更及时(其进度卡模板本就不展示执行中正文)。

### 前端(桌面 / Web)
- **新增全局命令面板**(`Ctrl/Cmd+Shift+P`,或点顶栏 ✨ 图标唤起):28 条内置命令输入即滤、↑↓ 循环选择、Enter 执行、Esc 关闭且焦点还原——覆盖搜索会话(与 Ctrl/Cmd+K 同源)、新建会话、终端 / 左右面板 / 收件箱 / 我的指令开关、会话列表刷新、退出登录、15 个设置页直达与快捷键速查表;浏览器(非 Electron)下「服务器」「桌面」两条置灰,终端命令跟随既有可用性判定。共享 UI 生效(Web / Electron / 安卓壳同入口)。
- **新增快捷键速查表**:主界面按 `?` 弹出全部 7 条快捷键(输入框 / 编辑器聚焦时不触发);命令面板内「快捷键速查表」命令亦可打开。
- **主题切换主界面直达**:命令面板内执行「主题:亮色 / 暗色 / 跟随系统」即时生效(当前模式带 ✓ 标记),与个人资料页三态选择器同一单例、天然同步,无需再进资料页切换。
- **工作区文件跨面板拖拽**:文件管理器 / 文件树的文件可直接拖进聊天输入框,成为可点击的 `@` 文件引用(多选逐条插入、顺序与选择一致,目录忽略;插入随草稿持久化,切会话不丢);拖到中心 Tab 栏直接打开预览页签(重复拖同文件只激活既有页签,不重复开;从工作区总览打开的其他工作区文件管理页签拖出时,按其所属工作区打开预览,不会错开当前会话工作区的同名文件)。拖拽光标在聊天输入框 / Tab 栏显示 copy、面板内部移动仍为 move;OS 本地文件拖入上传、面板内部移动 / 归组行为均不变。

## 0.0.475 (2026-11-08)

### 后端
- **修复 2026-11-07 逻辑审查全部问题(BUG 6 条 + 候选 5 条)**(报告 `docs/code-review/2026-11-07-logic-bug-review-01.md`,存在性证明用例已全部转正为回归测试):
  - **grep_search rg 分支把 `-` 开头的 pattern 当命令行 flag**:合法 flag(如 `-n`)被吃成选项后静默返回「0 命中成功」、非法 flag(如 `--foo`)把合法搜索误报成工具故障——pattern 改以 `-e` 显式绑定(agent-cli 本地同型缺陷同步修复)。
  - **容器 read_bytes 把 0 字节文件判成读取失败**:判空从 `!base64` 改为 `== null`——空串是空文件的合法编码,docker 模式下读取/发送/编辑空文件的会话恢复正常(host 模式本就正常)。
  - **哨兵复制(copyFrom)在请求头键未下发时静默丢弃源哨兵全部请求头**:改为整体继承源行解密值(与 body 的 undefined=继承语义对齐;显式 null 仍为清空)——官方 CLI 文档示例(`--copy-from` 不带 `--headers`)复制的哨兵不再无凭据、401 错误页不再被当正常基线。
  - **重试入口不消费 pendingCancels**:FAILED 会话点「重试」后、提交前窗口点「停止」被静默吞掉、任务照跑——注册 cancelFlag 前按入口时间消费取消标记,命中即按取消收敛(不提交执行、记录不卡 RUNNING)。
  - **容器 agent 重连后 connectionDead 永不清除、健康容器被强制重建**:一次通道抖动(已自动重连)后下次工具调用会把健康容器 stop+rm+run,shell/PTY/后台任务全丢——hello 重连成功即清标记;旧连接替换先从连接表摘除,不再把正常替换误报为连接失效。
  - **工具结果摘要失败/超时语义失真**:wait_subagents 等满超时不再谎报「后台子代理已完成 (0 个结果)」;ask_user_questions 仅「等待超时/会话取消」标「超时或取消」、参数非法等即时失败改标「(错误)」(超时结果补结构化 `timed_out` 标记)。
  - **候选 5 条**:容器内 MCP stdio connect/listTools 加 60s 超时与失败清理(不再每轮执行泄漏子进程);一次性任务换 cron 不再绕过「有未结算工作禁止重开档期」不变量(重试运行中判据补 hasRunningRun、旧档期收尾按新 cron 重排,新计划不再被抹掉);deleteSession 运行中拒删判据移入事务内复查(消除 check-then-act 窗口);POST 探针有 body 时按 utf8 字节数代填 Content-Length(要求定长的目标不再 411、不再建假基线);失败退避统一 Node 时钟(不再混用 MySQL NOW(),DB 时区差异不再让退避整体漂移)。

### 前端(桌面 / Web)
- **边路会话发送收尾不再无条件清空输入框**(2026-11-07 审查候选):上传/连接/等待 user_message_saved(上限 60s)窗口内继续打字,收尾时不再被静默清空且不可恢复——与主会话同口径的「输入未改才清空」守卫(`clearInputIfUnedited`),附 8 例回归测试。

### CLI(mao-agent)
- **修复 2026-11-07 逻辑审查全部问题(BUG 1 条 + 候选 3 条)**:
  - `/resume` 切入运行中会话(含启动续接路径)后,等待期间排队的输入在续接结束时自动消费——不再静默滞留、也不再在下一条消息提交时乱序先行发出。
  - 本地 grep 同型修复:pattern 以 `-e` 绑定,`-` 开头的搜索词不再静默 0 命中/误报失败。
  - 打印 / JSON 模式失败时输出 error 帧原文:text 模式 stderr 打「执行失败:…」、json 终态对象带 `error` 字段,脚本消费方不再只见 `status=FAILED` 无原因。
  - 审批「已提交」态 30s 兜底收尾:回声因服务端重启/断线永不到达时模态可关闭,不再吞掉全部按键(含 Ctrl+C)。
  - MCP HTTP `tools/call` 加超时并给出友好超时文案(病态 trickle 响应不再让调用永不返回)。

## 0.0.474 (2026-11-08)

### 后端
- **修复 Docker 模式会话内 CLI 全部断链(0.0.473 回归)**:0.0.473 把技能视图按 (user, agent) 物化到 `skill-views/{u}/{a}/{skill}`、容器挂载收窄为 `{u}/{a}` 后,`deps/{u}/npm` 里更早安装的 npm 全局 CLI 软链仍相对指向历史路径 `skill-views/{u}/{skill}`,而容器内该路径不存在——`dama-cli` / `access-cli` / `xxl-job` / `bigdata-cli` / `lark-cli` / `sls` / `apollo-cli` 等全部 `command -v` 失败,脚本报 `FileNotFoundError`(宿主机旧副本残留掩盖了该问题,仅在容器内复现)。现由 `runtime-agent` 在容器启动时于用户视图根为当前 Agent 的每个技能补 `{skillName} -> {a}/{skillName}` 相对软链(仅补缺失、不覆盖既有条目、幂等、失败仅告警),恢复历史软链解析;存量容器在下一次按代码漂移自动重建后生效。

## 0.0.473 (2026-11-07)

### 后端
- **Docker 容器隔离维度从「人」升级为「人 + Agent」**(方案 docs/plan/2026-11-07-user-agent-container-isolation-technical-design.md,决策与调研见 docs/research/2026-11-07-container-isolation-user-agent-dimension.md):同一用户的不同 Agent 各自独占一个容器(`mao-user-{userId}-agent-{agentId}`),其他 Agent 的技能视图在容器内从此不可见——此前 `skillViews/{userId}` 整根挂载可读该用户全部 Agent 关联过的技能视图。skill 视图按 (user, agent) 物化为恒定路径(`{skillViewsDir}/{userId}/{agentId}/`),skillNames 增删/技能源更新只改目录内容、零容器重建;share 目录维持整根 rw 共享(含团队经验库与管理员投放的共享文件,不做按 Agent 收窄,经验库跨 Agent 收口仍靠工作区软链 + 提示词);Unix socket 通道、PTY 终端、预热、admin 容器管理全部按复合键隔离。同用户多 Agent 并存互不挤占单容器资源上限;重建的爆炸半径从「该用户全部会话」缩小到「单个 Agent 的会话」。
- **会话内切换 Agent 的容器侧行为**(已拍板接受中断):切换不打断当前轮(agentId 按轮快照),下一轮路由到新容器(PATCH 成功即预热、无冷启动等待);旧持久 shell 的 session_id 失效并报「请重新创建 shell 会话」,已 attach 终端随旧容器存活到关闭或回收,async 后台任务在旧容器成孤儿。群聊(飞书/钉钉)路由与技能同步本就按发言者运行身份,与复合键天然一致;bot 热切换 Agent 与会话内切换同口径。
- **用户级共享数据维持不变**(已拍板):deps/HOME/tmp/workspace/用户个人技能与 share 目录(整根)继续共享挂载;host 模式、LOCAL 模式、桌面端零变化;无 DB 迁移。
- **升级自动收敛**:上线后存量容器首次使用时按新挂载指纹自动重建一次(既有机制);`container-gc.sh` 新增旧命名孤儿容器(`mao-user-{纯数字}`,升级后必为孤儿)与三级 socket 目录的清理规则。

### admin
- **容器管理面板按 (用户, Agent) 展示**:列表新增「Agent ID」列,单行「重建」按钮精确回收该 (用户, Agent) 容器;`POST /v1/users/:id/container/recycle` 支持 body `agentId`(缺省保持整用户回收语义)。

## 0.0.472 (2026-11-07)

### 前端(桌面 / Web)
- **修复边路任务执行中刷新页面后,正在执行的工具调用不回显**:此前刷新后只显示已完成的历史工具,正在执行的 shell 调用要等结果出来才凭空冒出。根因是主会话修复「刷新后在途工具回显」时只做了两段配套——后端订阅重放在途调用(边路会话同样生效)+ 前端拉 REST 历史时保留重放写入的流式气泡——而边路 Tab 拉历史时没传保留标记,重放先写入的「执行中」气泡被随后到达的 REST 历史整表覆盖吞掉。现边路面板与主会话 / 子代理面板同口径:历史覆盖时保留本地正在流式的内容(判定在响应到达后做,重放帧若在请求期间写入也能被认出),气泡继续承接后续结果事件并随之收尾;服务端只在会话活跃时才重放在途调用,不会保留出无人收尾的幻影转圈。

## 0.0.471 (2026-11-07)

### 前端(桌面 / Web)
- **键盘、焦点与命中区可达性**(方案 docs/plan/2026-11-07-keyboard-focus-hit-area-technical-design.md):①发完消息焦点回归输入框——点「发送」按钮后光标掉到 body、连发需重新点输入框,现在 `handleSend` 在 emit 后统一归还焦点(主聊天/边路/队列全部发送路径单点覆盖,输入框执行中 disabled 时仍不抢焦点);②「已执行 N 个步骤」折叠块键盘可达——由纯点击 div 改为 `<button type="button">`,Tab 可聚焦、Enter/Space 展开收起、读屏播报展开状态,并补 `:focus-visible` 轮廓(显式样式重置,外观与改前一致);③复制按钮补可访问名称——同一行六个操作里唯独它无 title/aria-label,现在悬停提示「复制消息」/复制成功 1.5s 内变「已复制」,读屏可念出;④触屏命中区放大(仅 `pointer: coarse` 生效,桌面像素零变化)——搜索浮窗关闭钮配 44px 命中泡,消息操作行与队列操作钮实体内扩到 32px,安卓壳上一次点准;⑤搜索跳转后焦点回归搜索触发按钮——跳转成功不再从文档头重走 Tab,触发按钮补 `tabindex`/Enter/Space 键盘激活(`Ctrl/Cmd+K` 行为不变),目标视图既有的「会话切换/面板挂载自动聚焦输入框」联动按「跳转在途窗口 + 焦点停驻触发按钮」让位(联动可能晚于跳转完成运行、也可能先于跳转聚焦并经 tiptap rAF 后写,四条跳转路径焦点归属一致);⑥动态状态对读屏播报——排队计数「待发送消息 (N)」与执行中打字指示分别加 `role="status"`。另:新增源码级回归测试 `chat-a11y.test.ts` 锁住上述全部属性修复;AGENTS.md 规范节固化「桌面端图标按钮必须带可访问名称」。

## 0.0.470 (2026-11-07)

### 前端(桌面 / Web)
- **移动端 / 窄屏下审批级别只显示图标**:输入框工具条的审批级别徽标(只读/读写/智能预审/替我审批/完全权限)在 `isMobileViewport`(视口 ≤ 768px,含手机端)时省略文案与下拉箭头,只保留级别图标(`PermissionLevelSwitcher` 新增 `compact` prop);当前级别说明仍由 tooltip 与下拉选项完整文案承载,点击展开后可见全部级别名称与描述。

## 0.0.469 (2026-11-07)

### 前端(桌面 / Web)
- **静默失败可见化:加载失败可重试**:历史消息加载失败不再伪装成「空会话」——消息区空态渲染「历史消息加载失败 · [重试]」(业务拒绝如新建会话尚未落库仍保持静默,仅网络 / HTTP 层失败置错误态);长会话上滑加载更早消息失败时列表顶部出现「加载更早消息失败,点击重试」小条;任务列表初始加载失败时不再静默掉进「新建任务」态——任务面板顶部出现错误条,中央渲染错误卡,提供 [重试] 与 [仍要新建任务] 显式出口,重试成功自动落到最近会话。
- **断线有终态有出手点**:连接横幅原「正在自动重连」一句话刷屏到底,现在连续重连失败 ≥3 次(约 7 秒)或鉴权被拒时升级为红色终态「重连失败,网络或服务端异常」,附 [立即重试](归零退避立即重连)与 [重新加载页面] 两个按钮;任一次连接成功立即恢复原自动重连文案。
- **复制失败有反馈**:消息复制在 `navigator.clipboard` 与 `execCommand` 双路都失败时(如 http://IP 访问、权限被拒)此前完全静默,用户误以为复制成功;现在弹出「复制失败,请手动选择文本复制」error toast(与执行错误横幅复制会话 ID 同口径)。
- **编辑态图片可见可删**:编辑最后一条用户消息时原消息图片此前完全不展示、无法感知也无法去掉;现在编辑区渲染原图缩略,每张附移除按钮(tooltip 明示「仅本次重发不携带此图,历史消息不受影响」,可再点恢复),确认后按剩余图片集合重发——移除全部图片现在能正确表达为重发不带图(此前传空会被回落吞掉照发原图)。
- **@文件面板空态与失败可见**:输入 @ 过滤无匹配结果时显示「未找到匹配的文件」(此前只渲染组标题「文件」,看起来像坏了);文件列表拉取失败时显示「文件列表加载失败 · [重试]」并可在面板内重试。

## 0.0.468 (2026-11-07)

### 后端
- **15 处工具失败摘要统一接上原因**(纯展示文案,成功路径输出逐字节不变):`tool-result-summarizer.ts` 此前失败可读性三档并存——file/search 五类带 40 字原因、8 处只有裸 `(失败)`(图片×2、微信×2、钉钉、飞书、MCP×2)、7 个函数压根不查 `error` 键。现在任何工具失败都能从卡片头看出「失败了 + 为什么」:shell 的 exec/write_stdin 段失败此前分别误报 `(exit -1)` / 毫无标识,现在显示 `(失败: 原因)`(`completed=false` 超时维持「未完成/超时」、非零退出码维持 `(exit N)` 不变);web_search 失败不再误报 `(0 条结果)`;open_web_page 失败带上 url;定时任务四兄弟失败带上原因;裸 `(失败)` 的 8 处换成 `(失败: 原因)`(图片两处失败路径顺带带上 prompt;钉钉/飞书 `success=false` 且无 `error` 键的形态兜底保留裸 `(失败)`)。摘要只落 `tool_calls.summary` 展示列,模型上下文与工具成败判定不受影响。

### 前端(桌面 / Web)
- **`generate_image` / `edit_image` 卡片渲染图片网格**:画图后不用再展开原始 JSON 找图。prompt 头行 + 网格缩略(`el-image` lazy + 固定 160px 高 + 点击原生放大,单次最多渲染 9 张、超出显示「共 N 张,其余 N-9 张略」)+ 大小/mime 角标 + 底栏摘要(`N 张图 · model · size`);`/uploads/` 静态地址不可达时(LOCAL/反代)error 槽降级显示路径与「复制路径」按钮,不白屏。空 `images`/非数组/解析失败回落原始 JSON。
- **`ask_user_questions` 已回答问答对富视图**:回答落库后回看会话不再是一坨转义 JSON。每组问答渲染问题行 + 选中标签 chip(多选全展示,样式对齐 QuestionPanel)+ 自定义输入引用块;底栏摘要「N 组问答」。
- **`check_subagent` / `wait_subagents`(超时分支)子代理进度行富视图**:每行 = `#taskId` + agentType + status 徽标(RUNNING 蓝 / COMPLETED 绿 / FAILED 红 / 其余灰)+ 轮数/工具数/token 统计 + `recentOutput` 折叠区(默认单行省略,「展开/收起」切换,展开态 160px 滚动);底栏摘要 check 给「N 个任务」、wait 给「完成 N 个 · 全部完成/已超时」。`wait_subagents` 的自由格式 `results[]` 保持原始 JSON 展开。
- **7 个工具的执行中卡片不再显示「参数加载中...」假象**:`getToolInputPreview` 补 `check_subagent`/`cancel_subagent`(`任务 #N`)、`create_scheduled_task`(name 截 30)、`update_scheduled_task`(name 优先,否则 `任务 #N`)、`delete_scheduled_task`(`任务 #N`)、`send_wechat_image`/`send_wechat_file`(与飞书同口径)的输入预览,流式期间卡片头直接显示任务名/任务号/文件名。
- **LOCAL 模式 MCP 审批标题不再通用化**:审批堆叠卡此前对 `mcp:<server>` 形态的工具名全部显示「操作待审批」,现在解析为「MCP <server> 待审批」,能看出在等哪个 server(标题解析抽为 `approvalTitle.ts` 纯函数)。

## 0.0.467 (2026-11-07)

### 后端
- **信号哨兵请求头支持凭据占位符 `{{MAO_TOKEN}}` / `{{ECP_TOKEN}}`**:探针要访问需登录态的目标服务时,此前只能把短效 token 明文粘进自定义请求头(MAO_TOKEN 按次签发很快过期、ECP_TOKEN 会被自动轮换,粘进去必然静默失效连败到停用)。现在请求头值支持这两个占位符:落库的是占位符本身(随 headers 整体 AES-GCM 加密,不含明文),`runCheck` 与「立即测试」在探针执行前按哨兵属主身份现取当时有效的 token 替换后发出——MAO_TOKEN 走 shell JWT 签发链(与 CLOUD shell 同权),ECP_TOKEN 经只读实例读取 ECP 登录态(不写 / 不清 AccessOne 布局)。解析失败(用户异常 / ECP 票过期未重新登录)只跳过该请求头继续探针,不计连败、不停用;值含未识别 `{{XXX}}` 保存时直接拒绝;只由占位符组成的敏感头回显不打码(`Bearer {{MAO_TOKEN}}` 等混合值仍打码),复制继承与编辑掩码还原口径不变。存量哨兵头值里已保存的 `{{...}}` 字面量不受新校验影响:编辑 / 改名 / 复制原样回传与继承、执行时与旧版一致原样直发,仅新填写的未识别占位符被拒绝。

### 前端(桌面 / Web)
- **信号哨兵表单请求头值输入框新增「插入凭据」控件**:MAO / ECP 两个小按钮在光标处插入对应占位符(拿不到光标时追加到值末尾),值输入框 placeholder 提示支持占位符;控件内嵌值输入框 suffix,不改动请求头行三列布局。tooltip 明示「插入的凭据会在每次探针时自动发往上面配置的 URL」,请确认目标服务可信。

### 终端 CLI(mao-cli)
- `mao signal-watcher create/update --headers` 与后端同口径本地校验占位符白名单:值含未识别 `{{XXX}}` 时直接报错,不再等一次请求往返才拿到参数错误;`skills/mao-cli/reference/signal-watcher.md` 核心规则表与常见排障补占位符说明(含 ECP 票过期与目标服务 401 的区分)。

## 0.0.466 (2026-11-06)

### 后端
- **新增因 402/403(额度不足等)失败会话的一键批量恢复**(管理后台「会话管理」入口):一次余额中断会在各用户手里留下多个停在 FAILED 的会话,此前只能逐个点「重试」。现在提供 `GET /v1/admin/sessions/recover-failed/preview`(候选预览:最近 1–168 小时内活跃、当前 FAILED、且最后一次 agent 场景 LLM 调用为 402/403 的 NORMAL 主会话,逐条给出可恢复/跳过原因,`scope=QUOTA` 默认仅额度类、`ALL_402_403` 可放宽)与 `POST /v1/admin/sessions/recover-failed/run`(按勾选 id 异步续跑,单批上限 20,立即返回受理/跳过明细)。续跑完全复用崩溃恢复内核(`CrashRecoveryRunner` 新增 FAILED 相位入口):清理尾部 → RESUMING → RUNNING → 基于历史执行;执行前重查 phase 并与实例内恢复/WS 执行占位/执行池并发防护对齐(池满返回 `EXECUTOR_BUSY`)。LOCAL 模式、子代理/边路会话、仍有待重试定时任务 run 的会话自动排除。恢复全程 `/v1/admin` 审计留痕,服务日志前缀 `[failed-session-recovery]`。

### 管理后台
- **会话管理页新增「恢复 402/403 失败会话」**:页头入口按钮(`session:write` 可见)打开预览弹窗——扫描窗口(最近 24 小时/3 天/7 天)与口径(仅额度类/全部 402/403)可切换,清单展示用户、标题、失败时间、错误摘要、最后用户消息、是否可原地续更渠道卡片与跳过原因,默认全选可恢复项、支持人工取消勾选;普通二次确认后发起恢复,提示受理/跳过数量。恢复后原飞书/钉钉进度卡片原地复活并持续更新,无卡片映射的会话在完成后推送最终回复。

## 0.0.465 (2026-11-06)

### 前端(桌面 / Web)
- **工具调用执行中计时不足 1s 不显示**:绝大多数工具调用远小于 1s,此前一起手就闪一下 `12ms` / `648ms` 之类转瞬即逝的数字,只是噪音。现在耗时满 1s 才渲染计时(跨过阈值后随首个秒级 tick 出现),不足 1s 的调用全程不显示;≥1s 的调用展示口径不变。轨迹 tab 的执行中计时同口径。

## 0.0.464 (2026-11-06)

### 后端
- `tool_call_start` 的订阅重放(页面刷新 / 网络重连时对在途工具调用的状态重放)新增 `elapsed_ms`:服务端自该调用首次 sighting 起算的已耗时(参数增量到达不重置起表)。实时(非重放)事件载荷不变。

### 前端(桌面 / Web)
- **修复工具调用执行中计时在页面刷新 / 重连后从 0 重启**:0.0.463 的执行中耗时以 WS 事件到达客户端的时刻起表,页面刷新或网络重连后,在途调用由服务端重放、计时基座被重置,显示值远小于真实耗时(在途 1m45s 却显示 3s)。现在订阅重放的 `tool_call_start` 带回服务端已耗时 `elapsed_ms`,前端据此把基座换算到真正的开始时刻(传相对耗时而非绝对时刻,两端时钟偏差不影响);实时事件仍以收包时刻起表。轨迹 tab 的执行中计时同源修复(它此前同样是客户端打表)。已完成的调用仍不显示耗时;历史回放消息不带计时字段,同样不显示。

## 0.0.463 (2026-11-06)

### 前端(桌面 / Web)
- **工具调用执行中显示实时耗时**:聊天里执行中的工具调用,在转圈图标旁实时显示已耗时(如 `1m30s`,秒级刷新),回答「这个调用卡了多久」;多调用组(如「2次写入文件、2次执行命令」)的组头显示组内仍在执行、最早开始那个调用的耗时,组内每行各自显示自己的耗时。已完成的调用不显示耗时;历史回放消息不带计时字段,同样不显示。计时取自 `tool_call_start` / `tool_call_result` 事件到达客户端的时刻,无后端改动。

## 0.0.462 (2026-11-06)

### 管理后台
- **会话管理主会话 / 子代理标签区分颜色**:会话列表「类型」列此前「主会话」与「子代理」都是灰色 `info`,肉眼不可分辨;现改为「主会话」灰、「子代理」蓝(`primary`)、「边路任务」橙,列表与详情页同步生效。

## 0.0.461 (2026-11-06)

### 后端
- **会话管理关键词搜索超时治理 + 活跃时间窗**:管理端 `/v1/admin/sessions` 的关键词搜索此前走**全局**消息检索——`id IN (SELECT ... MATCH ...)`(ngram 全文索引)或全表 LIKE 兜底,会话上的过滤传不进消息子查询。message 表 68 万行、正文 1.5GB 时实测单次 70~80s(`push_once` 这类普通关键词必现),前端 30s 超时直接报「网络错误」。现在改为「先按活跃时间筛会话、再逐会话 EXISTS 自身窗口内的消息」,实测 1.5~2.2s;同时新增 `last_activity_at` 索引(V188)支撑范围扫描与列表排序。新增接口参数:`range=7D|30D|90D|ALL`(大小写不敏感)与 `dateFrom`/`dateTo`(`YYYY-MM-DD`,Asia/Shanghai 日历,含首尾日,优先于 `range`),缺省按**最近 7 天**兜底;`ALL` 为全部时间(不限窗,慢查询由调用方自行承担)。关键词命中片段(`matchSnippet`)与召回同口径,同样限定在时间窗内。
- 管理端列表关键词搜索不再使用 ngram 全文索引(该索引在 message 表上的性能问题另行立项);`SEARCH_FULLTEXT_ENABLED` 只影响用户侧消息搜索。

### 管理后台
- **会话管理新增「活跃时间」筛选**:默认「最近 7 天」,可选最近 30 / 90 天、自定义区间、全部时间;关键词的「消息」匹配与列表过滤都限定在同一时间窗内。预设档位由后端按上海日历计算窗口,避免浏览器时区造成边界漂移;`last_activity_at` 为空的存量会话仅在「全部时间」下可见。

### 命令行(mao-cli)
- `mao admin-session list` 新增 `--range`(`7d`/`30d`/`90d`/`all`)与 `--date-from` / `--date-to`,与后端同口径:不传参数默认只看最近 7 天活跃的会话,要看更早的用 `--range all` 或显式区间。

## 0.0.460 (2026-11-06)

### 终端 CLI(mao-agent)
- **REPL 内切换会话(`/resume`)**:此前换会话必须退出重进(`mao-agent resume <id>`),当前对话的输入上下文全部丢失。现在交互 REPL 新增 `/resume`:无参数弹出最近会话选择面板(最近 20 条,↑↓ 选择、Enter 确认、Esc 取消),`/resume <id>` 直接切换,`/resume <关键词>` 按标题搜索(命中 1 条直切、多条弹面板)。切换后自动把旧会话标记已读、退订旧会话事件、加载最近 3 轮摘要、同步状态栏 Agent / 模型 / 上下文,并让下一条消息使用新会话的模型;目标会话正在执行时提示并续接输出。首版只支持同模式切换:LOCAL 进程只列 LOCAL 会话、CLOUD 进程只列 CLOUD 会话,且 LOCAL 要求目标工作区在当前目录内——跨模式 / 跨目录请退出后用 `mao-agent resume <id>` 打开。

## 0.0.459 (2026-11-06)

### 后端
- **本地任务支持多端审批**:LOCAL 工具需要审批时,服务端把审批请求(工具名 / 参数 / 危险原因 / 「总是允许」hint / 执行设备名)广播给该账号的全部在线端,任意一端「执行 / 拒绝 / 总是允许」都由服务端**先到先得**认领,认领结果再广播回所有端;执行端本地的等待(Electron 主进程审批 Promise / CLI 模态)按这份结果放行,不再要求人回到执行机。订阅 / 重连时重放待审批请求;参数超过 8 KiB 只广播前缀(全文仍只发给执行端);迟到 / 重复 / 已超时的决策帧一律忽略,会话规则只由认领成功的「总是允许」创建。

### 前端(桌面 / Web)
- 浏览器 / 移动端可审批本地任务的工具调用(卡片与桌面一致,含「总是允许」与执行设备名);同一请求在桌面本地卡与远端广播卡之间按 requestId 去重;审批结果回到各端后自动撤卡与收敛计数;本轮结束 / 切走会话时不再留下悬挂的等待。

### 终端 CLI(mao-agent)
- 本地审批模态可由其它端的决策解除,不必再回到执行机按键;旧的 `tool_approval` 通知帧兼容不变;旧后端或连接断开时仍按本地选择立即放行(不等待回声)。

## 0.0.458 (2026-11-06)

### 后端
- **用户用量明细改按 Token 倒序排列与截断**:原按消息数降序、按消息数截断 Top 20;Token 口径切换(0.0.440)后会出现「Token 高但消息少」的用户排不上榜的问题。现在按 Token 倒序(同值按消息数降序)截断 Top 20,与 Token 排行口径一致。

### 管理后台
- 「用户用量明细」表头提示同步改为「按 Token 倒序截断的 Top N」。

## 0.0.457 (2026-11-06)

### 终端 CLI(mao-agent)
- **交互模式不再产生空白会话**:此前每次打开 `mao-agent` / `mao-agent --local` 都会先在服务端建好会话,即使没发任何消息、直接退出,也会在任务列表留下一条空白会话。现在交互 REPL 的新会话**延迟到第一条消息**才创建:打开时 banner 显示「新会话」,发送首条消息后才落库并提示「已创建会话 #id」;首条消息前 `/session` 与首条消息前切换的 `/model`(随新会话一起写入)均已适配。`--resume` / `--continue` 复用旧会话、打印模式 `-p` 立即创建,行为不变。

## 0.0.456 (2026-11-06)

### 终端 CLI(mao-agent)
- **登录成功后自动记住站点地址**:`login` 成功时,若本次地址只来自 `--base-url` / `MAO_BASE_URL` 且用户配置里还没有 `baseUrl`,自动写入 `~/.mao/agent-cli/config.json`——修复「带 `--base-url` 登录成功后,下一次裸跑 `mao-agent` 仍提示未配置站点地址」的割裂体验。已有用户配置不覆盖(临时连其它环境不会改写长期默认),项目配置不参与判断;保存失败不影响登录结果,只提示手动设置方式。

## 0.0.455 (2026-11-06)

### 后端
- **修复 mao-agent 建的本地任务在桌面端对话必报「技能同步失败:客户端 60 秒内未回报」**:同一台机器上桌面端与 mao-agent 各自生成 deviceId(两套独立实现,同机也不同),会话被绑定到 mao-agent 的设备;mao-agent 退出后用户在桌面端继续对话,`skill_sync_required` / `mcp_sync_required` 仍按 deviceId 定向投给那条已无人响应的连接,服务端空等 60 秒后把会话判失败,点「重试」再撞一次,表现为「老是出现这个问题」(线上 session 2297)。现在:
  - 桌面端设备身份与 mao-agent 合并到同一份 `~/.mao/agent-cli/device.json`:CLI 装过就以它为准,CLI 未装时由桌面端创建,两端天然同一个 deviceId,服务端据此把「这台机器」识别为一个整体;
  - 技能 / MCP 同步改为投给该 deviceId 下的所有存活本地连接(新增 `StreamingWsRegistry.sendToDeviceFamily`):同机两端虽然是两条独立 WS 连接,但谁在线谁就能接手响应;mao-agent 退出后残留的半死连接(活着但没有该会话上下文,收到请求也不回报)不再霸占投递目标,桌面端可直接应答;
  - 设备族里一个可投递连接都没有时立即以「没有可用的本地执行端连接(桌面端 / mao-agent 未连接或已掉线)」失败,不再空等 60 秒(MCP 同步同口径直接降级),避免把「没连上」误导成「没回应」;
  - 后端重启后的崩溃恢复执行前先预检 LOCAL 执行端可达性,不可达直接判 FAILED 并给出「请启动客户端后重试」,不再跑到技能同步处才失败;该 FAILED 终态与异常 / 成功路径同口径走收尾回调,定时任务运行记录与会话队列接力正常收敛。
- **修复带图片的用户消息里快捷指令内容不生效**:只要消息里带了图片(粘贴截图、上传图片),这条消息在引擎里就是「多模态 content parts」而非纯字符串,而 marker 展开逻辑此前只处理字符串——`#{命令}#`、`${技能}$`、`@{文件}@` 三类 marker 被整条跳过,Agent 只看到 marker 以外的正文,指令正文(如 `talk` 的对话模式约束)从未进入上下文。表现为「注入了快捷指令但 Agent 像没看到」,且纯文本消息完全正常,故只在带图时才复现。现在展开逻辑同时覆盖两种形态:多模态消息逐 part 处理,只改写 text part 里的 marker,image 等其它 part 原样保留且顺序不变;纯文本消息路径不变。展开仍只作用于请求副本,不写回会话历史(同一命令内容嵌套其它 marker 时不会逐轮二次展开),`senderYmd` 等字段一并保留(跨天提示标签不漏发)。

## 0.0.454 (2026-11-06)

### 前端(桌面 / Web)
- **修复点飞书 / 钉钉分组标题右侧「+」新建任务仍掉到临时工作区**:0.0.451 只修了任务列表上方那条「新会话」宽按钮,分组标题右侧的「+」(`TaskIndexPanel.onGroupNewTask` → `newTaskFromGroup`)是另一条独立代码路径,仍按旧口径只对 LOCAL 预填工作区、只对共享云项目带项目键,通道会话两支都为空,点下去照样落到后端自动建的全新临时工作区。现在该入口改用与「新会话」完全相同的两个助手(`cloudProjectKeyForNewTask` / `newTaskWorkspaceFromSession`):共享云项目沿用项目,通道会话(飞书群聊 / 私聊、钉钉)以 `reuseWorkspace` 显式预填该群 / 私聊工作区,临时与其他自定义目录不预填。emit 载荷与 `handleNewTaskFromGroup` 同步补上 `reuseWorkspace` 字段(路由 query 本就序列化 `reuse=1`,无需改后端)。自此四个新建入口口径统一:宽按钮、分组「+」、总览「新任务」一致;首启向导的示例任务仍是干净起步(本就无会话上下文可沿用)。

## 0.0.453 (2026-11-06)

### 后端
- **Git 凭据注入按 Agent 收敛(Agent 级 Git 凭据域)**:修复「同一用户的全部 Git token 会被注入到任意 Agent 会话」的隔离缺陷——`user_git_credential` 只按用户维度存储,此前 Agent shell、云端终端、容器 envelope、容器 MCP、建会话 clone、Git 面板写操作六条通道都按 userId 全量取 token,导致同时服务两家公司的多个 Agent 互相可见对方凭据(出现过「B 公司 Agent 拉取 A 公司仓库」的越权事件)。现在:
  - `agent` 新增 `git_allowed_domains`(三态:NULL=未配置、`[]`=显式禁止、非空=白名单),统一解析器 `GitTokenResolver` 按会话 / Agent 上下文取「用户凭据域 ∩ Agent 允许域」,六条注入通道全部改走它;
  - 兜底口径:会话查不到 / 归属不符 / Agent id 不存在 / 任何异常一律 fail-closed(空集 + warn 日志);`agentId` 为空的存量历史会话与未传会话上下文的调用按全局策略处理;白名单场景只解密交集域的 token(`getTokenMapByUserFiltered`,SQL `IN`),不再全量解密;
  - 新增系统设置「Git 凭据域策略」(`gitToken.scopePolicy`):宽松=未配置允许域的 Agent 不限制(默认,升级无感)、严格=未配置即不注入(fail-closed);切换后已打开的终端 / 容器 MCP 连接需重开才生效(shell 工具下一条命令即生效);
  - 管理后台 Agent 表单新增「Git 凭据域」(启用开关 + 域名多选 / 手输,候选来自当前管理员自己的凭据域);列表能力概览显示三态(未配置 / 已禁止 / N 个域)并支持筛选「未配置」,便于收敛存量;
  - `AgentVO.gitAllowedDomains` 仅对具备 `agent:read` / `agent:write` 权限的调用方返回(最小暴露)。

### 管理后台
- Agent 编辑页新增「Git 凭据域」配置项;Agent 列表新增 Git 域状态与「未配置」筛选;系统设置新增「Git 凭据域策略」下拉(宽松 / 严格)。

## 0.0.452 (2026-11-06)

### 前端(桌面 / Web / 安卓)
- **任务面板「任务」标题在更窄的面板宽度下也保留**:0.0.448 把头部「+」换成通栏「新会话」按钮后,头部图标从 5 个减为 3 个,但标题隐藏阈值仍沿用 200px 的旧口径——150–199px 区间里标题被「多余」藏掉,图标只占 76px、空间明明够。现在标题阈值独立为 160px(`HEADER_TITLE_MIN_WIDTH`),160px 起标题与三个图标同框显示;行内操作按钮(重命名 / 删除确认)与分组头部的打开文件夹 / 终端入口仍保持 200px 门槛不变——窄行里 hover 操作会盖住会话标题,该隐藏的继续隐藏。面板可拖至 120px、移动端默认 140px,两档极窄宽度下标题依旧隐藏、图标缩至 20px。

## 0.0.451 (2026-11-06)

### 前端(桌面 / Web)
- **「新会话」在飞书 / 钉钉等机器人通道会话里沿用该通道工作区(不再掉到临时工作区)**:0.0.450 修掉"首条消息报工作区不存在"后,从通道会话点「新会话」会因为没有任何项目 / 工作区被沿用,直接掉到后端自动建的全新临时工作区——群共享目录里的文件一份都看不到,与「新会话」本该有的"同工作区、开新一轮对话"语义不符(WORK 项目沿用项目、LOCAL 沿用目录,只有通道这一支被掏空)。现在对通道会话(飞书群聊 / 私聊、钉钉)改为显式预填当前工作区:与总览「新任务」完全同链路(`reuseWorkspace` 标记 → 路由 query → `newTaskWorkspacePrefill`),后端 `assertOwnedSessionWorkspace` 对本人参与过的通道工作区放行,新会话直接建在该群 / 该私聊的共享目录、并按 workspace 归入侧栏同一个飞书 / 钉钉分组。同时:芯片与配置条对通道目录显示通道标签(如「飞书群1·oc_d1254d0」)而非裸 chatId;`lastViewedSession` 回落路径(从会话退回新建态)同步只存"该预填的"工作区,并把残留的 git / existing 模式复位——否则 git 模式下 payload 不带 workspace,预填的通道工作区会被悄悄丢成临时工作区。共享云项目仍沿用项目、临时与其他自定义目录仍不预填,行为不变。

## 0.0.450 (2026-11-06)

### 前端(桌面 / Web)
- **修复在飞书 / 钉钉等机器人通道会话里点「新会话」后首条消息报「工作区不存在」**:通道会话(飞书群聊 / 私聊、钉钉、微信)的 `projectKey` 是通道工作区标识(`feishu-chat-{botId}-{chatId}` 等),不是 `projects/` 下的项目 slug,此前点「新会话」会被原样带进新建态并把工作区来源切成「选择工作区」,首条消息按「已有工作区」解析 `{workspaceRoot}/{userId}/projects/<通道key>`,目录不存在直接报错、整笔建会话回滚。现在只有共享云项目(工作区在 `projects/` 下)才沿用项目,通道会话与独立临时工作区一律不带回,新建会话回到"后端自动建独立临时工作区"的默认行为(通道工作区的沿用见 0.0.451)。任务列表分组右键新建本就是该口径,此次与它对齐;同一判定也覆盖 `lastViewedSession` 回退路径与边路任务面板,刷新后从通道会话新建、在通道会话里开边路任务同样不再报错。

## 0.0.449 (2026-11-05)

### 前端(桌面 / Web)
- **写入文件工具调用同步改为自然形态渲染**:沿用 0.0.442 起编辑文件、执行命令、读取文件及 0.0.444 搜索 / 网页四类工具的富视图模式,原始输入 / 输出 JSON 统一收起在「查看原始输入输出」开关后。**write_file**:直接展示写入的整篇内容——带行号槽(从 1 起算)并按扩展名做语法高亮(复用消息代码块同款 monaco 着色,懒加载、跟随亮 / 暗主题,着色失败回落纯文本,超 2000 行自动降级);底栏显示「共 N 行 · +a -d」(行数差取后端 `file_change` 口径,新建无 +/-、覆盖有,据此可区分两种写入)。文件路径与新建 / 覆盖不在正文里单独占一行——卡片头 summary 已由后端 summarizer 带上路径与行数,正文只留内容本身。写入失败时与 read_file 同口径:不渲染内容块,只在底栏直显 error 文案。不做 diff:旧内容在客户端拿不到——后端 `file_change_diff` 在 WS 推送与落库前都被 `stripPrivateDiff` 剥掉,实时流式与历史回放同源地只有新内容;覆盖类改动的 diff 看文件变更面板(快照由服务端另存,不走工具结果)。
## 0.0.451 (2026-11-06)

### 前端(桌面 / Web / 安卓)
- **新建会话入口改为任务列表上方的「新会话」宽按钮**:原先只能点任务列表标题旁一个小小的「+」图标——目标小、窄面板与触屏上更难命中,文案也只能靠 tooltip 才看得见。现在列表头部(任务标题右侧)保留模式切换 / 多选 / 刷新三个图标,「+」移除;在任务列表上方新增一条通栏「新会话」按钮(会话图标 + 文案,羊皮纸底 + 细边框 + 灰字,与侧栏同气质;静止时不抢列表视觉,悬停加深底色提示可点),任务再多也不必滚动即可新建。点击后的新建会话逻辑与原来完全一致(仍走同一 `newTask` 链路,含从分组 / 工作区预填的沿用规则)。窄面板(可拖至 120px、移动端 140px)下按钮整条保留,图标与两字文案仍完整可见。

## 0.0.447 (2026-11-05)

### 前端(桌面 / Web)
- **执行端提示条视觉重设计,融入对话流**:0.0.445 引入的执行端提示条用「pill 边框 + 染色底」的警告芯片样式,横在消息列表顶部像外来件,与对话流不和谐。现在改为历史锚点条(边路面板「正在查看较早的消息」)同款的柔和实底细条:`--aw-surface-hover` 底 + 8px 圆角、无边框、sticky 吸顶;未登记 / 他端离线为 warning 橙字,绑定他端在线为 info 绿字(`--aw-success`),语义与严重度由文字颜色承担而非染色底。文案同步精简:去掉设备名两旁的「」引号,展示名剥离 mDNS 的 `.local` 尾巴(悬停 title 仍为完整主机名),超长设备名省略号截断。

## 0.0.446 (2026-11-05)

### 后端
- **桌面端打开本地任务即完成执行端登记(不再限执行中)**:0.0.439 设备委托的惰性认领原先只在会话*执行中*被订阅时触发——功能上线前创建的老会话,在桌面端打开后仍显示「未登记执行端」,要等首次发送才补绑。现在订阅入站点放宽:连接自带设备身份(Electron / CLI)时,打开任意 LOCAL 会话即条件认领(`WHERE target_device_id IS NULL` 只补空值,已绑定会话永不被覆盖);浏览器无设备身份,保持原口径仅执行中触发,认领面不随放宽扩大。认领证据与拒绝口径不变(发起连接自身 → 唯一在线且平台一致 → 多设备 / 平台不符保守拒绝并提示),换机或设备身份丢失后的「重建会话」恢复路径不变。

## 0.0.445 (2026-11-05)

### 前端(桌面 / Web)
- **本地任务执行端标识移出输入工具条,改为消息列表顶部提示条**:会话态输入工具条在窄屏(≤768px)下把 `+`、工作区指示(文件夹+目录名)、执行端徽标「本地任务 · 未登记执行端」、权限切换、模型名、发送全挤在一行,徽标文案最长,把工作区目录名都挤没了。现在执行端标识从工具条整体移除(全平台统一),改为消息列表顶部的会话执行端提示条(`LocalTargetBanner`,sticky 吸顶、细条不挡内容):未登记执行端(warning 态,引导去桌面客户端打开一次完成登记)、绑定他端离线(warning 态,提示去该设备打开 Mao)、绑定他端在线(info 态,说明任务在哪台设备执行)三态;且当前设备就是会话绑定的执行端时(桌面端常态)完全不渲染——没有信息量就不占地方。工具条回归「+ / 工作区 / 权限 / 模型 / 发送」,工作区指示器(点击打开工作区)原位保留。浏览器端照常提示(拿不到本机设备身份,无法确认「本机即执行端」);边路任务面板同款处理;后端认领 / 发送校验 / 引导文案均不变。

## 0.0.444 (2026-11-05)

### 前端(桌面 / Web)
- **单个工具调用一次点击即展开**:工具组(`ToolCallGroup`)的组头与内部工具卡片头信息完全重复——同一段 summary、同一个收起箭头,组内只有一个调用时会出现两行一模一样的行,看内容必须点两下(组头一下、卡片头一下)。现在组内只有一个工具调用时不再渲染组头,直接展示工具卡片:一次点击即展开内容,同时消掉这层同义折叠。组壳本身没有丢:卡片头新增 `standalone` 样式充当组壳——内边距、圆角、悬停底色与多调用组的组头完全同口径(`--aw-canvas-parchment`),点击热区一样大;状态徽标(成功 / 失败 / 执行中 / 输出已截断 / 审批放行 / 查看子代理过程)本就比组头全。工具类型图标从组头挪到卡片头(图标表提取为 `utils/toolIcons.ts`,组头与卡片头共用,未知工具回落通用工具图标),多个调用时卡片行也带图标,扫一眼即可辨认工具类型;组内 ≥2 个调用时行为不变(组头汇总「2次读取文件、执行命令」→ 点开列表 → 再点各卡片)。
- **搜索 / 网页四类工具调用同步改为自然形态渲染**:0.0.443 已覆盖编辑文件、执行命令、读取文件,本轮补齐搜索与网页类工具,同样把原始输入 / 输出 JSON 收起在「查看原始输入输出」开关后。**glob_search**:查询行(pattern 主色 + 搜索根目录)+ 文件列表,每行「目录淡显 + 文件名」,点击在会话中心标签打开该文件(沿用文件变更面板同款 CLOUD / LOCAL 打开逻辑);无结果显示「未找到匹配的文件」,结果超上限提示已截断。**grep_search**:查询行(pattern + glob 过滤芯片 + 搜索路径)+ 按文件分组的结果列表,命中行带行号槽、行内匹配片段 `<mark>` 高亮(内置 `grepHighlight` 工具:转义非法正则后按字面量兜底),上下文行淡显;点击文件头同样打开文件。**web_search**:查询行 + 结果列表,每条 = 标题(外链,Electron 经 `setWindowOpenHandler` 转系统浏览器)+ host + 两行截断摘要。**open_web_page**:页面标题 + url 外链 + 正文(复用消息同款 MarkdownContent 渲染 turndown 产物,基础排版规则随组件 scoped 生效),正文超长提示「正文已截断」并给出完整内容文件一键打开;无正文提示「未提取到正文内容」。四个工具共用同一富视图骨架,底栏显示「N 个文件 · 已截断」/「N 处匹配 · M 个文件」/「N 条结果」/「正文 N 字符」摘要,工具级失败直显 error 文案。文字一律不用主色(蓝):查询词取墨色 `--aw-ink`(与终端会话块命令同口径),网页标题同取墨色并以悬停下划线表明可点、url 取中性灰 `--aw-ink-muted-64`,蓝色只留给行 / 文件头的交互态底纹。
- **终端会话块命令 / 输出改用墨色阶配色**:0.0.443 起 shell 工具调用渲染为终端会话块,命令与 `$` 提示符沿用主色(蓝),在亮色代码底色上扎眼,且与输出的主次关系全靠色相区分、并不清晰。现在命令取 `--aw-ink`(亮色近黑、暗色近白)、输出取中性灰 `--aw-ink-muted-64`、`$` / `›` 提示符压更暗一档灰(`--aw-ink-muted-60`)——亮色主题即「命令黑、输出灰」,暗色主题自动翻转为「命令白、输出灰」,无需两套硬编码色值。工具级失败(拒绝执行等)仍为红色直显,exit 徽标、其他工具与消息代码块配色不变。
- **本地任务(LOCAL)上传入口不再限制只能选图片**:桌面客户端本地任务点「+」上传时,文件选择框被收窄为 `accept="image/*"`,只能选图片,选其他文件(日志、压缩包、代码等)在系统对话框里直接置灰。实际上传链路本就支持任意文件——发送时拷贝到本机 `~/.mao/attachments/<host>/<sessionId>/` 并以 `@{绝对路径}@` 引用,不出本机(主进程仅校验大小 / 空文件 / 目录 / 软链接,无类型限制)。现在移除该收窄,本地任务与云端任务一样可任选文件类型;浏览器端本地任务添加附件仍会提示改用桌面客户端(原有边界不变)。

## 0.0.443 (2026-11-05)

### 前端(桌面 / Web)
- **执行命令 / 读取文件工具调用同步改为自然形态渲染**:沿用 0.0.442 编辑文件的富视图模式,原始输入 / 输出 JSON 统一收起在「查看原始输入输出」开关后。**shell**:渲染为终端会话块——`$` 提示符 + 命令(`write_stdin` 动作为 `›` 提示符 + stdin 标签)+ 分隔线 + 输出原文,命令换行时续行与首行文本对齐,命令以主色与输出区分(`--aw-text-code` 与 `--aw-ink` 在两主题下同值,直接复用会主次不分);工具级失败(拒绝执行等)在输出位红色直显;底栏只保留 exit code 绿 / 红徽标与「输出已截断」/ 后端 message 提示,工作目录不再展示。`close` / `list` 等无命令也无输出的动作自动回退原始 JSON。**read_file**:默认展示文件内容代码块并带行号槽(起始行号取 `offset`,分页读取时与编辑器行号对齐),并按扩展名做语法高亮(复用消息代码块同款 monaco 着色,懒加载、跟随亮/暗主题,横向滚动行号列吸顶跟随;着色失败自动回落纯文本);底栏显示「共 N 行 · 第 X–Y 行」;读取失败直显 error 文案;图片读取保持图片预览不变;超过 2000 行的文件自动降级为纯文本块避免海量 DOM。三个工具(edit_file / shell / read_file)共用同一骨架,后续 grep_search 等可照此接入。

## 0.0.442 (2026-11-05)

### 前端(桌面 / Web)
- **编辑文件工具调用改为 Diff 片段展示**:工具卡片展开后,原先「输入」区直接把 `{"path":...,"old_string":...,"new_string":...}` 整段 JSON 平铺、「输出」区也是 `{"success":true,"replacements":1,...}` 原文——替换片段稍长就满屏转义符,到底改了什么完全看不出。现在默认只展示行级 diff:删除行红底 `-`、新增行绿底 `+`,每个变化块前后各带 3 行上下文,相距远的多处修改折叠为「⋯ N 行未变化」(口径与后端 `FileChangeDiffUtil` 一致:LCS、3 行上下文、末尾换行不产生空行;超过 200 万 cell 的超大片段降级为整段替换,不卡渲染);diff 底栏一行显示结果摘要「替换 N 处 · +a -d」(行数取后端 `file_change` 口径)或失败原因。原始输入 / 输出两块 JSON 收入「查看原始输入输出」开关,点击后才展开,便于排查。数据源为工具入参本身,实时流式与历史回放同源,不依赖 `file_change` 事件,CLOUD / LOCAL / Web 全端一致;其他工具的输入输出展示不变。

## 0.0.441 (2026-11-05)

### 后端
- **autoConsume 重入保护不再丢弃并发调用(0.0.434 跨通道接力链的补充修复)**:重入保护原先对并发调用直接 `return`——当首轮用「忙快照」(IM 通道执行占用 / phase 活跃)进入 `sendQueueUpdated` 等等待,而对应执行恰在等待期间结束并发起接力时,该接力调用被吞,排队消息只能等下一次执行收尾(即 0.0.434 要修的滞留形态)。现在被吞调用记入补跑标记(`relayToIm` 意图并集),当前轮结束后在同一保护下按该意图补跑一轮;补跑有软上限(5 轮,超限仅留日志,消息保留在队列不丢)。同时 `dequeue` 返回空(队头在 `listPending` 与 `dequeue` 之间被取消排队 / 插队 / 跨实例消费移除)时,释放占位后按「队列已空」补上 IM 队列接力,不再浪费这次消费机会。

## 0.0.440 (2026-11-05)

### 后端
- **用量分析「用户 Token」三处展示统一改用 LLM 调用口径(与调用/成本同源)**:用户用量明细、用户 Token 排行、模型页「用户 Token 占比」此前均取 `message.token_count`、按会话归属人汇总,而同一行的调用次数 / 失败调用 / 成本取 `llm_call`、按调用发起人汇总——飞书群聊等「执行人 ≠ 会话归属人」场景下会出现「有调用量、成本,但 Token 为 0」(群成员在他人创建的群里触发调用,Token 记到会话归属人名下)。现在三处统一取 `llm_call.total_tokens`(含压缩、标题等不落消息的旁路调用;排行 / 占比跟随「排除连通性测试」开关),会话数、消息数口径不变。

## 0.0.439 (2026-11-05)

### 后端
- **本地任务跨设备委托:LOCAL 会话创建即绑定执行端设备(不可改绑)**:`session` 表新增 `target_device_id` / `target_device_name`(V186,设备名为主机名快照,存量行为 NULL)。WS 握手 auth 帧支持上报 `device:{id,name,platform}`,注册表按设备登记执行连接;LOCAL 工具调用改定向投递给会话绑定的设备(未绑定的历史会话保持用户级广播回退),技能/MCP 同步帧同样定向。`POST /v1/sessions` 的 LOCAL 分支接受并落库两列(字符集/长度校验,CLOUD 忽略),会话 VO 返回 `targetDeviceId / targetDeviceName / targetDeviceOnline`(在线态为请求时快照,未绑定时缺省)。历史会话惰性认领:WS 入站发送时优先由发起连接的设备认领;无设备身份时仅在「唯一在线且平台一致」的设备上认领;多台同平台设备在线则拒绝并提示到桌面客户端打开一次完成登记;认领使用 `target_device_id IS NULL` 条件更新,已绑定会话永不被覆盖。边路任务 / 子代理 / 边路提升均继承目标设备绑定。
- **浏览器可继续已有 LOCAL 会话对话**:服务端不再依赖前端拦截——浏览器 WS 连接无设备身份,但已绑定设备的 LOCAL 会话仍会投递到绑定设备执行;浏览器仍不能创建 LOCAL 会话。

### 前端(桌面 / Web)
- **本地任务展示执行端与在线态**:会话输入区显示「本地任务 · 设备名(在线/离线)」,历史会话未登记执行端时显示「本地任务 · 未登记执行端」;绑定设备离线时发送前拦截并提示。
- **浏览器边界按「新建 / 继续」区分**:浏览器新建本地任务仍被拦截;打开已有本地任务可继续对话(执行端仍是绑定设备),本地任务附件仍需桌面客户端添加。

### 桌面 Electron
- **设备身份与 LOCAL 绑定**:首次需要时生成稳定 `deviceId`(UUID,与偏好同存 `userData/desktop-prefs.json`,设置不可改写),新增 `device-info-get` IPC(返回 `{ id, name: hostname, platform }`);WS 握手首帧 auth 携带设备身份,创建 LOCAL 会话自动带上本机 `targetDeviceId / targetDeviceName`。

### 终端 CLI(mao-agent)
- **LOCAL 会话绑定本机设备**:`deviceId` 持久化到 `~/.mao/agent-cli/device.json`(缺失/脏值自动重建),WS auth 帧上报 `{ id, name: hostname, platform }`;`--local` 创建会话时携带本机设备绑定。

## 0.0.438 (2026-11-05)

### 桌面 Electron
- **服务器设置改版:状态卡 + 历史一键切换**:原「填地址 → 连接并保存」的两步切换重构为三层结构——① 顶部「当前连接」状态卡:状态点(检测中/已连接/无法连接/未配置)+ 当前站点 + 探测详情(version/可达性)+ 壳版本、LOCAL 命名空间,打开即自动探测,也可「重新检测」;② 「连接过的服务器」列表:每行显示站点标识、主机名、归一化地址与最近连接时间(相对时间),HTTP 站点带警示标;非当前行提供「切换」按钮——点击即探测并切换,仅当切换到不同服务器时弹一次确认(提示将清除登录态、LOCAL 任务数据按站隔离、需重新登录),同站重连不打断;「删除」改为行内两步确认(3 秒内再次点击才生效),防误触;③ 手动表单默认折叠为「连接到其他服务器…」,未配置服务器(首启)时自动展开,输入时实时预览归一化后的站点地址,支持粘贴带 `/api`、`/api/v1` 的地址。应用菜单「服务器设置…」与设置页「服务器」行为保持一致(设置页同步新增状态卡与历史快切);Esc 可关闭独立设置窗口。
- **修复设置窗口保存时重置自动更新源**:从「服务器设置」窗口保存会把更新源策略硬编码为「跟随当前服务器」,覆盖用户在设置页「服务器」中选过的「安装包内置地址 / 关闭自动更新」;现在保存时原样保留当前策略,仅设置页可修改该选项。

## 0.0.437 (2026-11-05)

### 后端
- **shell / 终端 / 容器注入 `MAO_BASE_URL`**:终端 CLI(`mao` / `mao-agent`)不再内置默认站点地址后,云端执行环境必须显式告知本站地址,否则 Agent 里的 `mao` 命令会报「未配置站点地址」(改造前静默连开源示例 `https://mao.etarch.cn`,与私有化站点不同源,实际一直 401)。现在:宿主 shell 每次执行命令前、终端 PTY 创建时注入 `MAO_BASE_URL`(取系统设置「站点根地址」`site.baseUrl`,留空回落 `upload.baseUrl`;未配置则不注入;shell 侧站点地址带 60s 缓存避免每条命令查库);容器模式随 envelope 下发 `maoBaseUrl` 字段,容器 shell 与 MCP stdio 子进程 env 同口径注入;既有 `MAO_` 前缀保留键规则已覆盖,用户环境变量不能覆盖该值。

### 终端 CLI(mao-cli)
- **不再内置默认站点地址**:未设置(`--base-url` / `MAO_BASE_URL` / 兼容旧名都没有)时任何联网命令直接报错并给出两种设置方式,不再回落到开源示例域名;`auth logout` 未配置时跳过服务端调用、仅清理本地登录态,帮助文本同步更新。

### 桌面 Electron
- **LOCAL shell 注入 `MAO_BASE_URL`**:本地 shell(终端与 LOCAL 工具会话)除 `MAO_TOKEN` 外同时注入 `MAO_BASE_URL`,取桌面当前连接的服务器地址(未配置服务器时不注入,避免占位值被当成真实后端);持久会话每次执行前刷新,切换服务器后下一条命令即生效。
- **服务器设置记住历史连接地址,快速切换**:连接成功过的地址会存本地历史(最多 10 个,最近连接置顶),打开服务器设置时在输入框下方列出「连接过的服务器」,点地址即填入输入框(连同 HTTP 开关),再点「连接并保存」切换;当前地址标注「当前」,其余可一键删除。历史存在客户端 userData 的 `server-history.json`,与当前生效配置分开,删除历史不会影响正连的站点。

## 0.0.436 (2026-11-05)

### 终端 CLI(mao-agent)
- **不再内置默认站点地址(baseUrl),首次登录要求先设置**:此前未配置时静默回落到开源示例 `https://mao.etarch.cn`,私有化部署装完首次登录会打到不可达域名、报错难懂。现在 `mao-agent` 无内置默认:`login` 在 TTY 下发现未配置(`--base-url` / `MAO_BASE_URL` / 项目 `.mao/agent.json` / 用户 `~/.mao/agent-cli/config.json` 都没有)会提示输入站点地址——校验必须 `http(s)://`、只填站点根(可带 `/api`、`/api/v1`,不接受额外路径 / query / hash),通过后保存到用户配置再继续登录;非交互场景(CI / 管道)与其它需要连接服务器的命令(`ls` / `resume` / chat)直接报错并给出三种设置方式,`logout` 仅做本地清理不受影响;`status` 在 baseUrl 行显示 `(未设置)`,`update` 不依赖站点、不受影响。登录成功时若地址仅来自 `--base-url` / `MAO_BASE_URL`,追加提示如何写入配置以便下次沿用。`mao-cli` 行为不变(仍回落示例域名)。

## 0.0.435 (2026-11-05)

### 后端
- **运行时容器 locale 统一为 C.UTF-8**:docker 模式下用户容器此前未设置 `LANG`(镜像 env 与 `docker run` 均无),libc 回落 POSIX/C locale——`wc -m`、`grep -i`、字符类等 locale 敏感工具对中文等非 ASCII 内容按字节处理,与宿主机模式(`en_US.UTF-8`)行为漂移(实测 `wc -m` 统计「中文abc」得 9 而非 5,`grep -i` 非 ASCII 大小写不匹配)。现在 `docker run` 显式注入 `LANG=C.UTF-8`(Bookworm 镜像自带 C.utf8,零额外装包/无需重建镜像),容器内 UTF-8 多字节语义与宿主机对齐;存量容器需重建后生效。

## 0.0.434 (2026-11-05)

### 后端
- **修复定时任务「排队中」长期不动:IM 通道执行收尾不消费平台排队消息**:定时任务在会话忙碌时按设计入 `message_queue` 排队,但该队列的消费入口只在 WS 侧(执行收尾的 `autoConsumeQueue` / 手动插队 / 崩溃恢复)。飞书/钉钉/微信入站执行有自己的执行路径,收尾只排空各自的入站队列、从不消费 `message_queue`——当该会话后续只有 IM 通道执行(用户在群里对话)时,排队消息永远停在 `QUEUED`(管理后台一直显示「排队中」,24h 后才被安全网标「长时间未消费」取消,内容不会执行)。现在:IM 入站队列排空且会话空闲后按会话归属者接力消费 `message_queue`(`relayToIm=false` 防两条链互相传递),WS 侧平台队列排空后同样接力消费飞书/钉钉入站队列;跨通道忙检查互可见(IM `isSessionExecuting` / WS `registerExternalBusyCheck`),且「检查-占位」在同一同步段原子完成,避免两套执行簿记双跑;`autoConsumeQueue` 增加函数级重入保护与 DB phase 复查,IM 队列领取占位提前到 `claimNext` 之前,失败语义保持「不自动消费下一条」。
- **跨通道忙判定并入定时任务直跑与 WS 手动入口**:定时任务「直跑 vs 排队」判定、WS 手动发送/编辑重发/重试、开放接口直跑统一改用合并忙检查(WS 执行或 IM 通道执行占用)——IM 执行期间这些入口一律让行(定时任务走排队,后续由 IM 收尾接力消费),堵住「飞书执行刚开始、DB phase 尚未置 RUNNING」窗口内的跨通道双跑。

## 0.0.433 (2026-11-04)

### 桌面 Electron
- **服务器设置保存成功后自动关闭窗口**:原保存成功仅显示「已连接」绿色文案,独立的「Mao · 服务器设置」小窗口会一直停留,用户需手动点红点关闭。现在保存成功后提示「窗口即将关闭…」,800ms 后自动关闭该独立窗口;首次启动等嵌入主窗口的场景不关闭主窗口(主进程实际无此窗口可关,调用无副作用)。

## 0.0.432 (2026-11-04)

### 后端
- **修正哨兵新旧值片段迁移撞号**:该迁移与群聊运行身份同为 V183,Flyway 遇到重复版本会直接拒绝启动;若按文件名先执行先看到的那一个,片段列会被静默跳过。片段迁移改号为 V185(V183 仍是 `session.last_runtime_user_id`),下次启动补执行。

## 0.0.431 (2026-11-04)

### 后端
- **信号哨兵轮询记录新增新旧值片段**:`signal_watcher_check` 新增 `old_value_snippet` / `new_value_snippet`(V185,与触发 prompt 的 `{diff}` 同为 500 字截断口径),在判定值算出的同一点一次写入——旧值取 runCheck 开头那份快照(与比对基准同一份,不用触发前复读值)。watchers 读端点透出 `oldValueSnippet` / `newValueSnippet`,哨兵行新增 `last_change_old_snippet` / `last_change_new_snippet`(V184)供列表卡「上次变化」一句;这两列只在触发成功时与上次变化时间一起写,触发失败不改它们(列表卡仍是上一次成功触发,本轮新旧值只留在该条轮询记录)。基线重置 / 重新启用时清空这两列。含一项存量缺陷修复:编辑哨兵的基线重置此前静默丢弃 `lastValueSnippet` 清空,片段永远清不掉。管理后台列表同时透出 `jsonPath`,JSON 字段模式的「上次变化内容」带上取值路径。

### 前端(桌面 / Web / 安卓)
- **信号哨兵轮询记录「详情」列值人读化**:`changed` 行显示「旧值 → 新值」(与触发会话 `{diff}` 同三模式文案),未变行显示新值,error 行优先展示错误,其中「触发失败」行(变化已发生但触发失败)在错误下换行附新旧值对;上线前历史行两列为 NULL 时回落 `—`。哨兵列表卡在「上次变化:时间」旁新增一句旧值 → 新值,与该时间同为上一次成功触发(触发失败不改这句);从未变化 / 基线重置后整句不渲染。
- **定时任务「最近运行」失败行内联展示错误摘要**:桌面端「最近运行」展开后,失败行的错误原因(后端已写入的 `errorSummary`,如「会话已删除」「预算不足,执行被准入闸门阻止」「排队执行失败」)直接显示在行内(次级字色、超长省略,悬停看全文),不必再点进行运行绑定的会话深挖;无原因时该行布局与改动前完全一致,不留空位。

### 管理后台
- **信号哨兵「轮询记录」Tab「详情」列同桌面端人读化**,并在列表新增「上次变化内容」列显示同样的旧值 → 新值(一句,JSON 字段模式带上取值路径);从未变化 / 已重置基线时显示 `-`。触发失败行在错误文本下换行附新旧值对。

## 0.0.430 (2026-11-04)

### 后端
- **修复审批 denylist 两处绕过(安全)**:① 词匹配增加 basename 归一——命令切词刻意不切 `/`(保护 `rm-cache`/`docs/su/` 不误伤)同样放过了 `/bin/rm`、`/usr/bin/sudo` 这类合法可执行路径,模型用绝对路径即可让裸词 denylist 静默失效;现在 token 与其 basename 都参与比对(已知代价:`cat docs/rm` 这类「文件名恰为受保护词」的命令会多弹一次审批,误伤不静默、方向安全)。② denylist 扫描对象改为**未截断**的归一化全命令——原先扫的是 512 截断后的 `rule_value`(截断只服务于存储宽度),`echo <600 字符> ; rm -rf /` 尾部危险命令整个绕过。`match()`(规则自动放行)与 `buildApprovalHint`(审批卡「总是允许」按钮)同步修正;用户手建规则链路本就按 ≤512 校验,不受影响。
- **修复兜底价格缓存把「查库异常」当「模型不存在」缓存 60s**:`llm-call` 兜底价格查询原先无论成败都写 60s TTL 缓存——一次 DB 抖动会让该模型随后 60s 的调用全部 `cost_micros=NULL`,`SUM` 忽略 NULL 行导致当期消耗被低估、COST 类预算 BLOCK/WARN 静默穿透。现在只有成功查到(含模型不存在的负缓存)才写缓存,异常分支不写、保留重试机会。
- **修复环境变量缓存把「查库失败」缓存为空数组**:用户 / 工作区 / 系统三层变量加载原先 catch 后仍把空结果写进 5s TTL 缓存——一次 DB 抖动会让其后 5s 内新建的 shell / 容器会话注入不了 API key,命令以「缺凭据」失败(间歇性、难归因)。现在失败分支只 fail-open 返回 `[]`、不写缓存;成功路径的缓存与主动失效语义不变。
- **grep_search 负 `context_lines` / `max_output_chars` 不再静默返回「0 命中」**:负数上限会让首个条目即触截断判断,工具返回干净的 `matches: []` 且无 error,模型误判「无结果」继续推进(搜错关键词 / 写错文件)。现在负 `context_lines` 或 `<1` 的 `max_output_chars` 直接返回参数错误,与 `pattern` 缺失回 error、ripgrep 失败不吞成 0 命中同一口径。
- **后端进程时区对齐 Asia/Shanghai**:全站时间口径本是上海(日志时间戳、`common/json.ts` 序列化、DB 连接 `+08:00` 及 `CURRENT_TIMESTAMP` 落库、用量周期窗口注释),但 systemd 单元不注入 TZ、宿主时区未声明,预算月度窗口与 `nowSql()` 靠进程本地 getter——宿主非 +08:00 时月初 8 小时窗口内预算整体偏移一个月(误 BLOCK / 已超额预算「复活」一个月),本地时间列与 +08:00 落库列混排。现在入口(`main.ts` 最早处)对齐进程 TZ,与宿主时区解耦(先例:runtime-agent 容器入口同款对齐)。

## 0.0.429 (2026-11-04)

### 前端(桌面 / Web / 安卓)
- **信号哨兵「轮询记录」弹窗「触发/告警」列空值显示「-」占位**:该列展示本次轮询的后续动作(触发会话链接、告警标记),内容未变化的轮询两者皆无时桌面端整格空白,与管理后台已有的「-」占位不一致,易被误认为列异常。现在桌面端同样补上「-」,与管理后台对齐。

### 后端
- **群聊会话运行身份对齐:任一成员发言,技能 / CLI / 产物 / 记忆都跟随发言者执行**。修复「提示词里的技能路径按会话归属者拼装、工具却在发言者容器里执行」导致的线上缺陷(`read_file` 技能文档报 `Path escape attempt`、技能 CLI 缺失、任务实质不可执行;docker 模式下群聊非归属成员发言必现)。统一身份口径为 **运行身份 = 本次发言者 ?? 会话归属者**(`executionUserId ?? session.userId`):技能同步与技能清单、提示词技能路径上传目录与用户命令、`dump_user_messages` / `open_web_page` 缓存 / 生成与改图产物 / 飞书钉钉微信发送工具、记忆注入与抽取、子代理与崩溃恢复、失败卡重试均改按运行身份;提示词在身份不一致时新增「执行身份」段落(私聊 / 普通会话零变化),容器与宿主侧跨成员路径错误给出可执行的专属文案(他人 `uploads` 产物提示公开 URL 转发,他人 `runtime` 目录提示不可读且无 URL)。会话归属(`session.user_id`)继续用于权限、审计与订阅;技能集合随发言者变化属预期行为(按人授权)。
- **新增开关 `MAO_RUNTIME_IDENTITY`**(默认 `execution`;`owner` = 应急回退到「按会话归属执行」,重启生效);**V183 迁移**新增可空列 `session.last_runtime_user_id`(崩溃恢复 / 子代理继承 / 重试的事实源,不回填),随 TS 启动自动执行,无数据修复。容器无需重建——容器侧新增的跨成员诊断文案随 runtimeBuild 指纹漂移在容器空闲时自动重启加载(`autoUpgrade` 关闭时需手工重启容器)。
- 隔离边界不动:容器挂载清单、镜像与沙箱允许根零改动;MCP 连接仍按会话归属(已知差异)。

## 0.0.428 (2026-11-04)

### 管理后台
- **定时任务管理「状态 / 完结」合并为单一状态列与筛选项**:列表原「状态(启用/暂停)」与「完结(进行中/已完结)」两列合并为一个三态「状态」列——启用(绿)/ 暂停(灰)/ 已完结(悬停显示完结时间);完结为系统置位的终态,优先于启/停展示(完结后启/停开关本就禁用)。筛选区两个下拉同样合并为一个「状态」下拉(全部 / 启用 / 暂停 / 已完结),前端映射到后端既有参数(启用=`ACTIVE`+未完结、暂停=`PAUSED`+未完结、已完结=`finished=true`),后端无改动;详情弹窗仍分开展示启/停与完结时间,信息不丢失。

## 0.0.427 (2026-11-04)

### 管理后台
- **修复会话详情工作区 PDF 预览在微信等内置浏览器里白屏**:预览原先用 `<iframe>` 直接指向 PDF 的 blob URL,依赖宿主浏览器的原生 PDF 渲染——桌面 Chrome / Edge / Firefox 正常,但微信内置 WebView(iOS WKWebView、Android X5)对 blob URL 的原生 PDF 支持不完整,表现为预览区整块空白、没有任何提示,用户只能误以为文件坏了。现在改用 pdf.js 把 PDF 栅格化到 canvas 自渲染(与桌面端同一方案),不再依赖宿主能力,各端表现一致;同时补齐缩放、适合宽度、实际大小、页码指示与滚动按页加载。PDF 解析失败或字节流为空时会给出明确错误与「下载后查看」提示,不再静默白屏。

## 0.0.426 (2026-11-04)

### 后端
- **容器模式并行工具调用不再提前清空请求信封(envelope)**:`container-tool-executor` 的 `currentEnvelope` 增加在途调用引用计数,同一批并行工具调用全部结束后才清空(`handleReadBytes` 同口径)。修复前每次调用结束都无条件清空——同批先完成的快工具(`read_file` 等)会把仍在执行中的 `shell` 调用所需的 envelope 清掉,导致其创建/刷新会话时读不到 `depsEnv`:PATH 缺 `deps/{uid}/npm/bin`,容器内**已安装的 CLI 间歇性 `command not found`**;`MAO_TOKEN` / ECP / larkUat 注入同源偶发丢失。仅容器模式受影响(host 模式 depsEnv 为纯函数、无共享状态);存量容器在用户下次调用工具时按既有 build drift 机制自动重启进程后生效,无需人工重建。

## 0.0.425 (2026-11-03)

### 后端
- **技能视图(Skill View):容器技能挂载收口,技能源不再进容器**:容器内技能可见面从「全员全量共享技能」收敛到「该用户被授权的技能」——会话技能软链(`{runtimeDir}/{uid}/{sid}/skills/{name}`,提示词与工具使用的路径不变)由指向 `SKILLS_DIR/{name}` 改为指向新的**用户级只读技能视图** `{skill-views}/{uid}/{name}`;容器挂载由全局可写 `skills`(rw)改为按用户 `skillViews`(ro),容器内不再存在 `/opt/mao-data/skills`,对视图目录的写操作返回只读错误(EROFS);任一用户容器也无法再修改共享技能源。新增配置 `SKILL_VIEWS_DIR`(默认 `./skill-views`,**生产部署须在 `backend-ts/.env` 补配 `/opt/mao-data/skill-views`**,漏配会回落到后端 cwd 下的相对目录)。视图按需物化:源(部署 rsync / 管理后台上传 / 部署侧直写源目录)指纹变化时,在用户下次执行会话前原子重建(路径不变,`npm install . -g` 的 CLI 链接永不悬空);源侧技能删除时清理视图;视图元数据(`.meta/{uid}`)在挂载根之外,容器内视图目录只含技能;用户技能(`userskills`,同名覆盖系统技能)与本地(LOCAL)模式链路不变。
- **部署注意**:挂载清单指纹变化会触发存量容器在用户下次调用工具时自动重建一次(一次性代价,无需人工干预);「容器内安装/更新共享技能」能力随本改造取消(产品确认无真实场景),共享技能更新走部署 / 管理后台 / `skill-docs` API;回滚只需回退代码重新部署(软链会自动指回源、容器再次重建)。

## 0.0.424 (2026-11-03)

### 后端
- **一次性定时任务在排队待执行时重新启用不再重开档期**:会话忙导致本档已入队(`QUEUED`)后,暂停再启用不会再排下一档,避免同一一次性任务被调度器和队列各跑一次。
- **5 段 cron 的固定月日也会识别为一次性任务**:`0 8 15 8 *` 与 `0 0 8 15 8 ?` 是同一触发点,创建和更新都会落成 `once=1`,执行一次后完结,不会次年同一天再触发。
- **创建或更新定时任务时拒绝永远排不出下次触发的表达式**:如 2 月 30 日,与预览接口一致返回参数错误,不再留下「进行中」但永远不执行的任务。
- **信号哨兵触发失败会累计连续失败**:预算拦截、Agent 停用或会话创建失败不再把连败清零,连续 5 次后自动停用并通知。
- **信号哨兵重新启用后马上恢复轮询**:从停用或退避中的暂停切回启用时清空上次检查时刻,不再被失败退避写成的未来时间挡住(日更最长约 17 天)。编辑判定字段重置基线时同样清空。凭据解密失败按本轮失败处理,不再丢掉鉴权头照发探针。
- **Git 提交信息与文件预览按字符边界截断**:多字节字符落在字节上限上时不再留下乱码替换符,生成提交信息和大文件预览不再因此超出上限而失败。
- **用量自定义周期拒绝非法日历日**:`2026-02-31` 这类日期按非法入参回落默认周期,不再把非法日期字面量下发到 SQL。

### Embed SDK
- **浮窗入口按钮缩小为原来的 50%**:右下角浮动入口(含管理后台内置的助手浮窗)由 56px 缩至 28px,窄屏(≤480px)由 52px 缩至 26px;品牌图标 / Agent 头像满铺按钮随之同比缩小,运行环、未读红点、拖动避让同步适配,点击打开对话的行为不变。

### 管理后台
- **会话详情工具调用不再裸显英文工具名**:`dump_user_messages`(导出用户消息)、后台子代理系列、飞书 / 钉钉收发文件图片、`page_*` 页面工具、`mcp_call` / `http_call` 等此前在管理后台直接显示内部英文名(桌面端一直正常),现与桌面端同口径显示中文名;参数预览同步按工具名还原,如导出用户消息显示「最近 N 条」、MCP 调用显示 `server.tool`、历史消息里的 `mcp__server__tool` 还原为 `server.tool`。

### 前端(桌面 / Web / 安卓)
- **修复信号哨兵弹窗点击「添加请求头」后窗口自行关闭**:新建 / 编辑 / 复制哨兵弹窗基于 Element Plus 的 `el-form`(渲染为原生 `<form>`),其中的裸 `<button>` 未声明 `type`,浏览器默认按提交按钮处理,点击即触发表单提交、整页重载——打包后 Electron 走 `file://` + hash 路由,hash 在提交跳转中丢失,表现为窗口自行关闭、页面重置;「添加请求头」与请求头行内删除按钮均已显式声明 `type="button"`,点击仅增删行。

## 0.0.423 (2026-11-03)

### 后端
- **会话来源新增「桌面端 / 终端 CLI / 安卓」三种客户端形态**:`session.source` 闭集扩为十值,`POST /v1/sessions` 接受 `web / embed / desktop / cli / android` 五个客户端自报值(系统来源仍不可自报,`PUT /v1/sessions/:id/source` 维持 embed-only,两层校验文案统一)。边路任务、子代理与边路提升主会话改为继承父会话来源,不再一律落 Web。V181 把存量本地模式会话的来源订正为「桌面端」(含 `mao-agent --local` 建的会话——两者无字段可区分);存量云端会话无法区分桌面 / CLI / 浏览器,仍一律记为 Web。

### 管理后台
- **会话列表「来源」列与筛选新增「桌面端 / 终端 CLI / 安卓」**:Web 口径收窄为浏览器;三个新值分别用 primary / info / success 色调。
- **系统设置新增「菜单可见性」**:管理员可按部署场景隐藏不用的功能菜单(例如某实例不接钉钉,就藏掉「钉钉机器人」)。配置为部署级全局,所有账号一致;侧栏、窄屏抽屉与 ⌘K 菜单搜索同步生效,保存后当前会话即时刷新,无需 F5。隐藏只影响菜单展示——路由、权限校验与直接输入网址访问都不变,如需收回访问权限请使用「角色权限」。「系统设置」不可隐藏(后端同步拒收),面板内「恢复默认(全部显示)」可一键还原;隐藏「用量分析」等首页候选页后,登录落地、访问后台根地址、Logo / 返回首页与顶栏不可关闭的首页签都会落到下一个可见且有权限的页面。

### 桌面 Electron
- **安装后的默认服务器地址改为 `https://mao.example.com`**:还没有 `server-config.json`、也没有 `MAO_DESKTOP_SERVER_URL` 时,首次配置页预填该地址。已经保存过服务器的客户端保持原地址。

### 前端(桌面 / Web / 安卓)
- **顶栏「我的工作区」文件夹入口暂时隐藏**:该功能打磨中、暂不面向用户开放,顶栏最左侧的文件夹图标不再展示;中央页签本身及各目录的文件管理 / 新任务能力保留,恢复入口即可再用。
- **桌面端与安卓 App 新建会话自报客户端形态**:Electron → 桌面端、安卓 Capacitor → 安卓、浏览器 → Web,管理后台可按形态筛出对应会话;WS 握手与建会话共用同一份形态判定。

### 终端 CLI(mao-agent)
- **新建会话自报来源 `cli`**(交互建会话与 `createAndAttach` 两条入口一致),管理后台可筛出「终端 CLI」会话;依赖后端 ≥ 本版本,旧后端会 400 拒绝。

## 0.0.421 (2026-11-03)

### 后端
- **飞书群新会话标题按完整消息生成**:此前标题命名只喂入站原始文本,在群里用裸 @机器人 触发任务时(真正的需求写在更早的群消息里),标题会退化成「提及梁子」这类没有信息量的标题。现在标题素材与 Agent 执行所见一致——含【群内最近消息】【引用的消息】【用户消息】的完整文本;标题模型也会在用户消息仅为 @提及时,从群上下文里提炼实际要办的任务。

### 前端(桌面 / Web / 安卓)
- **Diff 查看器「并排」在窄中间栏也会真正左右分栏**:三栏都开着时中间区常常不到 900px,Monaco 会无视「并排」把视图强制收成行内,切换看起来没有变化。现在胶囊上的选择始终生效;视口窄于 640px 且从未切换过时仍默认行内,切换后的偏好照旧记住。

## 0.0.420 (2026-11-03)

### 管理后台
- **模型列表不再展示「关联 Agent」列**。

## 0.0.419 (2026-11-03)

### 管理后台
- **Agent 列表「能力概览」省略数量为 0 的类型**:只显示大于 0 的项,例如「技能 3」、「技能 3 · MCP 1」;三项都为 0 时显示「—」。悬停仍列出技能、MCP 与自定义工具的具体名称。

## 0.0.418 (2026-11-03)

### 前端(桌面 / Web / 安卓)
- **快捷指令搜索改到页签右侧**:「设置 → 我的指令」和顶栏指令抽屉里,搜索框不再贴在「新建指令」旁边,改为放在「我的指令 / 系统指令」页签行右侧。两个页签共用这一处搜索,切换时位置不跳动;窄屏时搜索框落到页签下方并拉满一行。

## 0.0.417 (2026-11-03)

### 后端
- **审批规则支持查看、删除与提升会话级规则**:`GET /v1/approval-rules?scope=SESSION` 返回本人全部会话级规则并带出所属会话标题(`sessionTitle`);`DELETE /v1/approval-rules/:id` 放宽为本人名下 `USER` / `SESSION` 规则皆可删(撤回会话里点错的「总是允许」,PATCH 编辑仍仅限用户级);新增 `POST /v1/approval-rules/:id/promote` 把会话级规则一键复制为用户级规则(重过归一化与受保护命令 denylist 校验,同值幂等去重,原会话级规则保留)。删除只让下一次同类命令重新弹审批卡,不影响在途请求。
- **环境变量编辑支持「留空不修改值」**:`envValue` 未传 / 空串一律保留原密文,仅显式非空字符串才覆盖(用户级 / 工作区级 / 系统级同口径),与 Git 凭证、MCP 环境变量「留空则保留原值」完全统一——改备注不必再重输密钥。
- **长期记忆列表新增关键词过滤**:`GET /v1/memory?keyword=` 对 `content` / `project_key` 做包含匹配(`%` / `_` 等通配符按字面转义),可与 scope / status 筛选叠加。

### 前端
- **「设置 → 审批规则」新增「用户级规则 / 会话级规则」二级 Tab**:会话级规则卡片展示规则值、来源会话(可点击跳回该会话,已删除则提示)与放行次数,支持「提升为用户级」与「删除(恢复审批)」;页面新增规则值关键词过滤框(两个 Tab 均可用,搜索时按命中排序取前 100 条过滤并隐藏分页)。
- **「设置 → 环境变量」与右侧「任务」面板工作区变量编辑免重填密文**:编辑态变量值留空即可直接保存备注,占位文案统一为「留空则保留原值;输入新值则覆盖」。
- **「设置 → 长期记忆」新增搜索框(300ms 防抖)**:按内容或项目关键词过滤,百条自动记忆里可快速定位;自动沉淀的记忆卡片显示「来源 #会话ID」链接,点击跳回原会话(已删除则提示)。
- **快捷指令管理新增搜索框**:按名称与内容即时过滤(我的指令 / 系统指令两个 Tab 通用),无匹配时区分空态文案。

## 0.0.416 (2026-11-03)

### 后端
- **自定义工具试运行支持沿用已存密文**:`POST /v1/admin/http-tools/test` 新增可选 `toolId` 参数——含加密请求头(如 `Authorization`)的已存工具不必重填明文 Token,传入 `toolId` 后掩码头(`******`)自动从库内密文解密回填实调;未传 `toolId` 时仍拒绝掩码(口径不变)。测试响应只含目标接口的 HTTP 状态与响应体,绝不回传请求头明文。
- **自定义工具新建支持复制继承密文**:`POST /v1/admin/http-tools` 新增可选 `copyFromId` 参数,掩码头从源工具已存密文沿用(与编辑保存同一套 `encodeHeaders` 掩码语义),用于同一内部网关下快速克隆第 2 个接口。

### 管理后台
- **自定义工具页**:列表「测试」按钮不再因含加密请求头而禁用(随时可测);操作列新增「复制」,预填新建弹窗(工具名自动加 `_copy`、展示名加「副本」),保存时自动继承源工具密文;新增「关联 Agent」列,悬停列出引用该工具的 Agent,点击直跳。
- **模型 / MCP 服务器列表**:新增「关联 Agent」列,停用或删除前一眼看清影响面(Skill 列表的关联列同步升级为悬停详情)。
- **Agent 列表**:新增「默认模型」列(未配显示「跟随全局默认」);原 Skills 计数列升级为「能力概览」(技能 · MCP · 自定义工具数量,悬停列出具体名称);筛选栏新增「按模型筛选」。

## 0.0.415 (2026-11-03)

### 后端
- **定时任务支持手动「立即执行」**:新增 `POST /v1/scheduled-tasks/:id/run`(属主或持 `scheduled-task:write` 可调),在绑定会话中立即试跑一次任务,同步返回触发凭据 `{ taskId, sessionId, fireTime }`、执行异步进行。核心不变量:不改写下次触发计划(`nextFireTime`)、不把一次性任务标为已完结、失败不自动重试也不计入连续失败自动暂停;`PAUSED` / 已完结任务允许试跑且跑完保持原状态。运行记录照常写入(`attempt=0` 标记手动触发,与周期触发行区分,崩溃收敛不会误完结一次性任务);任务在飞或绑定会话正忙时返回明确错误(code 3055)提示稍后再试。
- **Git 提交历史支持按提交信息搜索**:`GET /v1/files/workspace-git-log` 新增 `keyword` 参数(`--grep` 字面匹配、大小写不敏感,截断 100 字符),可与既有 `path` 文件路径过滤叠加使用;空关键词行为与原先完全一致。
- **提交文件清单接口返回提交正文**:`GET /v1/files/workspace-git-commit` 新增 `body` 字段(完整提交说明去除首行标题后的正文),展开单条提交时可见多行说明。
- **Git 分支切换支持基于当前 HEAD 新建本地分支**:`POST /v1/files/workspace-git-switch-branch` 在 `create=true` 且为普通分支名时执行 `git switch -c <name>`(此前仅支持从远端分支建跟踪分支);与已有本地分支重名时返回「分支已存在,请换一个名字」,首字符 `-` 的分支名明确拒绝。

### 管理后台
- **左侧菜单重做:新增菜单搜索与分组折叠**:菜单项已有 22 个,平铺列表切页要来回滚动找入口。现在侧边栏顶部提供「搜索菜单」框(快捷键 ⌘K / Ctrl+K 聚焦),输入即跨分组过滤并高亮命中文字,↑↓ 选择、回车直达、Esc 清空,无结果时给出空态;侧栏收起为图标窄栏时搜索框变为搜索按钮,点击展开并聚焦。「能力 / 运行 / 安全 / 系统」四个分组标题可点击折叠(带菜单数徽标与展开动画),折叠状态记住偏好,当前页面所在分组被折叠时会自动展开,保证高亮项始终可见。菜单视觉同步打磨(统一行高与圆角、搜索框焦点环、hover 显细滚动条、触屏隐藏快捷键提示),移动端抽屉同样支持搜索。

### 前端(桌面 / Web / 安卓)
- **定时任务面板行内「立即执行」**:每行操作区(编辑与删除之间)新增「立即执行」按钮,二次确认文案「立即在绑定会话中执行一次?不会改变原有的下次触发计划」;触发成功 toast 并自动展开该任务「最近运行」刷新进度;「最近运行」中手动触发的行显示「手动」徽标,可点击跳回绑定会话查看效果。
- **文件预览工具条补完(文本 / Markdown / 图片)**:右上角统一工具条提供「预览/源码」(Markdown)、「自动换行」(一键切换长行折行,偏好记忆下次沿用)、「复制全文」、「引用到对话」(把文件路径插入聊天输入框)、「下载」与「刷新」;图片预览同样可引用 / 下载 / 刷新,不再是没有操作入口的孤岛。
- **Diff 查看器支持「并排 / 行内」切换与变更跳转**:右上角浮动胶囊一键切换并排与行内视图(窄屏首次默认行内、切换后记忆偏好),显示「共 N 处变更」计数,`↑` / `↓` 在各处变更间循环跳转;三栏全开窄屏下代码不再被挤成两条缝。
- **Git 管理器历史视图体验闭环**:提交行与文件行 hover 出现「添加到对话」(提交说明以引用、文件路径以 @file 插入聊天输入框);顶部新增「按文件路径筛选」与「按提交信息搜索」输入框(防抖、可叠加、「加载更多」沿用筛选);展开提交时显示完整提交正文。
- **分支视图支持新建本地分支**:本地分支分组头部「+ 新建分支」输入合法分支名即基于当前 HEAD 创建并切换,非法分支名实时拦截,重名给出明确提示。

### 终端 CLI(mao-cli)
- **`mao scheduled-task run --id <id>`**:与信号哨兵 `test` 体验对齐,命令行手动触发一次定时任务;返回触发凭据,配合 `runs` 子命令查看进度与结果。他人任务需 `scheduled-task:write`;手册 `reference/scheduled-task.md` 同步更新语义说明与错误码。

## 0.0.414 (2026-11-03)

### 后端
- **新增本地账号自助修改密码接口**:`PUT /v1/users/me/password` 登录态即可调用(无需 `user:write`),校验当前密码与新密码(8~64 位含字母和数字、新旧不同、两次输入一致);仅本地密码账号可用,LDAP / 飞书 / ECP / SSO 账号调用时明确提示「请前往对应平台修改」。改密成功仅更新密码,不踢出已登录设备(与管理后台代改密同口径)。
- **出站订阅新增测试投递接口**:`POST /v1/open/subscriptions/:id/test`(仅属主)用该订阅真实 Secret 同步发送一条带 `test: true` 标记的示例事件(HMAC 签名头与真实投递完全一致),返回 `{ success, httpStatus, error, durationMs }`;不写入投递历史、不触发重试,停用订阅也可测试。

### 管理后台
- **顶栏「修改密码」补上当前密码校验**:改为调用自助改密接口,新增「当前密码」输入框,移除「服务端未提供自助改密接口」的提示横幅;无 `user:write` 权限的普通后台角色也能修改自己的密码(菜单入口改为仅本地密码账号可见)。

### 前端
- **「设置 → 个人信息」新增修改密码入口**:本地账号在资料卡片下方点「修改密码」,输入当前密码与新密码即可自助改密,不必再找管理员代改。
- **「设置 → 开放接口」Webhook 触发器支持就地编辑**:卡片新增「编辑」按钮,可改名、更换 Agent、换绑或解绑会话;触发器 URL 与 Secret 保持不变,外部系统无需重新配置(此前只能删除重建)。
- **「设置 → 开放接口」出站订阅新增「测试投递」**:配好 HTTPS 回调地址后一键发送测试事件,toast 展示 HTTP 状态码与耗时,立即验证签名与连通性,不必等真实任务跑完。

## 0.0.413 (2026-11-02)

### 后端
- **管理后台信号哨兵新增只读详情与轮询记录接口**:`GET /v1/admin/signal-watchers/:id` 返回单个哨兵的完整配置与属主展示名,脱敏口径与用户侧一致(敏感请求头打码、请求体只回「是否配置 + 字符数」,不下发明文);`GET /v1/admin/signal-watchers/:id/checks` 按页返回轮询记录(时间 / 状态码 / 是否变化 / 耗时 / 触发会话 / 错误摘要 / 告警)。两者均要求 `signal:read`,哨兵不存在时返回「哨兵不存在」。

### 管理后台
- **「信号哨兵」可以查看配置详情和轮询记录了**:列表新增「详情」操作,抽屉分「配置详情」「轮询记录」两个 Tab。配置页展示 URL / 方法 / 间隔 / 判定模式 / JSON 路径 / 触发方式 / Prompt 模板 / 请求头(打码)/ 请求体(只显示长度)等完整配置;记录页按 20 条分页展示每次探针结果,触发产生的会话可直接点进会话详情(需 `session:read` 权限)。管理后台保持只读,不提供编辑 / 暂停 / 删除。

## 0.0.412 (2026-11-02)

### 管理后台
- **「开放调用」「信号哨兵」「自定义工具」的分页布局与其他列表页统一**:这三页的分页组件此前没带布局样式,分页条靠左、紧贴表格底边;其他列表页都是右对齐并与表格留出上间距,看起来像两套风格。现补上 `margin-top: 16px` 与右对齐,与用户、Agent、审计日志等页面一致。同时全量核对了后台其余 16 处分页,均有正确的右对齐与上间距,无同类问题。

## 0.0.411 (2026-11-02)

### 管理后台
- **顶栏显示当前登录用户头像**:右上角此前固定是默认占位图标——桌面端已支持展示的用户头像(本人在「设置 → 个人信息」上传,或飞书 / ECP / 公司 SSO 首次登录同步)在管理后台看不到,因为顶栏的头像组件没接用户数据。现在与桌面端同源展示 `/users/me` 的 `avatarUrl`(`/uploads/` 相对路径按当前站点解析);未设置头像时回落为默认图标。

## 0.0.410 (2026-11-02)

### 后端
- **修复数据库瞬断导致后端进程整体崩溃**:LLM 流结束后,会话上下文锚点与助手消息的落库属于「旁路写入」——先入 `afterStream` 队列、等流适配器返回后才统一 await。若数据库在此期间断开(如 MySQL 主备切换产生的 `ECONNRESET`),队列里的写入会在挂上处理函数之前失败,被 Node 判定为 unhandledRejection 并按默认策略终止进程,所有会话随之中断。现在旁路写入入队即挂 `catch`,失败只记一条 warn 日志,不再影响主流程与进程存活。2026-11-02 16:25 的 MySQL 瞬断即由此导致后端中断约 7 分钟。

## 0.0.409 (2026-11-02)

### 后端
- **角色权限可以从角色侧批量管理成员了**:新增 `POST /v1/roles/:id/users`(批量添加成员)和 `DELETE /v1/roles/:id/users`(批量移除成员),都要求 `role:write`。两个操作都幂等——给已加入的用户再添加、移除没绑定的用户都不报错,按实际生效数量返回;单次最多 100 个用户,用户 id 会去重并校验存在,角色不存在时明确报「角色不存在」。从「系统管理员」角色批量移除成员带最后管理员保护:移除后一个活跃管理员都不剩会被拒绝,防止把系统锁死。批量写入在同一事务内完成,中途失败不留半截状态。

### 管理后台
- **「角色权限」的「成员」按钮改为打开成员弹窗**:以前点「成员」只是跳到用户管理按角色筛选,要加成员还得去用户管理一个个改角色。现在弹窗里直接看当前成员数和用户列表(行内标注「已加入」),能按关键词搜索、分页,勾选用户后「添加为成员」或「移出角色」(移出有二次确认)。未加入的用户才能添加、已加入的才能移出,操作成功后成员数实时刷新。

### 终端 CLI(mao-cli)
- 管理后台手册同步:角色权限的「成员」改为弹窗,可批量添加 / 移除成员;按角色浏览完整用户列表仍走用户管理的角色筛选(`mao user list --role-id <id>`)。

## 0.0.408 (2026-11-02)

### 后端
- **回填飞书通道历史会话的来源标记(V180)**:V109 给 `session` 加 `source` 列时默认 `web`,而飞书建会话补写 `source='feishu'` 是 0.0.381 才落地的——其间创建的飞书私聊 / 群聊 / 话题会话全被记成 Web,管理后台「来源」列与来源筛选把 1,058 条飞书会话(2026-08-28 ~ 10-30)算进了 Web。V180 只按飞书通道自己的映射表(`feishu_session_channel` / `feishu_chat` / `feishu_thread_session`)圈定,仅把仍为 `web` 的主会话(`session_type='NORMAL'`)改标 `feishu`(含 28 条已软删除行,共 1,086 行);不按 workspace / project_key 模糊匹配,同名工作区里由桌面端新建的会话保持 Web;子代理与边路任务的来源口径按各自创建入口,不在回填范围。迁移随 TS 启动自动执行,无新增环境变量。

### 终端 CLI(mao-cli)
- 管理后台手册「会话管理」来源列说明同步回填口径:0.0.408 起历史飞书会话不再显示为 Web,钉钉 / 微信等其它通道的早期会话仍保持 Web。

## 0.0.407 (2026-11-02)

### 后端
- **公司 SSO 首次建号带入用户头像**:换票时读取公司 checkToken 的 `data.claims.avatar`,按头像地址规则(http(s) 或 `/` 开头、无空白、≤512 字符)归一化后,仅在**首次自动建号**时写入 `user.avatar_url`;字段缺失、为空或不合法一律忽略,不影响换票结果。已有账号(绑定或再次登录)一律不覆盖用户自行维护的头像,与飞书登录不同步头像的策略一致。此前 SSO 用户的头像只能在 Mao 内自行上传。
- **ECP 飞书登录首次建号同样带入头像**:换票响应 `data.user.avatar` 按同一套头像地址规则归一化,仅在首次自动建号时写入;已有账号不覆盖。ECP renew 响应不含用户信息,不受影响。

### 终端 CLI(mao-cli)
- Embed SDK 公司 SSO 接入说明补充:claims 增加可选 `avatar`,只在首次建号时写入。

## 0.0.406 (2026-11-02)

### 前端
- **Web 飞书登录不再把已打开的授权页当成弹窗拦截**:`window.open` 带 `noopener` 时,新标签成功打开也会返回空。登录页据此提示「授权页面被浏览器拦截」并停止轮询,飞书里授权完成、回到原页面刷新都登不进去。现在先拿到新窗口再切断 `opener`,只有真正被拦截才提示。授权页打开后原登录页继续轮询,成功后自动进入。飞书 / 钉钉渠道绑定的授权页同样不再误报拦截。
- **授权完成后不再停在「飞书登录未完成」**:回调向飞书换票时,状态会先变成 `PROCESSING`。登录页把这个中间态当成结束并停止轮询,授权完成后页面就停在这句文案。现在只有成功、失败或过期才停止;换票期间显示「正在完成登录…」,成功后自动进入。管理后台登录页同样处理。
- **Web 飞书登录改为弹窗,授权完成后自动回到原页面**:不再新开一个标签让用户自己切回去。授权成功后弹窗关闭,原来的登录页收到结果并进入。用户中途关掉弹窗且授权尚未开始时,回到密码登录。安卓壳仍打开系统浏览器。管理后台登录页同样改为弹窗。
- 单测:打开成功返回 true 且 `opener` 被置空;返回空视为拦截;`opener` 不可写仍算打开成功;Electron 仍走 `openExternal`。`PROCESSING` 继续轮询,成功 / 失败 / 过期才结束。

### 终端 CLI(mao-cli)
- 桌面手册与排障补充:Web 飞书登录要保持原登录页打开,授权在新标签完成。0.0.406 前误报弹窗拦截、或授权完成后停在「飞书登录未完成」时,刷新原页面不会登入。

## 0.0.405 (2026-11-02)

### 后端
- **可以关闭桌面端账号密码登录**:系统设置新增 `auth.password.enabled`,默认开启。关掉之后,`POST /auth/login` 拒绝(本地密码和 LDAP 都走这个接口),桌面 / Web / 安卓不再显示用户名密码。`POST /auth/admin/login` 不看这个开关,管理后台始终可以用账号密码进入。已登录会话的刷新不受影响。保存为关闭时,必须已经启用飞书 OAuth(真实 App ID)或 ECP 飞书登录;反过来关掉这两者里的最后一个时,如果密码登录已经关了,也会拒绝,避免桌面端没有任何入口。
- 单测:没有其它登录方式时不能关;飞书 OAuth 可用时可以关,关了之后不能再关掉最后一个飞书入口;密码登录关闭时普通登录不查用户、不走 LDAP,管理后台登录仍可用。

### 管理后台
- **系统设置 → 登录认证**增加「账号密码登录」开关。这个开关不隐藏管理后台自己的用户名密码,admin 账号仍可从这里进入。
- 飞书 OAuth 与 ECP 同时打开时,登录页在密码按钮下方分成「飞书登录」和「飞书登录(ECP)」。

### 前端
- 桌面 / Web / 安卓登录页同样听从这个开关:关闭后只保留已启用的飞书按钮。接口没返回该字段时仍显示用户名密码。
- **登录页按钮改成同一套胶囊**:密码登录是唯一的实心主按钮。飞书登录改成线框次按钮,彩色飞书标直接放在文字左侧,不再用另一套蓝底方角按钮。两种方式都有时,中间用「或」分开。登录页底改成浅灰,卡片浮在上面。

### 终端 CLI(mao-cli)
- 配置、管理后台和认证说明同步:账号密码登录可关;`mao auth login` 在关闭后会得到「账号密码登录已关闭」。`auth features` 增加 `passwordEnabled`。

## 0.0.404 (2026-11-02)

### 前端
- **登录页飞书入口改为蓝底带图标的按钮**:ECP 文案由「ECP 飞书登录」改为「飞书登录(ECP)」。飞书 OAuth 的「飞书登录」同样从白底描边改成飞书蓝底,左侧放白色圆角底上的飞书标。授权页里的重试按钮同步这套样式。

### 终端 CLI(mao-cli)
- 桌面端进入方式补充:飞书 OAuth 与 ECP 的登录按钮都是带飞书图标的蓝底按钮,文案分别是「飞书登录」和「飞书登录(ECP)」。

## 0.0.403 (2026-11-02)

### 管理后台
- **系统设置可以按名称或 key 搜索并跳到所在分组**:配置分散在多级目录里,只能展开分组逐个翻。标题栏增加搜索,按中文名称、说明或配置 key 匹配(如「空闲超时」「SMTP」「harness.shell」),结果标出「分组 / 卡片」。回车或点击后滚到该项并高亮,同名项(Shell 的空闲超时、云端终端的空闲回收)能直接看出各自在哪一组。跳转后搜索框保持焦点,继续输入会重新打开结果,不用再点一次输入框。
- **容器自动升级只留在「容器管理」**:该开关此前还会出现在「运行环境」分类里,并且渲染成普通文本框。现在只保留容器管理卡片上的开关。

### 终端 CLI(mao-cli)
- 管理后台手册补充系统设置搜索:不知道配置在哪一组时,按名称或 key 定位。

## 0.0.402 (2026-11-02)

### 后端
- **会话文件管理可以只读打开「团队经验」**:CLOUD 会话工作区里的 `团队经验` 是指向该 Agent 经验库的符号链接。文件管理此前把它当成普通文件去读,严格路径检查又拒绝任何越出工作区的链接,于是双击得到 403。现在列目录、读取、搜索、下载、打包只放行当前会话这个挂载,目标必须正好是该 Agent 的库目录;其他符号链接、其他 Agent 的库、共享目录的其余内容仍然拒绝。进入该目录后标记为只读。顺着这个链接的新建、改名、删除、上传、移动会被拒绝;删掉链接本身也不允许。同名的真实目录仍按普通工作区目录处理。
- 单测:放行后的列表 / 读取 / 搜索,改指向或未授权的链接仍拒绝,写操作拒绝文案,以及真实同名目录仍可写。

### 前端
- **文件管理把「团队经验」当成可进入的只读目录**:双击进入,侧边文件树可以展开。目录内隐藏新建、上传、删除、重命名、剪切和拖拽,并提示内容由 Agent 写入、修改请到管理后台的 Agent 经验库。文件读取失败时展示服务端文案(如「路径访问被拒绝」),不再只显示 HTTP 403。

### 终端 CLI(mao-cli)
- 桌面端文件管理说明与 Agent 团队经验库说明同步:会话文件管理可只读浏览 `团队经验/`。

## 0.0.401 (2026-11-02)

### 后端
- **用户列表支持按角色筛选**:`GET /v1/users` 增加可选参数 `roleId`。此前角色页「成员」只会把角色 ID 带到地址栏,列表接口不认这个参数,打开后仍是全部用户。现在按 `user_role` 过滤,可与关键词、状态、账号类型一起用;`roleId` 不是正整数时返回参数错误。不传时结果与原来一致。
- 单测:传入 `roleId` 时计数与分页 SQL 都带 `user_role` 条件且参数绑定;`0`、负数、小数在查询前拒绝。

### 管理后台
- **用户管理可以按角色筛选**:筛选区增加「角色」下拉,选中后只显示该角色的用户,并写回地址栏 `?roleId=`。从角色权限点「成员」进入时,下拉自动选中该角色,列表即该角色成员。原先那条「后端尚未支持 roleId」的提示已去掉。

### 终端 CLI(mao-cli)
- `mao user list` 增加 `--role-id`,对应 `GET /users?roleId=`。

## 0.0.400 (2026-11-02)

### 后端
- **「我的工作区」页签新增机器人通道工作区(飞书 / 钉钉)**:飞书群、飞书私聊、钉钉群、钉钉私聊的会话工作区此前位于全局根 `{workspaceRoot}/feishu-chat|dingtalk-chat/{账号}/{目录}`、不在每用户根内,总览一律看不到,只能去左侧会话栏逐个会话翻。现在总览在本人根目录之外追加枚举两个通道根:私聊工作区只列本人的,群工作区只列本人有会话(含归档)参与过的;群目录按设计多人共享,「关联会话 / 最近使用」改为跨用户聚合(此前按用户过滤会少计同群其他用户的会话)。文件管理(浏览 / 读写)与「新任务」对受管通道工作区开放;删除守卫拆分两套口径——群工作区多人共享一律拒绝删除(400),私聊工作区仍可删(走既有确认输入 + 活跃引用检查);用户自建会话也允许落在受管通道工作区(私聊须本人、群须参与过,缺省判定时拒绝)。
- 单测:通道枚举(双通道四类条目、他人私聊 / 未参与群不可见、符号链接逐级跳过)、跨用户聚合、两套守卫口径、建会话通道工作区放行 / 拒绝、两个新仓库方法的 SQL 形状与参数。

### 前端
- **「我的工作区」页签收录飞书 / 钉钉通道工作区**:机器人通道会话的工作区(群目录多人共享、私聊按人隔离)此前被总览明确排除,文件只能经左侧会话栏逐个访问。现在页签会列出本人参与过的群与本人私聊:名称列用与左侧会话栏同源的合成标签(如「飞书群3·OC_67F0F76」「飞书私聊」「钉钉群…」),类别列新增「机器人通道」;「文件管理」「新任务」照常可用,群条目的「删除」按钮禁用并提示「群聊工作区为多人共享,暂不支持删除」(物理删除会断掉全群用户历史会话的文件浏览)。顶部提示文案同步更新。

## 0.0.399 (2026-11-02)

### 前端
- **任务列表不再显示会话来源小图标**:0.0.381 起,非 Web 来源(飞书 / 钉钉 / 微信 / 开放接口 / 浮窗 Embed / 信号哨兵)的会话条目行尾会带一个渠道小图标,与所在分组的图标重复。现在移除条目级徽标,行内只保留状态点 / 运行旋转器、未读点与相对时间;来源仍可从分组头图标辨认(机器人通道会话本就按渠道独立分组),完整来源信息在管理后台「会话管理」的来源列 / 筛选与会话详情中查看。

## 0.0.398 (2026-11-01)

### 管理后台
- **修复用量分析「近 3 / 7 / 30 / 90 天」全部按近 30 天统计**:0.0.375 周期选择器改版后,前端把滚动窗口按 `range=3/7/30/90` 下发,而后端 `range` 白名单只认语义周期(`today` / `yesterday` / `week` / `lastWeek` / `month` / `lastMonth` / `custom`),滚动窗口本应走 `days`——数字被 `parseAnalyticsQuery` 静默回落成默认 30 天窗口。表现为切「近 3 天 / 近 7 天 / 近 30 天」时用户活跃排行、Token 排行(以及总览、趋势、模型、Agent、会话各 Tab)的数据完全一样。现在滚动窗口改回 `days=N` 口径:`AnalyticsQuery.range` 收窄为语义周期并新增 `days`,缓存键同步加入天数,避免切 3/7 天命中同一份缓存。
- 单测:`buildAnalyticsQuery(3/7/30)` 断言产出 `{ days: N }`、缓存键区分 3/7 天且滚动窗口不进 `range`;修正此前按错误实现写下的 `{ range: 7 }` 断言。

## 0.0.397 (2026-11-01)

### 后端
- **`feishu_download_file` 的失败信息增加 lark-cli 兜底提示**:机器人没有该消息的阅读权限时(不在会话,或资源属于合并转发的子消息)飞书返回 `230002` / `234003`,bot 身份无解,Agent 此前只能看到错误码,通常直接放弃或盲目重试。现在这两类失败会在 `error` 文本后追加提示:说明原因,并给出改用 lark-cli 以当前用户身份下载的命令(`lark-cli im +messages-mget --message-ids <消息ID> --download-resources`,落盘 `lark-im-resources/`;合并转发子消息改用外层消息 ID,拿不到时请用户重发;前提是当前用户对该消息可见)。其它失败(网络错误、大小超限等)保持原样,不追加提示。
- 新增单测:230002 / 234003 失败带提示,普通错误(socket hang up)不带提示。

## 0.0.396 (2026-11-01)

### 后端
- **修复合并转发里的图片下载必失败(内置工具与预下载双链路)**:飞书合并转发的子消息 `message_id` 不是「真实消息」,用它调详情接口返回 `230002 Bot/User can NOT be out of the chat`、调资源接口返回 `234003 File not in msg`——展开预下载与 `feishu_download_file` 懒加载兜底都是这条路径,导致所有来自合并转发的图片 100% 下载失败,Agent 最终只剩文字可读。修复:合并转发展开时,图片预下载改用**投递到当前会话的顶层父消息 id** 作下载锚点(`MergeForwardImageRef.anchorMessageId`),落盘命名仍按子消息 id 保持唯一;嵌套合并转发统一用最外层父消息 id。经 bot 身份实测:父消息 id + 子消息 image_key 可正常下载(414,976 字节),子消息 id 必然失败。
- **工具与日志透出飞书业务错误码**:飞书 SDK 对非 2xx 只抛 `Request failed with status code 400`,`feishu_download_file` 返回值与预下载日志里都看不到真正原因。新增 `describeFeishuApiError` / `wrapFeishuApiError`(`feishu-api-error.ts`),消息详情与资源下载两处统一把 `code/msg` 包进错误信息(如 `获取飞书消息失败: om_xxx (code=230002 msg=Bot/User can NOT be out of the chat.)`),识别不出业务码时保留原始错误。
- 新增/更新单测:下载锚点与子消息分离(含 post 内嵌图、嵌套转发、去重、上限、并发场景的调用记录改为 `[anchor, child, imageKey, index]`)、详情接口错误透出业务码、非业务错误原样保留。

## 0.0.395 (2026-11-01)

### 后端
- **修复 Docker 模式容器时区为 UTC**:运行时镜像(`node:22-bookworm-slim`)默认 `Etc/UTC`,创建容器时也未注入时区,容器内 `date`、脚本时间戳、Node 本地时间、`git commit` 全部按 UTC 落——比北京时间慢 8 小时,北京时间 00:00–08:00 时段日期还会显示为前一天;与后端全站 `Asia/Shanghai` 口径(日志时间戳、系统提示「当前日期」、Cron 调度)不一致,Agent 自查时间时自相矛盾。现在 `runtime-agent` 启动时对齐容器时区:① 进程内设置 `TZ=Asia/Shanghai`(优先级最高、立即生效,覆盖 agent 自身与其派生的 shell / pty / MCP 子进程);② 幂等对齐 `/etc/localtime` 符号链接与 `/etc/timezone`(临时文件 + rename 原子替换),使 `docker exec` 等非 agent 派生进程同样正确。tzdata 随镜像已存在,不依赖挂载宿主机文件;对齐失败仅告警、不阻塞 agent 启动。存量容器在下一次代码漂移懒重启(0.0.394 机制)时自动收敛,无需人工重建。
- 新增单测:时区对齐幂等(localtime 链接 + timezone 内容)、普通文件 localtime 替换为符号链接、zoneinfo 缺失时零改动降级。

## 0.0.394 (2026-11-01)

### 后端
- **容器内代码版本漂移自动收敛(Docker 模式)**:容器内 `runtime-agent` 是常驻进程、`dist` 无热加载,此前发版后容器仍跑旧代码,需人工 `docker restart` / 后台重建(0.0.393 的技能符号链接修复即因此需要人工重启才生效)。现在:① 构建期新增闭包指纹——`npm run build` 在 tsc 之后遍历 `runtime-agent.js` 的静态相对 import 闭包,生成 `dist/harness/container/runtime-build.json`(内容哈希,`generatedAt` 不参与哈希,同源重建幂等);② 容器 agent 启动时读取该 manifest,`hello` 帧新增可选 `build` 字段上报(协议 v1 不变、旧宿主松校验忽略);③ 宿主机 `ContainerRegistry` 读取同一份 manifest 作为「本实例加载的代码版本」,在 `ensureRunning`(用户下次调工具)时懒比对:代码漂移且容器空闲 → 自动重启进程(`stop` + `start`,保留容器/挂载/env/容器名,不重建);忙碌则只记日志(`deferred-busy`),下次再判;挂载 / hosts 漂移仍走重建(正确性优先)。
- **防环与守卫**:每(容器 × host build)只自动尝试一次(蓝绿过渡期旧实例缓存旧 build 时不会反复重启);draining 实例跳过自动升级(由承接流量的实例执行);`dist` 无 manifest(LOCAL/dev / 异常部署)时检查整体关闭;旧 agent 不报 build 视为 unknown → one-shot 自愈(首次发布后各存量容器在空闲时自动重启一次完成切换,无需人工)。
- **新增总开关**:`system_setting.runtime.container.autoUpgrade`(V178 迁移,默认 `true`,后台「系统设置 → 运行环境 → 容器管理」可切换;保存后即时生效,5s TTL + 保存失效双保险)。关闭只停用自动重启,人工「重建容器」入口不变;缺行/非法值/读库失败一律回落 `true`。
- 新增单测:闭包指纹(确定性 / 仅闭包内容变化 / 注释内 import 不误判 / manifest 读取降级)、开关解析与 TTL、hello 上报(含旧 agent 无 build)、registry 漂移收敛矩阵(空闲重启 / 忙碌延后 / 开关关闭 / draining / one-shot / stopped 仅 start / 挂载漂移优先重建 / manifest 缺失零动作)、设置值域校验。

### 管理后台
- **「系统设置 → 运行环境 → 容器管理」新增自动升级开关**:「发版后自动重启空闲容器以收敛代码版本」。
- **容器列表新增三列**:「代码版本」(容器 agent 上报,未上报显示「待上报」)、「后台版本」(本实例 dist 版本,manifest 缺失显示「未启用」)、「升级」(漂移时显示「待升级」徽标,一致显示「一致」)。

### 部署
- `npm run build` 自动生成 manifest,无需额外步骤;本次发布后存量容器会在各自「空闲 + 下次使用」时自动重启一次收敛(每容器一次,不是发版风暴),无需人工重启容器。

## 0.0.393 (2026-10-31)

### 后端
- **修复 Docker 模式读会话技能被判「符号链接逃逸」**:会话技能自 0.0.205(66c77a36)起以符号链接挂载(`{runtimeDir}/{userId}/{sessionId}/skills/{name}` → 技能源目录),平台技能的目标根是宿主机 `skillsDir`(`/opt/mao-data/skills`);而容器内文件工具的沙箱允许根只登记了每用户目录 + share + `/tmp`,strict 模式的符号链接逃逸检测因此把 `read_file` 读 `SKILL.md` 判成越界,返回 `Path escape attempt (symlink): .../skills/{name}/SKILL.md`。宿主机模式(宽松解析、不校验符号链接)不受影响,切换 `runtime.mode=docker` 后首次暴露。修复:`ContainerRoots` 增加 `skillsDir`,容器创建时注入 `MAO_SKILLS_DIR`,runtime-agent 经 `resolveRoots()` 读取并由 `ContainerToolExecutor` 登记为允许根——该目录本就以 rw 挂进容器(产品要求容器内可安装/更新共享技能),工具层放行与挂载口径一致,不扩大隔离边界;指向 `skillsDir` 之外的符号链接仍按逃逸拒绝。影响 `read_file` / `write_file` / `edit_file` / `glob_search` / `grep_search` 与外部发送的容器读字节。存量容器需重启(runtime-agent 常驻,dist 更新不热加载)。
- 补回归用例:容器内读会话技能符号链接(read_file / read_bytes / write_file 放行 + 指向外部的链接仍拒绝)、容器创建参数含 `MAO_SKILLS_DIR`、未设置该 env 时 `resolveRoots` 回落 `/opt/mao-data/skills` 并登记为允许根。

## 0.0.392 (2026-10-31)

### 后端
- **容器模式 HOME 对齐用户数据目录(与宿主机模式一致)**:创建容器时注入 `HOME=/opt/mao-data/users/{userId}`,容器内 npm(`.npmrc`)、pip 缓存、git(`.gitconfig`)等用户级配置直接读持久挂载的用户目录,容器重建不丢;Java 在 Linux 上经 getpwuid 取 `user.home`(不认 `$HOME`),runtime-agent 启动入口将 `/etc/passwd` 中 root 的 home 幂等对齐(仅告警、不阻塞启动),Maven / Gradle 的 `~/.m2`、`~/.gradle` 随之落到用户目录——挂载的 `settings.xml`(私服 mirror)与本地仓库缓存直接生效。存量容器重建后生效。

### 部署
- 如需在容器内跑 Java 构建,可在运行时镜像预装 `openjdk-17-jdk-headless` + `maven`;本部署的 overlay 定制层已包含(见 `docs/plan/2026-10-31-runtime-image-preinstall-chrome-lark-cli.md`)。

## 0.0.391 (2026-10-31)

### 后端
- **容器 /etc/hosts 注入(Docker 模式)**:创建容器时把「宿主机 `/etc/hosts` 自定义条目 + 管理后台自定义规则」通过 `docker run --add-host` 注入容器,解决宿主机本地域名映射(如内网服务域名指向可达 IP)在容器内不生效、容器按 DNS 解析到不可达地址的问题。宿主机条目自动继承(可开关),后台规则可追加/覆盖;配置变化后受影响用户的容器在下次使用时自动重建(注入指纹漂移),也可在后台立即重建(单用户 / 全部,默认跳过有在跑任务的容器)。新增 `container:manage` 权限(默认授予管理员角色)。
- 新增设置项(V176):`runtime.container.inheritHostHosts`(默认 `true`)、`runtime.container.extraHosts`(JSON 规则数组,最多 50 条,禁环回/云元数据/保留段);新增只读预览接口 `GET /v1/system-settings/container-hosts/preview`。
- 新增接口:`GET /v1/admin/containers`(在册容器列表)、`POST /v1/users/:id/container/recycle`(单用户重建,body 支持 `force`)。

### 管理后台
- **系统设置 → 平台与运维新增「容器域名解析」**:宿主机 hosts 继承开关、当前生效条目预览(含来源标记)、自定义规则增删改。
- **新增「容器管理」**:在册容器列表(状态 / 忙碌 / 启动时间)、单行重建、「重建全部」串行执行(显示进度与成功/跳过/失败汇总)。
- **用户管理 → 用户详情新增「重建容器」按钮**(需 `container:manage` 权限)。

## 0.0.390 (2026-10-31)

### 后端
- **管理后台「会话管理」列表改按最后活动时间倒序**:排序从创建时间(`created_at DESC, id DESC`)改为 `last_activity_at DESC, id DESC`——最近有动作的会话浮到最前,更符合「先看最新动静」的排查习惯;从未产生过活动的会话 `last_activity_at` 为 `NULL`,由 MySQL 归到列表末尾。`last_activity_at` 为秒级精度,仍以 `id DESC` 作唯一 tiebreaker,深翻页不重复/丢行。「运行监控」列表复用同一查询方法,排序同步生效。

## 0.0.389 (2026-10-30)

### 管理后台
- **移除 Agent 导入、导出与检查更新**:Agent 列表不再提供这三个入口,系统设置不再有 Bundle registry 开关。

### 后端
- **移除 Agent Bundle**:删除文件导入、URL 导入、registry 拉取与检查更新接口,以及 `agent_import_origin` 表(V175)。技能独立 Bundle(`mao-skill-bundle`)保留。

### 终端 CLI(mao-cli)
- **移除 `mao agent export` / `mao agent import`**。

## 0.0.388 (2026-10-30)

### 管理后台
- **移除 Agent「从模板开始」**:Agent 列表不再提供入门模板入口;创建 Agent 改为空白表单。就绪横幅里「创建第一个 Agent」不再提示从模板开始。

### 后端
- **移除入门 Agent 模板**:删除 `GET /v1/agent-templates` 与「信息助理」常量。

## 0.0.387 (2026-10-30)

### 前端(桌面 / Web / 安卓)
- **工作区操作列「用于新任务」改为「新任务」**:点下去仍是用该目录预填一个新任务,行为不变。

## 0.0.386 (2026-10-30)

### 后端
- **工作区总览不再统计目录大小**:打开「我的工作区」和刷新都不再遍历目录。列表仍返回 `sizeBytes: null`、`sizeState: ready`,旧客户端不会停在「统计中」。
- **按目录打开文件管理**:总览里的项目目录和其他自定义目录可以不依赖当前会话直接浏览和修改。路径仍限制在本人工作区根下的一级目录(或 projects 下的一级项目目录)。

### 前端(桌面 / Web / 安卓)
- **「我的工作区」去掉大小列**:表格不再展示占用大小,顶部刷新只重新拉列表。
- **操作列增加「文件管理」**:点击后打开该工作区自己的文件管理页签,和当前会话的文件管理并存。在里面打开的文件也读这个目录。

## 0.0.385 (2026-10-30)

### 前端(桌面 / Web / 安卓)
- **「我的工作区」入口挪到顶栏最左**:文件夹图标从任务列表标题旁移到顶部栏左侧第一个位置(macOS 窗口态仍为红绿灯留白)。任务面板不再放这个按钮。
- **工作区列表不展示临时工作区**:数字目录的临时工作区不再出现在「我的工作区」表格里。项目目录和其他自定义目录仍列出。

## 0.0.384 (2026-10-30)

### 后端
- **边路任务列表带上 agentId**:`GET /v1/sessions/:id/side-tasks` 每条返回该边路任务自己的 `agentId`,与主会话独立。

### 前端(桌面 / Web / 安卓)
- **当前边路任务可切换智能体**:打开某条边路任务(或其运行轨迹)时,右侧检查器展示这条任务自己的智能体,点击即可切换,确认后只改这一条、下一轮生效。主会话和其他边路任务不变。子代理仍只读。
- **检查器智能体信息靠左**:可点击的智能体头像与名称与标题、工作区左缘对齐,不再在行内水平居中。

## 0.0.383 (2026-10-30)

### 后端
- **移除「用其他 Agent 重开」**:删除 `POST /v1/sessions/:id/reopen-with-agent` 与接手说明生成(LLM 场景 `agent_handover_summary`)。换智能体只保留会话内切换(`PATCH` 的 `agentId`,下一轮生效,聊天与工作区都留在当前会话)。

### 前端(桌面 / Web / 安卓)
- **顶栏左右面板按钮展开时不再高亮**:面板打开时按钮保持与其他顶栏图标相同的默认色,悬停仍有反馈。
- **macOS 全屏时左侧面板按钮贴左**:窗口态仍为红绿灯留白;进入全屏(红绿灯隐藏)后按钮靠到左边,与移动端一致。退出全屏后留白恢复。
- **移除检查器「用其他 Agent 重开」**:智能体分区不再提供该入口。用量明细里已产生的 `agent_handover_summary` 记录仍保留,场景名回落为原始枚举。
- **会话内切换智能体改到检查器**:输入框工具条不再放智能体名称。主会话下点击右侧「智能体」分区的头像与名称即可选择,确认后下一轮生效;边路任务和子代理的该分区只读。

## 0.0.382 (2026-10-30)

### 后端
- **工作区总览端点**:新增 `GET /v1/files/workspace-overview`(登录态即可,无会话上下文)——枚举本人工作区根下全部一级目录(数字名临时工作区 / `projects/` 项目子目录 / 其他),返回类别、是否 Git、占用大小、关联会话计数(进行中 · 归档,`isActivePhase` 唯一口径的聚合 SQL 一次分组完成、按目录前缀归并)与最近使用时间;`projects` 容器本身不作为条目,符号链接一律不跟随也不列出。既有 `GET /v1/sessions/cloud-projects` 原样保留,新建任务下拉零回归。
- **大小统计**:后台异步计算 + 进程内 TTL 缓存(10 分钟)+ 目录间并发 4;单目录三重上限(深度 24 / 条目 20 万 / 30 秒超时,超限即停标 `timeout`,符号链接 `lstat` 识别后永不跟随);计算队列按最近使用降序入队。缓存过期后再次打开总览会重新入队计算(过期条目不当作命中跳过)。首屏未就绪返回 `pending`,`POST /v1/files/workspace-overview/refresh`(body 可选 `{ path }` 单目录)失效后立即返回并由后台重算;单目录刷新的路径同样限本人根之内。
- **删除工作区目录**:`DELETE /v1/files/workspace-overview`(body `{ path }`)——守卫链:解析后严格位于本人根内且为一级目录或 `projects/*` 一级子目录(越界 / 容器 / 不存在回 400 信封);存在活跃相位会话引用(`workspace` 精确或转义前缀命中)回 409 拒绝,引用清单由 `GET /v1/files/workspace-overview/references?path=` 在弹层打开时预先拉取(失败信封无 data 字段,409 不携带结构化清单);通过后 `fs.rm` 物理删除(`force:false`,进程内 per-path 互斥防双击,ENOENT 容错为已删除)并失效大小缓存。归档会话引用允许删(弹层明示后果)。
- **文件树缺根兜底文案**:会话工作区根目录不存在时,目录列举端点返回「工作区目录不存在(可能已被删除)」而非裸「目录不存在」/「路径访问被拒绝」(后者对用户无法理解)。

### 前端(桌面 / Web / 安卓)
- **工具分组「导出用户消息」改用下载图标**:`dump_user_messages` 此前未登记图标,分组头落回通用齿轮;现与「导出」语义对齐。
- **「我的工作区」中央页签**:任务面板头部新增文件夹入口(全局可见,含新建任务态),中央页签注册方式与 Git 管理器一致(单例 id `workspace`,不随刷新重建)。表格呈现名称(Git 标记)/ 类别 / 大小 / 关联会话(进行中 · 归档)/ 最近使用,默认按最近使用降序、可切大小 / 名称;顶部「刷新」强制重算,`timeout` / `failed` 行显示行内刷新,`pending` 行 3 秒后自动重查一次。页签空态注明机器人通道工作区不在范围内。并发请求(挂载 / 切页签 / 重查 / 刷新 / 删除后重载)按代际守卫,过期响应不覆盖更新的列表。
- **删除确认流**:弹层展示目录名、大小、会话计数(写明「归档会话的文件浏览与 Git 面板将不再可用」)与活跃引用清单(有进行中引用时确认按钮禁用),要求**输入目录名**启用删除;删除成功后刷新列表 + toast,失败原因由统一拦截器提示、弹层保持打开。
- **行操作「用于新任务」**:项目目录按「复用已有工作区」同链路预填(含 cloudProjectKey),临时 / 其他目录直接预填工作区路径(CLOUD 建会话请求体带显式 `workspace`,后端复用它、不再自动建新目录;配置区以只读「复用」条目展示,用户改选其他工作区来源即让位)。
- **新建任务下拉富化**:「复用已有工作区」条目加副标题(大小 + 会话数,来自总览端点按 path 合并);总览不可用时静默回落纯目录名,既有行为不变。

### 桌面 Electron
- **修复「重启安装」在默认关窗收托盘时只隐藏窗口**:macOS 的 `quitAndInstall` 先关窗、后发 `before-quit`,关窗拦截看到退出标记仍为 false,把关闭改成 `hide()`,进程不退出、更新不安装。现在进入安装前先置退出标记,关窗放行,安装能走完。

## 0.0.381 (2026-10-30)

### 后端
- **会话来源全链路标记**:`SESSION_SOURCE_VALUES` 闭集扩为 `web / embed / feishu / dingtalk / weixin / signal / openapi` 七值(`VARCHAR(16)` 容纳,零迁移),四个写入点补齐真实来源——飞书(p2p 与群聊共用 factory)、钉钉、微信建会话各落对应渠道值,开放接口触发的 API / WEBHOOK 新会话由回落 `'web'` 改落 `'openapi'`(哨兵仍落 `'signal'`;复用既有会话时不改写原 source)。来源是事实标签不是授权依据:`USER_SETTABLE_SESSION_SOURCES` 白名单与 `markSessionSource` 的 embed-only 守卫原样不动,用户入口永远伪造不了系统来源;分组逻辑不改用 source(`SessionGroupKey.of` 对新值无分支、自然落回既有路径 / 模式启发式,落桶与扩宽前一致)。
- **管理后台会话管理按来源筛选**:`/v1/admin/sessions` 新增 `source` 等值过滤参数(非法值宽松忽略),`toAdminSessionVO` 输出 `source`(缺省归 `'web'`);`SessionSource` 联合类型下沉 `@mao/contracts`,两端汉化表用 `Record<SessionSource, string>` 吃编译期穷尽。存量数据不回填:V109 前 / 通道接入标记时期的历史会话一律是 `'web'`,筛选口径为「按创建时来源」。

### 管理后台
- 会话列表新增「来源」列(中文标签 tag:Web / 浮窗 Embed / 飞书 / 钉钉 / 微信 / 信号哨兵 / 开放接口,web 用灰色不做强调)与顶部来源筛选下拉;移动端卡片同步展示来源;会话详情信息卡新增「来源」行。

### 前端(桌面 / Web / 安卓)
- 任务面板分组列表条目对非 `web` 来源的会话显示渠道小徽标(飞书 / 微信用品牌图、钉钉 / Embed / 哨兵 / 开放接口用对应图标)+ 中文 tooltip,统一规则不特判——信号哨兵组内条目同样带徽标;数据来自既有 `SessionVO.source`,零后端改动。

## 0.0.380 (2026-10-30)

### 后端
- **会话内切换 Agent**:`PATCH /v1/sessions/:id` 新增 `agentId`——目标 Agent 需存在、启用且客户端可见(与新建会话选择列表同口径,停用返回「该 Agent 已停用」、隐藏返回「该 Agent 不对客户端开放」),通过后更新会话绑定;下一轮执行按新 Agent 重建系统提示词、工具集、MCP 工作集与经验库路标(`buildContext` 每轮重读,执行内核零改动)。会话显式 `modelId` 保持不变,未显式时自动跟随新 Agent 默认模型;不传 `agentId` 的请求行为逐字节不变。
- **「用其他 Agent 重开」**:新增 `POST /v1/sessions/:id/reopen-with-agent`(仅 CLOUD 会话,非 CLOUD 拒绝)——同一工作区 / 权限档位新建会话,显式模型、isGit、projectKey 从旧会话沿用(飞书私聊等工作区的 projectKey 不被路径推导改写);用新 LLM 场景 `agent_handover_summary` 对最近 40 条真实消息(排除压缩摘要与 `<system-notice>` 注入,单条 1500 字截断)生成 300 字内接手说明(30 秒超时、异常吞掉返回 null、模型解析与标题生成同链),随后旧会话归档,返回 `{ sessionId, handoverNote }`。两个动作与用户手动「新建任务 + 复用工作区 + 归档」同形态,无新增并发语义。
- LLM 场景契约三处同改:`LlmCallScene` / `LLM_CALL_SCENES` 新增 `agent_handover_summary`,desktop 与 admin 汉化表补「接手说明生成」(编译期穷尽兜底)。

### 前端(桌面 / Web / 安卓)
- **输入框工具条新增智能体切换器**:模型选择器旁显示当前 Agent 名称(可搜索弹层、头像 + 名称,与模型切换同构交互),选择后确认弹层写明模型跟随口径与「Skill / MCP / 经验库 / 子代理预设自下一轮起按新 Agent 配置,会话级审批放行规则保留」;运行中可切、不打断当前轮,成功后检查器身份分区即时刷新。仅会话态显示(按会话是否绑定 Agent 判定),新建任务态与边路任务不渲染。
- **检查器「用其他 Agent 重开」**:「智能体」分区新增入口(仅 CLOUD 主会话、非活跃相位、未归档时显示),弹层选择接手 Agent(排除当前值)并提示「同工作区新建 + 旧会话归档、聊天记录不复制」;成功后刷新列表、跳转新会话并把接手说明预填进输入框(可编辑、不自动发送),生成失败(null)时提示「接手说明生成失败,可直接描述需求」。
## 0.0.379 (2026-10-30)

### 后端
- **修复后台任务结果注入产生非法 JSON**:`BackgroundTaskManager.normalizeResult` 对 output 的限长预算(MAX-400)未计入 `JSON.stringify` 的转义膨胀(引号/反斜杠/换行逐字符 +1),引号密集的构建日志、JSON 转储类输出会触发外层二次截断,在字符串值中间切断产出非法 JSON,并连带丢掉 `output_file` / `current_workdir` 尾部字段(模型失去完整日志的读取入口)。改为按整体序列化长度收敛 output 预算(倍减直至整体不超限),结果恒为合法 JSON。
- **修复 `shell` 工具 `await_async` 使用过期会话快照**:拿锁等待期间命令可能已被并行调用方替换,`settleSession` 却仍用锁前快照的 `keepSession`,会把新命令方要保留的会话错误关闭(丢失 cwd、环境变量等状态)。改为与 `write_stdin` 路径同口径使用锁后实时值。
- **`wait_subagents` 超时参数加固**:`timeout_seconds` 传空串不再被 `Number('')=0` 变成 0ms 即刻假超时(回落默认 30 分钟),非数字字符串回落默认、负数仍报错;换算毫秒后钳制 24h 上界,杜绝 `1e308 × 1000 = Infinity` 让等待 deadline 永不到期、工具无限阻塞整个 agent 循环。
- **`cancel_subagent` 的 `task_id` 改严格整数校验**:`Number('')/Number(false)/Number([]) = 0`、`Number([5]) = 5` 不再通过 `isInteger` 校验,空串/布尔/数组一律报「必须是整数」,杜绝把类型混淆入参当真实任务 id 去取消子代理。
- **`update_scheduled_task` / `delete_scheduled_task` 的 `task_id` 改严格整数校验**(与 task 工具同口径):`null`/空串不再被当成任务 #0 查询后回传误导性的「任务不存在」,直接报「缺少必填参数」。
- **`TokenEstimator.contentToString` 与序列化器对齐**:content part 的 `type` 字段可选,凡带 `text` 的 part 都会发给模型,token 估算不再要求 `type === 'text'`(消除与 `serializeContentPart` 的口径分叉隐患)。
- **`send_wechat_file` 的 `filename` 空串兜底**:`??` 不处理空串,显式传 `filename: ''` 会把空文件名发给微信协议层导致发送失败;改用 `||` 链兜底回落到文件名推导与 `'file'`。
- **修复 Docker 运行模式下工具调用全部失败**:团队经验库(0.0.342)让容器侧 `runtime-agent` 的依赖闭包里多了 `yaml`,而运行时镜像 `mao-runtime:latest` 只带固定依赖集、未随代码同步重建——容器内 agent 启动即 `ERR_MODULE_NOT_FOUND` 退出,表现为每个用户的任务等满 15 秒后报「容器运行时通道连接超时」。已把 `yaml` 补进镜像构建链(`Dockerfile.runtime` + `scripts/build-runtime-image.sh`,版本跟随 `backend-ts/package.json` 读取)。
- **`ContainerRegistry.precheck()` 新增「镜像 + 当前 dist」加载冒烟**:切 docker 前用当前 dist 在镜像里实际加载一次 runtime-agent 与 sharp / node-pty(只加载、不启动 agent),发现新增 npm 依赖或 Node 升级后漏重建镜像时,后台保存「系统设置 → 运行环境」直接以明确错误拒绝并提示重建镜像命令,不再等到用户任务逐个超时。

### 桌面 Electron
- **LOCAL shell 会话 `cd` 失败不再静默**:`workdir` 指向不存在目录(或 cd 超时)时,此前协议 cd 的结果被完全忽略,命令照常在旧工作目录里执行并以 `exit_code: 0` 假成功返回,模型完全无感知。现在与云端同口径:cd 失败报「工作目录切换失败」、超时关闭会话并报「工作目录切换超时」,命令绝不在错误目录里静默执行;协议命令(cd/pwd)回显退出码以支持该判定(PowerShell 先清 `$LASTEXITCODE`)。
- **`write_stdin` 触发 bash 退出后立即回收死会话**:输入含 `exit`/`exec` 使 bash 退出时,此前死会话留在会话表里继续占用每会话 30 个的配额(最长 60s 后才被周期清理兜底),与返回文案「会话已关闭」自相矛盾。现在与云端同分支同口径立即回收。

### 终端 CLI(mao-agent)
- **`read_file` 的 `offset`/`limit` 改严格整数解析**(与云端 `read-file-tool` 同口径):此前用会 `Math.trunc` 的宽解析,`offset: 1.9` 在云端报错、在本地静默按 1 读,同一份模型参数两种执行模式结果不同;`-0.5` 还会变成 `-0` 绕过负数校验。新增与云端同语义的 `asIntStrict` 并替换。
- 同步桌面端 localShell 的两处修复(cd 判定、死会话回收)到 `vendor/localShell.cjs`(两份副本保持逐字节一致)。

## 0.0.378 (2026-10-29)

### 后端
- **新增内置工具 `dump_user_messages`:把本会话最近 N 条用户消息原文落成文件**。用户在输入框粘贴成百上千个 ID 这类「大批量精确数据」时,模型必须复述这批数据才能交给工具处理,而 8-9 位长数字串正是生成的高频出错区(漏位 / 错位 / 幻觉),压缩触发后摘要还会把列表退化成「14084181–14084253 等连续 ID」丢掉精度。新工具按消息 id 把原文逐条写成文件(`user-message-{消息id}.txt`),Agent 拿到绝对路径后用 `read_file`(支持 offset/limit 分页)、`grep_search` 或 shell 脚本精确处理,全程不需要复述原文;参数只有 `recentCount`(默认 1、上限 10),同一消息重复导出覆盖同一文件(幂等)。工具**不会**把原始消息从上下文移除,只额外提供一份文件副本。CLOUD 落盘到会话 runtime 的 `userMessages/` 目录(与 `webPages/`、`compaction/` 并列,Agent 的读文件 / 搜索 / shell 无需额外放行);LOCAL 会话经 WS 委托桌面端写到本机 `~/.mao/attachments/{host}/{sessionId}/`,桌面端未连接或单条失败时如实返回 `error`、其余消息继续导出。工具进 `SERVER_ONLY_TOOLS`:两种执行模式都由服务端读消息并决定落点。
- **`SessionService.getRecentUserMessages(sessionId, limit)`**:按 id 倒序取最近 N 条「真实用户消息」后翻转为正序返回,口径与压缩 / 无进展判定共用 `isRealUserMessage`(排除压缩摘要 handoff 与 `<system-notice>` 系统注入);非真实行被过滤时按游标继续向前扫描,保证「最近 N 条」不缩水。
- **`RuntimeDataResolver.resolveUserMessagesDir`**:新增 `runtime/{userId}/{sessionId}/userMessages` 路径解析。
- **工具展示三处同步**:`tool-result-summarizer` 新增结果摘要(按实际写出的文件数,失败条目不冒充成功;全失败时显示「未能导出」);`no-progress-notice` 固化「文件路径不是易变字段」结论并补用例(同一消息重复导出路径稳定、不同消息算真实进展);`desktop/src/utils/toolDisplay.ts` 新增中文名「导出用户消息」与 `recentCount` 参数预览。

### 前端(桌面 / Web / 安卓)
- **输入框粘贴超长文本自动转附件**:粘贴纯文本达到 2000 字符(典型:从 Excel 复制的上千个 ID)时,文本不再插入编辑器,而是静默生成 `pasted-text-N.txt` 待发附件(输入框下方多出一个附件 chip,输入框保持原状、不写任何提示语),发送时最终消息形态为「用户指令 + `@{绝对路径}@`」:CLOUD 走既有上传链路;LOCAL 下这类附件没有本机源文件,发送时把内容交给主进程写入会话附件目录(`~/.mao/attachments/<host>/<会话id>/`,同名自动追加 `-1`,不覆盖已有附件),与普通附件同形态引用。此前这类粘贴只能整段进入消息正文,Agent 处理时既要复述数据、又会被压缩丢精度。判断顺序为先看长度再看 `@{...}@` / `${...}$` / `#{...}#` marker 语法——超长文本即使含 marker 也整体转文件;图文混排粘贴行为不变;附件位已满(10 个)或浏览器打开 LOCAL 会话时不丢弃用户文本,回落为直接插入并提示。粘贴附件序号跨输入框实例连续(新建任务首条发出后输入框重建也不会重名),阈值与附件上限各抽成常量。
- 工具卡片新增 `dump_user_messages` 的中文名与参数预览(最近 N 条)。

### 桌面 Electron
- **新增 LOCAL 文本落盘工具 `write_local_text_file`**:写到 `~/.mao/attachments/{host}/{sessionId}/`,复用附件路径解析(basename 化、host / session 强校验、home 相对断言)与异步写盘;同名处理分两种语义——`dump_user_messages` 委托的文件名由消息 id 决定、覆盖同一文件(幂等),渲染层粘贴转出的 `pasted-text-N.txt` 则与普通附件一致自动追加 `-1`/`-2` 副本,避免同会话多段粘贴互相覆盖、让历史消息引用静默指向新内容;失败以 `{ error }` 回传、不抛异常。同时暴露 `local-write-text-file` IPC 与 `electronAPI.localWriteTextFile` 备用入口。

### 终端 CLI(mao-agent)
- **`mao-agent --local` 支持 `dump_user_messages` 的落盘委托(`write_local_text_file`)**:此前 LOCAL CLI 会话调用该工具会逐条失败(`Unknown tool: write_local_text_file`),长用户消息文件化只在桌面端可用。现在本地执行器新增该工具:把服务端下发的用户消息原文写到 `~/.mao/agent-cli/runtime/<sessionId>/userMessages/user-message-<消息id>.txt`,文件名 basename 化并限制长度、内容按 100MB 封顶、同名按消息 id 幂等覆盖(与桌面端 dump 语义一致),回传本机绝对路径;落点固定在会话 runtime 目录内(服务端下发 `needApproval=false`,不弹审批),Agent 拿到的路径可直接用 `read_file` / `grep_search` / shell 读取。

## 0.0.377 (2026-10-29)

### 前端(桌面 / Web / 安卓)
- **Git 面板的「刷新」与「获取」改为同步全部远端**:此前只同步 upstream 对应的那个远端(例如 `origin`),多远端仓库里其它远端的分支(如另一个 remote 的 `main`)永远不会更新——在「对比」视图里选这类分支会看到过期几百个提交的差距与文件清单,用户无法在应用内把数据刷新到最新。现在刷新与「获取」会同时获取配置里的全部远端(每个远端独立执行 `git fetch --prune <remote>`、并行获取),所有远端的跟踪引用一并更新;某个远端不可达或挂起不影响其余远端,状态条给出「部分远端获取失败:<远端名>(其余远端已更新)」提示,全部远端都失败才标记远端状态不可用;并行执行使最坏等待时间以单个远端的 60 秒超时为上界、不随远端数量叠加。无远端的仓库行为不变;CLOUD 下这两个端点的前端超时与提交等长操作对齐(150 秒),挂起远端场景不再被 30 秒默认超时打断、拿不到失败详情。
- **「对比」视图默认预选「远端基准 → 当前分支」**:进入对比视图时自动把目标预选为当前分支、基准预选为当前分支的 upstream(如 `origin/main`;无 upstream 时退回已存在的 `origin/<当前分支>` 或任一远端同名跟踪分支),打开即自动开始对比,不再需要两次下拉选择;用户已选 / 已交换的选择不被覆盖,无可预选值时保持原「请选择两个分支开始对比」空态。

### 后端
- **`/v1/files/workspace-git-fetch` 与远端状态刷新改为并行获取全部远端**:逐个远端执行 `git fetch --prune <remote>`(每个远端一个独立子进程并行执行、各自 60 秒超时,总等待不随远端数叠加);不再使用 `git fetch --all`,也不再解析 git 的本地化失败输出,失败/超时天然归属到具体远端。失败语义按「部分/全部」区分:部分失败返回成功并提示失败远端名(如「部分远端已更新(github 获取失败)」),全部失败才报错(单失败保留「超时 / 认证失败 / 远端不可达」细分文案);按「刷新 = 同步全部远端」语义,被 `remote.<name>.skipDefaultUpdate` 标记的远端同样会被获取(不再遵循该配置)。远端状态刷新(`refreshRemoteStatus`)在部分失败时仍标记远端状态可用并附提示,`hasCommitsToPush` 的参照远端仍按 upstream → origin → 唯一远端选择。

### 桌面 Electron
- **LOCAL 侧 `refreshGitStatus` 与 `fetch` 同口径升级为并行获取全部远端**:与 CLOUD 行为一致(每个远端一个独立子进程、各自 60 秒超时、并行执行,失败归属到具体远端);部分失败时状态仍可用并提示、操作返回成功,全部失败才报错;无远端仓库的既有提示不变。

## 0.0.376 (2026-10-29)

### 后端
- **新增 Git 分支对比两个只读端点**:`GET /v1/files/workspace-git-branch-compare`(两个分支当前内容的差异清单 + 各自独有的提交数)与 `GET /v1/files/workspace-git-branch-compare-file-diff`(单文件双 ref diff)。语义为两点 `<base> → <target>`(`git diff <base> <target>`,直接比较两棵树):两侧变更同屏展示,基准分支独有的改动会以「删除 / 反向修改」形式出现(两点对比的固有方向性,由界面方向标注承接);单文件 before 取基准分支内容、after 取目标分支内容;base / target 走更严的分支名校验(首字符排除 `-`,防 option 注入),ref 不存在、对比失败分别给出明确文案,空列表只表示「两分支当前内容完全一致」,无共同祖先的两个分支也可正常对比;仓库根解析 / 路径 `..` 与沙箱校验、二进制与截断口径均沿用现有 Git 端点族。

### 前端(桌面 / Web / 安卓)
- **中央 Git 面板新增第五个子视图「对比」**:两个分支下拉(基准 / 目标,本地 + 远端全量、均不预选、不联动)自选对比,汇总条以 `基准 → 目标` 标明方向并展示两侧领先 / 落后提交数、文件数与总增删行数;文件按目录分组树展示(单链目录压缩为 `a/b/c` 包名样式,改名行显示旧路径,仅基准分支存在的文件带「仅基准分支」标记),点文件在中心 Diff Tab 打开该文件在两个分支间的 diff(页签 id 前缀 `git-branch-diff:<base>-><target>:`,与工作区 / 提交 diff 互不覆盖,路径重定向后保留 base / target 段);支持交换基准与目标、分支列表加载态 / 空态区分、「两个分支当前内容完全一致」提示与错误重试。
- **分支视图每行新增「对比」按钮**:点击切到「对比」视图并把该分支预填到目标槽(基准槽不自动改动,仍由用户选);多仓库工作区下对比跟随当前选中仓库,切仓库 / 切会话清空对比选择与结果,写操作后已加载的对比自动重算。

### 桌面 Electron
- **Git 分支对比新增 2 个只读 IPC**:`git-branch-compare` / `git-branch-compare-file-diff`(LOCAL 模式直接读本机 git 对象,与后端同命令同解析;分支名同规则校验,`git diff` 失败与「没有差异」严格区分,不会把超时 / 缓冲区溢出误报为空列表;两点语义下无共同祖先的分支同样可对比)。单文件 diff 遇超过 2 MiB 的大文件时保留缓冲区内的内容并按截断返回(与 CLOUD 同口径),不再静默渲染成空 diff。

## 0.0.375 (2026-10-29)

### 后端
- **用量分析接口新增语义统计周期入参**:`/v1/admin/analytics/*` 各 scope 接口新增可选 `range`(`today` / `yesterday` / `week` / `lastWeek` / `month` / `lastMonth` / `custom`)与自定义区间 `start` / `end`,服务端按 Asia/Shanghai 日历(周一为一周起点)解析成既有的窗口天数 + 结束日前移天数,下游窗口计算无需感知周期种类。不传 `range` 时仍按原 `days` / `endOffset` 取数,mao-cli 等既有调用方行为不变;自定义区间跨度上限 90 天、结束日不晚于今天且不早于 365 天前(与原有 clamp 一致)。
- **用量分析「协议分布」分组 key 归一化**:`/v1/admin/analytics/models` 的 `protocolStats` 此前对空串 protocol(存量写入约定中空串 = OpenAI 兼容)原样返回空 key,前端拿到渲染成空白。现在空串归一到 `openai-compatible`,NULL 仍归 `unknown`;mao-cli 等既有调用方拿到的 key 与界面展示一致。

### 管理后台
- **用量分析统计周期新增「本周 / 上周 / 本月 / 上月 / 自定义区间」,周期选择器改为弹层式布局**:此前工具栏右侧一排 6 个分段按钮(今日 / 昨日 / 3 / 7 / 30 / 90 天),日历周期与自定义区间都没有入口。现在点周期按钮弹出选择面板:「日历周期」6 项(今日、昨日、本周、上周、本月、上月)与「最近」4 项(近 3 / 7 / 30 / 90 天)分组平铺,底部「自定义区间」进入自定义模式后工具栏内联出现起止两个日期选择器——未来日期、一年以前、以及超过 90 天的区间在日历里直接置灰,从源头选不出非法区间;工具栏左侧的周期摘要同步带上前缀(如「本周 2026-10-26 ~ 2026-10-29(4 天)」),环比窗口紧随当前窗口。周期与自定义起止日落入 URL(`period=week`、`period=custom&start=&end=`),分享链接与刷新可还原,旧链接 `?days=7` 仍按滚动窗口兼容;mao-cli 同步新增 `--range` / `--start` / `--end`。
- **修复用量分析「协议分布」协议名显示空白**:模型页「协议分布」卡片此前提炼出的协议名对 OpenAI 兼容调用渲染为空白——模型表用空串表示 OpenAI 兼容,而聚合 SQL 的 `COALESCE` 只兜 NULL,空串原样成了分组 key,前端再裸渲染 key 就是一片空白。现在后端把空串归一到 `openai-compatible`(NULL 仍归 `unknown`),前端按场景同款汉化表显示「OpenAI 兼容 / Anthropic / OpenAI Responses」,未知 code 回落原文。
- **修复调用流水详情「协议」显示为「-」**:同一根因——OpenAI 兼容调用的 `llm_call.protocol` 是空串,详情弹窗的协议行直接显示「-」。现在显示中文协议名,未记录(NULL)显示「未知」。
- **用量分析「用户活跃排行」「用户 Token 排行」改为半宽同行排列**:用户 Tab 下这两张排行图此前各自整行、纵向堆叠,一屏看不到几张图。现在各占约一半宽度,宽屏下并排一行(图高仍随条目数自适应),「用户用量明细」表保持整行在下方;窄屏(≤768px)两张图仍各自整行,行为不变。
- **用量分析趋势页「流量」图改半宽,「Token」与「调用」合并为一张双轴图**:趋势 Tab 第一行原先是「流量」独占整行、下面「Token」「调用」两张半宽图,一屏要滚动两次才看全四类趋势。现在「流量」同样改为半宽,与合并后的「Token 与调用」图并排一行——左轴为对话 / 后台 Token 堆叠柱,右轴为调用次数(柱)与失败次数(虚线),Token 与次数量级差多个数量级,双轴各自缩放,tooltip 按系列套单位(Token 紧凑 / 次数千分位);窄屏(≤768px)仍各自整行,「质量」「成本」两图位置不变。
- **定时任务列表「完结」列两种状态统一为标签**:此前「已完结」渲染为灰色标签,「进行中」只是一段灰色文字,同一列两种视觉样式。现在「进行中」同样渲染为标签(绿色),与「状态」列启用/暂停的标签风格一致;列表页与详情弹窗同步修改,悬停提示说明文字不变。

### 前端(桌面 / Web / 安卓)
- **会话轨迹调用元信息「protocol」行显示中文协议名**:调用详情的元信息表格此前直接展示 `llm_call.protocol` 原值,OpenAI 兼容调用是空串、显示为破折号,`anthropic` 等 code 也是裸英文。现在与管理后台同款汉化(空串 = OpenAI 兼容,未记录 = 未知,未知 code 回落原文)。
- **中央 Git 面板:工作区自身即 git 仓库时不再显示仓库徽章行**:此前只要打开 Git 面板,顶部固定有一行显示当前仓库名(如 "mao")的徽章。当会话工作区自身就是一个 git 仓库时,它是唯一仓库、无处可切,这行只是重复用户已经知道的工作区目录名,没有信息量。现在这种情况下整行不再渲染,面板直接从分支状态条(分支 / ahead-behind / 远端状态)开始;仅当工作区自身不是 git 仓库、且一级子目录中存在 git 仓库(多仓库工作区)时,才显示仓库下拉选择器,行为不变。
- **「我的用量」抽屉统计周期改弹层式选择器,与管理后台同一套交互**:此前抽屉头部一排 9 个分段按钮(今日 / 昨日 / 本周 / 上周 / 本月 / 上月 / 近 7 天 / 近 30 天 / 自定义),393px 手机上内容宽约 438px、抽屉内容区只有 321px,只能让控件内部横向滚动看全;自定义区间还要再占一行的 daterange 选择器。现在点周期按钮弹出选择面板:「日历周期」6 项与「最近」2 项分组平铺,底部「自定义区间」进入自定义模式后,抽屉头部内联出现起止两个日期选择器——未来日期、一年以前、以及超过 90 天的区间在日历里直接置灰,从源头选不出非法区间;切到其他周期时自定义区间保留在内存里,切回来还是用户选过的那一段。弹层与内联日期条在 390px 窄屏下都不出视口、不撑出横向滚动。
- **中央 Git 面板:拉取 / 推送图标上移到分支状态条,与刷新按钮并列**:此前「变更」子页标题行右侧放着拉取(↓)、推送(↑)两个图标,刷新(↻)却在面板顶部分支状态条上,一组关联操作被拆成两处,切到「历史 / 分支 / Stash」子页时还看不到拉取推送入口。现在拉取 / 推送与刷新一起收敛到分支状态条右侧(顺序为拉取、推送、刷新),任何子页都能直接同步;禁用原因(没有可拉取的更新、没有可推送的提交、detached HEAD、远端状态未确认等)与侧边栏检查器里的口径完全一致,悬停即见。侧边栏检查器中的拉取 / 推送按钮位置不变。

## 0.0.374 (2026-10-28)

### 后端
- **移除已废弃的子代理工具 `delegate` / `delegate_followup` 及配套代码**:二者早已不在注册表中(后台子代理 `spawn_subagent` 系列上线后停用),本次删除实现、测试与残留装配(含 `SubagentInvocationService.createDelegate`、工具注册表死依赖、子代理上下文排除集合中的旧名)。
- **修复「会话轨迹」子代理会话链接对新工具失效**:轨迹页收集子代理链接的识别集合原先只含旧工具名,`spawn_subagent` / `subagent_followup` 产生的委派不会显示子代理会话链接;现将新旧工具名一并识别(旧名保留以兼容历史会话)。
- 说明:`delegate` / `delegate_followup` 的中文展示映射与结果摘要分支保留,老会话历史仍正常显示;旧版同步委派(invocationType=DELEGATE)只存在于历史数据,其恢复补记录逻辑保持不变。

## 0.0.373 (2026-10-28)

### 后端
- **移除飞书通道专属工具 `feishu_read_doc`**:飞书会话不再内置「读取飞书云文档」工具,配套删除 `feishu/doc-reader.ts`、工具注册与装配、结果摘要逻辑;读飞书文档统一走 `lark-cli` 技能(`docs +fetch`,支持 docx / wiki,且支持大纲 / 章节 / 关键词局部读取)。

### 前端(桌面 / Web / 安卓)
- 移除 `feishu_read_doc` 的工具名中文映射与参数预览。

## 0.0.372 (2026-10-28)

### 后端
- **工作区 Git 提交支持传入提交信息,新增「只生成」接口**:`POST /v1/files/workspace-git-commit` 新增可选 `message` 参数——传入时直接用它提交(仅校验非空 / 长度 / 非法字符,不限定格式),不传时保持「服务端 LLM 生成后提交」的既有口径(侧边栏仍走这条)。新增 `POST /v1/files/workspace-git-commit-message`(CLOUD 会话专用,LOCAL 一律拒绝):服务端组装变更摘要后只生成提交信息、不做任何写操作,供中央 Tab 两步提交的第一步使用;生成期间持仓库锁防止并发提交让消息过期,无待提交变更时与提交同口径报错。未新增权限码、无表结构变更。
- **修复会话压缩被「估算超配窗」误杀**:会话历史触发压缩后,系统原先会先估算压缩请求(全量历史 + 压缩指令)的 token 数,一旦 ≥ 压缩模型配置的 `contextWindowTokens` 就直接失败,提示「已达到或超过有效上下文窗口…请改用更大窗口模型或新建会话」,整个会话卡死只能新建。但该数值是后台按模型维护的稳定区间、并非供应商硬上限,配置偏保守时会话在还能正常压缩的水位就被判死。现在改为先发请求让供应商裁决:供应商接受则压缩照常完成;只有供应商明确以体积类错误(400 context length / token limit、413 请求体过大)拒绝时,才把该拒绝转成原来可操作的终态提示(估算值与压缩模型窗口口径不变),限流、服务端错误等其余压缩失败行为不变。压缩触发日志现同时记录请求估算值与配窗,供应商拒绝后可直接比对。
- **用量分析趋势接口新增每百万 Token 平均成本**:`/v1/admin/analytics/trends` 的每个趋势桶(按天 / 按小时)新增 `costPerMTokens`(桶内成本 ÷ 桶内 llm_call 调用 Token × 1e6,分母与成本同口径);桶内无调用(分母为 0)时为 null,前端断线不画 0。

### 前端(桌面 / Web / 安卓)
- **Git 管理器提交信息框的 LLM 生成按钮移入框内右上角**:中央 Git 管理器两步提交的 ✨ 按钮原先在提交信息输入框右侧、与输入框并排占宽度,窄栏下输入区被挤压。现在移入输入框右上角内侧——输入框顶部预留出按钮高度的内边距,文字从按钮下方开始,按钮悬浮固定不随内容滚动;输入框随预留高度一起变高(约 3 行文字可见)。placeholder 中「右侧按钮」的表述同步改为「右上角按钮」。右侧边栏 Git 页没有该输入框,行为不变。
- **Git 管理器「获取远端更新」与刷新按钮合并为顶部一个刷新图标**:中央 Git 管理器原先有两个功能重叠的入口——状态条右侧的「获取远端更新」(fetch 成功后本就会全量刷新各面板)和变更工具栏的 ↻(内部也带一次远端确认 fetch)。现在只保留状态条上一个刷新图标:有远端时先 `fetch --prune` 再刷新全部面板数据(toast 与活动日志口径不变),未配置远端的仓库也能点,退化为纯本地刷新(此前该场景顶部按钮禁用,只能靠工具栏 ↻);非 git 仓库与 Git 操作进行中仍禁用,按钮悬停说明原因。变更工具栏的 ↻ 在中央管理器隐藏,右侧边栏任务检查器的刷新入口不变。
- **中央 Tab「Git 管理器」的提交拆成两步**:变更视图顶部新增提交信息输入框——✨ 按钮调 LLM 生成提交信息填入输入框,可继续自由编辑,再点「提交」落库;生成期间 ✨ 旋转、提交期间按钮禁用,生成失败仅提示且不清空已填内容;切换仓库自动清空输入框,提交成功后清空。工具栏原有的「✓ 一步提交」按钮在该视图隐藏,避免两个提交入口。右侧边栏 Git 页提交保持原有「生成即提交」的一步式不变。
- **提交信息不再限定 Conventional Commits 格式**:此前本地与服务端都强制标题符合 `type(scope): 描述` 且正文为空行后的 `- ` 列表,用户手输或编辑 LLM 生成结果时常被「提交标题格式不合法」挡下。现在只校验非空、长度(64KB)与非法字符,消息原样落库(不再拆成多个 `-m` 被 git 用空行拼接改写);LLM 生成通道仍按原提示词产出规范格式。

### 桌面 Electron
- **`gitCommit` 的提交信息校验放宽**:`validateCommitMessage` 去掉 Conventional Commits 标题正则与正文列表校验,只保留非空 / 64KB 长度 / `\0` 检查;`git commit` 改为单个 `-m` 传整条消息,用户原文(含多段、无空行分隔的正文)不再被改写。

### 管理后台
- **用量分析「成本」图新增每百万 Token 平均成本线**:与桶内成本合计并排展示(成本合计走左轴,每百万 Token 平均成本走右轴、虚线区分,两条线量级差一个数量级,同轴会互相压扁);tooltip 两条线都按成本格式化。同时「质量」图改为半宽,宽屏下与「成本」图同行排列,窄屏仍各自整行。
- **用量分析「流量」图会话、消息合并为一张双轴图**:此前会话与消息是并排的两张单轴小图(窄屏下还要各占一行)。现在合并为一张图——会话走左轴(实线),消息走右轴(虚线、隐藏右轴分割线);两条序列同为计数但量级常差一个数量级(小时粒度会话多为 0/1),双轴各自自动缩放,小数值序列不再被大数值压成直线。图例居中,tooltip 两行分别给出原始计数值;小时 / 天粒度切换与数据缩放行为不变。

## 0.0.371 (2026-10-28)

### 后端
- **工作区 Git 管理器新增 9 个接口**:`workspace-git-log`(提交历史分页,可按路径过滤)、`workspace-git-commit`(单提交文件清单,merge 提交按 first-parent 统计)、`workspace-git-commit-file-diff`(单提交单文件前后全文)、`workspace-git-branches`(本地 / 远端分支、upstream、ahead / behind,空仓库兜底显示未出生分支)、`workspace-git-stashes`、`workspace-git-remotes`(fetch / push URL 合并为一条并脱敏凭证),以及写操作 `workspace-git-fetch`(fetch --prune,不动工作区)、`workspace-git-switch-branch`(切换前自动 stash、切换后恢复,恢复失败保留现场并给出 stash 引用)、`workspace-git-stash`(apply / pop / drop,产生冲突时保留现场不自动 reset)。全部按 sessionId + 可选 repoPath 定位仓库,LOCAL 会话一律拒绝,未新增权限码、无表结构变更。
- **用量分析趋势接口新增平均耗时序列**:`/v1/admin/analytics/trends` 的每个趋势桶(按天 / 按小时)新增 `avgDurationMs`(平均总耗时 = Σ`llm_call.duration_ms` ÷ 桶内调用数)与 `avgFirstTokenMs`(平均首字耗时 = Σ`llm_call.first_token_ms` ÷ 桶内流式调用数),口径与模型页「耗时均值 / 首 token 均值」逐条一致;桶内无调用时两个值均为 null,桶内无流式调用(全非流式)时只有首字均值为 null,供前端断线而不是画 0。

### 管理后台
- **用量分析「质量」图新增两条平均耗时线**:平均总耗时(每次 LLM API 请求总耗时的均值)与平均首字耗时(每次请求首字输出耗时的均值,仅统计流式调用)画进质量图——成功率 / 缓存命中率继续走左轴百分比,两条耗时线走右轴 ms 并以虚线区分;tooltip 按系列给单位(百分比带 %、耗时按 ms/s 折算),无调用 / 无流式调用的桶断线不画 0。

### 前端(桌面 / Web / 安卓)
- **修复 Git 管理器 Tab 首次打开误报「远端状态不可用」**:首屏只发只读状态查询,`remoteStatusAvailable` 恒为 false 且不带失败原因,徽标只能回落到默认文案「远端不可用」(实际只是还没向远端确认过),领先 / 落后数也停在本地缓存值。现在打开 Tab(切走再切回、切仓库同样)在本地状态出来后自动补一次远端确认(`fetch --prune`):确认期间徽标显示中性的「确认远端中…」,成功后变「远端可用」并显示最新 ahead/behind,失败则保留本地状态并给出真实原因(凭证失效 / 网络不通等)。非 git 目录与本地状态读取失败都不发确认请求;确认在途时切换会话或仓库,迟到的结果按序号丢弃,不会串写到新仓库。
- **新增中央 Tab「Git 管理器」**:会话中心区打开「Git」页签(一个会话一个单例页签,与文件 / diff / 文件管理器页签共存),把常用 Git 操作收进变更 / 历史 / 分支 / Stash 四个子视图——变更清单可直接提交 / 拉取 / 推送并打开工作区 diff;历史按 50 条一页滚动加载,展开某条提交看它的文件清单,点文件打开「单提交 diff」页签(页签 id 带提交 hash,同一路径的不同提交可以同时打开对照,重命名 / 移动工作区路径后仍指向同一提交);分支分组展示本地 / 远端(含 upstream、领先 / 落后计数与最近提交),点本地分支切换(有本地改动时自动 stash 并在切换后恢复)、点远端分支创建跟踪分支;Stash 可应用 / 弹出 / 丢弃(丢弃有二次确认)。多仓库工作区用顶部仓库下拉切换,状态栏显示当前分支、detached HEAD 警告、upstream 与领先 / 落后数、远端徽标(悬停显示脱敏后的 URL),「获取」按钮拉取远端更新、不可用时按钮上直接说明原因(未配置远端 / 多个远端无法确认等)。入口两处:任务检查器的 Git 摘要行点击、变更清单工具栏的展开按钮。
- **Git 管理器页签图标换成提交图(三点连线)图标**:此前页签栏没给 `git` 类型配专门图标,落到了默认的文件图标,看起来和一个普通文件页签没区别;现在与任务检查器里 Git 摘要行的入口图标一致。
- **移动端 Git 变更工具栏只留「展开」入口**:窄视口(手机 / 平板竖屏)下,任务检查器 Git 页签的变更工具栏原先并排摆着提交 / 拉取 / 推送 / 刷新 / 展开五个图标,窄屏上挤成一团还容易误触。现在只显示「在中央 Tab 打开 Git 管理器」一个按钮,四个操作移师展开后的中央 Git 管理器(那里的工具栏在窄视口下保持全量,不会空);桌面宽视口行为不变。

### 桌面 Electron
- **本地模式补齐 9 个 Git IPC**(`gitLog` / `gitCommitFiles` / `gitCommitFileDiff` / `gitBranches` / `gitStashes` / `gitRemotes` / `gitFetch` / `gitSwitchBranch` / `gitStashOp`):与云端 REST 同命令同解析口径;切换分支的自动 stash 复用拉取时的 stash 生命周期(`mao-auto-switch-*`),恢复失败保留现场;空仓库(未出生 HEAD)的分支列表用 `symbolic-ref` 兜底显示当前分支;远端 URL 一律脱敏后展示。

## 0.0.370 (2026-10-28)

### 终端 CLI(mao-cli)
- **新增 `mao signal-watcher` 模块,信号哨兵可全程用 CLI 配置**:按间隔探测外部 HTTP(S) 接口、判定值变化触发 Agent 的哨兵,此前只能在桌面端「设置 → 信号哨兵」自助配置——mao-cli 的知识库自 0.0.276 起就登记了「管理信号哨兵」的 CRUD 能力,但命令模块始终没有实现,属于空头承诺。现补齐 `list` / `get` / `create` / `update` / `delete` / `status` / `checks` / `test` 八个本人命令与管理端只读的 `admin-list`(需 `signal:read`),请求体形状与后端 `/v1/signal-watchers` 对齐:create 必填 `--name` / `--agent-id` / `--url` / `--prompt-template`,`--method` / `--headers` / `--body` / `--interval` / `--compare-mode` / `--json-path` / `--trigger-mode` / `--copy-from` 可选并按桌面端口径补默认值(GET / 30 分钟 / HASH / 每次新建会话)。update 是部分更新,只提交显式传入的字段,`--headers '{}'` / `--body ''` / `--json-path ''` 即显式清空;改判定字段会重置基线、掩码 `******` 请求头可原样回传等口径与桌面端一致。枚举越界、POST 缺请求体、GET 带请求体、间隔越界、JSON_FIELD 缺路径、禁改请求头、URL 非 http/https 等参数问题在本地即给出可读报错,请求不会发出。附 31 条回归用例(`agent-cli/test/mao-cli-signal-watcher.spec.ts`)。

## 0.0.369 (2026-10-28)

### 前端(桌面 / Web / 安卓)
- **修复大文件的变更 diff 显示成原始 patch 文本**:文件超过 512KB 时后端只发 unified patch(不含前后全文),前端此前把这段文本直接塞进一个普通 Monaco 编辑器、语言标成并不存在的 `diff`,结果看到的是带 `--- a/`、`@@` 头的纯文本,没有红绿高亮也不是 diff 视图(CHANGELOG.md 这类随版本累积涨过 512KB 的文件必中)。现在前端把 patch 解析回 original / modified 两个模型,按 `@@` 头把增删行摆回真实行号、未变更区用空行占位,继续用 Monaco diff 编辑器渲染:高亮、行内对照、行号、首处变更跳转、行区间引用都与普通文件一致,且渲染成本只取决于变更量、与文件大小无关。

## 0.0.367 (2026-10-27)

### 后端
- 会话标题生成不再吞掉未闭合引用围栏之后的文字:文件行区间引用畸形(缺合围栏)、或用户自己写了「第 N 行:」后接未闭合代码块时,只剥掉行号行本身,后面的真实问题保留进标题素材——此前会一路扫到文本结尾,用户的真实问题被丢掉,标题永久停在占位标题。

### 前端(桌面 / Web / 安卓)
- 助手回复底部操作栏不再放「引用」。要引用回复,在正文里选中文字,用选区上的「引用」。输入框里的引用改成贴着正文的短条:左侧细线、字号与附件标签一致,右边是关闭,不再铺成一整条灰底。点「引用」后光标会进输入框,可以直接接着打字。
- 修复 Diff 查看器里引用按钮不跟着换文件失效:代理改文件、切主题都会换 Monaco 模型,而换模型不触发选区事件——旧行号的快照还挂在按钮上,点下去会按旧行号从新文件取内容(引错或引空)。现在换模型即清快照、收起按钮,重新选中后可正常引用。
- 修复 HTTP 部署下回复引用按钮静默失效:引用 id 生成从裸 `crypto.randomUUID`(要安全上下文,HTTP 下是 undefined)改用仓库的 `generateUUID`,与其它入口一致。
- 修复输入框里行区间引用的「内容可能已变化」刷新会进撤销栈:此前每个文件变更事件都往撤销栈推一步,Ctrl+Z 会先撤销这个不可见的刷新而不是用户刚打的字。现在该刷新标记为不进历史。
- 引用助手回复里的表格时,行尾不再多出一个「 |」分隔符。

## 0.0.366 (2026-10-27)

### 前端(桌面 / Web / 安卓)
- **修复运行轨迹里关掉工具详情把整个抽屉链一起收掉**:从「轮次详情」的工具页点某条工具进入工具详情后,按 × / Esc / 点遮罩关闭会直接收掉所有抽屉、回到轨迹列表,想接着看该轮其它工具得重新下钻。现关闭只收当前一级:工具详情关掉回到轮次详情,再关才回到轨迹列表。

## 0.0.365 (2026-10-27)

### 前端(桌面 / Web / 安卓)
- **修复运行轨迹点任务名只开抽屉、展不开轮次明细**:0.0.346 把消息预览做成点击入口后,预览文字占满摘要行大部分宽度,且点击被 `stopPropagation` 拦下、不再触发行本身的展开切换——点任务名(最自然的点击目标)只能看消息原文,展不开详细轮次,只有点箭头 / 时间 / 耗时等边角区域才能展开。现恢复「点行内任意处(含任务名)展开 / 收起轮次明细」;用户消息完整原文改由展开区顶部(时间条带下方)常驻的「查看原文」按钮进入,消息详情抽屉的入口不丢。

## 0.0.364 (2026-10-27)

### 后端
- 用量汇总 / 趋势查询新增解码聚合字段(`decodeTokens` / `decodeMs`):只统计「可解码」的调用(流式、首字严格早于结束、有输出),非流式调用不计入,供前端按加权平均口径算输出速度,历史数据无需迁移。
- 管理后台模型统计新增「吞吐均值」列、会话质量新增「吞吐 p50 / p95」两项;延迟分位查询泛化为指标参数(首字 / 总耗时 / 吞吐三选一),吞吐口径与桌面端一致(输出 tokens ÷ 解码耗时)。

### 前端(桌面 / Web / 安卓)
- **用量抽屉显示 LLM 输出速度**:明细表新增「吞吐」列,按「输出 tokens ÷ 解码耗时」实时算出(非流式调用无首字耗时,显示「—」而不是拿总耗时凑数);总览新增「平均速度」卡片,趋势页新增「输出速度趋势」图,均为窗口内加权平均(Σ输出 ÷ Σ解码秒),不随分页波动。运行轨迹面板的调用元信息沿用同一口径,数字与明细列对得上。

### 管理后台
- 调用明细表新增「吞吐」列(移动端卡片、详情弹窗同步),导出的 CSV 一并包含该列;模型统计表新增「吞吐均值(tok/s)」列,会话详情质量项新增「吞吐 p50 / p95」。

## 0.0.363 (2026-10-27)

### 后端
- 会话标题不再被回复引用和文件行区间片段占满;用户自己写的代码块仍参与标题。

### 前端(桌面 / Web / 安卓)
- **回复选区引用、文件与 Diff 行区间引用**:助手回复里选中文字可引用到输入框,操作栏「引用」引用整段回复;文件预览和工具变更 Diff 的修改侧可引用行区间。引用发出去仍是普通消息文本。气泡、待发送队列、轮次目录和分叉标签会把开头的回复引用折成一行,避免挡住真正的问题。文件在引用后又被改过时,输入框标签提示「内容可能已变化」。终端、Git Diff 和安卓选区浮钮不提供这入口;安卓仍可用操作栏「引用」。

## 0.0.362 (2026-10-27)

### 后端
- **待办超过 3 分钟无人处理时外推提醒**:本地工具审批,以及桌面端仍连接时的提问,不再因为 WebSocket 在线就一直静默。挂起满 3 分钟仍未批准、拒绝或作答,经已开启的飞书 / 钉钉 / 邮件发一条提醒;审批提醒只含会话标题和固定说明,不含待执行命令。人离线时的提问仍立即提醒。人在发出前处理完则取消这条提醒;已经发出的不撤回。完成 / 失败通知仍是桌面送达即不再外推。

### 终端 CLI(mao-agent)
- 需要在终端里确认的 shell,会等你选完允许或拒绝,才告诉服务端「这单审批已经处理」。确认框还没出来时不再提前销掉 3 分钟提醒。

## 0.0.361 (2026-10-27)

### 后端
- **信号哨兵取消 20 个数量上限**:每用户最多 20 个哨兵的硬上限移除(V174 迁移只加字段、不改数据),新建与复制都不再被数量卡住;桌面列表页的上限提示与新建按钮禁用态一并移除。
- **信号哨兵新增「复用会话」触发方式**:新建 / 编辑哨兵时可二选一——「每次新建会话」(原行为,默认)或「复用会话」。选「复用会话」后首次触发自动为本哨兵建一个专属会话,之后每次判定值变化都接着这个会话聊,上下文持续累积,适合需要记住上次结论的场景(例如「上周提的那个接口修了没」)。复用的会话 id 由服务端维护,前端只选模式、不选会话;会话被删除、归属用户 / 类型 / Agent 变化后指针自动失效并在下次触发重建,无需手动干预。在两种模式间来回切换即可随时重置指针。
- **信号哨兵支持一键复制**:列表点「复制」直接打开预填的新建对话框,名称自动带「副本」后缀;敏感请求头以掩码行带过去(留空 = 沿用源哨兵的凭据)、请求体留空 = 沿用源请求体,明文不经过前端。删光全部请求头行表示「不要请求头」,与编辑态「删光行 = 显式清空」的语义区分开;复用的会话指针不继承(副本首次触发自建)。
- **哨兵会话归入独立「信号哨兵」分组**:哨兵触发产生的会话来源为 `signal`,在任务面板 / 会话列表里归入专属分组「信号哨兵」(排在各分组最后),不再混进「临时工作区」或被算进别的来源分组。该分组会话不计未读角标,但收件箱通知与出站事件订阅照常送达——该看到的通知不丢。用户不能再手工把会话来源改成 `signal`(`POST /v1/sessions` 与来源修改接口只接受 web / embed),只能由哨兵触发经开放运行链路产生。
- 管理后台(`signal:read`)只读清单新增返回 `triggerMode` 字段。

### 前端(桌面 / Web / 安卓)
- 设置 → 信号哨兵:新建 / 编辑表单新增「触发方式」单选(每次新建会话 / 复用会话),列表卡片显示当前触发方式角标,不点开对话框也能看出差别;新建按钮不再有 20 个上限的禁用态与提示。
- 列表操作区新增「复制」按钮:点它只打开预填的新建对话框,不调任何写接口(不会随手多出一条哨兵)。复制态对话框标题为「复制哨兵」并提示敏感字段的继承语义,POST 请求体留空即可沿用源请求体。
- 任务面板 / 会话列表新增「信号哨兵」分组(排在各分组最后),哨兵会话不再落入「临时工作区」;从轮询记录深链打开哨兵会话时分组标签正确显示。
- 收件箱里来源为信号哨兵的通知显示「信号哨兵」角标,点击仍照常跳回会话。

### 管理后台
- 信号哨兵只读清单新增「触发方式」列(每次新建会话 / 复用会话)。

## 0.0.360 (2026-10-27)

### 前端(桌面 / Web / 安卓)
- **Mermaid 图支持点开看大图**:修好节点文案丢失后发现新问题——聊天气泡里的图被强制缩到气泡宽,宽架构图(数千 px 宽)的 16px 基准字号被压到 4~5px,仍然读不了,而原有的横向滚动因图永远不大于气泡从未生效。现在点图打开全屏查看器,缩放用 5%~400% 的离散档位:`+`/`-` 逐档切换,档位可预期、点按即达,且 SVG 是矢量、放大不失真;中间的数字显示当前倍率,点一下回到 100%;「适应」按舞台宽把整图收进来(只缩不放,小图不虚胖)。打开时若「适应」会把文字压到 12px 以下(窄屏看宽图的常见情形),默认退到 75% 先保可读,看全靠「适应」或往低调。触屏没有 hover,光标与提示都不可见,故图上另给一个常驻「放大」按钮作入口。Esc 或点遮罩关闭;查看器里的图会换掉根 id,避免与气泡中的同源图 id 冲突。
- **移除对话区顶部 sticky 工具条**:上滑看历史时弹出的灰条(「回到顶部」按钮)、以及定位到旧消息时的「正在查看较早的消息 / 回到最新」条幅一并移除——阅读长会话时它频繁弹出、遮挡内容,而大多数时候条上只有一个按钮。移除后对话区完全跟随手动滚动:滚回底部即恢复自动跟随;用搜索 / 轮次目录定位到旧消息后,在该会话发消息或切换会话再进入即可拉回最新(边路任务面板的同款条幅不受影响)。
- **任务面板多选操作条移到列表上方**:原先「已选 N / 归档 / 取消」固定在面板最底部,长列表里勾选几项后要滚到最底下才能点归档。现移到「任务」标题栏正下方、列表之上,勾选与操作同屏可见;分隔线由 `border-top` 改为 `border-bottom`,桌面端外观与交互不变。顺带修了紧凑布局(≤768px / 触屏,面板宽 140px)下该行放不下两个按钮、「已选 N」被 `flex:1` 压到 3px 宽导致计数不可见的问题:紧凑断点下计数独立成行,桌面端仍是单行。

## 0.0.359 (2026-10-27)

### 前端(桌面 / Web / 安卓)
- **修复对话里的 Mermaid 架构图只剩空框、节点文案全部消失**:图能正常画出来、连线也对,但每个节点方框里没有任何文字,subgraph 标题却还在。原因是渲染配置把 `htmlLabels: false` 写在了 `flowchart` 下面,而 mermaid 只读顶层的 `config.htmlLabels`;这个键不在 `defaultConfig` 里、被当作 `undefined`,于是 `?? true` 回落成默认值 true,节点标签全部走 `foreignObject` 渲染,随后被 SVG 二次消毒的 `FORBID_TAGS` 整块剥掉。改为顶层 `htmlLabels: false` 后标签改走原生 SVG `text`/`tspan`,中文、`<br/>` 换行、暗色主题均正常。该失败无报错、无降级提示,只是静默丢字,故补了配置形状的回归用例。
- **任务面板「多选归档」图标减重**:该入口的对勾图标笔画比同排的聚焦模式 / 刷新 / 新建粗一倍,视觉上不协调;换成同字形、笔画与邻座一致的细款对勾,按钮位置与交互不变。

## 0.0.358 (2026-10-27)

### 前端(桌面 / Web / 安卓)
- **移除任务列表标题过滤**:0.0.351 上线的标题过滤入口(任务面板头部放大镜按钮)实际使用场景太少——列表本就按会话分组 + 最近活跃排序,滚动扫视比打字过滤更快,且想不起标题时过滤也无从下手。任务面板头部恢复为「聚焦模式 / 多选 / 刷新 / 新建」,相关空态文案一并移除。
- **移除对话区「只看回复」开关**:该开关与同期上线的轮次目录解决同一个问题(长会话回看),功能重叠;日常以前看新回复为主,开关大多闲置。对话区顶部工具条保留「回到顶部 / 回到最新」。
- 批量归档、轮次目录、回到顶部不受影响;后端会话列表 `keyword` 参数保留(管理后台检索在用)。

## 0.0.357 (2026-10-27)

### 后端

- **修复 V172 迁移 SQL 语法错误导致后端无法启动**:`V172__llm_call_request_capture.sql` 第 3 行注释写成 `--(应用层截断…`,MySQL 要求 `--` 后必须有空白字符才识别为注释,该行被当作 SQL 解析直接报 `You have an error in your SQL syntax`,Flyway 迁移失败、后端拒绝启动(蓝绿部署健康检查失败、切流中止,0.0.355 起新实例无法上线)。补上空格即可;从零部署的新实例两条迁移按顺序执行,已记录失败的实例需删除 `flyway_schema_history` 中 `success=0` 的 V172 行后重试(DDL 已回滚,`llm_call` 无残留列)。

## 0.0.356 (2026-10-27)

### 前端(桌面 / Web / 安卓)

- **修复信号哨兵「轮询记录」抽屉在手机上只剩右侧几列**:抽屉固定宽 560px,在 393px 安卓窄屏下左边缘落在屏幕外 167px,「时间 / 状态码」两列被整个推出屏幕,只看到「结果 / 耗时」及之后的内容。抽屉与同页两个弹窗(新建/编辑哨兵、探针测试结果)统一改为 `min(目标宽, 100vw - 32px)` 随视口收缩,与「我的用量」抽屉、收件箱抽屉同一口径。表格列宽同步收紧为「时间 128 + 状态码 56 + 结果 60 + 耗时 68 = 312px」,手机上这四列关键信息一屏可见;「触发/告警」「详情」两列改为省略号截断 + 表格横向滚动查看(表头与表体由 Element Plus 同步)。分页条在窄屏改为左对齐并允许容器内横向滚动,页码多时不再被推出屏幕。列宽必须写在模板里:EP 的 `table-layout: fixed` 以 `col` 属性为准,用 CSS 覆盖 `col` 宽度无效。桌面端抽屉仍是 560px,观感不变。


## 0.0.355 (2026-10-26)

### 后端
- **新增「我的预算」接口**:`GET /v1/me/budgets` 返回当前登录用户可见的月度预算全貌——生效中的全局预算 + 分配给本人的用户级预算(额度、当期消耗、状态、周期、重置时间)。仅看得到自己的,不含他人用户级与 Agent 级预算;消耗口径与管理后台一致(成本类除以百万单位)。

### 前端(桌面 / Web / 安卓)
- **用量抽屉新增「预算」Tab**:顶栏铃铛 → 用量抽屉现在有四个 Tab(总览 / 趋势 / 明细 / 预算)。预算 Tab 展示每条生效预算的额度、当月已消耗、状态(正常 / 已越线 / 已拦截)与月度重置时间;没有生效预算时显示友好空态(说明团队预算由管理员配置),底部附当月各 Agent 消耗占比参考。
- **收件箱预算提醒不再点不动**:收到「用量预算提醒」后点击条目,直接跳到用量抽屉的「预算」Tab 查看额度全貌;其余类型通知的点击行为不变。
- **预算用尽被拦时有入口**:任务因月度预算超限被拒绝时,错误横幅新增「查看我的预算」按钮,一键跳到预算 Tab(横幅原有的消耗 / 上限文案与重试行为不变)。

## 0.0.354 (2026-10-26)

### 前端(桌面 / Web / 安卓)
- **定时任务可以改了**:设置页定时任务面板每行新增「编辑」——名称、Cron 表达式、任务提示词、一次性开关、失败重试 / 重试间隔 / 错过补偿都能改,以前只能启停和删除,改个提示词得让 Agent 代劳或登管理后台。Cron 边打边校验并实时列出未来 3 次触发时间(北京时间);改成「固定月 + 日」形态时一次性开关自动跟随,手动拨过开关后以开关为准,不一致时提醒。已完结任务保存后若还能算出下次触发时间会自动重新激活,并提示已切回「进行中」。行内现在始终显示重试 / 间隔 / 错过补偿的当前值(默认值弱化);启停仍走行内开关,编辑对话框不改变运行状态。
- **工作区文件预览扩展到 CSV / 压缩包 / 表格 / 文档 / 音视频**:文件树双击打开的文件,除原有 Markdown / PDF / 图片 / 文本外新增五类可直接阅读——CSV / TSV 渲染成表格(分隔符自动识别、5000 行截断、hover「复制 CSV / 复制 Markdown」);zip 展开为条目列表(名称 / 大小 / 压缩率,点条目在覆盖层里预览文本、图片、PDF,其他类型与超大文本直接单条目下载);xlsx / xlsm / xltx 渲染为多 sheet 表格(标签切换、同样支持复制);docx 转为排版后的正文(外链点击与 Markdown 同链路);mp4 / webm / mov / m4a / mp3 / wav / flac / ogg 内嵌播放。无需再下载到本地找外部工具,安卓壳与 Web 同源获得该能力。
- **预览降级都有明确出口**:zip 条目数 / 解压总大小 / 单条目超限、表格超 20MB、文档超 10MB、媒体超 50MB、压缩包条目数据损坏、播放失败(如缺编解码)时,一律提示原因并给出「下载后在本地查看」或「在 Finder 中显示」的可点出口,不再出现「点击没反应」或只有一句文案的死路;解析库按需加载,不含这些格式的会话首屏体积零增长。

### 管理后台
- **「提示词版本」入口挪到 Agent 编辑页**:列表行操作去掉「提示词版本」,改到编辑弹窗「角色提示词」Tab 的角色定义下方(仅编辑已有 Agent 时出现,创建 / 复制时还没有版本可看)。回滚成功后编辑页的角色定义同步刷新为历史版本内容,不用关掉弹窗再打开确认。

### 后端
- **Cron 预览端点对普通用户开放**:`cron-preview` 是纯计算端点(不读任何任务数据),此前误挂了「跨用户读」权限门,默认角色用户在桌面端编辑定时任务时预览 Cron 会 403;现在登录即可用。

## 0.0.353 (2026-10-26)

### 前端(桌面 / Web / 安卓)
- **「只看回复」开关不再打断自动跟随**:修复切换开关会把「跟随中」永久打成「上滑中」的问题——此前开启/关闭一次后,后续流式新内容就不再自动滚动,只能手动点「回到最新」。现在正在跟随时开关只切过滤态(内容塌陷/展开仍自然写底),仅当已上滑看历史时才显式暂停。
- **任务列表过滤失败有收口**:过滤请求失败时提示「过滤任务列表失败,请稍后重试」并清空勾选集,不再出现「过滤图标是激活态但列表还是旧数据、批量归档却打到已过滤掉的会话」。
- **新手引导连点智能体串答案**:快速连点两个智能体卡片时,先发的详情请求后返回会把后选中智能体的推荐问题覆写成先点那个的;现在按请求代际丢弃过期响应(过期请求失败也不会误清空当前推荐问题)。
- **运行轨迹「请求构成」同名工具徽标**:同一轮内多次调用同名工具时工具名徽标补上序号键,避免 Vue 复用 DOM 导致徽标显示错乱。

### 后端
- **飞书绑定回调页不再假成功**:ECP 渠道绑定完成后若待恢复消息未能重放(绑定链接已过期 / 被更新的链接替换 / 重放失败),回调页显示「绑定未完全成功」并提示回飞书重新发送消息;该飞书身份已绑定到其它 Mao 账号时显示「绑定未完成」,不再一律报成功。
- **压缩后补抓工具结果不再撑爆上下文**:按 callId 补洞查询在 compact 场景下改走紧凑列(剔除附件 `data_uri`、带思考内容),补抓不再把已压缩掉的多模态原文整个拉回上下文。
- **经验库索引原子写去跨进程互毁**:临时文件改带进程唯一后缀,两个进程同时重建索引不再互相清掉对方的半成品;顺带清理历史固定名残留,rename 失败不再留 `.tmp` 垃圾。

### 管理后台
- **会话详情「加载更多」防跨会话串页**:keep-alive 复用下「加载更多」在飞时切到别的会话,旧会话的迟到响应不再 prepend 进新会话的消息列表(游标也不再被旧会话的 id 空间覆写,后续加载更多不会撞 beforeMessageId 归属校验报错)。

### 桌面 Electron
- **拖大文件进 LOCAL 会话不再卡死界面**:本机附件拷贝改异步执行,主进程单文件硬封顶 512MB(此前同步拷贝大文件会把主进程阻塞到整个界面冻结)。超限 / 空文件 / 符号链接仍按既有口径拒绝并有提示。
- **通知点击跳转不再丢**:应用未启动或窗口已销毁时点击系统通知,跳转意图先由主进程排队,窗口就绪后补发;修复了通知监听注册晚于点击、跳转静默丢失的问题。

## 0.0.352 (2026-10-26)

- **管理后台**
  - **会话列表关键词新增「搜索范围」**:默认「仅对话消息」,只匹配标题、摘要与用户/助手消息正文;工具输出、读到的文件内容与 `ls` 文件名不再把无关会话顶上来(实测同一关键词命中会话由 117 个收敛到 40 个)。需要反查工具输出时切「全部消息(含工具输出)」,保留旧口径。
- **后端**
  - **管理端会话检索新增 `messageScope`**:`GET /admin/sessions` 与 `/admin/runtime/sessions` 支持 `CONVERSATION`(默认)/ `ALL`,召回、总数与命中片段 `matchSnippet` 同口径;非法值直接报参数错误。

## 0.0.351 (2026-10-26)

- **前端(桌面 / Web / 安卓)**
  - **任务列表标题过滤**(技术方案 2026-10-26-session-list-navigation):任务面板头部新增过滤按钮,展开输入框后按标题关键字过滤(300ms 防抖,回车立即生效,清空一键还原)。过滤词收敛为单一状态源,标准模式分组预览、「展开更多」分页与聚焦模式全量列表同口径生效;手动刷新、WS 重连静默重拉、恢复归档三条路径都不会丢过滤条件;无匹配时显示「没有匹配的任务」专属空态。
  - **任务多选批量归档**:标准模式新增多选入口,勾选后底部操作条一键批量归档;运行中(含等待审批 / 取消中 / 恢复中)与已归档的任务自动跳过并在提示里计数(「已归档 N 个,M 个跳过」),失败条目原位保留可重试;批量归档与单条归档可混用,归档区顺序一致。
  - **长会话导航**:对话区右缘新增轮次目录把手,点开列出已加载轮次(序号 + 摘要 + 时间 / 步数耗时),点击即定位到该轮用户消息并高亮,执行中的轮次不列入目录;目录只索引已加载轮次,上滑分页补齐后自然更新。
  - **「只看回复」开关**:对话区顶部常驻工具条新增开关,开启后隐藏历史轮次的工具步骤与思考块、只留对话主干(摘要行、文件变更、压缩标记保留),关闭即还原。
  - **回到顶部按钮**:上滑离开底部后常驻工具条出现「回到顶部」按钮,点击回顶并保持不跟随(流式内容增长不把视野拽回底部)。

- **后端**
  - **移除会话收藏与置顶**(V173):下线收藏与置顶能力——删除 `PUT /sessions/:id/favorite`、`PUT /sessions/:id/pin` 端点、相关服务方法与 VO 字段,`session` 表 drop `is_favorite` / `is_pinned` 两列(V173 迁移,随启动执行);会话列表与分组排序统一为「活跃任务优先 → 更新时间倒序」(此前置顶键插在排序最前,且翻转置顶会连带 bump `updated_at`)。分享页、子代理面板、边路任务行为不变。

- **桌面 Electron**
  - **LOCAL 附件支持(技术方案 2026-10-26-electron-shell-completeness)**:LOCAL 模式下发送非图片文件不再被拒绝——发送时按会话懒拷贝到本机 `~/.mao/attachments/<服务器host>/<会话id>/`(按服务器与会话隔离,文件不出本机),消息以 `@绝对路径@` 形态追加,Agent 通过绝对路径读取;单文件超 100MB / 空文件 / 目录 / 符号链接被拒绝并有明确提示,重名自动追加 `-1`、`-2` 序号。图片附件仍走既有上传链路(多模态需服务端可读)。首次暂存文件时提示「文件将在发送时复制到本机 Mao 目录」。
  - **托盘与关窗行为**:关闭窗口默认最小化到托盘(任务继续执行),托盘菜单提供「显示/隐藏主窗、新建任务、退出」。关窗行为可在设置 →「桌面」切换为「直接退出」(切换时明确提示退出后将收不到系统通知)。macOS 使用 Template 托盘图标自动适配深浅菜单栏。
  - **系统通知与角标**:任务完成 / 待审批 / 待提问等通知改由桌面主进程投递,窗口最小化或隐藏到托盘时也能收到;点击通知回到对应会话(隐藏窗口自动恢复)。macOS dock 角标跟随未读消息数。通知授权被拒时给出可读提示。
  - **全局快捷键与开机自启**:默认 `Alt+Shift+M` 任意位置唤起/隐藏主窗口(可在设置 →「桌面」录制新组合键或清除;被其他程序占用时注册失败并提示);开机自启以隐藏模式驻留托盘(仅打包后可用,开发模式禁用)。
  - **单实例锁**:重复启动聚焦已运行实例,不再开多个进程。

- **终端 CLI(mao-cli)**
  - 移除 `session favorite` / `session pin` 命令与相关文档(能力随后端一并移除);`mao session` 其余命令(mark-read / archive / list 等)行为不变。

## 0.0.350 (2026-10-25)

- **前端(桌面 / Web / 安卓)**
  - **回复渲染完整度:Mermaid 图、LaTeX 公式与表格复制**(技术方案 2026-10-25-message-rendering)。回复中的 ` ```mermaid ` 代码块原位渲染为 SVG 图(跟随浅 / 深主题,图下方「查看源码」折叠内保留可复制的原代码块,语法错误或加载失败保持代码块形态不打断阅读);行内 `$...$` 与块级 `$$...$$` 公式由 KaTeX 渲染(价格语境「$5 和 $6」、纯数字编号不误判,公式渲染出错回落原文);Markdown 表格 hover 出现「复制 Markdown / 复制 CSV」工具条(CSV 引号 / 换行 / 逗号正确转义,Markdown 优先按原始表源码复制保真内联格式)。聊天、文件预览、运行轨迹、技能预览与公开分享页共用同一渲染链路。安全:mermaid 以 `securityLevel: strict` + 关闭 HTML labels 渲染,SVG 产物经独立白名单二次消毒(script / foreignObject / 事件属性一律剥除);KaTeX 固定 `trust:false`;主渲染链 DOMPurify 白名单未放宽。
  - **按需加载与流式体验**:mermaid / katex(含字体与 CSS)全部动态 chunk,构建新增 `check-main-chunk` 断言主 chunk 不含两者内部字面量——不含图与公式的会话首屏体积零增长;任务执行中(流式)的回复暂以代码块形态显示,任务结束后自动渲染出图(避免流式期间整段重绘闪烁),SVG 按源码 + 主题缓存,主题切换即时换肤。

## 0.0.349 (2026-10-25)

- **前端(桌面 / Web / 安卓)**
  - **运行轨迹抽屉升级:回复快照与请求构成**(技术方案 2026-10-25-run-trace-content-and-prompt)。轮次抽屉从单页改为四个 tab,默认「回复」:本轮模型回复的 markdown 正文与思考内容折叠块直接在轨迹里看,崩溃恢复 / 中断导致回复未落库时显示明确空态而非空白;「工具」tab 收纳原「本轮工具」清单(点击仍可跳工具抽屉);「计时与元信息」保持原元信息表。轮次抽屉与工具抽屉都新增「请求构成」tab:该轮实际发送的系统提示词原文(超长截断带「已截断」徽标,支持换行 / 原样切换)与工具名清单——「这轮为什么行为变了」终于能看到当时的提示词。升级前产生的历史调用在请求构成 tab 显示「未记录(历史行)」,不回填。
  - **run 摘要行的消息预览可点**:点开该条用户消息的完整原文抽屉(多模态消息合并文本分片并显示「图片 ×N」附件计数,原始分片数组收进折叠详情;编辑重发过的消息标注「已编辑」),不再只有 80 字预览。
- **后端**
  - **llm_call 请求构成采集(V172)**:`llm_call` 新增 `system_prompt`(MEDIUMTEXT)与 `tools_json`(JSON)两列,RecordingLlmAdapter 在每次调用落库时从请求原文采集系统提示词全文(超过 256K 字符截断并带原长标记)与工具名清单;采集是纯读取,不改请求、不影响前缀缓存。历史行不回填。
  - **新增轨迹消息详情端点** `GET /v1/sessions/:id/trace/records/message/:messageId`(会话归属校验同既有记录详情端点,非法参数返回空态 VO);轮次详情 VO 增加 `messages`(本轮窗口内的助手消息正文与思考,窗口口径与「本轮工具」一致)。轨迹主列表接口与导出结构不变,正文一律按需拉取。
  - **技能小节提示词加固(防跨租户误用技能)**:系统提示词「可用技能」小节新增边界声明——只能使用列出的技能、不得为寻找更多技能而列举/搜索文件系统、需要未列出技能时告知用户由其决定而非自行绕过;技能目录项去掉「目录:」行只保留 SKILL.md 文件路径,减少技能目录布局暴露(目录可由文件路径一步推出)。纯提示词层调整,技能 staging 与工具执行逻辑不变。
## 0.0.348 (2026-10-25)

- **后端**
  - **压缩提示词去「交接」化**:会话压缩的注入摘要与生成指令此前通篇用「交接 / 接手 / 另一个 Agent」口吻(注入消息直接命令「请立即接手并继续执行」),触发过压缩的会话里 Agent 会跟着对用户说「接手任务…」「已接手并完成…」,听起来像换了人。现改为「你自己继续同一任务」的延续框架:注入标题改为「会话压缩摘要」,明确任务与执行者未变、对用户就是同一任务的延续、无需解释压缩机制;摘要生成指令同步改写并禁止摘要正文出现「交接 / 接手」字样。注入外壳每次请求时重建,存量旧会话下次请求即生效(旧摘要正文的存量措辞待下次压缩自然消失)。
  - 上下文构成分节标签由「交接摘要」改为「压缩摘要」(manifest 下发,随请求更新)。
- **前端(桌面 / Web / 安卓)**
  - 上下文构成 tooltip、构成说明与 mao-cli 文档同步改为「压缩摘要」措辞,无结构改动。

## 0.0.347 (2026-10-25)

- **前端(桌面 / Web / 安卓)**
  - **新用户首启向导**:首次登录工作台且实例已就绪时,弹出三步引导——选智能体(含推荐问题预览)、CLOUD / LOCAL 执行边界图解、预填一条示例任务到输入框(发送仍由用户手点);完成或跳过后不再出现(一次性体验,无重进入口)。实例未配模型 / 无可用智能体时改为「等待管理员配置」提示页而非报错对话。
  - **空状态按根因分流**:任务列表与智能体选择器为空时,按实例就绪状态给出针对性指引(模型未配 → 联系管理员;无智能体 → 联系管理员创建),替代笼统的「暂无」。
- **管理后台**
  - **实例就绪横幅**:管理员登录后,默认密码未改(红色,点击直接打开改密弹窗)、模型未配置、无启用智能体三项未就绪时在顶栏下方常驻提醒并可直跳对应页面,全绿自动消失;可折叠当日不再显示(有红牌项时次日重现)。
  - **Agent「从模板开始」**:Agent 列表新增入门模板入口(首批「信息助理」:联网检索 + 带来源回答),一键预填名称 / 提示词 / 推荐问题,可改后保存为普通 Agent。
- **后端**
  - **就绪度接口**:新增 `GET /v1/readiness`(默认密码是否仍为 admin123(仅管理员可见真实值) / 可用模型数 / 启用智能体数 / 当前用户向导状态)与 `PUT /v1/readiness/onboarding`(向导完成 / 跳过落库,幂等)。新增 `V171` 迁移(`user_onboarding_preference` 表);存量用户按「已有会话历史」运行时静默判定,不弹窗、不回填。
  - **入门 Agent 模板**:新增 `GET /v1/agent-templates` 只读常量端点(模板只依赖内置联网工具,全新实例创建后即可直接跑通)。

## 0.0.346 (2026-10-25)

- **前端(桌面 / Web / 安卓)**
  - **会话 ID 可见可复制**:右侧边栏头部新增会话 ID 行(如「会话 #123」),点击整行即复制,遇问题时可把精确会话 ID 提供给管理员;ID 跟随当前展示对象(主会话 / 边路任务 / 子代理 Tab 各自显示对应会话)。此前客户端(Electron / 安卓无地址栏)完全看不到会话 ID。
  - **执行异常横幅支持一键复制会话 ID**:报错横幅新增「复制会话 ID」按钮,把排障入口放在用户看到报错的最直觉位置;新建任务尚未落库(无会话)时不显示。

## 0.0.345 (2026-10-25)

- **前端(桌面 / Web / 安卓)**
  - **修复粘贴「图文混排」内容丢文字**:从网页 / Word / 飞书文档复制「一段文字 + 截图」粘贴进聊天输入框时,此前只有图片进入待发列表、整段文字被静默丢弃;现在文字按原段落一并插入输入框(2026-10-25 前端评审 #2)。
  - **收件箱类型徽章暗色主题适配**:任务完成 / 失败 / 待问卷 / 待审批 / 日报五类徽章此前沿用亮色口径的硬编码深色文字,暗色主题下对比度不足难以分辨;现在暗色下换用亮色文字口径(2026-10-25 前端评审 #3)。
  - **主包瘦身**:图标注册从全量 293 个 Element Plus 图标改为按实际使用注册 71 个(与 admin 同一按需口径),未压缩主包减小约 121KB;冷门图标不再进首屏包(2026-10-25 前端评审 #5)。
- **管理后台**
  - **经验库新建撞名不再静默覆盖**:跨成员共享的经验文档此前「新建经验」输入已存在文件名保存时直接整文件覆盖、无确认无提示;现在保存前弹覆盖确认,且空文件名直接拦截(2026-10-25 前端评审 #1/#6)。
  - **经验库错误提示不再双重弹出**:打开经验库 / 读取文档失败时拦截器与页面各弹一次 toast、文案还不一致;统一依赖拦截器单次提示。行内「编辑」按钮读取内容期间转 loading,慢网络下不再像点击无效(2026-10-25 前端评审 #4/#6)。


## 0.0.344 (2026-10-24)

- **前端(桌面 / Web / 安卓)**
  - **子代理 Tab 冷启动还原**:上次停在某个子代理 Tab 时,退出 App / 刷新后自动回到该 Tab(此前始终回落主会话);其余子代理 Tab 仍不自动重建,避免刷新刷屏,与深层边路任务的点名还原(0.0.343)同一机制。
  - **边路任务 Tab 关闭后重开,刷新不再丢失**:从检查器 / 左侧列表重新打开曾被手动关闭的边路任务 Tab 时清除「用户曾关闭」标记(此前只有从搜索结果重开才清),退出重开 / 刷新后该 Tab 不再被再次隐藏。
- **后端**
  - **修复 Flyway 迁移版本号重复导致后端无法启动**:`V169__agent_experience_library.sql` 与 `V169__site_base_url.sql` 占用了同一版本号(不同分支合并冲突),Flyway 启动时报 `duplicate migration version 169` 直接拒绝启动,蓝绿部署健康检查失败、切流中止。站点根 URL 迁移顺延为 `V170__site_base_url.sql`,从零部署的新实例两条迁移按顺序执行;已应用 V169 的存量实例不再重复执行(`INSERT IGNORE`,无副作用)。)

## 0.0.343 (2026-10-24)

- **前端(桌面 / Web / 安卓)**
  - **修复深层边路任务退出重开后不回原 Tab**:从边路会话里再 fork 出来的二级及更深任务,退出 App / 刷新后停在主会话 Tab,必须手动在右侧边栏点一次边路任务标题才能切回去。边路任务支持任意深度 fork(0.0.238)后,刷新恢复被收敛成「只重建直接子级」,深层任务的 Tab 不再重建,持久化的激活态因此匹配不到目标而被静默跳过。现在按持久化激活态点名恢复那一个深层任务:仅「上次正停在其上」的会被重建,其余深层任务维持不自动重建(避免刷新刷屏),被用户关闭过的也不复活。

## 0.0.342 (2026-10-24)

- **后端**
  - **新增 Agent 团队经验库(渐进式发现)**:每个 Agent 一个跨会话、跨成员共享的经验库,位于共享目录 `<MAO_SHARE_DIR>/agent-experience/<agentId>/`。系统提示只注入一小节固定「路标」(何时 grep 索引、何时沉淀新经验),长经验正文不占提示词;Agent 用现成文件工具经工作区符号链接 `团队经验/` 直接读写,无需新工具。
    - CLOUD 会话创建时自动挂载:建库目录、生成索引、在工作区放符号链接;git/项目工作区自动把 `团队经验` 写入 `.git/info/exclude` 防误提交;Agent 关闭开关后链接自动移除。仅 CLOUD 生效(LOCAL 文件工具跑在用户本机,不注入路标)。
    - `INDEX.md` 索引自动生成:写文件/编辑文件工具落盘后扫描子文档 frontmatter(title/keywords/summary/applicable/last_verified)重建,文件名倒序、一行一条、grep 友好;删除与 shell 旁路写入由下次会话挂载或后台读取兜底自愈。
    - 容器模式共享目录挂载由只读改为读写(经验库需容器内写入);挂载 mode 参与容器指纹,升级后存量容器自动重建。管理员投放内容防误删从挂载层退化为信任 + 快照兜底(建议每日备份整个 share,见 DEPLOY)。
    - 新增 `V169` 迁移:`agent.experience_library_enabled`(默认启用,按 Agent 可关)。
    - 新增管理接口六个:开关 PATCH、文档列表/读取/写入/删除、强制重建索引(`/v1/agents/:id/experience-library/**`)。
    - 经验库加固:库目录与子文档读写拒绝符号链接、索引原子写先清理同名临时文件——share 改 rw 后容器内可建链,防止经 admin 接口或索引刷新穿透到宿主任意路径。
- **管理后台**
  - Agent 管理列表新增「经验库」入口:按 Agent 浏览经验文档(文件名/标题/关键词/最后验证/大小/更新时间),支持开关启用、新建/编辑/删除文档、强制重建索引、查看索引原文。

## 0.0.341 (2026-10-24)

- **后端**
  - **修复 IM 卡片「会话详情」跳转假域名**:飞书 / 钉钉进度卡与任务完成通知里的「会话详情」链接,域名取自 ECP 配置的 `desktopCallbackUrl`。该字段即使 ECP 功能整体关闭也保留迁移写入的占位值 `https://mao.example.com/...`,于是所有深链都指向打不开的 `https://mao.example.com/tasks/{id}`。新增独立配置项 `site.baseUrl`(V170,管理后台「系统设置 → 集成配置 → 站点」)作为网页深链的站点根,留空回落上传基础地址 `upload.baseUrl`;ECP 占位回调地址不再参与深链推导。存量部署在配置前仍回落 `upload.baseUrl`,已配好上传地址的实例无需额外操作。
  - **SMART 权限档安全评估改为 fail-closed**:危险命令分类器返回空响应、拒答或无法解析的输出时按「危险、需审批」处理,不再默认放行。
  - **修复 Git 工作区克隆失败删除既有项目**:同一仓库再次创建会话时 `git clone` 必然失败,原先会递归删除整个项目工作区;现在只清理本次新建的目录。
  - **修复管理端会话详情里长输出的失败调用显示为成功**:失败态嗅探同时检查结果头尾,并支持 `exit_code: -1`(超时/被 kill)。
  - **修复会话阶段分布漏「取消中」**:分析页 `phaseDistribution` / `livePhases` 补齐 `CANCELLING`,分布合计与会话总数一致。
  - **修复「未关联模型」下钻为空**:`modelId=0` 的分析筛选与调用明细改按 `model_id IS NULL` 命中。
  - **修复 ECP 登录 `authSource` 误报 LDAP**:换票用户行补齐 `external_provider`,登录响应与 `/auth/me` 一致。
  - **修复 `task_create` 参数非数组时假成功**:`items` 非数组或为空直接报错,不再返回「已创建 0 个」和「任务已创建」引导语。
  - **修复不支持视觉时读图失败被判成功**:视觉不可用的工具结果带 `error` 键,UI、审计与统计按失败记录。
  - **修复 `read_file` / `task_update` 的小数参数被截断**:`offset` 与待办 id 严格要求整数,`1.5`、`-0.5` 一律报错。
  - **修复运行监控默认相位漏「取消中」**:默认过滤集由 `ACTIVE_PHASES` 派生,包含 `CANCELLING`。
- **管理后台**
  - 会话阶段标签与配色补「取消中」(`CANCELLING`),分析页阶段分布与运行态标签不再显示原始枚举值。
  - **修复会话详情轮询丢上一轮对话**:静默刷新改为按消息 id 游标合并(早于最新页首条消息的历史全部保留),轮询期间新开一轮时上一轮不再从页面消失。

## 0.0.340 (2026-10-24)

- **后端**
  - **飞书「新用户绑定」卡片新增「重新获取链接」按钮**:授权链接 3 分钟内有效,过期后由卡片原发送者点击该按钮即可换发一条新链接,不必重发消息。新链接沿用原卡片记录里的入站消息,绑定成功后仍会重放原始消息;换链接时旧记录标记失败,避免重启恢复时把同一条消息重复执行。按钮走卡片回调(`feishu_ecp_auth`),仅原发送者可点,回调直接带回更新后的卡片。

## 0.0.339 (2026-10-24)

- **后端**
  - **飞书通道「点我绑定」支持在公司网络外完成**。此前 ECP 换票后回调到 `https://mao.acg.team/auth/ecp/feishu-callback`(内网地址),不在内网 / 未连 VPN 的用户点完飞书授权后页面一直卡住。新增服务端渲染的公网回调页 `GET /api/v1/auth/ecp/feishu/channel-callback`(由网关 apihub 暴露):服务端完成换票、按邮箱对齐账号、绑定飞书身份并重放原消息,返回一张「绑定成功,可以回到飞书」的 HTML 页。该页不下发 JWT、不写 cookie,错误文案做 HTML 转义,`no-store` + `noindex`;桌面端登录与「会话详情」链接仍走原回调地址不变。
  - ECP 配置新增 `channelCallbackUrl`(留空回落 `desktopCallbackUrl`),飞书通道引导卡改用该地址发起 ECP 授权。
- **管理后台**
  - ECP 飞书登录面板新增「通道公网回调 URL」,须在 ECP App Config → Login Integration 登记同一地址。

## 0.0.338 (2026-10-23)

- **后端**
  - **用量分析首屏提速:总览「今日」0.90s → 约 0.2s,7 / 30 / 90 天 1.3–1.6s → 约 0.3s;用户 / Agent / 模型 / 会话 Tab 同量级下降**。管理后台登录后默认落在用量分析总览,一次请求约 20 条 SQL,两条慢路径把每个 Tab 都拖到 1s 以上:
    - **修复消息聚合的坏执行计划(「有时候慢」的根源)**:活跃用户与用户 / Agent / 模型的消息聚合,默认计划会以 session 为驱动、按 `idx_session` 逐会话回查 `message`,在 40 万行 / 2.1GB 的表上产生数十万次随机回表,且随统计信息在窗口间来回切换——昨日活跃用户实测 0.97–1.25s、30 天用户消息聚合 1.15s,而今日窗口只要 87ms。四处查询统一加 `JOIN_ORDER(m, s)`,固定为「先按时间窗扫 message、再按主键回查 session」:昨日活跃用户 966ms → 111ms、30 天 921ms → 490ms。
    - **新增 `V168` message 覆盖索引** `(created_at, deleted, token_count, session_id, model_id)`:窗口聚合不再回表读正文(实测索引区间扫描 7d 0.10s / 30d 0.18s / 90d 0.22s,原方案 0.38–0.56s),也不再因为扫 2.1GB 正文页把 buffer pool 里的热点数据挤出去。新索引以 `created_at` 为最左前缀,冗余的 `idx_created` 同迁移下线,减少热表写放大。
    - **修复 `V168` 阻断后端启动的问题**:迁移脚本原先用 `DELIMITER` + 存储过程表达条件 DDL,但 TS 版 Flyway 是通过 mysql2 整段下发、不支持该客户端语法(历史 `DELIMITER` 迁移都是 Java Flyway 时代就已应用,故此前未暴露),升级到本版本的后端在迁移阶段即报 SQL 语法错并退出。现改为 `SET @ddl` + `PREPARE` 动态 SQL(与 `V080` 同范式),并对「索引已存在 / 旧索引不存在」两种状态幂等。
    - `StatisticsService.getOverview()` 的 7 条全表计数由串行改并行(约 130ms → 单条最慢的 ~73ms),返回字段不变,mao-cli 的 `analytics overview` 不受影响。
    - 环比窗口(已结束区间)聚合结果加进程内 5 分钟 TTL 缓存(上限 64 个窗口,按写入序淘汰):所有 Tab 的 `previousTotals` 复用同一份,过去窗口最多滞后 5 分钟;本期窗口仍每次实时计算,刷新按钮语义不变。
  - **会话详情首屏瘦身:带截图的会话从 13.7MB 降到约 0.6MB(gzip 后 ~150KB)**。`GET /v1/admin/sessions/:id/messages?compact=true` 原先把工具产出图片的 base64 整包返回(实测 5 轮 327 条消息 13.7MB,其中 12.5MB 是 37 张 1800×1200 截图),服务端还要从 MySQL 搬 13.8MB:
    - `compact=true` 时 `metadata.attachments[].data_uri` 不再返回,只留 `mime` / `path` / 下标;列表 SQL 同步用 `JSON_REMOVE` 去掉前 5 个附件的 base64,DB→应用也不再搬这 13MB。
    - 新增 `GET /v1/admin/sessions/:id/messages/:messageId/attachments/:index`:按需取单张图片字节,带 `Cache-Control: private, max-age=86400`,会话归属与 `session:read` 双重校验。
    - 导出(`export/jsonl`)与桌面端走非 compact 路径,行为不变。

- **管理后台**
  - 会话详情的工具卡片图片改为**展开时才加载**(axios blob + objectURL,滚走即释放);附件缺失(老会话 / 工作区已清理)静默降级,不影响卡片其它内容。
  - keep-alive 切回会话详情页时 30s 内复用已加载内容,不再每次整页重拉(截图里 4 个会话 Tab 之间来回切,每次都会重下十几兆)。
  - 运行中会话的 5s 静默轮询只拉最新 1 轮再合并(原先每 5s 重拉 5 轮),带截图的会话轮询流量降到约 1/5。

- **前端(桌面 / Web / 安卓)**
  - **设置页技能入口改名、默认展示系统技能并禁止上传系统同名技能**:侧边栏「我的技能」更名为「Skills 技能」;技能页默认 Tab 由「已上传」改为「系统技能」,进入页面即可看到系统预置技能,上传完成后仍会自动切到「已上传」;「本地技能」列表中与系统预置技能同名的技能名称后显示「系统已有同名」标记,上传按钮禁用、「一键全部上传」自动跳过并在汇总里说明,拖拽 / 选择上传前按 SKILL.md 的 name 预检拦截;后端 `POST /v1/user-skills/upload`(含管理后台代传用户路径)按 frontmatter 名判重返回 409 拒绝——个人技能会在会话里覆盖同名系统技能,放行上传会让用户版本静默顶掉系统预置版本、管理员更新系统技能也对已上传同名技能的用户不生效。

## 0.0.337 (2026-10-23)

- **后端**
  - **修复 WebSocket 首帧鉴权与业务帧的并发竞争(客户端无限重连)**:客户端在 `onopen` 里同一 tick 连发 `auth` + `subscribe`,而首帧鉴权的账号状态校验是异步查库、同一连接的入站帧又是并发处理——`subscribe` 会先看到「尚未 register」的连接,被按未认证 `1003` 关闭;客户端随即重连并再次命中同一时序,表现为一直重连(查库越慢的部署越容易稳定复现)。现在鉴权窗口内到达的后续帧先排队,auth 处理完成后再按到达顺序冲刷;鉴权完成后的帧仍并发处理,`cancel` 等依赖插队打断在途 handler 的帧不受影响。
  - **会话详情补「正在执行」的工具调用**:一轮 assistant 消息(含 `tool_calls`)要等整轮工具全部返回后才落库,管理后台只读 DB 时正在跑的工具整段看不见。现在工具派发时登记到内存态 `InFlightToolRegistry`、返回/异常时注销、一次执行结束整体清空;`GET /v1/admin/sessions/:id/messages` 最新一页会把这些调用补成 `pending-tool-*` 的进行中助手消息(`status=running`,含解析后的参数),与既有等待作答的 `pending-ask-*` 卡片同口径。
  - **修复图片注入行在编辑后翻倍**:注入行剥离正则要求整行以非空白字符结尾,用户在编辑框里对注入行行尾敲空格、或正文带 CRLF 让注入行以 `\r` 结尾时,剥离失效 → 再次提交会追加一套新行,旧行残留并进入 LLM 上下文与气泡。现在行尾容忍空格 / 制表 / `\r`,并在剥离后一并收敛行尾 `\r`。行内仍有空白的正文(`[图片 1] xxx 尾注`)依旧不匹配,不会被误删。
  - **修复带图消息编辑后 URL 注入行重复进上下文**:与上一条同一根因,编辑重发走「先剥后追加」的幂等路径,剥离失效时同一张图的 URL 会在消息文本里出现两份,前端气泡与模型输入都能看到。
  - **修复 `page_screenshot` 回传字段被白名单裁剪**:压缩后的截图原先用 7 个固定键重建结果对象,上游(SDK / 桌面)后续在 `result`(viewport、deviceScaleFactor…)或 outcome(`pageVersion`、`snapshotId`…)上加的字段会被静默丢弃,模型与前端都看不到,排障时也缺页面版本信息。改为扁平透传(剔除已被压缩版取代的原始 `dataUri`,避免请求体出现两份 base64)。
  - **修复轨迹工具调用与轮次归属的时间窗方向**:`llm_call.created_at` 是调用结束时刻、消息 `created_at` 是落库时刻,落库顺序为「流结束即记 llm_call → 工具执行 → 插 ASSISTANT + TOOL」,原实现用消息时间夹窗口导致窗内往往一条 `llm_call` 都取不到,轮次元信息与工具计时大面积缺失。现在 `llm_call` 配对只传上界(下界开放),本轮工具窗口的上界取下一轮 `llm_call.created_at`,同仓库 `notAfter` 口径保持一致。
  - **修复工具详情「参数」tab 恒为空**:`tool_calls` 是 MySQL JSON 列,取出来是 `"id": "call_x"`(冒号后有空格),原实现按紧凑 JSON 的 `"id":"call_x"` 做 LIKE,永远匹配不到声明该调用的助手消息。现改用 `JSON_CONTAINS(tool_calls, JSON_OBJECT('id', ?))`,不依赖 JSON 文本格式,也不受 LIKE 通配符影响。
  - **修复轮次详情「本轮工具」混入历史轮次**:原实现只传下一轮上界、没有下界,最后一轮更是直接取全会话工具。现下界取本轮 `llm_call.created_at`(本轮助手消息在工具跑完后才落库,必然晚于它),上界只取下一轮 `scene='agent'` 调用,标题 / 压缩等旁路调用不再把本轮工具截断。
  - **停用账号的 API Token 同步失效**:`mao_` API Token 路径此前在账号状态检查之前就 return,停用 / 删除账号的 API Token 仍可调用 `/v1/open/**`。现与 JWT 路径同口径拦截;同时鉴权阶段查到的用户行供 `onResponse` 审计复用,已认证请求不再对 user 表查两次。

- **管理后台**
  - 会话详情新增「正在执行」区块(默认展开):进行中的工具以卡片展示,带 loading 状态与参数摘要,不再埋在执行过程的折叠区里。
  - 运行中会话(`RUNNING` / `WAITING_APPROVAL` / `CANCELLING`)每 5 秒静默刷新会话信息与最新消息:不发 loading、不弹错误、保留「加载更多」拉到的更早历史;仅当原本停在底部时才跟随滚动。
  - 工具卡片摘要统一走中文工具名(执行命令 / 读取文件 / 编辑文件…),单个工具的分组标题不再为空。
- **前端(桌面 / Web / 安卓)**
  - **修复 WebSocket 重连风暴**:连接 open 不再直接视为可用,改为收到服务端 `connected` 鉴权确认帧后再置 `connected`、重置重连退避并补发订阅;鉴权确认前不发送任何业务帧。鉴权被拒(`1003` + `Not authenticated` / `Missing or invalid token` / `Account disabled` / `Authentication expired`)时退避保持增长(最长 30s),不再 1s 一次热循环;每次重连都会重读 token,令牌刷新后自动恢复。握手阶段被判死时同步结束在途 connect Promise,避免后续连接拿到永不 settle 的旧 Promise。
  - **修复带图消息发送后「多出一行 URL」**:图片 URL 注入行是给 Agent 下载文件本体用的(`[图片 N] <url>` 追加在消息文本尾部),落库文本带注入行而发送瞬间的乐观消息只有正文,Agent 跑完重拉历史后气泡会多出一行 URL,看起来像消息被服务端改掉。现在气泡、编辑框回填、复制、添加到指令统一走剥离注入行后的口径,URL 行不再出现在用户可见的展示与剪贴板里;落库内容与 Agent 可见内容不变,编辑往返的幂等也不受影响。
  - **修复记录详情抽屉遮罩铺满整个视口**:抽屉层是 `position: absolute`,但祖先链上没有任何定位祖先,包含块回落到初始包含块(视口),遮罩会盖住面板外区域、抽屉右边缘贴屏幕右边。`.run-trace-panel` 改为 `position: relative`,遮罩回到「只挡面板内」的设计意图。
  - **修复时间条带 key 与行锚点重复导致定位错行**:用户手动文件写入 / Git 操作这类未挂到轮的活动没有 `tool_call_id`,条带 key 与行锚点 key 只用 `toolCallId ?? name`,同一 run 内出现两条同类活动时 key 完全相同——v-for 重复 key、两个 tool-row 的锚点 id 相同,点击第二条色条会定位到第一条行上。现在条带 key 与行锚点 key 统一为 `toolCallId|target|name` + 段内序号,与行渲染 `:key` 同源。
  - **修复切换色条时 tooltip 短暂显示旧节点内容**:色条之间移动不触发 svg 的 `mouseleave`,`showTip` 只清了定时器没有立即撤下已弹出的 tooltip,从 A 快速移到 B 时 A 的内容会继续显示约 300ms,与鼠标所在色条不符。现在切换时立即清空;同时补上组件卸载时清理定时器,避免 run 收起后回调写入已销毁组件。
  - **修复记录详情缓存无上限增长**:缓存原先只在删会话 / 登出时清理,正常使用路径下随点开的工具 / 轮次数单调增长(每条含大文件内容或长命令输出等敏感正文),且切会话不清轨迹时也不释放。现在单会话上限 200 条、跨会话桶上限 20 个,均按写入序淘汰最旧,`clearTrace`(切会话 / 清轨迹)时一并释放。
  - **修复时间条带工具色条点击定位失效**:行锚点原先用轮内 / 列表内下标,条带节点用跨段全局序号,且遍历顺序相反(条带先未挂轮工具再轮内工具,模板先轮内再未挂轮),第 2 轮及之后的工具色条点击找不到对应行。现在行锚点直接复用条带节点 key(同一份序号与遍历顺序),并消掉同 `toolCallId|target|name` 的重复 `:key`。

## 0.0.336 (2026-10-23)

- **后端**
  - **修复终端审计 IP 可被任意伪造**:终端(Web Shell)审计此前无条件采信请求头 `X-Forwarded-For` / `X-Real-IP`,任意调用方带一个伪造头就能把操作记录到任意 IP,审计日志失去可信度。现与 REST 审计同一口径——仅当对端地址是已配置的可信代理(`TRUSTED_PROXY_ADDRESSES`)时才采信转发头,否则记录直连对端地址。未配置可信代理的反代部署会把终端审计 IP 记为反代地址,需在部署环境配置该变量(参考 `skills/mao-cli/reference/config.md`)。
  - **修复账号被禁用后存量 token 仍可调用全部接口**:请求鉴权此前只验签 + 判过期,不查账号状态——管理员在后台禁用用户后,其手上未过期的 access token(默认 24 小时)仍可读写数据、建终端、调模型。现补齐与登录 / 刷新 token 一致的口径:停用或已删除账号的 token 按未认证拒绝(停用判定带 30 秒负缓存控制查询开销,重新启用最多 30 秒后生效)。
  - **修复 runtime 清理定时任务可能把后端进程带崩**:清理扫描对取不到 stat 的条目(如用户容器内制造的悬空软链、权限异常的目录)没有容错,单条异常让整轮扫描 reject 成 unhandledRejection 终止进程。现跳过取不到 stat 的条目,定时回调补兜底日志。
  - **修复微信入站图片被静默丢弃**:入站图片的媒体节点选取此前只在 `media` 整个缺失时回退 `thumb_media`,`media` 存在但缺 `encrypt_query_param` 时不再回退——缩略图可用也直接丢图,Agent 收不到任何图片。现与文件路径同口径(取第一个带合法参数的节点,AES key 同步取该节点)。
  - **修复会话未读红点的两处口径错误**:`RUNNING → IDLE` 的回滚路径(用户消息都没落库的失败路径)被误判为「任务刚结束」而写入未读,会话挂假红点;`IDLE → 终态` 又漏写未读且与 WS 广播不一致(前端收到红点、刷新后按 DB 渲染红点消失)。现终态口径与仓内其余定义对齐(`IDLE` 既非活跃也非终态)。
  - **修复邮件通知永远关不掉**:在个人资料清空过邮箱的用户,在前端关闭邮件通知时被「请先在个人资料中填写邮箱」拦截,`enabled` 永远停在开启;稍后补回邮箱,邮件又在明确关闭之后恢复投递。现关闭(或非切换到邮件渠道)时不再强制要求邮箱与 SMTP 可用。
  - **修复待办工具把非法 id / 对象 content 当成功处理**:`task_update` / `task_delete` 的 id 为 `null` / 空串时被强转为 0,命中 0 行却回报「任务 #0 已完成」「已删除 1 个事项」,模型据此认为任务已推进;`content` 误传成对象时把 `null` 写进 NOT NULL 列,整条 UPDATE(含 completed 标记)一起回滚。现 id 必须为正整数、content 与创建侧同口径兜底、删除计数只统计真实命中的条目。
  - **修复业务失败被审计日志记成成功**:业务错误按仓库约定返回 HTTP 200(错误在响应体 `code` 上),而审计判定只看状态码——失败操作全部落库 `success=1`、失败原因恒为空,管理后台「审计日志」的「错误」列永远显示 `-`,失败操作被统计成成功。现失败原因随响应写入审计日志。
  - **修复 `read_file` 越界 offset 静默返回空内容**:`offset` 超出文件行数时被悄悄钳到文件尾,返回「空内容 + success」,模型可能误判文件为空。现显式报错(`offset` 等于总行数的合法空尾窗口不受影响)。
  - **聊天图片新增 URL 注入,Agent 可自行获取图片文件本体**:用户发送图片时,消息文本 part 尾部追加 `[图片 N] <url>` 行(序号与图片 parts 顺序一一对应),纯图片消息也会生成只含注入行的文本 part。模型需要文件本体(格式转换、像素处理等)时,可自行用 shell(curl/wget/python3)把 URL 下载到工作区再按本地路径处理,不再只能通过多模态视觉通道"看图"。编辑重发、边路会话首问、队列插队 / 自动消费等全部消息组装路径均已生效,且重复编辑不会堆积多套 URL 行(strip-then-append 幂等);会话标题生成会剥离注入行,URL 片段不会进入标题。视觉通道行为不变(适配器仍下载转 base64),纯文本消息与历史消息零变化。
  - **运行轨迹 VO 增量(零迁移)**:轮次 / 旁路调用 / 工具三类行补 `startedAtMs`(服务端按 `created_at − duration_ms` 估算的起点,两侧任一缺失为 `null`)、`provider`,轮次行另补 `llmCallId`(下钻定位 key)。解码耗时与吞吐量仍是前端派生量,不进 VO,避免同一数字两处真相。
  - **新增记录详情两个只读端点**:`GET /v1/sessions/:id/trace/records/tool/:toolCallId` 与 `GET /v1/sessions/:id/trace/records/call/:llmCallId`,按 key 分流,权限复用 `requireSessionOwner`(`buildCallRecord` 内再校验 `llm_call.session_id`,防越权读他人会话)。轨迹主 VO 仍不带正文,参数 / 结果原文与 `llm_call` 全字段元信息按需拉取;找不到返回空态 VO(200)而非 404。工具调用按 `tool_call_id` 精确配对,`llm_call` 用助手消息与 TOOL 消息的 `created_at` 夹半开窗取窗内 `scene='agent'` 的最后一条,**不跨窗猜**。
  - **新增「请求体体积」压缩触发**:除 token 外按实际请求体字节判断,超过 `triggerPayloadBytes`(默认 40 MiB,可在系统设置关闭)即触发会话压缩;模型配置新增「请求体上限」(`llm_model.max_request_body_bytes`,管理后台按 MB 填写),存在上限时生效阈值取 `min(全局, 上限 × 0.8)`,避免请求体先撞上供应商 413。体积检查独立于 `loopMidwayCompact`,每个工具轮边界都会执行。
  - **压缩请求默认保住前缀缓存**:压缩调用不再剥图、不再裁剪历史,只追加一条交接指令,正常轮次零影响;只有压缩请求逼近上限(供应商上限 −64KB;未配上限时用独立的 40MiB 安全线)才兜底剥图,已知上限且剥后仍超才抛业务异常而不是发出必然失败的请求;mid-loop 的体积压缩最多尝试 2 次(含异常),避免失败后每个工具轮重复烧全量历史。
  - **图片写入侧前置压缩**:`read_file`、`page_screenshot` 产出的图片超过 1.5MB 时压到 ≤1.2MB(有 alpha 用 WebP、无 alpha 用 JPEG,质量 80/70/60 再按 0.85 缩尺寸,最多 6 次编码);IM 入站(飞书 / 微信 / 钉钉)在消息入库时对 data URI 做同样压缩(>1.5MB → ≤1.2MB),落盘原图不变,Agent 仍能读原图。
  - **远程图片按 URL 缓存并压缩**:三个 LLM 适配器下载上传图时走进程内 LRU(默认 64MB),首次下载压到 ≤1.2MB 后缓存 data URI,后续命中不再下载/重编码;缓存过期用 `ETag` / `Last-Modified` 条件协商,304 复用、网络失败回退旧值,输出字节稳定不破坏前缀缓存。
- **管理后台**
  - 系统设置新增「请求体体积阈值 (字节)」,模型表单新增「请求体上限 (MB)」。
- **前端(桌面 / Web / 安卓)**
  - **修复页面更新提示在发版后失效**:版本轮询把「本地运行版本基线」改写成尚未安装的远端版本——同浏览器其它仍跑旧代码的窗口 / 标签页从此收不到更新提示,永远停在旧前端;服务端回滚版本后还会出现撤不掉的假提示。现基线只在页面加载 / 刷新时同步一次,提示每轮按「基线 vs 远端」重算,远端回落到基线即撤销。
  - **修复工具调用卡的参数预览恒为空**:`page_observe` / `page_inspect` / `open_web_page` 的参数预览读了后端 schema 里不存在的字段(如 `reason`),预览恒为空。现按真实入参展示(`snapshotId` / `includeHidden` / `url`)。
  - **修复轨迹面板一直显示上一次执行的残留**:断线重连恰好落在终态快照时,上一次执行的临时行未清理,轨迹 Tab 持续显示旧轮次与旧工具计时、当前执行不出现。现在终态快照分支补清理(与 `session_status` 分支对齐),并取最新一次执行。
  - **修复 delegate 卡片可能绑错子代理**:按 `agent_type` 过滤子代理时「无匹配」被当成「不筛选」,点「查看子代理过程」打开的是别的子代理的会话。现无匹配时不再回退到其它类型。
  - **修复带图消息发送后重复上屏两条用户消息**:`user_message_saved` 帧丢失或迟到(网络重连期间只发给当前连接、被陈旧过滤器吞掉等)时,本地乐观消息的 id 停在临时 id,之后重拉历史走 REST 兜底;落库文本带图片 URL 注入行、而本地乐观消息是原始正文,两侧比对不相等导致同一次发送显示两条。现在乐观消息与落库消息比对时剥离注入行,`user_message_saved` 正常到达的链路行为不变。
  - **run 展开区顶部新增时间概览条带**:SVG 三泳道(轮次 / 工具 / 旁路),色条起点与宽度取估算起止,同一泳道内并行色条自然重叠以表达 `Promise.all` 并行;助手轮次分「首字 / 解码」两段。悬停 300ms 显示 tooltip(起止均标注「估算」),点击色条展开该 run 并平滑滚动定位到对应行(行加 1.2s 高亮)。横轴取 run 内全部节点的 min/max,不用墙钟跨度;起止不可估算的节点不画但在图例报「N 项起止不可估算未画」。
  - **工具行 / 轮次行支持记录详情下钻**:点「详情」开面板内右侧抽屉。工具抽屉三个 tab(参数 / 结果 / 计时与元信息),JSON 原文支持「格式化 / 原样」切换,非法 JSON 按原样展示并标注「非 JSON 文本」;轮次抽屉为单页,展示 `llm_call` 全字段 + token 明细 + 派生三行(解码耗时 / 吞吐量 / 请求编号)与「本轮工具」清单(可继续跳到工具抽屉)。记录按 `sessionId` + key 分桶缓存,重复点同一行不重复请求;`Esc` / 遮罩 / × 三种方式关闭。抽屉为面板内 absolute 定位,不用 `el-drawer`,避免 append-to-body 带出跨会话错配。
  - **轮次行计时补全**:新增 provider 徽标、解码耗时(`durationMs − firstTokenMs`)、吞吐量(输出 tok/s),全部前端派生。首字缺失、首字晚于结束(时钟异常)、解码为 0、输出为 0 一律显示「—」,不出负数与 Infinity。
  - **CSV 导出新增 `provider` 列**(置于「模型」之后,18 → 19 列):模型行填值,工具行留空。`startedAtMs` / `llmCallId` / 解码 / 吞吐不进 CSV——前者是 epoch ms 与主键,对人类复盘无意义;后两者是前端派生量,与 CSV 的「落库事实」定位冲突。JSON 导出结构与接口一致,自然带上新字段。
  - 任务详情的上下文容量块下方显示「请求体积 / 触发上限」,便于判断离触发线还有多远。
- **终端 CLI(mao-agent)**
  - **修复 LOCAL 模式 `read_file` 静默钳制 offset/limit**:负数 limit 被钳成 0 后返回「空内容 + success」,模型可能误判文件为空并在后续 `write_file` 中整体覆写清空它。现与云端同口径:非法值显式报错让模型重试。
  - **修复活跃相位集合漏相位**:`RESUMING`(恢复中)/ `CANCELLING`(取消中)不在活跃集合里,撞上这两类会话时 `--if-running=fail` 不再快速失败、`--if-running=cancel` 不发取消,REPL 也把「还在取消中」显示成空闲。现补齐四相位,与后端 / 桌面端 / 契约一致。
  - **修复 value 型选项的内联空值让 CLI 起不来**:`--timeout-ms=` 等选项在 shell 变量未设置(展开为空串)时直接抛错退出,任何子命令都无法启动。现空串按「未下发」处理走配置链与默认值,与 `--output-format` 既有口径一致(`--permission-level` / `--if-running` / `--on-question` / `--on-approval` / `--max-duration` / `--resume` 同批修正)。
- **终端 CLI(mao-cli)**
  - **修复 `mao llm-call` 把 CLI 上下文当结果输出**:`emitResult` 参数顺序颠倒,stdout 打印的是 `{baseUrl, token, ...}`——接口结果完全丢失,且用 `--token` / `MAO_TOKEN` 注入时 access token 随输出明文外泄(输出常被重定向进日志或管道),`--raw` 标志也失效。现输出 `records[]` 等接口数据。
  - **修复 `--permission-level` 不接受 PROXY**:mao-cli 的白名单漏了代理审批档,`mao session create/update --permission-level PROXY` 在本地直接被拒、请求根本不发出。现五档齐全,参考文档同步。
  - **修复全局开关 `--raw=false` / `--json=false` 反而把开关打开**:字符串 `'false'` 被当成布尔真,用户显式关闭 raw 却拿到完整 Result 信封(`mao --raw=false session list | jq .data` 取不到数据)。现按布尔值口径解析。

## 0.0.335 (2026-10-22)

- **后端**
  - **修复自定义 HTTP 工具的 SSRF 与凭据转发缺口**:云元数据硬拒名单补齐阿里云 `100.100.100.200`、腾讯云 `169.254.0.23`、ECS `169.254.170.2` 与 `metadata.tencentyun.com` 等主机名,`100.64.0.0/10`(CGNAT)纳入私网/保留段——此前该地址被当成公网主机直接放行,模型可通过 URL 模板的主机占位符读到实例 RAM 角色的临时 AK/SK;重定向到**其他主机**(或 https 降级为 http)时剥离 `Authorization` / `Cookie` / `X-Api-Key` 等凭据头,同主机的 HTTPS 升级与端口变化不受影响。
  - **修复「必填 + 默认值」参数的自定义工具永远无法调用**:参数校验不再把有默认值的参数判为缺失,默认值按「实参 > 默认值」口径参与 URL / body 渲染。
  - **修复请求头掩码与 `enc:` 前缀的两处编解码缺陷**:把敏感头改名为非敏感键时,表单回传的 `******` 不再被当成真实凭据落库(对应不到旧值时拒绝保存并提示重填真实值);解码改为按「键是否敏感」判定,非敏感头的值即使以 `enc:` 开头也按明文透传,不再让工具永久不可用。
  - **修复空串环境变量被当成有效配置**:`SKILLS_DIR=` / `WORKSPACE_ROOT=` 不再顶掉 `${VAR:default}` 的默认值(路径类配置落成进程 cwd),`MAO_TS_PORT=` / `MAO_CLEANUP_INTERVAL_MS=` / `MAO_CONTAINER_MEMORY_MB=` 等数字型配置不再解析成 0,`FLYWAY_ENABLED=` 等布尔开关不再被静默当成 false。
- **前端(桌面 / Web / 安卓)**
  - **修复对话区自动跟随的三个缺口**:内容单帧暴涨(大工具卡 / 长 diff)不再被判成「用户已离开底部」而永久停止贴底;键盘翻页(ArrowUp / PageUp / Home 等)现在能停止跟随并触发「加载更早历史」;流式输出期间拖动滚动条上滑不再被「内容高度变化」分支否决。滚轮 / 触摸上滑行为不变,历史回填导致的内容塌陷仍不会被误判成上滑。
  - **修复发送失败回滚打错会话**:发送 / 执行失败时只回滚消息实际发往的会话,不再因为期间切到别的会话而删掉当前会话的乐观消息、甚至摘掉它正在流式输出的气泡。

## 0.0.334 (2026-10-22)

- **前端(桌面 / Web / 安卓)**
  - **文件管理「复制绝对路径」修复,并新增「复制相对路径」**:CLOUD 模式下「复制绝对路径」复制下来的其实是相对路径(云端文件 provider 此前没有实现绝对路径拼接,静默回退),现已修复。右键菜单与底栏「复制路径」下拉同时提供「复制绝对路径 / 复制相对路径」两个入口,多选时逐行拼接,与侧边栏文件树、Git 变更面板口径一致。
- **后端**
  - 工作区目录与递归搜索接口响应新增 `workspaceRoot` 字段(工作区绝对根路径),供前端拼绝对路径;条目 `path` 仍相对该根,接口其余行为不变。

## 0.0.333 (2026-10-22)

- **前端(桌面 / Web / 安卓)**
  - **边路任务与主会话的会话动作收敛为同一实现**:待发送队列的增删改 / 撤回编辑、发送前附件上传防线、`user_message_saved` 落库等待、取消执行的本地收尾统一抽到 `useChatQueue` / `chatSessionActions`,两个面板共用。边路因此补齐三处行为:附件全部上传失败时整条消息不再发送(即使有文字),部分失败会弹二次确认;停止执行会清掉流式 / 思考状态并把气泡标记为「执行已被中止」;重试会等本轮执行收敛后再恢复发送按钮。

## 0.0.332 (2026-10-22)

- **前端(桌面 / Web / 安卓)**
  - **对话区自动跟随改为「尺寸驱动 + 手势判定」**:`useChatScroll` 不再靠枚举「可能影响高度」的状态(anchor 字符串)和从 scroll 事件反推手势——改用 ResizeObserver 观察滚动容器与消息子元素:队列面板 / 审批卡 / 软键盘压缩高度、流式文本 / 图片 / 工具卡片撑高内容,都走同一个 rAF 合并的定位入口;只有滚轮 / 触摸 / 按压手势之后 250ms 内的 scroll 才允许改变「是否跟随」,窗口外的位移(浏览器钳制、滚动锚定、内容塌陷)一律按布局产物处理。
  - 手势上滑会同步取消已排队的定位帧,高频流式输出时不再出现「上滑第一下被吃掉」;自动补位设两屏上限,用户真正离开底部后不会被强行拉回;搜索定位到某条历史消息视同显式暂停跟随。主会话、边路任务、子代理三个面板共用同一实现。

## 0.0.331 (2026-10-22)

- **前端(桌面 / Web / 安卓)**
  - **修复边路任务发送消息后不贴底、停在页面中部**:边路任务与子代理面板的自动滚动改为复用主会话的 `useChatScroll`(此前两处各复制了一套),并新增 `resumeFollow()` 作为显式「恢复跟随」语义——发送消息(含执行中入队)、切回 Tab、点「回到最新」都会复位上滑状态并定位到底部,不再沿用历史上(可能是布局变化引起的)上滑判定。顺带补齐边路缺失的恢复期保护、`generation` 失效与触摸手势,滚动条隐藏时的体感与主会话一致。

## 0.0.330 (2026-10-22)

### 前端(桌面 / Web / 安卓)
- 顶栏头像左侧新增齿轮「设置」入口:0.0.274 把设置收敛进头像菜单后,日常打开设置需要两步;现在工作台顶栏(头像左侧)直接有一个齿轮图标,点击进入设置页,头像菜单里的「设置」菜单项保留不变。设置页顶栏不显示该齿轮(避免在设置页里再放一个进入设置页的图标),「我的指令」书本图标同理在设置页隐藏。

## 0.0.329 (2026-10-22)

- **后端**
  - **修复普通 JSON 数组文本在多条链路上被清空**:任务通知卡片预览、运行轨迹用户消息、自动记忆抽取读取正文时,只要 content 以 `[` 开头就按多模态分片数组解析,`["任务","失败"]`、`[1,2,3]` 这类用户原样发送的文本解析后没有 text 分片,被清成空串——通知卡片缺少用户消息、轨迹轮次无法定位、记忆抽取因 `hasText` 失败整轮静默跳过。现三处统一复用 `isMultimodalContentParts` 结构守卫,非多模态数组原样保留。
  - **修复 PathSandbox strict 模式父目录符号链接逃逸**:原先只对最终路径 `lstat`,工作区内 `link -> /outside` 时访问 `link/secret.txt` 的最终节点是普通文件,直接放行,`read_file`/`write_file` 等文件工具可越出 workspace 与允许根。现逐段检查路径上的符号链接并对目标 realpath 校验,最终节点与中间目录都被覆盖。
  - **修复钉钉机器人渠道会话被当作人工会话**:终态未读写入、`session_status` WS 事件、站内收件箱三处只排除了微信/飞书,钉钉会话结束后被置顶显示红点并写入无意义收件箱;记忆域的 `isRobotChannelProjectKey` 也未识别钉钉私聊/群聊键,导致项目下拉污染、自动记忆被写成聊天级 PROJECT 记忆。现统一用 `isDingtalkChannelSession` 与钉钉键前缀判定,与飞书/微信同口径。
  - **修复信号哨兵 prompt 占位符二次替换**:先替换 `{diff}` 再替换 `{url}`/`{name}`,被监控网页正文里原样出现的 `{url}`/`{name}` 会被二次替换,Agent 看到的 diff 与真实页面不一致。现改为对模板单遍正则替换,替换值不再参与扫描。
  - **修复 open_web_page 原始响应截断未标记**:`fetchHtml` 读满 `maxRawBytes` 后销毁响应并按成功返回,上层只按抽取后正文长度判断 `truncated`,大页面被砍断却返回 `truncated=false`、无提示。现 `fetchHtml` 回传 `rawTruncated`,与输出正文截断统一标记并给出截断说明。
  - 新增 8 个 `*.bugcheck.spec.ts` 存在性证明测试,修复前 16 条失败、修复后 26 条全部通过;全量回归 384 个测试文件通过。

## 0.0.328 (2026-10-21)

- **后端**
  - **修复飞书任务通知 webhook 静默失效**:0.0.319 把任务通知卡从飞书卡片 JSON 1.0 升级为 2.0 时,沿用了 1.0 独有的 `tag:'note'` 元素(卡片底部时间小字)。2.0 已废弃该元素,飞书服务端整体拒绝卡片(返回 HTTP 200 但 `code:11246`,`ErrPath: ROOT -> body -> elements -> [N](tag: note)`)。由于投递失败判定只看 HTTP 状态码,200 被当作不可重试,仅尝试一次即置 FAILED,表现为所有飞书渠道用户任务完成后收不到任何通知。现改为 2.0 兼容的 `markdown` 元素 + `<font color='grey'>` 灰字近似原视觉,微信降级推送卡同步修复;补充回归测试断言卡片不含 `note` 等 1.0 独有元素。
  - **新增 Agent 无进展工具循环自动止损**:同一组工具调用(工具名 + 参数)连续多轮得到完全相同的结果时,第 5 / 10 轮结束向模型注入一次「换策略」提醒(第 6 / 11 轮请求可见,不落库、不污染历史),第 15 轮仍无变化则自动中止并把会话置 `FAILED`,错误文案对桌面 / Web / 飞书 / 钉钉 / 子代理统一可见。提醒按持久化消息确定性重算,跨执行与重启保持前缀缓存;`shell await_async` / `write_stdin`、未完成的等待结果与 `wait_subagents` 超时不参与中止判定,避免误杀长构建。

- **终端 CLI(mao-cli)**
  - 飞书机器人文档(`reference/feishu-bot.md`)使用流程补充:创建飞书应用可使用飞书「创建飞书智能体应用」一键创建带机器人能力的基础应用,但要获得完整集成能力还需额外开通三个权限(`im:message.group_msg.include_bot:read`、`im:chat`、`contact:user.basic_profile:readonly`)并订阅 `im.chat.updated_v1` 事件,分别对应群聊 @ 触发与群上下文、群信息(群名)读取、发送人姓名解析,以及话题群模式切换感知。

- **后端**
  - **修复自定义 HTTP 工具加密密钥环境变量兼容性**:继续支持旧部署配置的 `APP_USER_TOOL_SECRET`(同时设置时 `APP_HTTP_TOOL_SECRET` 优先),避免升级后静默回退到内置默认密钥;旧变量名也重新纳入子进程敏感环境剥离,防止主密钥透传给 Agent Shell / 云端终端。
  - **修复关联工具失效后 Agent 无法编辑**:工具被停用或删除后,Agent 的存量关联保留(装配侧照旧跳过),编辑名称、提示词、模型等任意字段不再被校验器拒绝;新关联停用工具仍会被拒绝。同时为 `httpToolIds` 增加运行时类型校验,非数组、字符串 id、非正整数返回业务错误,不再 500 或静默丢失关联。

- **管理后台**
  - 自定义工具编辑页的「敏感」勾选改为只读标识:按与后端一致的关键词规则(token / secret / key / authorization / cookie)显示「加密 / 明文」并修正提示文案,不再出现勾选后值仍以明文落库、回显的误导。
  - Agent 列表的「当前没有客户端可见的 Agent」提示改用不受搜索过滤的全量数据,搜索到仅管理端可见的 Agent 时不再误报。

- **前端(桌面 / Web / 安卓)**
  - 修复任务进度列表自动跟随的两个边界:高于列表可视高度的任务项改为顶部对齐,不再把任务名开头滚出视野;从「文件 / Git」页签切回「任务」页签时重新跟随当前任务并重算底部渐隐。

---

## 0.0.327 (2026-10-21)

### 前端(桌面 / Web / 安卓)
- 设置页移除「我的工具」入口:自定义 HTTP 工具不再由用户在客户端配置,改由管理员在管理后台统一配置并关联到具体 Agent(见管理后台小节)。Agent 调用工具的方式不变。

### 后端
- **自定义 HTTP 工具归属模型变更:管理员配置 + 按 Agent 关联**(V166 迁移,初版不考虑存量数据兼容,旧 `user_tool` 表重建为 `http_tool`)。工具定义不再属于单个用户:管理后台创建的工具全局唯一,通过 Agent 的「自定义 HTTP 工具」关联(`agent.http_tool_ids`,与 MCP 服务器关联同构)决定哪些会话可以调用;未关联任何工具的 Agent 看不到也调不到这些工具。执行口径不变:`http_call` 仍走服务端直连、无审批门,SSRF 默认拒私网(管理员经系统设置放行内网段)。同时移除「每用户启用上限」系统设置——启用工具数量不再受限,仅受 Agent 关联范围约束。

### 管理后台
- 「自定义工具」页(原只读清单)升级为完整管理页:支持新建 / 编辑 / 启停 / 删除工具,编辑弹窗含请求头(敏感值加密存储、掩码回显)与参数定义,「测试调用」抽屉用当前草稿实调一次(不落库,SSRF 判定与正式调用一致)。清单移除「属主」列与用户名筛选。
- Agent 编辑表单新增「自定义 HTTP 工具」多选:选择已启用的工具即关联到该 Agent;在工具页停用某工具后,所有关联它的 Agent 立即无法再调用。
- 系统设置移除「每用户启用上限」(tools.http.maxEnabledPerUser)项。

## 0.0.326 (2026-10-21)

### 前端(桌面 / Web / 安卓)
- 新建任务的智能体选择器移除「团队共享」分区,恢复为单一 Agent 列表。Agent 的可见性与可用性不变——管理后台创建的 Agent 继续直接出现在工作台智能体列表中,选择与使用方式不变。

### 后端
- **移除「上架到团队共享」功能**:删除 `/v1/shared-agents`、`/v1/agents/:id/shared-entry` 等共享目录接口与 `shared_agent_entry` 表(V165 迁移 drop)。Agent Bundle 的导出/导入、registry、URL 导入与「检查更新」不受影响(检查更新的远端来源改回仅取 URL 导入时记录的来源)。

### 管理后台
- Agent 列表移除「上架 / 推荐语 / 下架」操作与「上架到团队共享」弹窗;「导入」「检查更新」「导出」等 Agent Bundle 功能保留。

## 0.0.325 (2026-10-21)

### 后端
- **新增公共共享目录 `/opt/mao-data/share`(`MAO_SHARE_DIR` 可覆盖)**:管理员在宿主机向该目录投放文件后,全体 CLOUD 用户的 Agent 即可读取(`read_file` / `glob_search` / `grep_search` / `shell` 等文件工具,绝对路径直接访问)。目录由后端启动自动创建,投放即时生效(目录级挂载,新增/修改文件无需重启或重建容器)。容器(docker)模式下该目录以只读方式挂载进每用户容器,容器内写入返回只读文件系统错误;宿主机(host)模式下为普通目录、只读不强制。共享即公开:请勿向该目录投放敏感内容。LOCAL 模式(本地执行)不可见该目录(与工作区同边界)。系统提示词不主动提及该目录,由用户指令或 Agent 自行探索发现。`MAO_SHARE_DIR` 必须配置为非空绝对路径,空值或相对路径后端启动即报错拒绝。

## 0.0.324 (2026-10-21)

### 前端(桌面 / Web / 安卓)
- 任务详情右栏「进度」列表最多显示约 5 项,超出后在列表内部滚动,不再把下方的「边路任务」「子代理」挤出屏幕。滚动条不占位(侧栏窄,任务名可用宽度不再被吃掉),改为内容溢出的底部渐隐提示。任务名改为换行完整展示,长标题不再被省略号截断(长英文标识符强制断行,不会撑破侧栏)。列表自动把当前进行中的任务(没有进行中则取最后一条已完成)滚进可视区,回看其它任务时不被强行拽回;切换任务 / 边路任务 / 子代理时滚动位置重置。

### 后端
- **修复飞书进度卡中子代理轮次在等待期间冻结**:主代理调用 `wait_subagents`(或主线结束自动挂起等待)阻塞等待后台子代理时,父侧不再产生工具 / 轮次事件,进度卡整段时间停在进入等待那一刻——子代理实际已执行到十几轮,卡片仍显示进入等待时的「第 9 轮」。现在等待期间每 10 秒驱动一次卡片刷新,子代理轮次随执行持续推进;刷新失败(如卡片 PATCH 网络错误)只告警,不影响等待本身。

## 0.0.323 (2026-10-21)

### 前端(桌面 / Web / 安卓)
- **修复子代理会话右侧边栏不显示上下文占用百分比**:0.0.244 把上下文徽标和「上下文详情」抽屉的可见性合并成同一个条件(抽屉只对主会话 / 边路任务开放),子代理视图连只读百分比也一起消失了。现在拆回两层:子代理恢复只读徽标,展示自己的 token 占用与窗口占比;抽屉入口仍只对主会话 / 边路任务开放。子代理缺少自身上下文水位时不再回退主会话数值,避免把主会话占用显示成子代理的。
- **移除输入栏左侧的 Agent 徽标**:产品反馈该徽标与相邻的工作区指示挤在同一行、信息重复,已移除;会话 Agent 身份保留「主会话 Tab 头像」与右侧检查器「智能体」分区两处。
- 新建会话的智能体列表只拉取客户端可见的 Agent(`GET /agents?scope=client`);全部隐藏时显示「暂无可用智能体」并阻止发送。

### 后端
- **Agent 新增「客户端可见」开关(`agent.client_visible`)**:与「启用/停用」分离——关闭后仅「客户端新建会话」的智能体列表不再展示该 Agent,已绑定它的历史会话照常加载与继续对话,定时任务 / 信号哨兵 / 开放接口 / 渠道按 `agentId` 指定的入口也不受影响。`GET /v1/agents` 新增 `scope=client` 只返回可见项(不传保持全量,非法值直接拒绝);`POST` / `PUT /v1/agents/:id` 接受 `clientVisible`,Agent VO 回传该字段。默认 Agent 双向保护:不能关闭可见,不可见的 Agent 也不能设为默认。团队共享目录(`GET /v1/shared-agents`)同步过滤不可见项,避免从共享入口选到隐藏 Agent。Agent bundle 导入导出携带 `clientVisible`(旧 bundle 缺字段按可见处理)。

### 管理后台
- Agent 表单基础信息页新增「客户端可见」开关(默认开):关闭时提示仅影响新建会话列表;默认 Agent 的开关禁用,勾选「默认 Agent」时自动回弹并提示。列表状态列叠加「仅管理端」标签,全部 Agent 都不可见时页面顶部给出警示。

## 0.0.322 (2026-10-20)

### 前端(桌面 / Web / 安卓)
- **会话 Agent 身份改为主会话 Tab 头像**:移除 0.0.321 的顶部身份横条(占垂直空间、割裂视口)。主会话 Tab 的默认图标改为当前 Agent 头像,工具条左侧只读徽标与右侧检查器「智能体」分区保持不变;三处仍共用同一套解析(Agent 停用或不可见时回退名称并标注,无归属 Agent 的历史/系统会话不显示)。Tab 栏只在打开多个 Tab 时出现,此时头像可见;单 Tab 时身份由工具条徽标与右栏分区承担。

## 0.0.321 (2026-10-20)

### 前端(桌面 / Web / 安卓)
- **会话 Agent 身份可见**:管理后台配置多个 Agent 后,用户在客户端很容易选错 Agent 却难以察觉——此前只有右侧边栏「工作区」分区里一行灰字。现在已有会话的当前 Agent 出现在:会话区顶部身份条(头像 + 名称 + 一句话描述)、输入框工具条左侧的只读徽标(移动端只保留头像)、右侧检查器最上方的「智能体」分区(原先挤在「工作区」里的 Agent 名已移出)。
- 点击工具条徽标展开右侧边栏并滚动到「智能体」分区;本次只做展示,不提供会话内切换 Agent。

## 0.0.320 (2026-10-20)

### 后端
- **新增自定义 HTTP 工具(懒加载集成)**:在设置页「我的工具」把自己的 HTTP 接口封装成 Agent 可调用的工具——声明端点 URL、方法、参数(string/number/boolean,可设默认值与必填)、固定请求头(Token / Key 等敏感值 AES-GCM 加密存储、保存后掩码回显)与 `{{参数名}}` 请求体模板。工具 schema 一律不进入模型 `tools` 数组,只在系统提示词的目录分节里列名与用途;模型按需 `http_describe(工具名)` 取参数定义、`http_call(工具名, 参数)` 发起调用,执行内核统一做参数校验、模板渲染、超时与 64KB 响应截断。LOCAL / CLOUD / docker / CLI / 渠道全模式口径一致:请求由服务端直连目标地址,**没有审批确认环节**(可写工具请只封装可信端点),每工具可单独启停,单用户启用上限默认 20(系统设置可调)。
- **SSRF 默认拒私网 + 管理员 CIDR 放行**:自定义工具请求默认拒绝环回、私网、链路本地与云元数据地址(环回与 `169.254.169.254` 永不放行,放行清单保存时即拒绝与这两段相交的网段);确需访问内网时由管理员在系统设置「工具与终端 → 自定义 HTTP 工具」按 CIDR 网段放行(如 `["10.0.0.0/8","192.168.1.0/24"]`),主机名解析出多个地址时每个地址都必须在名单内,重定向最多跟随 2 跳且每一跳重新做放行判定。请求头加密密钥经 `APP_USER_TOOL_SECRET` 配置。
- **管理后台新增「自定义工具」只读页**(运行组,权限「查看自定义工具」):跨用户工具清单(按关键字 / 用户名 / 启用态筛选)+ 调用统计(调用次数、失败次数、失败率、调用用户数、首次 / 最近调用时间,按工具名聚合,数据来自运行轨迹中的 `http_call` 记录)。

### 前端(桌面 / Web / 安卓)
- 设置页新增「我的工具」页:工具卡片列表(端点、只读 / 可写标记、启停)、编辑器(工具名 / 展示名 / 用途说明 / 方法 / URL / 请求头键值对带敏感标记 / 参数动态行 / 请求体模板 / 只读开关 / 超时)与「测试调用」抽屉——按参数表动态生成表单、默认值预填,实调一次并展示原始响应与截断提示(不落库,与正式调用同一执行路径)。可写工具在编辑器内明示「没有审批确认」;保存前可先试运行验证。
- 工具调用卡片中文名与参数预览:`调用自定义工具` 显示内层工具名与参数摘要,`查询自定义工具参数` 显示工具名。

### 桌面 Electron
- 修复 Mac 桌面客户端「我的用量」抽屉头部点不动:刷新按钮与统计周期前 7 项(今日~近 7 天)落在窗口拖拽区几何范围内,mousedown 被系统吞掉(无 hover 变化、无点击),只有伸进右侧免拖拽区的「近 30 天 / 自定义」可点。Element Plus 浮层(抽屉 / 对话框遮罩、下拉菜单、日期面板、气泡提示)整体标记 `-webkit-app-region: no-drag`,从拖拽区里抠掉;无浮层时拖拽区与此前完全一致,Web / 安卓端不受影响。

### 管理后台
- 新增「自定义工具」页(运行组):清单表 + 调用统计表,只读无写操作。
- 系统设置「工具与终端」分组下新增「自定义 HTTP 工具」分类:`tools.http.privateAllowlist`(CIDR JSON 数组多行编辑器,保存前校验格式,留空 = 一律按私网拒绝)与 `tools.http.maxEnabledPerUser`(单用户启用上限,1~100)。

---

## 0.0.319 (2026-10-20)

### 后端

- **Embed SDK 新增页面操作总开关(连接级)**:WS 鉴权首帧新增 `pageEnabled` 声明(缺省/非布尔按 `true`,旧版 SDK 不发送该字段,行为与现状一致);后端按连接记录该标记,`buildContext` 对声明关闭的 embed 连接过滤全部 `page_*` 工具(Agent 侧表现为「没有页面操作能力」,不浪费轮次);`EmbedPageToolRegistry` 对关闭态连接的在途/新请求直接返回可解释错误 `page_operation_disabled`,不下发到宿主页面。连接非 OPEN/未鉴权时一律向「关闭」收敛(fail-closed)。

### 前端(桌面 / Web / 安卓)

- **Embed SDK 新增 `page.enabled` 初始化选项**(缺省 `true`,存量接入零改动):传 `false` 时端到端关闭页面操作——Agent 拿不到 `page_*` 工具;SDK 不创建页面引擎(不 patch `history`、不挂窗口监听、不扫描 DOM);浮窗不展示授权下拉、确认卡片、动作日志与目标高亮;`inspectPage()` / `capturePageScreenshot()` 抛错,`executePageAction()` 返回 `page_operation_disabled`,`getPageAuthorization()` 固定 `per_action`,`setPageAuthorization()` 为空操作;与 `initialLevel` 并传时后者被忽略。声明随 WS 连接发送、重连自动重发;新旧版本交错窗口期内行为安全(新 SDK + 旧后端:禁用态请求被 SDK 直接失败;新后端 + 旧 SDK:按开启处理)。SDK 产物版本 0.2.0 → 0.3.0。

---

## 0.0.318 (2026-10-20)

### 后端
- **任务通知第三渠道「邮件」(EMAIL)**:新增实例级 SMTP 配置(7 个系统设置项,密码行加密存储)与邮件发送器,复用既有投递链路(调度、快照、重试、终态 CAS、WS 抑制、metrics);通知邮件为 HTML + 纯文本双 Part,主题取通知文本首行,飞书卡片与会话深链在邮件中被忽略。渠道枚举 `NotificationChannel` 扩展为 `DINGTALK / FEISHU / EMAIL`。
- **通知偏好邮件分支**:选择邮件渠道时地址取自个人资料邮箱(每次保存重算密文),未填邮箱或实例未启用邮件时保存被拒绝并给出引导文案;「获取偏好」返回邮箱掩码(`z***@example.com`);「发送测试」在未保存偏好时回落到当前资料邮箱。
- **邮箱变更即时刷新收件地址**:用户自助或管理端修改邮箱后,即时重写邮件偏好的收件密文;清空邮箱时同步清空收件地址(不再投递),刷新失败仅记日志不阻断资料更新;管理端更新邮箱新增格式校验。
- **微信降级推送放行邮件渠道**:微信回复发送失败时,选邮件渠道的用户同样能收到降级推送(原仅钉钉 / 飞书)。
- **新增管理端点** `POST /v1/system-settings/test/smtp`:表单配置与已存值合并后发测试信到当前管理员邮箱;数据库迁移 V162(邮件 SMTP 设置项 seed)。

### 前端(桌面 / Web / 安卓)
- **消息通知新增「邮件」推送方式**:通知设置的推送方式三选一(钉钉 / 飞书 / 邮件);选择邮件后不再要求填写 Webhook 地址,改为展示「将发送到:{个人资料邮箱}」,未填邮箱时引导前往个人资料填写;「发送测试通知」与「保存」对邮件渠道同样可用(不传 Webhook 地址)。
- **个人资料邮箱补充用途说明**:邮箱输入框下新增「用于接收任务通知邮件」提示;修改邮箱后,已选邮件渠道的通知收件地址自动随之更新,不会继续发往旧地址。

### 管理后台
- **新增「邮件(SMTP)」通知渠道配置**:「通知与消息」分组新增邮件设置卡片组——渠道开关、SMTP 主机 / 端口 / TLS 开关 / 登录用户名 / 密码(加密存储,掩码回显)/ 发件人地址,保存后即时生效;「测试连接」用当前表单配置(未保存也可)发一封测试邮件到管理员自己的邮箱,管理员未填邮箱时拒绝并提示先去个人资料填写。

---

## 0.0.317 (2026-10-20)

### 后端

- **修复飞书完成卡点踩后内容丢失与旧卡点踩反馈错位**:飞书点踩此前按「会话级」处理——反馈固定落到会话最后一条 ASSISTANT 消息、重绘固定取当前进度句柄,导致两个线上问题:进程重启后点踩,内存快照清零,卡片被重建为无轮次/无耗时/无正文的空壳卡;点聊天记录里的旧完成卡,反馈打到最新回复上、旧卡被最新任务快照整体重绘、点踩态串到最新卡,最新消息已有反馈记录时还会被误删。现改为按卡归属:新增 `feishu_card_snapshot` 表,在完成卡终态 PATCH 前落一行终态快照(卡片 messageId → 该卡对应的 ASSISTANT 消息、轮次、耗时、正文);点击时从回调事件的 `open_message_id` 取被点卡片身份,反馈记录 toggle 到该卡对应的消息(`toggleSessionDislike` 增加可选 messageId 参数并校验消息归属,桌面端点踩链路不变),重绘按「内存按卡句柄 → 持久化快照重建 → 会话级兜底」三级降级,兜底仅命中改造前发出的存量卡并打 warn 日志;点踩态按卡隔离,不再影响同会话其它卡片。消息已被清理时与桌面端口径一致提示「未找到可反馈的任务结果」。

### 管理后台

- **点踩明细修复两处显示异常**:时间列此前恒显示「无效时间」——明细查询重写 SELECT 时漏掉了 `created_at`,接口返回字面量 `"undefined"`,现补回该列;消息内容列此前把「消息存在但无文本」误报为「(消息已不存在)」——工具调用中间消息的 `content` 为空即触发该文案,现按 `messageExists` 区分「消息已不存在」与「消息无文本内容」,并对无正文消息回退展示首个工具调用摘要(`[工具调用] …`,多条时标注「等 N 项」)。
- **飞书完成卡点踩定位修正**:改为取会话中最近一条**有文本内容**的 ASSISTANT 消息,不再落到执行期 `content` 为空的工具调用中间消息上,反馈对象与后台预览因此不再错位、不再恒为空。

---

## 0.0.316 (2026-10-20)

### 后端

- **容器 `/tmp` 改为每用户持久目录**:容器内 `/tmp` 从 tmpfs 改为挂载宿主机 `{MAO_CONTAINER_TMP_DIR}/{userId}`(默认 `/opt/mao-data/tmp/{userId}`),容器 stop/重建后临时文件与脚本不再丢失;目录权限统一 1777,超过 `MAO_CONTAINER_TMP_RETENTION_DAYS`(默认 7 天,按 mtime)的条目在容器创建与空闲巡检时清理。顺带消除 tmpfs 的 `noexec` 限制。新增 `MAO_CONTAINER_TMP_DIR` / `MAO_CONTAINER_TMP_RETENTION_DAYS`,移除 `MAO_CONTAINER_TMPFS_SIZE_MB`。挂载清单新增 `purpose=tmp` 条目(容器路径唯一例外:`{tmpDir}/{userId}` → `/tmp`),已有容器在下次创建/重建时自动带上。
- **子代理等待引导修正**:`wait_subagents` 由「慎用」改为「需要子代理结果时直接调用」(阻塞到全部结束或超时返回),并明确只在看进度/纠偏时用 `check_subagent`、禁止固定 sleep 轮询;`spawn_subagent` 的委派纪律与 `wait_subagents` 工具描述同步补上该指引。
- **修复微信回复降级推送的飞书卡片不渲染 Markdown 表格**:该卡片原先沿用任务通知卡的 1.0 结构(顶层 `elements` + `div > lark_md`),而 `lark_md` 是「文本格式」tag,只渲染加粗/斜体/链接等基本语法,Markdown 表格会被原样吐出管道符。现改为与飞书会话进度卡同口径的卡片 JSON 2.0(`schema: '2.0'` + `body.elements` + 顶层 `tag:'markdown'` 元素),表格正常渲染。二者仅一字之差但语义完全不同——`markdown` 才是元素 tag。发送侧无需改动:`msg_type:'interactive'` 原样透传卡片,2.0 schema 由卡片自身声明。钉钉渠道与飞书文本回退形态不变。
- **任务通知卡片统一升级为飞书卡片 JSON 2.0**:`buildTaskNotificationCard`(任务完成 / 失败 / 提问通知,经 webhook 投递)原先是最后一处 1.0 结构卡片,与其余五处出站卡片(会话进度卡、终态卡、排队卡、ECP 引导卡、微信降级推送卡)不一致。现同样改为 `schema: '2.0'` + `body.elements` + 顶层 `tag:'markdown'` 元素,正文含 Markdown 表格时可正常渲染;「会话详情」按钮因 2.0 已移除 `tag:'action'` 交互模块,改放入 `column_set` 并以 `behaviors.open_url` 跳转(与进度卡按钮同写法)。影响三条链路:正常任务通知调度、微信回复降级推送、设置页「发送测试通知」。钉钉渠道与飞书文本回退形态不变。

---

## 0.0.315 (2026-10-19)

### 后端

- **容器 `/tmp` 改为可执行(tmpfs 显式 `exec`)**:Docker 默认把 tmpfs 挂成 `noexec`,导致容器内 `/tmp` 下的脚本无法直接执行——最典型的是平台注入的 `GIT_ASKPASS` 凭据脚本(`git push` / `git clone` 直接报 `cannot exec ... Permission denied`),npm / pip / node-gyp 的临时编译脚本也可能受影响。现在容器启动参数显式加 `exec`;隔离边界仍由挂载清单、无 docker.sock、凭据不进容器保证。本次不改挂载清单,已有容器不会自动重建,需 stop/rm 容器或等下次重建后生效。

---

## 0.0.314 (2026-10-19)

### 后端

- **容器模式下平台技能目录改为可写**:`/opt/mao-data/skills` 原先以 `ro` 挂入每用户容器,容器内无法安装/更新共享技能;现改为 `rw`。这是挂载校验里唯一的共享可写例外,且仅限登记的 `skillsDir` 根,不会放宽其他挂载。已有容器会在下一次工具调用时因挂载指纹变化自动重建一次。注意该目录为全员共享,容器内 root 修改会影响其他用户加载的技能;个人技能仍写 `userskills/{userId}`。

---

## 0.0.313 (2026-10-19)

### 管理后台

- **用户运行时模式支持后台切换(全局)**:系统设置 → 平台与运维 → 运行环境新增「用户运行时模式」下拉(宿主机 host / 容器隔离 docker)。此前全局开关只能改后端 `.env` 的 `MAO_CLOUD_RUNTIME` 并重启进程;现在 DB(`system_setting.runtime.mode`,V160)优先于环境变量,保存后无需重启、5 秒内生效,单用户覆盖(用户管理 → 编辑用户 → 运行时模式)语义不变。
- 切到 docker 时后端先做容器运行时预检(Docker 可用、`mao-runtime` 镜像存在、socket 根目录可写),失败直接拒绝保存并返回具体原因;保存成功后立即失效模式解析缓存,下一轮工具调用即按新模式判定。

### 后端

- 新增 `RuntimeModeResolver`:全局运行时模式按「`system_setting.runtime.mode` → `MAO_CLOUD_RUNTIME` → host」解析,带 5 秒缓存;查库失败只告警并回落,不阻断会话。
- 系统设置写接口新增保存前校验 / 保存后回调钩子;容器运行时改为可动态激活(预检 + 空闲回收巡检幂等启动)。`MAO_CLOUD_RUNTIME=docker` 启动预检失败仍按原语义退出,DB 覆盖为 docker 时预检失败仅记错误、不阻止服务启动。

---

## 0.0.312 (2026-10-19)

### 后端

- **修复正文为 JSON 数组字面量的消息在历史 / 分享 / 管理端详情里显示为空**:`toMessageVO` 只要 content trim 后以 `[` 开头就无条件按多模态 content part 数组解析,`[1,2,3]`、`["a","b"]`、`[]` 这类纯文本消息解析后没有 text part,content 被清成空串(LLM 侧早有结构守卫、接口侧没有)。现与 LLM 侧共用同一 `isMultimodalContentParts` 判定,非多模态结构原样返回,真正的多模态数组照旧展开。
- **修复 WAV 语音时长按头部声明长度放大、畸形文件可挂死后端**:`parseWavInfo` 直接采信 data chunk 声明长度,流式 / 截断文件把长度写成 `0x7FFFFFFF` 占位值时,4 个真实字节被换算成约 12.4 小时的假时长(管理后台模型连通性 / 语音测试展示即为此值);chunk size 按有符号读取时,非 fmt/data chunk 声明负 size 会让解析 offset 原地踏步、同步死循环挂死 Node 事件循环。现 data 长度钳到实际字节数、chunk size 按无符号读取并强制 offset 每轮严格递增。
- **修复日报调度器慢 tick 重叠时重复投递**:`setInterval` 不等待上一轮 tick,单轮超过 60 秒时下一轮带着过期快照并发进入,对尚未写回 `last_digest_date` 的用户二次处理——把已读日报打回未读、让 Webhook 订阅方收到两份。现加在途标志,上一轮未结束直接跳过本轮。
- **修复 SKILL.md frontmatter 值含 `---` 时被静默截断或误判非法**:结束定界符用裸 `indexOf('---')` 子串搜索,`description: 处理步骤 --- 详细说明` 这类合法值会被提前截断、正文被污染,加引号的值更被切在中途判成「YAML 非法」而拒掉合法技能文件。现改为按行锚定,只认独立成行、trim 后恰为 `---` 的第二行作为定界符。
- **修复管理端会话详情把合法 JSON 工具入参截成非法 JSON**:工具参数压缩的纯文本兜底在「JSON 合法但没有单个顶层字段超 8000」时也被触达(如两个各数千字的字段、或长内容藏在嵌套结构里),把 `fn.arguments` 截成含未转义换行的非法片段,管理后台 `parseToolArguments` 解析失败导致工具卡片「输入」面板整段消失。现 JSON 解析成功后不再走纯文本兜底,无字段可压时原样返回。
- **修复 shell 结果摘要行数恒多算 1**:`split('\n').length` 把结尾换行产生的空段计入长度,终端输出几乎总以换行结尾,于是两行输出显示成「3 行输出」。现按 read_file 的 splitLines 口径计数,结尾换行不产生空行。

### 终端 CLI(mao-agent)

- **修复 glob_search 把「命中数恰好等于 head_limit」误报为截断**:`truncated` 用 `files.length >= headLimit` 判定,恰好命中 N 条时也报 `truncated: true`,与云端 glob_search(只有真的遇到第 N+1 个匹配才置 true)口径不一致,可能诱导模型做无谓的追加搜索。现遍历时多探一个候选,仅当确有多余匹配才判截断。

---

## 0.0.311 (2026-10-19)

### 后端

- **飞书进度卡片展示运行中的子代理**:主任务执行期间,卡片在正文下方、本轮工具上方新增「子代理 · N 个运行中」,按「调研 / 开发 / 审查」类型 + 任务标题逐条列出,最多 5 条(其余折叠为「另有 N 个运行中」)。仅展示 `spawn_subagent` / `subagent_followup` 产生的后台执行,已废弃的 delegate 不再出现;无子代理或任务终态时不渲染,卡片其余布局保持不变。
- **修复定时任务回复在「超过 24 小时未在微信对话」时微信与 Webhook 都收不到的问题**:24 小时主动发送窗口预检判定超窗后,原先只写一条站内收件箱通知就直接 `return`,没有把回复原文降级推送到用户已配置的钉钉 / 飞书 Webhook——而 webhook 降级分支此前只在「`sendText` 实际调用且失败」和「异常」两条路径上触发,预检短路永远走不到。表现为用户配好了消息通知 Webhook,定时任务在长时间未对话后,微信(窗口已关必然被拒)和 Webhook(压根没调)两边都是静默。现在预检判定超窗后与 ilink 拒收同口径:回复原文照旧推送 Webhook,站内收件箱通知不变,用户至少有一个渠道能拿到完整内容;未配置通知的用户行为与之前一致。
- **微信回复降级推送改用飞书卡片,与其它通知形态统一**:此前只给发送器传 `text`,而飞书发送器是「有 `card` 才发 interactive 卡片,否则发文本」,导致这条降级推送在飞书里退化成纯文本气泡,与任务完成 / 失败 / 提问等通知的卡片观感不一致。现在按正常任务通知的同口径同时下发 `text`(钉钉渠道与飞书回退)与 `card`(飞书 interactive 卡片,橙色告警头 + 分隔线 + 回复原文 + 时间脚注,无按钮——降级场景没有会话深链可点)。钉钉渠道形态不变。卡片内正文单独按 3000 字符预算裁剪(文本出口仍为 4000),避免超长回复把飞书卡片撑坏。

---

## 0.0.310 (2026-10-19)

### 管理后台

- **会话管理「类型」列主会话不再留空**:原先只有边路任务与子代理着标签,主会话整格空白,容易被误判成字段缺失。现在三种类型都着标签——主会话(灰)/边路任务(橙)/子代理(蓝),悬停说明来源(父会话 ID,或「由主会话拉起的边路任务」「由主会话委派的子代理」「用户直接发起的主会话」)。移动端卡片与详情页「类型」栏同步;后端 `sessionType` 透出与 `parentSessionId` 口径不变。

---

## 0.0.309 (2026-10-19)

### 后端

- **修复飞书话题群「群内最近消息」重复注入**:话题会话的水位线存放在 `feishu_thread_session.last_context_log_id`,但该行原先由 `handler.onMessage` 创建,晚于 `buildGroupContext` 读取/推进水位线。话题首个触发消息推进水位线时 UPDATE 命中 0 行且不报错,水位线停在 0;同一话题的下一次触发会把上一次已注入的整个窗口再注入一遍(超出 20 条 / 120 分钟窗口时则表现为旧消息被重新做成「更早历史消息摘要」)。现在触发路径在读取群上下文前先确保话题会话映射存在(授权通过后),首个触发即可正常落库水位线。与队列 / 纠偏无关——上下文在消息入队前就已构建。

---

## 0.0.308 (2026-10-19)

### 前端(桌面 / Web / 安卓)

- **「环境变量」入口移到右侧「任务」面板的工作区区块**:入口从输入框工具栏的齿轮按钮改为任务面板「工作区」分组下与路径、Git 摘要并列的一行按钮,与它所维护的对象(当前会话的工作区)在同一处呈现。展示条件不变——仅 CLOUD 会话且有会话 id 时出现;LOCAL 会话用本机环境不注入,新建任务占位态没有会话可鉴权,均不显示。切换会话时抽屉自动关闭,避免把变量改到另一个会话的工作区上。

### 管理后台

- **会话工作区文件弹窗支持图标 / 列表双视图**:工具栏新增图标 / 列表切换(与桌面端「文件管理」一致),图标视图按自适应网格平铺,目录、图片、符号链接、普通文件分色显示(规则对齐桌面端 `iconKindOf`),卡片悬浮浮现「预览 / 下载」操作;列表视图沿用原有的名称 / 大小 / 修改时间表格。视图偏好记在本地(`mao.admin.workspace-files.viewMode`),刷新与下次打开保持。搜索结果同样跟随当前视图。弹窗高度放开(`top` 4vh、上限 92vh,内容区弹性填满剩余空间、超出才滚动,不再被固定 44vh 限成「扁罐子」)。窄屏下图标网格收紧为紧凑密度,操作按钮常显(触屏无 hover)。

---

### 后端

- **`read_file` 剥离 BOM**:读取侧与写回侧(`write_file` / `edit_file`)统一口径,读出的首行不再带 `\uFEFF`。此前模型把读到首行原样复制进 `edit_file` 的 `old_string` 必然匹配失败(磁盘上无 BOM),且报错无法指向真实原因;CLOUD 与 LOCAL 在同一文件上返回的 `content` 也不一致。写回仍保留原文件的 BOM 与 CRLF。
- **`read_file` 的 `offset` / `limit` 改为显式校验**:负数与非数字入参不再被 `Math.max(0, ...)` 静默钳成 0 行窗口——那会返回「空 content + success + total_lines=100」,模型极易误判文件为空并用 `write_file` 整体覆写清空它。现在返回带 `error` 键的错误(下游据此判失败态),模型可重试;`limit: 0` 仍是合法的空窗。
- **工具结果摘要可见失败态**:`read_file` / `write_file` / `edit_file` / `glob_search` / `grep_search` 五个分支补上 `error` 判定,与 `summarizeShell` / `summarizePageTool` 的既有口径一致。此前路径写错、`edit_file` 匹配歧义、grep 正则非法、rg 崩溃这些最高频的失败,在会话摘要、运行轨迹与活动流里全部显示成一次正常的「读取 X」「搜索内容 (0 处匹配)」——「0 处匹配」更会被误读成「确实搜过了」。
- **信号哨兵触发失败不再重复落记录、不再反复触发**:触发失败(预算 BLOCK / Agent 停用 / 会话创建失败)时复用本轮已落的 check 行补写失败原因(此前另起一行,同一次轮询显示「变化」+「失败」两行自相矛盾的记录),同时把基线 hash 推进到本次读到的值。此前目标值不变时每轮都判为「变化」并重新发起一次 Agent 运行,退避期内会重复触发 N 个会话与计费。
- **一次性定时任务暂停期间也能收口**:`settleQueuedExecution` 的 once 收口判据从 `once === 1 && status === 'ACTIVE'` 改为只看 `once === 1`。此前 busy 入队后用户暂停任务、队列消息随后执行到终态时,整个 once 分支被跳过,任务停在 `finished=0 + nextFireTime=null` 的悬空态;用户之后从 PAUSED 重新启用时 `reactivate` 分支命中并重开下一档,**once 任务被执行第二次**。`status` 现在只决定「取消时是否重开档期」:ACTIVE 下取消仍恢复下一档,PAUSED 下不重开。

### 前端(桌面 / Web / 安卓)

- **文件体积展示不再出现 `1024 KB` / `1024 GB`**:`formatSize` 在展示前按 `Math.round` 的实际进位点(`>= 1023.5`)再判一次档,与 `usageFormat.pickTokenUnit` 的进位口径对齐。此前体积落在单位上限附近(如 `1023.5 KB`)的文件会显示成跨档读数。

---

### 后端

- **MCP 工作集(Working Set,默认开启)**:新增用户级「MCP 工具加载模式」。工作集模式下会话不再把每台 MCP server 的全部工具 JSON Schema 注入 `tools` 数组,改为在系统提示新增独立一节「MCP 工作集」,只列 server 名、用途、工具名与只读标记;模型不确定参数时先调 `mcp_describe(server, tool)` 取 schema,再调 `mcp_call(server, tool, arguments)` 执行。全量注入模式保留为可切换项(行为与改造前一致)。工具清单获取链路(CLOUD 连接 / LOCAL 桌面上报)不变,两种模式复用同一套连接与执行内核。
- **存储与接口**:新增 `V159` 迁移——用户级单值表 `user_mcp_load_mode`(`WORKING_SET` / `FULL`,与 per-server 的 `user_mcp_preference` 解耦)与系统设置 `mcp.loadMode` 全局默认(默认 `working_set`);新增 `GET/PUT /v1/mcp-servers/load-mode`。解析顺序「用户偏好 → 全局默认 → 工作集」,带 5s TTL 缓存,查询失败降级为工作集、不阻断会话。
- **失败信息与只读注解**:`McpToolsRegistry` 与 `CloudConnectResult` 新增结构化失败清单(`failedServers` / `connected:false` 的 server 与原因),工作集对连接失败的 server 标注「当前不可用」而不静默消失;`McpToolRef` 新增 `annotations.readOnlyHint`,CLOUD 连接、桌面上报、`mao-agent`、docker 容器四条来源全链路透传。工作集模式下 LOCAL 审批对明确只读的 MCP 工具在 READ_ONLY / READ_WRITE 级免审,未标注一律维持审批;SMART 级仍人工审批。全量注入模式不消费该注解,保持改造前「MCP 一律审批」的行为。
- **审批与展示**:LOCAL 会话的 `mcp_call` 在 `ToolDispatcher` 内还原为 `mcp__{server}__{tool}` 全名并拆出内层参数下发桌面端,规则匹配 / 「总是允许」hint / 审计口径沿现有链路生效;参数无法还原时按 MCP 工具保守审批。工具结果摘要新增 `mcp_call` / `mcp_describe` 分支,流式事件保留原始工具名,桌面展示层还原为 `server.tool`。

### 前端(桌面 / Web / 安卓)

- **设置 → MCP 服务器**:新增「工具加载模式」单选(工作集 / 全量注入),切换后对新会话生效;模式为用户级单值偏好,与每台服务器的启用开关互不影响。
- **工具卡片**:`mcp_call` / `mcp_describe` 显示中文名与 `server.tool` 参数预览;全量模式与历史消息里的 `mcp__server__tool` 名称同样展示为 `server.tool`。
- **运行轨迹「贵」改为按成本判定**:「贵」不再看 token 量,改为单轮成本 `cost_micros` 达到阈值;阈值单位从 tokens 改为金额(元,默认 0.5,范围 0.001–1000),输入框保留三位小数。模型未配置价格的轮次一律不算贵(不设 token 回退口径)。阈值 localStorage 换 key(`mao:trace-thresholds:v2`),老用户阈值一次性重置为默认值。慢阈值、run 汇总、导出结构与后台「最贵的轮」榜不变。

### 终端 CLI(mao-agent)

- LOCAL 模式下 MCP 工具上报透传 `annotations`,使工作集的只读标记与审批免审对 CLI 会话同样生效(旧版 CLI 未升级时按缺失处理,功能降级但不报错)。

---

## 0.0.305 (2026-10-18)

### 后端

- **三级环境变量(用户 / 工作区 / 系统)**:环境变量从代码硬编码升级为三层可配置,优先级「工作区 > 用户全局 > 系统全局」,平台内置凭据(`GIT_TOKEN_*` / `MAO_TOKEN` / `ECP_TOKEN` / `LARKSUITE_CLI_*` / `HOME` / `PATH` / deps env)任何一层都不可覆盖。三条 CLOUD 执行链路(Agent shell、云端交互终端、docker 容器内 shell)共用同一套合并逻辑:host 模式每条命令前随 `refreshUserEnvironment` 刷新,容器模式经 `RuntimeEnvelope.envVars` 下发(容器内零查库),云端终端在 spawn 时注入。
- **存储与安全**:新增 `V158` 迁移与 `user_env_variable` / `workspace_env_variable` / `system_env_variable` 三张表,值一律 AES-CBC 加密(复用 `APP_GIT_CREDENTIAL_SECRET`),任何用户侧接口都不返回值。保留键清单(`PATH` / `HOME` / `LD_PRELOAD` / `NODE_OPTIONS` / `BASH_ENV` / `PYTHONPATH` 等,前缀 `GIT_TOKEN_` / `MYSQL_` / `APP_` / `MAO_`)在写入与合并两处双重拦截;单层≤50 条、值按 UTF-8 字节≤2048、解密失败跳过并告警。
- **新增接口**:`/v1/user/env-variables`、`/v1/user/workspace-env-variables`、`/v1/sessions/:id/effective-env`(仅会话归属人可读)、`/v1/admin/system-env-variables`(`settings:read/write`)、`/v1/admin/users/:id/env-variables` 与 `.../workspace-env-variables`(`user:read`,只读、值按密文长度掩码 `**** (N chars)`,读操作落 `/v1/admin` 审计)。管理员对用户层变量无任何写入口。
- **生效与开关**:进程内 5s TTL 缓存(开关与系统层同 TTL 同时失效),写入后本实例即时失效;`system_setting` 新增 `shell.envVars.enabled` 总开关(行缺失按开启处理,显式 `false` 才停用)。工作区绑定键统一 `path.resolve` 归一化,`workspace=null` 与全局工作区根同键,host / 容器 / 终端行为一致。

### 前端(桌面 / Web / 安卓)

- **设置 → 环境变量**:维护个人全局变量(新增 / 编辑 / 删除,值加密保存后不回显),页面说明优先级、生效延迟与保留键。
- **会话头部「环境变量」抽屉**:按来源分组查看当前会话生效变量(工作区 / 用户 / 系统 / 内置 / 继承,带条数),并管理当前工作区变量;仅 CLOUD 会话且非新建任务态显示,明确提示「配置态视图」与「已打开的终端需重开」。
- **修复打开运行轨迹后右侧边栏变成主会话信息**:从边路任务 / 子代理视图点任务状态徽标打开运行轨迹,中心区换成轨迹面板,但右侧边栏的标题、状态、上下文、进度、模型仍显示主会话的数据,排查时容易张冠李戴。现在边栏按轨迹所属会话展示:边路 / 子代理的轨迹 Tab(`trace:{sid}`)跟随该会话,主会话自身的轨迹保持主会话视角;轨迹所属子会话的列表尚未加载完时先回落主会话,列表到达后自动收敛。

### 管理后台

- **系统设置 → Shell 环境变量**:管理系统级变量(`settings:write`),并保留 `shell.envVars.enabled` 总开关。
- **用户详情 → 环境变量 Tab**:只读查看该用户的用户层与工作区层变量(键名明文、值脱敏),顶部明示「查看行为将被审计」,无操作列。

---

## 0.0.304 (2026-10-17)

### 后端

- **修复运行轨迹 Token 总量重复计入缓存**:统一 `ChatUsage.promptTokens` 本身已包含缓存命中/写入(OpenAI 的 `prompt_tokens` 含 `cached_tokens`,Anthropic 适配器已把 `cache_read`/`cache_creation` 并入 `promptTokens`),轨迹的「贵 ≥ N tokens」判定不再把 `cachedTokens`/`cacheCreationTokens` 再加一次,避免缓存越多阈值越早误触发。
- **修复 CRLF 文件多行编辑失败**:`read_file` 向模型展示的是归一化后的 LF 文本,`edit_file` 之前却按保留 `\r\n` 的原文匹配 `old_string`,导致 Windows 检出/`eol=crlf` 文件的多行替换必然报「文件中未找到 old_string」。现在匹配前统一按 LF 比较,写回时仍保留原文件 CRLF/BOM;CLOUD 与 LOCAL 工具同步修复。
- **修复微信语音文本清洗吞掉 snake_case 下划线**:只移除成对的 Markdown 强调符,`user_id`、`session_id`、`__init__` 等标识符不再被读成 `userid`/`sessionid`;`**重点**`、`_斜体_` 等成对强调仍正常去除。

### 前端(桌面 / Web / 安卓)

- **修复运行轨迹 Token 汇总虚高**:轨迹面板的 run/轮次/旁路调用与整页汇总改为「输入 + 输出」口径,与后端 `llm_call.total_tokens`、管理后台用量一致;缓存命中仍作为分项展示,不重复计入总量。
- **修复工作区 Markdown 相对链接无法点击**:链接渲染不再只放行 http(s),`./other.md`、`../README.md`、`#anchor` 与 `mailto:` 均保留 `href`,`FileViewer` 内的文件间跳转恢复;`javascript:`、`data:` 等危险协议继续降级为纯文本。
- **修复微信超窗与每日摘要不弹系统通知**:桌面系统通知的 kind 白名单补齐 `WEIXIN_REPLY_WINDOW_CLOSED` 与 `DAILY_DIGEST`,与后端「无偏好开关、始终通知」的口径一致;窗口失焦时这两类收件箱条目也会弹出通知。
- **修复删除已归档会话后分组计数错位**:删除归档会话只扣减归档区计数,不再误减活跃分组;归档区徽标与活跃分组总数保持与实际列表一致。

### 终端 CLI(mao-agent)

- **修复 LOCAL `edit_file` 对 CRLF 文件的多行匹配**:与 CLOUD 同口径,匹配前统一 LF、写回保留原行尾。

---

## 0.0.302 (2026-10-16)

### 前端(桌面 / Web / 安卓)

- **Token 紧凑展示全站统一保留两位小数**:K/M/B(桌面端含 T)折算从一位小数改为两位,1.4B 显示为 1.40B、667.6M 显示为 667.58M。覆盖管理后台「用量分析」的趋势纵轴、模型 / 用户 / Agent 排行标签与 tooltip、模型 Token 占比环图中心值,以及桌面端「我的用量」抽屉的总览卡片、趋势图纵轴与汇总、使用记录表、运行轨迹面板的 Token 汇总、任务检查器的上下文占用与「约节省 Nk tokens」。小于 1000 的 token 仍按千分位原样展示,不进紧凑单位;上一版(0.0.301)只改了总览主指标,图表与桌面端仍是一位小数,本次补齐。

---

## 0.0.301 (2026-10-16)

### 管理后台

- **「用量分析 · 总览」Token 消耗固定保留两位小数**:主指标的 Token 紧凑展示(K/M/B)从一位小数改为两位,1.4B 显示为 1.40B、1.45B 显示为 1.45B,与已有的计数卡片(1.70 万)和成本(最多两位)口径对齐。此前 0.0.290 的「万/亿计数两位」与 0.0.294 的「成本两位」都漏改了 Token 这一档。命中 / 入 Token 与「窗口构成」的对话 / 后台 Token 同页同步为两位;趋势图纵轴、模型 / 用户 / Agent 各 Tab 的条形排行与 tooltip 仍为一位小数,避免坐标轴被长刻度撑开。

---

## 0.0.300 (2026-10-16)

### 前端(桌面 / Web / 安卓)

- **浮窗入口换成品牌图标**:Web Embed SDK 右下角浮动按钮与展开后的面板标题栏头像,由抽象的「对话气泡 + M 声波」矢量标识换成 Mao 品牌图标(深色圆盘 + 金色羽毛,与 `docs/assets/logo-design` 规范一致)。入口图标按圆盘原样满铺按钮(56px),不再有矢量标识在按钮内偏小、留白偏大的问题;面板头像位同源缩小到 21px。图标随 SDK 产物内联,不增加任何网络请求。

---

## 0.0.299 (2026-10-16)

### 管理后台

- **移除「转经验」功能**:「点踩反馈」页的行内与批量「转经验 / 转为经验」按钮、「转为经验」向导(LLM 自动起草、编辑、确认写入)整体下线,后续重新设计后再上线。点踩反馈的查看、筛选(状态 / Agent / 原因 / 时间)与「忽略」处理不受影响;历史已转为经验的反馈行仍以「已转经验」状态展示、可按该状态筛选,「处理信息」列仍显示关联的经验 ID。

### 后端

- 移除 `POST /v1/feedback/admin/draft` 与 `POST /v1/feedback/admin/convert` 两个端点及其实现(经验草稿 LLM 起草、转经验同事务写入);「忽略」端点 `POST /v1/feedback/admin/dismiss` 与用户端点踩接口不变。
- 删除 `feedback.draftModelId` 设置项(V157 迁移):「系统设置 → Agent 与模型」里的「负反馈转经验的起草模型」选项移除,留空回落默认模型的逻辑随功能一并删除。
- 移除仅供转经验使用的「按消息 ID 精确定位 run 轨迹」能力(`RunTraceService.locateRunByMessageId`);翻页式运行轨迹接口不受影响。
- `message_feedback` 表结构不变:`status='CONVERTED'` 与 `experience_id` 作为存量数据保留可查;「负反馈转经验」用量场景(`feedback_experience_draft`)的历史用量记录仍在「我的用量」正常显示中文。

---

## 0.0.298 (2026-10-16)

### 管理后台

- **浮窗助手(接入 Web Embed SDK)**:管理后台右下角新增 Mao 助手浮动入口。管理员登录后点开浮窗,即可就当前页面与配置的 Agent 对话(流式输出、思考过程、工具执行状态、追问作答、手动停止、历史会话、粘贴附件),排障时不必再切到桌面端。浮窗复用 admin 登录态与 Embed SDK 既有能力,无新增鉴权链路;Agent 可在 `per_action`(每个写操作前在浮窗确认)授权下操作当前后台页面。在「系统设置 → 登录认证 → 集成配置」选择浮窗使用的 Agent,留空回落系统默认 Agent。已知限制:浮窗层级极高,展开时会覆盖 `el-dialog` / `el-drawer`(默认收起不遮挡);视觉不跟随后台主题;切换 Agent 后浮窗历史不再显示旧 Agent 的会话(会话本身不受影响);浮窗会话混在「会话管理」列表,无法按来源筛选。
- **会话详情支持查看工作区目录与文件**:信息卡新增「工作区」行(绝对路径省略展示 + 复制 + 「查看文件」入口;LOCAL 或无工作区时入口禁用并说明原因)。点「查看文件」打开全屏文件浏览器:面包屑导航 + 单层列表(名称/大小/修改时间,目录优先,符号链接标识且不可进入)、按文件名递归搜索(大小写不敏感,上限 100 条)、文本/Markdown(渲染态/源码双态)/图片/PDF 四种预览(其余格式提示下载后查看)、单文件与整目录 zip 下载(后端 1GB 上限,超限给明确错误)。聊天记录里文件变更面板的路径可点击,直接打开该文件当前内容预览。只读定位:不含上传/删除/重命名等写操作,也不含 Git 面板。
- admin 新增 vitest 单测基建(`npm test`,仅覆盖纯逻辑,不改变构建产物)。
- **用户管理三态状态口径修正**:用户状态为文档化三态(1=启用 / 0=禁用 / null=未设置),此前后台把「未设置」显示为「禁用」,且编辑未设置用户后保存会静默将其写成真禁用。现在列表(桌面表格与移动端卡片)与用户详情抽屉将 null 显示为「未设置」;编辑框打开未设置用户时状态开关落「启用」(与后端登录口径一致,未设置即活跃);「启用」筛选现在包含未设置账号(此前 `status = 1` 两边都查不到)。
- 修复「查看文件」打开会话工作区一律提示「工作区目录不存在或已被清理」:文件端点请求实际打到 `/api/v1/api/v1/admin/...` 得到 404,被前端归一成目录加载失败——而后端接口与工作区目录本身都正常。根因是 API base 的归属搞反了:admin 的 `api` 实例自带 `baseURL`,且 axios 只豁免 `http(s)://` 绝对 URL、`/` 开头的路径一样会被拼上 base,所以 JSON 端点必须传不含 base 的相对路径,只有裸 fetch 的字节流端点才需自带完整 base。现拆成 `sessionFilesPath`(api 实例用)与 `sessionFilesUrl`(裸 fetch 用)两个函数,并补 8 条 URL 拼接回归单测(含「`/` 开头不等于 axios 豁免 baseURL」的陷阱锁定)。
- 修复工作区文件预览一直显示「加载中…」:`previewFile` 请求返回后把结果写在**传入的原始对象**上(`preview.loading = false`),而 `state.preview` 是 Vue reactive 的代理——读写虽互通,依赖触发只认代理的 setter,于是模板收不到 trigger、永远停在「加载中…」(后端接口其实正常返回)。改为统一经 `state.preview` 回填(`fillPreview`)。此前还叠加过一个 `state.preview !== preview` 恒为 true 的守卫缺陷(对象入 reactive 即被包装成代理,引用比较永不相等),已改用 `toRaw` 比较;两者叠加导致该功能自上线起完全不可用。补 2 条 `watchEffect` 订阅回归,直接验证「字段变更能触发依赖」。
- 工作区文件弹窗由全屏改为常规弹窗(宽 1080px、最高 88vh,窄屏自适应拉满宽度):目录列表区有最大高度(44vh,窄屏 34vh)超出滚动,表头吸顶;面包屑固定不随内容滚动。
- **文件预览拆为独立弹窗**:原先预览面板与目录列表在同一个弹窗里纵向堆叠,打开预览会把弹窗从约 509px 撑到 901px(900px 高的视口直接溢出,`max-height: 88vh` 因内容相加而失效)。现预览拆到单独的 `WorkspaceFilePreviewDialog`(宽 960px、最高 88vh),列表弹窗高度不再随预览变化;预览态被清空(列表内刷新、切换目录)时预览弹窗自动收起,关闭列表弹窗会一并释放预览占用的 PDF blob URL。四种预览格式(文本 / Markdown 渲染态与源码双态 / 图片 / PDF)与「暂不支持预览,请下载后查看」兜底行为保持不变。

### 后端

- **IM 入站消息防抖合并(飞书 / 钉钉 / 微信)**:同一发送者在同一会话(飞书含话题)内 1 秒窗口期中连续发出的多条**纯文本**消息,合并为一次任务触发,正文按到达顺序空行拼接,不再每条消息各起一轮执行。合并只发生在「文本类且会触发」的消息上:带内嵌图片的富文本(飞书 post / 钉钉 richText)、图片、文件、语音/视频、私聊 `---` 新建会话、私聊引用切换(飞书)一律立即单独触发,不等窗口;不同发送者、不同会话互不影响。微信侧仅合并纯文本,图片/文件/语音消息单独处理(与既有的「后到消息接管」语义叠加:间隔超过 1 秒的两条消息仍是取代而非合并)。窗口时长可按通道分别配置:`FEISHU_BOT_MERGE_WINDOW_MS`、`DINGTALK_BOT_MERGE_WINDOW_MS`、`WEIXIN_BOT_MERGE_WINDOW_MS`(默认均为 1000,单位毫秒;设为 0 即完全关闭合并,行为与升级前一致,重启后端生效)。停机(含蓝绿发布排空)前会把在途窗口里的消息冲刷触发出去,不再静默丢弃;绑定成功后补投的原始消息不受窗口影响,仍即时触发。
- 新增只读接口 `GET /v1/embed/admin-config`:仅要求登录(不要求 `settings:read`),返回浮窗助手最终使用的 Agent ID;配置值非数字 / 不存在 / 已停用一律回落系统默认 Agent,均不可用时返回空(前端不初始化浮窗),不抛 500。
- 新增系统配置 `embed.agentId`(V156 迁移,分类「集成配置」):浮窗助手使用的 Agent ID,保存时校验必须为有效、未停用的 Agent,留空回落系统默认 Agent。
- **管理后台会话工作区只读文件端点**:新增 `/v1/admin/sessions/:id/files/{directory,search,read,preview,download,download-zip}` 六个只读端点,复用 `WorkspaceBrowseService`(与用户侧 `/v1/files/workspace-*` 同一套 `StrictPathResolver` 越界收口:相对穿越、越界绝对路径、符号链接逃逸一律拒绝)。鉴权只校验 `session:read` 权限码 + 会话存在、有工作区且为 CLOUD 执行模式,不做出属主校验(管理后台跨用户查看是本职;LOCAL 会话工作区在用户设备上,服务端不可浏览);用户侧属主校验与现有端点零改动。审计由 `/v1/admin` 前缀的全局 `onResponse` 钩子自动留痕(action=READ、objectType=admin.sessions、objectId=会话 id、queryString 含 path/dir),无显式审计代码。
- **修复 SSO / ECP 登录把「未设置」状态账号判为已禁用**:用户状态是三态(1=启用 / 0=禁用 / null=未设置),此前 SSO / ECP 登录用 `status !== 1` 判定,把 null 账号拒之门外(403 `account_forbidden` / 「账号已禁用或不存在」),而同一账号本地密码登录成功——两个入口结论相反。现两处校验统一到 `isActiveAccount` 口径(仅 `status === 0` 停用),null(未设置,SSO/ECP 外部账号的默认取值)正常放行;`deleted != 0` 与 `status = 0` 仍按原语义拒绝。
- **修复系统技能上传路径穿越**:`POST /v1/skill-docs/upload` 的落盘守卫原先只查 `includes('/.')`,`..` 段位于路径最前时(如 `myskill/../evil.txt`)不被拦截,文件会被写到技能子目录之外(skills 根或其它技能目录,可污染/覆盖其它技能的 SKILL.md)。现补齐与用户技能安装相同的双重防护:按段拒绝 `..` 与空段,外加解析后路径必须落在技能子目录内的收口检查;合法文件行为不变。
- **修复 Git 面板同目录改名文件增减行数恒为 +0/-0**:git numstat 对同目录(同前缀目录)改名输出花括号紧凑形态 `src/{a.txt => b.txt}`,后端切片后残留尾部 `}`,与 name-status 的键匹配不上,该文件的 insertions/deletions 被静默丢弃。现按紧凑形态(公共前缀 + 新名 + 公共后缀)完整还原新路径,同目录与跨目录改名口径一致;仓库级总数统计本就正确,不受影响。
- **修复分组展开页排序漏掉取消中的会话**:分组展开页 ORDER BY 的活跃相位原先只列 RUNNING/RESUMING/WAITING_APPROVAL,漏掉 CANCELLING,与侧边栏分组预览、仪表盘查询的口径矛盾——取消流程进行中的会话会被当成空闲排到后面。现四处口径(isActivePhase、VO、dashboard SQL、分组展开页 ORDER BY)统一由 `ACTIVE_PHASES` 单一常量派生,防止再次漂移。

**部署提示(顺序敏感)**:SDK 产物 `/embed/mao-chat.js` 由 desktop 部署提供(desktop prebuild 自动构建 `sdk/embed` 并拷入 `desktop/public/embed/`),**必须先部署 desktop 再部署 admin**;反序部署 admin 会引用 404,浮窗不出现(控制台有 warn,不影响后台其他功能),补齐 desktop 部署后刷新即恢复。无其他手工步骤。

### 终端 CLI(mao-agent)

- **修复 `MAO_AGENT_OUTPUT_FORMAT` 置空串导致任何子命令无法启动**:环境变量置空但未 unset(Docker `ENV MAO_AGENT_OUTPUT_FORMAT=`、CI 继承空值)或 `--output-format=` 内联空值时,空串被当作「已下发」进入枚举校验并抛错。现空串按「未显式下发」处理,回落 `resolveConfig` 的项目/用户配置链与默认值,与同仓 `envBool` 的口径一致;显式有效值行为不变。

## 0.0.297 (2026-10-15)

### 后端

- **开机自动拉起后端**:新增 `mao-backend.service` systemd 单元与 `scripts/mao-backend-boot.sh` / `scripts/mao-backend-stop.sh`。后端进程由 `restart.sh` 以 `nohup` 拉起、不随 systemd 常驻,机器重启后需人工登录执行 `restart.sh` 才能恢复服务。现在开机在 MySQL 与 Nginx 就绪后按 `active-backend-port` 记录的端口冷启动;该端口已健康则直接跳过,不会顺带做一次蓝绿切换。`systemctl stop mao-backend` 会停掉 9080 与 9081 两个端口。日常发版仍用 `restart.sh` / `scripts/restart-backend.sh`,不要改成 `Restart=always`(蓝绿会主动停旧端口,systemd 再拉起来会和切流抢进程);进程崩溃也不会被该单元重新拉起。安装方法见部署文档「开机自启」。
- **用户运行时容器隔离(docker 模式,默认关闭)**:新增全局开关 `MAO_CLOUD_RUNTIME=docker|host`(默认 host,行为与现状完全一致)与用户级覆盖 `user.runtime_mode`(管理后台用户管理页可按用户切换「跟随全局 / 容器隔离 / 宿主机」)。开启后,该用户 CLOUD 会话的执行类能力(shell、read_file / write_file / edit_file / glob_search / grep_search、MCP stdio、云端终端)在每用户独立 Docker 容器内执行:容器内 root、只挂载该用户自己的数据目录(工作区 / 虚拟 HOME / runtime / 依赖目录 / 个人技能,以及飞书 / 钉钉入站会话的工作区),跨用户数据不可读;MySQL、JWT secret、LLM API key、平台级 IM appSecret 一律不进容器,用户级凭据(Git token、MAO_TOKEN、ECP_TOKEN、飞书 UAT)由宿主机在每次派发时按次下发,容器用完即弃。容器空闲 60 分钟回收(`MAO_CONTAINER_IDLE_MINUTES`,须大于 shell 会话空闲超时),72 小时强制重建(`MAO_CONTAINER_MAX_LIFETIME_HOURS`);用户打开客户端即按活跃信号预热容器。依赖目录 `/opt/mao-data/deps/{userId}` 挂载进容器,`npm install -g` / `pip install` 的包在容器重建后不丢。
- **宿主机侧越界收口(host 模式同样生效,与容器化无关)**:管理后台/桌面的 REST 文件服务(`/v1/files/workspace-*`)路径解析从「绝对路径直达任意位置」收紧为「严格限定在会话工作区内」(含符号链接逃逸拒绝),相对穿越与越界绝对路径一律拒绝;MCP stdio 服务器的子进程环境从整包继承 `process.env` 改为白名单(`PATH`/`HOME`/`LANG` 等),`MYSQL_URL`、`APP_*`、`GIT_TOKEN_*` 等平台凭据不再泄漏给用户配置的 MCP 进程;微信 / 飞书 / 钉钉发送工具的本地文件读取同步收紧,越界路径拒绝。docker 模式下发送工具的本地路径改由容器读字节、宿主机只负责上传发送。
- **容器生命周期按用户串行 + 会话工作区归属校验**:同一用户的容器创建 / 重启 / 重建 / 回收串行执行。此前两个并发调用会各自「停-删-建」并互相摧毁对方刚建好的容器(容器名固定为 `mao-user-{userId}`),表现为容器反复重建、挂载缺失、在册条目与真实容器错位;现在重建走原地更新条目,调用方持有的句柄始终有效。CLOUD 会话创建时校验 `workspace` 必须位于本人工作区根之下(飞书 / 钉钉通道工作区由入站消息自动创建,不接受手工指定),越界返回参数错误;挂载侧另对私聊工作区 `private-{userId}` / `p2p-{userId}` 做本人校验,他人的私聊目录不会被挂进本人容器。容器启动 / 重启失败(含 idle 回收后被 `container-gc.sh` 清掉的场景)一律摘除条目并按完整重建自愈,不再把该用户的容器运行时卡死在失败态;失败收尾按世代与在册条目身份双重校验,失败等待期间被并发重建 / 首建重入交错建出的新容器不会被误删。
- 容器模式部署前置:生产机需安装 Docker 并运行 `bash scripts/build-runtime-image.sh` 构建 `mao-runtime` 镜像(内含与镜像 Node ABI 匹配的 sharp / node-pty;宿主机 Node 升级后必须重建镜像)。详见 DEPLOY.md「容器隔离运行时」。
- **图片产物按用户隔离 + 访问 URL 修对**:`generate_image` / `edit_image` 的落盘目录从上传根平坦目录改为 `{FILE_UPLOAD_DIR}/{userId}/` 子目录(用户上传 / 头像仍写平坦根,语义不变),产物 URL 修为 `https://<域名>/uploads/{userId}/<文件>`——此前拼接少了 `/uploads/` 一段(实际给出 `https://<域名>/api/<文件>`),无任何路由匹配,模型拿到的 `image_url` 必然 404,只能退化为发送工具读不了的绝对路径(静态托管本身注册了 `/uploads/` 与 `{contextPath}/uploads/` 两个前缀,即使拼成 `/api/uploads/...` 也会被 Nginx 扩展名正则 location 吞到前端构建产物而 404,新机器部署需按 DEPLOY.md 的 Nginx 片段包含 `^~ /api/uploads/`)。docker 模式下 `{uploadDir}/{userId}` 按用户同名挂载进容器(挂载清单附指纹,升级后既有容器在下一次工具调用时自动重建一次补齐挂载,在途 shell / pty 任务会丢失,属一次性行为),容器内 `read_file` 读图、`edit_image` 以上一张产物为输入、微信 / 飞书 / 钉钉发送工具的绝对路径发送在两种运行时模式下均可用;跨用户目录依旧拒绝。历史平坦目录的旧文件原样保留、不做迁移。
- 图片产物静态 origin 改按 `server.servlet.contextPath` 剥后缀,不再硬编码 `/api`:后台把 contextPath 配成 `/api/v1` 时,硬编码会残留一段导致 `image_url` 拼成 `/api/v1/uploads/...` 而 404。
- 微信 / 飞书 / 钉钉发送工具的路径描述统一为「工作区相对路径,或允许集内的绝对路径(会话工作区、本人 `uploads/{userId}` 产物目录、runtime、HOME、`/tmp`),或 http(s) URL」,与 `edit_image` 同口径,避免模型照旧描述传历史平坦目录路径后只拿到一句 `Path escape attempt`。
- 修复测试夹具落在 `/tmp` 导致 7 条越界拒绝类用例失败:`/tmp` 本身是严格路径解析的允许根,夹具默认改到仓库内 `.tmp-test/`(可用 `MAO_TEST_TMP_DIR` 覆盖);git / 环境信息探测类用例改用仓库外夹具,避免向上发现仓库 `.git` 使「非 git 目录」断言翻转。

### 管理后台

- 用户管理页「编辑用户」新增「运行时模式」下拉(跟随全局 / 容器隔离 docker / 宿主机 host),需 `user:write` 权限;用于容器模式的按用户灰度。
- 「用量分析 · 总览」指标卡适配手机宽度:此前指标条窄屏固定 3 列,390px 手机上主指标 36px 的数值会溢出所在列、与右侧卡片的数字粘连(360px 溢出 25px),「1.45 万」还会被折成「1.45 / 万」两行、环比徽章紧贴数值。现在 ≤960px 收成 3 列并拉开列间距,≤768px 收成 2 列且「Token 消耗」独占整行,数值与环比徽章允许换行、数字整体不拆行,手机上不再拥挤。

## 0.0.296 (2026-10-15)

### 管理后台

- 「会话管理」展示失败原因:此前失败会话只显示红色「失败」标签,看不到为什么失败。现在列表的「失败」标签悬浮即可预览失败原因,会话详情「会话信息」新增「失败原因」一行展示完整文本(长异常串可滚动),移动端卡片限 3 行预览、完整文本进详情页。

### 后端

- 管理后台会话 VO(列表 / 详情)新增 `failureReason` 字段:`phase=FAILED` 时取任务终态落库的失败原因(`runtime_status_json.executionError`),非失败态为 `null`。按相位取值,重试跑起来后不会把上次的失败原因误当成本次执行的现状。
- 补齐此前丢失失败原因的终态链路,让管理后台能看到全部失败会话的原因:飞书 / 钉钉通道会话(入站处理器的 `onExecutionFinished` 增加 `failureReason` 回调参数,失败时传 `failText`)与子代理会话(`finishSubagent` 增加原因透传,覆盖 delegate、后台子代理、崩溃恢复各失败路径)失败时同样写入 `executionError`。

## 0.0.295 (2026-10-15)

### 管理后台

- 修复「点踩反馈」页转经验向导的「生成经验」必然失败:点击后固定报「服务内部错误」,现可正常起草经验。两处根因——起草服务未接上 LLM 客户端(装配时误传了按协议路由的工厂函数 `routeChatClient`,它没有 `chat` 方法,调用在发出请求前即失败),以及用量表 `llm_usage.session_id` 为 `NOT NULL` 而经验起草属于非会话调用,用量落库异常在 `finally` 中顶掉了真实错误。

### 后端

- `llm_usage.session_id` 改为可空(V154 迁移):该表面向「非 chat / background LLM 调用」,反馈转经验起草这类管理动作本就没有关联会话;既有按 `created_at` / `model_id` 的聚合口径不受影响。
- `ExperienceDraftService` 的 LLM 客户端依赖改用 `Pick<LlmAdapter, 'chat'>` 真实类型,去掉服务内自定义接口与装配处的 `as never` 断言,让编译器拦住同类误配。
- 修复微信 Bot 通道一次网络抖动后就永久失联的问题:账号轮询循环连续失败达到 `maxConsecutiveFailures`(默认 3 次)后会自行退出并打「停止监控」日志,但此前没有任何机制把它重新拉起来——已绑定账号在 ilink 短暂超时后就再也收不到消息,用户侧表现为机器人彻底没反应,只能等后端进程重启或去渠道绑定页重新扫码。现在 `start()` 会按 `monitor.reconcileIntervalMs`(默认 5 秒)周期对账,把「应当活着但已掉线」的账号重新拉起,与飞书 / 钉钉 Bot 的 reconcile 行为一致;对账查询账号列表失败只记日志,不影响其他账号与进程。线上 10-15 06:07 两个账号同时超时掉线即属此类,本次部署后自动恢复,无需重新绑定。

---

## 0.0.294 (2026-10-14)

### 前端(桌面 / Web / 安卓)

- 「我的用量」抽屉的调用结果从「成功 / 失败」两态改为三态:用户点「停止」产生的取消(后端 `errorMessage` 含 `Cancelled by user`)此前一律显示为「失败」,现在单独标为「取消」;筛选栏的「结果」改为「全部 / 成功 / 失败 / 取消」四选一,「失败」只查纯失败。概览提示同步拆成「失败 N 次 / 取消 N 次」,趋势图悬浮提示分别给出两类次数。
- 「我的用量」成功率口径调整:分母改为「调用数 − 取消数」(取消不计入失败,也不算成功)。历史窗口按同一口径重新计算,含取消的时段成功率会比以前显示得高一些;窗口内全部为取消时显示 `-`。

### 管理后台

- 「调用流水」页支持取消态:用户点「停止」产生的取消此前被标成「失败」并计入失败数与成功率分母,现在列表、详情弹窗与移动端卡片统一显示灰色「取消」标签,CSV 导出的结果列同步输出「取消」。结果筛选合并为「全部 / 成功 / 失败 / 取消」单选,「失败」只含纯失败。
- 分析页「趋势」「会话」两个 Tab 的质量卡片新增「取消次数」一项;失败数(`failCount` / `callFailCount`)统一剔除取消,成功率分母改为「调用数 − 取消数」。历史窗口按新口径重算,含取消的时段成功率会比以前高;`sceneStats` / `protocolStats` 同步新增 `cancelledCount` 字段,模型 / 用户 / Agent 行新增 `callCancelledCount`。
- 「数据统计」各 Tab 与成本趋势图的成本展示改为最多保留 2 位小数(如 149.627 → 149.63),不再保留 3 位;千位以上仍取 1 位、万位以上取整。「LLM 调用」页与「预算」页的单次调用成本仍保留 3 位小数,避免小额被显示成 0。

### 后端

- 调用流水接口(`GET /v1/llm-calls/me`、`GET /v1/admin/llm-calls`)新增 `cancelled` 查询参数(`true` 只查取消、`false` 排除取消,与 `success` 可叠加)与记录字段 `cancelled`(读时按 `errorMessage` 是否含 `Cancelled by user` 派生,历史行同样生效,无数据库迁移);汇总、模型 / 场景 / Agent / 趋势各维度新增 `cancelledCount`。
- 统计口径:所有 `failCount` 剔除用户取消,成功率分母改为「调用数 − 取消数」(管理后台分析页与「我的用量」一致)。`--success false` 语义收紧为纯失败。
- 修复后台子代理重试的状态残留:开新一轮重试时作废上一轮尝试的结论文档指针(`finalMessageId`)与轮数 / 工具调用 / token 统计。此前这些字段原样保留,重试执行期间进程崩溃后,恢复协调器会把上一轮(已被用户放弃)的结论当成本轮终态落库、重试一次都没执行;重试期间 `check_subagent` 也会读到上一轮的统计。
- 修复崩溃恢复复用已完成通知时跳过文件变更聚合的问题:通知去重与变更聚合现在相互独立,幂等去重保证不会复制出重复行。此前进程若在「通知已落库、变更未复制」之间崩溃,该后台子代理本轮的文件变更永久丢失,父会话的变更列表里静默缺失。
- 修复信号哨兵轮询间隔完全失效的问题:到期扫描按每行配置的 `interval_minutes` 扣减阈值(技术方案 §5.3 口径)。此前成功轮询后下一轮 30 秒扫描必然再次命中,配置 1440 分钟(每日一轮)的哨兵实际每 30 秒就打一次对方接口,`signal_watcher_check` 记录表也随之快速膨胀。
- 修复 JSON 字段哨兵提取路径命中原型链成员时静默建基线的问题:路径段改按自有属性判定,`toString` / `constructor` 等段名现在按「段不存在」计取值失败。此前这类哨兵哈希退化为空串基线,既不失败停用也永远检测不到变化,与「字段消失」无法区分;现在会计入连败,连续失败达阈值自动停用。
- 修复飞书 Bot 长连接重连退避失效的问题:连续失败计数改为跨重连常驻,按 1s / 2s / 4s … 指数退避至 `reconnectMaxMs`,超过 `maxConsecutiveFailures` 后归一收敛(与钉钉 Bot 同一行为);管理员手动点「重连」仍会归零重新开始。此前计数每次重连启动时归零,故障期间恒定 1s 重连。
- 修复会话列表把「取消中」当空闲的问题:`running` 口径与 `treeRunning`、删除 / 提升守卫、桌面端活跃相位统一含 `CANCELLING`,同一响应不再自相矛盾;仪表盘「运行中」分桶与子代理组预览排序同步修正。

### 终端 CLI(mao-cli)

- `mao-cli llm-call` 新增 `--cancelled` 筛选(`true` 只查取消、`false` 排除取消),`--success false` 改为只查纯失败;汇总与趋势返回 `cancelledCount`。

## 0.0.293 (2026-10-14)

### 前端(桌面 / Web / 安卓)
- 修复文件树目录列举失败后点「重试」没有任何反应的问题:失败时那一行只是占位的错误节点,点「重试」既不发新的列举请求、也不收起错误行,只把错误文案抹掉留下一行空白;根目录列举失败时同样点不动。现在重试会重新拉取该目录,成功后正常展开。
- 修复点文件树里列举失败的那一行会把目录当文件打开、弹出一个内容为空的页签的问题:这一行的路径其实是失败目录而非文件,此前点击会走打开文件分支。
- 修复定时任务「上次执行」状态显示为 "-" 的问题:后端会把执行状态落成「取消」「错过」「运行中」,前端汉化表漏了这几项。同时统一「执行记录」徽标与「上次执行」列的文案(排队状态此前一处显示「排队」、一处显示「排队中」)。

### 管理后台

- 会话列表新增「类型」列:边路任务(橙)与子代理(蓝)各自着色标注,悬停显示来源(父会话 ID),主会话不标标签——此前这几类会话混在列表里只能靠标题猜,无法区分。移动端卡片在非主会话时同样显示类型行;会话详情「类型」一栏同步补充,并可从父会话 ID 跳回父会话。

### 后端

- 管理端会话列表与详情接口(`GET /admin/sessions`、`GET /admin/sessions/{id}`)新增返回 `sessionType`(NORMAL/SIDE_TASK/SUBAGENT)与 `parentSessionId` 字段,供管理后台区分边路与子代理会话。
- 修复钉钉 Bot 配置了同一个 Client ID 时旧连接的 socket 一直不释放的问题:重复的一条被判重标记为失败,但它此前已经建立的 Stream 连接不会被断开,同一个 Client ID 上同时留着两条并发连接,钉钉侧消息会被重复投递。现在重复方的旧连接会一并摘掉;反过来已经持有连接的认领方也不会被误杀。
- 修复钉钉 Bot 重连退避永远是第一档(固定间隔重试)的问题:连续失败计数在每次重连启动时归零,退避时长永远算的是第一次,连接持续不可用时相当于按固定频率反复重试。现在计数跨重连保留,按 1s / 2s / 4s … 指数退避到上限;管理员手动点「重连」仍会归零重新开始。
- 修复会话活动统计里 glob_search / grep_search 两类工具活动全部落进「其他工具」的问题:工具改名后映射表还是老的 glob / list 标签,管理后台「数据统计」里看不到文件搜索与内容搜索的量。
- 工作区规则(AGENTS.md)改为**会话级冻结**:同一会话内该节文本固定不变,会话内编辑 `AGENTS.md` 只对新会话生效,压缩提交成功后也会刷新。此前同一工作区任一会话(含 Agent 自己收尾补索引表)编辑 `AGENTS.md`,就会打掉该系统提示节之后的全部前缀缓存,在跑的会话整段重算——实测一次多花约 0.87 元、首字延迟增加 5~6 秒,同工作区 100+ 会话会连带受影响,现在这类编辑不再引发塌陷。行为变化:会话内改完规则不会立刻在本会话生效,新开会话、触发一次压缩、或删除会话后才会生效;排障日志与运维手段见 `docs/guides/workspace-rules-snapshot.md`。
- 管理端会话列表关键词搜索提速:消息正文的命中判定从 `content LIKE '%kw%'` 全表扫描改为走已有的 ngram 全文索引(`ft_message_content`),单次查询从 1~2 秒降到几十毫秒。标题/摘要仍按 LIKE 匹配;单字符关键词(ngram token_size=2 索引不到,会漏召)与显式关闭全文检索时仍退回原来的子串扫描。

## 0.0.292 (2026-10-14)

### 管理后台

- 修复会话详情里用户消息「看着被截断」的问题:用户气泡的折叠判定此前按源码字符数估算(换行 > 10 或字符 > 800),与 CSS 实际把气泡压到 10 行的规则不一致——手机窄屏下一条 161 字符的消息会折成十几行被裁掉半截,判定却认为「不用折叠」而不渲染「展开全部」按钮,后半句永远看不到。现改为按实际渲染行数测量:挂载与尺寸变化(转屏、改宽)后量一次真实高度,10 行内放得下就完全展开、不显示按钮,超出才折叠并提供展开入口。宽屏放得下、窄屏放不下的同一条消息现在各按各的宽度判定。
- 「数据统计 · 总览」指标条的计数卡片(新增会话、消息数、活跃用户)及「会话」Tab 环形图中心的会话总数,万 / 亿紧凑展示改为固定保留两位小数(如 1.7 万 → 1.70 万),不再被四舍五入成一位小数;图表坐标轴与条形排行标签仍为一位小数。
- 「数据统计 · 总览」指标条新增「缓存命中率」卡片:窗口内命中缓存的 Token 占入 Token 的比例(llm_call 口径,默认不含连通性自检),卡片副行标注「命中 / 入」两个 Token 量;窗口内没有调用时显示 `-` 而不是 0%,环比按百分点差展示(30% → 35% 是 ↑ 5pp,不是 ↑ 17%)。


## 0.0.291 (2026-10-13)

### 前端(桌面 / Web / 安卓)

- 修复在手机上用完回到电脑客户端时,频繁弹出「偏好设置已在其他端被修改,请刷新后重试」红色报错的问题:手机端与电脑端共用同一份左侧任务面板偏好(分组顺序 / 折叠状态 / 分组别名),电脑端回到前台时会自动保存一次,若期间手机端改过就会被乐观锁拒绝并返回 3032。前端本来就会自动重取服务端版本合并后重试成功,但响应拦截器仍会对这个「可自愈」的冲突弹红色错误条,用户以为出了故障、实际数据已同步。现在该请求显式跳过 3032 的默认提示,只有重试耗尽才给出明确失败提示。
- 修复手机端改动的任务面板折叠状态与分组顺序被电脑端改回去的问题:冲突合并此前只合并了分组别名,分组顺序和折叠状态仍按电脑端的旧快照整份写回,会静默覆盖手机上刚拖好的顺序和折叠结果。现在三份偏好都以服务端为基线合并——本地独有的分组 key 追加到末尾,折叠状态取并集,别端的改动不再被回退。
- 修复点「停止」后半段回复在另一种竞态下仍会丢失的问题:看到半段回复后重发同一问题、或紧接着的下一轮刚好已落库时,终态刷新会把本地保留的中断气泡误判成「下一轮已落库回复的本地副本」而删掉——被中止的这一轮服务端本就不落库,删掉后刷新也无法恢复;纯工具调用轮次(落库内容为空)更容易触发。现在重复回复的去重严格限定在同一轮内,只与该轮自己的回复比较。
- 修复编辑历史消息重发后,同一轮回复被劈成「空气泡 + 新气泡」两条的问题:此前重发用的占位气泡没有登记进流式气泡表,首包增量认不出它是本轮气泡又新建了一条,流式期间对话区多出一条空白回复、回合步骤计数也多加 1 步。现在与普通发送路径同口径登记。

### 后端

- 修复侧边栏会话分组「展开更多」后条数对不上的问题:嵌入 SDK(source=embed)的会话按设计只按 Agent 独立分组,但 LOCAL / CLOUD 工作区分组的过滤条件漏了排除 embed——同一条会话在分组预览里出现一次、展开后却多出一条,分组总数对不上、分页偏移错乱。现在三个「模式 + 工作区」桶与临时工作区桶口径一致。
- 修复后台 shell 命令(async 执行 / await_async 领结果)的结果摘要丢字段的问题:摘要此前按白名单重建,构建输出被裁掉尾段时模型看不到 truncated 标识、也拿不到完整日志的 output_file 指针,bash 已退出时模型还会继续 await 一个死会话。现在与同步路径同构透传;异步执行结束后的当前工作目录也随结果刷新(此前 cd 之后仍报旧目录)。
- 修复 read_file 的失败(路径写错、目标是目录、伪造图片格式、被沙箱拦截等)在界面上渲染成绿色成功卡的问题:错误只写进了 content、没带 error 键,下游一律判成成功。现在与 write_file / glob / grep 一致带 error 键判失败。
- 修复 grep_search 的 ignore_case 传字符串 "true" 不生效的问题:模型偶发把布尔参数输出成字符串,此前静默按大小写敏感搜索,0 命中会让模型误判「代码里不存在」。现在与 edit_file 的 replace_all 同口径接受字符串布尔。
- 修复微信 Bot 扫码状态查询超时被当成硬错误、扫码绑定流程直接中断的问题:ilink 响应超过 10 秒时本应返回等待让前端继续轮询等用户扫码,实际抛出「查询扫码状态失败」,管理后台连续报错 5 次后停止轮询,用户必须刷新二维码重来。现在超时正确返回等待,连接被拒等真实网络错误仍上抛。另修复微信会话创建锁的表项按 userId 无界泄漏(每个接入用户漏一个条目)。

### 终端 CLI(mao-agent)

- 修复本地文件工具数值参数的非法值处理与云端不一致的问题:read_file 的 limit 传 "abc"/"" 时返回空内容(模型会误判文件为空,进而在 write_file 里整体覆写清空它),现在回落为读全文件;glob 的 head_limit 传 0 / 负数时被静默改成 100、或返回「全部结果砍掉末尾几条」的错乱子集,现在显式报错;grep 的 max_output_chars 传 0 被静默改成 10000,现在按 0 预算执行。
- 修复本地 read_file 失败被后端判成成功的问题:路径不存在、目标是目录、二进制文件等失败返回只写了 content、没带 error 键,后端按「结果顶层含 error 键」判定成败时一律算成功——终端里失败渲染成成功卡,调用统计也计成功。现在与云端 read_file 及 write_file / glob / grep 同口径带 error 键。

## 0.0.290 (2026-10-13)

### 前端(桌面 / Web / 安卓)

- 修复新建会话发出首条消息后,右侧任务信息区的「执行中」徽标偶现不显示(只剩一个空徽标)的问题:切换进新会话时会拉一次会话详情,而这个详情请求常比 Agent 真正开跑更早发出、响应更晚到达——详情里带回的还是启动前的「空闲」相位,把已经实时显示为「执行中」的徽标又打回空态,直到下一次状态变化才恢复。服务端越忙(同时有多个任务在跑)越容易触发。现在徽标与任务列表同源,只认更新的实时执行状态,迟到的旧快照不再回退;边路任务 / 子代理的状态徽标、以及执行中手动刷新任务列表,也同样不再被旧快照打回空闲。

## 0.0.289 (2026-10-13)

### 后端

- 飞书 / 钉钉 / 微信入站图片的「图片已保存到会话工作区」提示改为工作区相对路径(如 `chat-files/2026-10-13/feishu-image-om_xxx.jpg`):此前注入的是 `/opt/mao-data/workspace/feishu-chat/...` 一长串绝对路径,冗长且没有额外信息。读文件、glob/grep、图片编辑等工具与 Shell 均按会话工作区解析相对路径,Agent 二次读取原图的行为不变;落盘位置本身不变,独立文件消息的 `@{路径}@` 引用仍为绝对路径。

## 0.0.288 (2026-10-13)

### 前端(桌面 / Web / 安卓)

- 助手回复操作栏的四个图标按钮统一为 24px 见方圆钮:此前「复制」「点踩」是带水平内边距的胶囊钮(单个约 34px 宽),「Fork 到边路任务」「分享会话」是 24px 方钮,同一行里宽窄不一。现在四钮同形同度量、悬停表现一致;「已复制」「已点踩」两种带文字的反馈态仍会临时放宽成胶囊显示文案,不影响常驻宽度。

## 0.0.287 (2026-10-13)

### 前端(桌面 / Web / 安卓)

- 修复回复引用溯源的一处显示问题:模型偶尔会按标准 Markdown 脚注的习惯,在回复末尾写 `[^n^]: 链接` 定义行。此前桌面端会把定义行里的标记转成上标、后面跟着 `: 链接` 残句;飞书 / 钉钉 / 微信渠道卡片剥掉标记后则只剩 `: 链接` 行。现在桌面渲染在解析前整行删除这类定义行,复制消息同样不再带出;正文里「说法[^1^]:……」的句内写法不受影响。

### 后端

- 引用规范提示词补充显式约束:`[^n^]` 只有句末插标记这一种用法——回复里不得出现 URL、`[^n^]: 链接` 脚注定义行,也不得自行在文末罗列「来源 / 参考资料」列表(来源列表由系统按标记自动生成,自己写只会重复);web_search 与 open_web_page 两个工具的描述同步补充该约束,从源头减少模型混用脚注语法。

## 0.0.286 (2026-10-12)

### 前端(桌面 / Web / 安卓)

- 助手回复操作栏新增「分享会话」图标(Fork 按钮旁),点开与任务列表右键「分享…」完全相同的弹窗:查看 / 复制链接、刷新快照、撤销,匿名链接开关也一致。主会话与边路任务窗口都可用;未创建会话(新建会话中心态、边路占位 Tab)不显示入口,只读分享页自身也不再出现该图标。
- 移动端「文件管理」图标视图适配:图标与网格单元格缩小到紧凑密度(34px → 22px、110px → 84px 最小列宽、内外间距收紧),窄屏一屏能多看两三行条目;底部操作按钮改为单行横向滚动,不再换行占掉两三行高度;图标视图的时间 / 大小行加了省略号,长条目不再撑破格子。桌面端密度不变。
- 侧边栏文件树工具栏精简:移除「上传文件」「上传文件夹」两个按钮(上传仍可从右键菜单「上传到此处」或把文件拖到目录上进行),入口文案「文件管理」改为「管理」。

### 后端

- 修复每日运营摘要从未推送的问题:调度查询的用户表条件被拼成无效列名 `u.u.deleted`,每分钟 tick 都直接失败,摘要不再生成。已改为正确的单层别名条件,并补了会被这类拼写错误立即打断的断言。

---

## 0.0.285 (2026-10-12)

### 后端

- 修复连续多天使用的会话在跨天后首次提问时代理「记错日期」且提示词缓存整段失效的问题:系统提示里的当前日期原先取服务器当天,日期一变整段系统提示随之变化,历史最长的这一轮要从系统提示起全量重算。现在该日期锚定为本会话首条用户消息的日期、会话内固定不变,真实日期推进改由用户消息前新增的 `<date-change>` 提示标签承载(仅出现在发给模型的内容里,不落库、不影响界面显示、导出与检索),同日对话内容与之前逐字节一致,跨天只在新消息上新增标签。附带修复含快捷命令的用户消息在展开时丢失内部字段、导致跨天提示可能漏发的问题。
- Anthropic 协议模型启用前缀缓存:请求默认带上顶层自动缓存断点(`cache_control: {"type":"ephemeral"}`),断点随对话增长自动前移,长会话的命中率不再依赖网关是否自行注入;新增运行参数 `harness.llm.anthropicAutoCache`(开关,默认启用)与 `harness.llm.anthropicCacheTtl`(`5m` / `1h`,默认 `5m`),改后重启后端生效。网关不认该字段时首个 400 之后自动停止发送,不再影响后续请求。
- 默认(chat-completions)协议请求现在携带会话级 `prompt_cache_key`,多上游网关下同会话的前缀缓存不再靠概率命中;网关因该字段报 400 时自动去掉字段重试,并按模型记住不再发送(一次失败请求的成本,不影响限流重试额度)。
- 提示词前缀变化可归因:每轮请求会计算「系统提示逐节 + 工具定义」指纹并与上一轮比对,变化时日志输出 `prefix changed` 并点名是哪几节变了(如 `changedSections=[memories,skills]`),缓存回归不再靠猜。
- 修复一条记忆被更新就整体打掉会话前缀缓存的问题:长期记忆注入仍取最新 8 / 12 条,但输出顺序稳定为按 id 升序,无关的记忆变动不再重排整个记忆块。
- 修复云端 MCP 工具集每轮重连、某服务器连不上就整体丢失的问题:工具集改为会话快照(与本地模式一致),单个服务器连接失败时沿用上次成功快照的工具并在尾部提示该服务器当前不可用、相关调用可能失败;仅服务端配置变化或新会话才改变工具集。
- 新增前缀稳定性回归测试:同一上下文连续两次构建请求、追加消息后再构建,均要求字节级前缀与工具定义不变;任何人把易变内容塞进系统提示或让工具顺序抖动,测试立即失败。

---

### 后端

- 修复 shell 会话可被跨会话操作的问题:工具结果里暴露的 `session_id` 此前能直接复用到其他会话(含其他用户会话)的 shell 上,命令会以原会话的工作目录与令牌环境执行;现在会话归属校验不通过时会话不复用、读写与关闭同样拒绝,模型只会拿到属于自己的 shell。
- 修复 shell 工具指定 `workdir` 时目录切换失败被静默吞掉的问题:此前 `cd` 失败后命令仍会在原工作区根目录执行且无任何失败提示,现在切换超时或失败会直接返回错误,命令不再放行。
- 修复信号哨兵 JSON 取值失败时一次轮询记录两条 check 的问题:失败历史与总数不再翻倍,列表不再出现同一次失败的重复行。
- 修复信号哨兵基线被误清、真实变化被吞的问题:此前编辑哨兵时回传 `get`(小写方法)、带首尾空格的 jsonPath、或仅主机大小写不同的 URL 都会被判定为「配置变化」而清掉基线,从上一次成功轮询到编辑之间的真实变化不再触发告警;现在按归一化后的值与库内值比较,仅配置真正变化才重置基线。
- 修复手动压缩被取消时会话用量少记一轮的问题:取消「压缩」这个附带动作不再回滚本轮已完成并落库的用量,客户端收到的增量与会话统计、数据库记录一致。
- 修复管理后台深翻页时同一行重复出现、另一行不出现的问题:审计日志、用户、会话、个人指令的分页排序补上唯一排序键,管理员连续操作产生的同秒记录翻页后不再乱序。

### 终端 CLI(mao-agent)

- 修复打印模式(`-p`)断线重连后永久挂起的问题:任务在断线窗口内已跑完时,重连后服务端回传的终态快照此前无法结束本轮等待,命令永远不返回;现在终态快照会正常结算,交互模式下该轮也不再卡在「执行中」。
- 修复 `permissionLevel` / `outputFormat` 配置永久失效的问题:此前即使不传对应命令行参数,内置默认值也会挤掉用户级与项目级配置文件,`~/.mao/agent-cli/config.json` 里的设置从不生效;现在恢复「命令行 > 环境变量 > 项目配置 > 用户配置 > 内置默认」的优先级。

### 前端(桌面 / Web / 安卓)

- 修复边路任务首发时流式回复被劈成两个气泡的问题:新建非 fork 模式的边路任务发送首条消息,气泡上方不再多出一个空白 assistant 气泡,折叠头的步骤数不再虚增。
- 修复文本流式中途点「停止」后已流出的部分回复整体丢失的问题:被中止的轮次服务端不落库,此前终态重拉会把这条唯一副本静默删掉、刷新后依然没有;现在停止时已输出的内容会保留在气泡中(正常完成后仍按服务端结果覆盖)。

## 0.0.283 (2026-10-12)

### 前端(桌面 / Web / 安卓)

- 统一修复自定义按钮文案贴左显示的问题:全局按钮样式 flex 化后浏览器原生的文字居中失效,被拉宽的按钮(「上下文详情」的「立即整理 / 上次摘要」、提问面板操作按钮等)文案会贴在左边缘,现全局恢复水平居中,与原生按钮的默认表现一致;目录选择行、多行选项按钮等按设计左对齐的按钮保持原样。

## 0.0.282 (2026-10-12)

### 前端(桌面 / Web / 安卓)

- 新增「回复引用溯源」:模型用联网搜索 / 打开网页拿到外部信息作答时,回复正文的说法旁边出现可点击的引用上标(如 [1]),回复末尾新增「来源 N」区列出本轮引用的网页标题与域名。点上标跳到对应来源,点来源条目在新标签页打开原网页;来源 ≥4 条时默认折叠,点「展开 / 收起」切换。回复没有引用时界面与之前完全一致;标记无法对应到真实来源(模型编造或编号越界)时自动剥掉、不会显示成无效上标。分享、导出 JSONL 等场景的回复正文同样不再出现 `[^n^]` 原始标记。

### 后端

- 新增回复引用溯源能力:任务完成时按系统提示词的引用规范,从本轮联网搜索 / 打开网页的工具结果构建来源白名单(仅 http(s) 链接、最多 20 条、按出现顺序编号),校验模型回复里的 `[^n^]` 标记与白名单一致后写入最终回复的 `metadata.citations`(复用既有 JSON 列,无数据库迁移)。标记与白名单不符时按无引用处理。飞书、钉钉、微信渠道下发的回复正文(含实时会话、崩溃恢复续跑后的终态卡片、定时任务结果推送)同样剥离引用标记,渠道消息不再出现原始记号。

## 0.0.281 (2026-10-12)

### 前端(桌面 / Web / 安卓)

- 新建会话时,选定工作区后右侧边栏立即出现「文件」页:不必等首条消息发出,即可提前浏览目录树、双击在中央页签打开文件、通过右键「添加到聊天」把文件引用插入输入框。本地模式(Electron)直接读所选本地目录;云端模式按已选项目提供只读浏览。切换工作区 / 项目时文件树立即跟随切换;首条消息发出、会话创建后,文件树无缝切换为会话视角,内容不变。写入、下载等依赖会话的操作仍在会话创建后可用。
- 新增「文件管理」页签:对话页中央 Tab 区(右侧文件树工具栏的文件夹图标进入)以整幅空间管理 CLOUD 工作区文件,不再局限于侧边栏的窄栏列表。图标 / 列表双视图,可按名称、大小、修改时间排序(目录恒在前);上传文件 / 文件夹(按钮挑选,或拖拽到窗口空白处与目标目录)、新建文件 / 文件夹、重命名、删除(物理删除,确认框列出全部路径并提示目录将连同内容一并删除)、剪切 / 复制 / 粘贴(与文件树共用剪贴板,不能跨会话)、拖拽移动、下载(多选批量下载文件、目录打包 zip)、复制绝对路径、添加到聊天、右键「在文件树中显示」回跳并展开选中。面包屑 + 前进 / 后退在各目录间导航;搜索框按文件名递归整个工作区(最多 100 条),点击结果跳到所在目录并选中。仅 CLOUD 模式可用,LOCAL 侧边栏行为不变。
- 「设置 → 消息通知」新增「每日运营摘要」卡片:总开关、推送时间(默认 09:30)、「同时推送到出站订阅」(无订阅时禁用并引导至开放接口页)、「上一份摘要」三态(待生成 / 已发送 / 无事件未发送)与「立即发送昨日摘要」试发按钮;各控件变更即保存。
- 收件箱支持「每日摘要」条目:点击打开详情对话框,六个板块分区展示(空板块不渲染),失败任务与审批项可一键打开关联会话,成本板块跳转「我的用量」,定时任务 / 哨兵板块跳转对应设置页。

### 后端

- 飞书新会话(`---` 私聊新会话、话题群首条消息建的会话)标题改为 LLM 生成短标题:与桌面端同一套标题模型与提示词(约 15 字内、跟随消息语言、剔除寒暄),模型取系统设置「会话 → 生成会话标题使用的模型」,留空用默认模型;生成失败或超时(30s)降级本地启发式截断,不再出现「首条消息前 20 字」的硬截断标题。生成异步执行、不阻塞 Agent 执行,仅对待命名的新会话触发(已命名会话的后续消息不再调用 LLM),标题更新同时推 WS 让已打开该会话的桌面 / Web / 安卓客户端实时刷新。
- 新增「每日运营摘要」:每天在用户设定时间(默认 09:30,Asia/Shanghai 口径)自动汇总前一天的运营情况——任务成败与失败原因 Top3、定时任务执行与自动暂停、信号哨兵触发与自动停用、审批待办挂起数、Token 成本 Top3(未配价调用单独计数、不计入合计)、开放接口调用与拒绝数——生成后写入站内收件箱。六项全无数据的「安静日」不写消息;按天一次性生成,重启不重复,停机跨天会在恢复后补发前一日摘要。出站订阅新增事件 `digest.daily`,payload 同时携带结构化数据与 Markdown 全文,HMAC 签名与退避重试沿用既有投递管道。新增用户偏好表 `user_digest_preference`(V150:总开关、推送时间、是否同步出站、已处理日期)。
- 新增一组按项目(projectKey)读取云端工作区的只读接口:列目录 / 读文件 / 按名搜索 / PDF 预览。新建会话尚未落库时,前端凭已选项目即可浏览该工作区;项目目录尚不存在时返回空列表而非报错。权限口径与既有 project-list 一致——按用户隔离,仅能访问本人名下的项目。
- `files/workspace-directory` 目录项新增可选 `mtimeMs`(epoch 毫秒),供前端按修改时间排序;缺省时前端按「未知时间」处理,搜索接口不返回该字段。
- 修复数据库迁移版本号撞车:负反馈闭环与信号哨兵两个功能分支各自编了 `V148`,而迁移执行器按版本号去重、同版本只执行先扫到的那一个——全新部署时负反馈的 `status` 处理列与信号哨兵两张表必缺一个,在已应用过 `V148` 的环境里另一个则被静默跳过、不报错。哨兵迁移已重编为 `V151`(脚本幂等,已执行过的环境重跑无副作用);同时给执行器补了重复版本号硬失败,防止同类问题再次发生。

## 0.0.280 (2026-10-12)

### 前端(桌面 / Web / 安卓)

- 修复一批按钮(渠道绑定的「解绑」「绑定钉钉账号」、信号哨兵弹窗的「取消 / 保存」等)文字不垂直居中、整体偏低的问题:根因是裸 `<button>` 在固定 24px 高度下,文案行盒(继承正文 1.47 行高)高过按钮内容区,Chromium 不再居中而是自上而下排布。现统一在全局按钮样式里声明 `inline-flex` 垂直居中,漏写该声明的按钮一并生效。

## 0.0.279 (2026-10-12)

### 前端(桌面 / Web / 安卓)

- 「设置 → 开放接口」新增「接口文档」卡片:弹窗内查阅开放接口的完整调用说明(鉴权方式、端点与请求/响应字段、HMAC 签名算法、错误码与排障),并支持下载 Markdown(`mao-open-api.md`)或一键复制,直接交给外部系统接入。文档随桌面端版本内置,不对外暴露 Swagger。
- 修正「整理上下文」分隔线的位置:它原先插在每轮最终回复与这轮的文件变更面板之间,把一条回复和它自己的变更割成两半,看起来像分隔线属于下面那块面板。现在分隔线排在本轮回复与文件变更之后,作为轮次边界更符合直觉。
- 修复「我的用量」里负反馈转经验的模型调用显示英文枚举 `feedback_experience_draft`、且场景筛选取不到的问题:desktop 与 admin 的场景清单补齐该值(0.0.277 新增该场景时两端漏改),明细行、总览饼图、运行轨迹标签与导出统一显示中文「负反馈转经验」。

## 0.0.278 (2026-10-11)

### 后端

- 新增「信号哨兵」:按固定间隔探测外部 HTTP(S) 接口,判定值变化时自动新建一次 Agent 会话,适合盯汇率、库存、发布状态、接口健康度等「有变化才需要处理」的信号。判定模式三种:内容哈希 / JSON 字段(点分路径 + 数组下标)/ HTTP 状态码;提示词模板支持 `{name}` `{url}` `{diff}` 占位,变化时把新旧值摘要一起交给 Agent。
- 哨兵能力在 `settings/signal-watchers` 页自助管理(新建 / 编辑 / 启停 / 删除 / 立即测试 / 轮询记录),管理后台 `signal:read` 权限提供只读清单。单用户最多 20 个;间隔 1~1440 分钟;请求头与请求体整体加密存储,列表与详情回显均打码;连续失败 5 次自动停用并发站内信(`WATCHER_DISABLED`);同一哨兵每小时触发超过 4 次时照常触发、仅在轮询记录上标记 `TRIGGER_RATE_LIMITED`;失败后按 `间隔 × min(2^失败次数, 32)` 分钟退避。新增环境变量 `APP_SIGNAL_SECRET`(哨兵密钥加密密钥,缺失时哨兵功能不可用)与设置项 `signal.checkRetentionDays`(轮询记录保留天数,默认 90)。

### 前端(桌面 / Web / 安卓)

- 设置页新增「信号哨兵」页:哨兵卡片列表(状态、间隔、判定模式、上次变化 / 轮询时间、连续失败数)+ 新建 / 编辑对话框 + 启停 + 删除二次确认 + 轮询记录抽屉(含触发会话跳转)与「立即测试」探针预览。站内收件箱新增「哨兵已停用」类型。
- 修复信号哨兵的两个编辑问题:编辑时只新增一个请求头,会把已保存的敏感请求头(如 `Authorization`)静默丢掉;把请求头行全部删掉时无法真正清空(改为显式清空)。

### 管理后台

- 「运行」分组新增「信号哨兵」只读清单(名称 / 属主 / 探测地址 / 方法 / 间隔 / 判定模式 / 状态 / 连续失败 / 上次轮询与变化 / 创建时间),支持按状态、属主、判定模式筛选,需 `signal:read` 权限。

## 0.0.277 (2026-10-11)

### 管理后台

- 点踩反馈页(「点踩反馈」)从只读统计台升级为可闭环处理的工作台:新增「待处理」汇总数与「Agent × 原因」待处理矩阵,点击矩阵单元格即可下钻到该 Agent、该原因的待处理反馈;明细列表新增状态筛选(待处理 / 已转经验 / 已忽略,默认待处理)与 Agent 筛选,Agent 列带「同类 N 条」聚集徽标(同 Agent 同原因的待处理条数),支持多选。
- 新增「转为经验」向导:选中一条或多条点踩反馈后打开抽屉,左列看反馈原文,右列选目标 Agent(默认预选该反馈所属 Agent)、编辑由模型起草的经验草稿(可增删,最多 2 条,每条限 300 字,可点「重新生成」再起草一次),确认后写入该 Agent 的经验并进入它的 system prompt;单条反馈基于自身轨迹提炼,多条则归纳成更通用的经验。写入后该反馈标记为「已转经验」并记录经验 ID。
- 新增「忽略」操作(行内与批量,均有二次确认):忽略后反馈进入终态、不可恢复,用于处理无需沉淀经验的点踩。已转经验 / 已忽略的行操作按钮禁用,避免误触。
- 「系统设置 → Agent 与模型」分组新增「负反馈转经验的起草模型」选项:可指定起草经验草稿用的模型,留空则使用默认模型。

### 后端

- 点踩反馈新增处理态:`message_feedback` 表加 `status`(PENDING 待处理 / CONVERTED 已转经验 / DISMISSED 已忽略,后两者为终态)、`handled_by`、`handled_at`、`experience_id` 四列,存量反馈按待处理处理;新增 `feedback:write` 权限码(默认授系统管理员),转经验、忽略、起草三个动作走该权限,汇总与明细仍走 `feedback:read`。
- 新增三个管理端点:`POST /v1/feedback/admin/draft`(按反馈 ID 装配原料并让模型起草经验草稿,可反复调用)、`POST /v1/feedback/admin/convert`(确认写入,经验与反馈状态同事务,并发下只有一个管理员能写入,其余提示「该反馈已被处理,请刷新后重试」)、`POST /v1/feedback/admin/dismiss`(批量忽略,全或无);`GET /v1/feedback/admin/list` 与 `/summary` 扩展支持状态、 Agent 筛选与待处理统计。
- 起草原料来自该点踩消息所在轮次的运行轨迹(新增按消息 ID 精确定位 run 的能力,不再依赖翻页位置):包含点踩原因、用户原话、被点踩的回复与工具调用序列(仅工具名 / 目标 / 状态 / 耗时,不含工具结果正文,命中敏感路径规则的目标替换为 `[sensitive]`),产出 1~2 条不超过 300 字的中文经验。模型调用计入「负反馈转经验」场景的用量统计。

## 0.0.276 (2026-10-11)

### 后端
- 修掉管理后台编辑 Agent 时删掉最后一个 MCP 服务器后保存无效的问题:`PUT /v1/agents/:id` 会把请求里的空数组当成“没传这个字段”而跳过更新,于是重新打开编辑页时被删的 MCP 服务器又回来了。现在提交空数组会正确清空该 Agent 的 MCP 关联,不传该字段时仍保持原值不变。
- 修好一次性定时任务「等待重试期间被重新启用」会重复执行的问题:任务执行失败并排下次重试时任务行进入待重试态,此时从「已暂停」重新启用(管理后台 / 桌面的启停开关只切换状态、不改表达式)会按当前表达式重开下一档,重试到期前「到期档期」和「待重试」两个扫描入口都会命中同一个任务,同一条日报被推两遍。现在待重试期间任何保存路径(原样保存表达式、只切状态、终端工具只回传状态)都不再重开档期,由重试入口独占驱动;显式关掉「一次性」或本来没有待重试时排期照常。
- 修好微信入站消息的两个时间窗口误判:`shouldSkipDuplicate`(24 小时去重)与 `findLatestInboundAt`(24 小时主动发送窗口)把数据库返回的上海墙钟时间按 UTC 解析,整体前移 8 小时——30 小时前已处理完的消息被算成 22 小时,用户合法重发同样内容被当成服务端重投悄悄跳过;26 小时前活跃过的账号被算成 18 小时,主动发送窗口误判为仍打开,超窗发送必然被拒。现在统一按 `+08:00` 解析,与调度、登录等模块同一口径。
- 修好统计口径在每天最后一秒丢数据的问题:`message` 与 `llm_call` 的 `created_at` 已是毫秒精度,而用量趋势、管理后台调用流水、我的用量总览等聚合路径用「当天 23:59:59」作为闭区间上界,字典序上 `23:59:59.000 > 23:59:59`,导致每天最后一秒内的会话、消息与模型调用恒不被计入。现在这些窗口统一改为半开区间 `[当日 00:00:00, 次日 00:00:00)`。
- 修好停用「默认模型」会连带取消默认指定的问题:管理后台模型列表的启停开关只切换启用状态,却顺带清掉了 `is_default` 标记,随后该模型可以绕过删除守卫被删掉,且会话绑定的模型被静默置空、之后再发起对话直接报模型不存在。现在停用只写启用状态,取消默认仍需在编辑弹窗里显式取消;「不能停用唯一启用模型」的守卫也改为只统计文本模型,与管理后台可选的默认模型口径一致。

### 前端(桌面 / Web / 安卓)

- 修好站内收件箱少显示一类提醒的问题:「微信回复窗口已关闭」提醒写入后在桌面端不显示,顶栏铃铛未读数比列表多 1 且该条永远点不到、无法标已读。现在这一类型正常出现在收件箱列表里。
- 修好「我的用量」里「代理审批」「记忆抽取」两类调用显示英文枚举、且无法按场景筛选的问题:场景下拉和场景标签补齐这两项(与后端实际产出、管理后台展示一致)。

## 0.0.275 (2026-10-11)

### 管理后台

- 用户管理列表的用户名列和用户详情抽屉都展示用户头像(`avatarUrl`):有头像时按当前站点解析 `/uploads/` 相对路径显示图片,没有头像时回落显示名称或用户名的首字符;移动端卡片头部同样带头像。

## 0.0.274 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 顶栏入口按「常用留图标、次要进菜单」重新收敛:站内信、设置两个图标从顶栏移除,改到头像菜单里(连同原有的「个人信息」);切换主题色的图标也一并移除,浅色 / 深色 / 跟随系统三档挪到「设置 → 个人信息」页顶部。顶栏因此少了三个图标,右侧只留下面板开关、搜索、我的用量、我的指令、终端、刷新和头像。
- 顶栏新增「我的指令」入口(书本图标),点击后以抽屉形式打开指令管理界面,与「设置 → 我的指令」页是同一套内容(我的指令 / 系统指令双 Tab,可新建、编辑、复制、删除);设置页的「我的指令」Tab 页保留不变。头像菜单里也加了同一个「我的指令」入口。
- 站内收件箱不再显示未读红点徽标:顶栏与头像菜单入口都只保留图标和文字,未读状态只在抽屉内的条目标识体现。抽屉入口从头像菜单的「站内收件箱」进入,功能(只看未读、全部已读、加载更多、点击跳转关联会话)不变。

## 0.0.273 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 修好「我的用量」抽屉明细 Tab 在手机上分页条错位的问题:明细条数一多(如 2141 页),分页条上「总数、每页条数、上一页、页码、下一页」连排有 451px 宽,而手机上的抽屉内容区只有 321px,右对齐时「共 21410 条」整段被推到内容区左侧之外,每页条数选择器也跟着跑出屏幕——既看不到总数,也点不到条数切换器。现在手机上这行单独给出总数,分页条只留「条数、上一页、页码、下一页」,超宽时在容器内横向滑动而不是把内容推出屏幕。
- 「我的用量」抽屉的统计周期新增「上周」「上月」和「自定义」三档。上周是上一个完整自然周(周一至周日),上月是上一个完整自然月;「自定义」选中后会出现日期区间选择器,起始时间和结束时间都由自己选,默认最近 7 天,趋势图和总览卡片随之刷新。后端 `GET /v1/llm-calls/me/summary` 与 `/trends` 的 `range` 新增 `lastWeek` / `lastMonth` / `custom`,自定义区间用 `startDate` / `endDate`(`YYYY-MM-DD`)下发,只在该周期生效;起止日缺了或写错时回落默认的近 30 天。

### 后端

- 用户端统计周期(`GET /v1/llm-calls/me/summary`、`/v1/llm-calls/me/trends`)的 `range` 参数新增 `lastWeek`、`lastMonth`、`custom` 三个取值。`lastWeek` 为上一个完整自然周(周一为一周起点),`lastMonth` 为上一个完整自然月;`custom` 配合新增的 `startDate` / `endDate` 查询参数(`YYYY-MM-DD`,闭区间含首尾全天,时区口径同为 Asia/Shanghai 日历日)使用,两个日期只在 `range=custom` 时被读取。起止日缺失、格式非法或起晚于止时统一回落默认周期近 30 天,不会把窗口算成「不限」。

## 0.0.272 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 设置页把「微信Bot」「飞书机器人」「钉钉Bot」三个入口合并成一个「渠道绑定」页:三个渠道的绑定状态、绑定/解绑、授权弹窗改到同一页的「微信Bot / 飞书机器人 / 钉钉Bot」三个 Tab 里,侧边栏只留「渠道绑定」一项。三个 Tab 都是懒加载,只有点开的那个会拉自己的绑定状态。钉钉授权回调仍回到这一页并自动选中「钉钉Bot」Tab,提示照旧;旧地址 `/settings/weixin-bot`、`/settings/feishu-bot`、`/settings/dingtalk-bot` 不再有效。

## 0.0.271 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 「我的用量」抽屉的明细 Tab 现在改了筛选条件或每页条数就回到第 1 页。此前停在第 3 页再改条件时,请求仍按旧页码发出,结果落在筛选后的数据范围之外,只能看到一张空表,必须手动翻回第 1 页才有数据。同时每页条数补上 100 一档,并带出条数切换器(此前只有 10/20/50 且没有切换入口)。
- 删掉设置页里的「使用记录」页面和侧边栏入口:它和抽屉明细 Tab 调的是同一个接口 `GET /v1/llm-calls/me`,列表内容、筛选条件、表格列完全相同,只是容器不同,留着两处入口反而要分别维护。明细 Tab 现在就是查看自己调用流量的唯一入口(原来页面底部那条「查看完整使用记录」的跳转链接也一并去掉)。接口 `/v1/llm-calls/me` 本身不变,`mao llm-call me` 命令不受影响。
- 修好「我的用量」抽屉在手机上的三个问题。一是右上角关闭按钮不见了:抽屉头部用了自定义插槽,而 Element Plus 的样式让插槽内容占满头部整行宽度,把关闭按钮整个挤到屏幕外(393px 宽的手机上按钮被推到 439px 处),只能靠系统返回键退出;现在插槽内容不再抢占按钮位置。二是统计周期那一行被挤得不成样子:「统计周期」四个字被压成 11px 宽的竖排细条,6 个周期选项加标签加刷新按钮在 360px 里排不下,整体溢出屏幕;现在手机上这一行单独占满一行,6 个周期选项完整显示不再被裁。三是饼图下方的图例文字挤成一团:模型名和占比之间只隔一个半角空格,11px 字号下几乎看不出距离,「step-5 (plan2) 98%」和下一项糊在一起;现在改用全角空格分隔,并放宽了图例项之间的间距。
- 接着修掉手机上抽屉头部与 Tab 之间那一大块空白:周期那一行实际被 flex 挤成了三行(刷新按钮掉到第三行),头部被撑到 132px 高,加上 Element Plus 默认的 32px 下边距,Tab 上方空出一大截。现在刷新按钮移到标题行右侧,周期控件单独一行,下边距收紧到 12px,头部降到 76px;窄屏下「统计周期」文字标签隐藏(分段控件本身就说明了用途),省下的宽度让 6 个周期选项完整显示、不再被截成「近 ...」。同时修正一处静默失效的样式:抽屉内部结构的覆盖原本写在组件的 scoped 样式里,但 `class="usage-drawer"` 是透传给 Element Plus 内部节点的,scoped 的 `data-v-*` 属性不会跟着过去,导致那些规则一条都没生效(实测 `matches=false`);现已移到全局 `style.css`,与既有的 `.management-dialog .el-dialog__body` 同一写法。

## 0.0.270 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 修复桌面端「我的用量」抽屉的统计周期点不动:「今日」完全没反应,「昨日」要左右挪鼠标才能点上。抽屉没有传送到 body,而是就地渲染在顶栏里,而顶栏是 Electron 的窗口拖拽区,只有右侧图标栏那一小块是非拖拽的;统计周期从 3 个选项扩到 6 个后分段控件变宽,左端越过了非拖拽区边界,落在里面的点击被系统当成拖动窗口吞掉。现在「我的用量」与站内收件箱抽屉都传送到 body,整个抽屉头部都在可点击区域内。

## 0.0.269 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 客户端所有按钮统一为同一套小尺寸(24px 高、12px 字):设置页、会话消息操作栏、任务面板、终端按键条、文件与 PDF 查看器、技能与指令管理、飞书/钉钉/微信机器人页等不再有大有小。上一版只统一了 Element Plus 的按钮,但项目自己的按钮类并不是 el-button——「签发 Token」「新建触发器」这类 create-btn、各弹窗的 dialog-btn、消息下方的复制/点踩/fork、任务面板的刷新与分组加号、终端的接管与按键条按钮等仍停留在各自写死的 26/28/30/32px 与 13px/14px 字,全局规则管不到它们。现在这些按钮全部改用统一的度量令牌定义,和 EP small 完全一致;分享弹窗的确认/取消按钮也删掉旧的放大覆盖,回到统一尺寸。
- 以下几类按钮按用途保持原样,不参与统一:登录页与飞书登录的醒目大按钮、对话输入区的发送/添加圆形主按钮、移动端(安卓/窄屏)放大的触控目标、标签页与选项卡卡片、无边框的文字链按钮(如「更换」「重试」)。

## 0.0.268 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 「我的用量」总览的三张饼图补上细片的占比:占比写进图例(如「Git 提交信息 <1%」「上下文压缩 1.6%」),环上仍只给够大的扇区贴百分比,不用逐个悬浮也能看全分布。同时修掉一个渲染瑕疵——占比不足 8% 的扇区隐藏标签后,echarts 仍会画出指向空白处的引出线,几根线挤在环顶像渲染坏了;现在细片的标签与引出线一起关掉。

## 0.0.267 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 运行轨迹面板改成「汇总 → 下钻」的渐进结构,解决一打开满屏同权卡片、不知道先看哪张的问题。顶部新增会话总览:最近 N 次任务的成败分布、总耗时、总 token、总成本(任一 run 未配价时不显示)、工具成功率,先回答「整体怎么样」再往下钻。每个 run 的摘要行加上开始时间与结果状态图标(✓ 成功 / ⚠ 中断 / ✕ 失败),指标从「墙钟 + token + 成本 + 工具成败」精简为耗时与 token 两个(成本上移到总览,工具成败看总览与展开明细);重试 / 慢 / 贵 / 压缩 / 编辑前等关注点徽标与消息预览同行平铺展示。轮次行里的「重试」从指标行中的一行灰色小字提为带次数的彩色徽标(如「重试 2」),和「慢」一样能一眼指到具体是哪一轮——此前只有 run 级徽标,展开后找不到重试点。token 数百万级改用 M 折算(20.4M 而不是 20392.7k)。数据与导出口径不变。

## 0.0.266 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 运行轨迹入口扩展到边路任务和子代理:在边路任务 / 子代理视图里,任务信息区的状态徽标(已完成 / 执行中 / 失败 / 已取消等)现在同样可以点,打开那个会话自己的运行轨迹——轨迹数据本来就按会话落库,此前只有主会话视图露入口。边路 / 子代理的轨迹在中心 Tab 条上单独开一个 Tab,标题带来源任务名(如「轨迹 · 提交代码并部署」),和主会话的「轨迹」可以同时开着对照;刷新后不还原(与文件 Tab 一致),重新点徽标即可。

## 0.0.265 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 「我的用量」抽屉的趋势页从原来的「Token 柱状图 + 成本折线」双轴一张图,拆成三张独立的折线图:Token 趋势、请求数 / 缓存命中率趋势(请求数走左轴、缓存命中率走右轴)、成本趋势。纵轴读数沿用 K / M / B / T 折算,缓存命中率在没有调用的时间点断开而不是掉到 0。分桶口径不变:今日 / 昨日按小时,其余周期按天。

### 后端

- `GET /v1/llm-calls/me/trends` 的每个分桶新增 `promptTokens` / `cachedTokens` 字段(入 Token 与其中命中缓存的部分),补零桶同样为 0;前端据此在趋势页计算每个时间点的缓存命中率,无需再拿总量倒推。

## 0.0.264 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 「我的用量」抽屉的总览页把「按模型」「按场景」两张汇总表换成了饼图,并新增「按 Agent」饼图,一眼看清调用次数花在哪个模型、哪种场景、哪个 Agent 上。饼图按调用次数占比,悬浮显示该维度的调用数、Token 与成本;维度过多时尾部自动合并为「其他」,不会碎成一地渣。

### 后端

- `GET /v1/llm-calls/me/summary` 响应新增 `byAgent`:按 Agent 汇总的调用数、失败数、Token 与成本(`agent_id` 为 NULL 的压缩/标题等无归属调用保留为一组,Agent 名字经 agent 表解析,已删的 Agent 名字为 null 由前端兜底)。仅需登录、按 JWT userId 过滤,不加权限码。

## 0.0.263 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 全端按钮统一为小尺寸(24px 高、12px 字),设置页、使用记录、收件箱等地方不再有大有小。此前模板里 23 个按钮没写尺寸、取默认 32px,和已写 `size="small"` 的按钮混在一起;现在按 EP small 的度量在全局统一定义,确认弹窗、日期面板、popconfirm 这些 EP 内部渲染的按钮也一并覆盖。输入框前/后缀里的按钮仍跟随输入框高度;登录页的登录/飞书登录按钮保持原有的醒目样式。

## 0.0.262 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 「我的用量」抽屉的入口图标从柱状图换成仪表盘样式:原图标与「轨迹」标签页用的是同一个,顶栏上两个入口长得一样。统计周期同步扩展为今日、昨日、本周、本月、近 7 天、近 30 天(原为近 7/30/90 天),默认今日;选今日或昨日时,趋势图自动改按小时分桶,否则按天。
- 用量的 Token 数值不再用「万 / 亿」和完整千分位长数字,统一折算成 K / M / B / T:总览的五张卡片、按模型与按场景两张汇总表、明细列表的入 / 出 Token 与缓存列都按此显示(667,577,705 → 667.6M);趋势图的纵坐标也会按窗口峰值自动选定单位,读数形如 50M、100M、1B,不再拖着 250,000,000 这样的长数字。

### 后端

- 「我的用量」汇总 / 趋势接口的周期参数由 `days`(近 N 天)改为 `range`:取值 `today` / `yesterday` / `week` / `month` / `last7` / `last30`,默认 `last30`;窗口口径为 Asia/Shanghai 日历日(本周按周一起算,本月从 1 号起算至今天)。趋势接口的 `hour` 分桶在窗口已结束(如昨日)时补满全天 24 个整点,窗口含今天时仍只到当前整点。两个接口仅需登录、按 JWT userId 过滤,不加权限码。

## 0.0.261 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 顶栏新增「我的用量」入口(柱状图图标,位于收件箱图标右侧),点击弹出抽屉,用三个 Tab 展示当前登录用户自己的模型用量:**总览**(调用次数、总 Token、估算成本、成功率、缓存命中率五张卡片,外加按模型、按场景两张汇总表)、**趋势**(每日 Token 柱状图 + 成本折线双轴图,可切换近 7/30/90 天)、**明细**(分页表格,支持场景/模型/结果筛选,底部可跳设置页查看完整使用记录)。窗口默认近 30 天。成本按模型价格填写口径估算,未配置价格的调用不计入(卡片下方有脚注说明)。
- 设置页「使用记录」的筛选新增「模型」维度,与抽屉「明细」Tab 共用同一套筛选栏;模型选项来自 `/models/active`(仅启用中的模型,已停用模型的调用仍按记录快照的模型名展示)。

### 后端

- 新增 `GET /v1/llm-calls/me/summary` 与 `GET /v1/llm-calls/me/trends`:当前登录用户自己的用量总览(窗口总账 + 按模型 + 按场景)与趋势序列。窗口默认近 30 天、钳制 1..365 天;仅需登录、不加权限码,按 JWT userId 过滤。趋势支持 `granularity=day|hour`,由后端按 Asia/Shanghai 日历日/整点补零(序列连续无缺口,前端不做日期运算);成本口径与管理后台一致(`llm_call.cost_micros`,未配价行 SUM 自动忽略)。明细筛选 `modelId` 参数 `/v1/llm-calls/me` 本已支持,本次无改动。
- 修复飞书群里「先合并转发聊天记录、再马上 @ 机器人」时 Agent 看不到转发内容的问题。合并转发行落库后要后台展开(拉子消息、解析姓名,约 1~2 秒),这期间该行标记为待富化;紧随其后的 @ 触发会在群上下文里于该行处截断,本轮 prompt 完全拿不到转发摘录,而用户往往不会有下一轮触发来补。现在触发前会先同步补齐范围内待富化的合并转发行(已在跑的富化只等待、不重复展开),本轮即可拿到全文摘录;进程重启等遗留的僵死行同样有效。图片/文件/卡片仍维持占位懒加载,行为不变。
- 合并转发里的图片不再只剩「[图片]」两个字:展开时按直接入站一致的惯例把图片预下载到会话工作区,摘录改为 `[图片已保存: @{路径}@]`(独立图片)或在图文正文后追加 `@{路径}@` 引用(富文本正文与位置标记保留),Agent 免工具直接读取。单条转发最多预下载 9 张、3 张并发(同步补齐触发时也不能逐张串行拖慢),同一张图转发多次只下载一次;超出上限或下载失败的保留 `[图片 msg=消息ID]` 占位,仍可经 `feishu_download_file` 懒加载——该兜底现已支持图文消息(从消息详情里取内嵌 `img.image_key`)。

## 0.0.260 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 统一 Element Plus 控件的字号,从此不再出现单个页面按钮或表单标签偏大的问题。此前全局把 EP 的字号基准设成了 17px(本意是对齐 App 正文,但 EP 在本项目里只用于表单、弹窗和控件),导致按钮、输入框、下拉选项、表单标签、checkbox/radio、确认弹窗等 49 处规则全部取到 17px,比控件区 13px 的文案大一圈;各页面只能各自打补丁压字号。现在基准改为 13px(small/extra-small 为 12px),并删掉这些补丁;EP 的 size="small" 按钮仍是写死的 12px,不受影响。
- 「开放接口」API Token 列表新增「复制 Token」按钮,可随时重新获取完整明文,不再限于创建时展示一次;签发弹窗的提示文案同步改为「已加密留存、可随时再次获取」。本次升级前签发的旧 Token 未留存加密明文,按钮置灰并说明原因,需吊销后重新签发。

### 后端

- API Token 明文改为 AES-GCM 加密留存以支持再次复制:`api_token` 新增 `token_cipher` 列(V147),新增 `GET /v1/open/tokens/:id/plain` 供属主解密取回明文,列表响应新增 `canCopy` 标识能否复制。密钥走独立配置 `APP_OPENAPI_TOKEN_SECRET`(未配置回退内置默认密钥,仍建议显式设置);轮换该密钥后旧 Token 不能再复制明文,但 sha256 鉴权不受影响。2026-10-10 之前签发、未留密文的旧 Token 无法再取回明文(哈希不可逆),需吊销重签。

### 终端 CLI(mao-cli)

- `mao open token reveal --id <id>` 再次打印指定 API Token 的明文;`open token create` 等帮助与参考文档同步更新。

## 0.0.259 (2026-10-10)

### 前端(桌面 / Web / 安卓)

- 会话搜索里日期筛选的「开始日期」「结束日期」字号和其他输入框对齐。此前 Element Plus 把这个输入的字号绑在全局 17px 基准上,比弹窗里其他控件大一圈。
- 修复「开放接口」签发 API Token 后弹窗里多出一个空的「Secret」输入框的问题。API Token 本身不配 Secret,该字段只属于 Webhook 触发器和出站订阅,此前少了条件判断导致它一直渲染。
- 服务器设置页的「检测连通性」「保存并切换」字号从 17px 调回 13px,和页面标签一致。Element Plus 默认按钮字号同样取全局 17px 基准。

## 0.0.258 (2026-10-10)

### 后端

- 微信通道回复超过单条上限时自动分片发送:ilink 对单条文本的上限按 UTF-8 字节数计算,实测纯中文 2000 字符(6000 字节)是通过上限、2001 字符(6003 字节)即被拒,因此分片按字节预算而非字符数切——按字符切会把中文消息高估成 3 条,白耗微信侧配额。切分优先段落、行、空格边界,都没有则硬切且保证不切出半个中文字符;每片独立 `client_id`、复用同一 `context_token`,用户侧表现为连续多条消息。任一分片失败即停止后续发送,避免半截内容后面继续刷屏。
- 微信下行失败不再静默:ilink 返回业务错误或 HTTP 非 2xx 时,错误日志补记原始响应体(截断到 300 字符并遮罩 `token`/`secret` 字段),并单独提取 `errmsg` 字段。这是区分「消息体过大」(`errmsg="prepare failed"`)与「24 小时窗口过期」(同样返回 `ret=-2`,需结合距上次入站时间判断)的关键线索,此前代码两条都不记。同时给用户回一条「回复发送失败了,但回答已经生成,请在电脑端打开 Mao 查看完整内容」,不再出现 Agent 跑完而微信上什么都没收到。
- 定时任务向微信回流回复前先做 24 小时主动发送窗口预检:`context_token` 只在用户最近一条入站消息后的 24 小时内有效,定时任务多在用户长时间未说话后触发,此前直接打 ilink 只会拿到 `ret=-2`。现在超窗则不打 ilink,改为写一条站内收件箱通知指路到桌面/网页端。
- 微信下行失败时,若用户已在「消息通知」里配置了钉钉/飞书 Webhook,会把回复原文降级推送到该 Webhook。触发条件是微信侧完全不可达(原始回复与降级提示都没发出去,如 24 小时窗口过期);降级提示能送达时不推,避免重复。Webhook 自身失败只记日志,不影响主流程;未配置通知的用户行为不变。

## 0.0.257 (2026-10-09)

### 后端

- CLOUD 会话可以在工作区里新建、改名、移动、复制、删除和上传文件(`/v1/files/workspace-mkdir|write|rename|move|copy|delete|upload`)。路径限制在当前会话工作区内;Agent 正在写同一文件或所在目录时返回冲突,调用方可以显式强制。成功的操作记入该会话的活动记录。LOCAL 会话调用这些接口会被拒绝。
- 会话搜索改为全文检索:可搜自己的用户消息和助手回复,支持按 Agent、日期、主会话/边路筛选和分页。两字及以上走 MySQL ngram 全文索引,过短或无法解析的关键词仍按原文模糊匹配。
- 打开某条命中时,消息列表直接定位到那一轮(含助手回复),并告知后面是否还有更新的消息。
- 首次发版会给 `message` 表建全文索引(V146)。建索引期间消息写入会卡住,且健康检查必须盖住整个构建时间,见部署说明。应急可设 `SEARCH_FULLTEXT_ENABLED=false` 后重启,查询退回模糊匹配,索引保留。

### 前端(桌面 / Web / 安卓)

- CLOUD 工作区文件树可以新建、重命名、删除、移动、复制和上传文件。删除前会列出路径并说明不可恢复;Agent 正在写同一路径时会先询问,默认取消。LOCAL 工作区菜单保持只读。
- 运行轨迹里,用户自己改的文件会标上「用户」,和 Agent 的工具调用放在同一条轨迹里。
- 顶栏搜索按会话分组展示命中,可筛选 Agent、时间和会话类型。点开一条命中会滚到该消息并短暂高亮;如果不在最新几轮,可以回到最新。

---

## 0.0.256 (2026-10-09)

### 前端(桌面 / Web / 安卓)

- 定时任务卡片可以展开最近运行:每次触发的时间、成功/失败/错过/待重试、第几次尝试、耗时和成本。点一行会打开对应会话;会话已经删掉时只提示,不跳进空白页。
- 连续失败被自动暂停的任务会标明失败了几次。重试次数、间隔或错过补偿如果不是默认值,卡片上会写出来。
- 定时任务因连续失败被自动暂停时,收件箱会多一条「定时任务已自动暂停」。这条不受「任务完成」开关影响;桌面窗口不在前台时也会弹系统通知(仍受系统通知总开关控制)。

### 管理后台

- 定时任务编辑可以设置失败重试次数、重试间隔和错过补偿。详情里能看到连续失败次数和最近运行记录。

### 后端

- 定时任务每次触发留下一条运行记录。失败后按任务配置自动重试(默认再试 2 次、间隔 5 分钟);预算不足、会话已删除和用户取消不会重试。重试不另算一次触发,也不把档期往后推。
- 服务停机错过的触发点会记下来。默认补跑最近一次,更早的记为错过;任务可以改成只记录不补。
- 同一个任务连续 3 个触发点最终失败后自动暂停,并写入收件箱。手动重新启用会把连续失败次数清零。运行记录保留 90 天。

### 终端 CLI(mao-cli)

- `mao scheduled-task update` 可以改重试次数、间隔和错过补偿。`mao scheduled-task runs` 查看最近运行记录。

---

## 0.0.255 (2026-10-09)

### 前端(桌面 / Web / 安卓)

- 设置里的每个 API Token 和 Webhook 触发器可以查看最近的调用记录,并跳到对应会话。
- Token 可以自行开启「完整记录」。开启前会再次确认;开启期间卡片上会一直提示请求体可能含敏感信息,开关会留下审计。
- Token 因连续失败被自动停用后,卡片上会出现停用标记,属主可以重新启用。
- 通知设置新增「开放调用失败通知」,默认关闭。打开后,同一个 Token 的同一种错误每 10 分钟最多收一条。

### 管理后台

- 新增「开放调用」页:入站调用流水、按天统计、成功率、P95、异常高亮,以及 CSV 导出。
- 详情里可以复制 cURL。有重放权限的管理员可以对 API Token 调用再发一次;Webhook 不能从服务端重发。用量记在 Token 属主头上。
- 系统设置「开放接口」分类可以改流水保留天数、自动停用开关和每小时失败阈值。

### 后端

- 入站 API Token 触发和 Webhook 触发会留下调用流水,包括被拒绝的请求。查无 Token 或乱扫 Webhook 地址时按来源 IP 限流落库,避免被打爆。
- 排队执行结束后回写成功、失败或取消;用户删掉排队消息,或插队打断上一条时,流水记为已取消。崩溃没能写回终态的调用在统计里单列「结果未知」,不猜测结果。
- API Token 在一小时内连续失败达到阈值(默认 10 次,可关)后自动停用,调用返回 403,并在收件箱通知属主。限流、取消和重放不计入这个次数。流水默认保留 90 天。
- 未开启完整记录时,流水只保存消息长度和字段名,不保存正文。脱敏和「原样重发」不能同时做到:没开完整记录时,重发需要手工粘贴 message。

---

## 0.0.254 (2026-10-09)

### 后端

- 修复夹在后续消息中间的孤儿工具结果会让会话压缩永远失败的问题。这类结果仍会从发给模型的历史上丢掉,但不再卡住压缩边界;历史撑过窗口后,发消息不再因此直接失败。
- 修复按轮翻页时,补写在后面的工具占位被当成孤儿丢掉的问题。桌面向上翻历史、分享只读页和 JSONL 导出会把它贴回原来的助手消息。
- 修复飞书群里尚未展开的合并转发被挤出最近消息窗口后,水位线越过它、展开摘录再也进不了上下文的问题。占位行完成前,其后的消息不会被注入或写进更早历史摘要。
- 修复边路任务预览向上翻页时,上一页第一条消息会再出现一次的问题。
- 修复技能 Bundle 导出在同一用户有多个同名技能目录时静默导出其中一个的问题。现在会拒绝导出,并列出这些目录。

---

## 0.0.253 (2026-10-09)

### 后端

- 修复运行轨迹里工具几乎全部落入「未挂到轮」的问题:`llm_call` 的落库时刻(调用结束)与助手消息的落库时刻同为秒级,流收尾与消息落库几乎总落在同一秒,原来的「严格早于」判定因此很少命中。改为同秒也认(同秒仍有多条候选时不猜),挂轮判定恢复正常。历史缺 `tool_call_id` 的活动行仍按原口径留「未挂到轮」,不做回填。
- 将 `message` / `llm_call` 的 `created_at` 提升到毫秒精度(`DATETIME(3)`,列默认值同步改为 `CURRENT_TIMESTAMP(3)`),`message.updated_at` 一并提升。升级后同一秒内的多条模型调用可区分先后,工具组能稳定挂到正确的一轮;秒级时间戳的字典序不变,轨迹的窗口查询与新旧数据混读都不受影响,存量数据不回填。

---

## 0.0.252 (2026-10-09)

### 前端(桌面 / Web / 安卓)

- 「运行轨迹」入口改为点击任务信息区的任务状态徽标(执行中 / 已完成 / 失败 / 已取消 / 恢复中 / 取消中),不再单独占一个「轨迹」按钮;徽标悬停提示可点。取消与恢复过程也补上了徽标,取消后仍进得去轨迹;边路任务与子代理视图不露入口。

---

## 0.0.251 (2026-10-09)

### 前端(桌面 / Web / 安卓)

- 新增「运行轨迹」透视:任务检查器点「轨迹」打开轨迹 Tab,按「一条用户消息到下一段」拆成 run,逐轮展示模型调用耗时、首字、重试、token、成本,轮内是工具(名称、目标、耗时、审批标记)。排障时能直接看出慢在哪一轮、卡在哪个工具上。
- 轨迹摘要条默认收起,异常(失败 / 重试 / 中断 / 慢 / 贵)按严重度描边并打徽标,正常 run 不打扰。展开后按「编辑前 / 当前」分段——改完消息重发时,发送前的调用单独成段,不与重发后的混在一起。
- 执行过程中轨迹 Tab 实时显示「第几轮、是否仍在模型调用中」以及进行中的工具已耗时多久(工具结束才有服务端耗时,进行中由本地计时)。执行结束后自动刷新为完成态。
- 轨迹 Tab 顶部可调「慢」「贵」两个阈值(浏览器本地记住),用于决定哪些轮打徽标;支持导出 JSON / CSV,CSV 列口径与管理后台调用流水一致。
- 待发送队列在刚打开会话时也能拖拽排序。此前队列来自接口时消息编号是数字,拖拽比较用的是字符串,松手后顺序不会保存,要等下一次队列推送才恢复。
- 边路任务页签被指到另一个会话时,上一个会话迟到返回的状态不再写进当前会话,也不会把当前会话标成正在执行。

### 管理后台

- 「用量分析」新增「运行轨迹」Tab:最慢的轮、最贵的轮(按 Agent / 用户维度)、工具失败率 Top。口径只统计对话轮(scene=agent),标题生成与连通性测试不占榜。

### 后端

- 新增 `GET /v1/sessions/:id/trace`:按 run 聚合读模型(轮、旁路调用、工具、压缩标记、墙钟、成本),支持 `beforeRunId` 翻页与 `slowMs` / `expensiveTokens` 阈值。读时聚合,不新增表。
- `recordActivity` 透传工具耗时 `duration_ms` 与 `detail_json`(`toolCallId` / `approvalMark`),WS 的 `activity` 事件带 `duration_ms`;新增 `round_start` / `round_end` 轮事件(只下发、不落库)。
- 新增 `GET /v1/admin/analytics/run-trace`:运行轨迹三榜聚合(`scope=agent|user`、`days`、`scene`)。
- 「立即发送」碰到已经发出、取消超时或会话仍在忙而没有真正插队时,不再把该会话剩下的待发送消息卡住。此前这种中途退出会留下一个抑制标记,队列要等后端重启才会继续自动发送。
- 自动压缩的触发线改回按会话所用模型的上下文窗口计算。单独配置的压缩模型只负责写摘要,并防止摘要请求超出压缩模型自己的窗口。此前触发线跟着压缩模型的窗口走:窗口更大时会话一直不压缩,直到被供应商以超长上下文拒绝;窗口更小时会过早压缩,并直接中断这一轮。
- 流式工具调用如果开头没有调用编号、后面才补上,不再多出一张永远转圈的工具卡片,刷新后也不会再把这张假卡恢复出来。
- 飞书群聊溢出摘要的调用记到实际使用的模型上,用量和成本可以归到该模型。
- 刷新页面后,上下文构成与上一次推送一致,不会退回更早一轮的分节和 token 数。
- 流式调用被上游截断或中途取消时,已经返回的 token 仍会计入调用流水和成本。此前这些调用记成 0 token、0 成本。

---

## 0.0.250 (2026-10-08)
### 前端(桌面 / Web / 安卓)

- 边路任务和子代理对话的消息区不再显示滚动条,与主会话一致。内容超出时仍可滚动。

### 管理后台

- 模型价格由「输入 / 输出」两项扩为四项:新增「缓存读价格」「缓存写价格」,「输入价格」改为只覆盖非缓存输入。此前缓存命中由系统统一按 5 折折算,与各供应商真实价目差得多(OpenAI 缓存读 50%、无缓存写;Anthropic 缓存读约 10%、缓存写约 125%),账目对不上发票。现在四类 token 分别计价,可直接照抄官方价目表。
- 调用流水新增「缓存写 Token」列(表格 / 移动端卡片 / 详情 / CSV 导出),能看出钱花在缓存写入还是普通输入上。协议无缓存写概念(OpenAI 系)时显示 `-`。
- 价格留空语义收紧为「该类 token 不计成本」:某类 token 实际产生了但对应价格留空,该次调用成本整行留空,不按 0 计入——低估的账目会骗过预算闸门。只填输入/输出即可覆盖无缓存的调用。
- 升级时若模型已填输入价,缓存读价自动推为输入价一半、缓存写价推为与输入价相同,与旧的 5 折口径算出的成本完全一致;之后可在模型表单自行调整。
- mao-cli `model create/update` 新增 `--price-cache-read` / `--price-cache-write`。

### 后端

- 模型价格四项化(V141):`llm_model` 新增 `price_cache_read` / `price_cache_write`(每百万 token 价格,DECIMAL(12,6),NULL 表示该类不计成本);`llm_call` 新增 `cache_creation_tokens`(缓存写入 token 数,OpenAI 系恒 0)。
- Anthropic 适配器把 `cache_creation_input_tokens` 并入统一 usage 的 `promptTokensDetails.cacheCreationTokens`(此前已计入 `promptTokens` 但分项被丢弃,无法单独计价)。
- 成本公式改为逐项相加:`非缓存输入×输入价 + 缓存读×缓存读价 + 缓存写×缓存写价 + 输出×输出价`,删除原先固定的 5 折折算。

---

## 0.0.249 (2026-10-08)
### 管理后台

- 添加、编辑、复制模型弹窗分成「基本信息」和「接入配置」两个 Tab。模型身份与能力在前一页,协议、地址、密钥和价格在后一页;保存时另一页有未填项会自动切过去。

## 0.0.248 (2026-10-08)
### 管理后台

- 调用流水表格在宽屏下铺满卡片。此前每列都是固定宽度,列宽合计小于窗口时右侧留白。

## 0.0.247 (2026-10-08)
### 前端(桌面 / Web / 安卓)

- 「上下文详情」的容量仍是模型实际占用。构成各节按字节估算的比例分摊到这个总数,合计与容量一致。此前容量是模型 token、明细是偏小的字节估算,两边对不上。
- 「上下文详情」不再显示「本会话注入记忆」及其开关。
- 「上下文详情」里的「立即整理」和「上次摘要」改成同一行的轻边框按钮,不再用实心主按钮配文字链。

## 0.0.246 (2026-10-08)
### 前端(桌面 / Web / 安卓)

- 会话与边路任务右键「导出 Markdown」改为「导出 JSONL」:首行是导出元信息(会话标题、Agent、导出时间、消息数、文件变更汇总),其后每行一条原始消息,字段与会话压缩归档的 `compaction-NNN.jsonl` 完全一致(含 `toolCalls` / `metadata` / `tokenCount` / `modelId` 等),不再有四节摘要模板造成的细节丢失;内联图片 base64 仍替换为占位符。文件名后缀改为 `.jsonl`。
- 「上下文详情」不再单列「用户上传文件」。这段是云端会话里关于上传目录与 `@{绝对路径}@` 的固定说明,不代表实际上传的文件,现计入「系统提示词」。
- 刷新页面后「上下文构成」仍显示上一次任务的分节。此前只把水位数字记在会话上,构成只活在当次推送里,刷新后抽屉会显示「尚无构成数据」。
- 上下文构成里的「可用技能」改称「技能」。
- 上下文构成按 token 数从大到小排列。此前按四舍五入后的占比排序,同为 0% 或 1% 的项不会按实际 token 分先后。
- 分享链接打开的只读页与客户端一致:已完成轮次的执行过程默认折叠,复制按钮只留在该轮最终回复上,不再出现在中间步骤。底部文件变更按路径合并,同一文件多次改动只占一行,增删行数合计。

### 后端

- 会话导出改为 JSONL:`GET /v1/sessions/:id/export/jsonl`(原 `/export/markdown`),首行为 `session_export` 元信息头,其后每行一条消息,行结构与压缩归档 `compaction-NNN.jsonl` 共用 `session/message-jsonl.ts` 序列化,两处格式不会再各自漂移;消息按 `messageId` 附 `fileChanges` 摘要。`thinkingContent` 不导出,内联图片 base64 替换为占位符。步骤 500 条截断与四节模板随之取消;超过 5MB 仍返回 413。
- Context Manifest 把上传文件说明并入「系统提示词」:`incoming-file` 不再作为独立构成节,提示正文仍注入系统提示,实际文件引用仍在「会话消息」。
- 会话保存最近一次上下文构成快照(V140 `context_manifest_json`),与 `context_window` 推送同一份 JSON。刷新后会话列表和详情把这份快照带回,不按当前消息重算。没有构成的旧会话仍只显示水位。

## 0.0.245 (2026-10-08)
### 前端(桌面 / Web / 安卓)

- 「撤销分享」确认框的标题、正文和按钮收回到与「分享会话」弹窗同一档(正文 13px,按钮内边距与弹窗里的「复制 / 撤销」一致)。此前确认框沿用全局 17px 字号,叠在分享弹窗上显得偏大。
- 「上下文详情」抽屉的构成区修复数字对不上的问题:新增「系统工具」一节(工具定义的估算 token 与工具数),让分节合计与顶部「上下文容量」口径一致——此前容量含工具定义、构成不含,同样会话下两者能差出整个工具段,看起来像数据错误。两侧 tooltip 与脚注同步改为「同口径合计」表述。
- 构成区分节聚合:Agent 人格、工作环境、当前日期、工具指引、任务管理、子代理等系统提示固有文案合并为单节「系统提示词」;长期记忆、最佳实践经验、可用技能、用户上传文件、工作区规则等可治理的注入内容仍独立成节。构成列表从十几行收敛到 5–7 行,一眼看出上下文被什么占满。

### 后端

- Context Manifest 与构成口径对齐(技术方案 5.2 修订):`buildContextManifest` 增加「系统工具」节(`estimateToolDefinitions` 同源口径 + 工具数),并把系统提示固有分节聚合为单节「系统提示词」,注入型分节(经验 / 记忆 / 技能 / 上传文件 / 工作区规则)保留独立条目。manifest 序列化上限与推送频率不变(去掉十余个细碎分节后体积反而下降)。

## 0.0.244 (2026-10-08)
### 前端(桌面 / Web / 安卓)

- 任务检查器去掉「上下文」顶层页签,改为点击任务信息区的上下文徽标打开「上下文详情」抽屉:水位、构成、本会话注入记忆与手动整理都在同一处查看,习惯的自动压缩与治理能力不变。徽标在尚无 token 数据时也展示(冷启动会话同样可打开抽屉),切换会话会自动收起抽屉,避免治理动作落到上一个会话。
- 「上下文详情」抽屉的水位与构成改为容量卡片式呈现:顶部「上下文容量」一行给出「估算 / 窗口(占比)」与一条三段式色调进度条(常规 / 提示 / 告警),下方「上下文构成」改为圆点 + 标签 + 占比 + token 的紧凑行并按占比倒序,一眼看出上下文被什么占满。原有 token 估算口径、分节口径差说明均保留。
- 修复「分享会话」弹窗嵌在侧边栏里、显示不全:弹窗此前悬浮层直接挂在任务面板内,被侧边栏的 `overflow: hidden` 裁掉且定位基准变成面板左上角。现在挂到 `body` 正常居中,并去掉侧边栏上无实际毛玻璃效果的 `backdrop-filter`(它会让祖先成为 fixed 定位的包含块,同类问题还会复现);宽度改为随视口收缩,窄窗与安卓端不再超出屏幕。
- 修复任务列表右键菜单在当前会话 Agent 执行时被反复关掉。对话区随执行状态贴底滚动也会触发全局 scroll 监听;现在只有任务栏(以及边路任务列表、文件 Tab 栏)自身或其父级滚动时才关闭,窗口尺寸变化仍会关闭。

## 0.0.243 (2026-10-08)
### 前端(桌面 / Web / 安卓)

- 收件箱「加载更多」会把下一页接在已有列表后面。此前翻页会整页替换,第一页条目会消失。
- 新增「预算提醒」收件箱类型:预算超额时写入站内信并以「预算提醒 / ¥」徽标展示,预算越限被拒绝入队时同口径提示;通知设置新增「预算提醒通知」开关(默认开),可单独控制是否推送系统通知。预算越限与触发器停用两类条目的渲染此前因 kind 白名单缺失而被静默过滤,本次一并补齐。

### 管理后台

- 新增「用量预算」页(菜单入口,需 `budget:read` / `budget:write` 权限):按全局 / 用户 / Agent 三个维度新建月度预算,可分别按金额或 token 定额,动作可选「超额提醒」或「超额拦截」,支持启停与删除;列表展示当期已用与上限的进度条(金额按成本单位、token 按原值),越线或临近上限时变色提示,被删除的目标照常展示为「已删除」。
- 模型表单新增「输入价格 / 输出价格」两项(每百万 token 成本单位,留空表示不计成本):输入价格与输出价格可分别填写,仅文本类模型显示;价格与后续调用记录的成本展示口径一致,仅影响新调用、不追溯历史。
- 系统设置新增「压缩模型」选项:上下文压缩可指定独立模型(下拉选择),缺省继续使用会话主模型。
- 用量分析各 Tab(总览 / 趋势 / 模型 / 用户 / Agent)与调用明细新增「成本」列与成本趋势图:总览展示当期总成本及与上一周期的环比;明细页与 CSV 导出均含成本列。模型价格未填写时该次调用成本留空(不按 0 计),已配置的模型按写入时价格快照计入,历史数据不做回填。

### 后端

- 飞书/钉钉私聊里没有 Agent 的会话,展开分组后能翻到。此前查询写成 `agent_id = NULL`,结果恒为空。
- 钉钉排队卡片「立即执行」在消息已经开始时不再报成功,也不会打断当前会话。
- 一次性定时任务在会话忙碌时先入队。若这条排队消息被取消,任务回到未完结并重新计算下次触发时间,不会因为没跑成就永久完结。
- 最后一名管理员的判断与登录一致:`status` 为空的账号(SSO / 外部账号默认)也算活跃管理员,不再误拦降级或禁用。
- 开放接口和 Webhook 触发指定会话时,会话必须属于同一个 Agent。不一致会直接拒绝,不会改用会话上的另一个 Agent 执行。
- 上下文压缩记录的压缩前用量与触发判断使用同一口径。此前节省 token 按低估的估算值计算,数字会明显偏小。
- 流式工具调用在首片没有 id、后续分片才带回真实 id 时,参数会拼进同一次调用。此前后半段参数可能被截掉。
- 模型价格与成本落账(V138):`llm_model` 新增 `price_input` / `price_output`(每百万 token 价格,DECIMAL(12,6),NULL 表示不计成本,填 0 表示免费);`llm_call` 新增 `cost_micros`(成本单位 × 1e6 的整数快照)。价格在调用写入时快照,后续改价只影响新调用;缓存命中的输入 token 按 5 折计入。价格落在 DECIMAL(12,6) 值域外或小数位超过 6 位的输入会被直接拒绝(不静默取整)。
- 新增 `usage_budget` 表与预算管控(V139):按 GLOBAL / USER / AGENT 三个维度设月度预算,口径可选金额(COST)或 token 数(TOKENS),动作为 WARN(超额时给属主写一条 BUDGET_WARN 站内信,同预算同月只提醒一次)或 BLOCK(直接拒绝发起新任务)。周期按服务器本地时区的自然月计算。
- 任务发起的 5 个入口统一做预算准入:WebSocket 手动发送、消息编辑重发、排队消息自动消费、打开已有会话的任务、定时任务直接执行。BLOCK 命中时返回明确错误且不写入消息,排队消息重新入队并通知一次,任务相位不会被改写;被拦截的定时任务标记为失败并照常推进下一档期。子代理与边路任务跟随主会话的准入结果。
- 开放 API 发起运行与入站 Webhook 触发同样纳入预算准入:命中 BLOCK 时开放 API 返回错误、Webhook 触发置为失败状态并按既有逻辑计入连续失败计数。
- 连通性测试等不计成本的 scene 不参与成本聚合统计。
- 上下文压缩支持独立模型(`compaction.modelId` 设置项):可给压缩指定单独模型,缺省或配置失效(未设置 / 非数字 / 模型已删 / 已停用 / 非文本模型)时自动回退会话主模型;压缩产生的调用开销按该配置计入成本。
- `GET /v1/admin/budgets`(列表,含当期消耗)、`POST` / `PUT` / `DELETE /v1/admin/budgets[/:id]`(增改删,需 `budget:write`);全局预算仅允许一条生效。收件箱偏好新增 `budgetWarnEnabled`(默认 true)。

---

## 0.0.242 (2026-10-08)

### 后端

- 飞书合并转发在交给 Agent 之前展开为聊天摘录(时间、发送人、正文)。私聊和会触发的群消息同步展开;群里未触发的先落占位,异步展开后回写群日志,之后的【群内最近消息】和引用读到摘录。嵌套最多 3 层,单次最多 100 条;超过 4000 字时全文写入会话工作区 `quoted/merge-forward-{messageId}.txt`。展开失败仍保留「Merged and Forwarded Message」,不阻断入站。

## 0.0.241 (2026-10-08)

### 前端(桌面 / Web / 安卓)
- LOCAL 工具审批卡片新增第三个选项「总是允许」:审批卡片副标题展示将要记住的确切命令模式(如 `npm run`),点击后当次照常执行,并在服务端创建本会话级放行规则;同会话内后续命中该模式的调用静默放行,工具卡片显示「规则放行」徽标,不再逐次弹卡。含受保护命令(rm、sudo、git push --force 等 denylist 命令)的命令不出现第三按钮、已建规则也不放行。READ_ONLY / FULL 档不受规则影响(前者审批是边界本身,后者无审批)。
- 设置页新增「审批规则」管理页(`/settings/approval-rules`):用户级持久规则清单(命令前缀 / 完整命令 / MCP 工具三类),按命中次数排序,展示命中次数(= 省掉的审批次数)与最近命中时间,支持启停、编辑、删除与手动新增(服务端归一化并在响应中返回归一化后的规则值,前缀规则自动截取前两个词)。

### 后端

- 新增 `approval_rule` 表(V137)与审批规则域:`ApprovalRuleService` 承担模式归一化(env 前缀剥离、空白折叠、前两 token 前缀)、denylist 校验(内置种子 + `approval.rule.denyTokens` 系统设置可扩展,admin「审批」分组可编辑)、规则匹配(会话级优先于用户级,同级内全等优先于词边界前缀)与命中计数。
- 判门接入:`READ_WRITE` / `SMART` / `PROXY` 档的 shell 与 MCP 调用在原审批判定之前先查规则,命中即静默放行并短路 Jev 前置 / DangerAssessor / ProxyApprover(省掉审批用 LLM 调用),落 `metadata.approvalMark = { mode: 'rule' }`(复用既有徽标管线);未命中则原五档判定链行为不变。READ_ONLY / FULL 档零规则查询。
- `tool_execute` 帧增量下发 `approvalHint`(服务端生成,客户端不可注入 pattern);`tool_approval` 帧增量消费 `alwaysAllow` 布尔位:后端按 requestId 从审批注册表取回服务端存储的 hint,按 `(session_id, rule_type, rule_value)` 查重后创建会话级规则(断线重发幂等);deny 帧与 hint 缺失(超时/重启)时静默忽略。
- 新增 `/v1/approval-rules` 用户级 CRUD(本人数据,规则管理动作写审计日志);新增 `/v1/admin/approval-rules` 只读清单(新权限码 `approval-rule:read`,仅系统管理员,含用户名 / 会话标题 / 命中统计 enrich,支持 user / type / enabled 筛选)。会话删除级联物理删其会话级规则,用户级规则跨会话持续生效。

### 管理后台

- 「安全」分组新增「审批规则」只读清单页:全用户规则(用户 / 范围 / 所属会话 / 类型 / 规则值 / 命中统计 / 状态),支持按用户、类型、启停筛选,无操作列。
- 修复模型配置列表名称旁的「视觉」与「启用/停用」图标空白:图标改为按需引入后,`Picture`、`CircleCheckFilled`、`CircleCloseFilled` 未在页面局部导入,外层图标容器只剩空占位。协议品牌图标不受影响。

### 桌面 Electron

- 审批透传链扩展:preload `toolExecute` 增第 8 参 `approvalHint`、`respondToolApproval` 增第 3 参 `alwaysAllow`,主进程把 hint 透传到 shell / MCP 审批卡片。旧版 Electron 壳(7 参 / 2 参签名)收不到 hint、传不出 alwaysAllow → 卡片退化为两按钮、只执行不建规则,方向安全(不会误放行)。

## 0.0.240 (2026-10-08)

### 后端

- 新增 `POST /v1/system-settings/test/jev`(需 `settings:write`):用表单或已存的端点、模型名、API Key 向 Jev 决策接口发一次只读探测,校验 Key 是否有效、响应是否符合决策协议。留空项回落已存配置;端点或模型名为空时使用默认值。

### 管理后台

- 系统设置「审批」增加「测试连接」,可在保存前验证 Jev 端点与 API Key。成功时提示实际返回的模型名。
- 系统设置左侧目录重做:19+ 项平铺改为两级分组手风琴,共 7 组——登录认证(公司 SSO、ECP 飞书登录、LDAP 认证、飞书 OAuth 登录)、文件与存储(上传配置、OSS 对象存储)、Agent 引擎(Agent 运行、LLM 超时与重试、上下文压缩)、模型任务(会话、代码、记忆)、工具与终端(网络工具、网页抓取、Shell 会话、云端终端、审批)、通知与消息(任务通知、微信)、平台与运维(Agent 资产、分享、审计、运行环境);后端新增未声明分类自动归入「其他」。目录默认只显示分组标题行,点击分组或滚动到该组内容时自动展开并跟随高亮,滚动右侧配置时目录固定不随页面滚走;右侧卡片顺序同步按分组排列,配置项名称、保存逻辑与锚点 id 不变。

### 终端 CLI(mao-cli)

- `mao settings test jev` 支持可选 `--endpoint`、`--model`、`--api-key`。

## 0.0.239 (2026-10-07)

### 前端(桌面 / Web / 安卓)

- 检查器新增「上下文」页签(仅主会话 / 边路任务;子代理会话不显示):把"这一轮模型到底看到了什么"透明化,只读、不改发送内容。逐节展示系统提示各节、对话消息、交接(压缩)摘要的估算 token 与占窗口比例;token 为字节估算口径、与顶部含工具定义的水位有差,条形区有 tooltip 说明;尚未产生本轮请求(冷启动)时暂无构成明细。
- 「本会话注入记忆」:列出本轮注入的长期记忆条目 chip,点击跳「设置 → 长期记忆」定位管理;节头有「本会话注入」开关(默认开),关闭后**下一次任务开始起**不再注入长期记忆、构成里也不再有记忆节(运行中的会话本轮不回灌,开关旁提示"将于下次任务开始时生效")。切换所检查的会话后按新会话重新拉取 chip 文案,条数相同也会更新,不会停在上一会话的「记忆 #id」。
- 「立即整理上下文」手动压缩:会话空闲时立即压缩(越过自动阈值);运行中点击则排队、在本轮工具结束后执行并提示"将在本轮工具结束后执行";若本轮之后模型直接以纯文本结束、不再调用工具,也会在本轮退出前执行,不会只提示已排队却不整理。压缩进行中发消息提示会话忙、结束后恢复。手动压缩生成的消息区分隔线标注"手动整理上下文";自动整理关闭(`harness.compaction.enabled=false`)时手动按钮仍可用。
- 「查看上次摘要」折叠面板按需读取本会话最近一次压缩摘要全文,附压缩次数 / 压缩模型 / 更新时间;无压缩历史显示空态。切换会话后丢弃上一会话尚未返回的摘要,新会话打开面板时重新读取,不会显示上一会话的摘要。
- 工具结果被后端截断时(如 `read_file` 大文件、`grep_search` / `glob_search` / `open_web_page` / `shell_session` 命中截断),对应工具卡片显示「输出已截断」徽标——表示展示的是后端截断后的内容,与卡片本地"展开完整输出"(前端字符折叠)并存、含义不同;纯文本(非结构化)工具输出不带此徽标。
- 会话(含边路任务、归档任务)右键可「分享…」与「导出 Markdown」。分享对话框展示只读链接、访问次数,可复制、刷新快照、撤销(二次确认),并说明弱冻结语义:新消息需刷新才出现,撤回/删除同步消失,编辑重发以新内容呈现并隐藏其后消息。分享表单在每次打开时复位,切换会话不会残留上一个会话的「匿名链接」勾选与有效天数;生成失败(如开关未开启)时给出明确提示。
- 新增只读页 `/share/:token`(需登录,未登录回跳)。复用消息气泡渲染,隐藏编辑、分叉、点踩、思考过程与子代理「查看过程」;文件变更只展示路径与行数。匿名链接页 `/share/public/:token` 免登录,开关关闭或过期时显示失效占位。
- 边路任务右键菜单在贴右 / 贴底唤出时不再被视口裁切。

### 后端

- Context Manifest:`buildRequest` 构建请求时同源产出上下文构成清单(系统提示逐节 tokens + 以最终请求口径统计的 messages 节 + 由 `sessionSummary` 驱动的 handoff 摘要节 + 本次注入记忆 id 列表 + 生效窗口 tokens),随既有 ws `context_window` 事件帧推送(不新增事件类型;序列化 ≤ 8KB,超限裁剪 memoryIds),保证"所见即所发"。`buildSystemPrompt` 改为分节收集后 join,逐字节等于重构前输出(等价性快照单测锚定,覆盖普通 / embed / 微信 / LOCAL / CLOUD 各通道与记忆、经验、AGENTS.md 分支)。
- 手动压缩:新增 ws 入站 `compact_now`(属主校验)。空闲路径先占 `executionClaim` 再压缩(判定与占坑之间无 await,压缩期间 `send_message` 被既有 `session_already_running` 语义拒绝),`requestCompaction` 以 `force` 旁路阈值判定 + `skipAutoCompact` 避免双重压缩,合成 listener 下发 compaction_start/end/marker 与水位刷新(否则全程 UI 失明),成功路径回收云 MCP 连接;运行中路径经 `CompactionSignalBus` 置信号 + ws 回执"已排队",在下一工具轮边界(置于 `midLoopAllowed` 门槛之外,使 `enabled=false` 不禁止手动)消费执行、重建 `preparedRequest`;若之后不再有工具轮(模型以纯文本结束),退出循环前同样消费,避免已回执「已排队」的整理被静默丢弃。信号双向清理(执行启动丢弃陈旧信号、finally 清理)防下一次执行意外压缩。`orchestrator.compact` 增显式 `triggerMode` 参数,手动留痕 `trigger_mode='manual'` 落 `session_compaction_event`,既有 `request_start` / `mid_loop` 调用点同步补参不回归。
- 新增 `GET /v1/sessions/:id/compaction`(登录用户,属主校验,只读):返回 `{ summaryText, lastCompactedMsgId, compactCount, compactModel, updatedAt } | null`(无压缩记录 data 为 null,不算错);摘要沿用站内脱敏视角,不做二次脱敏。
- 会话级记忆注入开关:`session` 增 `memory_injection_disabled` 列(V135 迁移,TINYINT 默认 0);`UpdateSessionRequest` + PATCH handler + `sessionService.updateMemoryInjectionDisabled`(`updateFields`)全链路;`HarnessService.loadMemories` 读到标志为 1 时短路返回 null(`context.memories=null`,长期记忆节自然不渲染),生效口径为"下一次执行"、运行中不回灌;`SessionVO.memoryInjectionDisabled` 透出。
- `MemoryHint` 增 `id`(`memory.service.ts listForInjection` 透传,bullet 文本与注入行为零变化),供上下文页签记忆条目与设置页一一对应。
- `read_file` 截断分支结果补顶层 `truncated: true` 字段(与 grep/glob/open_web_page/shell_session 口径对齐,输出 schema 同步);`AgentLoop.processToolResult` 落库前嗅探结果 JSON 顶层 `truncated===true`,写消息 metadata(`resultTruncated`,与 approvalMark 同走合并通道、互不覆盖)并经执行层 meta 通道带回,`ws tool_call_result` payload 增 `result_truncated`(实时事件走 meta、历史回放走 metadata 双通道各管一半)。

- 新增会话只读分享:`session_share` 表(V136)与属主端点 `POST/GET/PUT/DELETE /v1/sessions/:id/share`(一会话一活跃链接,创建幂等,刷新水位可回落,撤销立即失效)。`GET /v1/share/:token` 按水位截断返回消息(服务端去掉 thinking),仅首页计入访问并记审计。
- 属主可 `GET /v1/sessions/:id/export/markdown` 下载含任务目标、最终结论、关键步骤、文件变更的 Markdown;步骤超过 500 条截断,正文超过 5MB 返回 413。文件名按 Unicode 码点截断,含 emoji 的会话标题导出不再 500。
- 管理后台「分享」开关 `share.tokenLinksEnabled`(默认关闭)。开启后可生成带过期时间的匿名链接 `GET /v1/share/public/:token`(免登录,关闭、失效与服务内部故障均统一 404)。
---

### 管理后台

- 系统设置目录增加「分享」分组,用于开关匿名分享链接。

---

## 0.0.238 (2026-10-06)

### 前端(桌面 / Web / 安卓)

- 边路任务支持从边路会话 fork(任意深度):边路任务窗口(含深层)具备与主会话完全一致的入口——每轮助手回复的「Fork 到边路任务」按钮(支持按轮切点)、窗口右下角「+ 边路任务」、占位 Tab 的上下文继承单选。从边路任务发起时新边路任务的父会话即该边路会话,「边路的边路」可继续再分叉、不做层级限制;占位继承单选文案按来源动态化(来源为边路时显示「来源会话摘要 / Fork 来源会话」),模型与权限级别的缺省值跟随来源边路会话(来源改过这两项时不再回落主会话)。
- 右侧检查器「边路任务」列表平铺展示主会话全部后代边路任务(含深层,不树形缩进),打开 / 重命名 / 删除 / 提升对深层任务全部生效,深层任务状态变化实时刷新;任务列表页主会话卡片的执行中 / 未读徽标统计口径同步调整为计入全部深层后代(刷新首刷与实时推送一致)。搜索命中深层边路会话时,点击结果跳转到其根主会话并打开该边路 Tab(此前深层任务不可搜索)。深层边路任务的 Tab 刷新后不自动重建(维持直接子级口径),从检查器列表重新打开。
- 边路任务提升为主会话放宽限制:名下仅有边路任务子会话时允许提升,子树自然挂到新主会话名下;存在子代理子会话时仍拒绝。
- 边路任务新建页支持 Fork 预览:选中「Fork 主会话 / Fork 来源会话」时,占位 Tab 的消息区立即按同一口径画出发出首条消息后这条边路会话会得到的历史消息(含按轮分叉的切点),不用先发消息再看结果。预览走与真实 fork 完全一致的轮次分页(默认最近 5 轮、滚到顶部继续往上翻),消息区顶部有提示条说明「仅为预览」。预览只存在本地、不写任何会话缓存——会话转正 / 切回其他继承方式 / Tab 卸载时立即清空,真实消息改由发送成功后从 REST 拉取,两侧不会双份上屏。按轮分叉的切点变化会重新拉取,在途的旧响应不会覆盖新切点。

### 后端

- `GET /v1/sessions/:id/side-tasks` 新增 `?recursive=1`:按根主会话递归平铺全部后代边路任务(`updated_at` 降序),VO 新增 `parentSessionId` / `permissionLevel`;缺省行为不变(直接子级)。归档子树整体隐藏,父链断开的孤儿不可达。
- `session_tree_status` 树信号收敛到根主会话:任意层边路任务的状态变化统一在根主会话上聚合并发布唯一信号(含全部后代的运行 / 未读 / 待审批 / 待提问计数),深层任务变化不再丢失;`GET /v1/sessions` 等列表接口的 tree* 首刷字段同步改为全后代口径。
- 消息搜索候选放宽:边路会话不再要求父会话为主会话(父未删除即可),深层边路任务可被搜索;搜索结果新增 `rootSessionId`(根主会话 id),根不可达的孤儿候选剔除。
- 边路任务提升校验放宽:仅存在子代理(SUBAGENT)子会话时拒绝提升,SIDE_TASK 子树放行(子树零改动跟随新主会话)。
- WS 创建边路会话链路对「父会话为边路」补齐回归测试(fork 全量 / 按轮切点 / 字段继承 / LOCAL 在线检查);创建 / fork 逻辑零改动(本就按父会话抽象)。
- 新增 `GET /v1/sessions/:id/fork-preview`(登录用户,仅本人会话,只读):边路任务发出前预演「这条边路会话会得到哪些历史消息」。参数 `forkFromMessageId`(切点 = 被点击那一轮的助手最终回复,不传 = 全量)与 `roundLimit` / `beforeMessageId`,返回与 `GET /sessions/:id/messages` 完全相同的 VO 形状(`messages` / `hasMore` / `nextBeforeMessageId` / `compactionEvents`),前端可直接复用同一套轮次折叠渲染与翻页逻辑。内部复刻 fork 落库口径:带切点时「用户消息起点」与「取数范围」两个查询都改用含该切点上界的版本,否则按轮分叉的来源轮次整轮查不到、预览会比真实结果少一轮;向上翻页的上界取「翻页游标」与「切点」的较小值,不会越过切点把更晚的会话内容泄进预览;切点落在压缩边界之后才回传该标记(与落库侧「边界不映射则整体不复制压缩状态」一致)。切点不属于本会话 / 非法时返回 `MESSAGE_NOT_FOUND` / `PARAM_INVALID`,不会退化成全量预览。
- 修复后端重启、任务自动恢复续跑后,前端进度永久卡住(转圈/步骤不再推进)的问题:崩溃恢复与通道入站的执行会换新 executionId,但 `session_status RUNNING` 与订阅回执 `session_snapshot` 都不带它,前端陈旧帧过滤把恢复执行的全部流式帧当作旧执行帧静默丢弃。现执行启动即把当前 executionId 登记到 WS registry(终态清除),RUNNING 通知与订阅快照均带上新 executionId,前端重绑后进度正常推进;重连早于/晚于恢复启动两种时序均覆盖,边路任务与通道会话同样生效。
- 修复恢复续跑中的任务「停止」按钮失效:恢复执行的取消标志注册在 AgentLoop 而非 WS handler 簿记,`cancel` 误入 pendingCancels 分支并未真正中止执行(DB 已标「已取消」但任务照跑)。现识别 AgentLoop 上的在途取消标志并走中止路径,停止对恢复执行、通道入站执行同样生效。
- 后台子代理完成通知落库后新增 `assistant_message_saved` WS 广播(带 messageId / content / metadata / status),并列入关键帧通道(弱网下不因增量帧队列满而丢弃)。此前通知只写库不推帧,前端只有在发送、会话终态或切会话触发 REST 重拉时才看得到,盯屏期间完全无感知。

### 前端(桌面 / Web / 安卓)

- 修复后台子代理完成通知「只有刷新页面后才出现」的问题:子代理结束时服务端只往父会话写了一条消息,没有任何实时帧,而消息列表只在发送 / 会话终态 / 切会话时才重拉历史,因此一直盯着会话看永远看不到「后台子代理已完成 · 点击查看详情」卡片,刷新后才从 REST 历史里补出来。现消费新的 `assistant_message_saved` 帧实时插入该气泡(metadata 解析按消息行同口径,同 messageId 去重,metadata 损坏或无 id 时安全跳过),卡片即时出现且刷新后不重复。

---

## 0.0.237 (2026-10-05)

### 后端

- 新增 `POST /v1/shared-agents/:agentId/fix-deps`(登录即可,安装目标为操作者本人):用户技能复制安装走既有暂存交换机制(校验链对齐 bundle 导入),MCP 按操作者 `mcp:write` 分类(dangling / will-enable / needs-admin)。
- 新增 bundle registry 只读端点 `GET /v1/agent-bundle/registry/:agentId`(默认关闭,系统设置显式开启;可选访问 token 非常量时间比较;关闭/停用/未授权一律 404 不暴露存在性;用户技能按 `name@userId` 全量内联;附 `X-Mao-Content-Hash` 内容指纹头;开启即意味着内网可读资产内容,全量审计)。
- 新增 `POST /v1/agent-bundle/import-from-url`(`agent:write`):服务端拉取远端 bundle(http/https、10s 超时、20MB 上限、禁用重定向)后完全复用文件导入两段式;落库写入导入来源(source_url + contentHash + systemPrompt 快照,V134 `agent_import_origin` 表)。
- 新增 `POST /v1/agent-bundle/check-updates`(`agent:write`):批量比对远端 contentHash 与导入时快照(`changed`),并按 systemPrompt 快照检测本地漂移(`localEdited`);双来源(导入来源 / 共享条目 source_url)不一致时条目优先;应用更新 = 重新导入生成新副本,永不覆盖旧 Agent。
- 新增 `mao-skill-bundle` v1 技能独立搬运格式:`GET /v1/skill-bundles/:name`(系统技能 `skill:read`;`?owner=` 导出用户技能)与 `POST /v1/skill-bundle/import`(`skill:write`,两段式写系统技能目录,同名不覆盖);`skills[].sourceUrl` 为预留格式位,导入端忽略、旧 bundle 零影响。
- 上架接口 `PUT /v1/agents/:id/shared-entry` 支持可选 `sourceUrl`(远端 registry URL,V134 加列)。
- 同一用户存在多个 frontmatter 同名技能目录时,用户技能查看/删除按名寻址返回 409 并列全部候选目录(此前会静默命中第一个目录,删除即误删无关技能且误报成功);查看/删除接口新增可选 `folder` 参数按目录精确寻址,桌面与管理后台按列表行的「路径」自动透传,`mao skill get/delete` 同步支持 `--folder`,依赖补装的占用指引同步改为目录级双标识并附重名告警;registry 内联 token 生成、fix-deps 补装等技能名处理同步按 Set 去重(历史重复 skillNames 不再导致 registry 导出 409 与矛盾报告)。
- 新增开放接口域 `backend-ts/src/openapi/` 与 V133 迁移(`api_token` / `webhook_trigger` / `outbound_subscription` / `outbound_delivery` 四表,`message_queue` 增 `source_type` / `open_trigger_id` 两列)。
- 新增 P1 REST 触发端点:`POST /api/v1/open/agents/:agentId/run`(Bearer `mao_` 前缀 API Token 鉴权,scope `open:run`),以 202 异步语义把消息投递到指定 Agent(可选复用 `sessionId`),会话忙时自动进入待发送队列;每次请求按 Token 维度限流 60 次/分钟(超限返回 429 + `Retry-After`)。Token 仅创建时展示一次明文(sha256 存库),每人上限 20 个,默认 90 天过期。
- 新增 P2 入站 Webhook 触发器:用户自建 Agent 可绑定 `POST /api/v1/open/hooks/:pathToken` 公开 URL,外部系统带 `X-Mao-Timestamp` + `X-Mao-Signature`(HMAC-SHA256,±300s 时间窗,原始请求体参与签名)调用即触发一次运行;触发密钥仅创建/轮换时展示一次;不存在 / 已停用 / 验签失败统一返回 404 固定短语(不泄露资源是否存在)。连续执行失败 5 次自动停用并在收件箱推送「触发器停用」通知,避免故障外部系统反复触发。
- 新增 P3 出站事件订阅:任务完成 / 失败(`task.completed` / `task.failed`)与提问待答(`question.pending`)三类事件可推送到自建 HTTPS 端点,请求带事件签名头;投递记录可查询,失败按 1 / 5 / 25 分钟指数退避重试,最终失败保留记录不无限重投。
- 消息队列来源透传扩容:`source_type` 由 `MANUAL` / `SCHEDULED` 扩展为可区分 `WEBHOOK` / `API`,排队消息被消费或执行收敛到终态时,收件箱条目按来源打「定时任务 / Webhook / API」徽标;定时任务与会话锁逻辑复用同一实现(`withSessionLock` 导出共享),不再分叉。
- 收件箱新增第六类事件 `TRIGGER_DISABLED`(触发器连续失败自动停用),桌面端抽屉新增对应图标与「Webhook / API」来源徽标。
- 开放接口全部写入管理 API 审计(`/v1/open*` 前缀);Token / 触发器 / 订阅管理接口仅接受登录 JWT,且只能管理本人资源。

### 前端(桌面 / Web / 安卓)

- 工作台「团队共享」分区支持依赖一键补装:条目缺技能 / MCP 异常时显示「修复依赖」按钮,缺失的用户技能自动安装到本人名下(同名多归属、属主已删除、名称非法逐项标注不中断),MCP 停用提示需管理员启用;完成后自检徽标实时消失,无需人工到设置页上传技能。
- 新任务配置的智能体选择器顶部新增「团队共享」分区:展示管理员上架的 Agent(按排序置顶),卡片带推荐语与高亮标识;存在缺失依赖时显示警告角标,悬停提示缺哪些技能、哪些 MCP 停用/不存在。缺依赖的共享 Agent 仍可选中(依赖缺失只降级能力,不阻断建会话);依赖安装/启用后角标自动消失。
- 设置页新增「开放接口」分区(三张卡片):API Token 管理(创建/吊销,明文令牌仅展示一次)、入站 Webhook 触发器(创建/启停/轮换密钥,展示可直接复制给外部系统的 URL)、出站订阅(按事件配置目标 URL、查看最近投递记录)。
- 任务收件箱新增「触发器停用」类型图标;任务完成 / 失败条目新增「Webhook / API」来源徽标(原仅「定时任务」)。
- 优化顶栏收件箱红点:此前 `el-badge` 默认度量(18px 高 + 两侧内边距 + 1px 同色描边)把「1」撑成 18.7×18 的横向椭圆,相对 16px 消息图标偏大;现按图标尺度重定义为 16px 正圆、10px 字号、全圆角,停在图标右上角外侧不再压笔画。
- 顶栏终端图标放大一档:原图形为 18×14 的横向矩形,在 24 格 viewBox 里比同排方形图标矮一截,观感明显小一圈;现放大图形本身至 20×17 补上高度差,盒子保持与邻居一致的 16px(实际墨迹约 14.3×12.3),与相邻图标协调。
- 修复顶栏站内信未读徽标提前消失:点开收件箱再关闭、重开后点一条未读消息,徽标由 2 直接归零(预期 2→1)。原因是服务端置已读后已通过 WS 广播权威未读数,本地又按条目 `-1` 递减一次,同一条变更被计两遍;删除条目同理。现置已读 / 删除成功后一律重拉服务端权威未读数,不再本地递减(也一并修掉「条目不在当前页时未读数不更新」的问题)。
- 顶栏站内信图标由 `Message`(信封)改为 `ChatLineSquare`:原信封图形宽高比 1.27,16px 下墨迹仅 14×11,高度比同排图标矮 2px;该图标无法只补高度(等比放大到高 13 需宽 15.9,会超出邻居的 12~13.7 区间),故改用同为「消息列表」语义、墨迹 13×12.5 可对齐的方形图标。

### 管理后台

- Agent 管理页新增「导入」按钮与导入向导:选择 bundle JSON → 预检报告(导入后名称、systemPrompt 全文折叠展示、经验/推荐问题计数、技能动作表、MCP 动作表含完整定义、警告清单)→ 确认导入并刷新列表。
- Agent 行内操作新增「导出」:导出向导列出该 Agent 全部技能,系统技能标「引用(不可内联)」,用户技能按所有者复选内联(同名多候选并列展示),确认后带 `?inlineSkills=` 下载 bundle 文件。
- Agent 行内操作新增「上架 / 推荐语 / 下架」:上架填写推荐语(≤512 字)与排序数字,已上架行显示「推荐语」入口并可编辑或下架;列表实时显示各 Agent 的团队共享状态。
- Agent 导入对话框新增「从 URL 导入」页签:粘贴 registry URL 拉取预检 → 确认导入,与文件导入共用预检报告。
- Agent 列表新增「检查更新」:批量拉取远端状态,逐项展示远端变更 / 本地漂移 / 错误,有更新的条目可一键预填 URL 重新导入;列表行对有更新的条目显示「远端有更新」角标。
- 团队共享上架对话框支持配置远端来源 URL(可选)。
- 系统设置新增「Agent 资产」分组:Bundle registry 开关与访问 token(保存后掩码回显尾 4 位)。

### 终端 CLI(mao-cli)

- `mao agent` 新增 `export <id> [--inline-skills name[@userId],...] [-o <文件>]` 与 `import <文件> [--confirm]`:导出落盘 bundle JSON(缺省文件名 `mao-agent-bundle-<名称>-v1.json`);导入缺省输出人类可读预检报告,`--confirm` 落库并显示最终名称与 agentId,均需管理员凭据。同步更新 `reference/agent.md`(含团队共享目录 REST 说明)。
- `mao skill get/delete` 新增 `--folder <绝对路径>`:同一用户存在多个 frontmatter 同名技能目录时按名寻址会 409,带上列表行的「路径」才精确寻址。同步更新 `reference/skill.md` 与 `business_process.md`。
- 新增格式契约文档 `docs/guides/agent-bundle-format.md`:字段定义、脱敏规则(env 全量脱敏、HTTP url 不脱敏及其理由)、版本演进策略与导入语义边界,供社区跨实例交换。

## 0.0.236 (2026-10-04)

### 前端(桌面 / Web / 安卓)

- 「我的记忆」设置页与侧边导航文案更名「长期记忆」,页面描述同步精简(原描述与标题重复「长期记忆」字样)。
- 修复安卓窄屏(≤640px)下「新增记忆」按钮被拉伸为通栏宽度的问题:移动端工具栏改为纵向堆叠时按钮保持内容宽度并靠右对齐,与桌面端布局一致。
- 顶栏「左侧面板 / 右侧面板 / 终端」三个图标(手绘 SVG)线条比相邻图标粗:其描边宽 2(16px 下约 1.33px),Element Plus 图标等效线宽为 1.5/24(16px 下 1px);统一降为 1.5,粗细对齐。
- 顶栏右侧图标顺序调整为:右侧面板、搜索、站内信(原站内信在最左)。
- 顶栏「终端」图标由裸 `>_` 改为带横向圆角矩形外框的「终端窗口」样式(矩形框 + `>_`)。
- 待发送消息队列支持拖拽排序:每条消息左侧新增拖拽块,按住拖动即可调整发送顺序(原「上移 / 下移」按钮移除);排序协议由逐条上移/下移改为一次移动到目标位置,服务端事务内锁定整队列并重排 sort_order。

### 后端

- 新增 Agent 资产化「Bundle 导出/导入」(P1):`GET /api/v1/agents/:id/bundle?inlineSkills=name[@userId],...`(需 `agent:write`)将 Agent 主体、经验(含停用项及其启用状态)、推荐问题、当前生效提示词、configJson、技能引用与 MCP 定义打包为 `mao-agent-bundle` v1 JSON 下载;MCP 环境变量解密后**全量脱敏**为 `$MAO_REDACTED`(零密钥出包),解密失败直接报错不降级;用户技能可内联(同名多归属用 `name@userId` 消歧,系统技能仅引用),内联文本总量上限 10MB。`POST /api/v1/agent-bundle/import` 两段式导入:`confirm=false` 返回预检报告(名称冲突后缀、systemPrompt 全文、技能/MCP 逐项动作与警告),`confirm=true` 服务端重新执行全部校验后落库——名称冲突自动加「 副本」后缀且不覆盖现有 Agent、字段级校验对齐现有服务常量(经验 ≤300 字超限跳过、推荐问题 ≤5 条 ×100 字、MCP 名称合法性)、MCP 一律以停用态新建(同名跳过且不绑定,env 键保留值置空待管理员补填)、内联技能经校验与暂存交换原子写入系统技能目录(同名不覆盖)、导入后 `enabled=1`、`isDefault=0`、`defaultModelId=null`、起提示词版本 v1。
- 新增团队共享目录(P2):迁移 `shared_agent_entry`(V132);`GET /v1/shared-agents` 登录即可获取条目列表与**按当前用户实时计算**的依赖自检(`missingSkills`=系统技能与本人用户技能都缺、`mcpIssues` 形如 `context7(已停用)`);`PUT/DELETE /v1/agents/:id/shared-entry`(`agent:write`)上架/更新推荐语(≤512 字)与排序、下架;停用 Agent 不能上架,重复上架即更新;Agent 删除时条目服务层级联删除,停用时条目保留、重新启用即恢复展示。

### 管理后台

- Agent 管理页新增「导入」按钮与导入向导:选择 bundle JSON → 预检报告(导入后名称、systemPrompt 全文折叠展示、经验/推荐问题计数、技能动作表、MCP 动作表含完整定义、警告清单)→ 确认导入并刷新列表。
- Agent 行内操作新增「导出」:导出向导列出该 Agent 全部技能,系统技能标「引用(不可内联)」,用户技能按所有者复选内联(同名多候选并列展示),确认后带 `?inlineSkills=` 下载 bundle 文件。
- Agent 行内操作新增「上架 / 推荐语 / 下架」:上架填写推荐语(≤512 字)与排序数字,已上架行显示「推荐语」入口并可编辑或下架;列表实时显示各 Agent 的团队共享状态。

### 前端(桌面 / Web / 安卓)

- 新任务配置的智能体选择器顶部新增「团队共享」分区:展示管理员上架的 Agent(按排序置顶),卡片带推荐语与高亮标识;存在缺失依赖时显示警告角标,悬停提示缺哪些技能、哪些 MCP 停用/不存在。缺依赖的共享 Agent 仍可选中(依赖缺失只降级能力,不阻断建会话);依赖安装/启用后角标自动消失。

### 终端 CLI(mao-cli)

- `mao agent` 新增 `export <id> [--inline-skills name[@userId],...] [-o <文件>]` 与 `import <文件> [--confirm]`:导出落盘 bundle JSON(缺省文件名 `mao-agent-bundle-<名称>-v1.json`);导入缺省输出人类可读预检报告,`--confirm` 落库并显示最终名称与 agentId,均需管理员凭据。同步更新 `reference/agent.md`(含团队共享目录 REST 说明)。
- 新增格式契约文档 `docs/guides/agent-bundle-format.md`:字段定义、脱敏规则(env 全量脱敏、HTTP url 不脱敏及其理由)、版本演进策略与导入语义边界,供社区跨实例交换。

## 0.0.235 (2026-10-04)

### 前端(桌面 / Web / 安卓)

- 客户端打开 / 刷新后恢复「最后激活的中心 Tab」:上次停在边路任务或子代理 Tab 时,冷启动(含安卓 WebView 被回收后重开)会自动还原父会话并直接切回那个 Tab,不再停留在主会话;文件 / diff Tab 的激活态同样被记住。激活态按会话维度持久化,仅在目标 Tab 已重建出来时生效,用户在本次会话内的手动切换不会被覆盖;登出 / 换号、删除会话时随之清除。
- 新增「任务收件箱」(站内通知中心):顶栏消息图标实时显示权威未读数,点击展开收件箱抽屉,聚合五类事件——任务完成 / 任务失败、`ask_user_questions` 提问待答、LOCAL 审批待办、子代理结果回传。条目带类型图标、标题、摘要与相对时间;定时任务产生的条目带「定时任务」徽标,子代理条目按终态显示「已完成 / 执行失败 / 已取消」。点击条目即标记已读并跳转关联会话(提问/审批跳转后会话内对应面板自然呈现);关联会话已被删除时降级为提示,不再白屏。支持只看未读过滤、加载更多与全部已读。抽屉宽度随视口自适应(`min(420px, 100vw - 32px)`),安卓窄屏下不再被裁切。
- 修复顶栏收件箱图标与相邻图标未垂直对齐的问题:`el-badge` 为 inline-block,其由正文字号 × 行高撑起的行盒会把 16px 图标顶出点击区中心(视觉上偏高约 2.5px)。现将点击区与内部 badge 统一为居中弹性布局,图标与其它顶栏图标严格对齐。
- 顶栏收件箱图标由「铃铛」改为「消息」:铃铛与任务面板「聚焦模式」切换按钮重复,两个语义不同的功能共用同一图形易混淆;聚焦模式的铃铛保持不变。
- 提问被回答 / 取消 / 超时、审批被批准 / 拒绝 / 超时 / 断连后,对应收件箱条目自动置为已读(保留可查,不再计入未读徽标)。
- 新增「消息通知 → 站内收件箱」设置分区:任务完成、提问待答、审批待办、子代理完成四类开关(子代理完成默认关闭,防并行子代理刷屏),独立保存,不受上方 Webhook 配置门禁影响;关闭某类后该事件不再写入收件箱,但不影响 IM / Webhook 推送。
- Electron 桌面端新增系统通知:窗口未聚焦 / 最小化时把新增收件箱条目弹为系统通知,点击即聚焦并跳转关联会话;未授予通知权限时静默降级为站内徽标。Web 与安卓不弹系统通知。

### 后端

- 新增 `notification` / `user_inbox_preference` 表(V131 迁移)与 `src/inbox/` 领域:`/api/v1/inbox` 列表、`/unread-count`、`/:id/read`、`/read-all`、`DELETE /:id`、`GET|PUT /preferences` 七端点,全部登录态且按 user_id 隔离。
- 五类事件写入点:会话相位收敛到 COMPLETED / FAILED 的主会话(排除子代理会话、边路任务、微信 / 飞书通道、取消态)、`ask_user_questions` 派发、LOCAL 审批注册、后台子代理结果回传(覆盖完成 / 失败 / 取消三种终态,结果被抑制的分支不写)。写入按 `dedup_key` 幂等(`ON DUPLICATE KEY UPDATE id = id`),重试、取消后重发、排队消费不会产生重复条目。
- 权威未读数:每次写入 / 已读 / 删除后由服务端重新 COUNT,并经 WS `inbox_updated` 帧广播给该用户全部在线连接;事件已列入关键帧通道,弱网下仍有重连重拉与打开抽屉重拉双兜底。
- 定时任务来源透传:定时执行完成时条目 payload 带 `source: 'SCHEDULED'`,前端据此显示「定时任务」徽标。
- 保留 90 天自动清理(每小时一次的定时调度,已读未读一并清理)。

## 0.0.234 (2026-10-02)

### 前端(桌面 / Web / 安卓)

- 新增「我的记忆」设置页(`/settings/memory`):跨会话长期记忆的用户入口。支持新增 / 编辑 / 删除记忆条目,条目分「用户级」(跨项目生效)与「项目级」(绑定项目,仅该项目会话中注入);可忽略(不再注入且不被自动抽取复活)与恢复;顶部提供「任务完成后自动收集记忆」全局开关(**默认关闭**,需手动开启,关闭状态下现有功能零影响);来源徽标区分「自动抽取 / 手动添加」。安卓与 Web 同源远程加载,自动获得。

### 后端

- 新增跨会话长期记忆(Memory 层):新增 `memory_item`、`user_memory_preference` 表(V129/V130 迁移)与 `src/memory/` 领域。
- 会话启动注入:系统提示词在「最佳实践经验」之后新增「长期记忆」段落,按 `updated_at` 倒序注入用户级 8 条 + 项目级 12 条;查询失败仅降级不阻断会话;全渠道(Web/桌面/LOCAL/钉钉/微信/飞书/定时任务)生效。
- 任务收尾自动抽取:主会话任务收敛到 COMPLETED 后异步调用轻量 LLM 抽取「值得记住的长期事实」(最后一轮 USER 消息 + ASSISTANT 答复为输入,规范化后不足 20 字短路)。去重落库:抽取时把已知的生效中自动记忆与已忽略内容一并交给模型判定,同一事实输出「再确认」(仅刷新排序)或「内容更新」(新表述更完整时改写),只有判定为全新事实才插入;SHA-1 规范化哈希保留作精确前置,已忽略条目不复活,每用户生效中上限 200 条。自动收集默认关闭,未开启的用户不做任何抽取调用,抽取全程异常只记日志,绝不影响任务完成事件链。
- 记忆容量策略:生效中记忆满 200 条后不再停止学习——自动抽取到新事实时,优先将受影响分组(用户级 / 对应项目)内最旧的「自动抽取」记忆软降级为已忽略(保留、可恢复)腾出名额;该分组没有可淘汰的自动记忆时才放弃该条。手动新增不受影响,仍显式提示清理。
- 手工管理 API:`/api/v1/memory` 列表 / 新增 / 编辑 / 删除、`/api/v1/memory/settings` 自动收集开关、`/api/v1/memory/projects` 项目下拉数据源;全部登录态且个人归属隔离,越权与不存在一律按不存在处理。
- 管理后台只读审计:新增 `memory:read` 权限码与 `GET /api/v1/admin/memory` 端点、「用户记忆」审计页(按用户 ID 查询,仅查看)。
- 新增系统设置项 `memory.extractionModelId`(记忆自动抽取模型,留空回落默认模型),系统设置页渲染为模型下拉。

## 0.0.233 (2026-10-02)

### 前端(桌面 / Web / 安卓)

- 修复会话被其他端(飞书 / 微信 / 另一台设备)占用时,桌面端发送被服务端拒绝、但拒绝提示被前端当作过期流事件丢弃的问题:表现为「发送成功但没有任何反应」,消息既未执行也未入库、无任何报错。现在拒绝帧直达调用方,正确提示「该任务仍在运行,请先停止当前执行后再继续」。

### 后端

- 修复 grep_search 工具在装有 ripgrep 的服务器上把进程失败当成「无匹配成功」的问题:非法正则、搜索目标消失会静默返回 0 命中(模型据此得出错误结论继续推进),超过 30 秒或缓冲上限被杀时只返回部分结果且不标记截断。现在失败明确报错(与无 rg 机器的行为对齐),被中途杀死且有部分结果时标记 truncated。
- 修复 Responses 协议网关下工具调用参数被拼接两遍的问题:当流式 `output_item.added` 不带 call_id 而 `output_item.done` 带规范 call_id 时(网关桥接形态),完整参数会在增量之后再发一遍,拼成非法 JSON 导致该轮工具必然失败。现在 done 判重同时检查两个候选键。
- 修复待办工具状态校验缺口:task_update 传空字符串 status 会把空串写入数据库,该任务从此从统计与完成判定中消失;task_create 对 status 没有白名单,任意字符串直接入库。现在空串视为未提供,非法值明确报错。
- 修复钉钉 / 飞书入站文件落盘的文件名碰撞缺口:系统派生的「唯一文件名」本身已被占用时仍会覆盖已有文件,历史消息的附件引用被篡改。现在派生名继续递增避让,保证不覆盖任何已有文件。

### 终端 CLI(mao-agent)

- 修复 `resume` 无 prompt 进入 REPL 等待占用方执行时,`--max-duration` 到期会把占用方(桌面端 / 另一终端)正在跑的任务取消的问题。现在超时只结束本地等待,与帮助文本「不 cancel 对方」的承诺一致。
- 修复多行草稿首行为空、光标在行首时 Ctrl+U 会把首字符复制进草稿(越删越长)、Home 反而向右跳一格的问题。

## 0.0.232 (2026-10-02)

### 前端(桌面 / Web / 安卓)

- 设置页「我的指令」列表每条指令新增「复制指令内容」按钮(位于编辑图标旁),一键复制指令正文到剪贴板,成功/失败均有提示。复制走统一 clipboard 工具,http 非安全上下文访问时自动回退 execCommand,安卓与 Web 同源生效。

## 0.0.231 (2026-10-02)

### 后端

- 修复飞书进度卡片在「纯工具调用轮」上看不到上一轮正文的问题:Agent 常把登录链接写在 assistant content 里,再跳到下一轮用 shell 轮询登录状态,此时卡片只显示本轮工具、正文为空,用户看不到登录链接、任务看起来卡住。现在本轮没有 assistant content 时,卡片回退展示上一轮最近一条 content(连同本轮工具一起);一旦新一轮产出自己的 content 就立即替换,不再沿用更早的。钉钉进度卡走同一份监听器,同样生效。

## 0.0.230 (2026-10-01)

### 后端

- 修复 LOCAL 模式会话在桌面客户端未连接时发送消息会导致该会话被永久卡死的问题:发送在「本地客户端未连接」出口早退时只回补了自动消费的消息,手动发送的会话执行占位没有被释放,此后该会话的所有发送都被「该任务仍在运行」拒绝,只能重启后端恢复。现在手动发送在该出口同样释放占位,桌面客户端重连后即可正常继续对话。

## 0.0.229 (2026-10-01)

### 前端(桌面 / Web / 安卓)

- 修复任务侧栏样式(`task-index-panel.css`)非 scoped 全局引入导致的通用类名污染:命令审批「拒绝 / 执行」按钮被压成 22px 竖条、微信 Bot 设置页「解绑」按钮文字溢出、聊天斜杠命令与 @ 文件引用下拉的空态/加载态被撑高、顶栏与文件查看器刷新按钮被注入固定尺寸。全部选择器现收敛在任务侧栏根节点作用域内,不再影响其他页面。

### 后端

- 修复后台子代理「重试」走 WS 路径时未做子代理工具裁剪的问题:重试经 buildRetryContext 构建与后台执行/崩溃恢复一致的裁剪上下文,不再把 `spawn_subagent`(防无人消费的嵌套任务)与 `ask_user_questions`(防长时间挂起)等被排除工具重新交回子代理。
- 修复「最后管理员保护」检查与写入不原子的问题:并发禁用最后两名管理员时两个请求可能互相放行,系统进入零管理员状态。禁用状态写入并入 FOR UPDATE 守卫事务(`updateUserStatusWithAdminGuard`),用户状态取值收紧为 0/1/null(`updateUser` 与 `updateUserStatus` 两个入口)。
- 修复管理员自助「启用」自己被误拒的问题:不能禁用当前登录用户的守卫只对禁用(status=0)生效,启用 / 清空自身状态是合法自助操作(SSO / 外部账号默认 status=null 时可达)。
- 修复插队消费(insert_message)在删除队列行之后异常被静默吞掉的问题:现在会删除已落库未执行的孤儿 USER 消息、按需回补队首(透传定时任务来源),失败记录日志,与自动消费路径的补偿语义对齐。
- 修复取消「排队中」的执行后立即重发时,旧执行体迟到开跑会覆盖 CANCELLED、并删掉新执行 claim/取消标志的问题:执行入口增加取消复查,finally 的簿记回收改为按归属判定。取消在排队窗口内到达(执行体尚未开跑)时同样收敛——排队的下一条消息照常被自动消费执行,定时任务入队的消息不会永远卡在 QUEUED。
- 修复 `open_web_page` 截断全文落盘文件名只取 URL 路径末段的问题:同会话内同名末段的不同网页互相覆盖,模型按路径回读会拿到别的网页内容。现在文件名并入 URL 短哈希——同一 URL 重复抓取仍覆盖同一文件,不同 URL 互不覆盖。
- 修复钉钉 / 飞书入站文件按原始文件名落盘导致同日同名文件互相覆盖的问题:命中时改用带 messageId 的唯一名,历史消息中的文件引用不再被后到的同名文件篡改。
- 修复工具调用流式合并对「无 id、仅 index」分片整段丢失的问题:部分 OpenAI 兼容网关不回传 id 时,并行工具调用现在能正确建立与执行。

## 0.0.228 (2026-10-01)

### 后端

- 修复 OpenAI Responses 协议(`openai-responses`)在部分不支持 `reasoning.encrypted_content` 的网关(sub2api 类)上多轮工具调用必然失败的问题:适配器此前会把历史中「只有 `id`、没有密文」的 reasoning 项原样回传,而该 `id` 在网关侧是服务端续接引用,网关据此查存储——`store:false` 下无存储,于是整轮返回 `409 continuation_unavailable`(文案为 "continuation history is unavailable; send complete input history when store is false",但客户端发的本就是完整历史)。现改为**仅在携带 `encrypted_content` 时才回传 reasoning 项**,无密文时整项省略,退化为不含 reasoning 的无状态请求;密文齐全的网关行为不变。同类网关的 `deepseek-v4.1-flash` 此前需改用 chat completions 协议规避,升级后可直接使用 `openai-responses`。

## 0.0.227 (2026-09-30)

### 前端(桌面 / Web / 安卓)

- 修复微信Bot设置页「解绑」按钮文案在窄屏下被挤压换行、显示为两行竖排文字的问题:按钮改为不可压缩且文字不换行,标题过长时优先截断标题。
- 修复聊天区工具调用分组标题的字体异常:任务侧栏样式表(`task-index-panel.css`)为非 scoped 全局引入,其中的 `.group-header`(含 `text-transform: uppercase`、`font-weight: 500`)与聊天区 `.group-icon` 撞名,导致 `搜索内容`、`2次搜索内容`、`读取 文件名` 等分组标题被强制加粗、文件名被转成全大写。聊天区分组标题与图标类名改为 `tool-group-header` / `tool-group-icon`,不再受任务侧栏全局样式影响。

## 0.0.226 (2026-09-30)

### 前端(桌面 / Web / 安卓)

- 修复大文件拆分 refactor 误删 `TaskIndexPanel` / `ChatInput` 样式导致任务侧栏与输入框布局失真的问题(样式恢复为共享 CSS 并在父组件非 scoped 引入)。
- 修复 Element Plus 输入框文字(含 placeholder)过大的问题:`.el-input` / `.el-textarea` 整体字号由 17px 收敛为 13px,与页面标签、提示文字一致(消息通知 Webhook「留空则保留已配置地址」等占位文字随之变小);`size="large"` 控件保持原尺寸,各组件自有定制优先级不变。

### 后端

- 任务 webhook 通知标题调整:「Mao Agent 任务通知」按相位拆分为「Mao 任务完成通知」「Mao 任务失败通知」,「Mao Agent 提问通知」改为「Mao 提问通知」(飞书卡片头部与钉钉/回退纯文本同步生效)。

## 0.0.225 (2026-09-30)

### 后端

- `spawn_subagent` 新增可选 `title` 参数:主代理派发后台子代理时可自定义子代理会话标题(服务端 trim / 去控制字符 / 超 40 字截断,空标题自动回退到原有的「后台子代理(类型): 任务前 40 字」拼接),并行子代理在列表与完成通知中更易区分;工具调用结果摘要同步展示自定义标题。

## 0.0.224 (2026-09-30)

### 后端

- 修复飞书 / 微信 / 钉钉通道发送图片与文件工具的 URL 下载裸奔问题:新增连接(10s)/ 读取(30s)双超时与流式大小上限(超过各自上限立即断开并报错),避免对端挂起导致会话执行永久卡死且停止无效,以及超大响应体在超限检查前全量进内存导致后端 OOM。
- 微信入站消息新增认领表与失败重放:拉取到的消息先按内容指纹幂等入库(CLAIMED),处理成功置 DONE、失败置 FAILED 并在后续轮询自动重投(中断超 10 分钟的在途消息同样重放);同步游标改为在无在途消息时才推进,处理期异常或进程重启不再静默丢消息。
- 修复会话重试在线程池饱和被拒后相位滞留 RESUMING 的问题:提交被拒立即回滚为原终态(不再出现「请稍后重试」但发送/重试均被拒、且随后被孤儿巡检当作崩溃会话自动执行烧 token 的矛盾状态)。
- 修复 `open_web_page` 长网页截断时在本地(LOCAL)模式会话仍把全文写到服务端磁盘并把该路径指引给助手的问题:LOCAL 模式不再落盘、不再返回 `full_content_file`(该路径桌面端永远读不到),避免助手陷入「读不到又禁止重新抓取」的死路。
- 修复钉钉无卡片模板机器人的「正在处理」受理回执按会话永久去重的问题:改为每次任务执行发送一次(同一次执行内多次进度推送仍只发一条),同会话的后续任务恢复受理回执。

### 前端(桌面 / Web / 安卓)

- 断线可见:WS 断开后页面顶部出现「连接已断开,正在自动重连…」横幅,重连成功自动消失(此前断线无任何提示,任务看起来像卡住)。
- 断线重连更可靠:心跳检测到服务端 30 秒无响应而主动断开时,若连接迟迟未真正关闭(半开连接,常见于合盖休眠/拔网线),3 秒后强制进入重连,不再长时间假死。
- 发送前附件保护:图片/文件全部上传失败时中止发送并提示,部分失败时先弹「仍然发送 / 取消」确认,不再把缺附件的消息(甚至空消息)静默发给 Agent;入队发送路径同样生效。
- 登出/换号清理更彻底:退出登录时同时清空中心 Tab 条、待审批队列与「已关闭边路任务」标记,换号登录不再复活或误隐藏上一账号的界面状态。
- Electron 下登录令牌不再镜像进 localStorage(只存主进程 userData/auth.json),并清理历史版本残留的镜像;所有 localStorage 写入统一容错,存储不可用(如隐私模式)时不再导致登录失败或主题切换报错。
- Markdown 渲染复用解析器实例,长会话/高频流式渲染的内存分配更少。

### 管理后台

- 修复跨账号数据泄漏:退出登录现在会清空「用量分析」的页面级缓存(此前 5 分钟 TTL 内,同标签页换号登录可直接看到上一账号的用量数据)。
- 系统设置保存/刷新语义统一:公司 SSO、ECP、集成配置三个面板对齐为「本地无编辑则静默跟随服务端;有未保存编辑且服务端已被他人更新时弹『丢弃并刷新 / 保留本地编辑』确认」——修复了公司 SSO/ECP 面板在兄弟面板保存后静默丢失未保存编辑、集成配置面板已触碰字段永远停留在旧值的问题。
- 登录失败不再双重弹错误提示(拦截器与登录页各弹一次的问题);401 强制下线跳转地址改为跟随部署 BASE_URL,修复开发环境跳转 404。
- 用户详情抽屉切换用户时先清空上一用户的会话/任务/凭证等明细,加载期间不再短暂透出旧数据。
- Skills 管理的系统分支查看/删除 URL 与个人分支一样做 encodeURIComponent 编码,Skill 名含特殊字符时不再 404 或删错对象。
- 会话详情导出 JSON:下载方式对齐项目内已修复的 CSV 同款(挂 DOM 点击 + 延迟回收),修复部分浏览器下下载静默失败;导出游标循环增加同游标中断与 200 轮上限,后端游标异常时不再无限请求;调用流水导出 CSV 增加完成提示并同样修复静默失败问题。
- 定时任务启停开关增加二次确认,且确认前不再先行翻转状态(修复快速连拨竞态)。
- 模型管理 Tab 改用显式 loaded 标记:数据为空的 Tab 每次切入不再重复发请求。
- 删除 MCP 服务器、反馈分页、会话筛选项加载补齐失败兜底,不再产生未捕获的 Promise 异常;会话筛选项任一接口失败只置空对应下拉,不再连带两个下拉同时为空。
- 界面硬编码颜色统一收敛到 `--mao-*` 变量(告警红/成功绿/告警黄等),新增 `--mao-danger-bg`、`--mao-accent-ring`、`--mao-renamed`、`--mao-chip` 语义变量;表单提示小字抽为全局 `.form-hint`/`.form-hint-inline` 类;Element Plus 图标由全量约 300 个注册改为按需注册 7 个全局图标,主包体积下降。
- 角色权限页切回标签时自动刷新数据(原 keep-alive 下停留旧数据);Agent 编辑弹窗的经验文本编辑器改为模板引用 + scroll 事件监听,去掉 80ms 常驻轮询与全局 DOM 查询。

---

## 0.0.223 (2026-09-30)

### 前端(桌面 / Web / 安卓)

- 本地(LOCAL)模式权限档位从四级扩为五级:原「智能审批」改名「智能预审」(逻辑不变),新增「替我审批」档(介于智能预审与完全权限之间)——该档下文件读写自动执行,shell 命令与 MCP 工具由 AI 结合会话上下文(用户指令与近期工具调用轨迹)代替用户审批,批准直接执行、拒绝则把理由返回给助手换方案。工具卡片新增 AI 审批徽标(AI 已批准 / AI 已拒绝 / 低风险放行),悬停可见理由。

### 后端

- 新增权限级别 `PROXY`(替我审批):`shell`/`mcp__*` 调用经审批 LLM 两态拍板(批准/拒绝),拒绝在派发层短路、理由作为工具错误返回主模型;审批 LLM 失败时兜底转人工审批。上下文快照刻意最小化(仅用户消息与工具调用轨迹,不含 assistant 输出与工具结果),抑制诱导审批的攻击面。
- 新增 Jev 前置决策层(TypeSafe System One 决策模型):低风险调用直接放行、跳过审批模型推理,高风险才交审批模型,覆盖智能预审的 shell 预判与替我审批两条链路;未配置或调用异常时自动降级为直接走审批模型,审批能力不中断。
- 审批模型支持管理后台统一配置(`approval.modelId`),智能预审与替我审批共用;留空时回落会话模型。LLM 用量记账新增 `proxy_approve` 场景。

### 管理后台

- 「系统设置 → Agent 与模型」新增「审批」分组:审批模型(`approval.modelId`)、Jev 前置决策端点/模型名/API Key(`approval.jev.*`,Key 为 secret 加密存储)。注意:开启前置决策后工具名与参数会发往配置的第三方服务。
- 「点踩反馈」页布局优化:顶部汇总由多张宽卡片改为单卡片指标条(各项竖线分隔,不再拉宽显空);分页栏与表格增加间距并改为右对齐,与其他列表页一致。

### 终端 CLI(mao-agent)

- `--permission-level` 新增 `PROXY` 取值,可用「替我审批」档创建/恢复会话(审批决策在服务端完成,本地审批策略不变)。

---

## 0.0.222 (2026-09-30)

### 前端(桌面 / Web / 安卓)

- 修复文件面板「筛选文件」输入后页面陷入请求风暴、满屏「网络错误」的问题。此前筛选会把整棵文件树逐层预加载(多仓库工作区可达数千个并发目录请求),耗尽浏览器连接/内存后大量请求报 `ERR_INSUFFICIENT_RESOURCES`,聊天等请求被连带阻塞,只能刷新恢复。现改为一次搜索拿全部匹配:云端走后端新接口 `workspace-search`,本地(Electron)走有界的 IPC 遍历;自动跳过 `.git`、`node_modules` 与符号链接;匹配超过 100 条时提示「匹配较多,仅显示前 100 条」。搜索结果行内显示完整相对路径(打开页签与下载文件名仍用文件名),便于区分不同目录下的同名文件;切换会话时自动清空筛选关键词。

### 后端

- 新增 `GET /v1/files/workspace-search?sessionId=&pattern=&limit=`:按文件名递归搜索会话工作区(大小写不敏感子串匹配,仅返回文件),遍历有界(扫描目录 ≤2000、深度 ≤12、结果 ≤100),跳过 `.git`/`node_modules` 与符号链接目录,超限时返回 `truncated: true`,避免大工作区拖垮请求。

## 0.0.221 (2026-09-30)

### 前端(桌面 / Web / 安卓)

- 修复边路任务运行中刷新页面后,左侧任务列表对应任务只有蓝色圆点、没有 loading 转圈的问题:转圈此前只看本地边路任务缓存(刷新后为空,切到该任务才拉取),现同样消费服务端任务树聚合信号 `treeRunning`,刷新后立即恢复显示,与蓝色圆点口径一致。
- 修复边路任务处于 ask_user_questions(等待回答)状态时,左侧任务列表任务行圆点不变橙的问题:提问的实时记录记在边路会话 id 名下,而橙色圆点只查了主会话 id。现聚合边路任务的实时提问与边路自身 VO 计数(与聚焦排序同源),并叠加服务端 `treePendingQuestionCount` 聚合信号兜底,覆盖两类丢失场景:刷新后实时记录为空、断线期间提问事件丢失(重连后仅边路缓存被刷新)。边路 VO 计数随提问/取消/审批事件与树聚合信号保持同步,问题被回答后橙点正常熄灭不残留;待审批橙点同步补齐同样的聚合与兜底,修复刷新/断线后待审批圆点丢失的同源问题。
- 左侧任务列表新增「网页嵌入」分组:通过 Web 嵌入 SDK(`sdk/embed`)创建的会话此前全部混在「临时工作区」里,现在按所属 Agent 各自成组(组名显示 Agent 名、显示器图标,与飞书私聊分组同模式),排在「临时工作区」之后。分组与其他组一样支持重命名(别名)、折叠与拖拽排序;存量 embed 会话部署后自动归入新分组。
- 修复任务列表分组拖拽排序在向下拖动时落点与蓝色指示线不一致的问题:指示线显示插到某分组上方,松手后却落在该分组下方。原因是落点计算在移除被拖分组后仍按原目标下标插入,向下拖时目标已前移一位导致错位;现按指示线语义(插到目标分组前面)校正插入下标,向上拖动行为不变。

### 后端

- 会话分组服务新增 `EMBED:{agentId}` 桶:`/v1/sessions/groups` 与 `GET /v1/sessions?groupKey=EMBED:{agentId}` 按 `source='embed'` + 所属 Agent 过滤,组名取 Agent 名;「临时工作区」桶的 SQL 过滤同步排除 embed 会话,避免「展开更多」分页时把 embed 会话串回临时工作区。

### 管理后台

- 修复系统设置页左侧目录粘滞失效:Element Plus 卡片默认的 `overflow: hidden / auto` 把 `position: sticky` 的目录关在卡片内部,页面滚动时目录跟着滚出视口,必须滚回顶部才能看到。现仅在本页放开外层卡片的这两层 overflow,滚动右侧配置时目录始终钉在可视区左侧,滚动高亮不受影响。
- 系统设置页目录由 19 项平铺改为 6 个两级分组:登录认证(公司 SSO、ECP 飞书登录、LDAP 认证、飞书 OAuth 登录)、文件与存储(上传配置、OSS 对象存储)、Agent 与模型(Agent 运行、LLM 超时与重试、上下文压缩、会话、代码)、工具与终端(网络工具、网页抓取、Shell 会话、云端终端)、通知与消息(任务通知、微信)、平台与运维(审计、运行环境);后端新增的未声明分类自动归入「其他」。右侧卡片顺序同步按分组排列(模型绑定项「会话」「代码」不再垫底),配置项名称、保存逻辑与锚点 id 均不变。

## 0.0.220 (2026-09-29)

### 前端(桌面 / Web / 安卓)

- 修复安卓 App 里点击助手回复中的链接没有反应的问题。链接此前是普通的 `target="_blank"` 锚点,安卓 WebView 没有开启多窗口能力,`window.open` 直接失败,点击后既不开浏览器也不导航,也没有任何提示。现在所有 Markdown(含内联)渲染出的外链由前端统一接管并交给系统浏览器打开,桌面/Web 端行为不变。工作区内文件之间的相对链接、锚点跳转不受影响;如果设备上没有能打开该链接的应用,会给出明确提示而不是静默失败。

### 安卓原生

- 新增 `OpenUrl` 自研 Capacitor 插件(与 `AppUpdate` 同模式),用 `ACTION_VIEW` 把 http/https/mailto 链接交给系统浏览器或对应 App,替代不可用的 `window.open`。仅接受这三种协议,其余 scheme(如自定义 `intent://`)直接拒绝。

---

## 0.0.219 (2026-09-29)

### 前端(桌面 / Web / 安卓)

- 修复边路任务执行中无法把新消息加入待发送队列的问题。执行中发送互斥与「忙碌/loading」状态混用,点「加入队列」会被静默拦下。现在执行中输入内容点发送(或 Enter)会正常入队,本轮结束后自动接力;停止按钮与执行中提示不受影响。

- 修复会话列表「加载更多」在深链跳转后漏会话的问题。通过通知/链接打开一个不在分组预览里的会话后,再点加载更多,会把这条已打开的会话多算进偏移量,服务端对应位置的那条被整条跳过。现在偏移量按服务端已返回条数记账,不再从本地列表长度反推。
- 修复发送重复指令(例如两次「继续」)时,已分页加载的更早同内容历史消息被静默删掉的问题。内容相同不再直接当作同一条消息剔除,只有尚未落库的乐观发送才会被回显替换。
- 修复换号登录后仍会去请求上一账号「最后查看会话」的问题。登出/强制下线时会清掉本地记录的最后会话,避免登录后一次无意义的失败请求。
- 新增助手回复操作栏的「Fork 到边路任务」按钮(复制、点踩旁边)。点击效果等同于「+ 边路任务」再勾选「Fork 主会话」:直接开出一个继承主会话全部历史消息的边路任务,输入内容即可接着聊,不必再手动切继承方式。再次点击复用同一个未发送的边路任务,不会堆出一串空 Tab。
- 修复 Fork 按钮「点哪一轮都一样」的问题。此前无论点早期还是最新一轮的 Fork,边路任务拿到的都是主会话当前最后一轮的完整历史,且复制时点是「在边路任务里发出第一条消息」那一刻。现在按轮分叉真实生效:边路任务只继承被点击那一轮及其之前的全部内容,之后的一律不要。落在会话压缩边界之前的轮次也能分叉,边路任务从第一条消息起拿到原始历史(不带摘要)。分叉任务的 Tab 标题按首条消息自动生成,鼠标悬停显示来源(该轮用户消息摘录 + 时间)。若分叉后主会话被编辑重发截断了那一轮,发送时会收到明确错误,不会留下发不出去的幽灵消息。
- 修复展开执行步骤后,中间的工具调用消息也能 Fork,且切点用了该步骤自己的消息而不是这一轮最终回复的问题。Fork 只保留在已完成轮次的助手最终回复上。
- 修复后台子代理完成后,子代理的完整输出直接铺在主会话里、打断主会话阅读流的问题。完成通知现在默认只保留一行入口卡片(点击进入该子代理会话查看详情);执行失败/取消时卡片下方保留一行原因摘要,原因过长时默认折叠、可展开全文。

### 后端

- 修复飞书群图片/文件永远进不了机器人上下文的问题。群消息先落占位再异步下载回填,此前下载完成前若有 @bot 触发,上下文水位线会越过占位行,回填后的真实内容再也进不去。现在未富化完成的行不参与注入、也不推进水位线;图片下载失败保留懒加载占位,但不再卡住后续消息。
- 修复飞书混用话题的群里,非话题触发会把话题消息(或反向)从上下文里永久跳过的问题。话题上下文水位线与群级水位线分离,非话题上下文也不再混入话题消息。
- 修复管理后台批量保存系统配置时中途失败留下半截配置的问题。校验通过后的写入改为单事务,任一条失败整体回滚。
- 修复任务取消路径漏发回合结束/消息结束事件的问题。取消时统一补齐收尾事件,不再把已丢弃的本轮用量记成正常完成。

---

## 0.0.218 (2026-09-29)

### 后端

- 修复任务停止后再点「继续执行」无反应、一直提示「该任务仍在运行」的问题。取消执行时只改了数据库状态,若这次执行的模型流因连接失败等原因卡住、没能正常收尾,进程内的执行占位就不会被清理,导致该会话此后无论是继续执行、重试还是发新消息,一律被服务端拒绝(此前只能重启后端恢复)。现在取消时会主动回收这些执行占位,并且发送、重试、插队消息等入口在遇到「已取消但占位没回收」的残留状态时先自行清理再放行;真正还在执行中的会话仍照常拒绝,不会出现两个执行并发。补充了取消回收、陈旧占位自愈、在途执行仍拒绝等回归用例。
- 修复 Agent 对话内容里出现文档对快捷指令语法的字面描述时,每轮请求都刷 `Command not found for marker` 警告的问题。`#{...}#`、`#{skill_name}#` 这类写给自己看的说明文字此前会被当成真指令去查用户的快捷命令表,查不到就告警;`#{#{}}#` 这种嵌套写法则被旧正则从中间截断成一个假命令名。现在命令名不再允许包含花括号和空白,文档占位写法直接原样保留、不查表也不告警。用户真正创建的快捷命令(如 `plan_review`)展开行为不变。
- 修复云端 MCP 服务器连接挂起导致会话永远无法继续执行的问题(上述占位泄漏的上游根因)。CLOUD 会话在请求模型前会先连 MCP 拉工具清单,这条链路此前既不感知取消也没有超时:用户在这段窗口点「停止」没有任何出口,而 MCP SDK 的 `fetch` 本身没有超时、SSE 传输还会按 1s→30s 退避自动重连,一次挂起就能把整次执行无限期拖住——执行体到不了收尾逻辑,进程内的执行占位永久残留。现在连接整体有 60s 超时兜底,取消标志也会即时中断连接;取消或失败时只回收本次 server 自己的连接,不影响同会话里已连上的其它 server。

---

## 0.0.217 (2026-09-29)

### 后端

- `write_file` / `edit_file` / `read_file`、`glob_search` / `grep_search` 的搜索根统一改为宽松路径解析(`resolveLenient`):相对路径仍按当前会话工作区解析,绝对路径直达任意位置。原先只有 `read_file` 放开,写类与搜索类都锁在工作区内,模型想把产物写到工作区外只能整文件重写或先读到对话里,反而不易产生小 diff。`~` 开头路径在服务端仍然拒绝(无用户 HOME 概念)。
- 文件面板(浏览目录、读取与预览文本 / 图片 / PDF、下载、打包)同步放开:此前除 `PathSandbox.resolve()` 外还有一层 `assertRealPathInWorkspace()`(对目标做 `realpathSync` 并要求落在工作区内),工作区内的符号链接指到外部时会被「路径访问被拒绝」挡住,现已移除这层二次校验;符号链接作为直接目标仍按「不是普通文件」拒绝。

### 终端 CLI(mao-agent)

- LOCAL 模式与云端对齐:`read_file` / `write_file` / `edit_file`、`glob_search` / `grep_search` 的搜索根全部走宽松解析(相对路径按工作区、绝对路径直达任意位置),此前这三类在本地比云端更严,模型在 CLOUD 下能读 `/tmp` 与浏览器上传目录,LOCAL 下却被拦。默认拒绝清单照旧覆盖 `~/.ssh`、`/etc/passwd|shadow|sudoers` 等敏感路径;符号链接目标一律拒绝;同路径并行写仍按绝对路径串行化。`agent-cli/src/local/sandbox.ts` 的严格边界现在只服务于本地 shell 的 `workdir` 校验,`--local` 帮助文本已同步改写。

---

## 0.0.216 (2026-09-29)

### 前端(桌面 / Web / 安卓)

- 修复右侧「文件」面板刷新按钮异常变大的问题。该按钮在无障碍改造中由 `<el-icon>` 改为 `<button>` 包裹后,`font-size: 28px` 从图标自身转移到了按钮容器上:`.el-icon` 内部 SVG 仍是 28px,但外层按钮因默认 `padding: 4px` 加上文字的 line-height 总高度涨到约 43px,视觉上比输入框高出一截。现按项目其他图标按钮(如 `TopNav.vue`)的写法,按钮固定为 28×28 的方正点击区、`el-icon` 显式声明 `:size="16"`,图标与点击热区恢复正常比例,hover / disabled 态不变。

---

## 0.0.215 (2026-09-28)

### 后端

- 飞书任务完成卡片新增「👎 不满意」点踩按钮:IM 里 @机器人 派任务的用户,在结果卡片上可一键标记不满,与桌面端点踩汇入同一张 `message_feedback` 统计表。按钮是同一个 toggle:未点踩为中性灰「不满意」,点踩后变红「已点踩 · 再点取消」,再点一次即取消;点踩/取消均有 toast 反馈,卡片上的任务结果正文、轮次与耗时保持不丢。飞书侧不弹原因选项,固定写入专属原因 `NO_REASON`(后台展示「未选择原因(飞书)」),与桌面端四选一原因区分开。仅卡片原发送者可操作(与「取消任务」「重试」同规则),非发送者点击提示「仅消息发送者可操作」;同一条消息只保留一条反馈记录,桌面端与飞书任一侧的新反馈会覆盖另一侧的旧记录。
- 点踩状态以 `message_feedback` 表为准查询,不依赖进程内存,重启/崩溃恢复后仍可点踩、可取消。消息归属口径与桌面端一致:`user_id` 取会话所属用户,`message_id` 取该会话最后一条 ASSISTANT 消息。
- 新增迁移 `V125__message_feedback_source.sql`:`message_feedback` 表加 `source` 列(`desktop` / `feishu`),存量桌面端点踩记录回填 `desktop`,管理后台明细据此展示来源。

### 管理后台

- 点踩反馈明细列表新增「来源」列(桌面端 / 飞书)。
- 原因筛选项改为读取汇总接口 `byReason` 动态渲染,不再硬编码四个原因;飞书来源的 `NO_REASON`(未选择原因(飞书))自动出现在汇总卡与筛选项中。

---

## 0.0.214 (2026-09-28)

### 后端

- Git 克隆不再把访问凭证拼进 clone URL。`workspace-mode=git` 克隆仓库时,Token 统一通过 `GIT_ASKPASS` 脚本 + `GIT_TOKEN_<域名>` 环境变量注入(与 Agent Shell、Git 写操作同一套机制),URL 始终保持 `https://<host>/<group>/<repo>.git`。此前实现会把 `https://oauth2:<token>@<host>/...` 交给 git,git 会把它原样写进工作区 `.git/config` 的 `remote.origin.url`,并出现在 `git remote -v`、`cat .git/config`、clone 报错等输出中,导致同机其他用户的工作区与会话日志可读到该 Token,形成跨用户凭证泄漏。
- 克隆地址新增校验:URL 里携带 `user:pass@` 或 `token@` 形式凭证时直接拒绝并提示改用「设置 → Git 凭证」配置对应域名的 Access Token。

### 前端(桌面 / Web / 安卓)

- 修复发送消息后对话区突然跳到顶部、之后流式输出也不再自动跟随的问题。消息入队后「待发送消息」面板、审批面板、问题面板出现时会压缩消息区高度,此前这被误判成用户向上翻看历史,自动滚动随即停摆;现在只有位置真正向上移动才算上滑,被压缩或消息替换引起的位置变化不再打断跟随。
- 继续修复同一问题的另一条触发路径:消息发送后任务进入终态会重新拉取会话消息,而接口默认只返回最近若干轮(`hasMore=true` 表示还有更早历史),此前本地已通过分页加载的更早轮次被直接丢弃,消息区内容塌陷、滚动位置随之跳走;现在只会采用本次返回的范围,本地已有的更早轮次保留在头部。
- 自动滚动的触发条件补充「执行中」状态:发送消息后最后一轮对话会从折叠态展开为平铺,可见高度突变,此前这一跳不触发重新定位,现在会重新贴底。
- 边路任务(Side Task)同步修复上述问题。边路会话面板此前用的是最早的判定方式——只看「离底部多远」,主聊天已修好的三条路径它一条都没沾到,发送消息后同样会跳离底部且后续流式输出不再跟随。现在改为与主聊天一致的规则:只有位置真正向上移动才算上滑,面板挤压、消息被 REST 覆盖导致的内容塌陷、轮次从折叠态展开为平铺都不再打断跟随;切换边路会话、切回边路标签页时重新度量滚动基准。

---

## 0.0.213 (2026-09-28)

### 后端

- 修复刚启动或长时间未用后第一次发图片,提示「生成 OSS 临时凭证失败: ConnectTimeout」要重建会话才好的问题。STS AssumeRole 现在对连接/读超时等瞬断自动重试(最多 3 次,300ms/600ms 退避),并显式设置 connect/read 超时;配置类与鉴权类错误(如参数、权限)仍原样返回,不做无意义重试。

### 前端(桌面 / Web / 安卓)

- 修复获取图片上传凭证失败时,先弹一条长错误、再被一句「获取上传凭证失败」盖掉的问题。现在把服务端返回的具体原因留给用户,不再重复提示。

- 修复删除会话时连点确认会再发一次删除、并误报「删除失败」的问题。请求期间确认按钮会禁用,删除成功不会再弹假失败。
- 修复点「刷新」的同时恢复某个已归档会话,该会话可能从侧栏短暂消失的问题。并发刷新会丢弃过期响应,不再用旧列表覆盖新列表。
- 修复一次选择或粘贴超过 10 张图仍能发出、随后发送被拒并留下空白气泡的问题。附件个数上限改为同步生效,普通文件同样受限。
- 修复任务点「重试」后若服务端拒绝,会话一直显示运行中、之后消息发不出去的问题。重试失败会恢复可发送状态并提示。
- 修复问卷答案发送失败后按钮永久停在「已提交」、无法重试的问题。发送失败会恢复可点并提示重试。
- 修复任务侧栏「运行中 1分」长时间不更新的问题。相对耗时现在会自动刷新。
- 修复飞书/钉钉登录授权页被浏览器拦截后,界面一直停在「等待授权」、没有任何提示的问题。会明确提示并可重试。
- 修复带触摸屏的笔记本上按 Enter 只换行、不发送消息的问题。改为按指针类型判断,桌面 Enter 恢复发送。
- 修复智能体列表加载失败时和「暂无可用智能体」长得一样、无法重试的问题。失败时显示「加载失败」和重试按钮。
- 修复切换模型失败后界面显示已切换、实际仍是旧模型的问题。失败会回滚并提示。
- 修复暗色主题下 Git 变更徽章、「待审批」徽章颜色偏暗、对比度不足的问题,改用主题色。
- 修复深色主题下输入框聚焦环、部分按钮悬停底色仍偏亮的问题,统一走主题变量。
- 修复会话搜索过程中没有任何加载提示的问题。
- 修复顶栏多个图标按钮缺少无障碍名称、安卓上点击热区偏小的问题。
- 修复文件 Tab 右键菜单在屏幕右缘被裁切的问题,菜单会自动收回视口内。
- 修复会话搜索用 ↑/↓ 翻到后面时,高亮项滚出可视区却看不见的问题。
- 修复任务分组重命名时输入框为空、不预填当前名称的问题。现在会预填当前展示名(已有别名用别名,否则用推导名)。
- 任务分组重命名范围扩大到全部分组:飞书/钉钉私聊与群聊、「临时工作区」「未设置」等系统分组也可右键重命名或恢复默认名。别名仍仅影响显示,多端同步不变。

### 管理后台

- 修复点踩反馈页「每页条数」选了不生效、显示与数据不一致的问题。
- 修复从「角色权限 → 成员」进入用户页时,角色提示条不出现或仍显示旧角色名的问题。
- 修复系统设置切走再切回,未保存的修改被静默覆盖丢掉的问题。有未保存改动时会先确认。
- 修复 MCP / Skills / Agent / Model 列表切走再切回不刷新、看不到他人改动的问题。
- 修复 MCP 服务器停用/启用没有二次确认、失败时也没有提示的问题。
- 修复定时任务筛选下拉改完不自动查询、切回页面数据过期不刷新的问题。
- 修复「系统指令」里个人指令超过一页时翻不过去的问题。分页总数写在响应头里,之前被请求拦截器丢掉,页码只能停在当前页。
- 修复标签页在「离开将丢失未保存修改」里选择留下后,高亮或已关掉的标签和实际页面不一致的问题。
- 「点踩反馈」在窄屏改为卡片列表,会话编号可以跳进会话详情。
- 钉钉机器人在手机上可以启用或停用。
- 会话列表对运行中的会话禁用删除;删掉当前页最后一条后会回到上一页。
- 修复 MCP 列表在停用、启用或删除后跳回第 1 页的问题。筛选条件变化时仍会回到第 1 页。
- 模型测试进行中,其它行的测试按钮会一起禁用。
- 调用流水里会话 ID、Agent ID 不是数字时会提示,不再悄悄当成没有筛选。
- 角色改名保存后,右侧「权限分配」标题会显示新名称。

## 0.0.212 (2026-09-28)

### 后端

- 修复桌面本地模式无法创建、修改、查看或删除定时任务的问题:这四个工具改在服务端执行,不再被交给桌面客户端后返回「未知工具」。
- 修复删除会话后,绑在该会话上的定时任务仍按计划触发并反复失败的问题。删除会话时会一并停用这些任务;执行时若发现会话已不存在,则把任务标为完结,不再排下一次。
- 修复已完结的定时任务在只改名称或提示词时被重新排期的问题。重新激活只发生在修改了 Cron、一次性开关,或把状态设回启用时。
- 修复排队消息自动发出的瞬间再手动发送,会把同一句用户消息写进对话两次的问题。
- 修复定时任务刚启动、插队消息和编辑重发还没真正跑起来时点「停止」无效的问题。停止会被记住,任务不会再被改回执行中并跑完。
- 修复模型发出空提问时,会话会空等 15 分钟的问题。现在会立刻返回错误,让模型重新提问。
- 修复 Fork 边路任务时,用户的新问题排在复制历史前面,主会话做过上下文压缩时模型完全看不到这次提问的问题。现在先复制历史,再写入新问题。
- 修复边路任务不记录开始时间、耗时一直是 0,以及排队期间取消后仍会先变成执行中的问题。
- 修复创建边路任务、正在复制主会话历史时点「停止」无效的问题。停止会被记住,复制结束后不再启动任务。
- 修复子代理追问或重试时,把之前每一轮的文件变更重复记到主会话的问题。
- 修复子代理回答和思考交错输出时,交回主会话的结论被截断或变成「没有文本输出」的问题。
- 修复改会话标题、置顶、归档等操作和任务结束撞在一起时,已完成的会话被写回「执行中」的问题。
- 修复没有 ripgrep 时,工作区里的符号链接会让全文搜索整体失败、陷入循环,或读到工作区以外的文件的问题。符号链接会被跳过。
- 修复服务器时区不是东八区时,ECP / 飞书登录状态和票据过期时间判断错位的问题。读取过期时间时按上海时间解析,和写入时使用同一时钟。
- 修复上下文压缩失败时,已经连上的云端 MCP 连接(包括本地子进程)没有被关掉的问题。

### 前端(桌面 / Web / 安卓)

- 修复同时有多条待审批、其中一条发送失败后,恢复出来的审批按钮永久不能再点的问题。
- 修复消息发出后、保存完成前继续打字,新输入会被清掉的问题。只有输入还和刚发出的原文一样时才会清空。
- 修复本端刚发出纯图片消息时,其他端同时发来的文字消息会被丢掉、图片气泡还可能套上别人的消息编号的问题。

## 0.0.211 (2026-09-28)

### 后端

- 修复蓝绿部署后会话卡在「运行中」、消息发出后一直无响应的问题。根因是三重时序错位:切流后旧实例的飞书/微信/钉钉长连接还活着,排空窗口内到达的消息仍被旧实例接走并开始执行;启动期崩溃恢复的两轮扫描(启动时刻 + 延迟 60s/+15s)都早于旧实例真正被 kill 的时刻(systemd timer 可能晚几十秒),于是执行被切断、`session.phase` 永久停在 `RUNNING`,而没有任何机制再接管。
  - 旧实例轮询 `deploy.lock`,看到切流完成(`status=switched`、自己是被替换的旧端口、且 `active-backend-port` 已指向新实例)即主动收尾:静默飞书/微信/钉钉入站 → 等在途执行收尾(上限 40s,并按 `MAO_BLUE_GREEN_DRAIN_SEC` 收敛)→ 把仍被中断的会话原子标记为 `RESUMING` → 自行退出。何时停机不再由外部 drain 定时器决定。已建立的桌面 WebSocket 连接不会被部署流程主动断开,旧实例退出时会自然断开并由客户端重连;断开前发起的执行由"等待 + 标记 + 续跑"兜住。
  - 承接流量的实例每 30s 巡检一次孤儿会话(`RUNNING` 需静默超过 120s、`RESUMING` 需静默超过 45s),并排除本实例正在执行、以及被在途子代理执行占用的会话,自动续跑被中断的任务。该巡检与部署时序解耦,也覆盖非部署原因导致的执行中断;单实例安装(不写 `active-backend-port`)同样生效。被在途子代理占用的父会话仍只在启动期恢复。
  - 启动期的延迟恢复(快照重放 + 全库补扫)同样改为等部署结束再执行:它的候选是"部署窗口内仍活跃的会话",旧实例可能还在跑,抢跑会双跑。代价是部署后的续跑从"启动后 60s"推迟到"排空窗口结束(`MAO_BLUE_GREEN_DRAIN_SEC` + 60s)后",最坏约 2 分钟。
  - 执行期间新增独立心跳:原来只在每轮 LLM 调用前后刷新 `last_activity_at`,单个长工具调用期间时间戳不更新,会让"正在跑"的执行看起来像孤儿;现在执行期间按 30s 定时刷新,使"静默"可靠地指向"已死"。
  - 升级到本版本后,历史遗留的、静默超过阈值的 `RUNNING`/`RESUMING` 会话会被巡检自动续跑,无需人工改库。

## 0.0.210 (2026-09-28)

### 桌面 Electron

- 修复 Windows 本地 shell 关闭后 `powershell.exe` 仍占着工作目录的问题。原先 `process.kill` 返回时进程还没退出,接着删除目录会失败,会话结束后目录也被锁住。现在用 `taskkill /T /F` 等到进程树退出再返回。原生命令的退出码会按 `$LASTEXITCODE` 回报,不再把所有失败都记成 1。

## 0.0.209 (2026-09-27)

### 前端(桌面 / Web / 安卓)

- 边路任务新建时上下文继承方式从勾选框改为三选一单选:Fork 主会话(完整复制主会话消息、文件变更和压缩记录到边路会话,在对话页面可查看历史消息)/ 主会话摘要(注入最近对话摘要)/ 不继承(全新对话)。
- 修复任务面板分组别名多端保存互相覆盖的问题:保存改为携带后端返回的版本号,遇到「已在其他端被修改」冲突时先拉取服务端最新别名与本地合并后重试(1s / 3s / 10s 退避),失败时明确提示;不再出现 A 端改名后被 B 端旧数据覆盖、且只提示「稍后自动重试」却永不重试的情况。
- 点踩已点踩消息的回显不再反复请求注定失败的会话接口:加载已点踩 id 失败时直接会话级置空,不再无限重试。

### 后端

- 边路任务新建时上下文继承方式从 `inheritContext` 布尔改为 `contextMode` 三选一(`fork` / `summary` / `none`):Fork 模式在事务内物理复制主会话全部消息、文件变更和压缩记录到边路会话,并重映射消息 ID 到压缩边界;无数据库 schema 变更。
- 任务面板偏好(`GET/PUT /v1/user-preferences/task-panel`)新增乐观锁 `version`:保存可携带 `expectedVersion`,与服务端当前版本不一致时返回 `3032 PREFERENCE_CONFLICT`,避免并发保存整行互相覆盖(读-改-写之间被其他端写入时会被拒绝而非静默覆盖)。数据库迁移 `V124__task_panel_preference_version.sql`。
- 修复 WebSocket 出站队列打满时终态帧(`exec_end` / `error` / `tool_result` 等)被静默丢弃,导致客户端 `pendingCallbacks`、`activeExecutionIds` 永久悬挂的问题:终态帧与被跟踪事件改走独立的高优先级队列,不受队列容量限制;连接关闭时补发未决事件的失败结果。
- 修复提问通知在等待超时后仍可能送达的问题:会话 WebSocket 已把 15 分钟超时的提问任务行从 `PENDING` 抢占为 `SENDING` 时,抑制逻辑现会兜底改写已抢占的行(原仅处理 `PENDING` 行,抑制失败即按已作废的内容推送通知)。
- 修复「点踩反馈」汇总接口在只传开始日期或只传结束日期时按日趋势图恒为空的问题(改为与原因分布使用同一套日期条件,单边界筛选同样生效)。
- 修复「已点踩消息」接口将会话不存在与非本人访问都返回空列表的问题:分别返回会话不存在(`SESSION_NOT_FOUND`)与无权访问(`FORBIDDEN`)。

## 0.0.208 (2026-09-27)

### 前端(桌面 / Web / 安卓)

- 任务执行结果消息支持「点踩」反馈:assistant 文本消息底部(复制按钮左侧)新增点踩按钮,点击弹出原因单选(结果错误 / 处理速度慢 / 问题未解决 / 其他),选中即提交、可再次点击切换原因或取消;主聊天与边路任务会话均可用(子代理详情面板不提供)。

### 后端

- 新增 feedback 领域:`PUT/DELETE /api/v1/feedback/messages/:messageId/dislike` 提交/取消点踩(校验消息归属与 ASSISTANT 角色,每条消息最多 1 条记录可覆盖),`GET /api/v1/feedback/messages/disliked-ids` 按会话回显已点踩消息;新增 `message_feedback` 表(V123 迁移)与新权限点 `feedback:read`(默认授予系统管理员)。

### 管理后台

- 新增「点踩反馈」页面(运行分组菜单,需 `feedback:read` 权限):顶部汇总卡片(点踩总数、原因分布)+ 明细列表(时间、用户、会话、Agent、原因、消息内容摘要),支持按原因和时间范围筛选、分页。
- 修复「调用流水」页 keep-alive 返回后筛选条件只增不清、旧筛选残留的问题(`applyQueryFilters` 改为无条件赋值)。
- 修复「点踩反馈」页 keep-alive 首屏 `onMounted` + `onActivated` 双触发导致重复请求的问题。

## 0.0.207 (2026-09-27)

### 后端

- 服务出流量优化(P0/P1):WebSocket 流式 delta(`content_delta`/`thinking_delta`)按 40ms 合帧下发,`tool_call_args_delta` 降频为最新全量快照——前端消费语义不变,流式对话出流量降约一个数量级;`/uploads/` 静态托管加 `Cache-Control: public, max-age=604800`,自动更新清单(`releases/` 下 yml/json、`latest*.yml`)保持 `no-cache` 避免客户端拿到旧版本号。
- 运维配合(不进代码):服务器 Nginx `/api/` 开启 gzip(JSON 5–10× 压缩),`/uploads/releases/` 清单禁缓存(修复清单被 immutable 缓存 7 天导致 OTA 延迟生效的问题)。

## 0.0.206 (2026-09-26)

### 前端(桌面 / Web / 安卓)

- 任务面板分组支持右键重命名:分组头右键打开菜单(「重命名」/「重置名称」),行内编辑别名,Enter 提交、Esc 取消、失焦提交、空值等价重置;存在别名时 hover 标题显示原始推导名。仅本地/云端工作区分组可改名,飞书/钉钉等 Agent 身份分组与系统分组(临时工作区、未设置)不提供重命名。别名随账号多端同步,未登录时本地保存。

### 后端

- 任务面板偏好(`GET/PUT /v1/user-preferences/task-panel`)新增 `groupAliases`(分组 key → 自定义显示名映射,值截断 50 字符,自动剔除不可改名分组与空值);旧客户端不传该字段时保留已有别名。数据库迁移 `V122__add_group_aliases.sql`。

## 0.0.205 (2026-09-26)

### 前端(桌面 / Web / 安卓)

- 修复移动端输入 `@` 触发文件引用面板时,列表顶部文件被视口裁掉、无法查看和点选的问题:面板打开时动态测量上方可用空间并收窄 `max-height`(内部滚动),桌面端行为不变。

## 0.0.204 (2026-09-26)

### 桌面 Electron

- 修复 Windows 本地(LOCAL)任务下命令执行工具全部失败的问题:本地 shell 宿主由硬编码的 `bash` 改为按平台选择,Windows 走常驻 `powershell.exe -NoProfile -NonInteractive -Command -`(启动后切 UTF-8 输出编码,`cd`/环境变量注入/退出码上报随协议适配),非 Windows 行为不变。
- 修复 Windows 下 `glob_search` 及带 `glob` 参数的 `grep_search` 报 `minimatch is not a function` 的问题(minimatch v10 命名导出解构适配)。
- 本地 shell 宿主启动失败(如找不到可执行文件)时向模型显式返回 `spawn_error`,不再表现为空输出 + exit 1。

## 0.0.203 (2026-09-26)

### 前端(桌面 / Web / 安卓)

- 修复 Windows 环境下本地任务的分组标题显示完整路径(如 `D:\projects\aiprojects`)而非项目目录名的问题,分组名提取现已兼容反斜杠路径。

---

## 0.0.202 (2026-09-25)

### 后端

- 任务通知(成功 / 失败 / 等待作答)在飞书渠道改为发卡片:头部按状态配色(完成绿、失败红、提问橙),正文除任务标题外新增「用户消息」(超出 300 字自动截断),失败时附失败原因,并新增「会话详情」按钮直接打开网页端该会话页面。此前只发一条纯文本消息,看不出任务上下文。需要会话存在可作为回复锚点的用户消息与会话深链,取不到时对应段落/按钮自动省略,不影响通知送达。钉钉渠道仍发文本,文案不变;设置页「发送测试通知」在飞书渠道也会发卡片,便于确认样式。

---

## 0.0.201 (2026-09-25)

### 后端

- 修复飞书话题群定时任务结果卡片跑到新话题的问题:话题会话的定时任务结果改为「回复话题根消息」发送,卡片落在原话题内。此前统一用 `message.create` 发到群 `chat_id`,而话题群里发到群维度的顶层消息会自动成为新话题的首条消息,导致结果与话题上下文脱节。非话题会话(普通群 / 私聊)发送方式不变。

---

## 0.0.200 (2026-09-24)

### 后端

- 飞书会话重新屏蔽 `ask_user_questions`:工具列表不再暴露,执行层兜底拦截(返回「当前通道不支持向用户提问」),Agent 改为在回复中直接说明选项或所需信息。上线进度卡表单作答后,飞书端任务频繁被问询阻塞(单次最长挂起 15 分钟),体验明显下降,故恢复为与微信/钉钉一致的屏蔽策略。历史会话续跑不受影响;已有提问表单随发版失效。桌面 / Web 会话的提问能力不变。

---

## 0.0.199 (2026-09-24)

### 后端

- 管理端会话消息接口在最新一页末尾补一条「进行中」的 `ask_user_questions` 伪消息(来源为内存提问注册表,`id` 形如 `pending-ask-*`,工具 `status=running`)。此前该轮 tool_calls 要等工具返回后才落库,会话处于「等待回复」时管理后台聊天记录看不到对应的提问工具调用;用户作答并落库后伪消息由真实记录取代。历史分页(`beforeMessageId`)不注入。

### 管理后台

- 会话详情聊天记录在「等待回复」时展示进行中的「询问用户」工具卡片(转圈、可展开看问题入参);尾部仅含执行中工具的伪消息不再被当成最终回复,避免把上一轮正文挤进「执行过程」或末尾留下空回复块。

---

## 0.0.198 (2026-09-24)

### 后端

- 移除飞书会话的 `ask_user_questions` 专用系统提示注入(`feishuAskHint`)。该段提示原先额外引导「需要用户作答时调用 ask_user_questions」并强调在进度卡表单作答,叠加通用工具引导后容易让 Agent 频繁提问、任务被挂起。飞书会话与其它通道一样只保留通用工具引导;进度卡表单作答能力本身不变。

---

## 0.0.197 (2026-09-24)

### 后端

- 移除飞书提问时对原发送者的应用内加急。提问已经会在进度卡上展开表单并把卡片标题变成「等待你的回复」,再加急会造成重复打扰。飞书应用不再需要「发送应用内加急」权限。

---

## 0.0.196 (2026-09-24)

### 后端

- 收紧飞书会话的提问引导。原先只要会话在飞书中就提示「需要用户作答时调用 ask_user_questions」,加上通用引导里把「获取信息、收集需求、请求授权」都算作可提问场景,导致 Agent 频繁提问、任务被挂起(单次最长等 15 分钟)。现在明确:仅在缺少关键信息、且无法通过上下文、工作区文件或合理假设自行推进时才提问;能自己查证或按惯例假设的先推进;一轮把问题一次问完;目标明确的执行类请求直接做,不要提问。

---

## 0.0.195 (2026-09-24)

### 后端

- 管理端会话接口(列表与详情)新增 `pendingQuestionCount` 字段,暴露会话当前等待用户回答的提问数量。该状态原先只存在于 Agent 运行内存中,phase 仍为 `RUNNING`,管理端因此无法区分「模型推理中」与「等用户答复」。

### 管理后台

- 会话管理的会话列表与详情页,「任务阶段」在会话等待用户回答时显示为「等待回复」(区别于「运行中」),用户详情页的会话列表同步生效。

---

## 0.0.194 (2026-09-24)

### 后端

- 管理员可为任意账号设置本地登录密码,不再限于已有本地密码的用户。LDAP、飞书、ECP、公司 SSO 账号设置后仍可用原方式登录,同时可用用户名和该密码登录。有本地密码的账号在用户列表中显示为「本地」。

### 管理后台

- 用户列表「重置密码」对所有账号可用。非本地账号会说明设置的是本地登录密码,原登录方式仍然可用。
- Agent 列表的名称与头像、默认标记同一行显示,不再被挤成逐字竖排。

### 终端 CLI(mao-cli)

- `user reset-password` 说明改为可为任意账号设置本地密码。

## 0.0.194 (2026-09-24)

### 后端

- 管理后台权限点补全为 28 个。飞书、钉钉、指令、MCP 治理、用量分析、调用流水、会话归档/删除、Skills、定时任务查看、角色和审计改为独立权限码,不再只认管理员角色或借用其他菜单的码。
- 新权限默认只授给系统管理员。除普通用户外,原先靠借用权限才能看见的页面会补上对应的新码。普通用户仍只有查看 Agent 和查看模型。
- 会话筛选项里的用户、Agent 下拉同时给定时任务、调用流水和审计使用。只有这些页面的读权限时也能拉下拉,会话列表本身仍要 `session:read`。

### 管理后台

- 角色权限页按能力、运行、安全、系统、云端终端分组。菜单和写按钮与上述权限码对齐。有用量分析权限时登录进入该页,否则进入自己有权限的第一个页面。

## 0.0.192 (2026-09-24)

### 后端

- 飞书进度卡正文保留流式输出里的换行。此前每个文本增量都会 `trim`,单独到达的换行被吃掉,询问等待时标题和列表会粘成一段。

## 0.0.191 (2026-09-24)

### 后端

- 新增钉钉企业内部应用机器人通道(默认关闭,`DINGTALK_BOT_ENABLED`)。管理端可配置多个机器人,各自独立 Agent / 模型,Stream 随启停热生效。
- 桌面 / Web / 安卓设置页可绑定、解绑钉钉身份。未绑定的人在钉钉里收到「点我绑定」短链卡;登录 Mao 后完成授权并重放刚才那条消息。钉钉身份只做通道绑定,不创建用户,也不走 ECP 票闸门。
- 私聊支持文本、富文本、图片和文件;群里在 @ 机器人之后支持文本、富文本和图片。回复走主动发送。私聊 `---` 新建会话,执行中收到 `---` 会先取消当前任务。群上下文只注入本机器人收到的 @ 往来和本条引用正文。
- 会话忙时发排队卡(立即发送 / 取消本条)。进度卡展示执行中、完成、失败、取消,按钮为取消、失败重试和打开网页会话,只允许原发送者操作。没有卡片模板时退化为一条「正在处理」文本,终态仍发 markdown。
- 钉钉会话不提供 `ask_user_questions`。新增 `dingtalk_send_image` / `dingtalk_send_file`,只在钉钉会话出现。

### 管理后台

- 「飞书机器人」旁增加「钉钉机器人」:配置 Client ID、Secret、Robot Code、Agent、模型、卡片模板,以及启停、连接状态和重连。

### 前端(桌面 / Web / 安卓)

- 设置页增加「钉钉Bot」,可查看绑定状态、发起绑定和解绑。
- 侧边栏按钉钉私聊(同一 Agent)和钉钉群工作区分组。工具卡片支持「发送钉钉图片 / 发送钉钉文件」。

### 终端 CLI(mao-cli)

- 增加钉钉机器人通道说明与管理端 REST。

## 0.0.190 (2026-09-24)

### 后端

- `generate_image` 文生图协议对齐(OpenAI Images):请求仅发送 `model/prompt/n/size/quality`(支持 `quality` 与尺寸 `auto`,`n` 放宽到 1–10),去掉非标准的 `response_format`;统一 `Accept-Encoding: identity`、Bearer 防重复、10 分钟超时;对 `429/502/503/504` 与 `rate_limit_exceeded` 按 250ms/1s/2.5s 重试;支持响应 `b64_json` 与 `url`(含相对路径)双通道落盘,并回传 `usage` / `revised_prompt` / 响应内 `model`/`size`;异步 `task_id` 直接报错不轮询;错误契约带 `error_code`/`http_status`。
- 新增 `edit_image` 改图工具:本地图片 1–N 张 + 可选蒙版,multipart 调用 `/images/edits`;支持 `size`/`quality`/`background`/`output_format`/`output_compression`/`model` 覆盖;不接受图片 URL;产物与文生图一致落盘并返回可展示 URL。
- 模型仍由管理后台 `model_type=image` 配置(baseUrl / apiKey / model_id);调用可用 `model` 覆盖(别名 `flare`/`sunburst` 归一为完整名,点名失败不换模)。
- 飞书会话可以在当前任务的进度卡片里回答 `ask_user_questions`:一组提问一个表单,同一轮多组问题叠在同一张卡上,只有原发送者能提交。提交后按与桌面端相同的答案继续执行;桌面或网页若也开着提问面板,飞书提交成功后会收起。表单已经挂上时不再另发离线提问通知。没有进度卡且没有客户端连接时立刻返回「当前通道无法向用户提问」,不再空等。微信通道仍然不提供该工具。进程重启后旧表单失效。取消任务或插队会结束尚未提交的提问。
- 飞书进度卡在等待提问作答时改为橙色标题「等待你的回复」,并说明要在卡片里点「提交答案」、不必再发一条消息。这时不再把 `ask_user_questions` 显示成「执行中」。

### 前端(桌面 / Web / 安卓)

- 工具卡片支持 `edit_image`「编辑图片」展示与参数预览。

### 终端 CLI(mao-cli)

- 飞书机器人说明补充:提问时进度卡片内出现表单,仅原发送者可提交。

## 0.0.189 (2026-09-24)

### 后端

- 前台委派和后台子代理都不再提供 `ask_user_questions`。向用户提问只由主会话发起;子代理若仍调用该工具,会收到「不在当前允许的工具集内」。

## 0.0.188 (2026-09-23)

### 管理后台

- 会话详情页适配移动端:页头由「返回列表」竖排 + 按钮换行的 `el-page-header` 换成单行「返回 / 标题 / 导出 / 刷新」栏,会话信息默认收起(可点开,摘要显示用户 · Agent · 任务阶段),聊天区因此从原来约 149px 的可滚动高度扩到整屏可用;「加载更多」的历史消息插入后仍能保持原阅读位置。

## 0.0.187 (2026-09-23)

### 后端

- ECP、公司 SSO 首次登录自动建号的用户名改为取邮箱前缀(小写规范化,重名追加 8 位稳定后缀,邮箱不可用才退回 `sso_/ecp_` + 身份 ID),不再使用 `sso_<uuid>`、`ecp_<uuid>`;与飞书首次建号的取名规则统一。已有账号用户名不变,登录仍按邮箱绑定。
- 用户信息 `authSource` 新增 `ECP` 与 `COMPANY_SSO`:此前这两类账号被归为 `LDAP`。判定顺序为本地密码 → 外部身份提供方 → 飞书绑定 → LDAP;用户列表按来源筛选同步支持新取值。

### 管理后台

- 用户列表「账号类型」区分本地 / LDAP / 飞书 / ECP / 公司 SSO,筛选下拉可选按来源查询(此前非本地账号一律显示 LDAP)。

### 前端(桌面 / Web / 安卓)

- 个人资料页「登录方式」区分 ECP 与公司 SSO,不再与 LDAP 混同。

## 0.0.186 (2026-09-23)

### 后端

- 管理端会话消息支持 `compact=true`:不读取文件变更的 diff 正文,并把工具输出、过长工具参数截到详情页实际展示的长度。不传该参数时仍返回完整内容。

### 管理后台

- 会话详情的会话信息和聊天记录分开加载。打开详情时走精简消息载荷,超长代码块不再同步高亮,避免整页被工具输出和 diff 拖住。
- 聊天记录里的文件变更改为在每一轮回复末尾合并展示;这一轮还在执行时先不展示,和客户端一致。完整工具输出仍用「导出记录」获取。

### 终端 CLI(mao-cli)

- `mao admin-session messages --compact` 拉取精简聊天记录,与管理后台详情页相同。

## 0.0.185 (2026-09-23)

### 后端

- 飞书进度卡片在 Shell 执行中展示的命令加长:长命令从 80 字截到 240 字。工作区路径前缀不再把后面的实际命令挤掉。

## 0.0.184 (2026-09-23)

### 后端

- 管理员可将个人指令复制为系统指令:`POST /v1/admin/user-commands/:userId/:id/promote`。原个人指令保留;可用请求体 `name`/`content` 覆盖后再写入,名称与已有系统指令重复时拒绝。

### 管理后台

- 指令管理「个人指令」支持「添加到系统指令」:确认前可调整名称与内容,复制为全体用户可用的系统指令,原个人指令保留。

### 终端 CLI(mao-cli)

- `mao system-command promote-personal --user-id --id [--name] [--content]`,与管理后台「添加到系统指令」对齐。

## 0.0.183 (2026-09-23)

### 后端

- 管理端会话列表关键词同时匹配标题、摘要与消息正文(任意角色),可用聊天记录里的句子反查会话。命消息时记录带 `matchSnippet` 命中片段。

### 管理后台

- 会话管理关键词支持按对话消息内容搜索,列表标题下展示命中片段,便于定位用户反馈对应的会话。

## 0.0.182 (2026-09-23)

### 前端(桌面 / Web / 安卓)

- 新建任务时的智能体列表不再显示已停用的 Agent。
- 从飞书卡片「会话详情」或地址栏 `/tasks/{id}` 进入时,左侧任务栏会展开该会话所在分组,若它排在分组默认可见条数之外也会露出来,并滚动到对应一行。聚焦模式下若落在「历史」折叠区,会一并展开。

### 后端

- 用量分析趋势支持按小时或按天聚合:`GET /v1/admin/analytics/trends?granularity=hour|day`(默认 day)。小时按上海时区整点补零;窗口包含今天时,序列停在当前整点。
- Agent 可停用、启用(已有 Agent 默认仍为启用)。使用侧 `GET /v1/agents` 只返回启用中的 Agent;带 `includeDisabled=true` 且具备 `agent:read` 或 `agent:write` 时才返回全部。`PATCH /v1/agents/{id}/enabled`(`agent:write`)切换状态。默认 Agent 不能停用,已停用的也不能设为默认。已停用的 Agent 不能新建会话;已有会话可以继续。微信通道若仍指向已停用 Agent,会回退到默认 Agent。
- 管理端可将技能目录写入指定用户的个人技能,而不是系统技能库:`POST /v1/admin/user-skills/upload`(`agent:write`,multipart 字段 `userIds` 与 `files`)。同名个人技能会被覆盖,其他用户不受影响。选择用户用 `GET /v1/admin/user-skills/options/users`(`agent:read`)。
- 指定用户上传在请求中断或超过文件数时整批不写入。覆盖个人技能时先写入临时目录,替换失败会留下原来的技能;多名用户里后面的人失败时,响应会带上已经写成功的用户。
- 修复飞书后续消息在已打开的网页会话里只有用户消息、没有执行过程的问题:每一轮开始(含失败卡重试)会先广播带 `executionId` 的 `RUNNING`,网页端才会结束上一轮的流式屏蔽并接上工具调用与回复,不必整页刷新。
- 修复全新空库首次启动迁移失败的问题:迁移脚本中的 `DELIMITER` 存储过程语法 TS 版 Flyway 执行器不识别,现改由 mysql 客户端逐文件执行(E2E 搭建脚本内处理,应用内行为不变)。

### 管理后台

- 用量分析的趋势页同页平铺流量(会话/消息)、Token、调用和质量图表,不再用右上角按钮切换。
- 趋势图可切换时间粒度为小时或天。统计周期小于 7 天时默认小时,大于等于 7 天时默认天;切换统计周期会回到对应默认粒度。
- Agent 管理可停用或启用智能体,并按状态筛选。停用后仍留在管理列表,可再次启用;默认 Agent 的停用按钮不可用。飞书机器人和系统设置里的微信智能体下拉会标出已停用项,且不能新选它们。
- Skills 管理的「个人 Skills」可以先选择用户,再上传技能目录。技能只进入这些用户的个人技能,不会变成全体可用的系统技能;同名个人技能会被覆盖,并在这些用户的所有智能体中生效。手机端仍只查看和删除。
- 「个人 Skills」筛选增加用户下拉,可只看某一个用户的个人技能,并与关键词一起生效。
- 拖放上传会按松手时的页签和已选用户提交,读取目录期间不能切换页签。上传会跳过 node_modules、.git、dist 等目录,并限制文件数量和体积;失败后会刷新个人技能列表。
- E2E 测试基础设施改进:新增 `scripts/e2e-setup.sh` 一键搭建隔离测试环境(本地 MySQL 建 `mao_e2e` 库、执行全部迁移、写入最小种子数据、生成 `backend-ts/.env.e2e`);`npm test` 通过 `tests/global-setup.ts` 自动拉起隔离后端(:9180)与 admin/desktop dev server,已在监听的端口自动复用,测试结束自动回收,不再要求手工起三端,也不会误连线上 9080。运行期数据目录(runtime/users/workspace/uploads)一并重定向到 `backend-ts/.e2e-data/`,与生产 `/opt/mao-data` 完全隔离。
- 修复 6 个 E2E 测试文件与 UI 演进脱节导致的失败:Agent 编辑「最佳实践」启停改为 el-switch 后的断言、`/users/me` mock 缺 `isAdmin` 导致登录落地页被软回退、侧边栏 `span:has-text` 选择器失效、Agent/模型表头列变更、退出登录缺确认弹窗步骤、ElMessage 多条弹出时的歧义匹配。

### 终端 CLI(mao-cli)

- `mao agent list` 默认不列出已停用 Agent;有 Agent 读权限时可用 `--include-disabled` 查看全部。`mao agent set-enabled --id <id> --enabled true|false` 启停。
- `mao skill-docs assign --dir <目录> --user-ids 1,2` 把技能写入指定用户的个人技能,不进入全局技能库。

## 0.0.181 (2026-09-22)

### 管理后台

- 定时任务新增「查看」详情弹窗:一次看全任务提示词原文(可复制)、Cron、一次性标记、完结与执行情况、所属会话(可跳转),排查不再需要去翻会话。
- 定时任务新增「编辑」:可改任务名称、Cron 表达式与任务提示词,并提供「一次性任务」开关;编辑时实时预览未来 3 次触发时间(北京时间),Cron 不合法会直接提示且无法保存;改为一次性 Cron 形态时开关会自动跟随。任务已完结时提示保存后会重新激活。
- 编辑与删除他人任务改用新增的「管理定时任务」(`scheduled-task:write`)权限点控制;只有会话查看权限的账号在列表里对他人任务只读(开关置灰、不显示编辑/删除),本人任务不受影响。任务名称/提示词为空或超长时给出明确报错,不再写入脏数据。

### 后端

- 新增权限点「管理定时任务」(`scheduled-task:write`,默认授予系统管理员),跨用户编辑/启停/删除定时任务由它控制;列表、详情仍按 `session:read` 放开。
- 新增 `POST /v1/scheduled-tasks/cron-preview`:按与调度器同一套解析和时区预览 Cron 的未来触发时间,供管理后台编辑弹窗使用。
- 定时任务创建/更新增加任务名称(≤200 字符)与提示词(≤10000 字符)的非空与长度校验。
- 定时任务的编辑、启停、删除操作纳入审计日志(读取与 Cron 预览不写日志)。

## 0.0.180 (2026-09-22)

### 后端

- 修复子代理崩溃恢复后,父会话看到「工具结果丢失」、真实结论不再写回的问题:只补上缺失的那条工具结果,同一轮里已有的正文和其它工具调用会保留。
- 修复微信新消息和桌面端同时跑同一个会话的问题:微信会先取消桌面端正在执行的循环,等它收尾(含随后的队列自动消费)结束后再开始。旗标已经摘掉但阶段还没写完、或队列还在那 500 毫秒延迟里时进来的消息,也不会提前开跑。执行在进入代理循环前就失败时,会摘掉这次注册的取消旗标,避免之后每条微信都空等一分钟。
- 修复定时任务在真正开始前被暂停时,这次触发被跳过的问题:每天一次要等到明天,固定月日要等到明年。现把本次触发还回待执行,重新启用后会补跑。
- 修复定时任务在排队或执行期间改了 cron、或把一次性改成循环后仍按旧计划触发的问题:循环任务会按新表达式安排下一次,不再被提前标成已结束。
- 修复停止父会话时,刚好跑完的后台子代理会被改回「已完成」的问题:已经取消的执行保持取消。
- 修复父会话已经结束、重启后续跑时子会话一直显示运行中的问题:这类子会话会收成已取消。
- 修复飞书失败卡片点「重试」成功后,失败前排进来的消息一直不执行的问题:重试成功或取消后会继续处理队列。

### 管理后台

- 修复重新部署后,已打开的页面里部分菜单点击没有反应的问题:这些菜单还在请求部署前已经删除的页面脚本。现会自动整页打开刚点击的菜单并加载新版本;短时间内只自动跳转一次,避免新版本本身缺文件时来回刷新。
- 修复非管理员的首页、Logo 和顶栏固定标签仍指向「用量分析」的问题:只有模型读权限的账号登录后会在无权限页之间打转,其他非管理员则留着一个点开就会被弹走、又关不掉的标签。现按已有权限落到第一个可用页面(含模型管理),Logo 和「返回首页」也回到这一页。
- 修复用户列表按账号类型筛选只作用于当前页、手机布局完全不生效的问题:筛选改由服务端分页,总数与翻页一致。
- 修复会话列表在标签页缓存下不跟地址栏筛选的问题:从用量分析或会话详情带 `userId` / `phase` 等条件进来时,列表仍按上次的条件请求。地址变了会重新套用条件;地址没变时只刷新数据,不冲掉表单里还没查询的输入。
- 修复只有用户读权限时打不开用户详情的问题:详情不再和编辑、重置密码绑在同一个写权限上。

### 前端(桌面 / Web / 安卓)

- 修复已绑定飞书后再点「重新绑定」大约两秒就提示成功的问题:当时还没完成新的授权,旧绑定被当成这次成功。现会等到飞书身份或绑定时间发生变化才提示成功。
- 修复任务列表重命名时点「取消」仍会保存的问题:失焦不再抢在取消按钮之前提交。
- 修复消息还在保存时切到别的标签再切回来,输入框里的原文还在、容易再发一次的问题:原文没改过会清空;回来之后新打的字会保留。
- 修复队列消息点「编辑」时删除失败仍把内容填回输入框的问题:失败只提示重试,不再和队列里的原消息各留一份。
- 修复本地模式边路任务的权限开关点了没反应、并且一直显示「只读」的问题:显示并修改这条边路任务自己的权限;还没创建时,第一次发送会带上当前选择。
- 修复编辑 Git 凭证时清空备注保存后备注还在的问题:空备注会写成未填写,不再被当成「不修改」。
- 修复触控设备上终端标签的关闭按钮要悬停才出现、因此点不到的问题:窄屏和没有悬停的设备上关闭按钮保持可见。

## 0.0.179 (2026-09-22)

### 后端

- 修复飞书进度卡「取消任务」在执行还在跑(含崩溃恢复)时就写成已取消的问题:提前终态会删掉进度卡、给未完成的工具补占位结果,恢复期间新消息还会和仍在跑的循环并行。现已有执行时只置取消标志,等循环收尾再落终态。
- 修复 LOCAL 子代理在桌面端未重连时把父会话恢复永久挂住的问题:等待桌面连接超过 60 秒即失败该子执行,父会话恢复可以继续。失败说明只保留一层「子代理恢复失败」。
- 修复定时任务在执行池已满、提交被拒绝时本次触发被跳过的问题:原先先把下次触发推到下一档,拒绝后不再补跑(每天一次要等到明天,固定月日要等到明年),并记成一次失败。现把本次触发还回待执行,下一轮扫描再试。
- 修复修改定时任务的 cron 后「一次性 / 循环」不随新表达式更新的问题:循环改成指定月日仍会年年触发,一次性改成每天则只跑一次就结束。未单独指定一次性时,现按新 cron 重新判定。
- 修复后台子代理完成通知在「消息已写入、投递状态尚未标记」之间进程重启后出现两条的问题:恢复时若父会话已有该次完成通知,只补投递状态,不再插一条。
- 修复 ECP 飞书登录在容器时区不是上海时,刚发起就被判「state 已使用或已过期」的问题:state 过期时间与领取比较改为同一套上海时间。

### 前端(桌面 / Web / 安卓)

- 修复 Agent 一次提出多组问题、或刷新后重连时,只有最后一组能回答的问题:各组按到达顺序保留,答完一组再答下一组。

## 0.0.178 (2026-09-22)

### 前端(桌面 / Web / 安卓)

- 修复新建 Markdown 文件的变更 diff 点「源码」后恒为空白的问题:源码容器挂载后没有重新初始化编辑器,来回切换预览/源码也无法恢复。现切到源码即渲染,且同一文件因 Git 状态刷新而更新时不再被强制打回「预览」,不会打断阅读。
- 修复 Tab 右键「关闭其他文件」连带关掉边路任务 / 子代理 Tab 的问题:现只关闭文件与 diff Tab。此前被误关的边路 Tab 因为漏记「用户已关闭」,还会在下次恢复时自行冒出来。
- 修复队列「立即发送」发送失败后整个队列的按钮一起失效(失败那行一直显示「处理中...」)直到本轮执行结束的问题:现失败后最多 8 秒自动复位;在途期间其余行按钮明确置灰,不再点击无反应。
- 修复输入框 `@` 引用文件时慢响应覆盖新搜索结果、可能引用到错误文件的问题。
- 修复 Agent 连续发起多组提问时,头部计数显示总数但只有最后一组可作答(较早那组在界面上无法回答、对应分支一直等待)的问题:现按到达顺序逐组作答,计数改为显示进度。
- 修复微信 Bot 扫码在后端返回绑定信息不完整时仍提示「绑定成功」、实际并未绑定的问题:现提示「绑定信息不完整,请重新扫码」。
- 「在文件浏览器中打开」在 Web / 安卓上点击不再毫无反应,改为提示仅桌面客户端可用。

### 管理后台

- 修复用量分析页每次切换统计周期 / 子 Tab 都在顶栏新增一个「用量分析」标签、标签栏很快被挤满的问题:标签改按页面路径归一,点回标签仍会还原该页上次的筛选条件。
- 修复审计日志「自动刷新」开启后切到其他页面再回来永久停摆、而开关仍显示开启的问题(容易误以为在盯实时审计)。
- 修复系统设置点「刷新」对已显示字段无效的问题:原先只填充空字段,界面可能长期与数据库不一致,多人同时改配置时还会用陈旧值覆盖他人的修改。
- 修复调用流水页切走再回来仍展示离开前的陈旧数据、需手动点刷新的问题。
- 修复移动端布局下「系统 Skills」删除按钮未校验写权限、只读用户可点击并最终被后端拒绝的问题。
- 修复用户详情抽屉多个 Tab 并行加载时加载态错乱的问题:默认「会话」Tab 几乎从不显示加载骨架,其余在途 Tab 也会提前失去加载动画、空表格看起来像「无数据」。
- 用户详情抽屉会话「阶段」改用全站统一文案,不再对「空闲 / 待审批」显示英文枚举;Git 凭证「Token」列改为「已配置 / 未配置」状态标签(后端恒返回掩码,展示原值没有信息量)。
- 修复用量分析 CSV 导出在部分浏览器 / WebView 下无声失败的问题。

## 0.0.177 (2026-09-21)

### 后端

- 修复任务完成 webhook 通知在调度参数读取失败(DB 抖动等)后永久停摆的问题:原先失败分支只打日志「1 分钟后重试」却没有重排定时器,整条投递轮询链断开,`PENDING`/`WAITING_WS` 的通知直到重启后端才恢复。现失败后 1 分钟重排续跑。
- 修复 ECP 票自动续期在续期途中重启(部署 / OOM)后永久停摆的问题:`renew_status` 会卡在 `RENEWING`,而扫描只捞 `ACTIVE`,该用户票到期后突然变成「无有效 ECP 票」,只能手动重新登录。现每分钟把超过 5 分钟未收敛的 `RENEWING` 复位为 `ACTIVE`,重新进入续期队列。
- 修复 Shell 后台任务(`async: true`)运行超过 30 分钟后结果被静默丢弃的问题:原先超时即从登记表移除,任务跑完也无人消费(不再自动注入上下文),模型再 `await_async` 只会收到「后台任务不存在或已被消费」。现超时只告警、保留交付,长时构建结束后仍能拿到输出;仅超过 24 小时未结束才回收。
- 修复飞书忙碌期收到的文件在当轮任务失败(FAILED)时永不写入会话的问题:文件只留在内存里,用户后续提问在会话历史中看不到文件路径。现附件落库与上一轮成败解耦,FAILED 只阻止自动消费下一条排队消息。
- 修复队列自动消费在执行提交被拒(线程池打满)时产生重复用户消息的问题:回补队首却没删掉已落库的 USER 消息,下次消费会再写一条同内容消息。现出队之后任一步失败都统一补偿——回补队首 + 删除孤儿消息 + 刷新队列,消息不再丢失、重复或留下无执行的孤儿。
- 修复会话空闲时的「停止」把从未运行过的任务标成「已取消」的问题:`IDLE` 既不在活跃集合也不在终态集合,会被终态写入放行。现仅在确有在途执行时才落 `CANCELLED`。
- 修复 `shell` 工具 `write_stdin` 写入 `exit` 让 bash 退出后,返回文案称「会话已关闭」但会话未真正回收的问题:死会话最多占用 60 秒会话数配额,极端情况下误报「已达会话上限」。
- 修复终端断线重连后历史输出可能整段空白的问题:单帧输出超过缓冲上限时环形缓冲会把这一帧一起丢掉,连截断提示都不显示。现只截掉超限部分并保留最近输出(按 UTF-8 字符边界,不再切出乱码)。
- MCP 工具参数不是合法 JSON 对象时直接返回参数错误,不再兜底成 `{}` 后照常调用——原先会让工具在缺参数下执行或返回与真实原因无关的报错。

### 前端(桌面 / Web / 安卓)

- 修复工具审批在断线时点「批准 / 拒绝」后审批项直接消失、而服务端仍在等待回包(任务像卡死)的问题:现发送失败会把审批项放回队列并提示「审批发送失败,网络连接不可用,请重试」。
- 修复发送消息后切换会话,本轮耗时被写到另一个会话最后一条回复上的问题:耗时改按本次发送的会话回填。

## 0.0.176 (2026-09-21)

### 后端

- 飞书进度卡片在 Shell 执行中展示正在跑的命令(如 `shell:\`npm test\`(执行中)`),参数流式拼完后立即刷新,长命令截断到 80 字。
- 飞书任务崩溃恢复或失败重试时,进度卡片轮次按当前任务已完成的助手轮接续(例如崩溃前第 79 轮,恢复后从第 80 轮继续),不再从 0/1 重计。

## 0.0.175 (2026-09-21)

### 后端

- 飞书通道收到独立文件消息时只下载并写入当前会话(`@{路径}@`),不再单独开一轮任务:飞书无法在同一条消息里带文件和文字,原先会连续触发两次 Agent。只发文件、不跟文字时保持静默,等后续文字再处理;会话执行中收到的文件等当前任务结束后再落库,不插入正在跑的消息流。

## 0.0.174 (2026-09-21)

### 后端

- 修复崩溃恢复漏扫「蓝绿部署窗口内新建、随旧实例 drain 被 kill」的会话,导致其永久卡在运行中(飞书进度卡停在「正在处理」、后续消息只入队不执行)的问题:新实例启动时初始扫描为空便不再安排延迟恢复。现部署窗口内无条件安排一次全库补扫,并排除本实例正在执行的会话,避免对同一会话并发重复执行。

## 0.0.173 (2026-09-21)

### 前端(桌面 / Web / 安卓)

- 设置页入口从顶栏头像下拉菜单移到顶部栏齿轮按钮,减少进入层级;头像菜单保留「个人信息」与「退出登录」。
- 修复从工作台进入设置页时聊天输入框卸载保存草稿可能抛错(TipTap `getHTML` 读已销毁 schema),导致页面停在工作台、设置页不出现的问题。

## 0.0.172 (2026-09-21)

### 管理后台

- 「指令管理」从「系统」分组移到「能力」分组,紧挨「Skills 管理」下方。
- 「飞书机器人」从「系统」分组移到「能力」分组,紧挨「Skills 管理」上方。

## 0.0.170 (2026-09-21)

### 后端

- ECP 默认配置改为占位值(`EK0001`、`ecp.example.com`、`mao.example.com` 回调),不再内置内部生产网关与 appCode。

### 管理后台

- ECP 配置表单占位符与默认 JSON 同步为上述占位值;公司 SSO 白名单示例改为 `example.com`。

### 终端 CLI(mao-cli)

- 配置/Embed SDK 文档中的内部 SSO 校验地址、ECP 网关与 Origin 示例改为 `example.com` 占位域名。

## 0.0.169 (2026-09-21)

### 前端(桌面 / Web / 安卓)

- 修复任务执行中进入设置页再返回工作台后,会话执行状态不更新 / 显示不准确的问题:路由切换会整体卸载重建工作台组件,而 store 中活跃会话 ID 未变化导致新组件实例跳过会话恢复(未重新订阅 WS、未注册执行回调、未重拉消息)。现在组件挂载时会兜底执行一次会话恢复,与 watcher 行为保持一致。

## 0.0.168 (2026-09-20)

### 前端(桌面 / Web / 安卓)

- 「我的技能」「我的指令」管理入口从顶部栏迁移至设置页:顶部栏不再展示这两个图标按钮,设置侧边栏新增「我的技能」「我的指令」两项(位于「MCP 服务器」之后),页面内提供与原抽屉一致的上传、查看、删除、双 Tab(系统/个人)等完整管理能力。
- 聊天消息的「添加到我的指令」改为直接弹出新建设置弹窗(内容自动预填,保存成功即关闭),不再经过抽屉中转。
- 修复聊天消息中 @指令 标签内容缓存不失效的问题:指令创建/更新/删除后共享缓存立即失效并后台重拉,已渲染气泡自动刷新;同时修复原实现拉取失败后永不重试的缺陷,并消除失效与在途请求的竞态(过期响应直接丢弃)。
- 随迁移删除「我的技能 / 我的指令」侧滑抽屉及其安卓全屏适配样式;设置页沿用既有响应式布局。

## 0.0.167 (2026-09-20)

### 前端(桌面 / Web / 安卓)

- 修复边路任务发送消息后用户消息渲染两次的问题:`user_message_saved` 事件判别「本端乐观消息」时只认 `msg_` 前缀,未覆盖边路任务乐观消息使用的 `side_user_` 前缀,导致同一条消息被误判为远端回显而重复追加。现统一收敛为共享的乐观 ID 前缀判定(`msg_` / `side_user_` / `subagent-user-`),并同步修正 `updateLastMessageId` 的 ID 替换逻辑。

## 0.0.166 (2026-09-20)

### 前端(桌面 / Web / 安卓)

- 模型首轮请求即被限流(HTTP 429)时,会话无任何流式/思考事件,导致重试提示条(「上游响应异常,正在第 x/y 次重试」)无法显示。现改为按会话执行 phase(RUNNING / RESUMING / WAITING_APPROVAL)判定执行状态,重试期间正常显示提示与流式指示。

## 0.0.165 (2026-09-20)

### 文档

- `docs/plan/` 根目录历史方案文档统一增加 git 首次提交日期前缀(如 `2026-09-20-desktop-multi-server-baseurl-design.md`);子目录(`best-agent-spec/`、`technical-design/`、`design/`、`todolist-design/`)文件名保持不变。
- README、DEPLOY、AGENTS/CLAUDE、mao-cli、agent-cli、docs/code-review 及源码注释中的 `docs/plan/` 路径引用已同步更新。

## 0.0.164 (2026-09-20)

### 桌面 Electron

- 通用安装包支持配置/切换 Mao 服务器 `baseUrl`:首次启动展示本地服务器配置页;应用菜单新增「服务器设置…」;设置页(Electron)可查看与修改当前站点。
- 生产 `loadURL`、skill-sync API 回落不再写死 `https://mao.etarch.cn`,改读 `userData/server-config.json`(可用环境变量 `MAO_DESKTOP_SERVER_URL` 锁定)。
- 切换服务器时清除登录态、关闭终端/MCP/本地 Shell;LOCAL runtime 目录按服务器 host 命名空间隔离(`~/.mao/runtime/<host>/<sessionId>`);`auth.json` 记录 `serverHost`,跨站 token 不会被误用。
- 自动更新源支持 `follow-site`(跟随当前站点 `/api/uploads/releases/`)/ `package-default`(安装包内置)/ `disabled`,仍兼容 `MAO_DESKTOP_UPDATE_URL`。
- 地址归一化接受站点根、`/api`、`/api/v1`;保存前可探测 `version.json` / 首页;默认仅 HTTPS,内网 HTTP 需显式允许。

### 前端(桌面 / Web / 安卓)

- 设置侧栏在桌面客户端增加「服务器」入口(`/settings/server`),用于展示/切换连接的 Mao 站点;浏览器端不显示该项。

### 终端 CLI(mao-cli)

- `reference/electron.md` 补充桌面客户端多服务器配置说明与私有部署前提。

## 0.0.163 (2026-09-20)

### 后端

- `open_web_page` 网页正文默认输出上限从 500000 字符下调到 50000 字符(约 1.2~1.5 万 tokens),
  避免单个网页正文一次性占满会话上下文、把活跃上下文从数万 token 顶到数十万 token。
- `open_web_page` 正文被截断时不再丢弃后半部分:完整内容会写入会话 runtime 目录
  `runtime/<userId>/<sessionId>/webPages/<url-slug>.md`,并在工具返回的 `full_content_file` 字段给出该路径,
  同时在 `content` 末尾追加截断提示,引导 Agent 用 `read_file`(支持 offset/limit 分页)或 `grep_search` 回读被截断内容,
  而不是反复重新抓取同一网页。落盘失败(含 LOCAL 模式)时仅告警,仍返回截断后的内容并说明本次不可恢复。
- 运行参数 `harness.webPage.maxOutputLength` 的默认值同步调整为 50000(管理后台「运行参数」可改,重启后端生效;Flyway `V117` 同步刷新该项说明文案)。
- 运维清理调度器新增清理 `runtime/<uid>/<sid>/webPages/` 下超过 `MAO_CLEANUP_SHELL_MAX_AGE_DAYS`(默认 7 天)的网页全文文件,避免落盘无限增长。

## 0.0.162 (2026-09-20)

### 管理后台

- Skills 管理页列表增加分页:系统 / 个人 Skills 表格与移动端卡片按页展示(默认 10 条/页,可选 20/50/100)、关键词过滤、切换 Tab、删除后自动回到合适页码。
- 用量分析「模型」Tab:原通栏「模型 Token 占比」环图改为与新增「用户 Token 占比」环图并排(Top 10 + 其他,中心 K/M/B),补齐单模型时右侧空白。

## 0.0.161 (2026-09-20)

### 管理后台

- 用量分析页头新增自动刷新:可选关闭 / 5s / 10s / 30s / 1m,默认关闭;开启后按间隔强制刷新当前 Tab,页面隐藏或离开本页时暂停。

## 0.0.160 (2026-09-20)

### 管理后台

- 用量分析 Token 紧凑单位由中文「万/亿」改为业界惯例 K/M/B(K=千、M=百万、B=十亿),覆盖总览主指标、窗口构成、模型占比中心值、Token 趋势坐标轴、用户/Agent Token 排行条尾与模型场景/协议 Token 列表;明细表仍保留千分位完整数值,会话/消息等计数指标继续使用万/亿。

## 0.0.159 (2026-09-20)

### 管理后台

- 用量分析首页 UI 收敛:去掉与顶栏重复的页面标题;环比色说明改为 tooltip;总览次指标副文案与主指标对齐;运行态拆分为「实时 / 窗口结果」两组;右侧重复环比表改为窗口构成明细。
- 全局次要文字色 `--mao-muted` 由 `#86868b` 加深为 `#6e6e73`,小字对比度达到 WCAG AA;失败/等待/完成语义色同步对齐(`--mao-danger/warn/success`)。
- 仪表盘数字启用 `tabular-nums`,构成明细数值右对齐;总览主指标字号层级拉开;补齐可点击区域 focus-visible 与 Space 操作。
- 总览空态增加「放宽到近 7 天」;修复分析各 Tab 下钻「调用流水」误入 `/llm-call`(正确路由为 `/llm-calls`)的问题。

## 0.0.158 (2026-09-19)

### 管理后台

- 用量分析环比配色统一口径:上升=红、下降=绿(不再区分指标好坏,Token 消耗等指标同样按方向着色),页面口径说明文案同步更新。

## 0.0.157 (2026-09-19)

### 管理后台

- 修复微信内置浏览器上传 Agent 头像报「请选择 PNG、JPEG 或 WebP 图片」的问题:微信图片编辑器回传的文件 MIME 类型为空或为 `application/octet-stream`,前端改为回退文件扩展名判断。

### 后端

- 头像上传接口兼容 MIME 缺失/通用类型的文件(微信编辑器场景):MIME 为空或 `application/octet-stream` 时按文件魔数判断格式,声明了具体 MIME 但与内容不符时仍拒绝。

## 0.0.156 (2026-09-19)

依据 `docs/plan/2026-09-19-admin-ux-management-review.md` 评审,对管理后台 44 项 UX 与管理功能问题集中修复。

### 后端

- 安全修复:系统技能库 `POST /v1/skill-docs/upload`、`DELETE /v1/skill-docs/:name` 增加 `agent:write` 权限校验(原先任何登录用户可删除/覆盖系统技能并级联清理 Agent 引用)。
- 新增管理端会话运维接口:`DELETE /v1/admin/sessions/:id`、`PUT /v1/admin/sessions/:id/archive`(admin 鉴权,自动落审计)。
- 新增飞书机器人连接状态接口:`GET /v1/admin/feishu-bots/status`(ready/reconnecting/failed/disabled + 最近失败原因)与 `POST /v1/admin/feishu-bots/:id/reconnect`。
- 个人指令列表 `GET /v1/admin/user-commands` 支持 `pageNum`/`pageSize`/`keyword`/`userId` 服务端分页与过滤,总数经 `x-total-count` 响应头透出;不传参数时行为不变。
- 新增管理端用户关联数据接口:Git 凭证 `GET /v1/admin/users/:id/git-credentials`(`user:read`)与 `DELETE /v1/admin/users/:id/git-credentials/:credentialId`(`user:write`,Token 始终脱敏);用户 MCP 服务器 `GET /v1/admin/users/:id/mcp-servers`(admin 鉴权,env 不透出);个人技能 `GET /v1/admin/user-skills?userId=`(返回该用户技能列表)。
- Agent 提示词版本历史 VO 补充 `operatorName`(操作人名称)。

### 管理后台

- Agent 编辑「最佳实践」:表格模式的启用/停用由点击标签改为开关(el-switch);文本模式逐行斑马跳色——启用行按奇偶行交替底色、停用行(`#` 开头)警示色、空行不着色提示会被忽略,行间辨识度更直观。

- 审计日志:新增时间范围与用户筛选(后端能力对齐)、用户名可下钻至该用户会话、详情弹窗补对象 ID、支持 60s 自动刷新。
- 调用流水:新增 CSV 导出(当前筛选生效,上限 1 万条)、操作列「查看会话」跳转、移动端卡片补错误信息/耗时/重试字段、模型筛选改远程搜索、Agent 筛选改下拉。
- 会话管理:列表新增归档/删除运维操作(运行中会话禁删);详情页新增手动刷新、用户/Agent 下钻链接与全量记录 JSON 导出;移除冗余指标卡。
- 会话查看组件:修复单行超长消息不折叠;思考内容补复制按钮;工具调用复制补成功/失败反馈并明示截断。
- 用量分析:运行态卡(运行中/等待审批)可点击下钻;分析下钻链接透传统计周期到调用流水;明细 Top 列表 limit 提至 100 并支持导出 CSV;scope 缓存加 5 分钟 TTL;加载改骨架屏;趋势图补缩放;模型场景分布支持行选中高亮与清除;执行模式显示中文。
- 用户管理:新增批量启用/禁用、账号类型筛选、修改密码入口(顶栏下拉);编辑当前登录用户时禁用状态开关;操作列新增「详情」抽屉,以用户视角聚合查看其会话(可下钻详情)、定时任务、快捷指令、个人技能、Git 凭证(可删除)与个人 MCP 服务器。
- 角色权限:权限勾选按域分组并支持全选/清空;角色编码加格式校验;未保存修改在离开页面时拦截确认;支持跳转按角色查看成员(完整按角色筛选待后端支持)。
- Agent 管理:列表补创建人列、搜索支持回车、默认 Agent 禁止删除并前置禁用;提示词历史展示操作人名称;表单下拉加载失败可重试。
- 模型管理:默认模型加标识;供应商改为可创建下拉(复用 providers 接口);baseUrl 必填校验;编辑时禁止切换模型类型。
- Skills:系统技能删除按钮加权限守卫;「校验/状态」两列合并。
- MCP 服务器:列表接入客户端分页。
- 定时任务:支持跳转任务会话;关键词防抖即查。
- 指令管理:写操作按钮按管理员显隐;个人指令接入服务端分页。
- 飞书机器人:新增连接状态列(失败原因悬停可见)与重连操作;编辑时 App Key 禁改;写操作按钮按管理员显隐。
- 系统设置:已设置的密钥支持清除。
- 登录页补表单校验;退出登录加确认。

---

## 0.0.155 (2026-09-18)

### 管理后台

- 用量分析(首页)专项修复与打磨:
  - 修复 keep-alive 缓存失效:URL query 变化不再导致整页组件销毁重建(Tab 内视图选择等本地状态得以保留,图表不再闪烁)。
  - 修复首次进入页面并发重复请求的问题。
  - 「含自检调用」开关与趋势视图选择(流量/Token/调用/质量)同步进 URL,刷新/分享链接后状态可还原。
  - 修复 ECharts tooltip 对模型/用户/Agent 名称未做 HTML 转义的问题(潜在存储型 XSS)。
  - 用户明细「最后登录」列改用统一时间格式化(北京时间、无效值显示 `-`)。
  - 环比在上期为 0 时显示「新增」标记,不再静默留空。
  - 工具栏展示数据拉取时间,跨 Tab 缓存命中时数据新鲜度一目了然。
  - 总览空态改为 onboarding 引导(去创建 Agent / 去配置模型),不再渲染一排 0。
  - 可访问性:可点击指标卡支持 Space 键激活,链接类元素补 `:focus-visible` 焦点环。
  - 图表轴色/分割线从 CSS 变量读取;`PHASE_COLORS`、数字格式化等重复定义收敛到 chart-options 统一维护。
  - 清理死代码:未使用的 `error` prop、`trafficTrendOption`、`usePeriodMeta`、`periodLabel` 等。

## 0.0.154 (2026-09-18)

### 前端(桌面 / Web / 安卓)

- 多端会话消息实时同步:其他端(桌面/Web/安卓多标签页或双端并存)发来的用户消息现在会实时回显到当前打开的会话中,无需等待任务结束或重新进入会话;回显按消息内容与最后一条乐观消息比对去重,避免本端发送时出现重复消息。

### 后端

- `user_message_saved` WS 事件统一携带 `content` / `images` / `source`:桌面端发送、Side Task 创建、定时任务、微信入站通道均补齐消息内容;飞书入站通道(群聊/私聊/话题/排队消费)新增该事件广播,此前飞书通道的用户消息完全不推送 WS。
- 飞书卡片按钮统一改为小号(`size: 'sm'`):进度卡(取消任务/重试/会话详情)、排队卡(立即发送/取消本次任务/会话详情)、ECP 绑定引导卡(点我绑定)按钮更紧凑,不再显得过大。排队卡「取消本次任务」按钮文案改为「取消排队」。

---

## 0.0.153 (2026-09-18)

### 管理后台

- 修复用量分析模型页 Token 合计重复计算:`llm_call` 与对话/后台 Token 为包含关系,`totalTokens` 改为与总览页一致的 `chat + background` 口径,环形图与占比列不再虚高约一倍。
- 修复模型维度消息聚合漏掉软删除过滤:`selectMessageStatsByModel` 现在排除已删除消息及已删除会话下的消息,与其他维度口径对齐。
- 修复「含自检调用」开关泄漏到全部 Tab:连通性测试排除参数现在仅随模型 Tab 发送,其余 Tab 恢复后端默认口径(排除连通性测试),成功率/失败次数/延迟分位不再被自检调用污染。
- 修复总览页「运行态」卡片误回退:实时快照为 0(合法值)时不再回退显示窗口期统计。
- 修复用户/Agent Token 排行失真:新增独立按 Token 排序截断的 `tokenTop`,排行图不再遗漏消息少但 Token 消耗高的用户/Agent。
- 修复模型筛选(`modelId`)不进 URL 导致切 Tab 后丢失、以及 URL 残留筛选复活的问题,现与 Tab/周期一样双向同步。
- 修复路由守卫 fail-open:用户信息拉取遇非 401/403 异常(网络抖动)时不再放行受保护路由,改为清理登录态进登录页。

## 0.0.152 (2026-09-18)

### 前端(桌面 / Web / 安卓)

- 修复任务执行中刷新页面后,若会话恰好处于模型思考阶段,思考块错误显示「思考完成」但内容仍持续追加的问题:WS 订阅快照(`session_snapshot`)现在会带回服务端思考状态,前端据此恢复「思考中」转圈与实时预览。

### 后端

- `StreamingWsRegistry` 跟踪各会话的思考阶段(`thinking_start`/`thinking_end` 之间),订阅快照在会话运行中且思考中时附带 `thinking: true`;执行结束(含取消/失败/侧路任务/重试路径)统一清理,避免状态残留。

## 0.0.151 (2026-09-18)

### 管理后台

- 修复用量分析各 Tab 空白页:axios 拦截器已将响应解包为 `Result<T>`,分析页 scope 请求误用 `res.data?.data` 导致 payload 恒为空且不进入错误态。改为读取 `result.data`;无数据时回退加载态/空态,避免白屏。

### 后端

- 修复 DeepSeek thinking 模式偶发 400「The reasoning_content in the thinking mode must be passed back to the API」:官方要求携带 `tools` 的请求必须完整回传历史 assistant 的 `reasoning_content`(即使该轮未实际工具调用)。此前仅在字段非空时透传,模型偶发不输出思考时键被省略,导致「点重试续跑同一工具链」稳定失败,而发「继续」新 user 消息能打断校验反而成功。现 DeepSeek 系模型序列化时对 assistant 消息始终带上 `reasoning_content`(缺失补空串);非 DeepSeek 模型仍剥离,避免 GLM 等网关把该字段视为自家签发载体而 400。非视觉模型替换图片占位时同步保留 `reasoningContent`。

## 0.0.150 (2026-09-18)

### 管理后台

- 「系统指令」页升级为「指令管理」:系统指令 / 个人指令双 Tab(对齐 Skills 管理),支持关键词筛选(名称 / 内容 / 用户)与分页。
- 指令内容列表改为三行截断 + 悬停预览 + 「查看」完整弹窗,避免长提示词撑高列表。
- 个人指令 Tab 支持跨用户查看与删除;新建/编辑仍由用户在桌面端完成。

### 后端

- 新增 `GET /v1/admin/user-commands`、`GET/DELETE /v1/admin/user-commands/:userId/:id`,管理员可跨用户查看/删除个人指令(`user_id>0`)。
- 系统指令管理接口返回增加 `userId`/`createdAt`/`updatedAt`,个人指令列表附带 `username`/`displayName`。

### 终端 CLI(mao-cli)

- `mao system-command` 新增 `list-personal` / `get-personal` / `delete-personal`,与管理后台指令管理能力对齐。

## 0.0.149 (2026-09-18)

### 管理后台

- 用量分析 Phase 2:`llm_call` 成为调用质量权威数据源。模型明细增加调用次数、成功率、调用 Token、缓存命中率、首 token/总耗时均值;场景与协议分布可按选中模型切换。
- 趋势 Tab 新增「调用 / 质量」视图:调用与失败次数、成功率与缓存命中率日曲线。
- 会话 Tab 新增调用质量面板(成功率、重试占比、缓存命中、首 token/总耗时 p50/p95)与失败切片 Top(按模型 / 场景)。
- 用户 / Agent 明细合并调用次数与失败数,支持深链到调用流水(`/llm-call?modelId|userId|agentId|scene|success=`);流水页支持 query 预置筛选与 Agent ID 过滤。
- 分析聚合默认排除 `connectivity_test` 自检调用,模型 Tab 可勾选「含自检调用」。

### 后端

- 分维度分析接口接入 `llm_call` 聚合:models / trends / sessions / users / agents 返回调用质量字段;支持 `excludeConnectivity`、models 支持 `modelId`(场景分布按模型过滤)。
- 会话质量分位(首 token / 总耗时 p50、p95)在服务端用 OFFSET 近似计算;模型延迟为均值,避免逐模型分位拖慢 90 天窗口。

### 终端 CLI(mao-cli)

- analytics 分维度接口返回新增质量字段(`callCount` / `callSuccessRate` / `cacheHitRate` / `callQuality` 等),与管理后台 Tab 对齐。

## 0.0.148 (2026-09-18)

### 管理后台

- 用量分析页重构为按维度分 Tab 的分析工作台:总览 / 趋势 / 模型 / 用户 / Agent / 会话。壳层统一周期筛选(今日/昨日/3/7/30/90 天)与时间窗提示,URL 同步 `?tab=&period=`,首屏只加载当前 Tab,切换周期标记其他 Tab 待刷新。
- 总览压缩为「Token 主指标 + 紧凑次指标 + 运行态 + 规则洞察」,不再堆叠全量图表;趋势取消会话/消息双 y 轴,改为小多图;空态说明原因并支持一键放宽到近 7 天。
- 会话 Tab 新增窗口内会话类型(NORMAL/SUBAGENT/SIDE_TASK)与执行模式(CLOUD/LOCAL)结构拆分;环比色约定统一为绿=变好、红=变差。

### 后端

- 新增管理端分维度分析接口:`GET /api/v1/admin/analytics/{overview,trends,models,users,agents,sessions}`,公共参数 `days`/`endOffset`,users/agents 支持 `limit`。按 scope 取数,overview 不再拉取模型/用户/Agent 排行。
- 旧接口 `GET /api/v1/admin/analytics/summary` 保留(mao-cli 与兼容调用方),管理后台前端已切换至分维度接口。
- `llm_usage`/`message` 口径不变:`chatTokens` 来自对话消息,`backgroundTokens` 来自后台调用,合计 `totalTokens`。

### 终端 CLI(mao-cli)

- `mao analytics` 新增分维度子命令:`overview` / `trends` / `models` / `users` / `agents` / `sessions`(users/agents 支持 `--limit`),与管理后台 Tab 一一对应;`summary` 仍可用。

## 0.0.147 (2026-09-18)

### 后端

- 收紧 `create_scheduled_task` 工具描述与使用指南:定时任务仅服务于用户直接表达的定时处理需求,禁止 Agent 在自身任务执行中擅自给自己创建(避免任务结束后定时任务仍长期循环);执行期内的轮询/检查改由 Shell 工具处理。`update_scheduled_task` 描述同步标注维护边界。

## 0.0.146 (2026-09-18)

### 管理后台

- 定时任务管理页放开跨用户操作:持 `session:read` 的管理员可对任意用户的任务启停/删除(原先非本人任务按钮禁用,后端亦返回无权操作)。
- 定时任务管理页新增筛选(关键词/用户/Agent/状态/完结)与完整分页(页码、每页条数、总数);原先仅 `total > 20` 才显示分页且无任何筛选。
- Agent 编辑弹窗「最佳实践」改为双形态:表格形态紧凑管理单条(行拖拽排序、状态标签点击启停,去掉上移/下移与启用开关);文本形态一行一条批量编辑,`#` 开头表示停用,空行忽略。跨 Agent 复制经验可文本全选粘贴;正文强制单行且不得以 `#` 开头,文本形态直接保存前先解析校验。

### 后端

- `PUT/DELETE/GET /v1/scheduled-tasks/:id` 在调用方具备 `session:read` 时放行跨用户操作,与 `/v1/scheduled-tasks/all` 列表门槛一致;Agent 工具链路仍仅限任务归属人。
- `GET /v1/scheduled-tasks/all` 支持 `keyword`/`userId`/`agentId`/`status`/`finished` 筛选参数,与管理后台、`mao scheduled-task list-all` 对齐。

## 0.0.145 (2026-09-18)

### 前端(桌面 / Web / 安卓)

- 任务面板分组图标区分通道:飞书分组(私聊/群聊)使用官方飞书 logo(透明底 SVG),微信 Bot 会话分组使用微信 logo(透明底 PNG),其余云端分组仍用云朵、本地分组仍用文件夹;深色模式下不再出现白底块。

## 0.0.144 (2026-09-18)

### 后端

- 修复 CLOUD Shell / 云端终端全量继承 `process.env` 导致的凭据泄漏:创建会话时剥离 `GIT_TOKEN_*`、`GIT_ASKPASS`、`APP_GIT_CREDENTIAL_SECRET`、`JWT_SECRET`、`MYSQL_*`、`MAO_TOKEN` 等敏感键,仅注入当前用户 Git 凭据;刷新会话环境时按前缀清空残留 `GIT_TOKEN_*`,避免未配置 token 的用户看到他人或运维 shell 带入的 token。
- CLOUD shell / 交互终端在 ECP 飞书登录开启且配置了 `larkAppId` 时,自动注入 `LARKSUITE_CLI_APP_ID` 与 `LARKSUITE_CLI_USER_ACCESS_TOKEN`(用户飞书 UAT),供 lark-cli 以用户身份使用;UAT 走 ECP 换票接口 + 进程内存缓存,ECP 会话失效时不注入。
- 历史里某个 tool_call 缺少对应输出时(含并行工具结果被图片消息拆开),不再把 `No tool output found for tool call` 的 400 卡死会话:补占位结果、重排 tool 组,原会话可继续或重试。
- 飞书进度卡片执行失败时,正文改为展示具体 `error.message`(与客户端对话页「执行异常」一致);无错误信息时仍回退「抱歉,处理您的消息时出现了错误,请稍后再试。」
- 飞书进度卡点「取消任务」后终态文案由「已被下一条指令中断。」改为「任务已取消。」——取消按钮误用了「立即发送」插队的 interrupted 标记。
- 飞书失败进度卡新增「重试」按钮:点击后基于会话历史续跑(不插入新用户消息),并 PATCH 原卡片;仅原发送者可点,会话已结束/执行中时提示不可用。
- 飞书排队卡「取消本次任务」只移出该条排队消息、绝不打断当前执行;回调分派与两类取消路径补打日志(`queue.cancel` / `progress.cancel`),便于线上核对点的是哪张卡;回归用例锁死排队 cancel 不得调用 interrupt / cancelRunning。

### 管理后台

- Skills 管理新增「系统 Skills / 个人 Skills」分 Tab:个人 Tab 聚合展示全站用户上传的个人技能(含所属用户),支持关键词搜索、查看正文与删除;上传仍仅系统 Tab 可用。
- ECP 飞书登录配置新增「lark App ID」;留空则不注入 lark-cli 凭证。

### 后端

- 新增 `GET /admin/user-skills`(列表,`agent:read`)、`GET|DELETE /admin/user-skills/:userId/:name`(详情读 / 删除写,`agent:write`),跨用户聚合个人技能目录供管理后台分 Tab 展示。

## 0.0.143 (2026-09-18)

### 管理后台

- 移除首页「数据概览」,「用量分析」成为新的后台首页:登录、Logo 点击与 404 兜底均进入用量分析;侧边栏将用量分析置于首项,原「运行」分组中的独立入口合并到首页位。非管理员访问首页时按权限软回退到会话 / Agent / 用户 / 设置等首个可用页,避免登录即无权限。
- 移除「运行监控」页面与侧边栏入口;失败率 KPI 下钻改到会话列表(阶段=FAILED)。后端 `/admin/runtime/sessions` 与 CLI `mao runtime` 仍可用。

## 0.0.142 (2026-09-17)

### 管理后台

- 调用流水「用户」「模型」筛选由数字 ID 输入框改为可搜索下拉(用户取自用户列表,模型取自模型配置,含已停用);此前输入名称会被当成非数字 ID 静默丢弃导致筛选不生效。会话 ID 仍支持直接输入。
- 调用流水列表与详情的「缓存」列展示缓存命中率百分比(`缓存Token (命中率%)`,与桌面端使用记录一致)。

## 0.0.141 (2026-09-17)

### 管理后台

- 用量分析「统计周期」新增今日 / 昨日 / 3 天,与原有 7 / 30 / 90 天并列;默认选中「今日」,环比对照前一日。
- KPI 环比色改为红=好、绿=坏(对调原绿好红坏),与业务习惯一致;好坏语义仍由指标本身决定(活跃降=坏,Token 降=好)。

### 后端

- `/admin/analytics/summary` 新增可选参数 `endOffset`(默认 0,clamp 0–365):将统计窗口结束日相对今天前移 N 天,用于「昨日」等固定日窗口;原有 `days` 1–90 语义不变。

### 终端 CLI(mao-cli)

- `mao analytics summary` 支持 `--end-offset`,可查询今日(`--days 1`)与昨日(`--days 1 --end-offset 1`)。

## 0.0.140 (2026-09-17)

### 前端(桌面 / Web / 安卓)

- 新建会话居中输入框字号对齐会话态输入框:正文与占位符由 17px(`--aw-text-body`)改为 14px(`--aw-text-caption`),行距 1.5 不变,快捷指令/文件标签随正文同步缩小;桌面与移动端居中态现在同字号。
- WebSocket 自动重连握手失败会结束在途连接 Promise,避免网络指示已恢复但原发送一直挂起。
- 切换会话后,待办、消息队列和历史上拉的慢响应只写入原会话缓存,不再覆盖当前会话。
- 聚焦模式已加载时新建任务会立即进入聚焦列表。
- 编辑最后一条用户消息并重发时,失败回滚和收尾刷新都绑定开始编辑的那条会话,切换走后不会把当前会话的消息整表换成编辑前快照。
- 边路任务停止、以及边路/子代理重试会等待发送结果:网络不可用时提示失败并恢复按钮,不再把停止入口关掉或把重试按钮卡死。

### 桌面 Electron

- 本地 shell 在 `wait_for` 命中后立刻 `exit` 时先排空 stdout 再判定结果,避免 Linux 上进程退出早于管道输出导致漏掉匹配。

### 后端

- CLOUD shell 同样等 stdout 排空后再把「wait_for + 立即退出」标为完成,避免误报仍在运行或丢掉匹配文本。
- 飞书发送图片/文件工具改为 reply 方式发送:群聊中回复用户触发消息(话题群中正确落入当前话题而非创建新话题),私聊中回复用户最新消息。无入站消息记录时自动回退为 message.create()。
- 后台子代理重试收尾先落库并入队再释放运行标记,避免父代理提前结束把结果标为抑制;正式结果使用完整回答,不再截成 2000 字进度预览。
- `task_create` 按会话当前最大 `sort_order` 追加待办,不再从 0 重计,避免新事项插到既有列表中间。
- 云端 `write_file` / `edit_file` 写回时保留原文件的 CRLF 与 BOM,与 LOCAL 一致,避免 Windows 检出被整文件改成 LF。
- 文件变更行数统计与读文件、CLI 对齐:末尾换行不再算成多出来的空行。

### 管理后台

- 保存角色权限期间禁用勾选和角色切换,避免在途保存响应覆盖刚勾选的权限。

### 终端 CLI(mao-agent)

- 同步 `vendor/localShell.cjs`:与桌面本地 shell 一致,立刻退出的命令不再漏掉 `wait_for` 匹配。
- LOCAL `write_file` / `edit_file` 按路径串行化读写,同一轮并行改同一文件时两次修改都会落盘。
- `--max-duration` 从自己这条执行开始计时;等待其他客户端占用会话时超时只结束本地等待,不会 cancel 对方,随后自己的任务仍受墙钟限制。

## 0.0.139 (2026-09-16)

### 安卓原生

- 启动图标更新为新版品牌标识(深色圆角容器 + 金色羽毛):各密度 `ic_launcher` / `ic_launcher_round` 与自适应图标前景图重新导出,自适应图标背景色对齐 `#1C1C1E`,桌面与启动器显示更清晰。需发新 APK 生效。
- 本次 OTA 仅原生壳图标资源变更;其余前端改动(如全局下拉列表样式)由 App 远程加载页面生效,刷新即得,无需更新 App。

## 0.0.138 (2026-09-16)

### 前端(桌面 / Web / 安卓)

- 全局统一下拉列表样式:所有 `el-select` 下拉选项固定为 14px(`--aw-text-caption`)+ 应用字体 + 常规字重 + 深色文字,选中项统一为「淡蓝底 + 常规字重 + ✓」(不再用 Element Plus 默认的加粗主色)。原因是全局 `--el-font-size-base` 被覆盖为 17px,EP 下拉项默认取它,导致下拉文字比输入框和「模型选择」列表大一圈。各组件自带的 popper 定制(输入区紧凑下拉 12px、仓库选择器等宽布局)优先级更高,保持不变。涉及设置页用量筛选等所有下拉。

## 0.0.137 (2026-09-16)

### 前端(桌面 / Web / 安卓)

- 工作区下拉列表文字过大偏粗的真正原因:全局把 Element Plus 的 `--el-font-size-base` 覆盖成 17px(对齐 App 正文),而 `el-select` 下拉项正好用这个变量,于是比输入框(大号 14px)和「模型选择」列表大一圈。现显式把工作区下拉项定为 14px + 应用字体(`--aw-font-text`)+ 常规字重 + 深色文字,与「模型选择」列表完全一致(0.0.136 只改了字重/选中色,未解决字号)。

## 0.0.136 (2026-09-16)

### 前端(桌面 / Web / 安卓)

- 工作区下拉列表样式与「模型选择」列表对齐:选中项不再用 Element Plus 默认的加粗 + 主色文字,改为深色常规字重 + 淡蓝底标示,普通项文字统一为深色常规字重(适用于移动端「工作区设置」抽屉与新建会话配置条的下拉框)。

## 0.0.135 (2026-09-16)

### 前端(桌面 / Web / 安卓)

- 新建会话居中输入卡:模型名字号回落到与会话态输入框一致(12px),保留放宽后的可视宽度(148px),仍不按字数硬截断。
- 移动端「工作区设置」底部抽屉:工作区下拉框与 Git 地址/分支输入框由小号改为大号(40px、14px 字号,与智能体搜索框一致);「云端/本地」「现有/空白/Git」分段按钮同步放大到 40px 高的 14px 文字,与输入框对齐。

## 0.0.134 (2026-09-16)

### 前端(桌面 / Web / 安卓)

- 新建会话居中输入卡样式修复:工具条上的模型名放大到 14px、可视宽度放宽到 148px,并去掉「固定 8 字 + 省略号」的硬截断(改由 CSS 省略号按实际宽度截断,长名仍可悬停看全名),不再只露出 `ds-v4.1-…`;发送按钮恢复品牌蓝(居中态此前被覆盖成黑色,现与底部会话态一致);移动端「选择智能体」底部抽屉的搜索框由小号改为大号(40px、14px 字号),并与列表之间补上间距,列表改为占满抽屉剩余高度。

## 0.0.133 (2026-09-16)

### 前端(桌面 / Web / 安卓)

- 修复飞书话题群新建任务后「工作区」名跟着话题标题变的问题:话题多会话下会话标题是话题首条消息摘要,不是群名。右侧工作区标签、输入区 chip、左侧 `FEISHU_GROUP` 分组标签均改为按工作区路径合成稳定标识(如 `飞书群{botId}·{chatId前缀}` / `Agent:飞书群…`),不再读取 `session.title`。磁盘上仍是一群共享一个工作区目录,与设计一致。

### 后端

- 飞书任务进度卡片新增「会话详情」按钮(`open_url`):点击在浏览器打开网页端对应会话页 `/tasks/{id}`。执行中与「取消任务」并排,终态卡片仅保留该按钮;「取消任务」回调返回的终态卡同步带上。站点地址取 ECP 配置的 `desktopCallbackUrl` origin(私有化部署需保证该地址为正确公网域名),解析失败时不渲染按钮。定时任务结果回流卡片同样生效。

## 0.0.132 (2026-09-16)

### 前端(桌面 / Web / 安卓)

- 品牌 Logo 重设计:主标识改为深色圆角容器上的金色羽毛,替换原双菱形波浪标。已更新 README 图、桌面/管理后台 `app-icon-small.png` 与 favicon、安卓各密度启动图标;安卓自适应图标背景色对齐为 `#1C1C1E`。设计说明见 `docs/assets/logo-design/`。

### 管理后台

- 同步替换侧边栏与登录页使用的应用图标资源。

## 0.0.131 (2026-09-16)

### 后端

- LLM 调用不再向任何协议(OpenAI Chat / Responses / Anthropic Messages)发送 `max_tokens` 与 `max_output_tokens` 参数,输出上限交由上游/网关默认策略决定;删除 `RESPONSES_MAX_OUTPUT_TOKENS`、`ANTHROPIC_MAX_OUTPUT_TOKENS` 常量,连通性探测客户端同步去掉。

## 0.0.130 (2026-09-16)

### 前端(桌面 / Web / 安卓)

- 新建会话改为中心化 Composer:桌面主区垂直居中展示「问候语 + 大输入框 + 快捷起步」;智能体/模式/工作区收进工具条 chip(点开浮层配置),不再默认铺开智能体网格墙。已有会话仍为底部输入。
- 窄屏(≤768px,含移动 Web/安卓壳)新建会话使用上锚定紧凑态:缩短编辑区、chip 可换行、智能体与工作区改为底部抽屉,避免软键盘顶出发送按钮。

## 0.0.129 (2026-09-16)

### 终端 CLI(mao-cli)

- `MAO_BASE_URL` / `--base-url` 与 `mao-agent` 共用:可写站点根、`/api` 或 `/api/v1`,CLI 自行归一到 `/api/v1`。

### 后端

- 飞书通道 ECP/绑定引导改为交互卡片按钮跳转(`open_url`),不再把超长授权 URL 写进文本;私聊与群聊一致。卡片发送失败时回退文案提示去桌面/网页登录,避免链接被飞书截断。
- 飞书 ECP 引导卡片橙色标题为「新用户绑定」;正文「请点击下方按钮完成ECP用户绑定」,按钮「点我绑定」。

### 终端 CLI(mao-agent)

- 改读 `MAO_BASE_URL`,移除 `MAO_AGENT_BASE_URL`。同样接受站点根、`/api`、`/api/v1`。

## 0.0.128 (2026-09-16)

### 后端

- 修复飞书通道 ECP 登录引导只有文案没有链接:ECP 返回的 OAuth `state` 超过 `feishu_pending_binding_message.state` 的 64 字符上限,写入待重放记录失败后把已拿到的授权 URL 也清掉了。现将列扩至 768(V116),落库失败仍把登录链接发给用户。
- 修复 Responses 协议会话中途切到 Chat Completions 协议后请求 400(`messages[N]: missing field \`type\``):Responses 流式 toolCall 补写 `type: 'function'`;Chat Completions 序列化对历史缺失 `type` 的 tool_calls 兜底为 `function`。升级后端后原会话可继续使用,无需清空历史。
- 修复定时任务执行失败后仍向飞书/微信推送上一轮对话旧回复的问题:`liveExecution`(WS 执行路径)内部 catch 吞掉异常并落会话 FAILED 终态后正常返回,`executeTask` 误判为成功、标记 COMPLETED 并调用结果推送;`sendFeishuReplyIfApplicable` / `sendWeixinReplyIfApplicable` 从会话历史取最后一条 ASSISTANT 消息(本次失败未产出新消息,取到的是上一轮旧回复)推给用户。现 `liveExecution` 返回后回读会话真实终态,FAILED/CANCELLED 时不标 COMPLETED、不推送回复。
- 修复飞书群聊【群内最近消息】中机器人卡片仍显示“请升级至最新版本客户端,以查看内容”的问题:此前仅修了引用预取的消息详情接口,`im.message.receive_v1` 事件里 interactive 卡片 content 被飞书降级为该文案后仍会原样落入群消息日志;现归一化与卡片文本提取均过滤该降级文案,入库占位为 `[卡片消息]`,并后台按 messageId 拉详情(`card_msg_content_type=user_card_content`)回填真实卡片文本。

## 0.0.127 (2026-09-26)

### 后端

- 子代理角色对齐 Codex 并 thin 扩展:内置角色改为 `default` / `explorer` / `worker` / `reviewer`;新增通用 `default`(完整继承父会话);`researcher`→`explorer`、`coder→worker` 运行时归一并由 Flyway 迁移历史 `agent_type`;`reviewer` 保留为正式 thin 角色(仅 description + system prompt,无工具黑名单)。
- `spawn_subagent` 工具 schema 暴露四角色枚举,工具说明与 PromptEngine 写入委派纪律(选型、并行写入范围、worker 归属权、审查闭环 reviewer → worker → followup reviewer)。
- 未知 `agent_type` 仍报错;仅旧名 researcher/coder 归一;崩溃恢复与 followup 兼容历史类型。

### 前端(桌面 / Web / 安卓)

- 子代理工具卡片继续透传 `agent_type` 英文名(本轮不做中文映射);相关测试样例改为新角色名。

## 0.0.126 (2026-09-15)

### 前端(桌面 / Web / 安卓)

- 用户设置「使用记录」列表新增「缓存」「首字」列,展示缓存命中 token 数(含命中率百分比)与首 token 延迟;筛选区与分页改为小尺寸,表格列宽收紧以适配内容,每页条数支持 10/20/50/100。

## 0.0.125 (2026-09-15)

### 前端(桌面 / Web / 安卓)

- 修复云端工作区聊天内「文件变更」面板右键「下载此文件」提示「当前模式不支持下载」:`fileProvider` 注入的是 ComputedRef,原先未解包导致下载能力判断失败。

## 0.0.124 (2026-09-16)

### 后端

- 飞书话题群多会话:话题群内一个话题自动映射为一个 Mao 会话,用户在新话题发消息即创建新会话,在已有话题发消息自动路由到对应会话,多会话独立并行执行。
- 修复话题内消息 `parent_id` 误判为用户引用:话题内回复的 `parent_id` 恒指向话题根(飞书官方行为),现在仅当 `parent_id ≠ root_id`(显式引用话题内某条回复)时才注入引用内容。
- 话题内消息无需 @机器人 即可触发(话题会话映射命中时跳过 mention 门禁);非话题群行为不变。
- 新增 `feishu_thread_session` 映射表;`feishu_group_message_log` 新增 `thread_id` 列,话题内上下文按话题过滤注入。

## 0.0.123 (2026-09-15)

### 后端

- 新增 `llm_call` 表与统一落库:`LlmAdapterFacade` / `LlmChatClient` 装饰器记录每次 LLM 调用的场景、Token、首字/总耗时、成败与重试次数;对话主链路、压缩、标题、Git 提交信息、危险评估、语音合成、飞书摘要、连通性测试均写入。
- 新增 `GET /v1/admin/llm-calls`(管理员全站)与 `GET /v1/llm-calls/me`(当前用户)分页查询接口。

### 管理后台

- 新增「运行 → 调用流水」页:按用户/会话/模型/场景/时间筛选,展示 Token 与延迟明细。

### 前端(桌面 / Web / 安卓)

- 设置新增「使用记录」Tab,查看当前用户的 LLM 调用流水,支持跳转关联会话。

### 终端 CLI(mao-cli)

- 新增 `mao llm-call me` / `mao llm-call list` 查询调用流水(见 `skills/mao-cli/reference/llm-call.md`)。
- 产品文档与 CLI 帮助改为用 `mao.example.com` 占位;开源示例域名仅在配置说明中作为最低优先级回落提及。

### 终端 CLI(mao-agent)

- `--help` 不再展示开源示例域名,改为提示私有化必须设置 `MAO_AGENT_BASE_URL`。

## 0.0.122 (2026-09-15)

### 后端

- CLOUD 会话技能同步由「复制目录」改为「符号链接」:runtime 下的 `skills/<name>` 现在指向技能源目录(全局技能 / 用户技能),不再拷贝文件。定时清理删除 runtime 副本只删链接、不动源,此前「技能内 CLI 用 `npm install . -g` 全局安装后,runtime 副本被清理导致 CLI 失效」的问题不再出现;技能源更新后链接即时生效,无需重新同步。旧版本遗留的实体副本目录与悬空链接会在下次同步时自动替换为正确链接。

## 0.0.121 (2026-09-15)

### 后端

- 修复 ECP 会话 renew 必然失败:`renewSession` 不再复用登录解析,ECP renew 响应不含用户信息,此前被误判为「ECP 响应缺少用户邮箱」→ 会话被标记 `FAILED` → CLOUD shell 不再注入 `ECP_TOKEN`,`bigdata-cli` 等内部 CLI 报「ECP 登录态已失效」。
- ECP 会话不可用(renew 失败 / 已过期 / 解密失败)时清理虚拟 HOME 的 AccessOne 布局,避免下游 CLI 读到已作废旧票而误报登录态失效;同时输出跳过注入的原因日志。
- ECP 开启时飞书通道入站增加有效票闸门:已绑定但没有可用 `user_ecp_session`(缺票 / 过期 / renew 失败)也视为未授权,引导改为 ECP 飞书登录(不再优先 Mao 飞书 OAuth)。登录成功后按邮箱对齐已绑用户并重放原消息;邮箱对不上则不改绑,飞书侧提示联系管理员。

### 管理后台

- ECP 配置面板补充:开启后飞书机器人通道会检查发送者是否有有效 ECP 票。

### 前端(桌面 / Web / 安卓)

- 设置页「飞书Bot」在 ECP 开启时说明:仅绑定飞书仍不能触发任务,还需用同一邮箱完成 ECP 飞书登录。

## 0.0.120 (2026-09-14)

### 后端

- 飞书进度卡片终态补充任务耗时:完成/失败/取消时状态行改为「状态:处理完成 · 共 8 轮 · 耗时 8 分 26 秒」(不足 1 分钟只显示秒、超过 1 小时显示「x 小时 y 分」);执行中仍为「第 n 轮」且不显示耗时。耗时从卡片就绪(任务开始执行)时刻起算,排队等待不计入;崩溃恢复续跑的任务从崩溃前的开始时间起算。

## 0.0.119 (2026-09-14)

### 后端

- 取消 ECP 与其它登录方式互斥:密码/LDAP、Mao 飞书、公司 SSO 换票、ECP 飞书可同时可用;仅 ECP 飞书登录会写入 `user_ecp_session` 供 CLOUD shell 注入。
- CLOUD shell / 云端终端:用户有有效 ECP 票时除 AccessOne 布局外,额外注入环境变量 `ECP_TOKEN`(与 `MAO_TOKEN` 并列)。
- 修复 ECP 飞书登录回调报「state 不存在」:OAuth state 改为使用 ECP `feishu-authorizations` 返回值(不再预生成 UUID)。
- 迁移 V112:`ecp_oauth_state.state` 扩至 `VARCHAR(768)`,修复 `/auth/ecp/feishu/start` 写入 ECP state 时 500。

### 管理后台

- ECP 配置面板说明改为「新增登录入口、不关闭其它方式」。

### 前端(桌面 / Web / 安卓)

- ECP 与 Mao 飞书并存时登录页分别展示「飞书登录」「ECP 飞书登录」两个按钮;飞书 Bot 未绑定引导优先 Mao 飞书 OAuth,无配置时再回退 ECP。
- 修复安卓软键盘遮挡新建会话输入区:键盘弹出后可视高度骤减时,输入卡片改为参与收缩,新建会话的智能体/模式/工作区配置区内部滚动,输入框与发送按钮始终完整可见(进入会话后原本就正常)。

## 0.0.118 (2026-09-14)

### 后端

- 新增 ECP 原生飞书登录:管理后台「集成配置 → ECP 飞书登录」开启后,密码/LDAP/Mao 飞书/公司 SSO 换票入口关闭;飞书授权经 ECP `feishu-authorizations` + `sessions` 换票,按邮箱映射 Mao 用户并加密存储 `sessionToken`;常驻 `EcpRenewScheduler` 在到期前约 30 分钟 renew;CLOUD shell/云端终端向虚拟 HOME 写入 AccessOne 兼容目录供 `bigdata-cli` 等读取 Bearer。迁移 V111(`auth.ecp.config`、`user_ecp_session`、`ecp_oauth_state`)。设计见 `docs/plan/2026-09-14-ecp-native-login-technical-design.md`。
- ECP 开启时飞书 Bot 未绑定引导改为 ECP 飞书授权链接;登录成功后按消息事件中的 `union_id` 写入 `feishu_binding` 并重放原消息。设置页飞书绑定仍走 Mao 飞书 OAuth(仅绑定、非登录),ECP 模式下回调豁免。
- 新增 `POST /v1/auth/admin/login`:ECP 开启时管理后台仍可用账号密码登录;`POST /v1/auth/login` 继续对桌面端拒绝。

### 管理后台

- 系统设置新增 ECP 飞书登录配置面板;ECP 开启时登录页保留账号密码,并可选飞书登录;回调路由 `/admin/auth/ecp/feishu-callback`。账号密码走 `POST /v1/auth/admin/login`(桌面端仍仅 ECP 飞书)。

### 前端(桌面 / Web / 安卓)

- ECP 开启时登录页仅飞书入口;Web/Electron 回调 `/auth/ecp/feishu-callback`;Electron 授权窗支持 ECP 回调路径并可直接完成换票。设置页「飞书Bot」在 ECP 模式下说明绑定与全站登录分离。

### 桌面 Electron

- `openFeishuAuthWindow` 支持自定义回调路径,ECP 飞书登录完成后提取 `code`/`state` 交回主窗口。

## 0.0.117 (2026-09-14)

### 前端(桌面 / Web / 安卓)

- 修复 Web Embed SDK 对话窗口回显时执行过程未自动折叠:后端每个工具轮次落一条无正文 ASSISTANT,历史加载后会拆成一叠「1 个工具调用」。现在相邻纯工具 assistant 消息合并为一组折叠卡片(对齐桌面端 `mergeAdjacentToolMessages`),带正文的消息不吸收、流式气泡不参与合并。

## 0.0.116 (2026-09-14)

### 前端(桌面 / Web / 安卓)

- Web Embed SDK 页面操作修复 Element `el-select`(`filterable` + `multiple`)下拉点不中:`page_click` 改为派发完整鼠标序列(pointer/mousedown/mouseup/click),点中下拉 option 后回读并返回 `observation.selected`,避免 Agent 把「只填了关键字」当成已选中服务。快照为 Element 表单项内过滤框补充 form-item 标题标签。新增多选下拉集成测试覆盖「过滤框 fill → 重新 inspect → 点 option → 确认选中」完整路径。
- 同上场景继续修复「填了服务名但下拉仍不展开、没有可点选项」:`page_fill` 对 combobox/搜索框在建议为空时自动对容器派发鼠标序列展开弹层并重放输入,返回 `observation.dropdownOpened`,避免只 focus/input 导致 Element 多选弹层未打开。

### 后端

- 修复会话压缩后 Agent 把系统交接指令当成用户原话、停下当前任务:压缩指令改为 `<system-notice>` 系统通知(明确不是用户说的话,禁止写入交接正文);压缩后若增量历史里没有真实用户消息,自动把最新一条用户原话接回交接消息之后,后续真实请求不再被「不要继续执行」盖掉。
- 修复后端重启后续跑飞书任务时进度卡片停在重启那一刻:进度卡映射查询被 `Db` 转成 cam