Commit Graph

64 Commits

Author SHA1 Message Date
caoqianming 4ec2638d38 feat(web): embed interactive HTML artifacts 2026-08-03 11:34:35 +08:00
caoqianming f217672365 feat(web): render Mermaid diagrams in chat 2026-08-03 10:26:39 +08:00
caoqianming 8999fb1b77 fix(rendering): detect LibreOffice components 2026-08-03 09:27:30 +08:00
caoqianming 21a90cf201 fix(rendering): route PDF conversion through platform tools 2026-08-03 08:55:54 +08:00
caoqianming 75bb482015 feat(media): support GPT image output controls 2026-07-31 17:32:15 +08:00
caoqianming 59c238bd34 fix(media): use dedicated GPT image model 2026-07-31 17:01:46 +08:00
caoqianming 683bdf993d fix(web): restore conversation clearing 2026-07-31 16:37:39 +08:00
caoqianming 13e3e50371 fix(web): sort tasks by recent activity 2026-07-31 15:02:13 +08:00
caoqianming cd6a76cba9 fix(storage): 兼容消息中的 NUL 字符 2026-07-31 14:43:10 +08:00
caoqianming afdcf46577 feat(tasks): streamline task creation and embedded entry 2026-07-31 14:31:50 +08:00
caoqianming da265b88d5 fix(web): open embedded task working directory 2026-07-31 13:54:22 +08:00
caoqianming 3c022494f5 fix(web): retitle cleared conversations 2026-07-31 13:00:34 +08:00
caoqianming 5c80fb202b fix(web): hide lifecycle actions for channel chats 2026-07-31 11:30:50 +08:00
caoqianming 61648f1114 Add context menus to task and file lists 2026-07-29 16:35:20 +08:00
caoqianming bcdbdd4738 Streamline conversation activity UI 2026-07-29 15:59:00 +08:00
caoqianming 02b278b175 Refine mobile conversation layout 2026-07-29 15:13:51 +08:00
caoqianming 369c74483c Refine workspace reading and mobile layout 2026-07-29 14:59:10 +08:00
caoqianming 07eba5c402 Polish web workspace visual foundation 2026-07-29 14:34:46 +08:00
caoqianming ff3bc75fed 合并工作目录新建入口 2026-07-29 14:00:04 +08:00
caoqianming 03ee37e201 改进工作目录可搜索选择 2026-07-29 13:48:16 +08:00
caoqianming d75df5a4b4 优化新对话目录选择 2026-07-29 13:38:20 +08:00
caoqianming cfc1d17af7 优化工具失败观测与重复保护 2026-07-29 09:29:44 +08:00
caoqianming 6f2840a5d0 统一运行生命周期入口 2026-07-28 15:34:42 +08:00
caoqianming 7cfeebfa89 增强消息运行可靠性 2026-07-27 17:50:31 +08:00
caoqianming ade3a847f6 Fix web search date handling 2026-07-27 12:50:07 +08:00
caoqianming c3a6f7deee Add workspace-first new conversation flow 2026-07-27 12:28:56 +08:00
caoqianming 85c918dc2f Add safe conversational workspace rename 2026-07-27 11:23:20 +08:00
caoqianming 98713bb7fa feat(seedance): support image-to-video references 2026-07-27 10:50:13 +08:00
caoqianming e03a1ee7eb docs: bump 0.59.4 + CHANGELOG(kb 冷启动/查看原件/条目精简)+ PROGRESS
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:50:05 +08:00
caoqianming 0cd5b07124 feat(web): kb 下载入口(原件+解析 Markdown)+ bump 0.59.3
- kb.js:已入库条目行「⬇ 下载原件」、单篇页头「下载原件/下载
  Markdown」、待入库列表逐项下载;复用 media.js downloadFile 走现成
  /v1/files/download(.kb 在 user_root 下,safe_join 天然放行),零后端改动
