Commit Graph

71 Commits

Author SHA1 Message Date
caoqianming b77db25440 feat(origin): expand supported plot types 2026-08-14 12:16:37 +08:00
caoqianming 13ba6b1a61 feat(software): add local monitoring and multi-artifact jobs 2026-08-14 10:41:49 +08:00
caoqianming 51a213e3a7 fix(software): normalize job output artifacts 2026-08-14 08:57:36 +08:00
caoqianming 40f8899cc1 feat(software): require artifact inputs for Origin jobs 2026-08-13 17:25:21 +08:00
caoqianming 8aafe3272f feat(software): add professional software job center 2026-08-13 15:59:31 +08:00
caoqianming c4418711ab perf(vision): 收敛看图请求范围 2026-08-13 09:59:49 +08:00
caoqianming 2b4316ed25 refactor(external-systems)!: 统一为通用 API provider
移除 factory_mes preset 和兼容包装,新增存量 definition 一次性迁移脚本,并让 OpenAPI 参数直接遵循规格约束。

BREAKING CHANGE: 管理 API 不再接受 factory_mes provider,部署新版前必须先执行 definition 迁移脚本。
2026-08-10 17:26:39 +08:00
caoqianming 44b7df6f20 feat(external-systems): add generic MCP connector 2026-08-10 16:04:51 +08:00
caoqianming 9032c5b517 fix(external-systems): preserve credentials on definition updates 2026-08-07 11:25:29 +08:00
caoqianming 0e4bd0456a feat(external-systems): 重构连接治理与运行态缓存 2026-08-07 10:12:37 +08:00
caoqianming 7e544827c0 feat(files): toggle task hidden directories 2026-08-06 12:05:36 +08:00
caoqianming 41cd7858a1 feat(external-systems): add persistent result exports 2026-08-06 11:10:06 +08:00
caoqianming e81005435e fix(external-systems): preserve complete OpenAPI responses 2026-08-06 10:32:31 +08:00
caoqianming af0ad934bb feat(external-systems): guide MES aggregate queries 2026-08-05 15:01:43 +08:00
caoqianming cb9a39f33d feat(external-systems): add managed MES connections 2026-08-04 16:53:58 +08:00
caoqianming 137d27acdd fix(security): 强化工程类型检查与依赖审计 2026-08-03 16:59:32 +08:00
caoqianming dfda6fd772 feat(image): support GPT image editing 2026-08-03 14:31:38 +08:00
caoqianming 57655984dd feat(web): add task-scoped artifact references 2026-08-03 13:49:20 +08:00
caoqianming b0e0182b73 fix(agent): route PDF ingestion through markitdown 2026-08-03 09:43:57 +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 70683b7d09 增强知识库文件写入一致性 2026-07-27 16:37:56 +08:00
caoqianming ade3a847f6 Fix web search date handling 2026-07-27 12:50:07 +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 5a5ebfbe24 refactor(tools): 输出处理原语移出 base.py——base 只留 Tool 基类与路径边界
compact_tool_output / format_timeout_result 是「工具输出处理」关注点,与
Tool ABC / FileOutOfBounds 路径安全混装在 base.py(审查小项)。析出
tools/output.py,8 处引用(5 工具 + executor_docker + 2 测试)同步改。
318 测试全过。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:15:04 +08:00
caoqianming 8038fb7491 refactor(tools): 媒体工具同构析出 tools/media_common.py + 17 个单测
审查 P1#6:seedance/seedream/gpt_image/look_at_image/read_document 五处
同构(逐字或参数化后逐字)收敛为共享原语:

- quota_gate(每日配额闸,文案逐字保持)/ stamped_path(<ts>-<rand6> 落盘
  命名+建目录)/ write_meta(.meta.json)/ record_usage_safe(记账失败不
  阻塞)/ ark_chat_with_retry(超时透明重试,业务错误不重试)/
  extract_chat_answer(chat 文本+截断标志)/ find_first_url(递归找 URL,
  accept 谓词覆盖 seedance 的 video_url 优先语义)
- 五工具各自的 banner 格式 / body 组装 / seedance 轮询刻意保持不动
- tests/test_media_common.py:17 个纯函数/打桩单测(配额文案、重试次序、
  业务错误不重试、视频 URL 谓词等)

318 测试全过;真实配置冒烟 32 工具挂载不变、schema 完整。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:10:32 +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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 6008e1b8a0 fix(wechat,email): host-side 文件工具翻译容器路径,修复附件发不出 + bump 0.24.1
docker 模式下 fs 工具在容器跑,文件落宿主 users/<uid>/<wd>/,但 send_email /
wechat_push 是宿主进程工具:base_dir=cwd 且不识别容器↔宿主路径映射,agent 给的
相对路径拼到 cwd、容器绝对路径 /workspace/... 宿主上瞎解析,relative_to(user_root)
必越界 → 附件永远发不出(probe 直调 send_file 绕过解析,故"测试可发")。

