# 实施进度 > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。 最后更新:2026-08-03(Web Mermaid 直出 + Markdown 围栏容错,bump 0.60.24) --- ## 状态 | Phase | 标题 | 状态 | 备注 | |---|---|---|---| | 1-3 | 骨架 + Skill + run_python | ✅ | 多 skill(coding/proposal/ppt/research/documents/imagegen/videogen/review/patent);CoreCoder 唯一匹配 edit;敏感 env 过滤 | | 4 | 演化性能力 | 🟡 | Model Profile + Probing ✅;版本化 prompt 未做 | | 5 | Eval Suite | ⏸ 不做 | dogfooding 替代,probe 覆盖健康检查 | | 6 | 长任务工程化 | 🟡 | task + 恢复 ✅;双层记忆 ✅;context 压缩 ✅(加压力门槛);长会话中段折叠摘要 ✅(§8.8 Phase 2) | | 7 | 打磨 | ❌ | Docker 沙盒 / 更多 skill | | §7 SaaS | DESIGN §7 路线 | 🟡 | A 事件流化 ✅;B 完工 ✅;D `/v1` JSON API ✅;D' 过渡 auth + dev SPA ✅;单活 run 锁 + cancel ✅;0004 schema 瘦身 ✅;入口归位 ✅;真 OIDC 待;**C Step 1-3 + 3d ✅(Executor + Docker 池 + DockerExecutor + fs 工具进容器)+ Step 5 部署前置对账 ✅ + 容器资源 yaml + 应用层磁盘配额 ✅ + dogfood 网络放开 + 容器内 pip/npm 源持久化 ✅**;**Step 4 完整 egress proxy + Step 3b PGID kill 协议延后到外部用户开放前**(还需 egress proxy + xfs project quota OS 层硬化,§7.5 落地清单 #2 #4)。 | --- ## 已完成关键能力 ### 2026-08-03 - **08-03 / 0.60.24 / Web Mermaid 直出 + Markdown 围栏容错**:模型偶发用同长度围栏嵌套 Markdown/Mermaid 示例,CommonMark 会把后续正文吞进未闭合代码块;新增仅针对该明确形态的前后端确定性修复,提示词统一要求外层使用更长异类围栏,历史上下文加载时同样修正且不批量回写生产数据。聊天页本地 vendoring Mermaid 11.16.0,仅在助手文字段定稿后顺序渲染 `language-mermaid`,采用 strict 安全级别、文本/边数上限,语法错误或组件不可用时保留源码并提示;真实 Edge 冒烟确认中文流程图与 XYChart 柱线组合图可生成 SVG。Python 27 项、Node 9 项、Ruff、JS/Python 语法及 diff 检查通过;无 schema、migration、HTTP API 或 Python 依赖变化。 - **08-03 / 0.60.23 / Office→PDF 组件感知 + 展示路径兼容**:生产 task `92ac20cf` 暴露两层问题:上传消息给出 user-root 相对的 `测试pdf/x.doc`,host tool 又按 task_dir 拼接导致首次找不到;改用裸文件名后,host 仅安装 `libreoffice-impress` 却因只检测 `soffice` 而错误宣称支持 DOC,最终 Writer 导入返回 `source file could not be loaded`。现 `office_to_pdf` 同时解析 task 相对、user-root 展示路径与 `/workspace` 路径;Debian/Ubuntu 按 Writer/Calc/Impress 实际安装包缓存支持后缀,工具 schema 只声明可用格式,缺组件在启动转换前给出明确管理员提示。RUN bootstrap 与故障表同步要求 host 安装三组件;相关 30 项 unittest、Python 编译及 diff 格式检查通过,本机无 LibreOffice,真实 `.doc/.xlsx/.pptx` 冒烟留部署 host;无 schema、migration、HTTP API 或 Python 依赖变化,无需重建沙箱镜像。 - **08-03 / 0.60.22 / 通用 PDF 路由修复 + host Office 转换**:复盘生产 task 发现通用 system prompt 只展示 `report --format docx`,模型遂误判平台渲染器只能出 Word,进而在 Docker 内探测 LibreOffice、临时安装 WeasyPrint 并手写转换脚本;现补齐 `report --format pdf` 唯一入口,新写 Markdown 报告直接由沙盒 Chromium 出 PDF。新增按 host `soffice` 实际可用性注册的 `office_to_pdf` typed tool,已有 DOCX/PPTX/XLSX/ODF 文件通过 backend host LibreOffice 转换,复用 `/workspace`→user_root 路径翻译并强制用户目录边界,Docker 内不再探测宿主命令。相关 34 项 unittest、Python 编译及 diff 格式检查通过;本机未安装 LibreOffice,真实 soffice 冒烟留部署 host 执行;无 schema、migration、HTTP API 或依赖变化,新增 host 工具无需重建沙箱镜像。 ### 2026-07-31 - **07-31 / 0.60.21 / GPT Image 2 尺寸与质量参数**:`gpt_image` 开放 `size`(`auto` 或合法 `WIDTHxHEIGHT`)与 `quality`(`auto/low/medium/high`),按官方约束校验 16 倍数、3:1 比例、边长及总像素范围,并从 PNG IHDR 回填实际尺寸到 banner、meta 与 usage;真实 `1024x1024 + low` 请求返回同尺寸 PNG。同步确认 unifyllm `/images/edits` 的 JSON、multipart 及 Responses image_generation 三条改图路径当前均不可用,故不暴露必失败的参考图参数,继续明确引导改图切 Seedream;手工脚本显式 mock 用量记录,杜绝真实探针触库。相关 41 项 unittest 通过(1 项测试库门控跳过),Python 编译与 diff 格式检查通过;无 schema、migration 或 HTTP API 变化。 - **07-31 / 0.60.20 / GPT 生图模型路由修复**:unifyllm 的 Images 端点已由历史文本渠道名切换为专用图像模型,媒体配置同步改用当前 `/v1/models` 开放的 `gpt-image-2`,保持既有 `/images/generations`、prompt-only 工具契约与 `b64_json` 解析不变;手工干跑增加模型 ID 回归断言。真实 OpenAI 格式请求返回 HTTP 200 及完整 base64 图像,相关 16 项 unittest 通过(1 项按环境跳过),diff 格式检查通过;未连接数据库、无 schema、migration 或 API 变化。 - **07-31 / 0.60.19 / 清空对话 500 修复**:清空接口补查后续标题重置逻辑所需的 `title_source`,修复仅查询 `run_status` 后访问缺失字段导致的 `AttributeError` 与事务回滚;原有 DB 路由用例已覆盖自动标题和人工标题两类清空行为,无 schema、migration、API 或数据语义变化。16 项无 DB 路由测试、Python 编译、致命静态检查与 diff 格式检查通过;未设置 `ZCBOT_TEST_DB_URL`,DB 路由测试按安全门控跳过。 - **07-31 / 0.60.18 / 任务列表默认按最近更新时间排序**:普通任务列表的前后端默认排序统一由创建时间倒序改为更新时间倒序,最近继续处理的对话会回到顶部;显式 `ordering=-created_at` 等既有排序参数和全部下拉选项保持兼容。新增默认值、非法参数回退及创建时间兼容 3 项单元测试,Python/JavaScript 语法与 diff 格式检查通过;无 schema、migration 或依赖变化。 - **07-31 / 0.60.17 / 消息 JSONB NUL 字符兼容**:外部工具从论文、PDF 等来源提取的文本可能夹带真实 NUL,Python JSON 会编码为 PostgreSQL `jsonb` 无法转换成 text 的 `\u0000`,导致 tool message INSERT 回滚并中断 run。新增递归清洗并在 `Message.payload` ORM 写入边界统一守门,`Session.append` 同步清洗内存上下文,保留制表符、换行及其余 Unicode;无 schema、migration 或存量数据改动。相关 6 项单元测试、Python 编译及 diff 格式检查通过;未设置 `ZCBOT_TEST_DB_URL`,未连接数据库。 - **07-31 / 0.60.16 / platform 历史 task 稳定进入 + 新建任务名称可选**:普通 platform iframe 不再复用 localStorage 缓存 JWT 抢跑,始终等待父页面本次 `zcbot-token` 后再进入初始 task,消除旧 token 401 后界面永久停在“加载中”的时序;企微/App fragment 登录路径不变。`POST /v1/tasks` 省略/留空 name 且提供 working_dir 时自动以“新对话”占位并启用首条消息标题生成,旧 `auto_title` 语义继续兼容;快速新对话不再发送冗余字段,自定义创建统一为工作目录必填、任务名选填,并换用可搜索/可创建的目录组合框。27 项无 DB 测试、Python 编译、3 个 JavaScript 语法及 diff 格式检查通过;DB 路由用例已补,未配置 `ZCBOT_TEST_DB_URL` 时安全跳过。 - **07-31 / 0.60.15 / 嵌入初始 task 直接进入工作目录**:platform iframe、企业微信和 App 携带 `task_id` 首次进入时,由 `enterApp` 直接选择目标 task,跳过新对话草稿及 user_root 文件面板初始化,随后沿既有 `selectTask` 链路同步 `working_dir`;401 重签仍不重复定位,普通登录空态不变。JavaScript 语法、7 项静态前端测试及 diff 格式检查通过;无 API、schema、migration 或依赖变化。 - **07-31 / 0.60.14 / 清空对话后自动标题重生成**:tasks 新增 `title_source`(auto/manual/fixed)与 `auto_title_version`(0024 migration);快速新对话的自动标题在清空后重置为“新对话”,下一条消息重新生成,人工重命名及微信/企微/调度固定标题保持不变。版本闸阻止清空前在途标题跨轮次写回;历史任务无法可靠判定“自动命名后又人工改名”,除当前 pending 外保守回填 manual。相关 18 项无 DB 测试、Python 编译、Alembic 单 head 与 diff 格式检查通过;DB 路由用例已补,未配置 `ZCBOT_TEST_DB_URL` 时按安全门控跳过。 - **07-31 / 0.60.13 / 渠道常驻会话操作收口**:微信与企业微信的 web 只读镜像只保留导出和清空对话,隐藏普通任务才适用的完成、废弃、重命名和删除操作,顶部独立完成按钮同步隐藏;普通任务菜单不变。纯前端行为修正,无 API、schema、migration 或依赖变化;JavaScript 语法及 diff 格式检查通过。 ### 2026-07-29 - **07-29 / 0.60.12 / 任务与文件列表右键菜单**:任务行、文件行和目录行增加原生 `contextmenu` 入口,直接复用既有 `taskMenuItems` / `fileMenuItems`,因此与「⋯」按钮保持相同权限、状态禁用、确认弹框和后端调用;单例浮层菜单增加按鼠标坐标定位及视口双向钳制,右键任务不切换当前会话、右键文件不触发打开。纯前端加法,无 API、schema、migration 或依赖变化;3 个 JS 文件语法及 diff 格式检查通过,浏览器实例不可用故真实鼠标回放留页面复核。 - **07-29 / 0.60.11 / Web 对话流去卡片化 + 工具活动合并 + 语音状态修复**:工作台背景统一以纯白为主,浅灰仅保留在 hover、代码块等局部状态;助手历史/实时回复移除重复角色标签、大卡片边框、左色条与运行态红框,改为左锚开放文档流。历史态按 `tool_call_id` 将 assistant 调用与 tool result 合并,实时态复用同一 details 原位更新结果,统一为默认折叠单行活动项(执行中/成功/失败状态明确),产物 chip、媒体 banner 与后台进程活化继续保留;顶部 task progress dock 的位置和逻辑未改。另修短按语音转连续录音时直接覆写按钮 `textContent`、销毁 SVG/label 导致停止后无法恢复“语音”的问题,收口为 class/label/ARIA 状态函数。相关 Python 24 项 + Node 5 项及 JS 语法、diff 格式检查通过;无 API、schema、migration 或依赖变化。 - **07-29 / 0.60.10 / Web 工作台移动端空间优化**:根据第二轮手机实图反馈把暖白/浅米灰改回中性白色,桌面助手回答、工具卡与任务进度恢复左侧阅读轴;手机端压缩顶栏、Tab、任务操作区、模型区、消息间距和 composer,高频操作键由 44px 收至 40px、输入框最低高度由 64px 收至 52px,并隐藏空闲态“请选择文件保存位置/就绪”占位行,运行、上传与错误等有效状态提示继续按需显示。相关 Python 21 项 + Node 5 项及 JS 语法、diff 格式检查通过;无 API、schema、migration 或依赖变化。 - **07-29 / 0.60.9 / Web 工作台视觉复核修正(桌面 + 手机实图)**:据上线截图把冷蓝灰改为暖白/浅米灰;任务与文件行恢复明确分隔,助手消息收进居中 52rem 文档型回答块,工具与进度对齐同一阅读轨道;同时修掉 `task_progress`-only assistant 被过滤工具后仍留下空“助手”壳。手机端 composer 改为独立状态行 + 3 个 44px SVG 图标键 + 发送键,缩短 placeholder、加入安全区并压缩操作/模型区;录音与润色状态改只更新 label/ARIA,不再覆盖 SVG DOM。相关 Python 21 项 + Node 5 项及 JS 语法、HTML/SVG 结构、CSS 括号检查通过;无 API、schema、migration 或依赖变化。 - **07-29 / 0.60.8 / Web 工作台 Visual Polish v1**:新增零构建 `web/static/ui.css` 作为内部轻量视觉基础层,集中颜色、间距、圆角、阴影与 focus/disabled 等控件状态;收敛三栏层级、任务/资源导航、对话正文与工具过程块、输入区、文件栏、modal/toast 及 ≤640px 触控尺寸,不改 DOM 契约、API 或业务交互。新增 CSS 文件接入、MIME 与 `no-cache` 回归测试;相关 Python 20 项 + Node 5 项通过,浏览器连接不可用故截图级视觉验收留真实页面复核。 - **07-29 / 0.60.7 / 组合框内直接新建工作目录**:新对话目录 combobox 增加 allow-create 语义:输入时继续过滤已有目录,无完全同名项时在同一下拉内提供“创建工作目录「名称」”,明确选择后才进入新建状态,失焦搜索词不会误提交;删除外部新建按钮与二级输入框。纯前端改动,无 API、schema、migration 或目录绑定语义变化;全量 387 测试通过(17 项按环境跳过)。 - **07-29 / 0.60.6 / 新对话目录选择改可搜索组合框**:将上一版独立搜索框 + 原生 select 合并为单一 combobox,同一输入区完成已选目录展示、即时过滤和下拉选择,并补方向键、Enter、Esc、失焦恢复与 ARIA 状态;最近目录置顶、明确选择 working_dir 和新建目录入口均保持不变。纯前端改动,无依赖、API、schema 或 migration;全量 387 测试通过(17 项按环境跳过)。 - **07-29 / 0.60.5 / 新对话目录选择提速**:保留首发前明确选择 working_dir 的归档约束;本机最近实际使用目录在候选中自动选中、置顶并标记,目录达到 6 个时按需显示搜索框,目录较少时维持原有简洁界面;同步精简“文件保存位置”文案。纯前端改动,无 API、schema、migration 或目录绑定语义变化;相关静态资源与无 DB 路由 18 项测试通过。 - **07-29 / 0.60.4 / 工具失败信号分层 + run_python 同错保护 + wire 健康观测**:管理端保留近7天低阈值全量事实,默认把近24h活跃数据拆成跨任务系统性故障、单任务反复失败、按设计非零退出的质量门,已归零记录沉入历史区;空输出 shell 关联最近 assistant tool_call 补 `search/no match` / `dependency probe` 等稳定类别。RepeatGuard 仅将 `run_python` traceback+非零退出纳入同错 streak,普通 shell exit 1 保持原语义。新增只读 `/v1/admin/tool-wire-health`,按 model_profile+tool 对既有 `tool_salvaged/tool_malformed` 计算24h/7d抢救率,无新表、migration、索引或执行策略变化。全量387测试通过(17项按环境跳过)。 ### 2026-07-28 - **07-28 / 0.60.3 / Web、渠道、定时任务统一 run 生命周期**:新增 `web/run_lifecycle.py`,收口 task 行锁/忙碌判断、user 消息与 running 原子提交、broker/inflight 登记及调度失败转 error;网页、微信/企微、定时任务全部消费已持久化轮次,消除渠道与 scheduler 原有的 worker 启动前丢输入窗口。Web 模型降级/自动标题、渠道回复、定时结果统计继续留在各自模块,CLI 保留旧 `run(message)`,无新表、migration、队列或 run 实体。全量 379 测试通过(17 项按环境跳过)。 ### 2026-07 - **07-27 / 0.60.2 / Web 消息接收事务化(消除 202 后丢输入窗口)**:`POST /messages` 先完成媒体 variant 校验,再在 task 行锁保护下把用户消息与 `run_status=running` 同事务提交;Web worker 新增已持久化轮次入口,只消费 Session 末尾 user 而不重复 append,CLI/渠道/定时入口继续走旧入口保持兼容。后台 coroutine 调度失败会把 task 收敛为 error,不再留下假 running;不新增 runs 表、队列或 migration。全量 375 测试通过(17 项按环境跳过),新增 DB 路由测试因未设置 `ZCBOT_TEST_DB_URL` 按安全门控跳过。 - **07-27 / 0.60.1 / 联网搜索动态时效约束**:真实 task `24d6b609` 在 2026-07-27、用户未指定年份时,`deepseek_v4.flash` 两次自行给 `web_search` query 加 `2025`;工具原样转发且默认 `freshness=noLimit`,根因是 system prompt 虽注入 today 却仅标作宪法文件命名用途,工具 schema 也无年份策略。修为 system prompt 首段按 `Asia/Shanghai` 动态注入当前时间/日期/年份,并给唯一规则:未指定时间则 query 无年份,“最新/当前/近期”按当前日期配合 freshness,明确时间窗则服从用户;`web_search` 的 query/freshness schema 再局部强化。5 项定向测试通过。 - **07-27 / 0.60.0 / 目录优先的新对话草稿页 + 首条消息自动命名**:登录未选任务与左栏「+ 新对话」统一进入不落 DB 的草稿页,选择/新建 working_dir 后首发才创建 task,避免空任务;原完整表单保留为「自定义」。新增 0023 `tasks.auto_title_pending` 一次性闸与 `auto_title` 可选创建字段,首条消息并行生成短标题(`usage_events.kind=task_title`),只改 task 显示名不动目录,人工 PATCH name 清闸且条件 UPDATE 防在途覆盖;失败保留「新对话」不阻塞主 run。 - **07-27 / 0.59.6 / 对话内安全重命名 working_dir**:新增 `rename_working_dir` 受控工具,agent run 内只登记目标 leaf 名,正常回复结束并把当前 task 退出 running 后再落地,避免 executor/system prompt/宿主工具仍握旧 cwd;取消或失败不执行。文件面板与对话路径共用 `core/working_dirs.py` DB-aware 原语:锁定并同步更新共享目录的全部 task、活跃邻居与 no-subtask 冲突安全拒绝、DB UPDATE 后再做 FS rename。system prompt 明确当前 task_dir 禁走 shell/run_python 直接改名,定时 run 不挂该工具;无新增 schema/migration。全量 357 测试通过(17 项按环境跳过),DB 路由测试未设 `ZCBOT_TEST_DB_URL` 时按安全门控跳过。 - **07-27 / 0.59.5 / Seedance 图生视频(单图首帧 + 最多 9 张多图参考)**:`seedance` 在原文生视频契约上向后兼容增加 `image`(唯一 `first_frame`)与 `reference_images`(0-9 张 `reference_image`),两者合计最多 9 张;复用 image_ref 的 task/user_root 三形态路径解析、越界防护与 base64 内联,新增单图 10MB / 合计 30MB 请求体闸。请求按有无图片切图生/文生单价,banner/meta/usage snapshot 记录 mode、首帧及参考图;videogen skill、工具系统提示、RUN 与能力清单同步。5 个离线单测覆盖单图、多图、文生兼容、越界与超量拒绝。 - **07-24 / 0.59.4 / kb 冷启动契约(修「放进知识库」误存记忆)+ 查看原件 + 条目精简**:①真实用户事故——说「放进我的知识库」被 agent 写进 `.memory/`:根因 `kb_block` 零库返回空串,模型不知道 KB 机制存在(memory 空契约常驻恰是为解冷启动,kb 当时没照抄这课)。修 = 零库注 ~百 token 冷启动契约(建库步骤 / INDEX 行格式 / 「成篇资料进 KB、短事实进记忆」分工;仅用户明确要求才建库),建库落盘后下轮 build_agent 自然切全量注入;skill 方案再否(召回同为概率召回还多一层间接,且违背「机制而非 skill」定稿)。DESIGN §3.8 取舍随 commit 更新,`tests/test_kb_block.py` 4 用例锁两分支。②前端「查看原件」三入口(已入库条目行 👁 / 单篇页头 / 待入库列表),复用 `openFilePreview` 走 `.kb/<库>/sources/` 相对路径零后端;关键层级修正:file-preview-modal z 90→114 盖过 kb/skills/memory 等 112 层 modal(仍低于 app-dialog 130,删除确认在预览上),fp-open chat-form 95→115、mini 96→116,main.js Esc 关栈顺序同步先关预览再关 modal。「像文件目录浏览 .kb」提议对齐后定维持现状:.kb 继续对文件面板隐藏,守住删单篇「doc+原件+INDEX 行」三件联动判据。③已入库条目压两行:标题 + 单行省略摘要(原 `.sk-desc` 省略规则只覆盖左栏 `.sk-item`,右栏整段换行铺开),关键词挪进悬停 title 不丢信息。 - **07-23 / 0.59.3 / 知识库下载入口(原件 + 解析 Markdown,纯前端)**:kb 面板此前只能在线看不能取回。关键发现:`.kb/` 就在 user_root 下且 `/v1/files/download` 的 safe_join 只挡越界不挡 dotfile → 零后端改动,`kb.js` 复用 `media.js::downloadFile`(Bearer + blob)拼 `.kb/<库>/{sources,docs}/…` 路径即可。入口三处:已入库条目行「⬇ 下载原件」(agent 手写无原件的条目不显示)、单篇查看页头「下载原件 / 下载 Markdown」(doc→source 映射渲染详情时记 `_docSources`)、待入库列表逐项「下载」。弃选方案:新增 `/v1/kb/*/sources` 专属下载端点(与 files 下载全量重复,公测期白养一个对外契约)。改动仅 kb.js + dev.html 三行 CSS。 - **07-23 / 0.59.2 / 移动端对话附件入口(📎 拍照 / 照片库 / 选文件)**:chat 输入区此前仅粘贴 / 拖拽两条附件路(移动端双双不可用,手机没有任何传文件入口)。加 📎 按钮:触屏(`pointer: coarse`)弹 showMenu 三项菜单——拍照(`capture="environment"` 直起后置相机)/ 照片库(`accept="image/*,video/*"`)/ 选取文件(无 accept);桌面端点击直开系统文件选择器(拖拽 / 粘贴照旧)。三个隐藏 input 的 change 统一走既有 `uploadAttachFiles()`(chip 托盘 / 发送注入行零改动);change 后清 value 允许连选同一文件;微信 / 企业微信只读镜像随 `applyChannelComposerLock` 禁用按钮 + 点击侧 `_composerLocked()` 双保险。纯前端(dev.html / chat.js),无后端改动。裸 file input 方案(靠 iOS 原生三选单)因 Android 行为不确定被弃,三项菜单跨平台确定。 - **07-23 / 0.59.1 / 架构审查落地:15 commit 内部重构大扫除(对外行为零变化)**:全库审查(web/核心循环/工具渲染/数据层四路并行探查)后按优先级逐项执行。①**web 拆分**——app.py 3972→197 行:73 路由迁 11 个 `web/routers/*`(register 范式同 admin.py),lifespan 后台协程按域析出(`background`/`scheduler_runner`/`wechat_runner`),schemas/model_gate/runs/userfiles/common 各归位;②**计费收口**——units JSONB 读侧唯一出口 `core/storage/usage_report.py`(web 层清零 cast),写侧拆 `telemetry.py`(四类 cost=0 留痕与真计费分家,kind 常量成 loop↔toolfail 契约单一事实源);③**loop 拆分**——传输健壮性层(畸形/吐空检测、留痕、非流式降级重试)析出 `core/llm_transport.py`(loop 1158→812),`_execute_tool_call` 148 行拆 4 个正交方法,测试打桩缝(`_collect_stream_once`/`_nonstream_once`)原样保留;④**工具注册声明式化**——`core/tool_registry.py` (组名,gate,factory) 表收敛四种 env-gate 写法(§7.5 #7 获代码强制),32 工具挂载逐名比对不变;⑤**媒体工具去重**——五工具同构(配额闸/落盘命名/记账兜底/超时重试/递归找 URL)收 `tools/media_common.py`,净减 122 行,banner/轮询等刻意差异不动;⑥**渲染统一**——块收集器(fence 闭合/表格/blockquote/段落并合)与 brief inline 切分下沉 `rendering/common`,**golden 基线**(四 profile 渲富 fixture、document.xml 逐字节对账,`tests/golden/`)零 diff 验收,分派状态机(in_refs 等)留 profile;⑦**测试 286→351**——scheduler 服务层、usage 口径、media 原语、wecom 加解密(自造密文覆盖失败路径)、路由两套(无 DB 面:11 router 鉴权门 19 端点 + kb/skills/memory/files FS;DB 面:tasks CRUD 全链 + files 顶层目录 DB-aware 四分支 + upload);测试库基建 = docker PG@5433 + `ZCBOT_TEST_DB_URL` 显式门控(RUN.md 一键命令)。**修 2 个真 bug**:restore 恢复真软删过的任务必 500(server-side onupdate 列 flush 后 expired → DetachedInstanceError,幂等路径掩盖多时,路由测试抓出);DB 测试曾沿 .env 连到生产隧道(127.0.0.1:6012)——插入的到点 job 被生产 green 调度守护真跑,门控收紧为只认 ZCBOT_TEST_DB_URL + connect_timeout=5 兜挂库,产物已全量清理。附带:scripts 50→29(已结案诊断/backfill 归档 `scripts/archive/`)、`tools/output.py`(输出处理自 base.py 析出)、export_docx 复用 rendering.common 字体助手(rendering 保持无 core 依赖的沙箱可用性,修正审查"迁 rendering"建议)。 - **07-22 / 0.59.0 / 个人知识库(纯文件 .kb/ 机制,照记忆范式)**:方案 07-22 对齐后落地(DESIGN §3.8)。**core**:`core/kb.py`(状态/视图层:库名与文件名校验、INDEX 单行格式 `- [标题](docs/x.md)|来源 sources/x.pdf|摘要:…|关键词:…` 的 parse/format、"已入库判据 = INDEX 有条目"派生 pending、删单篇连带原件与 INDEX 行、`kb_block` 注入)+ `core/kb_ingest.py`(入库管线:markitdown Python API → 扫描件 PDF 文本近零走方舟 OCR 兜底(§8.13 同通道)→ flash 单次 chat 写标题/摘要/关键词(失败降级不阻塞)→ 追加 INDEX;编排 create_task+to_thread+per-(user,库) 锁,进度内存态供轮询)。**记账**:0022 迁移放宽 `usage_events.task_id` 可 NULL(kb 入库无 task 上下文),溯源 = kind="kb_ingest"/"vision" + units `{"kb","source"}`(record_chat/vision_usage 签名同步放宽)。**API**:`/v1/kb*` 8 端点(列/建/删库、详情带入库进度、上传即入库、手动 ingest、看/删单篇),不设 HTTP 检索端点。**agent 侧零新工具**:agent_builder 在 memory_block 后注 `kb_block`(有库才注,docker/host 路径换算同 .memory);documents skill「何时不用」补自建资料路由行。**前端**:`kb.js` 两栏 modal(建/删库、上传 XHR、入库进度 3s 轮询、看/删单篇),rail 底部四按钮改图标+小字两行布局(用户 07-22 定)。冒烟:core 层 28 项全过(真实 markitdown + flash 摘要),TestClient API 15 项全过(usage 行实测 task_id=NULL + units 溯源),受影响存量测试(usage_accounting / system_prompt_paths)绿。 - **07-21 / 0.58.55 / 扫描件 PDF 直读(read_document,方舟文档理解)**:markitdown 只抽文本层,扫描件(老标准/检测报告/红头指南)转出为空=死路。探针(`scripts/probe_ark_doc.py`)验证方舟 chat file 内容块直读 PDF:格式 `{"type":"file","file":{filename,file_data}}` + `data:application/pdf;base64,` 前缀、base64 内联 17MB 可用(免 TOS)、单页栅格化 3600 万像素硬限(PIL 存 PDF 需标对 dpi)、~1300 输入 token/页(约 1 厘/页)、100 页魔术串全覆盖。落地 `tools/read_document.py`(seed_2_lite 同 variant 同 key,记账走 record_vision_usage):体积/页数双闸(30MB/100 页,pdfminer 软探页数免白付撞上下文)+ `finish_reason=length` 截断提示 + **多页 OCR `save_md` 全文落盘只返 1500 字预览**(防上下文爆);`image_ref.py` 抽 `load_pdf_as_data_url` 复用三形态路径解析与 user_root 边界。agent_builder 注册 + 系统提示 `_MEDIA_READDOC_SEG`(何时调/何时不调防重复花钱);六 skill(paper/patent/standard/proposal/rebuttal/ppt)摄取段加扫描件兜底一行。冒烟 `scripts/smoke_read_document.py` 全过(3 页 ¥0.0066,表格→md 表、公式→LaTeX)。选型对比(外部解析 API=新增第三方数据面 / 本地 OCR=过度投资)见 DESIGN §8.13。host 侧工具,**无需重建沙箱镜像**。 - **07-21 / 0.58.55 / 长对话点目录圆点首次跳不到位修复**:根因链=loadMessagesAround 后 renderMessages 尾部无条件滚底钉到窗口末尾,底部 sentinel 入视口立刻触发 loadNewerMessages 整窗重渲染删掉平滑滚动目标。修:renderMessages 加 stickBottom 参数(三个调窗口路径传 false);jumpToMessage 重建窗口后瞬时定位(auto)不留动画窗口期;`_msgScrollObserver` 在 `_outlineJumpLock` 期间不补载、解锁时对 sentinel 重投交叉状态。 - **07-21 / 0.58.54 / 手机端文件预览改悬浮卡片(四边留边距、不压输入区)**:用户反馈移动端预览弹框全屏贴边(100vw×100dvh、直角)观感差且糊在消息输入框上。改 `dev.html` ≤640px 媒体查询:遮罩层 `padding:10px` 四边留呼吸边距、底部 `calc(--preview-bottom-inset + 10px)` 在 chat-form 让位之上再加间隙;卡片尺寸改 `100%`(相对遮罩内容区,弃 100dvh,旧 WebView 兼容更稳),恢复默认圆角。输入区仍走既有 `body.fp-open #chat-form` z-index 抬升保持可用;桌面端不动。 - **07-21 / 0.58.53 / App 套壳进入契约(embed app 变体 relogin_url)+ 对接文档**:移动 App 方案定型——原生壳(WebView)+ 原生登录页,登录走 platform 自有接口(`/api/login/token` → `/api/login/external-login`,已实测打通,后者服务端用 PLATFORM_KEY 换 zcbot JWT,user_id=platform user.uuid)。顶层 WebView 无父窗口,iframe 的 postMessage 协议失效(`zcbot-401` 经 `window.parent` 发不出去),故把企微免登模式泛化为 **app 变体**:`?embed=1&relogin_url=<绝对地址>#token=..&user_id=..` —— fragment 注入(读完即清,同 wecom)+ 401/logout 时 `location.replace(relogin_url)`(原生壳拦自定义 scheme 如 `zcbotapp://relogin` → 静默换新 token 重进;H5 登录页同契约)。改动:`state.js` 加 `EMBED_RELOGIN_URL` 解析+消毒(必须带 scheme 的绝对地址,拦 `javascript:`/`data:` 等可执行 scheme——该值喂 `location.replace`,不消毒是 XSS 口子);`embed.js` 抽共用 `readFragmentToken`/`gotoInitialTask`,加 `embedAppInit`/`embedAppRelogin`;`auth.js` logout 分支序 wecom→app→iframe。新增 `APP.md`(进入契约 + platform 登录链路实测 + 原生壳杂活清单 + H5 备选),`EMBED.md` 精简 243→约 120 行。ESM 语法检查过,浏览器级实测待跑(逻辑与线上企微免登同构)。顺带:实测发现 platform `/api/login/token` 响应把 `hashed_password` 下发到客户端,修复建议已写进 APP.md §5 转交对方。 - **07-21 / 0.58.52 / 窗口体量估算实测校准(50%压缩/85%折叠/前端占用环共用)**:diag 复盘窗口约束现状(`scripts/diag_context_pressure.py` 留仓):机制有效(折叠上线后唯一越线 task 9a863424 下个 run 起点即回落 400k→137k)、零撞硬上限(6 条终态错误无一 context 超限),但量出静态 `CHARS_PER_TOKEN=2.5` 对中文密集窗口**低估近一倍**——名义 85% 折叠线实际 ~155% reliable 才触发(该 task 实测 40.7 万 tokens);代码密集反向虚高(fe2d8b73 估 1.2M 实际 616k)。**修(信号校准,不加新机制)**:`context.py` 加 `estimate_window_tokens`(provider 实报 tokens_in/out 覆盖窗口主体,仅实测点后尾巴按 2.5 估)+ `calibrated_chars_per_token`(比值夹 [1.0,4.0] 带宽);`Session.last_measured_usage()` 从 messages 表取窗口内最后一条实报 usage(best-effort 绝不抛,idx→内存 pos 映射校验 role);`maybe_fold` 触发判定换 token 实测口径;loop 的压缩门槛与 `context_limit_chars`(前端环)用校准比值,每轮成功调用后以 (sent_chars/prompt_tokens) 刷新。已知残余:折叠后 run 若在首次 chat 完成前崩,下个 run 起点读到旧实测会多折一次(后果=摘要偏保守,原文全在 DB,不为此加持久化状态)。run 中途折叠/超限自愈按 §5 无信号不实施继续搁置。真实生产 task 验证映射与校准(9a863424 drift 0.95x、fe2d8b73 0.51x、74696048 0.86x),286 测试全绿。 - **07-21 / 0.58.50 / salvage 成功提示降噪(黄→灰)**:用户反馈"工具调用参数损坏但已就地抢救"黄字吓人、问是否影响后续对话——核实**零影响**(salvage 只就地改写 arguments,当轮照常执行,对话历史与正常轮无异;warn 是纯前端 SSE 展示事件,不入 messages 不回灌 LLM,刷新即消失)。处置:不全静默(上游返脏数据的事实要可见、可与 `tool_salvaged` DB 留痕对上),改**分级降噪**——`loop.py` 该 emit 加 `level:"info"` + 措辞软化「已自动修复工具调用参数…继续执行」;`chat.js` warn 渲染按 level 分流,info 走灰色 muted 无 ⚠,黄色 ⚠ 只留给真打断本轮的路径(丢弃重试/熔断等)。 - **07-21 / 0.58.51 / 系统提示护栏:大段中文正文别内联进 .py(run_python 语法预检失败治本)**:失败面板 `run_python/error` 语法预检簇(近7天31次/12task)根行为=模型手写 python-docx/pptx 把大段中文正文硬拼进 .py 源码,ASCII 引号/全角标点/缩进崩成 SyntaxError。定位:①现有正规路径充分(所有出 docx 的 skill 都指向 `rendering/render.py` md→docx、且写"别自己手搓"),但 DB 显示 16 个涉事 task 中 **7 个没 load 任何 skill**→够不着 SKILL 指引;②precheck(pysyntax)已 host 侧拦截+回根治 tip+喂 RepeatGuard,浪费已 bound(每任务~2.6次自愈、无失控循环)。故走**最高覆盖、最低改动**:base 系统提示 `prompts/system/general_v1.md`(所有任务加载)的 run_python 段加一句**原则级**护栏——生成/改 docx·pptx 时大段中文正文别内联进 .py、先 write 进 .md/.txt 再 read、有 skill 优先走其渲染路径(守 recipe-ban 不给可 copy 配方)。纯提示词引导、无法端到端 verify,ROI 本就低(precheck 已兜),只求少发。**遗留 Option 4**:改现有 .docx 无平台路径(render.py 只从 md 新生成),模型只能手写 python-docx→内联中文,需另设计(docx→md 拆/模板填充)。`prompts/` 每次 build 实时读、`git pull` 即生效免重启。 - **07-21 / 0.58.50 / salvage 成功提示降噪**:抢救成功的畸形 tool_call 对后续对话零影响(warn 纯前端 SSE 不入 messages),黄色⚠警告降级为灰色 info 行(emit 加 level:"info"),黄⚠只留真打断路径。改 web UI + loop emit。 - **07-21 / 0.58.49 / glm.pro52 空响应治本(禁 thinking + loop 区分截断)**:探针定层坐实根因——①glm-5.2 thinking **网关侧默认开**(线上探针实测 reasoning_content=766>0,尽管 config thinking_mode:false —— 那开关是 glm.yaml 未做的 TODO,根本没传);②重任务(100k 上下文)上思考膨胀烧穿模型自带输出上限(65536)被截断(finish_reason=length)、content 空 → loop 判空响应整轮丢弃 + **同上下文无效重试**(task 35744bea:5 次 empty 全 tokens_out=65536,事件4=attempt2 铁证);后果单任务烧 ~327k 输出 token。与 opus48/deepseek 网关 wire bug 不同根。**修**:`core/llm.py._build_kwargs` 加 family=="glm" 分支,据 thinking_mode 透传 `extra_body={"thinking":{"type":"enabled|disabled"}}`(GLM 协议,与 reasoning_effort 不同族)——线上探针 A/B 实测 disabled 后 reasoning_content 766→0、正文照常;`core/loop.py` 加 `_finish_reason`,空响应路径区分 length(截断,思考烧穿输出上限,重试无效)与 wire 吐空,warn 措辞据实 + `record_empty_response` units 记 finish_reason(JSON 免 migration,便于面板区分性质)。修好既坏的 test_loop_empty_response(mock 缺 executor)+ 补截断措辞用例,61 测试全绿。探针 `scripts/diag_glm_empty_probe.py` 留仓(monkeypatch _resolve_executor 绕沙箱池、PROBE_THINKING_OFF A/B 开关)。 - **07-20 / 0.58.48 / glm.pro52 空响应定层探针**:失败面板 empty 簇(15)主角从 opus48 换成 `glm.pro52`(11/15)。加 `scripts/diag_glm_empty_probe.py`——`build_agent(resume=True)` 复原真实 system prompt + 全工具 schema + **完整多轮上下文**(空响应发生在深层,单条 user 消息复现不了,这是与 2a1bc25d narrated 探针的关键差别),打 glm.pro52,三态判定 EMPTY(真吐空)/NARRATED(工具意图漏成正文)/STRUCTURED。默认锁 task 35744bea(近7天 empty 5 次最可复现)。本机 import litellm 卡 ~20min,交生产机 `.venv/Scripts/python.exe scripts/diag_glm_empty_probe.py` 跑,输出写 `scripts/_glm_empty_probe_*.txt`。定层后再决处置。 - **07-20 / 0.58.47 / 工具失败面板 6 簇巡检 + svg 质量门签名诚实化**:扫近7天失败聚集逐簇定根因。①**write/edit malformed(45/32)**=100% DeepSeek v4 wire 把并行 tool_call 的 arguments 拼成 `{...}{...}`,salvage(0.58.24)复盘提前到 07-20:命中率稳 ~85%(post-launch salvaged 252/malformed 44)、残差涉事 task 全在跑无终态失败=落非流式重试自愈,**决策保持现状不再写代码**(面板绝对数被上线前 07-13/14 尾巴撑高,要看 salvaged/malformed 比)。②**shell/exit "Font issues"(20)**=`svg_quality_checker` 质量门按设计 exit-1,但 `toolfail._classify` 取 `[exit]` 前尾行当签名,而尾行永远是通用 tip「4. Font issues:…」→ 所有门失败(typography/alignment/spec_lock drift)误标成 Font issues 误导排查。修:checker 在 errors>0 时末尾多打确定性 `gate_verdict_line()`「[GATE FAIL] svg_quality_checker: N error file(s) / M total; top issues: …」作为最后一行 stdout(放 main() 里 export 之后),toolfail 现有取尾行逻辑自动拾取诚实签名、toolfail 零改动;`_categorize_issue` 加 `spec_lock/typography` 类避免真错落 "Other"。补 3 测试(GateVerdictLineTests),39+3 全绿。③**run_python 预检(31)**=pysyntax 按设计拦中文正文硬拼进 .py(引号/缩进崩),非平台 bug=提示词治理待办。④**empty(15)**=主角换成 `glm.pro52`(11/15,非旧 opus48 案),需生产跑 narrated 探针定层(本机 import litellm 卡,task e7c2f50b 备用)。⑤**web_fetch(6)**=外部 403 拒抓为主,非平台锅。 - **07-20 / 0.58.45 / glob 绝对路径崩溃(工具失败聚集 #2)**:巡检近7天工具失败聚集,`glob` 排第2(3次/3task/3用户、近24h 仍在发)。根因:`tools/fs.py` GlobTool 直接 `base.glob(pattern)`,模型传绝对 pattern(沙箱 cwd 就是 `/workspace`,自然写 `/workspace/**/*.py`)时 pathlib 抛 `NotImplementedError: Non-relative patterns are unsupported`,整个调用崩返 `[Error executing glob]`。改:绝对 pattern 先 `_split_abs_glob` 拆成「存在的静态前缀目录 + 相对 glob 串」(左起吃掉不含 `*?[` 的组件当 base)再匹配;补首字符 `/`\`\\` 判定(POSIX 前导斜杠在 Windows 宿主 `is_absolute()` 为 False,沙箱是 Linux,两处对齐)。顺手删掉原 `if "**"`/`else` 两分支代码完全相同的死重构。`/sandbox/tools/` 是 Dockerfile `COPY tools/` 拷贝,**需重建沙箱镜像**才生效。 - **07-20 / 0.58.45 / PPT 页脚漂移治本(工具失败聚集 #1,A+C)**:`shell/exit` 排第1(近7天16条 exit-1、近24h 6条,9task/7用户),实测拆解 15/16 是**逐页手写绝对坐标偏离 `spec_lock.layout_grid` 锁定值 2–16px 的「想对齐没对齐」**(footer_y 近失 27行为最大头,margin_x/content_top 各7行,content_bottom 溢出12行),字体/spec_lock 只是 WARN 不 fail。关键发现:`executor-base.md §180-185` 早有「HARD—checker-enforced」的 snap 纪律却仍被漂移,加 prose 无用,治本须机械化。**A**:spec_lock_reference 新增 `## footer` 锁定片段——x/y 用 `margin_x`/`footer_y` 预填好、页码 `text-anchor="end"` 在 `canvas_w−margin_x`、留 `{PAGE}` 占位,executor 每页**逐字粘贴只换页码**,把 footer_y 这个跨页漂移第一大头从「每页手算」收敛成「一次编写」(executor-base 加对应硬规则,SKILL.md 同步);冒烟验证锁定片段过质检 errors=0、无 footer_y/margin_x 报错。**C**:`svg_quality_checker._ALIGN_TOL` 2→3px(2px 对手写实测过严),同步刷 SKILL/spec_lock_reference/executor-base 的「2–15px/2–12px」表述与测试值(margin drift 用例 60/63/66→60/64/68,30 测试全绿)。content_bottom 溢出(装太多项,另一性质)与 grid-snap 后处理(B)本轮未做。 - **07-19 / 0.58.43**:文件预览遮罩改整屏、消除"上暗下亮"割裂——旧实现用 `#file-preview-modal { bottom: var(--preview-bottom-inset) }` 把遮罩顶到 chat-form 之上,好让预览时能边看边打字,代价是底部整条(输入区+左栏入口+右栏+状态)全不压暗,与上方压暗区形成一条硬边界的通亮带,很割裂。改为遮罩铺满整屏(`bottom`→`padding-bottom`,仍让居中卡片停在输入区之上不遮挡),底部随整屏统一压暗;仅 chat-form 由新 `body.fp-open` 类 `position:relative;z-index:95`(压过遮罩 z90,层叠上下文已核:`#app`/`#pane-mid` 均不建 context)+ 上向阴影,上浮成"悬浮活动条",继续可打字。预览时左栏/右栏/状态点击落到遮罩=关预览(符合"背景不可用"预期)。改 `web/static/dev.html` + `web/static/js/preview.js`(open/close 加/去 `fp-open`)。 - **07-20 / 0.58.46**:文件预览背景撤回毛玻璃、改回压暗遮罩——用户反馈 0.58.44 的毛玻璃虚化(`rgba(20,22,28,.22)` + `blur(6px)`)"不行",要求变暗且背景仍看得到、注意度适中。改回半透明深色遮罩 `rgba(0,0,0,0.45)`(比 0.58.43 的 `.5` 略透、背景内容仍隐约可辨),去掉 `backdrop-filter`,chat-form 悬浮阴影恢复 `0 -10px 28px/.22`。因 0.58.44 之上已叠 0.58.45(工具修复),不走 `git revert`(会冲突版本/CHANGELOG),改为前向修复。只动 `web/static/dev.html`。 - **07-19 / 0.58.44**(已被 0.58.46 撤回):文件预览背景一度改毛玻璃虚化,用户反馈观感"不行",0.58.46 改回压暗遮罩。 - **07-19 / 0.58.42**:页内弹框替代原生 alert/confirm/prompt——用户反馈原生浏览器弹框(顶部丑框)应改成页内样式,且区分两类(对标 Element `dialog`/`message`)。新增 `web/static/js/dialog.js`:`dialogConfirm`/`dialogPrompt`(遮罩+卡片 modal,复用改密码那套头/体/脚布局,Promise 化以一比一替换同步的 `confirm`/`prompt`,Esc/点遮罩=取消、Enter=确认、danger 变体红键)+ `message`(顶部居中轻量 toast,success/error/info 三态,自动消失、点即消)。dev.html 加 `#app-dialog` 容器 + `#app-messages` toast + 对应 CSS。全量替换主应用 8 文件调用点(chat/files/crons/procs/wechat/skills/media/auth,共 30 处);`admin.js`(独立 admin-only 页、独立 `