- dev.html:新按钮三行 CSS(hover 淡入同删除按钮、待入库行间距)
- 弃选:kb 专属下载端点(与 files 下载重复,公测期白养对外契约)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:33:23 +08:00
caoqianming 8751f71f5a feat(web): 移动端对话附件入口(拍照/照片库/选文件)+ bump 0.59.2
chat 输入区此前仅粘贴/拖拽两条附件路,移动端双双不可用。加 [附件] 按钮:
触屏弹三项菜单(拍照 capture=environment / 照片库 accept=image,video /
选取文件无 accept),桌面端直开文件选择器;三入口 change 统一走既有
uploadAttachFiles 链路,只读镜像随 applyChannelComposerLock 禁用。
纯前端改动,无后端/schema 变化。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 14:49:16 +08:00
caoqianming c6232d8325 feat(kb): 个人知识库——.kb/ 纯文件机制+/v1/kb* API+前端 modal+注入契约(bump 0.59.0)
- core/kb.py: 状态/视图层(INDEX 单行格式 parse/format、已入库判据=INDEX 有条目、
  删单篇连带原件与索引行、kb_block 注入——有库才注)
- core/kb_ingest.py: 入库管线 markitdown→扫描件 OCR 兜底(方舟)→flash 摘要(失败降级
  不阻塞)→追加 INDEX;编排 to_thread+per-(user,库) 锁,幂等可续跑
- 0022 迁移: usage_events.task_id 放宽可 NULL(kb 入库无 task 上下文);
  记账溯源 kind="kb_ingest"/"vision" + units {"kb","source"}
- web/app.py: /v1/kb* 8 端点(列/建/删库、详情带进度、上传即入库、手动 ingest、看/删单篇),
  不设 HTTP 检索端点(agent 走 fs 工具)
- 前端: kb.js 两栏 modal(上传 XHR/进度轮询/删除确认);rail 左下新增知识库入口,
  四按钮改图标+小字两行布局
- agent_builder 在 memory_block 后注 kb_block(docker/host 路径换算同 .memory);
  documents skill「何时不用」补自建资料路由行
- DESIGN §3.8 机制小节 / CHANGELOG 0.59.0 / PROGRESS

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 15:20:43 +08:00
caoqianming a1db5756aa feat(tools): 扫描件 PDF 直读 read_document——方舟文档理解 base64 内联(bump 0.58.55)
markitdown 只抽文本层,扫描件(老标准/检测报告/红头指南)转出为空=死路。
探针(scripts/probe_ark_doc.py)验证方舟 chat file 内容块直读 PDF 后落地:

- tools/read_document.py:seed_2_lite 同 variant 同 key,记账走
  record_vision_usage;体积/页数双闸(30MB/100 页,pdfminer 软探页数)+
  finish_reason=length 截断提示;多页 OCR save_md 全文落盘只返 1500 字预览
  (防上下文爆),save_md 与读取侧同款三形态路径解析(相对/宿主绝对/容器
  /workspace)+ user_root 边界
- tools/image_ref.py:抽 load_pdf_as_data_url(复用 resolve_in_root;
  data:application/pdf;base64, 前缀为方舟硬校验)
- config/media/doubao.yaml:vision 段加 max_pdf_mb / max_pdf_pages /
  doc_request_timeout_s(probe 实测口径:~1300 token/页、100 页全覆盖、
  17MB 内联可用、单页栅格化 3600 万像素硬限)
- core/agent_builder.py:注册(跟 look_at_image 同 vision 段走)+ 系统提示
  _MEDIA_READDOC_SEG(何时调/何时不调,防对有文本层 PDF 重复花钱)
- 六 skill(paper/patent/standard/proposal/rebuttal/ppt)摄取段加扫描件
  兜底一行(markitdown 转出空/近空 → read_document OCR)
- 冒烟 scripts/smoke_read_document.py 全过(3 页 ¥0.0066,表格→md 表、
  公式→LaTeX,usage_events 记账验证)

选型对比(不接 MinerU 等外部解析 API=新增第三方数据面;不选本地 OCR=
过度投资;不选 file_url/file_id=免认证公网直链/TOS 依赖)见 DESIGN §8.13。
host 侧工具,无需重建沙箱镜像,pull + 重启 web 生效。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:57:23 +08:00
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 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 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 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 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 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 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 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 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