- tools/base.py: 共享 _resolve_user_file(/workspace 前缀翻回 user_root + 相对拼
  base_dir + 越界校验)+ FileOutOfBounds
- agent_builder: 两个 host 工具 base_dir=working_dir_path(宿主 task 目录)而非 cwd
- send_email / wechat_bot: 改用 helper
- tests: 加 3 例回归(翻译+越界、send_email 容器路径、wechat_push 相对路径)
- scripts/diag_wechat_push.py: 诊断脚本

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:02:48 +08:00
caoqianming 320f428dd3 feat(email): 配置 foxmail SMTP 发信 + 发件人显示名品牌化 + bump 0.23.2
- .env 填入 smtp.qq.com:25/STARTTLS/授权码,send_email tool 与定时任务
  notify 兜底投递生效(.env 不入库)
- send_email.py 发件人显示名由硬编码 zcbot 改读 SMTP_FROM_NAME,默认
  「总院科研辅助智能体」,对外不暴露内部代号
- RUN.md 补 SMTP_FROM_NAME 说明;PROGRESS 记一条

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:31:17 +08:00
caoqianming 528b974d9f feat(wechat): ClawBot 个人微信接入第一期(后端 + 绑定页)+ 双渠道设计 §8.7 + bump 0.22.0
把 zcbot 送进用户个人微信:对话 + 主动推送(简报/结果)。选官方微信 ClawBot
(iLink Bot API,零封号)先行;企业微信作渠道 B 留接口。协议全程真机实测
(scripts/probe_clawbot*.py,本人微信号在灰度内)。

核心(后端 import/编译自测过):
- core/wechat/{ilink 协议客户端, crypto 凭据加密, service 绑定CRUD+24h窗口推送
  +send_to_user 渠道抽象, inbound 长轮询管理器+回复提取}
- WeChatBotBinding 模型 + migration 0012;tools/wechat_bot.py WechatPushTool
  + agent_builder 注册(有开关才挂)
- scheduler.deliver_notify 加 wechat 通道(未送达退邮件);web/app.py lifespan
  起入站管理器 + _run_wechat_message 回调 + 5 端点;web/static/wechat_bind.html 绑定页

实测要点:每条 sendmessage 必带唯一 client_id(漏则同 token 后续被丢);context_token
24h 可复用→主动推(需用户先开口);文件 getuploadurl→AES-128-ECB(PKCS7)→CDN
(URL 带 filekey)→file_item,docx/pdf 原生直推。

激活:db upgrade head(带 0012)+ env ZCBOT_WECHAT_BOT_ENABLED=1
+ ZCBOT_WECHAT_SECRET_KEY=<串>。待办:部署端到端联调、SPA 集成绑定 UI、企业微信渠道 B。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 08:59:56 +08:00
caoqianming 108351864e feat(scheduler): 定时任务 v1 — 对话建/管 + 守护循环执行 + 只读前端 (DESIGN §8.5)
到点把一句自然语言 prompt 喂进 agent 主管线,可跑 skill 出简报 / 发邮件 / 打招呼等。
job 本体 = cron+时区 + prompt + 会话模式;"发邮件"不是字段,是 agent 据 prompt 调
send_email 的动作 → 加任何能力不改 schema。

后端:
- scheduled_jobs 表 + migration 0011(独立加表,公测兼容)
- core/scheduler.py:croniter 算 next_run(时区+vixie OR 语义)、claim+advance 防重复触发、
  失败阈值自停、notify 兜底投递、CRUD 服务层(工具与 REST 共用不漂移)
- 守护循环 _scheduler_loop(lifespan,仿 _disk_scanner 的 plain-asyncio,不引 APScheduler/Celery;
  复用 _run_agent_bg,抢 run 锁、超时协作 cancel、并发上限)
- tools/send_email.py(host-side,SMTP_* 齐才挂)
- /v1/schedules GET/PATCH/DELETE 三端点

对话端 = 完整 CRUD:schedule_create/list/update/cancel 四工具(定时 run 内不挂防自我繁殖)。

前端 = 只读 + 停用/删除:左栏 rail「定时」入口 + crons.js 只读 master-detail modal
(复用 skills modal 范式);建/改故意只走对话,规避 cron 构建器 UX。

会话模式:isolated(默认,每次新建临时 task 省 token)/ persistent(绑 bound_task_id 续上下文)。
env:SMTP_* / ZCBOT_DISABLE_SCHEDULER / ZCBOT_SCHEDULER_TICK_SECONDS / ZCBOT_SCHEDULER_CONCURRENCY。

已验:migration 上库、CRUD 端到端、3 REST + 4 工具注册、crons.js 语法。
待验:起 web 进程跑一轮真实触发 + 邮件 smoke。bump 0.18.0 → 0.19.0。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:42:31 +08:00