caoqianming
2e3a4db4e0
fix(web): 手机端文件预览改悬浮卡片——四边留边距、不压输入区(bump 0.58.54)
...
移动端预览弹框原为全屏贴边(100vw×100dvh、直角),观感差且糊在消息
输入框上。≤640px 下改为:遮罩层四边 10px 呼吸边距,底部在
--preview-bottom-inset(chat-form 让位)之上再加 10px;卡片尺寸改
100%(相对遮罩内容区,弃 100dvh,旧 WebView 兼容更稳),恢复默认圆角。
输入区仍由 body.fp-open #chat-form z-index 抬升保持可见可用;桌面端不动。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 15:20:08 +08:00
caoqianming
c1508d0df0
feat(web,docs): App 套壳 embed app 变体(relogin_url + fragment 注入)+ APP.md 对接文档(bump 0.58.53)
...
移动 App 方案:原生壳(WebView)+ 原生登录页,登录走 platform 自有接口
(/api/login/token → /api/login/external-login,实测打通,后者服务端换 zcbot JWT)。
顶层 WebView 无父窗口,iframe 的 postMessage 协议失效,泛化企微免登为 app 变体:
?embed=1&relogin_url=<绝对地址>#token=..&user_id=.. —— fragment 注入读完即清,
401/logout 时 location.replace(relogin_url),原生壳拦自定义 scheme 换新 token 重进。
- state.js: EMBED_RELOGIN_URL 解析+消毒(拦 javascript:/data: 等可执行 scheme)
- embed.js: 抽共用 readFragmentToken/gotoInitialTask,加 embedAppInit/embedAppRelogin
- auth.js: logout 分支序 wecom → app → iframe
- APP.md 新增(进入契约 + platform 登录链路 + 原生壳杂活清单 + H5 备选)
- EMBED.md 精简 243→约 120 行,指向 APP.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 14:59:16 +08:00
caoqianming
fb214de960
fix(context): 窗口体量估算实测校准——50%压缩/85%折叠/占用环换 token 实测口径(bump 0.58.52)
...
diag 实测(scripts/diag_context_pressure.py 留仓):折叠机制有效、零撞硬上限,
但静态 CHARS_PER_TOKEN=2.5 对中文密集窗口低估近一倍——名义 85% 折叠线实际
~155% reliable 才触发(task 9a863424 冲到 40.7 万 tokens);代码密集反向虚高
(fe2d8b73 估 1.2M 实际 616k)。
修法是信号校准、不加新机制:
- context.py: estimate_window_tokens(provider 实报 usage 覆盖窗口主体,仅实测
点后尾巴按 2.5 估)+ calibrated_chars_per_token(比值夹 [1.0,4.0] 带宽)
- session.py: last_measured_usage() 取窗口内最后一条实报 usage 的 assistant 行
(best-effort 绝不抛;idx→内存 pos 映射校验 role)
- context_fold.py: maybe_fold 触发判定换 token 实测口径
- loop.py: 压缩门槛与 context_limit_chars(前端环)用校准比值,逐轮以
sent_chars/prompt_tokens 刷新;校准态挪类属性兜默认,任何异常回退 2.5
已知残余:折叠后 run 在首次 chat 完成前崩掉会多折一次(摘要偏保守,原文全在
DB),不为此加持久化状态。run 中途折叠/超限自愈按 §5 无信号不实施继续搁置。
真实生产 task 只读验证映射与校准(drift 0.95x/0.51x/0.86x),286 测试全绿。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 12:49:02 +08:00
caoqianming
00bbe42823
fix(prompt): 系统提示加护栏——大段中文正文别内联进 .py 源码(bump 0.58.51)
...
失败面板 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 系统提示 general_v1.md(所有任务加载)的
run_python 段加一句原则级护栏——大段中文正文先 write 进 .md/.txt 再 read、有 skill 优先
走其渲染路径(守 recipe-ban,不给可 copy 配方)。纯提示词引导、无法端到端 verify,ROI 本
就低(precheck 已兜),只求少发。遗留:改现有 .docx 无平台路径,需另设计。
顺带补 PROGRESS 缺失的 0.58.50 条目(salvage 提示降噪,并行 session 提交)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 09:05:46 +08:00
caoqianming
62f3ddb658
fix(ui,loop): salvage 成功提示降噪——黄色警告降为灰色 info 行(bump 0.58.50)
...
用户反馈流内黄字「工具调用参数损坏但已就地抢救」吓人、疑影响后续对话。
核实零影响:salvage 只就地改写 arguments、当轮照常执行,对话历史与正常轮
无异;warn 是纯前端 SSE 展示事件,不入 messages、不回灌 LLM,刷新即消失。
处置取分级降噪而非全静默(上游返脏数据的事实要可见、可与 tool_salvaged
DB 留痕对上):
- core/loop.py:该 emit 加 level:"info",措辞软化为「已自动修复工具调用
参数…继续执行」;
- web/static/js/chat.js:warn 渲染按 level 分流,info 走灰色 muted 行、
不带 ⚠;黄色 ⚠ 只留给真打断本轮的路径(丢弃重试/熔断等)。
频发原因研究背书(DB 近7天,bad/chat 轮次):deepseek_v4.pro 14.5%、
flash 8.3%、unifyllm 5.9~7%,glm/sonnet 0 —— DeepSeek wire + unifyllm
网关两链路的流式 delta 乱序病,前缀实为同一调用自身内容的错位碎片
(与 0.58.24 定层一致);社区 opencode/goose/Cline/Roo-Code 同病且多数
closed-not-planned 未修,我方 salvage(85% 救回)+ 非流式重试已是最完整
处置 —— 频繁黄字本质是「救回成功」记录高频刷屏,降灰合理。
顺带:CLAUDE.md 文档节奏调整(bump/PROGRESS/CHANGELOG 改为一次 push
统一更新一次,不逐 commit;DESIGN 仍随触发它的 commit 走)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 08:47:44 +08:00
caoqianming
f535dbaa9a
fix(llm,loop): glm.pro52 空响应治本——禁 thinking 免推理烧穿输出上限 + loop 区分截断(bump 0.58.49)
...
失败面板 empty_response 簇 2026-07 主角 glm.pro52。探针 + DB + 代码三重定层根因链:
1. caps.max_output 是全仓死字段(只在 capabilities.py 定义,_build_kwargs 从不作为
max_tokens 发出)→ 网关放任 glm-5.2 跑到自带 65536 输出硬上限;
2. glm-5.2 thinking 网关侧默认开(线上探针实测 reasoning_content=766>0,尽管 config
thinking_mode:false —— 那开关是 glm.yaml 未做的 TODO,根本没透传);
3. 重任务(100k 上下文)上思考膨胀烧满 65536 被截断(finish_reason=length)、content 空
→ loop 判空响应整轮丢弃 + 同上下文无效重试(task 35744bea:5 次 empty 全
tokens_out=65536,事件4=attempt2)。单任务烧 ~327k 输出 token。
与 opus48/deepseek 的网关 wire bug 不同根 —— 这是我方 max_output 死 + 未约束推理模型。
修(禁 thinking + loop 健壮化):
- core/llm.py _build_kwargs 加 family=="glm" 分支,据 thinking_mode 透传
extra_body={"thinking":{"type":"enabled|disabled"}}(GLM body 级协议,与 OpenAI 的
reasoning_effort 不同族;litellm zai provider 转发 extra_body)。当前 glm 档均 false=disabled。
线上探针 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 测试全绿。
- 探针 diag_glm_empty_probe.py 加 monkeypatch 绕沙箱池 + PROBE_THINKING_OFF A/B 开关。
行为变化(知情):glm.pro52 用户现在拿禁思考的 glm-5.2(config 本就 thinking_mode:false)——
不再卡壳/空转,代价是硬任务少了推理链。遗留:max_output 对所有模型仍未生效(本次没盲发
max_tokens,怕截断大 write 的 args),需逐档评估安全值后另做。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 08:16:54 +08:00
caoqianming
9646f571f3
feat(diag): 加 glm.pro52 空响应定层探针(交生产跑)(bump 0.58.48)
...
失败面板 empty 簇(近7天15)主角从 opus48 换成 glm.pro52(11/15):assistant 轮
既无 tool_calls 又无正文,被 loop 当正常收尾静默 done。要判 glm 是①真吐空、
②工具意图漏成正文(narrated)、还是③瞬态。
scripts/diag_glm_empty_probe.py:build_agent(resume=True) 复原真实 system prompt +
全工具 schema + 完整多轮上下文(空响应发生在深层,单条 user 消息复现不了 —— 与
2a1bc25d narrated 探针的关键差别),打 glm.pro52,stream/non-stream 各一发,
三态判定 EMPTY/NARRATED/STRUCTURED。默认锁 task 35744bea(近7天 empty 5 次
最可复现),argv 可换 task/user/档做对照。
本机 import litellm 卡 ~20min,只能交生产机跑:
.venv/Scripts/python.exe scripts/diag_glm_empty_probe.py
输出写 scripts/_glm_empty_probe_*.txt。定层后再决处置。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 16:10:51 +08:00
caoqianming
795161adcd
fix(ppt,tools): svg 质量门末尾出 [GATE FAIL] 汇总行——失败签名从误标"Font issues"变诚实(bump 0.58.47)
...
工具失败面板 shell/exit 第1簇「Font issues x20」是误标:svg_quality_checker
按设计 exit-1(质量门),但 core/toolfail 取 [exit] 前尾行当签名,而尾行永远是
通用 tip「4. Font issues:…」→ 所有门失败(typography/alignment/spec_lock drift)
全塌成一条"Font issues",误导排查方向。多文件门也没有可靠尾行(最后文件可能只有 WARN)。
修在工具侧(toolfail 保持通用零改动):
- svg_quality_checker.gate_verdict_line():errors>0 时在 main() 末尾(export 之后)
多打一行确定性汇总 [GATE FAIL] svg_quality_checker: N error file(s) / M total;
top issues: … 作为最后一行 stdout,toolfail 现有取尾行逻辑自动拾取诚实签名,
模型也多一行有用汇总。
- _categorize_issue 加 spec_lock/typography 类,避免真错(typography px)落 "Other"。
- 补 GateVerdictLineTests(3):无 error 不出行 / [GATE FAIL] 打头带 top issues /
是最后一行 stdout。56 测试全绿。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 15:59:09 +08:00
caoqianming
ab55be4cde
fix(ui): 文件预览背景撤回毛玻璃、改回半透明压暗遮罩(bump 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。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 13:42:58 +08:00
caoqianming
a02d1aa5d8
fix(tools,ppt): 工具失败聚集查前3——修 glob 绝对路径崩溃 + PPT 页脚漂移治本(bump 0.58.45)
...
- glob(聚集#2):绝对 pattern(/workspace/**/*.py)先拆「静态前缀目录+相对 glob」再匹配,
不再抛 NotImplementedError: Non-relative patterns unsupported;补首字符 / 判定兼容 Windows 宿主;
删掉原 if"**"/else 两分支完全相同的死重构。/sandbox/tools 是 Dockerfile COPY,需重建沙箱镜像生效
- ppt(聚集#1,A+C):15/16 exit-1 是手写坐标偏离 layout_grid 锁定值 2-16px 的近失,footer_y 最大头。
A:spec_lock 加 ## footer 锁定片段(x/y 用 margin_x/footer_y 预填、{PAGE} 占位),executor 逐字粘贴只换页码;
C:_ALIGN_TOL 2→3px(手写 2px 过严),同步刷 SKILL/executor-base/spec_lock_reference 表述与测试值;30 测试全绿
- 新增诊断:diag_toolfail_scan(跑 scan_tool_failures 出聚集)/ diag_svgguard_breakdown / diag_malformed_samples
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 13:27:50 +08:00
caoqianming
d5854e3337
fix(ui): 文件预览背景改毛玻璃虚化——柔和聚焦替代硬深色遮罩、消除输入条突兀感(bump 0.58.44)
...
0.58.43 的整屏纯深色遮罩(rgba(0,0,0,.5))仍偏硬,亮着的悬浮输入条
浮在深黑上略突兀。改为毛玻璃:浅压暗 rgba(20,22,28,.22) + backdrop-filter
blur(6px)(带 -webkit- 前缀,老浏览器不支持则退化为纯浅压暗、不崩),
背景虚化把内容推远、聚焦到预览卡片又不刺眼;chat-form 悬浮阴影同步调柔
(0 -10px 28px/.22 → 0 -6px 20px/.12)。只动 web/static/dev.html。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 08:57:56 +08:00
caoqianming
9c39d0ba42
fix(ui): 文件预览遮罩改整屏、消除"上暗下亮"割裂——输入区上浮为悬浮活动条继续可用(bump 0.58.43)
...
旧实现用 #file-preview-modal { bottom: --preview-bottom-inset } 把遮罩顶到
chat-form 之上以便边看边打字,代价是底部整条(输入区+左栏入口+右栏+状态)
全不压暗,与上方压暗区形成硬边界的通亮带,视觉割裂。
改为遮罩铺满整屏(bottom → padding-bottom,仍让居中卡片停在输入区之上不遮挡),
底部随整屏统一压暗;仅 chat-form 由新 body.fp-open 类 position:relative;z-index:95
(压过遮罩 z90;#app/#pane-mid 均不建层叠上下文,已核)+ 上向阴影上浮为
"悬浮活动条",继续可打字。预览时点左栏/右栏/状态落到遮罩=关预览,符合
"背景不可用"预期。改 dev.html + preview.js(open/close 加/去 fp-open)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 21:05:15 +08:00
caoqianming
20c7512370
feat(ui): 页内弹框替代原生 alert/confirm/prompt——分 dialog/message 两族,复用改密码卡片样式(bump 0.58.42)
...
新增 web/static/js/dialog.js 两族原语(对标 Element dialog/message):
- 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 页(自带 <style>、无 CSS 变量)刻意保留原生 alert
避免缺样式。纯前端加法,不动后端/接口。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 09:38:31 +08:00
caoqianming
6efda0a157
feat(task): 任务改名 UI——⋯ 菜单加「重命名」,改完列表+顶栏全同步(bump 0.58.41)
...
后端 PATCH /v1/tasks/{id} 早支持 name(validate_task_name 校验)但前端无入口。
taskMenuItems 加「重命名」项(列表行 ⋯ 与顶栏 ⋯ 共用此菜单,一处插入两处生效),
renameTask() 走 prompt→PATCH→更 state.tasksById/taskMeta.name + renderChatMeta()
+ loadTaskList() 全同步。刻意只改 DB 显示名,不动 working_dir/磁盘宪法文件
(定位锚是 task_id/short_id,name 设计为自由可变标签)。仅改 web/static/js/chat.js。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 22:42:17 +08:00
caoqianming
e53211f9c8
docs(progress): 精简 PROGRESS——2026-07 段压回全文一行式,字符数 84K→43K(bump 0.58.40)
...
本文件头部自定「每条 1-2 句,细节查 git log」,但 2026-07 条目(尤其 0.58.x)膨胀成
几百字大段落,与 2026-06 及更早的一行式不一致。精简只压 2026-07 段:每条收成 1-2 句
保留版本号/日期/关键判断;mermaid 排查 saga 折叠为终案+一行注(结论沉淀进 memory);
同月细碎小版本按主题聚合。状态表/决策表/文件清单/下一步原样保留,内容零丢失。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 10:56:27 +08:00
caoqianming
f7e5a80ed9
feat(auth): JWT 滑动续签——活跃用户临期无感换 token,不再因 7d TTL 掉线(bump 0.58.39)
...
原状:无状态 HS256 JWT(默 7d TTL)无任何续签路径,过期即 401——platform 靠
PLATFORM_KEY 自行重换,SPA 用户只能重输密码。判断:平台层握长期 PLATFORM_KEY、
login 本就幂等换发,缺的不是 refresh token 而是"无感重换"这层;放鉴权中间件一次
改动同时惠及 platform 层与 SPA,且纯加法契约。
- web/auth.py:AuthConfig 加 refresh_threshold_seconds(env
ZCBOT_JWT_REFRESH_THRESHOLD_SECONDS,默 TTL 的 1/4,设 0 关闭);verify_token
拆出 decode_token+_uid_from_payload;require_user/admin 共用 _authenticate,
注入 Response,临期就地 mint 新 token 放响应头 X-Refreshed-Token /
X-Token-Expires-At(仅续未过期的,已过期先抛 401)
- web/app.py:CORS 加 expose_headers 放行两头,否则浏览器 fetch 读不到
- web/static/js/api.js:中央 api() 封装 absorbRefreshedToken——任一响应带新
token 就无感换 state.token+localStorage
- 验证:4 case 全绿(fresh 不续/临期续且 exp 正确延长/已过期仍 401/阈值 0 关闭)
顺带:EMBED.md 收敛为纯 platform iframe 对接文档——删企业微信免登 §9(实质併入
RUN.md 企微段「应用主页免登」的内部链路)、校准 §3 换 token 返回体过期字段、补
滑动续签说明;新 env 进 RUN.md、用户可感知条目进 CHANGELOG。
additive,不改字段语义/URL/DB schema;WS ASR 路径 verify_token 签名不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 10:21:35 +08:00
caoqianming
011aee1334
fix(channel-push): 定时简报产物文件链接点开 404——推送端收敛为相对 rel + 前端识别完整 URL 兜底历史(bump 0.58.38)
...
task 7f904e5f 复盘:定时任务/企业微信推送写进 DB 的 assistant 消息把产物
文件写成完整下载 API 链接 [名](/v1/files/download?path=<rel>),web 正文链接
拦截器(chat.js 的 .msg .body a[href] click)注释设定是"user_root 相对路径",
于是把整个 /v1/files/download?path=... URL 当相对 rel,去前导 / 后
rel=v1/files/download?path=scheduled-…txt,再塞进 openFilePreview 二次包进
download URL → 请求 path 不存在 → 404(node 复刻拦截器坐实)。
关键调研(纠了初判):一度以为链接得留完整 URL 给企业微信端点下载、改格式会
break wecom——错。push_wecom(service.py:266)企业微信端走 send_text(摘要)+
send_file(文件附件独立通道),压根不碰这个 markdown 链接;_build_push_message
的链接只进 DB 给 web 端渲染。焊死 /v1/files/download?path= 纯属把具体 API
端点这个渲染细节泄漏进数据层,还正好踩拦截器坑。
修(A+B 互补):
- B 源头(分层正解):core/wechat/service.py::_build_push_message 链接改
user_root 相对路径 [名]({rel}),与 chip/正文相对链接统一契约、走拦截器
已有相对分支;docstring 记明为何不用绝对 API URL(防后人再焊)。
- A 前端兜底:chat.js 拦截器在相对路径回退前先判 ^/?v1/files/,命中则
new URL(raw,origin).searchParams.get("path") 抽真正 rel 交 openFilePreview
(自动解码,txt/docx 复用同 modal)——救 DB 里已存 20+ 条历史简报(改不了)
+ 健壮兜底。
验证:node 复刻拦截器——B 新格式(相对)与 A 历史(完整 URL)都解析成同一
干净 rel;A 另测 URL 编码含空格#号 path / 普通相对 / ./ 前缀 / 外链全对、
外链仍开新标签;node --check chat.js + ast.parse service.py 通过。web 前端
需刷新页面生效。additive,不碰对外 API/DB/企业微信推送(send_file)契约,
仅 DB 内 assistant 文本链接写法由绝对 API URL 收敛为相对 rel。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 22:38:57 +08:00
caoqianming
eb45e771fa
fix(rendering+chip): 修 task d9c74a5a 三处独立缺陷——chip 粗体路径 404 / docx emoji 缺字 / 单文件被迫建 sections(bump 0.58.37)
...
一次"今日新闻 md→word"暴露三个不相干的坑,逐个定层修:
1) 前端文件 chip 点开弹框 404 —— media.js::extractArtifactRels 的 tail
字符集 _TAIL_CLS 没排除 `*`,助手把路径写成 markdown 粗体
`**<wd>/x.md**` 时前导 `**` 被 lead 边界吃掉、尾部 `**` 被贪婪吞进
rel(星号既不在 tail 集也不在尾部 strip 集),生成 `x.md**` →
/v1/files/download 404。修:`*` 加进 _TAIL_CLS 排除集,同反引号一样
在 emphasis 处干净截断;文件名含 `*` 在 Windows 非法、误伤≈0。
2) 导出 word 图标(emoji)缺失 —— docx_manuscript 正文/标题字体是宋体/
黑体/Times New Roman,无 emoji 字形,📰 🔥 🇨🇳 📊 写进宋体 run →
Word 渲染成豆腐块。修:common.py 加 EMOJI_RE + add_fonted_runs
(按 emoji 边界拆 run,emoji 段走 Segoe UI Emoji、其余走原中西文字体),
接进 _emit_plain_with_chem(不破坏化学式下标)与 add_inline
(bold/italic/code);标题走 add_inline 自动覆盖。
3) 过程中冒出 sections/ 文件夹 —— render.py docstring 承诺 src 可为
"目录或单个 .md",但 docx 三 profile 的 render_sections 全是 is_dir()+
glob 只认目录(只有 pdf 支持单文件),模型传单 .md 撞
`sections dir not found` → 被迫 mkdir sections && cp 兜底。修:
common.py 抽 collect_md_files(文件→[src],目录→glob),docx_manuscript
+ docx_brief 共用,figures 基准取 src.parent;兑现 docstring 承诺。
验证(实跑非仅单测):emoji 满满的今日新闻 md 跑单文件 --profile report
退出 0(不再报 sections 缺失),解包 docx 校 run 字体——emoji 全
Segoe UI Emoji、中文仍宋体、SiO2/CaCO3 下标未被破坏;正则修复 4 组样本
(粗体/反引号+间隔号/裸路径+中文句号/斜体)全绿;目录多文件+brief+
不存在输入三路回归无破坏。三处互不相干可独立修,均 additive,不碰对外
API/DB 契约。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 22:15:34 +08:00
caoqianming
1581b69bfa
fix(run_python): 超时返回超时前的部分输出 + 无缓冲(-u),模型据此续跑不整批重来(bump 0.58.36)
...
失败面板 #8(command timed out 19 次/7 task,累计最多)。定性(查 19 条超时的原始
tool_call 代码):不是死循环/chromium 老坑,全是真慢的批量 I/O——① 论文检索
skills.research.paper.search() 循环搜多期刊/关键词(最多,每次打 OpenAlex 累计爆超时)
② pdfplumber 多 PDF extract_text ③ 大文件下载 ④ pandas 大 Excel。超时机制正常,真正
浪费在 _run_subprocess 超时分支把已捕获的部分输出全丢了(kill 后 communicate() 已续读
超时前 stdout,却只回一句 command timed out → 模型看不到"搜到第6个"、整批重搜)。
修:
1. tools/base.py::format_timeout_result 把超时前 stdout/stderr 一并返回 + 续跑提示
(据已完成部分续跑/每项落盘/大操作用 background=true/单次别塞太多),docker
(_run_subprocess)+ host(run_python TimeoutExpired.stdout/stderr)两执行器共用。
2. -u 无缓冲:Python 管道下 stdout 块缓冲,不 flush 的 print 超时被 kill 时缓冲区就丢,
故 run_python 的 python 调用全加 -u(docker 前台 inline/script_path + host 前台 +
两处后台 procs.launch_host),让部分输出真正落管道被捕获(不碰 shell)。
提示指路(background/续跑)与语法预检不同、恰当:超时是"你这段太慢"的确定信号,
建议对任何超时都可靠。不做:不抬默认 timeout(120s 合理);不单独改 prompt。
测试 tests/test_timeout_partial.py(format 单测 + host 端到端:print 两项后 sleep
timeout=1 → 结果带两项 + 续跑提示)+ test_executor_docker argv 断言更新为 python -u,
全 34 相邻测试绿。additive(超时结果加信息不破坏 schema),不碰对外契约。至此失败面板
top 全部处置(② edit streak/③ 畸形/① SyntaxError 预检+渲染器/#8 超时)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:53:14 +08:00
caoqianming
5cf92ec71a
feat(rendering): 通用报告走平台渲染器 report profile + 基座引导,少让模型手撸 python-docx(bump 0.58.35)
...
接 0.58.34(语法预检=止血),这条根治那一头。关键发现:平台早有成熟 md→docx(+pdf)
渲染器 rendering/(bind-mount /sandbox/rendering:ro,任何 run_python 可达,与 skill
无关),标题/正文/列表/表格/图片图题/mermaid/化学式下标/目录/house 字体全有,
paper/proposal/brief 三 profile 在用。烧 token 的验收报告没走它、在裸手撸 python-docx,
唯一原因是没引导把"随手写 Word 报告"指向它。故不造轮子,只两件小事:
1. rendering/docx_manuscript.py 加 report profile(复用全套渲染;差异=报告不是申报书:
目录默认无[--toc 才带]/章节不强制分页/页边距 2.5-2.5/列表含"第X章节"不含"条"),
render.py --profile 加 report 选项 + 路由。
2. core/agent_builder.py 基座 prompt 加软引导:出 Word 报告→正文写成 sections/*.md
调 render.py --profile report,别手撸 python-docx 内联中文。
取舍红线(与用户对齐):渲染器是首选加法不是替代——绝不硬拦 run_python 生成文档,
确需精细定制版式/改写已有 docx/处理 xlsx 等渲染器覆盖不了的照样自由写代码;软引导
让位于更具体的 skill render 指引(proposal/paper)。刻意不把渲染器指路塞进语法预检
hint(会 overload 只该管语法的检查、误导合法手撸),预检保持只诊断语法。三层叠加:
渲染器(根治)> 预检(0.58.34)> RepeatGuard err-streak(0.58.33),run_python 能力不削。
测试 tests/test_rendering.py(四 profile 端到端 + report 目录可选)+ CLI e2e
(render.py --profile report 出 37KB docx)全绿;py_compile 校三文件无语法错。
DESIGN §8.6 记该 profile + 并存红线。additive,不碰对外 API/DB 契约。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 09:32:02 +08:00
caoqianming
2a60243c02
feat(run_python): 宿主语法预检——中文正文硬拼进源码的语法坏码提前拦下+锐化诊断(bump 0.58.34)
...
工具失败面板最大头(run_python/exit SyntaxError,20 条真实样本):模型手写
python-docx 生成 docx/报告时把中文正文内联进 T("中文…"),引号/标点崩坏 →
SyntaxError → 改 → 再崩,同一文件一分钟连撞 6 次烧 token。三类:
- A 引号提前闭合(最多):中文串里 ASCII 引号 " 提前闭合外层字符串,Python 却报
"forgot a comma?"/"unterminated string" 主动误导,模型照着加逗号越修越错。
- B 全角标点漏进代码位(center=True、/采购():invalid character U+XXXX。
- C 括号/引号结构崩。
根因不是缺写长文工具(write 早能写长文进文件),而是 docx/报告无渲染管线——
documents skill 只做检索、不管 docx 生成,模型裸手撸 python-docx 内联中文;ppt 有
SVG→pptx 管线故崩得少。本轮先落机检止血(硬约束靠平台机检,不塞菜谱):
新增 core/pysyntax.py(不 import litellm,本机可测)precheck_python(code):host 上
compile()(host 3.12 ≡ sandbox python:3.12-slim,零版本偏差;只解析不执行、与依赖
无关),命中 SyntaxError 返锐化中文诊断(出错行+光标+按错误类型定向:invalid-char→
全角改半角、forgot-comma/unterminated+含中文→ASCII 引号提前闭合三改法、含中文统一
追加"正文别拼进 .py,write 进 .md/.txt 再 read"根治提示)。接进 run_python 所有派发
路径(inline/script_path/background × docker+host 两执行器,_prepare_script 内嵌覆盖
script_path 全路径 + inline 分支各加一处),命中即早返、跳过 docker 往返。
零误伤:只拦本就跑不了的码(compile 不过=运行必失败),通过原样放行、行为不变;
既有 30 个 executor/run_python 测试(合法码 fixture)全绿印证。白捡协同:预检把语法
失败从 [stderr]…[exit 1] 改成 [Error] 前缀+固定首行,loop._RepeatGuard 的 err-streak
(0.58.33)得以兜住"反复交语法坏码"的循环(旧 [exit 1] 形态它抓不到)。
测试 tests/test_pysyntax.py 11 用例(A/B/C + 合法中文串/中文引号不误伤)全绿。
下一步(未做,单列选型):docx 渲染管线,正文 write 进纯文本文件→渲染工具 read 生成
docx,正文永不进 Python 源码、也不走大正文当工具入参(避免触发 0.58.33 修的 wire
乱序畸形)。additive 内部机制,不碰对外 API/DB 契约。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 08:55:55 +08:00
caoqianming
3c60c5c736
fix(loop): 堵失败聚集两裂缝——必填key被吞畸形走非流式重试 + RepeatGuard连续同错streak拦(bump 0.58.33)
...
扫近14天工具失败面板 top13 定位两条反复烧 token 的裂缝:
① 必填 key 被吞畸形(面板 #3 edit 缺 path,14次/13task/9用户,最广):
定层坐实是流式 wire 乱序(path 值碎片被吞进 old_str 尾部,独立 path 键消失),
跨 provider,非模型漏参。关键区别是 JSON parse 成功 → 既不命中
_malformed_tool_calls(只抓 parse 失败)、salvage 也救不了,一路漏到 executor
才报错反复烧。新增 _toolcalls_partial_args(非空 dict 且 0<缺失必填<全部必填)
接进 _stream_llm attempt 循环走非流式重试(服务端一次拼好绕开乱序),
_log_partial_args 落 usage_events(kind=tool_malformed,签名 missing required keys)
纳入面板可观测(此前伪装成普通 [Error] 完全是盲区)。
② 换参数撞同一堵墙(面板 #2 edit old_str not found,单task 9次/24h 7次):
模型微调 old_str 重试,精确 args 指纹每次不同 → RepeatGuard arg 判据不累计。
补第二道判据:按工具的连续同类错误 streak(_norm_err 抹平路径/数字后签名相同、
跨≥2 不同 args),SOFT 注入定向提示、HARD 拦截一次并重置到 SOFT(非永久封死),
任一成功立即清零。
测试 test_loop_repeat_guard.py +12(err-streak 5 + partial-args 7),20/20 通过;
相邻 salvage/toolfail_malformed 23/23 通过,未碰坏既有行为。additive 内部机制,
不碰对外 API/DB 契约。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 08:22:42 +08:00
caoqianming
1685004f96
feat(loop): 空响应防御——provider 吐空自动重试 + warn 自停不静默 done + 面板留痕(bump 0.58.32)
...
task 2a1bc25d 复盘:unifyllm 网关对某档 Claude 偶发把 tool_use 漏成正文/直接吐空,回来的轮
tool_calls=[] 且正文空,loop 当"模型答完"静默 done(run_status=idle、无报错、无终态失败),
表现为"自己中断",只能人肉挖 DB 才发现。复测该 bug 现已消失/瞬态,但"空 tool_calls 直接
done"这个失败模式本身太隐蔽,任何 provider 未来吐一次空都会静默卡死,故加防御(对称既有畸形
salvage 链)。
- _is_empty_response(tc 空且 content 去空白为空;纯 tool_call 轮不误判)挂进 _stream_llm 的
attempt 循环:空-空丢弃本轮走非流式重试(多数瞬态重发一次即恢复,用户无感)
- run() 收尾点分空-空分支:重试耗尽仍空不发静默 done,改发可见 warn「模型返回空响应,已停止,
回复继续可重试」+ done(复用 stall 熔断自停话术,run_status 落 idle 可续),不引入终态 error
- _log_empty_response stdout + usage_events(kind=empty_response,cost 0)双写留痕;core/toolfail
加第四段扫描聚成 tool=(empty)/kind=empty cluster(sample=model_profile 看哪个网关档在吐空),
admin 面板 + 巡检邮件零改动自动带上(单次瞬态 <阈值不触发,跨 task 系统性吐空才冒头)
- 刻意不做内容嗅探式"narrated tool_call 特征→重试":marker 每次变且与正常正文高度重叠,假阳性
(合法回答判坏反复重试)比罕见静默停更糟,那类只靠面板留痕兜、不改热路径行为
- tests: test_loop_empty_response.py 新增 7 件 + test_toolfail_malformed.py +2;顺手补回
test_loop_malformed_retry.py 因 salvage(0.58.24)落地后失修的 loop.executor 桩。全 247 测试绿
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:29:49 +08:00
caoqianming
7500b3b134
chore: 撤回 §8.11 delegate,只留验证过的批量工具(bump 0.58.31)
...
0.58.30 曾整体落地 delegate(隔离检索子循环),现 revert + force-push 抹除。
机制没错、对标 Claude Code subagent 也成立,但触发信号没坐实:自评回看
diag_search_args.py,motivating 案子 document_search 122 次呈"一批批不同材料
体系并行搜"形态,更像批量扇出而非结果驱动探索——若属实正解应与 mp_search 同为
批量工具 document_search_batch,delegate 是过度设计;叠加子循环 transcript 不落盘
(诊断驱动的功能反不可诊断)、20 轮上限对 122 次偏低、强制 flash 对难检索降质等
未验证坑。
重建前置收紧:先用 diag 确认某检索 task 的 query 是真探索(依赖前序结果、无法
一次列全)而非可枚举批量,是批量走批量工具、只有真探索才 delegate。批量工具
(0.58.28)验证过、保留。PROGRESS/DESIGN §8.11 记该教训免日后盲目重建。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:58:21 +08:00
caoqianming
d3bd40fad7
fix(web): 对话正文文件锚点两处回归——含·的 pptx 丢 chip + 链接点击整页404(bump 0.58.29)
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:40:12 +08:00
caoqianming
b5ac175ccd
feat(mp): mp_search_summary 批量入参 formulas[] 定点解检索烧 token(bump 0.58.28)
...
诊断 diag_tool_repeat.py 全库扫:pymatgen task fe2d8b73 里 mp_search_summary
占 562/687 次 tool_call、重复率 91%——逐化学式一 formula 一轮走 agent loop,
562 份中间 JSON 全流经主上下文且每轮重发。根因是工具只收单 formula + host-side
持 MP key(禁在 run_python 里循环)被迫一式一调。
这类"已知清单批量扇出"的正解是工具批量化而非 delegate 子循环(delegate 只把
562 轮搬进子上下文、基础 token 不省)。新增 formulas: list(≤300/次)触发 batch:
复用单个 MPRester 会话逐式查(num_chunks=1 fair-use 护栏不变)、每式按
energy_above_hull 升序、单式出错不连坐整批;结果落 materials/mp_search_batch_
<hash8>.json(内容寻址幂等),只回紧凑摘要+路径,中间数据不进对话。
单查询路径字节不变保向后兼容;working_dir 设可选(裸构造/无 key 测试降级内联)。
agent_builder 注入 working_dir_path;pymatgen SKILL.md 加批量引导(WHY+WHAT)。
tests +1(去重/落盘/稳定相排序/单式错误隔离/无 key 泄漏),原测试补回 chunk_size 断言。
additive 内部工具能力,不碰对外 API/DB 契约。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:49:35 +08:00
caoqianming
54db4fc348
feat(web): 对话卡片悬停「复制」按钮,取正文 Markdown 原文(bump 0.58.27)
...
renderMessages 给 user 卡与有正文的 assistant 卡挂 .msg-actions 栏
(纯 tool_calls 轮/error 卡不挂),默认隐、hover/focus-within 现、
触屏常显淡态。复制经 #chat-stream 既有 click 委托据 data-idx 回
state.loadedMessages 取 payload.content(非渲染 HTML),Clipboard API
主路径 + execCommand 降级,按钮短暂反馈「✓ 已复制」。直播卡不带此栏
——流结束 loadMessages 整屏重渲成持久卡自然带上。
本期只做零后端耦合的复制;重新生成/编辑重发需后端截断语义,另立项。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:51:28 +08:00
caoqianming
2066947061
fix(web): 用户停止后补持久「已停止」提示,cancel 落一等终态(bump 0.58.26)
...
点停止中断回复后对话底部什么都不剩:live-append 的「已停止」badge 被收尾
loadMessages 整屏重建冲掉(同 2a1bc25d 案)。补齐 cancel 终态,与 error 卡对称:
- 后端: agent.run 取消路径 return "[cancelled]",_run_agent_bg 据此收尾写
run_status="cancelled"(否则 idle)。run_status 是 Text 列,加新值零 migration。
- 前端: renderPersistedRunError → renderPersistedRunTerminal,同时认 error/cancelled;
cancelled 渲居中 muted 虚线「已停止 · 你中断了上次运行」(良性态,不用错误卡样式),
刷新/切任务仍在,下次起新 run 覆盖清掉。删掉会被冲掉的 live badge 及死 CSS。
- 兼容: run_status 加新值纯增量,蓝绿旧实例读到 cancelled 当良性态优雅退化不崩;
全部 run_status 消费点(409 门/reaper/SSE is_active/cancel 端点/clear)已核对。
- 同步 DESIGN §7.4 / models.py 注释 / filter allowlist / CHANGELOG / PROGRESS。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:38:54 +08:00
caoqianming
c655ce0a76
feat(skill): 已选国际旗舰模型时豁免 skill 定向模型切换(bump 0.58.25)
...
ppt 等内置 skill 会 pin glm.pro52,但用户若已主动选了国际旗舰模型
(unifyllm 网关族:Claude/GPT/Gemini)不应被切走。_skill_model_switch
在算出目标 profile 后加判据:current profile 的 family==unifyllm → 返回
None 不切。定向 pin 本意是给较弱的国产默认模型托底产物质量,已在旗舰
模型上的用户尊重其选择、不降级。判据在 DB 写入前(纯字符串检查,无副作用),
其余取舍(只信内置 skill / 跳档位门控 / 不自动切回)不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:22:02 +08:00
caoqianming
e1d5bf6788
feat(loop): 畸形 tool_call arguments 就地抢救(salvage)省一次非流式重试(bump 0.58.24)
...
失败聚集面板同形态跨 write/edit/run_python/shell 复发(err=Expecting value char 0):
char-0 是 provider 在 wire 上把别处正文字节错标成该 tool_call 的 arguments delta 粘的
垃圾前缀,尾部真 JSON 完好并跑到串尾(定层已证 provider-wire 脏、拼接层无辜)。
命中畸形后先试就地抢救,而非直接整轮丢弃 + 降级非流式重 roll:
- core/salvage.py::salvage_tool_arguments(raw, allowed_keys) 纯函数,左→右扫每个 '{'
试 json.loads(raw[i:]),第一个 parse-to-end 成功 + 非空 dict + 顶层 key ⊆ 该工具
schema 参数名的即采纳。双护栏:parse-to-end 挡尾部截断的半个 JSON,key 白名单挡前缀
里自洽的旁支 JSON。工具无关——allowed_keys 每工具从 executor.schemas() 现取。
- AgentLoop._try_salvage_response 全有或全无:本轮所有畸形都能抠出才就地改写 .arguments
当轮继续,任一抠不出则一个都不动、原样走既有 _log_malformed_args + 非流式重试(零回退)。
- 抢救成功记 usage_events kind=tool_salvaged(record_salvaged_tool_call,cost 0),与
tool_malformed 成对;toolfail 精确匹配 tool_malformed/run_error,salvaged 不进失败面板。
纯函数单列 core/salvage.py(不 import litellm)以便 tests/test_salvage.py 无依赖本机可跑
(loop.py 顶层 import litellm 本机导入期卡死);14 用例含 write/edit/run_python/shell 四工具
真前缀 + 截断/多键/旁支/尾残渣/空{}/跨工具白名单隔离等边界。盲区:provider-wire 抖动本机
复现不出、/verify 端到端跑不了,靠单测 + 生产灰度观察 tool_salvaged/tool_malformed 比。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:20:38 +08:00
caoqianming
72418a5321
feat(sandbox): 补 olefile/xlrd/extract-msg 老格式解析依赖(bump 0.58.23)
...
toolfail 巡检:run_python/exit ModuleNotFoundError olefile 3 用户/3 任务,
模型在 run_python 里手撸解析老 Office/Outlook 二进制件(OLE 复合文档)。
markitdown 只吃现代 xlsx/docx/pptx,盖不到 OLE;建材院高频收老 .doc/.xls/.msg。
按 Dockerfile「装齐免 ModuleNotFoundError 摩擦」策略补 olefile+xlrd+extract-msg。
纯 Python 无重依赖;要生效需重建 sandbox 镜像。additive 内部依赖,不碰对外契约。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 08:16:18 +08:00
caoqianming
732420a3b7
fix(exec): run_python script_path 宿主预检+前缀容错+code 落盘回退(bump 0.58.22)
...
失败聚集 run_python/exit「can't open file」15 次/7 天/13 task,DB 逐条核对三形态:
- 没写就跑(13/15):Docker 执行器补宿主侧 is_file 预检,返带纠偏提示的
[Error] script_path not found,与 host 执行器对齐,不再进容器撞裸 stderr
- 双重拼接:fs 工具输出渲 user_root 相对路径而解析按 workdir 相对(展示/解析
不对称),模型回传即踩坑;workdir 相对不存在时按 user_root 相对再试
- code+script_path 同给:文件不存在时 code 落盘到 script_path 再执行,
不再扔掉完好的 code(schema 描述同步注明)
_container_script_path 重写为 _prepare_script(前台+background 共用,code
回写在宿主侧故加 user_root 越界防护);tools/run_python.py host 路径同语义。
测试 +8;诊断脚本 scripts/diag_run_python_noent{,2}.py 留档。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:41:43 +08:00
caoqianming
859c02cf65
feat(ops+web): run 级错误持久提示+失败聚集+provider 致命错误即时告警(bump 0.58.21)
...
反 2a1bc25d 教训(Zai 余额不足连挂 3 次续跑,前端零提示、admin 零感知):
- chat.js: renderPersistedRunError 挂 renderMessages 末尾——run_status=error
且无 live run 时消息流末尾补「上次运行出错」持久卡(live 错误卡被收尾
loadMessages 整屏重建冲掉是"发了继续啥也没发生"的根因);下次 run 起跑
后端清 run_error 自然消失
- usage.py: record_run_error(kind=run_error, cost 0)——tasks.run_error 只留
最后一次,此行才是聚合的完整数据源;app.py _run_agent_bg except 落库
- toolfail.py: 第三段扫描聚成 kind=run / tool=(run),admin 面板+巡检邮件
零改动带上;alert_provider_critical 余额/配额/认证关键词命中即时邮件
ZCBOT_DEVELOPER_EMAIL(同签名 6h 进程内冷却,全路径静默失败)
- tests/test_toolfail_malformed.py +3 用例;PROGRESS/CHANGELOG/RUN 同步
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 14:06:29 +08:00
caoqianming
b2d665a100
docs: 补记 0.58.19 重试期思考中指示+停止即时生效(bump 0.58.20)
...
非流式重试 UX 修复(llm_start 每 attempt 重发 + _nonstream_once 线程级
poll cancel)的代码已随 da289b2(0.58.19)一并入库,但该提交信息与
PROGRESS/CHANGELOG 只覆盖了 toolfail 留痕部分 —— 本条补齐文档:
- PROGRESS 加 0.58.20 条目(根因:warn 关占位段后无人重建 + 非流式
同步阻塞吞掉停止按钮;含弃养线程的 token 成本说明)
- CHANGELOG 加 0.58.19 用户口径条目(不再假死 + 停止立即生效)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 12:42:48 +08:00
caoqianming
da289b2ce2
feat(ops): 畸形 tool_call 进工具失败聚集 + 巡检邮件只发活跃聚集(bump 0.58.19)
...
畸形轮整轮丢弃、messages 无痕,admin「工具失败聚集」原本看不见这类失败
(toolfail 只扫 role=tool 报错);且巡检邮件去重集是内存态,每次部署清零,
高频部署期每次重启都把 7 天窗口内早已安静的存量聚集重发一遍。
- 新增 record_malformed_tool_call:丢弃畸形轮时写 usage_events 一行
kind=tool_malformed(units={tool,len,err,head,tail,tokens_in/out}),
cost_cny 恒 0 —— cost 全 kind 合计且随任务展示,provider 抖动不算用户
花销;真实 token 快照进 units 供反推浪费。DB 写失败静默不阻塞重试
- core/toolfail.py 加第二段扫描(usage_events kind=tool_malformed),聚成
kind=malformed 同构 cluster,admin 面板/巡检邮件零改动自动带上;聚合
逻辑抽 _add 闭包双数据源复用
- 巡检邮件发信前加 count_24h>0 过滤:已安静的聚集重启后不再重发,还在
烧的重启后再提醒一次(刻意保留)
- 新增 tests/test_toolfail_malformed.py 4 用例(mock session_scope);
RUN.md env 巡检段 + 故障兜底同步;PROGRESS 更新
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 12:04:22 +08:00
caoqianming
3c714b34c4
fix(loop): 畸形 tool_call 首败即降级非流式 + 损坏原文留痕(bump 0.58.18)
...
task 716ed3be 实测:deepseek-v4-pro 大参数中文 write(3~4k 字符)流式重 roll
同轮连挂 3 次全畸形,失败强相关而非独立随机,每章拖 3 分钟+,最后总靠非流式
兜底救场,3 次流式重试纯烧 token。
- _MAX_MALFORMED_RETRIES=3(3 流式+1 非流式)→ _MAX_MALFORMED_ATTEMPTS=3
(1 流式+2 非流式),首败即降级;修掉旧 warn 计数可显示 "4/3" 的 off-by-one
- 新增 _log_malformed_args:丢弃前把损坏 arguments 首尾各 300 字符(ascii()
转义)+ JSON 报错位置打 stdout [malformed] 标签 —— 畸形轮不入库,这是唯一
留痕,供定性 provider delta 错位 vs 本地拼接 bug 及报 case 取证
- 新增 tests/test_loop_malformed_retry.py 4 用例;RUN.md 故障兜底同步
(v4-pro 也中招 + [malformed] 取证口径);CHANGELOG/PROGRESS 更新
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 11:02:27 +08:00
caoqianming
4a591e1a90
fix(web): 历史消息丢图片预览/chip——taskMeta 竞态 + 中文路径抽取(bump 0.58.17)
...
两根因:
1. selectTask 并发化(0.12.16)后 renderMessages 在 Promise.all 内执行,
state.taskMeta 必然还是旧值/null → wd 错 → chip+inline 图整体丢失。
loadMessages 加 {render} 开关,首屏渲染延后到 taskMeta 赋值后(零额外 RTT)。
2. extractArtifactRels lead 边界不认汉字("已保存到<wd>/xxx.png"不命中),
tail 会把紧贴的中文标点连后文吞进文件名。lead 改任意非 [A-Za-z0-9._-],
tail 排除集抽 _TAIL_CLS 常量补中文标点(主规则+legacy 双处)。
node 离线回归 12 用例全绿;CHANGELOG 面向用户补记。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 10:29:23 +08:00
caoqianming
4d7075aeb8
feat(web): 有 gpt_image 权限的用户生图默认 GPT 生图(bump 0.58.16)
...
- /v1/image_models 的 is_default 改为 _default_image_variant:档位里有
gpt_image(pro/admin)→ 默认它,否则仍过滤后第一个(seedream)
- _resolve_image_model 空串时按同一 helper 解析该用户默认,下拉默认选中
与实际起 run 挂的图像工具保持一致;用户仍可下拉切回豆包 Seedream
- 无 user_id 内部路径(定时任务/messenger)行为不变
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 10:26:31 +08:00
caoqianming
61137611a8
feat(prompt): 结构图 mermaid vs 生图改 ask_user 让用户点选(bump 0.58.15)
...
用户反馈画流程图/架构图时模型直奔 mermaid,想点名用生图没入口。根因:
岔路引导只在 imagegen SKILL.md 里,mermaid 路径不触发 load_skill,模型
看不到岔路口;且 SKILL.md 把"流程图/架构图"硬性划给 mermaid。
- agent_builder: 新增 _MEDIA_DIAGRAM_FORK_SEG(挂任一图像工具时进媒体段)
—— 结构图先 ask_user 二选一(mermaid 矢量 vs 生图视觉版);免问例外=
已点名工具 / 本对话已选过 / skill 管线内部要求 mermaid
- imagegen SKILL.md: 岔路段同口径重写 + 补"生图画结构图取舍"段(中文标签
易乱码/位图不可改字提前说清,prompt 按风格化示意少文字装配)
- SKILL_LIST / PROGRESS / CHANGELOG 同步
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 10:18:48 +08:00
caoqianming
dadcc8e7e7
feat(ops): toolfail 聚集加时间分布,活跃/已安静一眼分(bump 0.58.14)
...
修掉的问题旧记录挂满 7 天窗口,和还在发生的没视觉区别。聚合加
daily(24h 分桶)/count_24h/first_at,活跃在前安静沉底;admin 表
加近24h+趋势列,安静行淡化;告警邮件带近24h。API 只加字段。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:27:40 +08:00
caoqianming
073d9ef025
feat(prompt): look_at_image 引导改"慢、谨慎调"口径(bump 0.58.13)
...
tool description 与 system prompt 媒体段原本强调"便宜(< ¥0.01)",等于鼓励随手调;
实际痛点是慢(一次几十秒)。两处改为:SLOW / 谨慎调用 / 只在确实需要图内容时调 /
想问的一次在 question 里问全 / 绝不对同一张图反复看;"何时不调"补"图内容可有可无"。
纯 prompt 调整,无行为代码变化。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:23:42 +08:00
caoqianming
4cdaf9a5d3
feat(sandbox): 镜像补 file 命令(bump 0.58.12)
...
toolfail 巡检首批产出:7 天 13 任务 9 用户撞 command not found——
模型验产物类型的高频动作,slim 基底不带。独立小 apt 层,不打穿
pip/chromium cache。olefile/cairosvg 评估后不装(可 pip 自愈,
cairosvg 进共享 requirements 会坑 Windows host)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 08:50:55 +08:00
caoqianming
62bfb07312
feat(ops): 工具失败聚集巡检,同签名报错自动冒头(bump 0.58.11)
...
反 9dcae061 结构性缺口(mmdc 挂 90 天 0 成功无人知,靠人工扫 DB 才发现):
- core/toolfail.py:扫近 7 天 role=tool 错误([Error/timeout/exit!=0]),
按 工具名+归一化签名(路径/数字/uuid 抹平) 聚合,>=5 次且跨 >=2 task 判聚集
- web/app.py _toolfail_scanner:每天跑(仿 _disk_scanner),聚集发
ZCBOT_DEVELOPER_EMAIL(复用 SMTP_*,未配则日志);签名进程内去重
- GET /v1/admin/tool-failures + admin 页"工具失败"表(低阈值看全量)
生产 DB 冒烟:一次扫出 8 类真聚集(沙箱缺 file 命令 x13 跨 13 task、
web_fetch 断网 x7、mmdc 超时批次 x6 等)。无新表无 migration。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 08:36:31 +08:00
caoqianming
73d07c9560
fix(sandbox): init.sh 放行容器自身 loopback,mermaid 渲染真·终案(bump 0.58.10)
...
90 天 67 次 timeout 真凶:127.0.0.0/8 DROP 掐死 puppeteer 连 chromium 的
127.0.0.1 DevTools WebSocket(SYN DROP -> TCP 重试 ~127s,恒定 2m15s 超时
+ [object Object] + 零 CPU)。容器 127.0.0.1 是自己 netns 的 lo、到不了宿主,
DROP 无安全收益纯误伤。apply_blocklist 首条 -o lo ACCEPT(v6 同款),
127.0.0.11:53 豁免收编,127/8 与 ::1 DROP 降为 belt-and-suspenders。
服务器全款复刻容器实测:放行前小图也挂 2m14s;放行后小图 3.9s、
真实大图 -w 2800 -s 2 仅 5.2s(1 vCPU 渲大图慢亦为误判)。
之前 probe 全因 --entrypoint bash 绕过 init.sh 无 iptables 而全绿误判。
生效:镜像重建(init.sh COPY 层秒过)+ 回收存量容器。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 10:40:48 +08:00
caoqianming
b4931418f3
fix(sandbox): probe_mermaid.sh 临时容器用法补 -i 注释(bump 0.58.9)
...
不带 -i 时容器 stdin 为空,bash 读 /dev/stdin 静默退出零输出,
探针看似跑完实则没跑(0.58.9 服务器验证实测踩中)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 10:14:20 +08:00
caoqianming
d7add3926c
fix(sandbox): mermaid 渲染必崩定案:chromium 坏点版本;refresh 旋钮 + build canary 根治(bump 0.58.8)
...
9dcae061 三轮排查收官。真凶 = 7-08 镜像重建时 apt 烤进的 chromium 点版本
150.0.7871.46 自带启动即崩 bug(int3 自杀、零日志、strace 无失败 syscall;
上游 .100 已修,容器内 --only-upgrade chromium 实测复活出图)。被洗清的:
hardening 六件套 / seccomp / AppArmor / overcommit / ulimit / max_map_count /
QEMU EPYC vCPU / 镜像环境(纯净 trixie 对照 rc=0 反转定位)。
修复:
- Dockerfile ARG CHROMIUM_REFRESH cache-bust 旋钮:chromium 出问题改日期
重建,只刷 chromium 层,pip 大层不动
- Dockerfile build 末尾渲染 canary:真渲一张中文最小图,chromium/字体链坏
则 build 当场失败(有它 7-08 就不会把坏版本带上线)
- RUN.md 故障兜底条目改写最终版(两根因 + 判别特征);CHANGELOG 面向用户记
部署:服务器重建镜像(新 ARG 自然打穿 chromium 层 cache)+ 回收存量容器。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 12:19:31 +08:00
caoqianming
9f78b151c1
fix(sandbox): round-3 探针改 pinned 版本直下,绕开被墙的版本解析(bump 0.58.7)
...
@stable tag 要先访问 Google last-known-good 版本解析端点(被墙)才换到具体
版本号,--base-url 只管二进制下载不管解析 —— 这是上一轮三源全败的真因。
改从镜像内 puppeteer-core revisions.js 读 pinned chrome-headless-shell 版本
直下阿里 binary 源(且正是 mermaid-cli CI 实测版本);错误输出 grep Error 行;
加 [步骤] 参数支持只跑第 3 步(1/2 已有结论:静默 CHECK 无 FATAL、栈无符号、
CPU 无 pku/shstk/ibt)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:04:23 +08:00
caoqianming
0631aa4a4f
fix(sandbox): round-3 探针下载源改阿里(bump 0.58.6)
...
npx 拉 @puppeteer/browsers 包显式 npm_config_registry=registry.npmmirror.com;
chrome-for-testing 二进制按 阿里 CDN -> 阿里 registry binary -> 官方 三级
fallback(服务器实测默认源下不动)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:58:02 +08:00
caoqianming
6ce2a0b70d
feat(sandbox): chromium 秒崩 round-3 探针:FATAL 抓取 + headless-shell 修法验证(bump 0.58.5)
...
bisect 全 case FAIL(含无 hardening baseline 与 seccomp=unconfined)+ 宿主
overcommit/ulimit 数据干净 → 嫌疑收敛为 trixie chromium 150 构建 × 本宿主
组合本身跑不了。round-3 脚本:
1/2. 抓崩溃真实 FATAL 行(此前被 head 截断)+ gdb backtrace(best-effort)
3. 实测 Google 官方 chrome-headless-shell(npmmirror 下载)+ mmdc 接上出图,
直接验证「镜像弃发行版 chromium、改装 puppeteer pinned headless shell」
修法可行性,PASS 即定案动 Dockerfile
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:54:16 +08:00
caoqianming
6c3122eb4b
feat(sandbox): chromium 秒崩第二根因 bisect 探针(bump 0.58.4)
...
pids-limit 1024 部署后复测(task 9dcae061,green 实例全新容器):mmdc 仍
首调秒崩 "Failed to launch the browser process: Code: null" —— 排除 pids
打满,第二根因在 hardening flag × chromium 150(trixie)交互。
新 deploy/sandbox/probe_chromium_bisect.sh:线上 docker run 全款 hardening
逐 flag 开关(no-new-privileges / read-only / cap-drop / mem·cpu /
seccomp=unconfined),每 case 跑 mmdc + 裸 chromium 两条,--user zcbot +
HOME=/tmp 忠实复现 exec 路径,尾部带判读表。RUN.md 故障兜底条目同步。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:35:10 +08:00
caoqianming
0d7725b010
feat(admin): 用量表默认时间范围 all→近7天(bump 0.58.3)
...
admin.js「按模型」「各用户用量」两表初始 range 从"全部"改"近7天",
下拉仍可切全部/30天;API 端 range 默认值不动。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:09:49 +08:00
caoqianming
38f6157b3a
fix(sandbox): pids-limit 256→1024 修容器内 mermaid 渲染必崩(bump 0.58.2)
...
task 9dcae061 排查:近 90 天 mmdc 渲图 0 成功(26 launch fail + 67 timeout)。
根因:--pids-limit 线程也计数,chromium headless 一次 ~150-200 线程,叠加
shell 超时残留的 mmdc+chromium 树(超时只杀 host 侧 docker CLI),256 打满
→ pthread_create EAGAIN(06-23 c4f03d56 实锤)→ chromium SIGTRAP。
dbus 报错是干扰项(成功时也打)。
- agent.yaml sandbox.pids_limit + pool.py DEFAULT_PIDS_LIMIT 两处 256→1024
- RUN.md 故障兜底补条目;PROGRESS 记录
- 附诊断脚本 scripts/diag_task_9dcae061.py / diag_mmdc_history2.py / diag_mmdc_ok_case.py
- 待服务器验证:重启 web + 容器回收后跑 probe_mermaid.sh;若 chromium 150
仍秒崩另查宿主 docker/seccomp;彻底修法是 Step 3b PGID kill 协议(未动)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:02:45 +08:00
caoqianming
86f50ff133
feat(models): unifyllm 再加 gpt-5.5 / gpt-5.4-mini 进 pro 档(bump 0.58.1)
...
- config/models/unifyllm.yaml 新增 gpt55 / gpt54_mini variant(照 gpt56_sol
模板,temp 硬约束 1.0;mini 档 quality 记 good、max_iterations 100)
- agent.yaml model_tiers.pro 加 2 条
- diag_unifyllm.py 默认冒烟清单 5→7 个模型(RUN.md 同步),两模型
tool-calling 实测均过
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 21:06:37 +08:00
caoqianming
5a8f5bd930
feat(media): 第二图像后端 gpt_image(unifyllm 网关,pro 档)(bump 0.58.0)
...
- config/media/unifyllm.yaml + tools/gpt_image.py:仅 prompt 参数(网关忽略
size/quality,尺寸上游自动定,不支持 i2i),b64 直落 figures/,banner 协议
同 seedream,价目未知期 cost 记 0、usage tokens 进 units 留对账
- 媒体装配泛化:ArkConfig 通用键名(api_key_env/base_url,ark_* 兜底);
_choose_image_variant 跨 provider 选 image variant(默认仍 seedream_5,
本 run 只挂一个图像工具);_MEDIA_TOOLS_BLOCK 拆分段按实挂工具拼装
- web:_list_media_variants 扫 config/media/*.yaml(image/video 清单 +
admin 门控 catalog);media.js 三处白名单加 gpt_image;prompt 润色 hint
无 default_size 不再谎报 2048
- gpt_image 进 pro 档,与 seedream 共享 images_per_day 配额
- 手工验证 scripts/test_gpt_image_manual.py 全过(含真调网关落图),
unittest 201 过
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 20:47:06 +08:00
caoqianming
9ce992597b
docs(changelog): 0.57.0 条目去掉具体模型名,泛称国际旗舰模型(bump 0.57.1)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:18:29 +08:00
caoqianming
ba2afa9e98
feat(models): unifyllm 网关接入,5 个国际旗舰模型进 pro 档(bump 0.57.0)
...
- config/models/unifyllm.yaml:Claude Fable 5 / Opus 4.8 / Sonnet 4.6 /
GPT-5.6 Sol / Gemini 3.1 Pro,litellm openai/ 前缀 + api_base 覆盖,零代码
- 实测:gpt-5.x litellm 硬拦 temp!=1、gemini-3.1-pro temp=0.3 返回空,
两者 optimal_temperature=1.0;流式+tool calling+parallel 5 模型全通
- scripts/diag_unifyllm.py 冒烟脚本(--stream/--temp)
- 服务器需代理出口(直连 unifyllm.ai TLS 失败),RUN.md env 段已注明
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:16:46 +08:00
caoqianming
daf0db854a
feat(procs): 后台进程 bg proc + 对话锁/完成通知 + seedance 续查(bump 0.56.0)
...
- shell/run_python 加 background=true(模型判断,>~1min 走后台):进程 detach 独立运行,
不受工具超时/服务重启/蓝绿切换影响;新 check_process 工具查状态/日志尾部/终止
- 状态协议纯文件(<user_root>/.zcbot_procs/<task>/<proc>/,exit_code 出现即终态,
无 DB 无队列组件):host=stdlib wrapper detach(限时+杀树+日志截尾 10MB);
docker=专用容器 zcbot-proc-*(同款硬化+iptables,product=proc 与 sandbox
reaper/shutdown_all 生命周期解耦);web lifespan 每小时 sweep(7d TTL+孤儿容器)
- web:GET /v1/procs(用户级)+ POST /v1/tasks/{id}/procs/{pid}/kill;前端
[Background] 工具卡活化(spinner/跳秒/停止,历史重渲恢复)+ 完成 toast(跨 task
可跳转)+ 对话锁(bgproc 运行期 composer 发送→停止/Enter 拦截,先锁后放消
刷新时序窗口;润色/语音与 streaming 期一致可用)
- seedance:resume_task_id 续查(轮询超时/中断后取回结果,跳过提交不重复计费),
fast/pro poll_timeout 统一 1200s;取消/超时文案引导续查
- core/llm.py:litellm timeout 显式化(默认 600s 不变,ZCBOT_LLM_TIMEOUT_S 可调)
- DESIGN 新增 §8.12(含边界:不做 job 链/自动续跑/不引 Celery);RUN/PROGRESS/
CHANGELOG 同步;scripts/test_bgproc_manual.py 手工验证;unittest 全量 201 过
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:47:11 +08:00
caoqianming
3ef472c112
feat(files): 文件面板根目录隐藏定时/渠道系统工作目录(bump 0.55.2)
...
- /v1/files 根层与 /v1/folders 候选过滤 scheduled-*/wechat-*/wecom-* 工作目录
- 判定走 DB 回查(_system_wd_names:scheduled_job_id / channel),孤儿目录不隐藏
- 仅列表降噪非权限拦截:带 path 访问照常,点任务跳入目录的前端行为不变
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 12:12:22 +08:00
caoqianming
9e59cf9e44
chore(quota): per-user 磁盘配额 5GB -> 20GB(bump 0.55.1)
...
应用层软配额上调(agent.yaml quotas.disk_bytes_per_user,重启 web 生效);
RUN 故障兜底行 + CHANGELOG 用户条目同步。OS 层 prjquota 仍留待外部开放前。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 08:35:52 +08:00
caoqianming
5b6adb3746
feat(changelog): 用户版更新日志 CHANGELOG.md + /v1/changelog + 点版本号弹层(bump 0.55.0)
...
- 新增仓库根 CHANGELOG.md:面向用户口径(只记可感知变化),回填至 0.30.0 共 25 条
- GET /v1/changelog:公开无鉴权,`## <版本> — <日期>` 正则切段,mtime 缓存热改即生效,无 DB 零 migration
- 前端 changelog.js:右栏底部版本号可点弹更新日志;localStorage 记 last-seen 版本,
与 /healthz 版本不一致亮红点提示,打开即清;接入 Esc/遮罩关闭栈
- 单测 tests/test_changelog.py 3 项(含真文件格式校验);全量 201 过
- CLAUDE.md 加维护约定:bump 版本若用户可感知顺手补 CHANGELOG 条目
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 16:49:26 +08:00
caoqianming
8aa6bed79c
docs(models): 清理 deepseek-chat/reasoner 下线残留描述(bump 0.54.2)
...
旧模型 2026-07 已下线:删 deepseek_v4.yaml 里"可退回 deepseek-chat"的
误导注释(照做会打不通),DESIGN 附录迁移提醒改为已完成陈述。
全库 grep 确认代码/配置本体无旧 model id 引用。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:28:26 +08:00
caoqianming
66a515aa3d
feat(context): §8.8 Phase 2 长会话中段折叠摘要 + 头部压缩指示环(bump 0.54.1)
...
- 折叠(0.54.0):run 起点窗口体量达 reliable_context×85% → core/context_fold.py
把中段折叠为固定模板结构化摘要(路径/ID/数值原文保留),写 tasks.context_summary
(migration 0021)+ 推进 context_base_idx;Session.load 注入仅内存「前情摘要」
(_n_head/_base_idx 维护 idx 映射);摘要调用复用会话 prepare 后前缀 + 末尾追加
指令,与上一轮 chat 缓存字节一致近全程 hit;失败零阻塞(warn+跳过);「新话题」
硬重置 / web 清空对话顺带清摘要,gap 软重置保留
- 前端(0.54.0):新 SSE context_fold 事件(start 状态行提示 / done 流内分隔条);
补 warn 事件渲染 —— 此前 loop 熔断/重复拦截提示在 web 端被静默丢弃
- 压缩指示环(0.54.1):头部金额撤下明面(挪 hover tooltip),换 SVG donut
(绿<50% 未压缩 / 琥珀 50-85% 压缩区 / 红≥85% 达折叠阈值)+ 已整理×N;
GET /v1/tasks/{id} 详情新增 context_window_chars/limit_chars/pressure/folds
(additive,列表端点不加);折叠计费 kind="context_fold"(record_chat_usage
加 kind 参数,折叠次数 = count 该 kind,零 migration);llm_start 事件补
context_limit_chars,run 中实时刷环
- DESIGN:§8.11 新增「最小子循环 delegate」预留(上下文隔离而非多 agent 编排,
按 diag 数据触发,无信号不实施);§1/§6 "不做 subagent" 收窄为编排型;
§8.8 Phase 2 标 ✅ 沉淀六条取舍;§7.2 SSE 清单补 warn/context_fold
- tests/test_context_fold.py 12 项(切点/映射/apply_fold/maybe_fold 打桩),
全量 198 过;本地 DB 已升 0021
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:39:31 +08:00
caoqianming
f1c7f70e3c
feat(skills): 新增 rebuttal 审稿回复 + paper/review 移植 nature 系增强(bump 0.53.1)
...
调研 GitHub nature skill 生态(Yuan1z0825/nature-skills Apache-2.0、
Boom5426/Nature-Paper-Skills MIT)后移植三块:
- 新 skill rebuttal:决定信/审稿意见 → 逐点回复信 + 修改清单 + cover
letter;拆条分诊(8类×4档×12动作标签)、零编造铁律、先改稿后写信、
readiness 四档;渲染复用 --profile paper,中英双语
- paper:figure_discipline.md(一图一论断/panel 角色/图例规则,阶段三)
+ submission_audit.md(六步通读审计 + 数据可用性声明模式,阶段六)
- review:sci_polish.md 英文 SCI 润色子模式(四层递进诊断 + 中译英
先抽命题不逐句翻)
SKILL_LIST 刷新(18 个)+ PROGRESS 记录。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 16:18:47 +08:00
caoqianming
5d1da0672e
feat(asr): 录音文件转写工具 transcribe_audio(讯飞 LFASR,bump 0.53.0)
...
用户上传录音(会议/访谈/语音备忘)要"转文字/总结"的需求落地:
- core/asr_lfasr.py:讯飞录音文件转写标准版客户端(raasr v2 signa 签名 +
upload/getResult 异步订单 + lattice 解析 + roleType=1 说话人分离格式化);
单文件 5h/500MB,服务端解码,本地不需要 ffmpeg
- tools/transcribe_audio.py:host-side tool(look_at_image 同形态,路径复用
image_ref.resolve_in_root);全文落 <音频名>.transcript.txt 只回预览+路径;
轮询期响应停止按钮;凭据不齐不挂工具
- 新 env XFYUN_LFASR_SECRET_KEY(APPID 与 IAT 共用,SecretKey 独立)
- 实测:TTS 合成 12.6s 中文语音 13s 出稿逐字全对;originalDuration 是上传参数
回显,realDuration 才是真实时长
- smoke_transcribe_audio.py(离线 12 项全过)+ diag_lfasr.py 诊断脚本
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 15:33:14 +08:00
caoqianming
f59b4a6ce1
docs(run): 企微回调平台侧失效坑 + 语音落地收尾(bump 0.52.6)
...
企微文字/语音全无反应的根因:回调注册在企微平台侧失效(服务器/代码/env
全正常,消息不投递),后台重新保存「接收消息」API 配置即恢复。故障兜底
补该行(判据:`[wecom] inbound` 行没有=平台没投递,与代码无关)。
两渠道语音全链路用户实测跑通:个人微信 SILK->pilk / 企微 AMR->ffmpeg
-> 讯飞 IAT -> 回显已识别 -> 进对话。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 10:58:02 +08:00
caoqianming
5b09ec5c64
fix(wecom): 最终回复推送失败不再静默(bump 0.52.5)
...
_bg 最终 push_wecom 的返回值一直被忽略,message/send 失败(如企微
「企业可信 IP」拦截 60020)完全无痕。统一走 _push_checked,失败落
`[wecom] push failed: <reason>` 日志(带企微 errcode/errmsg)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 10:49:50 +08:00
caoqianming
52db2ef939
feat(wecom): 回调加入站留痕日志,断定语音回调是否被投递(bump 0.52.4)
...
企微发语音无反应且 [wecom-voice] 日志零出现(该分支被调用必打),疑企微
侧未投递语音回调;text 路径原先零日志无法对照。解密成功后每条打
`[wecom] inbound msgtype=... from=...`:text 行出现而 voice 行不出现
即可断定平台侧未投递,与处理代码无关。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 10:38:16 +08:00
caoqianming
386a9ee048
fix(sandbox): npm 步骤加重试,抗境内 mirror 断流(bump 0.52.3)
...
腾讯 npm 源拉 cytoscape 偶发 ECONNRESET 崩整个镜像 build(部署实测)。
fetch-retries 2->5 + fetch-retry-maxtimeout 120s;RUN 故障兜底补该行,
并写明部署不涉及镜像变化时用 update_bluegreen.sh --skip-build 跳过重建。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 10:25:37 +08:00
caoqianming
2ca210dbad
fix(sandbox): 镜像 build 剔除 host-only 依赖,修 pilk 编崩(bump 0.52.2)
...
pilk 进 requirements 后 sandbox 镜像也去装它,python:3.12-slim 无 gcc
整个 build 编崩(部署实测)。容器内 run_python 用不到语音解码:
- requirements 行尾标 `# [host-only]`(通用机制,后续 host-only 依赖复用)
- Dockerfile 装包前 `grep -v '\[host-only\]'` 过滤
- 不往镜像塞 build-essential(+~250MB 无收益)
- RUN 故障兜底行补 sandbox 变体处理
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 10:05:46 +08:00
caoqianming
98a40c2971
docs(run): 故障兜底补 pilk 源码编译缺 gcc 坑(bump 0.52.1)
...
服务器 py3.12 无 pilk 预编译 wheel,pip 退源码编译报
`command 'x86_64-linux-gnu-gcc' failed: No such file or directory`。
处理:apt install build-essential python3-dev 后重跑 update(部署实测踩到)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:52:31 +08:00
caoqianming
abe2f6b9f2
feat(wechat): ClawBot 语音接入 + 企微语音路径补日志(bump 0.52.0)
...
- ilink: 解析 voice_item(type=3,真机实测)-> InboundAttachment(kind=voice),
CDN 下载/AES 解密复用现有 download_media
- core/audio.py: 统一入口 transcribe_voice(bytes) -- 按字节头分流:
SILK v3(微信,\x02#!SILK_V3)-> pilk 解 24k PCM + ffmpeg 重采样 16k;
其余(企微 AMR 等)-> ffmpeg 直解;两渠道共用
- inbound: 语音附件就地转写,回执「已识别:...」,文本并入对话;失败回可读提示
- wecom 回调补日志: voice 入站行 / transcribe 异常行 / push_wecom 失败 reason
(原返回值被忽略纯静默)/ 未处理 msgtype 行 -- 排查"发语音无反应"
- 新依赖 pilk(requirements);RUN/PROGRESS 同步
- 已实测: pilk 造微信同款 silk 全链转写逐字正确;AMR 路径回归通过
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:47:22 +08:00
caoqianming
3579e38df5
feat(wecom): 入站语音消息转写进对话(bump 0.51.0)
...
- 回调新增 msgtype=voice 分支:media/get 下 AMR -> core/audio.py(ffmpeg
子进程解成 16k PCM)-> 讯飞 IAT 整段转写 -> 当用户消息走同一条对话链路
- 下载/解码/转写全放后台任务,回调窗口内只登记 MediaId(防 5s 超时重推)
- 识别结果先推「已识别:...」回显;缺 ffmpeg/讯飞未配/识别为空各回可读提示
- ClawBot getupdates 对未识别 item 打结构日志(个人微信语音探协议准备)
- 服务器新增系统依赖 ffmpeg(RUN.md env 段;可选 FFMPEG_PATH 指定路径)
- 已实测:TTS 中文 4.5s -> AMR-NB 8k -> 解码 -> 转写逐字正确
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:01:37 +08:00
caoqianming
09db2c9c93
feat(ppt): 验收改纯代码几何质检,撤除渲图 vision 验收闭环(bump 0.50.0)
...
真实会话复盘:沙箱 chromium "找到但渲染崩"无回退,硬验收门+死路逼模型
pip install cairosvg 手写渲染;look_at_image 逐页 26-42s 纯烧 token。
- pptx_cli.py:删导出渲图验收门(_acceptance_problems)+ --allow-unreviewed;
几何硬错误质检门(check13/14 越界/压字/错位)原样保留并继续在导出边界复跑
- 删 accept_pages.py 及 acceptance.json 登记机制
- svg_preview.py 保留为手动工具:chromium 渲染失败按页自动回退 cairosvg
(原来只在"完全没浏览器"时才回退,找到但崩=死路)
- SKILL.md:阶段五简化为 finalize 单步;渲图预览零提及(提示面不给渲染
入口);强调导出唯一入口 svg_to_pptx;Geometry warning 改"回源核对坐标"
- DESIGN §8.10 记录取舍推翻(0.36.0 渲图验收闭环);PROGRESS/SKILL_LIST 同步
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:41:28 +08:00
caoqianming
e9e549b606
feat(web): 语音输入流式化:边说边出字,松手秒出最终文本(bump 0.49.0)
...
根因:整段模式按 40ms/帧回放音频给讯飞 = 按录音时长等比例白等(16.8s 实测等 4.6s)。
- 新增 WS /v1/asr/stream 流式代理:首消息 JWT 鉴权(不走 query 防进 access log),
二进制帧实时转发讯飞(dwa=wpgs),动态修正(sn/pgs/rg)在服务端 XfyunStream 合并,
回推 {"text","final"} 增量全文;引擎提前收尾(静音/60s)推 final 前端自动结束
- 前端:录音即连 WS,增量降采样(carry 跨片连续)实时上行,录音面板实时字幕;
WS 失败静默退回整段 POST 兜底。实测 16.8s 音频 end→final 179ms(原 ~2.5s+)
- 整段兜底路径帧间隔 40ms→8ms(实测 4.6s→1.75s,识别文本一致)
- nginx 示例加 /v1/asr/stream WS Upgrade 段;RUN 故障兜底补讯飞 IP 白名单 403 与
反代挡 WS 两条
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:42:20 +08:00
caoqianming
7b20cae85b
feat(web): 语音手势两端统一:按下时长判别,PC 鼠标也可按住说话(bump 0.48.1)
...
- 不再按 pointerType 分流:按住 ≥300ms = 按住说话(松开转写,上滑取消);
快速点一下 = 连续录音免持(说完 Enter / 再点结束,Esc 取消)—— PC 手机同一心智模型
- press 发起的录音,松手后合成 click 全压掉;"点按钮停止连续录音"的 press 不压(_pressActive)
- getUserMedia 完成前松手按同一时长规则收尾;键盘激活按钮兜底走 click 起录
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:13:26 +08:00
caoqianming
e911eb2c6b
feat(web): 语音输入手势升级:触屏按住说话 + 桌面 Enter/Esc 收尾 + 波浪反馈(bump 0.48.0)
...
- 触屏(微信/DeepSeek 式):按住录音,松开转写,上滑 60px 松开取消;pointer capture + 合成 click 压制
- 桌面:点击开始,说完 Enter 结束转写 / Esc 取消(capture 拦截防触发发送);不做 VAD 静音自动停
(口述需求句间停顿 3~5s 常态,自动截断更伤;业界听写类全是手动收尾)
- 录音面板替换 textarea:实时波浪(采集 chunk 顺手算 RMS)+ 计时 + 手势提示;iOS 手势内 resume
- 纯前端(chat.js + dev.html),后端零改动
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:03:47 +08:00
caoqianming
a36a8f1da4
feat(web): 语音输入(讯飞语音听写 IAT)(bump 0.47.0)
...
- core/asr_xfyun.py:IAT wss 客户端(HMAC-SHA256 签名 / 9600B 帧 / vad_eos=10000),整段 PCM→文本
- web/app.py:POST /v1/asr/transcribe(裸 PCM 16k/16bit/mono body,>60s→413,未配 env→501,讯飞错→502)
- 前端「🎙 语音」按钮:Web Audio 采 PCM 降采样 16k → 转写 → insertText 填输入框(接 undo 栈,不自动发送);60s 自动停;渠道只读镜像锁定
- 凭据走 env XFYUN_APPID/API_KEY/API_SECRET(.env 当前为测试 key,待换正式)
- scripts/diag_asr.py 链路诊断;requirements 显式加 websockets
- 已实测:TTS 合成 5.2s 中文经端点转写逐字正确;企微语音(AMR 需 ffmpeg)留二期
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:16:58 +08:00
caoqianming
9ae1040faf
fix(web): 消息分页窗口对齐 user 轮次边界(bump 0.46.2)
...
原按固定 30 条翻页/跳转,窗口边界常切在 assistant 工具调用序列中间
(顶部孤儿 tool 消息);圆点跳转 before_idx=idx+11 启发式长轮拉不全。
业界调研(Discord/OpenAI):分页单位保持消息条数,轮次归组属前端职责。
- 向上翻页 alignedEarlierLimit:按整轮累加攒够 30 条,窗口顶恒为 user 消息
- 圆点跳转:after_idx=起点-1 + 相邻 outline 边界差精确拉整轮
(短轮补到 30,超大轮按 MSG_FETCH_MAX=100 截断,后续加载自愈对齐)
- outline 未加载/idx 缺失 → 回退旧按条行为;后端 API 零改动
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:07:40 +08:00
caoqianming
a47a7799e7
refactor(wecom): 撤除欢迎推送机制,聊天优先布局下不需要(bump 0.46.1)
...
实际部署改为聊天优先布局(不配应用主页 + 自定义菜单「工作台」按钮指向
/v1/wecom/entry),点应用直进会话,冷启动问题不复存在,0.46.0 的欢迎
推送成了死重:
- 删 wecom_welcomes 表(migration 0021 撤下;DB 已 downgrade 回 0020,
表建后无一行写入)、maybe_send_wecom_welcome、免登回调欢迎钩子、
scripts/wecom_welcome_broadcast.py、wecom.list_visible_userids
- 保留:未绑定入站回绑定指引(聊天优先下必需)、core/branding.py 品牌单一源
- RUN.md 企微段只留推荐布局 + 未绑定指引;PROGRESS 同步
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 11:26:13 +08:00
caoqianming
f3be3d5016
feat(wecom): 会话冷启动欢迎消息 + 未绑定入站回绑定指引(bump 0.46.0)
...
背景:应用配「应用主页」后点应用只进主页,应用会话要等应用发过第一条消息
才出现在成员消息列表,首次登录的员工没入口和 bot 打字。最终采用聊天优先
布局(不配主页、自定义菜单放「工作台」按钮指向 /v1/wecom/entry)。
- 回调:未绑定成员的 text/图片/文件消息不再静默,每条回绑定指引
(聊天优先下新员工上来就打字,静默会被当成 bot 坏了;事件不回)
- 新表 wecom_welcomes(0021)+ service.maybe_send_wecom_welcome:
免登入口回调给首次进入的成员推欢迎语"种"会话入口;两层去重
(已推过 / 已绑定且有 chat task),claim-first 并发安全
- scripts/wecom_welcome_broadcast.py:配主页布局下的存量批量种入口
(可选工具,--dry-run 先看名单);wecom.list_visible_userids 枚举可见范围
- 品牌名单一事实源挪 core/branding.py(app.py BRAND 与欢迎语共用)
- RUN.md 企微段补推荐布局;PROGRESS 同步
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 11:11:10 +08:00
caoqianming
7f77e5aca9
feat(web): 移动端显示修复 + 对外品牌名 ZCBOT_BRAND_NAME(bump 0.45.0)
...
移动端:
- 浮层菜单(⋯ 任务菜单 / ⚙ 媒体模型弹层)原只右对齐触发按钮,手机上触发钮
靠左时菜单冲出屏幕左缘盖住正文;改为放不下时切左对齐 + 双向钳视口内
- 手机断点:菜单项加大触控目标、模型下拉限宽 48vw、header 品牌名单行省略
- 「管理」入口挪到 任务/对话/文件 tabs 行右侧小按钮(CSS order 实现,
embed 与普通模式同款,embed 专属覆盖规则随之删除)
品牌名:
- 新 env ZCBOT_BRAND_NAME(默认「总院科研辅助助手」),zcbot 只是内部代号
- /healthz 返 brand 字段,前端 boot 拉取覆盖页面标题/顶栏/登录卡
- 微信/企微测试推送、OAuth 提示页、绑定页等用户可见文案改品牌名或中性「助手」
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:44:26 +08:00
caoqianming
b0a8802d35
feat(wecom): 企业微信应用主页免登进 embed 控制台(bump 0.44.0)
...
- 新端点 GET /v1/wecom/entry:企微客户端内走网页授权 snsapi_base 静默
无感,外部浏览器退 wwlogin 扫码,同一 URL 两端可用;state 签 login
哨兵防 CSRF(绑定回调显式拒绝该哨兵,两条流程不串)
- 新端点 GET /v1/wecom/entry/callback:code→userid→反查绑定→签 JWT→
302 dev.html?embed=1&wecom=1,token 走 URL fragment 不进日志;
未绑定成员提示先在控制台绑定,不自动建号(避免重复账号)
- 前端 wecom 变体:fragment 吃 token 即清 hash,不需要 parent_origin;
401/logout 回 entry 静默重签;iframe embed 与 postMessage 协议零改动
- wecom.py 新增 oauth_inclient_url();提示页 helper 提出共用
- EMBED.md §9 + RUN.md 企微段(应用主页 + 网页授权可信域名两项设置)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:09:52 +08:00
caoqianming
50e1d57ed5
fix(web): 跳秒指示覆盖刷新重连 + 发送后空窗(bump 0.43.1)
...
- 刷新/重连接管:phaseSince 随页面内存丢失,原先要等下一个 llm_start/
tool_call 才恢复跳秒——恰是用户因疑似卡死而刷新的场景;重建直播卡时
立即按"思考中"起跳,后续阶段事件校正(秒数从重连时刻起,非真实累计)
- 发消息 POST 返回即起跳:覆盖 build_agent + 首轮 TTFT,原先等 llm_start
才开始计时
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 09:16:00 +08:00
caoqianming
463e282afd
feat(web): run 长耗时可视化——reasoning 事件流 + 阶段跳秒指示(bump 0.43.0)
...
修"前端一直静止显示思考中被当成卡死":
- core/loop.py:新增 SSE reasoning{delta} 事件——thinking 模型分钟级推理
原先 delta.reasoning_content 整段丢弃,前端零反馈;流式/非流式兜底两路都发
- chat.js:reasoning 渲染成灰色可折叠"思考过程"卡(直播态随流展开、正文
开始折叠;历史消息里持久化的 reasoning_content 同款渲染)
- chat.js:活跃状态指示——TTFT/推理期占位段每秒跳"思考中/深度思考中 · Ns"
(data-status + CSS attr),工具卡运行中 spinner + 跳秒、tool_result 定格
为执行耗时;每轮 llm_start 重建占位段,工具轮之后不再空窗
- DESIGN §7.2 SSE 事件清单补 reasoning
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 09:02:31 +08:00
caoqianming
de53629573
docs(deploy): 497 跳转 301→308——修平台 POST 经跳转被降级成 GET(bump 0.42.5)
...
平台配 http:// 入口调 POST /v1/auth/login:497→301 后 requests 按规范把
POST 降成 GET 重发,只挂 POST 的路由回 405。示例改 =308(保留方法和
body,浏览器行为不变);治本是调用方直配 https(明文首跳会把
platform_key/JWT 暴露在网络上,重定向救不了已发出的那次)。进故障兜底。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 22:11:46 +08:00
caoqianming
db449b1723
docs(deploy): IP 访问证书警告的兼容边界(bump 0.42.4)
...
497 跳转目标示例改写死域名——明文 http://IP 访问被 301 带回域名,无
证书警告;https://IP 直连的警告是 TLS 固有行为(握手先于跳转)nginx 层
无解,入口统一域名 / 内网 DNS 补记录。进故障兜底表。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 22:03:42 +08:00
caoqianming
7db97f11c7
docs(deploy): nginx TLS 写法示例 + 497 跳转坑进故障兜底(bump 0.42.3)
...
实际部署上 SSL 踩出两坑:①同端口双 listen(ssl+明文)nginx -t 报
duplicate listen;②`if ($scheme = http)` 在 ssl-only 端口永远不触发,
同端口 HTTP→HTTPS 跳转要用 error_page 497 =301。示例配置补注释模板,
另提醒企微回调/可信域名只认 80/443 标准端口。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:25:19 +08:00
caoqianming
e586ffb79e
chore(deploy): pip install 加 -q 精简部署输出(bump 0.42.2)
...
日常部署依赖基本没变,全量 "Requirement already satisfied" 纯刷屏;
装新包 / 报错仍会输出,不影响排障。RUN.md SOP 说明同步。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:07:40 +08:00
caoqianming
700bb56ba3
fix(deploy): 蓝绿脚本 wait_health unbound variable(bump 0.42.1)
...
`local color="$1" url="...${PORT[$color]}..."` 同行多赋值:bash 先展开整行
参数再执行 local,url 里的 $color 展开时还没被赋值,set -u 下报
"color: unbound variable"。拆成逐行 local。服务器上重跑
update_bluegreen.sh 即可(会自拉本修复重执行;上次失败停在 green 已起、
nginx 未切,重跑幂等续走)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:05:48 +08:00
caoqianming
8b482e7a30
feat(broker): 外置 Redis pub/sub——蓝绿跨实例 SSE/cancel 可达(bump 0.42.0)
...
用户量上升重评落地(DESIGN §7.0):web/broker.py 拆 LocalRunBroker(原进程内
实现,默认)/ RedisRunBroker 同接口双实现,ZCBOT_REDIS_URL env 开关。
- event:PUBLISH zcbot:ev:<tid> + 单条 async pubsub reader 路由本地订阅 queue
(Redis 只管跨进程一跳,fan-out 最后一公里仍在进程内)
- done:SETEX 60s key + publish 双通道;订阅先挂 channel 再查 key,晚到不漏
- cancel:SETEX/GET/DEL key,loop chunk 间 poll,跨实例可达——蓝绿切换窗口
「刷新丢直播 / 停止失灵」两个边缘消失;稳态双实例分流只差 nginx 配置
- 容错:启动 ping 不通 fail-fast(同 sandbox init 纪律);运行中失败 10s 节流
log + 降级丢帧,run 不受影响;reader 1s 退避重连并重挂活跃 channel
- lifespan finally 加 broker.shutdown();sinks.py 解开对旧类名的 import
- tests/test_broker_redis.py 9 例(fakeredis:跨实例 emit/cancel、晚订阅 done、
降级不抛、fail-fast);requirements 加 redis/fakeredis
- RUN.md:env 段 + 蓝绿节 + bootstrap(apt install redis-server)+ 故障兜底 3 行
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:51:39 +08:00
caoqianming
bb6fd11dbb
docs: 规模设定重评——broker 外置 Redis 由搁置转推荐实施(bump 0.41.2)
...
用户量已非个位数,DESIGN §7.0/§8.4 与 RUN.md 蓝绿节的"当前规模不值得上
Redis"设定过时:①部署时总有 in-flight run,蓝绿窗口边缘常态化;②稳态双实例
分流是最近的扩容手段,broker 外置是硬前提。选型排除 PG LISTEN/NOTIFY(token
级 delta 耦主库)与 nginx sticky(upstream 变更时 hash 重排恰在部署窗失效)。
实施待确认,方案要点已记 DESIGN §7.0。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:28:17 +08:00
caoqianming
242548ea84
fix(deploy): 蓝绿端口反转——nginx 接管对外 8765,实例挪 8766/8767(bump 0.41.1)
...
对外契约(URL:8765)保持不变:客户端 / platform 嵌入 / EMBED 对接方零改动;
blue=8766、green=8767 只绑 127.0.0.1。bootstrap 顺序同步调整(停旧服务释放
8765 后 nginx 才能 reload 接管);已知边缘补文档:切换窗口内对旧实例 run
点「停止」送达不到(cancel 与 event 同为进程内 broker,根因一致)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:16:46 +08:00
caoqianming
1d62670366
feat(deploy): 蓝绿双实例无感部署落地——零 503 窗口 + SSE 不断流(bump 0.41.0)
...
- deploy/update_bluegreen.sh:复用 update.sh --prepare-only 做准备 → 起空闲色
→ healthz 验 instance → nginx upstream 切流 → 轮询 DB 等旧色 run 自然清空
(DRAIN_WAIT_SECONDS 默 600s,期间旧 SSE 继续直播)→ stop 旧色 → 残留
running 行标 error → enable 新色/disable 旧色;--switch 半途恢复调流
- deploy/zcbot@.service 模板 unit + deploy/instances/{blue,green}.env +
deploy/nginx/zcbot.conf.example(upstream 单行文件由脚本改写)
- tasks.run_owner(migration 0020):起 run 写 ZCBOT_INSTANCE,启动 reaper
只收自己色的孤儿,不误杀另一实例在跑的 run
- sandbox 容器名/label 带实例色,shutdown_all 只清自己色,并存期互不清扫
- 微信 ClawBot 长轮询 PG advisory lock 选主(detach 连接持锁 + 30s keepalive,
断连即让位),蓝绿并存不抢消息;定时任务 claim 本就 SKIP LOCKED 免改
- /healthz 加 instance 字段;update.sh 加 --prepare-only
- 单实例部署(ZCBOT_INSTANCE 未设)行为全部不变;RUN/DESIGN/PROGRESS 同步
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:03:10 +08:00
caoqianming
c52f399f07
fix(guard): ppt 伪导出根治三层——平台机检门 + 禁令去菜谱 + 脚本 hint 纪律(bump 0.40.0)
...
0.35.1 复发复盘:模型照 SKILL 禁令里的"配方"装 cairosvg 手搓 SVG→PNG
整页贴图导出,文档层禁令两次被证不够。三层治理:
- 平台层:core/pptx_guard.py + loop 钩子——shell/run_python 本步新产出
.pptx 命中"≥80% 页整版位图 + 全 deck 零原生文本"即在 tool 结果注入
[产物机检 ERROR] 当场逼返工;判定刻意保守(≥2 页、表格/图表算原生、
解析失败放行),tests/test_pptx_guard.py 8 例全过
- 禁令去菜谱:ppt SKILL.md:185/213、brief SKILL.md:26/103 否定式指令
改"正面窄门"(唯一入口 + 报错就修/如实上报),删掉可照做的违规配方
- 脚本 hint 纪律:pptx_media / svg_preview / crop_images /
fix_image_aspect / pptx_cli 共 5 处 pip install 提示改环境说明——
打印给 agent 的安装指令就是执行指令
DESIGN 新增 §8.9(推翻 0.35.1"平台层暂缓"拍板);PROGRESS 同步。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:40:54 +08:00
caoqianming
022eade18d
feat(research): paper_server 接口对接 API Key(bump 0.39.2)
...
paper_server GET 接口收紧要 Key:paper.py 全部请求带 ?api_key=
(env PAPER_SERVER_API_KEY),认证失败(服务端异常处理器压成
HTTP 400 + err_code)转带指引的 RuntimeError。这把低价值只读 key
是唯一刻意进 sandbox 的凭证:run_python env 过滤器加 allowlist
放行,docker 模式 _sandbox_env() 透传进容器(顺带修掉
PAPER_SERVER_URL 覆盖不进 docker 的缺口)。SKILL/RUN/PROGRESS
同步更新。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:15:51 +08:00
caoqianming
7533119039
docs: 精简 PROGRESS(865→188 行)与 DESIGN(797→218 行)(bump 0.39.1)
...
- PROGRESS 按自身契约压缩:每条收成一行(做了啥+关键判断),细节查 git log
- DESIGN 保留全部决策/取舍/硬协议清单/升级信号,砍实现复述;协议细节指向代码与 probe 脚本;§2 架构树补齐实际模块
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:52:12 +08:00
caoqianming
43775d531a
feat(skill): skill 定向模型——ppt 触发自动热切 GLM-5.2(bump 0.39.0)
...
内置 skill 可在 frontmatter 声明 model: <family.variant>(ppt → glm.pro52)。
单一执行点:load_skill 命中 → run 内热切 self.llm/self.caps(下一轮生效,
记账/压缩阈值自动跟上)+ 持久化 tasks 双列 + emit model_switch 事件;切失败
降级 warn 原模型继续。send 侧档外静默降级对定向模型豁免(免费档第二条消息
不被降回 flash)。跳档位门控(产品决策);只信内置 skill(用户 skill 的 model
字段忽略,防绕门控);不自动切回。"开关"= frontmatter 那一行本身(registry
每 run 现扫,删行即停,per-skill 粒度)—— 曾实现 app_settings 表 + admin
总开关 + 建 task 预切,架构评审后砍掉(第二事实源/第二执行点),详 DESIGN §3.5。
前端:直播流 model_switch 插模型分隔线 + 顶栏下拉同步(档外定向模型临时补
option 如实显示)。新增 tests/test_skill_model_pinning.py。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:57:32 +08:00
caoqianming
d24165a2fe
style(web): 列表状态灯挪到文件夹行左侧,数据行 space-between 均匀分布(bump 0.38.8)
...
终态徽章 + 运行圆点放进文件夹行行首(无文件夹行回落数据行,patch 逻辑同规则
找 host);底部数据行剩纯数据均匀铺开,时间自然落行尾。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:58:56 +08:00
caoqianming
259dde502d
fix(web): 列表 meta 行数字组靠左跟排——修 active 静默后的左侧缺口(bump 0.38.7)
...
active 徽章静默后,无 skill 行的 meta 左槽空置,条/tok 整组右挤留出一块
"缺了东西"的空白。数字组改靠左填槽,仅时间锚行尾;删无意义的 right-group。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:55:32 +08:00
caoqianming
2937b75143
style(web): status 徽章默认态静默——active 不挂徽章,终态行淡化(bump 0.38.6)
...
「进行中」徽章与运行圆点语义撞车,且列表主体都是 active,重复徽章是零信息
噪音。改为:active 不渲染徽章(列表 + chat-meta 同规则),completed/abandoned
保留徽章且整行淡化(st-* class,hover 恢复),脉冲圆点成为唯一动效信号。
删不再渲染的 .badge.active CSS。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:49:44 +08:00
caoqianming
7e6159af48
style(web): 运行态标识精简为纯脉冲圆点——文案收进 hover title(bump 0.38.5)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:43:58 +08:00
caoqianming
640bd0a1a3
feat(web): 后台 running task 自动挂 SSE——运行态标识刷新后也实时(bump 0.38.4)
...
loadTaskList 收尾 subscribeRunningRows:列表带出的 running/cancelling 行本地
未订阅的自动挂事件流(上限 4 条防同源连接占满),done/error 走现有收尾清标识 +
重拉列表,零轮询。ensureRunningTaskSubscribed 的 cancelling/workingDir 改由
调用方传 seed(后台 task 媒体 rel 解析要用各自 working_dir);后台订阅不再调
renderLiveRunIfVisible(避免重挂卡强制滚底误伤当前对话)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:41:26 +08:00
caoqianming
0ad7d08242
feat(web): 任务列表加运行态标识——多 task 并发时可见哪些在跑(bump 0.38.3)
...
列表行 run_status(后端本就返回,前端一直没用)渲成状态徽章旁的标识:
running 绿脉冲点/cancelling 橙/error 红点(hover 出 run_error)。取值叠加本地
liveRuns;run 开始与点停止时就地 patch 行 DOM(不重拉列表保分页),run 结束
沿用收尾 loadTaskList() 重拉。⋯ 菜单"清空对话"的 running 判断同源修正。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 16:25:59 +08:00
caoqianming
941554f9d7
feat(ppt): zongyuan_red 逆向重建为真实中国建材总院模板 + 主动提示(bump 0.38.2)
...
按官方 总院模板.pptx(中国建筑材料科学研究总院)把手搓的 zongyuan_red
重建为真实品牌模板:PowerPoint COM 渲真页 + 解 pptx 抽实测色/字/资产。
- 打包 logo.png(八边形字标,EMF→PNG)/ cover_bg.jpg(总部大楼灰度)/
ending_bg.jpg(材料马赛克);TIFF→压缩 JPG、EMF→透明 PNG
- 重写 5 页 SVG 忠实还原:封面(实景铺底+红块)/目录(红斜三角)/
章节(八边形水印,原件缺按 DNA 合成)/内容(灰底红顶条卡片+底部红条)/
尾页(材料创造美好世界+Thanks)
- 实测身份:主红 #D7000E、目录红 #D52C24、近黑 #181717、辅灰 #6F6F6F/#BCBDBD;
微软雅黑+Arial+方正兰亭黑
- 改写 design_spec.md;补登记 layouts_index.json(此前 dir 在但未注册)
- 质检 --template-mode 5 页零 error;finalize 内嵌 8 图 + 全量渲图逐页确认
主动提示:strategist.md §e + SKILL.md 默认主题段各补一条 —— 指向
中国建材总院·CNBM 系汇报(含职称评审)时策略阶段主动把 zongyuan_red
整套模板作为候选点名给用户,点头再按明确路径套入;唯一鼓励主动提模板的
场景,其余仍等明确路径。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 15:18:06 +08:00
caoqianming
dc721ba8a3
fix(web): 进度 dock 展开遮挡最新内容——task_progress 后补触底(bump 0.38.1)
...
#task-progress-dock 是 #chat-stream 上方的 flex 兄弟(flex-shrink:0),dock 一涨高就
从顶部挤掉 chat-stream 的可视高度,scrollTop 据置不变 → 原本贴底的最新内容被推到视口
折线以下看不见。直播态 task_progress 事件重渲 dock(=涨高)后早 return,跳过了末尾的
贴底兜底,故底部不自动回滚。修:在 task_progress 分支重渲 dock 后补一句
if (nearBottom) stream.scrollTop = stream.scrollHeight(与其余事件分支同款)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:59:34 +08:00
caoqianming
346930449a
feat(ppt): 反纯文字页+图表落地硬门(7aa49195 二代陶瓷 deck 复盘,bump 0.38.0)
...
0.37 网格锁生效后复评仍存两盲区:两栏裸文字页 x4(指纹看不见)、
全本零数据图表;另有内容被页脚裁掉、CJK 文字叠压两硬缺陷。修五处:
- 指纹加 text-columns 原型(0 卡片+<=3 图标+<=2 图形基元+左对齐文本
聚 >=2 列),裸文字页进单调门,4 页同指纹 error
- spec 指派图表落空检测:page_charts 指派了图表但该页 <3 图形基元
且 <4 卡片 -> error;executor 硬规则"不许把指派图表降级为文字"
- CJK 叠压升级:两 run 均 >=70% CJK 且互叠 >=50% -> error
(表意字宽 1.0em 估宽近精确,其余情形保持 warning)
- layout_grid 加可选 content_bottom,正文 baseline 越过 -> error;
executor 加"写页前垂直空间预算"纪律
- 策略层数据图表下限:素材含 >=3 组可比数值 -> 全本至少 1-2 页
真数据图表,零图表需在 spec 写理由
测试 +9(30 项)全过,全量 162 过;charts/decks 模板回归零新增噪音。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:34:51 +08:00
caoqianming
d30f6089bb
fix(web): 直播流式文字按轮次分段——修工具刷屏时文字被推出视口(bump 0.37.2)
...
一次 run 把整段(含几十轮 LLM)塞进一张 assistant 卡:文字全累顶部单块、
工具卡全追加其下,工具多时文字被越推越高滚出视口看不到。根因是直播态(单卡合并)
与历史态(每轮 LLM 一条独立消息、天然穿插)结构不一致。
方案 A(只动 chat.js live-run 路径,历史渲染不动):文字按轮次分段——
ensureTextSeg/closeTextSeg 维护当前打开的文字段,每个可见工具/选项卡(非隐形
task_progress)先关掉当前段(空占位段移除、有内容段定稿去光标+高亮),之后新文字
在卡片底部另起新段。流式文字始终在底部可见,且与历史结构一致,run 结束 reload 无跳变。
rAF 节流改闭包捕获 seg 防错渲;ctx.body/ctx.pending 单块模型换成 ctx.curSeg。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:22:15 +08:00
caoqianming
6f27b7cc5a
fix(seedream): size 面积钳制——修 1920x1080 被 ARK 400 打回(bump 0.37.1)
...
模型自选 16:9 出图(1920x1080=2.07M px)触发 ARK 硬门
`image size must be at least 3686400 pixels`(=1920²,卡总面积非单边),
整次文生图 400 失败。
- tools/seedream.py: 新增 _normalize_size(),出图前把 size 钳进
[min_pixels, max_pixels]:面积不足按 sqrt(min/area) 等比放大、
取整到 8 的倍数并复核达标(1920x1080→2560x1440);超上限等比缩小;
已合规原样透传(向后兼容)。归一化时返回串附 [note]、meta 记
requested_size,记账按真实出图尺寸。
- config/media/doubao.yaml: seedream_5 加 min_pixels/max_pixels
(旧 yaml 缺键=不设该侧,行为不变)。
- bump 0.37.0→0.37.1;PROGRESS 加一条。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 12:41:46 +08:00
caoqianming
0e02cff6c6
feat(ppt): 对齐网格锁+错位/单调质检(d1285247 陶瓷 deck 复盘,bump 0.37.0)
...
复盘 25 页陶瓷 deck 三类缺陷:跨页左基线漂移+并排块顶差 2-12px 的
"想对齐没对齐"、5 页同为图标卡网格的单调、标题语义不兑现(架构画成
横条列表)。修四层:
- spec_lock 新增 layout_grid 锁段(margin_x/content_top/footer_y/gutter),
strategist 派生、executor 每页吸附、checker 强制
- executor-base §3 网格对齐纪律(同 top 同高等 gutter、打破网格 >=16px、
同行文字 >=0.3em 禁贴字)
- svg_quality_checker 新增 check 14:兄弟卡片近失对齐 2-12px error
(底对齐/中心对齐/chart-plot-area 内数据柱三类豁免,71 charts 回归
误报清零)、layout_grid 偏离 2-15px error、gap 不等 warning、无锁
项目跨页左缘聚类漂移 warning、版式指纹单调门(>=3 同指纹 warn、
>=4 或过半 error;仅对 NN_ 编号 deck 页聚合)
- 策略纪律:同一版式原型整本 <=2 次 + 标题语义必须被图形兑现
顺手修 comparison_columns 模板胶囊 5px 错位。
新增 tests/test_svg_alignment_check.py 21 项;全量 153 过。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 12:16:42 +08:00
caoqianming
a89c7386fd
fix(web): 进度条自愈——回放层强制单调完成(d1285247 复盘,bump 0.36.2)
...
task_progress 回放非渲染 bug:模型跳步推进时漏给上一步补 completed,
导致"下面绿勾、上面红圈"。progress.js 加 enforceMonotonicProgress:
某步 completed 则其之前所有步自动 completed,set_plan/update_step 出口
各过一遍,漏发自愈。前端单测 +3(含复刻 d1285247 跳步序列→6/6)。
诊断脚本 scripts/diag_progress_d1285247.py。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 11:16:34 +08:00
caoqianming
fcc158dff6
fix(ppt): 门体系二轮硬化——逃生口收紧+导出自动质检+svg_final 嵌图修复(bump 0.36.1)
...
0.36.0 重跑复盘:门都触发了,但弱模型 8 秒内连按 --allow-iconless +
--allow-unreviewed 绕过,质检/渲图验收仍 0 调用,4/25 页错位漏出。修五处:
- A 验收门分层:"从没渲过/渲后又改/finalize 前渲的"= 硬问题,任何 CLI
flag 不豁免;--allow-unreviewed 只豁免"渲过但没标 pass";运维兜底走
ZCBOT_PPT_FORCE_EXPORT=1 环境变量(不进 --help/SKILL)
- B 拔 -s final 雷:图标门永远对 svg_output 源检测(消除 svg_final 展开
后误报"零图标"),wrapper docstring 老示例删除
- C 导出自动质检门:svg_to_pptx 导出前内嵌复跑 quality checker 逐页硬
错误,error 拒绝导出、无豁免参数
- E 几何质检加"文字骑卡片边缘"检测(warn 带坐标,P12/P14/P18 类命中)
- F 修 svg_final 嵌图失效:copytree 后 ../images/ 解析必落空,所有 deck
的 svg_final 一直嵌不进外链图(验收 PNG 图片为空);resolve 加 rebase
回 svg_output 兜底
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 08:58:49 +08:00
caoqianming
3c712031d5
feat(ppt): 渲图验收闭环+导出验收硬门+几何质检(139a59c5 错位复盘,bump 0.36.0)
...
复盘 25 页 deck 错位交付:阶段六全量渲图验收被整个跳过(svg_preview 0 调用,
进度步骤只跑了 echo),图标 regex 盲插压字、大字压说明、目录溢出页底全部漏出。
文档要求过但无机制强制,三层补齐:
- A 机制:svg_preview 渲图登记 .build/acceptance.json(源 sha1+verdict);
新增 accept_pages.py 标 pass/fail(校验渲过+源未改);svg_to_pptx 导出
边界加验收硬门(每页 pass 且 sha1 未变,--allow-unreviewed 逃生)
- B 提前拦截:svg_quality_checker 新增几何检测(估宽包围盒):图标压字/
基线出画布=ERROR,文字重叠=WARN 带坐标(密排设计误伤权衡,判断交渲图
验收);tspan 按视觉行归组续排,71 charts 模板 0 error 误报
- C 文档:SKILL.md 管线改"后处理→渲图验收→导出",反模式加"没看 PNG 就
--pass-all""为消警告批量盲插元素";SKILL_LIST 同步
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 13:37:59 +08:00
caoqianming
d79c28de06
fix(ppt): 禁自搓 SVG→PPTX 导出器硬约束(966041e5 复盘,bump 0.35.1)
...
复盘 陶瓷资源节点建设方案 (3).pptx:25 页全是整页 PNG 贴图、零原生
文本/形状。根因是模型整条绕开官方管线(svg_quality_checker/finalize_svg/
svg_to_pptx/svg_preview/total_md_split 调用次数全 0),自搓 cairosvg
export_pptx.py 逐页光栅化贴图,连带图标空方框、外链配图丢失、文字溢出。
上一条(0.34.7)硬化的是官方工具内部的门,只在模型用官方工具时生效;
本次证明模型可完全另起平行管线,内部门无从触发。改动仅在文档层:
- SKILL.md 阶段五:加「导出唯一入口=官方 svg_to_pptx.py,默认原生可编辑、
纯 Python 无需外部渲染器,渲染器没装不是自搓借口」
- SKILL.md 反模式:加「绕开官方管线自搓导出器 → 不可编辑贴图、价值作废」
不改线上跑法/官方脚本行为。残留风险(平台层自动检测整页贴图)按用户
选择暂缓,已记入 PROGRESS。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 09:25:14 +08:00
caoqianming
e46eb01766
feat(shortcuts): 加快捷指令(触发词→完整指令,入口层确定性展开)(bump 0.35.0)
...
预定义"简报 → 给我输出一份昨日的 AI 新闻简报",任意入口整条打"简报"就展开执行。
关键设计:快捷指令 ≠ memory。memory 是注上下文给模型概率召回的软上下文;快捷词是
入口层、模型跑之前的确定性替换(命中即换、零歧义)。性能上 shortcuts.md 内容永不注
上下文,存再多条平时也是 0 token;触发时进上下文的就是那条完整指令本身。
- core/shortcuts.py(新):shortcuts.md(| 触发词 | 指令 | 两列表)解析 + expand()
整条 strip()+casefold() 精确匹配展开(与「新话题」魔法命令同风格,不部分匹配)
- web/app.py 两处共用同一 expand:渠道核心 _run_channel_conversation(微信/企业微信)
+ 网页 post_message,起 run 前展开,任意入口行为一致
- core/memory.py memory_block:加一行契约让模型可维护 shortcuts.md;内容不注上下文
- tests/test_shortcuts.py(新):解析 + 展开全覆盖
- DESIGN §3.7 加"快捷指令 ≠ memory"取舍段 + 文件树;PROGRESS 加条目
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:58:55 +08:00
caoqianming
c2d24b20b4
fix(ppt): 导出图标门升硬 + 修 svg_to_pptx CLI 退出码不传播 + 验收改全量(bump 0.34.7)
...
诊断 ppt生成2(966041e5)真实产出的两个缺陷——23 页零图标、多处错位——
根因不是缺 gate 而是 gate 被打穿:
- svg_to_pptx.py 只 main() 不 sys.exit(main()),main() 里所有 return 1
(图标门/无 SVG/坏路径)全被吞成退出 0(最致命)
- 导出侧图标检查按设计只软 WARN、照常产出
- 模型质检用 `| head` 截断,吞非零退出码 + 截掉打在最后的零图标 [ERROR]
- SKILL.md 验收本就只要求抽查 3 页,错位藏在没看的页里;差评也未阻断
改动:
- svg_to_pptx.py: sys.exit(main()) 传播退出码
- pptx_cli.py: 导出图标门从软 WARN 升为硬门(锁图标却全 deck 零
<use data-icon> → [ERROR] 退非零、不产出 pptx),加逃生口 --allow-iconless
- SKILL.md: 阶段六验收改「默认渲整本 + 逐页过目 + 差评即阻断返工」,
阶段四/五/反模式补「别用 | head 截断」「别只看几页」「差评必返工」
合成测试三例(默认拒 / --allow-iconless 放行 / 有图标正常)全过。
仅改 skill 侧,不改动线上跑法;导出门只兜「锁了图标却零引用」,正常 deck 不受影响。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:16:49 +08:00
caoqianming
641c7d58aa
fix(tools): look_at_image/seedream 接受容器 /workspace 绝对路径(bump 0.34.6)
...
docker backend 下系统提示告知主模型一切在 /workspace 下,模型自然产出
/workspace/<wd>/x 绝对路径,但 image_ref.resolve_in_root 不翻译该前缀,
报「图片找不到或越界」。加容器根前缀翻译(与 send_email 的 _resolve_user_file
一致),按字符串前缀判断而非 is_absolute()(Windows 上 /workspace 缺盘符不算
绝对);越界仍靠 relative_to(root) 兜住。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:07:44 +08:00
caoqianming
eb9ffd654f
feat(admin): 各用户用量表加「最近使用」列(bump 0.34.3)
...
后端 _user_usage_page 加全量(不随 range 筛选)相关子查询
max(created_at) → last_used_at;前端 renderUserUsage 加列,
fmtTimeAgo 显示 + 全时间戳 title,无用量显示「—」。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:28:34 +08:00
caoqianming
d8f71aa7b2
feat(ppt): 页数改为用户必须显式拍板的 gate(bump 0.34.2)
...
页数原先只给「常 8–15 页」区间又被打包进 a–h 批量确认,用户一句
笼统「OK」就整批过、模型自取区间中位数(~12)。改(纯文档):
- SKILL.md b 项 → 推一个具体数字 + 标为「独立拍板项」
- SKILL.md 新增「🔒 页数 gate」:没给/没显式认可具体张数必须单独
追问「就定 N 页?」拿到明确整数才写逐页大纲;唯一例外是用户明说
「页数你随意」时按推荐数走、仍在预览写出供否掉
- strategist.md §b 同步补 Non-defaultable gate 硬约束 + 例外
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:01:51 +08:00
caoqianming
4b1dce6df9
fix(web): 清空对话时同步清空右侧导航条(bump 0.34.1)
...
clearMessages 成功分支只 renderMessages([]),漏了重置 outline;
切 task 路径有 state.outline=[]; renderOutlineRail(),清空路径补齐。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:13:51 +08:00
caoqianming
5bde2445a0
refactor(ppt): 工作目录收进隐藏 .build/ + 反卡片映射 + svg_preview 兜底/gate(bump 0.34.0)
...
累积一批(承接 ppt生成2 验证 + 用户"缺图形/卡片阵太多/文件夹过多"反馈):
- 工作目录重构:<project_dir> 根原本把"持久源 / 交付物 / 可再生构建产物"混摊。
新增 project_utils.build_dir/svg_final_dir/preview_dir/backup_dir 单一事实源,
把 svg_final→.build/svg_final、preview→.build/preview、backup→.build/backup/latest
(只留最新,不再堆时间戳)。.build 是 dotfile → /v1/files 自动隐藏 → 用户可见面
收敛到 源(sources/images/svg_output/notes/两个 spec)+ 交付物(exports)。改动:
finalize_svg / svg_preview(_collect)/ pptx_discovery('final'→.build/svg_final)/
pptx_cli(backup 路径 + rmtree 清旧)+ SKILL 工作目录约定/命令。端到端实测:根目录
只剩 exports/+svg_output/,.build/ 三子目录就位,导出/预览/backup 全正常。
- 反卡片映射(治"大段大段卡片阵"):executor-base §page_rhythm 的 dense 行去掉
"card grid 是 baseline"的背书;加一段硬映射「先看内容关系再选图形」(系统→
hub_spoke/分层、流程→flow、层级→树/金字塔、循环→环、互依→mind_map、对比→象限、
≥3数据→图表),卡片阵封顶 ~1/3 页、连画两页网格下一关系页必须上示意图,指回 page_charts。
- svg_preview 加 cairosvg 兜底:find_browser 改返回 None 不抛错;无 chromium 时回退
cairosvg,渲前用 embed_icons 预展开 <use data-icon> 成真 path(避 INVALID_MATRIX);
修 --screenshot 相对路径静默失败(改绝对路径 + 暴露 chromium stderr)。
- 扁平 gate 计入 circle/polyline:svg_quality_checker 图形图元加 <circle>(node/venn/
timeline 是真图,修 21-circle roadmap 误判);文字密集 deck ≥60% 页无图形 → ERROR。
架构结论(svg 目录):svg_output(可编辑源)与 svg_final(自包含编译产物)是两态、不能
合并成一个文件,但只暴露一个——现 svg_output 可见、svg_final 进 .build。终态(下一议题)
干掉持久化 svg_final、finalize 内存化 + web 按需预览,牵涉 web 层,本次未做。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:12:57 +08:00
caoqianming
13835a315a
feat(ppt): 加商务红品牌预设 + 配图默认主动提议(bump 0.33.5)
...
用户两个需求:(1) 加一款红色主题;(2) 用户没给图时在需要处主动配图。
- 商务红品牌预设:新增 templates/brands/business-red/design_spec.md(同 anthropic
格式:#C00000 全色表 + primary-deep/gold/info/positive/alert/surface/border/muted
派生色 + 宋体标题/黑体正文字体栈(栈尾收预装字体)+ 实心图标偏好 + 政企口吻;无
logo,注明用文字 wordmark / 可后补)+ brands_index.json 加条目。红色承载在 brand
而非 visual-style(后者不带色)。同时把商务红设为 strategist §e 默认配色候选:中文
政企/集团/科研商务汇报默认列入 ≥3 候选(红金 #BF9B5F / 红蓝 #2B4C7E 二选一点缀,
纯红只压标题/关键数据)。SKILL §默认主题 + 八条对齐 h 行同步指向。
- 配图默认主动提议:strategist §h + SKILL h 行改——用户没给图时不再默认整本 A
(no images);封面/分节/概念/breathing/氛围页主动把 ai 配图作为候选提给用户(数据/
列表/流程页仍走图表→§VII,不配装饰图)。仍全程 gated:用户在 h 确认 + imagegen
自带成本门(提议免费,确认才花钱)。
附:scripts/config.py 的 INDUSTRY_COLORS 未移植(ppt-master 残留引用),strategist
文档表是实际依据,已直接在表里加商务红行。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 15:57:52 +08:00
caoqianming
4a6182a76a
fix(ppt): 修生成 PPT 缺图形(扁平 deck 质检 gate + 策略层视觉下限)(bump 0.33.4)
...
延续缺图标排查,统计最近 ppt生成 任务 24 页 SVG 的元素构成:<path>=0、
<image>=0,整本是 <text> 摞 <rect>(文字方块),零示意图/图表/配图。根因同
图标——71 个 charts/ 模板没用、content→版式映射形同虚设,且策略层把"Not every
page needs a chart"当跳过口子(spec_lock 实际 page_layouts: free design、无
page_charts 段),输出层又无 gate 拦扁平 deck。两层修(用户选定):
- A' 输出 gate(svg_quality_checker):统计每页图形图元 <path>/<polyline>/
<polygon>/<image>(rect/line 是版面脚手架不算);≥6 页且文字密集(avg <text>
≥10/页)却全 deck 0 图元 → deck 级 error 退非零(逼回执行重写);多数页无图元
→ INFO;<6 页豁免(不误伤极简/teaser)。实测:8 页文字方块→exit 1;任一页带
path→放行;4 页→豁免。
- B' 策略层视觉下限(strategist.md GATE):把 §633「Template Match」从纯建议升为
硬下限——内容 deck(≥6 页)每个能结构化的内容页必须分配视觉处理(page_charts
模板 / page_layouts 结构模板 / §VII 自绘示意图),spec_lock 不许 page_charts +
page_layouts 同时空着;给出 content→图形映射速查;明示下游 A' 会硬卡。同步改
SKILL §大纲映射纪律 + §阶段四质检清单 + spec_lock_reference page_charts 段。
诚实边界:prompt+gate 抬下限(逼别交全文字 deck),执行模型设计功力是上限;gate
守"零图形"底线而非"每页必图表",避免误伤极简风。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:37:28 +08:00
caoqianming
5d23ee682b
fix(ppt): 修生成 PPT 缺图标(图标管线四层断点)+ 沙箱 SVG 预览渲染(bump 0.33.3)
...
查真实用户两个「ppt生成」任务的 DB 执行轨迹:24 页 SVG 共 0 个 <use data-icon>。
根因是图标管线四环节无一强制图标落地——策略层(有时)锁图标,执行层不放、
质检层不拦、工具层还断着。四层一起修:
- B 工具断点:references/SKILL 23 处路径仍指向已不存在的 skills/ppt-master/
(zcbot 是 skills/ppt/)→ 模型 `ls .../icons/<lib>/|grep` 验名得空集 → 放弃图标;
且 strategist 强制用的 icon_sync.py 在 zcbot 根本没有(GATE 空转,正是某任务连
图标都没锁的原因)。修:全量改路径(保留上游署名)+ 新建 icon_sync.py(复用
embed_icons 解析,验名+拷进 project/icons,缺名非零退出)。
- A 质检兜底(硬门):svg_quality_checker 加图标校验——锁了 icons.library + 非空
inventory 但全 deck 0 图标 → deck 级 error 退非零(逼回执行重写);单页 0 图标 →
warning(封面/分节/breathing/尾页豁免)。
- C 执行强制:executor-base §4 + SKILL 执行纪律改为"内容页必须放 1–3 个 inventory
图标"(自由设计无模板可继承图标,只能逐页手写)。
- D 导出兜底(纵深):svg_to_pptx 导出前预扫,锁了 inventory 却 0 图标 → stderr 大声
[WARN](非致命,防跳过质检直接导出)。核实 native 转换器本就自己从图标库展开
<use data-icon>,故原设想的"finalize 硬前置"前提不成立,D 改成与 A 同源的导出层警告。
同版附带修 svg_preview.py 在沙箱里渲不出 SVG(报"未找到 Chrome / Edge"):移植自
ppt-master 的 find_browser() 只认 Windows chrome/msedge,不认镜像自带 /usr/bin/chromium
(给 mermaid 装的)→ 视觉验收这关在容器里全程失效。对齐 rendering/pdf.py 发现逻辑
(认 chromium/chromium-browser/google-chrome + $CHROMIUM 覆盖);render() 补容器必需的
--disable-dev-shm-usage + 临时 --user-data-dir;并修一个静默已久的 bug——--screenshot
传相对路径 chromium 写不出文件(原代码吞 stderr,看着和"没浏览器"一样),改传绝对路径
并暴露 chromium stderr。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 13:59:00 +08:00
caoqianming
001f9af96f
fix(vision): look_at_image 超时透明重试 + 超时 60→120s(bump 0.33.2)
...
Seed 2.0 Lite 非流式,长 OCR 首字节可能逼近 60s read timeout → 偶发超时;
且返 [Error] 会触发主模型重发整个 tool call(图 base64 重传、输入 token 再付一次)。
- core/ark_client: 新增 ArkTimeoutError(ArkError) 子类,仅超时/网络抖动抛它;
HTTP 4xx/5xx 业务错误仍抛普通 ArkError 不重试。子类仍是 ArkError,seedream 等
现有 except ArkError 不受影响。
- tools/look_at_image: 对 ArkTimeoutError 退避重试(timeout_retries 默认 1 次,
2^n s),tool 内消化掉不抛给主模型,避免重传图烧 token。
- config/media/doubao.yaml: vision request_timeout_s 60→120,新增 timeout_retries。
smoke_look_at_image 通过(OCR 命中 + 记账正确)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:02:40 +08:00
caoqianming
ff276eb9b3
fix(web): SVG 预览强制 image/svg+xml(前端 blob mime + 后端 download)(bump 0.33.1)
...
SVG 在 <img> 里必须 Content-Type=image/svg+xml 才渲染。前端 preview.js 的
_showImage / mini 图片分支据扩展名强制 blob mime;后端 download 接口对 .svg
显式回 image/svg+xml(部分部署环境 mimetypes 未注册 svg → FileResponse 会猜成
octet-stream → 不显示)。双保险。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:28:11 +08:00
caoqianming
e3a432dcdd
feat(ppt): skill 重构为 SVG-first(移植 ppt-master,弃 python-pptx 版式件)(bump 0.33.0)
...
旧 python-pptx 固定组合版式件是版面单调/AI 味的架构天花板。改为 SVG-first:
AI 逐页手写 SVG 设计稿 → 纯 Python 转换器逐元素译成原生可编辑 DrawingML。
- 搬引擎:svg_to_pptx/ 转换器 + finalize_svg/svg_finalize + svg_quality_checker + total_md_split + update_spec(依赖闭包干净,只需 python-pptx)
- 搬知识:references(shared-standards/executor-base/strategist/image-layout-*/canvas-formats)+ 5 叙事骨架 + 19 视觉风格
- 搬模板:templates(layouts/decks/brands/charts + 图标库 1.1w+ + spec 骨架)
- 换 GUI:浏览器 Confirm UI → 聊天 BLOCKING 八条确认;live preview → svg_preview.py(无头 Chrome 渲 SVG→PNG);配图走 zcbot imagegen skill
- 默认主题改自由设计(商务红降为候选之一)
- 修 Windows GBK 控制台 UnicodeEncodeError:6 个入口脚本加 sys.stdout.reconfigure(utf-8) shim
- 端到端验证通过:4 页材料领域 deck,质检 0 error → finalize 嵌图标 → 导出原生 pptx → 渲图肉眼验收(swiss-minimal 设计级,非 AI 味)
移植自 github.com/hugohe3/ppt-master (MIT),适配 zcbot task_dir/聊天确认/imagegen 工作流。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 16:38:58 +08:00
caoqianming
d4aa5ccbec
docs(prompt): system prompt 加通用 context 纪律,堵大块输出滚雪球(bump 0.32.5)
...
反复 dump 全文 abstract 烧 2.5M token 不是 brief 专属——任何 skill 让弱模型
处理一批长文本都可能踩。在 system prompt 单一事实源 general_v1.md「工作原则」
段、紧挨「少来回」加一条全局铁律:大段 run_python/shell 输出会进对话历史每轮
重发,中间数据落文件、只 read 用得上的片段、别整批重复打印,否则烧 token 还
可能撑爆窗口/拖到超时被掐断。
与既有规则互补:行7(源码落 .py)管代码、行42(少来回)管轮数、本条管"大块
数据输出"。brief skill 0.32.3 的场景化版本保留做细化。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 14:56:44 +08:00
caoqianming
b5d75d2a7b
feat(scheduler): 定时任务默认单次超时 0→1800s(bump 0.32.4)
...
超时此前默认 0(不限),配合"超时被吞成 ok"的旧 bug,跑飞的 job 能无限拖。
改默认有限值 1800s(30min):新建 job 不指定 timeout_seconds 时给 1800,
显式 0 仍保留为"不限"逃生口。
- 单一事实源 core/scheduler.DEFAULT_TIMEOUT_SECONDS=1800;create_job 与
tools/schedule.py(agent 建 job 的工具)默认都引它,JSON schema 描述同步。
- create_job 里 int(timeout_seconds or 0) 保留显式 0=不限语义。
- 存量:线上 job e621c8a6「每日水泥科研简报」timeout 600→1800(直接 SQL,
未动其它 job)。
- RUN 故障兜底行同步默认值。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 14:52:09 +08:00
caoqianming
700176a0c6
docs(brief): 加 context 纪律,堵反复 dump 全文 abstract 烧 token(bump 0.32.3)
...
承接定时任务超时复盘:同一 job 的 agent 把 38 篇全文英文 abstract 用
run_python/print 反复灌进上下文(≥3 次),工具输出每轮重发 → 48 次 LLM
调用累计输入 2.5M tokens(输出仅 28K),既慢又贵还顶满 600s 超时。根因
brief skill 虽要求证据落 evidence.md 文件,却没明令"别反复 print 进上下文"。
skills/brief/SKILL.md 三处加指示文:
- 阶段二「context 纪律」:落文件、按需 read、别整批重打
- 阶段三:一次成稿别重复 dump + 论文多时按期刊分批 write
- 反模式加一条:反复 print 全文 abstract 让 context 滚雪球
纯指示文,frontmatter/description 不变 → SKILL_LIST 无需更新。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 14:38:32 +08:00
caoqianming
1646205364
fix(scheduler): 定时任务超时被掐断时记 error 而非误吞成 ok(bump 0.32.2)
...
实测 bug:isolated 定时 job 跑满 timeout_seconds 被协作式 cancel 后,
_run_agent_bg 对 ok/cancelled 都把 run_status 收回 idle(DB 不可区分),
而 _execute_scheduled_job 收尾只判 run_status=="error",于是超时中断被落成
last_status="ok" —— 掩盖"跑到一半没写 sections/没推送",且不计连续失败、
不触发兜底。复盘 job e621c8a6「每日水泥科研简报」:timeout=600s,task
创建→last_run_at 正好 600.0s,agent 停在"按期刊打印 38 篇摘要"(还在取数)。
修:超时分支置 timed_out 标志,run 收尾后若 timed_out → record_result(
status="error", 半成品不投递 notify)并直接返回。复用既有 error 语义(计入
consecutive_failures、到阈值自动停用、前端 crons 显示「上次失败」)。不动
_run_agent_bg 的 idle-on-cancel 共享语义(HTTP cancel/drain 也依赖)。
配套:PROGRESS/RUN 故障兜底各加一条;诊断脚本 scripts/diag_sched_e621.py
(dump 输出 scripts/_*.txt 入 gitignore)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 14:35:40 +08:00
caoqianming
89062d99b3
docs(design): 新增 §8.8 channel 长会话上下文治理(Phase 1 ✅ / Phase 2-3 design)(bump 0.32.1)
...
记 channel 常驻会话上下文软重置的设计:根因、业界对照(OpenClaw/Hermes/
Claude Code)、「边界而非删除」心智、Phase 1 已落地(context_base_idx 软重置
+ gap 自动分段 + 新话题命令 + 否决的替代方案)、Phase 2(阈值结构化摘要,
对齐 Hermes 阶段③)/ Phase 3(sqlite-vec/FTS5 持久检索)design。
回链修 §8.7、§8.2 两处引用。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 10:47:02 +08:00
caoqianming
b27cc9cd5b
feat: channel 长会话上下文软重置(gap 自动分段 + 新话题命令)(bump 0.32.0)
...
微信/企业微信常驻会话不再无限膨胀。tasks 加 context_base_idx,
Session.load 只把 idx>=base 的消息喂模型,base 之前历史全留 DB
(网页端照旧翻完整记录,一条不删)。
- 自动 gap 分段:入站距上次消息超 channel.session_gap_hours(默 6h)
→ 软重置,base=最后一条 user 消息 idx(保留上一轮做续聊锚点)
- 手动新话题:发「新话题/新会话//new/清空上下文」→ 硬重置 base=总数
- clear_messages 全删后归零 base;_db_idx 取真实总数避免 append 撞 idx
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 10:05:07 +08:00
caoqianming
e49ff641f9
feat(web): 消息框支持拖拽文件 + 修多次粘贴互相顶掉(bump 0.31.3)
...
附件 chip 拆出独立托盘 #chat-attach,与状态文字解耦:append+按 rel 去重,
上传进度只写 #chat-hint,不再互相覆盖。整个 #chat-form 加 dragenter/over/
leave/drop(计数防闪烁,只认文件拖拽,微信镜像只读不接收),复用 uploadFiles。
takePastedRels/删除/预览改查托盘;切 task 清残留未发送 chip。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 14:38:58 +08:00
caoqianming
d235cb7564
fix(web): 消息目录圆点错位再修(点击竞态 + 触底兜底)(bump 0.31.2)
...
- 点圆点不变红/点#1跳到#2:scrollIntoView 平滑滚动途中的 scroll 事件
抢走显式点选 → 加 _outlineJumpLock,跳转期间不重算,700ms 兜底解锁
- 点最后一个/滚到底倒数第二个变红:末项永远顶不到顶线(容器先到底)
→ updateActiveOutlineDot 加触底分支,判最后一个已加载轮为当前
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 14:27:50 +08:00
caoqianming
1352f092a3
feat(web): admin 近7天用量表加合计行(bump 0.31.1)
...
renderByDay 在 by_day_7d 表底加 tfoot 合计行,汇总 7 天
cost_cny/tokens_in/tokens_out;无数据时不渲染。后端无改动。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 14:21:40 +08:00
caoqianming
b4808b0370
feat: per-account 模型访问控制(档位制,复用 plan 列)(bump 0.31.0)
...
- core/model_access.py(新):档位制访问控制。users.plan 存档位名,
「档位→模型集合」配在 config/agent.yaml model_tiers;plan 空/未知→default 档,
role=admin 全开。无需 migration(plan 列 0001 起就有,之前休眠)。
- 两档:default(deepseek+local+seedream+seedance)、pro(+doubao+glm)。
- web/app.py:三个 list 端点按档过滤(用户只看到本档模型);三个 resolve 加
user_id 门控 —— 显式选档外模型 403;老 task 下次发消息模型已不在档位内→
持久落回 deepseek_v4.flash;定时任务执行 grandfather 不门控。
- web/admin.py:GET /v1/admin/tiers + PATCH /v1/admin/users/{uid}/plan;
用户行补 plan 字段。
- web/static/js/admin.js:各用户用量表加「档位」列(内联下拉)+ 档位图例 + apiSend。
- DESIGN.md plan 列语义 / RUN.md model_tiers 配置说明。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 14:11:22 +08:00
caoqianming
8263382fd1
feat: 新增豆包 Seed 2.1(turbo/pro/evolving)+ GLM 5.2 文本模型档案(bump 0.30.0)
...
- config/models/doubao.yaml(新建):Seed 2.1 turbo/pro + 自进化 evolving,
走 Ark OpenAI 兼容端点(openai/ 前缀 + ARK_API_KEY,同 local.yaml 范式)
- config/models/glm.yaml:加 pro52(GLM 5.2,zai/glm-5.2,1M 上下文),与 glm.pro(5.1)并存
- thinking_mode 均 false(深度思考走 body 协议,非 reasoning_effort 等级,留 TODO)
- 单价按火山/智谱 2026-06 发布价;evolving 单价未公布暂按 pro 估值兜底
- RUN.md 更新 ARK_API_KEY 说明(文本+图像+视频三处共用)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:05:07 +08:00
caoqianming
d633949a66
feat(web): 定时任务执行历史列表(右栏 Tab + 分页)(bump 0.29.0)
...
isolated 模式每次触发新建 task,旧的带 scheduled_job_id 被普通列表过滤、
UI 够不到,原来只有「打开它跑的任务」单按钮指向 last_task_id(最近一次)。
- 后端新增 GET /v1/schedules/{job_id}/tasks?page=&page_size=:按 scheduled_job_id
归属 + user_id 隔离,created_at desc 分页,复用 _task_dict,标准分页壳返回。
- 前端定时弹框右栏改 Tab(详情 / 执行记录),动作按钮提到顶部 head;
执行记录是分页列表,点某条打开那次对话。await 后重查 #cr-hist 防切换串显。
- 决策(与用户对齐):历史全部保留不剪枝;布局选 Tab 而非三栏。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:53:56 +08:00
caoqianming
a6d00b24ff
feat(web): 渠道卡片收拢绑定管理 + 删 rail 按钮 + bump 0.28.1
...
把渠道绑定/对话/管理全部收进「新建任务」下方的卡片,删掉左下角
rail「微信」按钮(精简页面)。后端 /v1/channel_tasks 返回
{ wechat: { bound, task }, wecom: { bound, task } },前端渲染三种卡片:
未绑定(点绑定)/已绑定无对话(占位)/已绑定有对话(点进+⚙管理)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:19:45 +08:00
caoqianming
e66fdd0ffc
feat: 定时任务对话归属 + push 统一记录到渠道对话(bump 0.28.0)
...
问题1:定时任务产生的 task(isolated 每次新建)混进普通对话列表。
- tasks 加 scheduled_job_id(nullable FK→scheduled_jobs,migration 0017 + backfill
persistent/isolated);列表 WHERE scheduled_job_id IS NULL 排除(+working_dir LIKE 兜底)
- ensure_local_task_row 加参数,_execute_scheduled_job 建任务时填
- mode 语义澄清:只管对话是否延续,文件夹两种模式都按 job 复用
问题2:任何 push(定时 deliver_notify / agent wechat_push 工具)推到微信渠道,
web 端渠道对话看不到、没法基于推送追问。
- 记录下沉到 send_to_user(两调用方统一入口):投递成功后对每个成功渠道
ensure_channel_chat_task(不存在自动建,与入站对话共用)+ 写 assistant 消息
(摘要+文件下载链接+../rel read 路径)
- Unified 进 agent 上下文(基于推送追问);source_task_id 去重(chat task 内调
wechat_push 时不重复插摘要);不塞正文,agent 按需 read 产物文件
- _run_channel_conversation 复用 ensure_channel_chat_task,消除建 task 重复逻辑
messages.kind 列(migration 0018):push 记录标 kind="push"(独立列不进 payload),
extract_last_assistant_text 加 WHERE kind IS NULL 跳过,避免 wecom 入站取回复
误取 push 摘要当回复。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-26 10:51:06 +08:00
caoqianming
133e350428
style(web): 渠道卡片改并排省纵向空间 + bump 0.27.4
...
接 0.27.3:两张渠道镜像对话卡片(微信/企业微信)从竖排改并排
(#channel-cards flex row,各 flex:1);窄栏内图标左、名称 + 条数·时间
堆两行(新增 .cc-body 列容器)。绑定弹框(左下角「微信」rail 按钮)保留
不动 —— 它是绑定/解绑/测试推送唯一入口,与卡片职责互补不重复。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:39:25 +08:00
caoqianming
7dfdf4c73b
feat(web): 微信/企业微信对话改成左栏固定卡片 + 企业微信也只读 + bump 0.27.3
...
把渠道镜像对话(每用户每渠道唯一的常驻只读对话)从「任务列表置顶行 +
绿徽章 + 绿边」改成「新建任务下方两张固定卡片」,与可滚动任务列表分离、
常驻可见;顺带补企业微信对话的 web 端只读锁。
- 后端 /v1/tasks 用 coalesce(channel,'web').notin_(CHANNEL_MIRROR_KINDS)
排除渠道任务并删掉 case() 强制置顶;新增 GET /v1/channel_tasks 返回
{wechat, wecom} 摘要(复用 _task_dict,无则 null)
- 前端加 #channel-cards 卡片块(:empty 自动隐藏)+ loadChannelCards/
syncChannelCardActive;移除列表行已失效的绿徽章逻辑
- applyChannelComposerLock / sendMessage 守卫从硬编码 channel==='wechat'
改读 CHANNEL_BADGE,微信 + 企业微信都 readonly,提示文案按渠道动态
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:32:12 +08:00
caoqianming
474597cfc6
feat(wecom): 企业微信入站对话支持图片/文件附件(media/get 下载 + 复用渠道无关核心)+ bump 0.27.2
...
接续 0.27.0 企业微信入站(此前只收文本)。
- wecom.download_media(media_id):走 media/get,成功回二进制流 + Content-Disposition
文件名,出错回 JSON errcode(40014/42001 重取 token);_filename_from_disposition 解
filename / filename* 两种形式。
- 回调按 MsgType 分支:image/file 下载后构造 InboundAttachment(kind/file_name/data,与
个人微信同结构)→ 喂同一 _run_channel_conversation,复用其落盘 + 拼 [用户上传的...] 行
(图片 agent 自调 look_at_image,文件走 Read)。纯图片/文件消息无文本时据附件行生成 text。
- 语音/视频/位置/链接/事件暂回 success 不处理;附件下载失败静默跳过(打日志)。
- dev.html「企业微信(仅推送)」文案纠正为「推送 + 对话」。
文件:core/wechat/wecom.py、web/app.py、web/static/dev.html。_filename_from_disposition
+ import 自测过。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:17:44 +08:00
caoqianming
d1aa2b12e2
fix(wecom): wechat_push 支持按渠道定向投递,修「点名企微仍推到个微」+ bump 0.27.1
...
用户说"推送给我的企业微信",消息却同时进了个人微信。根因:send_to_user
是无差别广播(for ch in active_channels() 逐个推),且 wechat_push 工具
没有指定渠道的参数 —— 部署同开 clawbot+wecom 时一条推送两边都到。
- send_to_user 加 channel=None:None 保持广播(定时任务/不点名沿用,向后
兼容);指定 wecom/clawbot 时只投那一条,该渠道未开返回单条 no_binding,
不静默回退到别的渠道。
- WechatPushTool 加可选 channel(enum wecom/clawbot)+ 描述教 agent
「用户点名某微信就传对应 channel」,execute 做渠道白名单校验。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:15:36 +08:00
caoqianming
d16297e556
feat(wecom): 企业微信支持入站对话(回调 webhook + AES 解密 + 复用渠道无关对话核心)+ bump 0.27.0
...
入站方式与 ClawBot 本质不同:ClawBot 走长轮询(getupdates + 常驻 run_inbound_manager),
企业微信走回调 webhook(企微服务器主动 POST 加密 XML)→ 无需后台轮询 task,只加 HTTP 端点。
agent 跑 >5s 超被动同步窗口 → 回复走 message/send 主动推回(复用 push_wecom),被动回 success 防重试。
- 抽 _run_wechat_message 为模块级 _run_channel_conversation(app, uid, text, atts, channel):
个人微信(wechat)与企业微信(wecom)同核心、各一张会话 task(企微 binding 也存 chat_task_id)。
- 新增 core/wechat/wecom_crypto.py:WXBizMsgCrypt 等价(SHA1 验签 + AES-256-CBC 解密 + corpid 校验);
与 crypto.py 的 Fernet 列加密、wecom.py 出站 API 全无关。
- service.py:get_user_by_wecom_userid 回调反查身份 + get/set_wecom_chat_task;
upsert_wecom_binding 改成合并 config(不再覆盖 chat_task_id)。
- web/app.py:GET/POST /v1/wecom/callback(无 JWT,身份从加密 XML FromUserName 反查)。
- env:WECOM_CALLBACK_TOKEN / WECOM_CALLBACK_AESKEY;暂只收文本,未绑定/空消息静默。
- 文档:PROGRESS/RUN/DESIGN/wecom 同步(DESIGN 把「只做推送不做对话」旧决策标为演进)。
crypto round-trip 自测过;create_app + 路由注册 + 全量 import 通过。端到端待企微后台配回调 URL(需公网 HTTPS)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:07:47 +08:00
caoqianming
5d3cd88e2c
fix(wecom): 扫码绑定改用扫码授权登录端点,修复「请在企业微信客户端打开链接」+ bump 0.26.10
...
oauth_authorize_url 原用 open.weixin.qq.com/connect/oauth2/authorize(网页授权,
只能在企业微信客户端内打开),桌面浏览器 window.open 它 → 企业微信报「请在企业微信
客户端打开链接」,扫不了码。
改用扫码授权登录端点 login.work.weixin.qq.com/wwlogin/sso/login(login_type=CorpApp),
桌面浏览器渲染二维码,企业微信 App 扫码确认后回跳带 code,verify_state / get_user_id
逻辑不变。前置:redirect_uri 域名须配在应用「企业微信授权登录」可信域名(另一项设置)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:21:30 +08:00
caoqianming
8ab1805df4
fix(wechat): wechat_push 工具漏挂企业微信 + 提取 active_channels 单一真相源 + bump 0.26.9
...
根因:wechat_push_available() 只看 clawbot_enabled(),没算企业微信。线上若只开
企微渠道(ClawBot 开关没开)→ 工具压根不注册到 agent → zcbot 照实回"没有直接
发企业微信的工具",用户已绑企微仍推不出。底层 send_to_user 早支持 push_wecom,
纯属注册门槛漏判。
修:提取 service.active_channels() 作渠道清单唯一真相源,门槛(wechat_push_available)
与投递(send_to_user)都引它,加渠道只改一处,根除"两处各列各的"这类偏差。
工具描述把 ~24h 窗口注明为 ClawBot-only(企业微信无窗口约束)。
纯内部重构,对外契约不变;test_secret_host_tools 8/8 过。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:09:21 +08:00
caoqianming
23c5ab20e0
feat(web): main.py web 支持 --ssl-certfile/--ssl-keyfile(uvicorn 原生 TLS,免 nginx)+ bump 0.26.8
...
两者同时给即在本端口跑 HTTPS,只给其一报错;都不给=明文(向后兼容)。
适配「只有 8765 对外」场景:zcbot 直接在 8765 上 HTTPS,不用 nginx/不挪端口。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:43:18 +08:00
caoqianming
0cf6e3e61e
chore(wecom): 加企业微信可信域名校验文件 WW_verify_THssshZfneJwIG5Y.txt(放 repo 根)+ bump 0.26.7
...
bot.ctc-zc.com 的可信域名归属校验;配合 /WW_verify_{token}.txt 路由在域名根 serve。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:25:06 +08:00
caoqianming
36964d9920
feat(wecom): 域名根 serve 企业微信可信域名校验文件 WW_verify_*.txt + bump 0.26.6
...
GET /WW_verify_{token}.txt 从 ZCBOT_WECOM_VERIFY_DIR(默 repo 根)读同名文件返回,
公开端点 + token isalnum 防穿越。解企业微信「网页授权可信域名」归属校验
(zcbot 根路径原是 302 跳 SPA,验证文件 404)。配好可信域名才能配可信IP(修推送 60020)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:22:34 +08:00