caoqianming
7b09c77f76
fix(sandbox): docker 下 system prompt / SKILL 文档统一用容器路径
...
docker backend 下 shell/run_python/fs 工具全在容器里跑(<workspace>/users/<uid>
bind 到 /workspace),但喂给 LLM 的路径多处仍是宿主绝对路径(容器内不存在),
agent 据此 find 全空、瞎转到 pwd 才发现真身在 /workspace/<wd>。
- core/agent_builder.py::_build_system_prompt:docker 下 task_dir + 「宪法」
glob 范例换成容器路径 /workspace/<wd>,去掉容器里无意义的 cwd 行(cwd 恒等
task_dir);host 不变。修法同 LoadSkillTool 的 container_skills_dir 改写。
- skills 文档同类宿主路径残留清扫:patent 跨 skill 调 proposal 脚本由
<repo_root>/...(硬编码 D:/projects/zcbot)改兄弟相对 <skill_dir>/../proposal/...;
research fetch_pdf 范例硬编码 D:/... 改 <task_dir> 占位;patent/proposal/ppt
的 <task_dir> 举例的废弃旧布局 workspace/tasks/<task_id> 改 host/docker 双形态说明。
- ppt 图标缓存写进只读挂载修复(方案 A):种子图标库 <skill_dir>/assets/icons/
降为只读(glob 读),fetch_icon.py 新拉图标一律 -o <task_dir>/assets/icons/,
读路径改种子库 + 本 task 两处都 glob。docker 下 skills 是 :ro bind,旧写法必败;
host 下还顺带污染仓库工作树。脚本已 out.parent.mkdir,无需改。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:33:59 +08:00
caoqianming
9a1e88d86f
docs: 精简 PROGRESS 每条压回 1-2 句 + DESIGN 状态表去重
...
PROGRESS.md 按头部自带规约(每条 1-2 句:做了啥+关键判断)把
"已完成关键能力"压缩:砍根因长推理 / 否决方案 (a)(b)(c) / 测试细节 /
部署操作 / 结尾"DESIGN不动·RUN不动"样板尾巴;同质 UI 碎条合并
(05-20~05-25 dev SPA 堆)。字符 131K→35K(-73%),细节仍在 git log/diff。
四段不动:状态表 / 关键决策 / 文件清单 / 下一步候选。
DESIGN.md §7.6/§7.7 phase 表删纯状态列(✅ /待,与 PROGRESS `## 状态`
重复=漂移源),保留设计意图(估时 / 撤销 / 前置依赖),状态指回 PROGRESS。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:26:10 +08:00
caoqianming
ed136f8ed7
fix(deploy): npm 默认源改回腾讯 — npmmirror 访问不稳
...
registry.npmmirror.com 实测访问半天连不上。换回腾讯 npm
(https://mirrors.cloud.tencent.com/npm/ )—— 它在历来 build 里一直 OK,
之前那次坏 wheel 只是腾讯 PyPI 的问题,npm 不受影响。pip+apt 仍清华不变。
备选记进文档:华为 repo.huaweicloud.com/repository/npm/ / USTC npmreg.mirrors.ustc.edu.cn。
update.sh:NPM_REGISTRY 默认 + 注释。RUN.md:最后更新 / §镜像构建说明 /
手动 build 示例 / 故障表 npm 行。PROGRESS.md:同条更新。DESIGN 不动。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:00:28 +08:00
caoqianming
fedf5a2cd5
fix(deploy): 默认镜像源改清华(pip+apt)/ npmmirror(npm)— 腾讯源给出损坏 litellm wheel
...
腾讯源返回的 litellm-1.87.0 wheel 文件损坏:镜像 index 声明的 sha256(fbbba7e…,
与 PyPI 官方一致)对,但实际吐出的文件字节算出 bbebefff…,pip 报
"THESE PACKAGES DO NOT MATCH THE HASHES"。本仓 requirements.txt 未钉 hash,
是镜像端 index 自声明 hash 与文件不符 = 镜像存的文件损坏/截断(非篡改、非 require-hashes)。
deploy/update.sh 三个默认值:
- PIP_INDEX_URL -> 清华 pypi.tuna.tsinghua.edu.cn(境内稳 + 同步及时;阿里 PyPI 曾滞后到没有 litellm>=1.83)
- APT_MIRROR -> 清华 mirrors.tuna.tsinghua.edu.cn(Debian / security 路径同官方,sed 替 host 即可)
- NPM_REGISTRY -> registry.npmmirror.com(清华无 npm registry,npmmirror 是境内事实标准)
换默认会让下次 build 从 pip 层全量重跑一次(~5-10min),之后命中 cache。
Dockerfile ARG 默认(官方源 fallback)不动。
RUN.md:头部「最后更新」、§镜像构建默认源说明、手动 build 示例、故障表
(新增 hash-不匹配诊断行 + 其余镜像行对齐新默认)。PROGRESS.md:2026-06-03 加一条。DESIGN 不动。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:46:54 +08:00
caoqianming
9cd33cf4d6
revert(workspace): 撤 ZCBOT_WORKSPACE_DIR env 覆盖(架构 bug),数据盘改用 bind mount
...
2026-06-02 给 resolve_workspace 加的 env 覆盖与路径存储层冲突:core/paths.py
把 DB working_dir 锚定 ROOT(代码仓库目录)存相对串,to_db_path 对 ROOT 外路径
直接 relative_to raise。env 一旦指向 ROOT 外的 /data/...,三家分叉:
- 文件面板 /v1/files 走 resolve_workspace(吃到 env)看数据盘
- agent resume 走 from_db_path(锚 ROOT)看 ROOT/workspace
- 新建 task to_db_path 直接 500
现场症状:文件面板"目录尚未创建",但 agent 文件其实写在老 ROOT/workspace。
改法:resolve_workspace 回退成 显式 arg > cfg workspace_dir > 默 workspace
(均 ROOT/<值>)。import os 保留(别处仍用)。要把重写入区落独立数据盘改用
bind mount 把 /data/zcbot/workspace 接到 ROOT/workspace —— .resolve() 不展开
bind,内核路径保持 ROOT 内,relative_to(ROOT) 照常过,DB 不用改,dev 不受影响。
RUN.md:删 .env 的 ZCBOT_WORKSPACE_DIR 段;「workspace 落独立数据盘」段改
bind mount(+ RequiresMountsFor 开机顺序硬化);故障表替换旧 env 行 + 加
"目录尚未创建"诊断行。PROGRESS.md:2026-06-03 加回退记录。DESIGN 不动。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:24:14 +08:00
caoqianming
382a85e88e
feat(workspace): resolve_workspace 加 ZCBOT_WORKSPACE_DIR env 覆盖(per-host 落盘)
...
prod 要把重写入的 workspace/users/ 落到独立数据盘(xfs prjquota),但
config/agent.yaml 的 workspace_dir 是 dev/prod 共用提交的,改绝对路径会带歪 dev。
resolve_workspace 优先级改为 env ZCBOT_WORKSPACE_DIR > yaml workspace_dir > 默 "workspace"
(对齐 sandbox.* 的 yaml+env 模式);env/cfg 值都按 ROOT/<值> 解析,绝对路径直接生效。
dev 不设 env 维持 ROOT/workspace,prod systemd 设数据盘绝对路径,两边不抢同一份 yaml。
PG 暂不迁(元数据库小,留默认 /var/lib/postgresql 少坑)。
RUN.md:env 段加 ZCBOT_WORKSPACE_DIR + 新增「workspace 落独立数据盘」段
(mkfs.xfs + fstab prjquota + rsync 迁移 + systemd env)+ 故障表一行。
PROGRESS.md:2026-06-02 加一条。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 14:42:00 +08:00
caoqianming
33bea27d85
fix(web): embed 模式登录页一闪而过 — body 首行同步隐藏 #login
...
#login 默认 display:flex(带 login-in 动画),加 body.embed-mode 隐藏它的
embedInit() 在 body 末尾才执行;单文件 3800+ 行,浏览器常在解析到底部脚本前
就先把登录卡画出来,造成"一闪而过"。改法:在 <body> 第一行加一段同步内联脚本,
?embed=1 时立即 add embed-mode,赶在 #login 解析/绘制之前隐藏它。
只是绘制闸门,底部 embedInit(postMessage 握手 / embed-waiting / token 分支)
完全不动,embed-mode 幂等。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:23:32 +08:00
caoqianming
4c1a26cee8
fix(deploy): update.sh 的 docker build 回到默认 TTY 进度 UI(撤 --progress=plain)
...
分层折叠刷新的 TTY UI 更直观;step 2 pip 去 -q 已足够让装包进度可见。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:07:35 +08:00
caoqianming
74580e951e
fix(deploy): update.sh 加自更新重跑守卫,修"改了源仍报旧错"
...
脚本 git pull 会改自己:变量默认值在 pull 前已求值、bash 又按字节偏移边读边跑,
首次拉到"改 update.sh"的提交那一轮跑的仍是旧脚本(默认源还是阿里 -> litellm 报缺版本)。
pull 后检出本脚本有变更即 exec 新版本从头重跑(ORIG_ARGS 原样回传,ZCBOT_UPDATE_REEXEC 防死循环)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:04:25 +08:00
caoqianming
4eacfe1bd9
fix(deploy): update.sh 默认源改腾讯避开阿里滞后(卡 litellm>=1.83);build 跳过改 --skip-build;进度可见
...
阿里 PyPI 镜像同步滞后(只到 litellm 1.82.6),撞 requirements 的 >=1.83.0。
- 默认 APT/PIP/NPM 源阿里 -> 腾讯(已到 1.88);step 2 host pip 显式 --index-url(sudo -u 会洗掉 PIP_INDEX_URL env)
- ZCBOT_SKIP_SANDBOX_BUILD=1 env -> --skip-build CLI flag(开发期不留兼容)
- step 2 pip 去 -q + step 4 docker build --progress=plain,部署逐行流式可见
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:00:00 +08:00
caoqianming
f66d55cc3b
feat(deploy): 加一键部署脚本 deploy/update.sh(pull/pip/migration/build/restart/healthz)
...
固化日常部署:git pull --ff-only → pip install → db upgrade head →
docker build sandbox → systemctl restart → curl /healthz 验活。
两处钉死:① migration 不能漏,env.py 直读 os.environ 不读 .env,脚本从
.env 抠 ZCBOT_DB_URL 显式喂进去;② build 必先于 restart —— 容器复用 +
tools/ 烤进镜像,restart 时 shutdown_all 清旧容器,下次 ensure() 才用
新镜像重建。sandbox 每次 build 无所谓(重活在 COPY tools/ 之上,cache
让改代码部署秒过)。镜像源默认阿里,可置空回落官方。前置守卫 + healthz
失败 dump journalctl 非零退出。bootstrap 不进脚本,留 RUN.md。
RUN.md §部署 SOP 重写为指向脚本 + 手动 fallback;PROGRESS 加一条。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 14:52:44 +08:00
caoqianming
fb5e68d9e7
fix(sandbox): 装 CJK 字体修 matplotlib / mermaid 出图中文方块
...
sandbox 镜像基于 python:3.12-slim 一个中文字体都没装,matplotlib /
mermaid(chromium)/ render_icon 出的 PNG 中文全是豆腐块。
- Dockerfile: apt 装 fonts-noto-cjk + fonts-wqy-microhei + fontconfig,fc-cache 刷索引
- style.py: 候选首位加 "Noto Sans CJK SC",matplotlib 优先用 Noto
- PROGRESS / RUN 故障表同步
改了 Dockerfile 必须重 build 镜像 + 清旧容器才生效。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:47:04 +08:00
caoqianming
0343dd6dd2
docs(progress): MP host 工具联网实测闭环 —— 换 next-gen key 后 step D 复测通过
...
初次 403 定位为 .env legacy 旧版 key 在新版 mp-api 失效;换 next-gen materialsproject.org
长 key 后查 Ca3SiO5 返 3 条 mp-xxxx + energy_above_hull,MP host 工具端到端通路确认可用。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:38:05 +08:00
caoqianming
68ce996ad2
feat(tools): documents/pymatgen secret-bearing 能力改 host-side tools,key 不进 sandbox
...
新增 tools/documents.py(document_list_kb/search/download)和 tools/materials_project.py
(mp_search_summary/get_structure/get_entries),key 只在宿主读取,sandbox/run_python 拿不到。
agent_builder 仅在对应 env 存在时注册。删 skills/pymatgen/materials.py::mp_rester() 旧入口,
smoke 改走 host tool。同步 DESIGN §6.7 secret-bearing 规则 + RUN/SKILL_LIST/两个 SKILL.md。
实测:MP step D 真连 api.materialsproject.org 返 403(工具行为正确,干净透传 [Error]),
疑似 .env 里 legacy key 在新版 mp-api 失效,待换 next-gen key 再验。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:35:10 +08:00
caoqianming
4bd074079a
design: 加 §8.1 seedream i2i + vision 后续步骤 + probe 实测 base64 通
...
用户场景"seedream 出图→基于该图二次修改"/"上传外部参考图让 agent 据此干活"
两条主模型 DeepSeek V4 纯文本覆盖不了。详评 3 方案选 E+C 组合(seedream
加 reference_images 走 5.0 i2i + 新 tools/look_at_image.py 走豆包 Seed 1.6
vision tool 调度),否决 A(换主模型降 code/tool calling 质量+改 loop/memory
工程面 5×)/ B(后台隐式 vision 路由失 agentic 控制+token 浪费)。
实测 scripts/probe_seedream_i2i.py:豆包 Seedream 5.0 /images/generations
接受 image_urls=[data:image/png;base64,...] 作 i2i 输入,200 返回新图 TOS URL
+ usage.generated_images=1。约束:输出 size≥3686400 像素(~1920²),单张参考
≤10MB,最多 14 张。**内网部署无需对象存储中介**,排除最大工程不确定性。
E+C 实施清单/风险/升级到 A 的信号已落 DESIGN §8.1,本版仅 probe+design,
tool 与 prompt 改造未启动。RUN.md / SKILL_LIST.md 不动(无 CLI/env/skill 变化)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 12:47:04 +08:00
caoqianming
e6eebbc9ad
feat(web): tool_call 标题行改显中文活动描述 + 修 args 字段 bug
...
dev.html 实时流读 ev.data.arguments 但后端 emit 的是 args,字段名对不上
导致参数永远为空、连带 artifact 路径提取失效。新增 toolActivityLabel 按
12 个工具的关键参数套中文动词(执行命令/运行 Python/读写编辑文件/查找搜索
/联网搜索/抓取网页/加载技能/生成图像视频),实时流与历史回放两处同步;完整
参数仍在折叠 <pre> 里。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 12:24:54 +08:00
caoqianming
aab1da3296
fix(executor_docker): 删 setsid 修 docker exec 延迟 stdout 丢失
...
实证:`docker exec ... setsid python -c "sleep(2); print"` 等满 2s 输出空,
同条件去 setsid 输出 hello。setsid 调 setsid() syscall 后 docker exec/runc
的 stdio attach 出问题,延迟输出被截。上一条 _run_subprocess 重写修了独立的
poll-loop bug 但不是用户当下症状元凶。setsid 历史是给 §7.5 Step 3b PGID kill
协议铺路,该协议未实现的当下是空头载荷 + 副作用。改 _exec_shell:141 / _exec_python:177
各删 1 个 "setsid"。回归测试加 test_run_subprocess_delayed_output_not_lost
(真子进程 sleep+print)+ test_argv_does_not_contain_setsid(防回潮)。19/19 PASS。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 11:02:17 +08:00
caoqianming
91cc14278c
fix(executor_docker): 重写 _run_subprocess 修 docker exec stdout 多 chunk 静默丢失
...
旧实现在 poll loop 里反复 communicate(timeout=0.5) 违反 subprocess API 假设,
配合 setsid bash -c block-buffered stdout 在多 chunk 输出时 chunk 静默丢失,
LLM 调 run_python / shell 拿到空 [exit 0] 8 字符,误判 paper_server 不可用。
改单次 communicate(timeout=full) + 侧线程 daemon poll cancel + 入口 inline
快路径。回归测试用真子进程 bash sleep 多次 echo 锁死,17/17 PASS。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 10:26:59 +08:00
caoqianming
f1a42ef13f
skills: 3 SKILL.md 标 sandbox env key 拦截现状(documents/pymatgen ⚠️ + research ✅ )
...
run_python `_SENSITIVE_PATTERNS` 过滤器拦含 API_KEY/TOKEN/SECRET 字面的 host env
(挡 prompt 注入抽 JWT_SECRET 等),误伤 documents/pymatgen 从 env 读 key 的 helper;
docker backend 下 host env 根本不入容器,问题更彻底。LLM 还把 research 跟它们排版
类推也放弃,可 research 不持 secret 任何模式都能用 —— 加 callout 阻止误推 + 反模式
扩到禁所有 HTTP 客户端裸调,免得 urllib 钻空子跳过 helper 教学。
broker 下轮做(host tool 范式不取),DESIGN 当时再补。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 09:23:41 +08:00
caoqianming
09378b56c1
skills/review: 加长文档处理段(骨架扫描 + 用户挑章节深审 + 中间文件落盘)
2026-05-28 16:29:26 +08:00
caoqianming
cf23c9d178
models: 加 local.{r1,qwen3} 内网模型档案,涉密任务用
...
DeepSeek-R1 (满血) + Qwen3-30B-A3B(服务端 alias 名是 Qwen/QwQ-32B,实际后端
Qwen3) 部署在 http://182.54.21.126:9000/v1,OpenAI 兼容,共享 LOCAL_LLM_API_KEY
env。thinking_mode=false(R1/Qwen3 默认就思考,不通过 reasoning_effort 控制)。
local.qwen3 已通连通性,local.r1 服务器侧调试中。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 15:55:37 +08:00
caoqianming
4b7d7e6f77
fix(skill_tool): docker backend 下返回容器路径而非 host 绝对路径
...
实测部署 dogfood analyze skill 时,LLM load_skill 拿到 host 绝对路径
`/home/lighthouse/zcbot/skills/analyze`,照 SKILL.md 拼 references 路径调 read
→ 容器内 namespace 没这条路径(容器只有 /sandbox/skills:ro 这 mount),抓瞎。
修法:LoadSkillTool 加 container_skills_dir 构造参数;agent_builder 在装它时
判 ZCBOT_SANDBOX_BACKEND==docker → 传 "/sandbox/skills",其它 → 保持原 host
绝对路径(开发期 host backend 不破)。
结构性收益:proposal/ppt/research/coding/pymatgen/stats_ml/plot_pub 全部 skill
references 在 docker backend 下自动 work,不用一个个改 SKILL.md 教用容器路径。
tests/test_load_skill.py 4 case 锁:host 走 host 路径 / docker 走 /sandbox /
末尾斜杠拼接不双斜杠 / 未知 skill 走原路径。docker executor 15/15 回归 PASS。
部署后:git pull + 重启 agent 进程让新代码生效(SkillRegistry 每请求重建但
LoadSkillTool 实例化在 build_agent 里,需要新进程)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 13:37:19 +08:00
caoqianming
203e14d15d
skills: 加 analyze(科学问题拆解 / 引导,PICO + Issue Tree + 5Whys/TRIZ/DoE)
...
服务"用户拿模糊高层科研问题过来、还没决定要不要立项 / 不知道从哪入手"的早期阶段
—— 之前 10 个 skill 没人接这场景(proposal/research/stats_ml 都要先想清楚)。本 skill
不执行任务,只把模糊命题翻译成可操作子问题 + 实施路线图,最后接力给下游 skill。
四段式工作流:
1. PICO/PECO 规范化(P/I/C/O + FINER 五维自检)— BLOCKING
2. Issue Tree 拆解(MECE,默认机理-现象-工艺三层,叶子标 类型/优先级/能力描述)— BLOCKING
3. 按叶子类型分支深化:根因→Fishbone+5Whys / 创新→First-principles+TRIZ / 优化→DoE 选型
4. 实施路线图(每步标干什么/能力/产物/判停)+ TODO + 接力建议
7 文件 657 行:SKILL.md + 5 references(按需读)+ 1 template(analysis.md 骨架)。
关键决策:
- 不硬编码"叶子能力→skill 名"映射(skill discovery 已注入 prompt,改名不破坏)
- description 双重防护:触发条件写死探索阶段 + 输出末尾推 X 能力接力
- 不要 Python helper(全引导式对话,跟 review skill 同范式)
- TRIZ 摘 10 对建材矛盾(强度↑韧性↑ / 早强↑后期↓ / 致密↑透气↑...)够 80% 场景
- DoE 只规划设计类型 + 因素表,具体点位生成由下游 stats_ml 跑 pyDOE2
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 13:18:54 +08:00
caoqianming
85415470d2
sandbox+infra: Python 3.10→3.12(host+Dockerfile)+ docker PYTHONPATH 加 /sandbox
...
为通过 3 个科学 skill 的 smoke 解决两件基础设施问题。
1. emmet-core 0.86.0rc1 用了 typing.NotRequired(3.11+),host .venv 是 3.10
→ mp_rester ImportError。选 3.12(当下 ML 生态默认稳定版,比 3.11 多一年
优化,比 3.13 wheel 覆盖更全 Windows 不踩坑)。Dockerfile python:3.11
→ 3.12 同步升,部署机 rebuild image 时生效。
2. executor_docker.py:172 PYTHONPATH 由 /workspace 改 /sandbox:/workspace,
修历史 bug —— skills/ bind mount 到容器 /sandbox/skills:ro,SKILL.md 教
LLM `from skills.xxx import yyy`,docker backend 之前根本 import 不到
(research/paper 同款受影响,只是 dogfood 一直跑 host backend 没暴露)。
test_executor_docker.py:243 regression 测试改为 assertIn 含 /sandbox,
15/15 PASS。
smoke 验证:pymatgen XRD / sklearn / statsmodels / plot_pub 全通,
mp_rester 联网遇 MP 服务侧 IP/ASN 403(LBNL 对国内 IP 临时封,非代码问题)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 11:35:02 +08:00
caoqianming
52f201404c
skills: 加 pymatgen / stats_ml / plot_pub(建材院无机材料场景)
...
服务中国建材院无机非金属材料 R&D。从 K-Dense AI scientific-agent-skills 仓库挑
3 个 ★★★ skill fork,不走 npx 一键装(138 个 description 入 prompt 噪声 + 误触发)。
- pymatgen: 晶体结构 / XRD / 对称性 / 相图 / Materials Project。helper
materials.py 内 CEMENT_PHASES 收 66 条中英文相名映射(水泥熟料 / 水化产物 /
陶瓷耐火 / 玻璃晶相 / 常见矿物)、lookup_phase 大小写不敏感、mp_rester 从
env MP_API_KEY 拿 key
- stats_ml: 纯指南 skill,场景导航 sklearn / statsmodels / PyMC 三选一,
5 个工作流(配方-性能回归 / DoE 二阶响应面 / 显著性分析 / 贝叶斯小样本 /
DBSCAN 异常配方)+ 16 条反模式
- plot_pub: 出版级 matplotlib,helper style.py 内 apply_pub_style() 一键
设置中文字体跨平台 fallback (SimHei / YaHei / WenQuanYi) + viridis +
dpi + PDF Type 42
requirements.txt 加 pymatgen / mp-api / scikit-learn / statsmodels
(pymc 注释,装包重按需开)。RUN.md env 段加 MP_API_KEY(可选)。
SCIENTIFIC_SKILLS.md 根目录沉淀整体评估,后续 materials_db 落地参考。
scripts/smoke_scientific_skills.py 三 skill 链路通路验证脚本。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 11:33:59 +08:00
caoqianming
1d6a9898e4
DESIGN §7.5: image 体积 / 多 user 资源 / 后续加包策略
...
把"image 大 = 资源占用大 = 多 user 容器爆炸"的直觉关联拆开:
① layer 共享 + 空载 sleep,image 大 ≠ 运行时吃更多 RAM;
② 多 user 瓶颈在并发 exec 非 idle 容器,杠杆全在 cgroup
limit / 并发 semaphore / idle 回收;
③ 新增依赖采用 base 收敛 + per-user 持久化 venv + 使用频次沉淀
(长尾 domain 包私有装,>30% × ≥3 次再沉淀回 base)。
落地排 Stage C Step 6 Executor。PROGRESS 加一条指针。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 08:13:39 +08:00
caoqianming
0b7c084458
skills+general_v1: ppt 歧义反问 + 路由产物形式歧义先问
...
skills/ppt/SKILL.md:3 上次收紧白名单+反例后,"MES 汇报方案" 这种请求
还是被路由命中 —— 反例列表只列了 "生成方案/写报告/出文档/做纪要",
"汇报方案" 未列入,而 "汇报" 在 LLM 语义里本就有强烈的 PPT 联想
(工作汇报/季度汇报多以幻灯片形式),足以压过 "必须明确点名 PPT"
的硬约束。
修法:
- ppt SKILL.md description 改三段(✅ 触发白名单 / ⛔ 不触发 [只留
"报告/文档/纪要" 明确指向文档的词] / ⚠️ 歧义先反问)。把 "汇报/
方案/材料" 从反例摘出来,改成 "先反问 'PPT 还是 Word/Markdown 文档'
再决定 load",把判断权还给用户而不是赌 LLM 路由词典。
- prompts/system/general_v1.md Skill 机制段加 "产物形式歧义时先问"
通用原则,跨 skill 生效(imagegen/videogen 各自 skill 内本来就有
'问清楚再画' 逻辑,抽到 system prompt 让新加 skill 也继承)。
否决:
- 继续往反例堆 "汇报方案/汇报材料/汇报内容":堆词典治标不治本,
下次 "做个 Q4 总结" 又得加。
- 路由层加 required_keywords 结构化字段,在 discovery_block 之前
grep 兜底:跨多 skill 都得补字段,工程量大,短期 LLM 反问范式
收益已够。
- ppt skill load 后再反问:路由命中就已是误触发,要在路由阶段拦。
代价:用户心里已经清楚要 PPT 但没说时会多一轮反问;缓解靠反问句式短
+ 暗示默认选项,一个字 "PPT" 就能过,比生成完整 deck 后推翻代价小
一个数量级。
热更新无需重启服务:agent_builder.py:197 每次 build_agent 现读
general_v1.md;SkillRegistry 也每次 build_agent 重建。DESIGN/RUN 不动。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 15:36:25 +08:00
caoqianming
8109f20345
ppt skill: description 收紧路由,避免 "生成方案" 被误命中
...
skills/ppt/SKILL.md:3 原文含 "方案" / "生成" 字样,Claude 路由时把
"生成一个方案" 也命中到 PPT skill。改成显式白名单
(PPT/幻灯片/演示文稿/.pptx/slide/deck)+ 显式反例
("生成方案/写报告/出文档/做纪要" 不触发)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 15:10:21 +08:00
caoqianming
c4229bede4
skills: /v1/skills 每次现扫,加/删 skill 目录无需重启
...
lifespan 静态快照改 per-request 扫描,SkillRegistry 构造 ~3ms / 次,
e2e ~20ms 在 JWT + DB 噪声里完全可忽略。agent_builder 早已每次新建
registry,本次只补前端下拉这一处。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:47:47 +08:00
caoqianming
da4e47139f
dev SPA: embed+task_id 模型下拉 + 导出/清空键 disable 修复
...
- loadModels 收尾若有 task 选中补一次 renderChatMeta,修 embed+task_id 模型下拉
在 models 接口 resolve 前 selectTask 已跑过的竞态(同步覆盖生图/生视频下拉)
- btn-export/btn-clear-msgs 不再按 n_messages 门禁,只要选中 task 就常亮;清空
保留 running 期禁用。修历史 bug:清空后两键 disable,新对话进来 taskMeta 不
重渲一直灰
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:42:19 +08:00
caoqianming
eaf7f3ea1e
Stage C 收尾包:资源 yaml + 磁盘配额 + 网络放开 + 容器内源持久化
...
dogfood + 信任同事白名单阶段 Step 4 完整 egress proxy 暂不做(沉淀为升级触发
信号:任一陌生用户注册 / 模型异常 outbound / 信任白名单出现非密切相识者 → 必上)。
本批 3 件:
(A) 容器资源 yaml 化(可调不重 build):
- agent.yaml 加 sandbox 段(memory/cpus/pids_limit)
- SandboxPool ctor 加三字段,优先级 env > yaml > 默(2g/1.0/256)
- setup_pool/init_pool 透传 sandbox_cfg
- sandbox check 输出加 [info] 4 行给运维一眼对账
(B) 应用层磁盘配额(§7.5 #4 软配额):
- migration 0008 user_disk_usage 单行 per user
- core/storage/disk_quota.py:parse_bytes("5gb"/int)+ scan_user_dir
(os.scandir 跳顶层 .zcbot_tmp / .memory)+ upsert ON CONFLICT
+ check_disk_quota + scan_all_users 串行
- lifespan _disk_scanner 后台 task(启动跑一次 + 默 15min 周期)
- DockerExecutor write/edit 起手 gate 超额 [Error] 不调容器
- /v1/files/upload 同款 gate 超额 HTTP 413
- yaml `quotas.disk_bytes_per_user: 5gb` + `disk_scan_interval_seconds: 900`
- race 接受:扫描间隙写入轻微突破(image/video 配额同款 race-tolerant);
外部用户开放前 OS 层 xfs prjquota 兜底
- 11 测试 covered parse_bytes / scan / 跳 dotfile
(C) 网络放开 + 容器内源持久化:
- network.py 去 --internal flag,容器走 docker bridge default 有 NAT outbound
- 已存在 internal network 不自动 rm 仅 warn,RUN.md 给迁移命令(避免破现有容器)
- iptables 红线段不动(169.254/127/10/172.16/192.168/100.64/PG_IP DROP),
挡 cloud metadata + 内网扫描 + loopback,基线不依赖 proxy
- Dockerfile 加 /etc/pip.conf(global index-url + timeout 60) + /etc/npmrc
(global registry),让运行时模型 `pip install foo` / `npm install bar`
也走 mirror(此前 --build-arg 只 build 时生效)
unittest discover 46/46 PASS(原 35 + 新 11)。
DESIGN 不动(延后决策仍在 §7.7 Stage C 阶段语义内,触发信号沉淀进
PROGRESS / RUN);RUN.md 加 env 列表 + 网络迁移 + 配额 + 故障兜底 3 行。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 08:35:53 +08:00
caoqianming
23ff996d38
Stage C Step 3d: fs 工具进容器 + DESIGN §7.5 #6 重写(物理边界替代代码护栏)
...
Ubuntu dogfood 暴露 host 工具漏底:base_dir=Path.cwd() 无 user_root 校验,
模型 glob "*" 列出 host /home/lighthouse/zcbot/.git/.venv/... zcbot 源码自身。
DESIGN §7.5 #6 原写"host 工具走 paths.py::resolve_user_path 校验"是假命题
(代码里没那函数),绝对路径完全不挡。
修法:fs 工具(read/write/edit/glob/grep)也走 docker exec,物理边界替代
代码护栏(Phase B path validator 那条不做 ── 脆弱)。
- core/sandbox/tool_runner.py 新增:容器内 helper,stdin 接 JSON args,
调 tools/fs.py 的 Tool 子类;base_dir=cwd,user_root=/workspace
- DockerExecutor 加 FS_TOOLS 信任域 + _exec_fs_tool:docker exec -i ...
python /sandbox/tool_runner.py <name>,stdin 喂 JSON args(CJK / 引号
透明传不被 shell metachar 切)
- _run_subprocess 加 stdin 参数 + is_fs_tool 分支返 stdout 直透(原 Tool
返回串语义保持),exit≠0 stderr 当 ToolResult content
- SandboxPool 加 repo_root 字段 + <repo>/skills:/sandbox/skills:ro mount
让容器内 read SKILL references 能解析
- Dockerfile COPY tools/ /sandbox/tools/ + tool_runner.py(build-time COPY
而非 mount ── 容器内代码不应跟随 host repo 改动)
- web/app.py 透传 ROOT 给 init_pool
- 留 host 的工具:load_skill(SkillRegistry 内存查找)/ web_search /
web_fetch / seedream / seedance(持 Bocha/ARK key 不入容器)
- DESIGN §7.5 #6 重写:"几乎所有工具进容器,host 只留持 key + 跨 user 的",
原假命题溯源标注 2026-05-26 修正
代价:每 fs tool call +~200ms docker exec overhead,对话级 N≤15 总 1-3s,
LLM 推理 5-30s 下噪声。升级触发(§7.9 升级表)docker exec → unix socket RPC
仍按原信号(overhead/total > 30% 持续 / 长驻服务工作流)。
测试:test_executor_docker 加 4 fs 路径测试(argv 形态 / CJK stdin JSON /
exit≠0 stderr 透传 / timeout);改原 read 直通测试 → load_skill 直通
(read 现在进容器)。unittest discover 35/35 PASS。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:56:41 +08:00
caoqianming
d93cc1a949
Stage C Step 3 hotfix: exec_user 跟随 build_arg + 镜像装 mermaid-cli
...
Ubuntu dogfood 暴露两个真问题:
(1) uid 错配:DockerExecutor 写死 --user 1000:1000,但镜像 build 时
--build-arg HOST_UID=$(id -u) 跟随 host 实际 uid(腾讯云轻量 lighthouse
uid=1001),docker exec 进容器 uid=1000 → bind mount owner 1001 错配 →
写 /workspace/<wd>/ 全 EACCES,文件落 /tmp。
改 DEFAULT_EXEC_USER = "zcbot"(username,docker 自动查容器 /etc/passwd
拿 uid),无论 HOST_UID build 成 1000/1001/其他都跟 bind mount owner 对齐。
(2) proposal/patent skill 渲 mermaid 缺 Node:render_diagrams.py 调
shutil.which("mmdc") 容器没装 → 退 mermaid.ink → sandbox --internal
默 deny outbound API 也不通 → ASCII fallback 出 docx 没图。
Dockerfile 加 chromium + nodejs + npm + @mermaid-js/mermaid-cli,
+~400MB 接受成本(ASCII 不能用)。容器 chromium 缺 setuid sandbox +
/dev/shm 不够大会跪,镜像落 /sandbox/puppeteer-config.json
(--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage) +
ENV MERMAID_PUPPETEER_CONFIG;render_via_mmdc 改读 env 拼 -p 注入,
host 上跑 env 没设行为零变化。
PUPPETEER_SKIP_DOWNLOAD + PUPPETEER_EXECUTABLE_PATH 让 puppeteer 用
容器内 chromium 不下载自带 Chrome(省 ~300MB)。
NPM_REGISTRY build-arg 同 pip 同款(腾讯云内网 / 阿里 npmmirror)。
RUN.md 加 NPM_REGISTRY 段 + 故障兜底 3 行(EACCES uid 错配 / mmdc 报
launch chromium / npm 慢)。DESIGN 不动(纯 bug fix + skill 依赖)。
unittest discover 31/31 PASS。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:47:33 +08:00
caoqianming
1a950dedb5
Stage C Step 5: main.py sandbox check + lifespan fs quota WARN
...
- main.py sandbox check 子命令:5 项独立探测 + 汇总 exit code
① docker daemon 可达 ② zcbot-sandbox:latest 镜像存在
③ zcbot-sandbox-net network 存在(warn 不 err) ④ 镜像 zcbot uid 与 host
uid 对齐 ⑤ workspace/users 所在 fs 类型可 quota
- core/sandbox/check.py:detect_fs_quota(path) -> (level, msg) 抽出来给
lifespan 与 CLI 共用;识别 xfs+prjquota/ext4+project/zfs/btrfs/tmpfs/其他
- web/app.py lifespan docker backend 启用时调 detect_fs_quota 打 WARN
到 stdout(不阻塞启动,应用层周期扫描仍生效)
- err vs warn 分界:err = docker backend fail-fast 根因(daemon/镜像/uid),
warn = 不阻塞启动但外部开放前要清(network 缺/fs 不可 quota)
- run_sandbox_check 用 module-level getattr 而非固化 CHECKS 元组,让
unittest patch core.sandbox.check.check_xxx 生效
- tests/test_sandbox_check.py 19 测试覆盖各分支 + exit code 汇总;
unittest discover 31/31 PASS
- RUN.md 加"部署前置对账"小节 + "配额硬化"重写(fs 状态→处理映射表 +
xfs 升级 4 步) + 故障兜底 3 行;DESIGN 不动
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 16:41:16 +08:00
caoqianming
dfac0acfa6
Stage C Step 3: DockerExecutor 集成 AgentLoop + web lifespan reaper
...
- core/executor_docker.py 新增 DockerExecutor:组合 HostExecutor+SandboxPool,
shell/run_python 走 docker exec(setsid + --user 1000:1000 + --workdir),
其他工具直通 host(§7.5 #6 信任域二分)
- run_python tmp .py 落 <user_root>/.zcbot_tmp/<task_id>/(dotfile,/v1/files
天然过滤),容器内对应 /workspace/.zcbot_tmp/...,跑完 unlink
- ZCBOT_SANDBOX_BACKEND=host|docker env 切 backend,默 host(Windows dogfood
零变化);docker 路径 pool 未 init → fail-fast 不静默退化
- web/app.py lifespan:docker backend 启动时 init_pool + shutdown_all 清孤儿 +
60s 后台 reaper(run_in_executor 调 sync reap_idle);关闭时 cancel + 兜底清
- pool.py 顺手清 Step 2 债:asyncio.Lock → threading.Lock,ensure 改同步
(主使用方是 BG 线程 tool call,ephemeral loop 会让 asyncio.Lock 跨锁失效)
- Cancel limitation 接受:Popen.kill() 仅杀 docker CLI 客户端,容器内进程靠
idle 5min reaper 兜底;升级到 PGID 协议(§7.5 #3 )等用户反馈触发
- tests/test_executor_docker.py 11 测试覆盖关键路径(host 直通/argv 形态/
tmp 清理/timeout/cancel/未知工具/enable_run_python=False)
- DESIGN.md 不动(纯按 §7.5 #5 #6 既有协议实施)
- RUN.md 加 ZCBOT_SANDBOX_BACKEND env 段 + 切 docker 的前置条件 + 集成验证路径
- unittest discover 12/12 PASS
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 16:13:16 +08:00
caoqianming
160e801ab0
Stage C Step 2: Docker per-user 容器池 + iptables blocklist 基底
...
- deploy/sandbox/Dockerfile: python:3.11-slim + tini + iptables + non-root uid via HOST_UID build-arg + 全套 requirements
- deploy/sandbox/init.sh: 6 段红线 IPv4 + ::1 + ZCBOT_PG_IPS 环境注入,任一规则失败 fail-fast
- core/sandbox/network.py: ensure_network() 创 --internal zcbot-sandbox-net
- core/sandbox/pool.py: SandboxPool 容器命名 zcbot-sandbox-<user_id>,per-user asyncio.Lock,
in-memory _last_active dict(Docker 23+ 移除 docker update --label-add),hardening flags
全套(read-only / tmpfs / cap-drop ALL + NET_ADMIN / no-new-privileges / pids/mem/cpu limit /
bind mount user_root → /workspace)
- core/sandbox/__init__.py: 公开 SandboxPool / container_name / setup_pool / NETWORK_NAME
Step 2 范围明确不含 AgentLoop 集成 / shell-run_python 切容器 / egress proxy / reaper task ——
pool 孤立 commit,Step 3 接入。本地 Windows 无 docker 走不动,Ubuntu 上 5 条 smoke 命令
(build / iptables 段 / non-root uid / read-only / 销毁)写进 RUN.md "Sandbox(Stage C,Ubuntu)" 段。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:44:34 +08:00
caoqianming
48f99cf66d
Stage C Step 1: Executor 接口骨架 + HostExecutor in-process backend
...
- core/executor.py: Executor ABC + ExecCtx(user_id/task_id/working_dir/cancel_check) + ToolResult
- core/executor_host.py: HostExecutor 包原 tools dict,统一三种错误为 ToolResult
- core/loop.py: AgentLoop 接 executor 而非 tools,_execute_tool_call 收成单条 call_tool 调用
- core/agent_builder.py: tools 装完后 HostExecutor(tools) 包一层,working_dir 透传 AgentLoop
接口形状与 DESIGN §7.5 #5 sketch (`call_tool(name, args, ctx)`) 完全一致,
backend 无关 —— Step 3 docker backend 接入时 AgentLoop 零改动,只换装配层。
行为零变化:smoke 4 分支(unknown/bad args/happy/schemas)全过,unittest 1/1 PASS。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:07:55 +08:00
caoqianming
d6af9a59fe
新增 patent skill + REVISIONS.md 修订日志机制
...
patent skill 写中国发明专利技术交底书,五阶段 workflow (素材摄取 → 挖点 → 检索 → spec → 逐章起草 → 自查渲染),BLOCKING 节奏同 proposal/ppt。复用 markitdown CLI + proposal scripts (render_diagrams/render_docx) + web_search/web_fetch + documents/research skill,零新增脚本;不实现 CNIPA 爬虫(维护成本高)。
REVISIONS.md 作为产物迭代 changelog,覆盖 proposal/patent/ppt 三个产物型 skill — spec = 宪法定调,REVISIONS = 每次卡点累加;单行 bullet 倒序追加,何时记/何时不记按 skill 领域定制(技术路线/区别特征/版式)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 09:34:01 +08:00
caoqianming
a6ae6c4647
在 DESIGN §7.5 末尾沉淀 Stage C 沙盒实施硬协议
...
把 §7.5 的原则具化成 6 条硬协议,Stage C 实施时按表对账而非凭记忆:
① 网络 blocklist 硬编码段(cloud metadata 169.254/16 / loopback / 内网三段 /
CGNAT,PG IP 单独再 block 一遍 belt-and-suspenders);
② egress proxy 模型(容器 HTTP_PROXY env + iptables DROP except proxy
端口防 SDK 绕 env;宿主侧 proxy 做域名 allowlist + 字节计量 +
network_audit 审计日志;allowlist 初始集列出 PyPI/GitHub/npm 等);
③ 进程组清理协议(docker exec 走 setsid + kill -- -PGID,防 nohup & /
disown 跨 exec 持久化破"同 user 不内隔离"残留风险假设);
④ 磁盘配额硬化时点(外部开放前必须升 xfs/ext4 project quota 或 zfs
dataset quota,否则扫描间隙打满共享 fs 拖死同节点其他 user);
⑤ Executor 接口走 backend driver + ZCBOT_SANDBOX_RUNTIME config 注入
(未来切 gVisor/Firecracker/e2b 应用层零改动,避免 Docker API 形状
泄漏到接口层);
⑥ 工具按信任域二分 dispatch — host in-process: read/write/edit/glob/
grep/load_skill/web_search/web_fetch(原本就在 host 持凭据 / 走
paths.py 校验,塞容器无收益付 200ms × N);container exec:
shell/run_python(执行任意代码必隔离)。
同时写死 gVisor / Firecracker-e2b / 容器内 tool-runner 三档升级触发信号
+ 不升级的反向兜底理由,防凭审美决策。
PROGRESS:状态表 §7 SaaS 行 + 下一步候选 #2 加锚指向新清单;时间估值
2-3 天 → 3-5 天;补 5 项 Stage C 完成 DoD 红队回归用例(metadata IP
timeout / PG IP block / nohup 残留 docker top 不见 / 跨 user 容器互访
阻断 / 非 allowlist 域名 403)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 09:00:16 +08:00
caoqianming
fe95df0b9d
Add web_search and web_fetch tools via Bocha AI search API
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:37:33 +08:00
caoqianming
ade7f3d1e1
Vendor markdown frontend assets
2026-05-25 09:31:36 +08:00
caoqianming
7a0d03fb29
Show upload progress
2026-05-25 08:54:06 +08:00
caoqianming
6e33f07bfb
Show pasted upload chips
2026-05-25 08:43:07 +08:00
caoqianming
f157a4e050
Add resizable web panes
2026-05-25 08:31:31 +08:00
caoqianming
f3017480d0
Show full file names on hover
2026-05-25 08:18:20 +08:00
caoqianming
3525399041
Fix task list scroll container
2026-05-25 08:06:33 +08:00
caoqianming
ae6b8630e2
ui: 手机端对话面板顶栏 + chat-meta 紧凑化 (5 按钮换行不竖排 + 模型 label 转 emoji)
...
- #pane-mid .pane-head 加 flex-wrap + 按钮 white-space:nowrap,
消除 "完\n成" / "废\n弃" 这种内部断行;藏 "对话" label / spacer
- #chat-meta 手机端 gap 8→6px,藏 .tid,.desc 限宽 60vw ellipsis
- 三个模型下拉 label 加 .mdl-text / .mdl-icon 双 span,
桌面文字 / 手机 emoji (💬 /🖼/🎬 ) CSS 切换,桌面零变化
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 16:00:52 +08:00
caoqianming
72ae0ded0a
feat(web): embed 模式接受 ?task_id=<uuid> URL 参数自动定位 task
...
首次签发 token / 已有缓存 token 两条进入路径都覆盖;
401 重签不重置选择,尊重用户中间 UI 切过的 task。
EMBED.md URL 参数表 + 故障兜底同步更新。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:27:19 +08:00
caoqianming
758486e2cd
feat(quotas): 媒体生成每账号每日上限 (默 20 图 / 5 视频, yaml 可配)
...
config/agent.yaml 加 quotas 段;core/storage/usage.py 加 check_daily_quota
(COUNT usage_events WHERE user_id+kind+created_at>=本地今日 00:00);
SeedreamTool / SeedanceTool ctor 收 daily_limit, execute() 起手 if 超额
返 [Error] 不调远端不烧钱。错误串只暴露已用/上限 + 重置时间,不写
yaml 路径 (避免 LLM 转述泄漏内部 schema 给外部用户)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:21:39 +08:00
caoqianming
84388b278e
ui: "+ 新建任务"按钮改通栏单独一行 (windows 320px pane 不再换行)
...
挪到 #pane-left 第一行 pane-head 之下的独立行,flex:1 撑满整行(primary 红底
通栏 CTA)。原塞 spacer 之后,中文五字在 320px pane 被挤断行。顺手删 @media
phone 里 #hd-new 紧凑覆盖(通栏不需要缩)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:44:30 +08:00
caoqianming
5e27ea9424
ui: "+ 新建任务"按钮从 header 挪到任务面板 pane-head
...
语义更贴(任务面板的动作);顶栏减负只剩身份区(brand/who/logout);两种模式
DOM 一致,顺手删了 embedInit 里动态 insertBefore 那段。文案保留完整"+ 新建任务",
样式加 small 跟周边对齐高度,保留 primary 红底显眼。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:40:49 +08:00
caoqianming
76f4c45350
feat(web): dev.html 加 iframe embed 模式 (?embed=1&parent_origin=...) + EMBED.md
...
embed 模式藏左上 brand / 用户名 / 退出登录,桌面整层 header 移除,移动端保留
header 给 mobile-tabs;"+ 新建任务" JS 移到任务面板 pane-head。postMessage 双向
握手:iframe 发 zcbot-ready / zcbot-401,父端回 zcbot-token{token,user_id,
user_name?};双向 event.origin 校验(parent_origin URL 参数白名单)。401 改写
logout 不 reload,而是通知父端 + 显灰底 spinner 等待层。复用已有 /v1/auth/login
SSO 入口,platform 后端 PLATFORM_KEY 代换 JWT,不下放浏览器。EMBED.md 写给
platform 工程的对接手册(URL / 协议 / Node+Python 后端示例 / 父端前端示例 / CORS
+ CSP frame-ancestors 收紧建议 / 调试 + 故障兜底)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:32:16 +08:00
caoqianming
d6cbe8194b
ui: chat-input 支持 Ctrl+V 粘贴文件上传 + chat-hint 反馈
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:59:53 +08:00
caoqianming
0c732590a4
ui: 文件预览弹框让出 chat-form 高度,打开期间仍可点击/打字
...
#file-preview-modal 加 bottom: var(--preview-bottom-inset, 0),openFilePreview 时 JS
量 chat-form 当前高度写到 inline style,关闭时清掉。card max-height 跟着收缩不溢出,
手机段用 100dvh 同理。无活动任务(chat-form 隐藏)走 0,弹框仍全屏。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:41:53 +08:00
caoqianming
eec7eb156f
feat(paths): 对外路径统一全形式 <wd_name>/<rel> + UI 一次性兼容历史简写
...
system prompt 加硬约束: 助手 echo 产物文件路径必须用 user_root 相对全形式
<wd_name>/<rel> (<wd_name> = task_dir 末段, 如 生图测试/videos/xxx.mp4),
不简写为 videos/xxx.mp4 这种 task 内裸形式 -- Web UI 按 <wd_name>/ 前缀挂
chip, 简写 → chip 失效用户点不开。媒体 tool (seedream/seedance) 的 saved:
行已是规范形式可直接照抄, ppt/proposal/coding 等 run_python/write 写文件时
自己拼。跨所有产物 skill 统一生效。
imagegen/videogen SKILL 把"把 saved: xxx 告诉用户"重复教学改成"照抄
saved 行, 详见 system「路径」段" (避免协议漂移, 新产物 skill 不用重复教育)。
ppt/proposal 等 SKILL 不动 -- system 协议自动管。
dev.html extractArtifactRels 加一次性兼容兜底: 产物目录裸路径
videos/xxx.<ext> / figures/xxx.<ext> (协议刚性前历史简写) prepend
<wdName>/ 拼成 user_root rel。**白名单显式枚举两项不扩展**, 长期老消息
归档后整段可删。
术语校准: 前缀叫 <wd_name> (working_dir 末段) 而非 <task_name> -- 用户允许
wd_name ≠ task_name, _display 锚 user_root 出来的是 <wd_name>。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 12:45:54 +08:00
caoqianming
7d3a93fc1f
ui: dev.html 手机自适应 (两档 @media + tab 单列切换)
...
平板 641-1024px 强制 rail (不写 localStorage,回桌面用户偏好仍生效);
手机 ≤640px 单列 + body.mv-{left,mid,right} 切换 + header tab 按钮换行铺底;
selectTask 自动切到对话视图,100dvh 解决 iOS 工具栏挤压,
input/textarea ≥16px 防 focus 缩放,4 modal 改 min(92vw,…) / file-preview 全屏化。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:39:42 +08:00
caoqianming
7ff58c488e
feat: 接入豆包 Seedance 2.0 Fast 视频生成 (文生视频) + videogen skill
...
- tools/seedance.py: 异步 submit /contents/generations/tasks → 5s 轮询 → succeeded
后 download mp4 + meta.json 落 <wd>/videos/;失败/cancel 不计费;cancel_check 在
轮询间检查,响应用户停止按钮
- config/media/doubao.yaml: 展开 video.seedance_2_fast (¥37/Mtok 文生 / ¥22/Mtok
图生,token 公式校验 720p 5s = ¥4.00 完全对上源数据)
- core/storage/usage.py: record_video_usage,kind=video,units jsonb snapshot
resolution/duration/ratio/fps/tokens/单价
- core/agent_builder.py: build_agent 加 video_variant + cancel_check 形参,
cancel_check 必须 build 阶段传 (SeedanceTool ctor 持有用于轮询)
- web/app.py: GET /v1/video_models + MessageRequest.video_model + 透传
- web/static/dev.html: 顶栏第三下拉 (image 旁边) + state.videoModels/videoModel
- skills/videogen/SKILL.md: 六维诊断 (运动+镜头 替代 imagegen 的光线);BLOCKING
门槛比 imagegen 更严 (¥4 vs ¥0.22) + 等 30-90s 出片
- prompts/system/general_v1.md: 加 seedance 触发指引 (平行 seedream)
phase 1 仅 t2v 文生视频,fast 上限 720p。API 端到端 smoke 跑过:路径/auth/错误解析
全通,body schema 待用户在火山方舟控制台开通模型后真出片才能验。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:30:54 +08:00
caoqianming
9a26e85da2
fix(ui): primary button hover 文字消失 (.primary:hover 守住 background)
...
button:hover:not(:disabled) 与 button.primary:hover 特异性同为 (0,2,1),
平手按源码序后者赢,但后者只声明 filter 没声明 background,导致 background
fallback 到前者的 --hover 浅灰,白字浅灰底视觉消失。
修法显式 background: var(--accent),brightness filter 在红底上正常提亮。
影响 "+ 新建任务" / "发送" 两个 primary 按钮。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:57:45 +08:00
caoqianming
29ff8e6a9e
doc: sandbox 阻塞地位写进 DESIGN + §7.9 工具层不再加强黑名单
...
- §7.7 Stage C 标外部用户开放 hard prereq
- §7.8 加 shell+run_python 无沙箱风险行
- §7.9 加 2026-05-21 取舍:tools/shell.py BLOCKED_PATTERNS 是 trivial-bypass
装饰品,不在它上面继续加规则;正确防线在 OS 层 (per-task docker exec
+ drop caps + read-only rootfs + bind mount + egress allowlist + cgroup)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:57:33 +08:00
caoqianming
6f9391dee3
ui(css): dev.html 圆角降一档 + 抽 token + modal 基类化 (style -11%)
...
抽 5 组 CSS token(语义色组 / 圆角分档 / mono / transition / shadow),顶栏按钮 hover + dd-item + badge 全切到 token(同色 selector 合并:export ≈ sp-copy 蓝、abandon ≈ sp-move 橙);4 个 modal 抽 .modal 基类(fixed/inset/bg/.show 五属性合并);.msg .body 与 file-preview .md-render 合并 markdown 渲染规则。圆角主流档 6px → 4px,modal card 8~12px → 6~8px,art-chip 999px 保留(胶囊语言)。功能 0 改动,JS 一行没动。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:50:17 +08:00
caoqianming
fa6cb72103
ui: 工作目录回到原生 select + sentinel + 二级 input (modal + 顶部 filter)
...
combobox 方案推翻 — 即使 show 不过滤,modal wd 因联动有值后用户直觉仍是
"得点开下拉看选项",自实现 panel 不如浏览器原生 select 稳。
- modal nt-wd-sel 第一项 sentinel "+ 新建「<name>」"(updateSentinelLabel
跟 name 实时刷),sentinel 选中显示二级 nt-wd-new 默认跟随 name,
选已有目录隐藏;wdManuallyEdited 锚到二级 input
- 顶部 filter-wd 改 select,onchange → loadTaskList(无 debounce)
- loadFolderSuggestions + populateFolderSelects 灌两个 select,保留当前选中
- enterApp fire-and-forget 预拉 folders 让左 pane 一打开就有选项
- hint 在"新名碰到同名"时提示"将复用而非新建"
- combobox 工厂 + .combo CSS + datalist 残留全删
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:20:34 +08:00
caoqianming
32a8c348a8
ui: 工作目录 combobox 从 datalist 改自定义 dropdown (modal + 顶部 filter)
...
datalist 在 input 非空时下拉被浏览器按前缀过滤丢失,联动 name 后
体验比原 select 还差。改方案:
- 抽 makeFolderCombo({input, panel, onPick}) 工厂:focus/click 显完整列表,
input 子串过滤,mousedown preventDefault 兜住 blur 提前关 panel,
键盘 ↑↓ Enter Esc
- modal nt-wd-sel 和顶部 filter-wd 都接工厂,各自传 onPick
(modal 置 wdManuallyEdited + updateWdHint,filter 走 loadTaskList)
- .combo / .combo-panel 样式提到全局
- 删 <datalist> 元素 + loadFolderSuggestions 灌 datalist 的代码,
ensureFoldersLoaded 改用 state.folders 判断
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:03:37 +08:00
caoqianming
8d7f60d899
ui: 新建任务弹窗工作目录改 combobox + 跟随任务名联动
...
- nt-wd-sel 从 <select> 改 <input list=folders-datalist>,删 "+ 新建目录…"
sentinel 和二级 nt-wd-new 输入框
- 加 wdManuallyEdited flag:name 输入时若未脱钩则同步到 wd;wd 非空输入
置 true 脱钩;wd 清空重置 flag 但保持空(避免 backspace 想换名字时被
立刻填回打断)
- loadFolderSuggestions 只灌共享 datalist,缓存到 state.folders 供 hint
比对"命中已有/新建"
- submit 保留 working_dir || name fallback 兜底空值
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:35:37 +08:00
caoqianming
468cef5fcc
feat: system prompt 注入 task 预选 skill 提示 + imagegen 加 BLOCKING 等 prompt 确认硬约束
...
1) core/agent_builder.py::_build_system_prompt 加 task_skill 参数,非空时在
「工作目录与 task 上下文」段加一行 "- **task 预选 skill**: `<name>`",空
字符串走老路径 prompt 字节级一致。LLM 拿到事实 + general_v1.md 已有
"对应 skill 领域先 load_skill" 规则自然组合 → 主动 load。否决"完整
SKILL.md 预注入 prompt"方案(会把 tasks.skill 升格成 binding,投入产出
比不划算)。
2) imagegen SKILL.md 加 ⛔ 调 seedream tool 前必须把最终 prompt 贴给用户看
+ BLOCKING 等明确确认硬约束:① 顶部流程加确认步骤;② 新增「调 tool 前
的强制门(铁律)」段定义回复分类(可以/OK/画吧/嗯 算确认;改 X → 重贴
重等;沉默 → 继续等;模棱两可 → 追问到明确);③ 新增「调 tool 前再过
一道」段给具体贴 prompt 的对话格式;④ 调用范式段加"前置条件已确认才调";
⑤ 反模式加两条(没贴就调 / 模棱两可当确认)。本质是把模型脑内装配摊
到对话层,装配 ≠ 授权调用。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:59:06 +08:00
caoqianming
02307a08d1
docs(skill): imagegen 补「比例」维度 + 修掉臆造的"只能正方形"
...
用户反馈生图 skill 缺比例引导。原 size 表写"比例只能正方形"是基于
doubao.yaml + tool 参数描述只列三个正方形例子的间接推断,无验证。
改:
- 诊断五维 → 六维,加「比例/尺寸」(ppt 16:9 / 海报 9:16 / 头像 1:1 /
公众号 2.35:1 / 书籍 3:4)
- 追问范式加比例项,上下文推断给 ppt/海报/公众号/学术示意四种用途的默认比例
- size 参数表重写成「按用途选比例 → 再选分辨率」二级表
(1920x1080 / 1080x1920 / 2560x1088 等参考值)
- 删掉臆造的"豆包 5.0 只能正方形"
- 失败解药表加 2 条:比例错改 size 不动 prompt;非方形 API 报错回退默认
- 反模式加 1 条:不问比例就默认走 yaml 1:1 大概率不对
承认 unknown:豆包 5.0 实际支持哪些非方形 size 没验证,首次用错就回退默认 +
让用户协商,不臆造支持列表。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:51:13 +08:00
caoqianming
bfcc231f1e
fix(skill): imagegen 触发修 — system prompt 强制 load_skill + 扩 description 触发词
...
实测"绘制一张办公室的艺术图"没 load skill,两根因:① general_v1.md 「媒体生
成工具」段把 seedream 写成一级直觉(列了"画/出/来张"等关键词 + 直接调 tool 的
how-to),压过 skill discovery block 的微弱声音;② imagegen description 关键
词覆盖窄(没有"画/绘制/艺术图/图片"等朴素词)。
修法:system prompt 那段改成"调 seedream 前必须先 load_skill('imagegen')",
细节判断全移到 skill,只留 ¥0.22 计费 + 不装饰生成 + 不连发三条兜底硬约束;
imagegen description 扩 17 个触发词(画 / 绘制 / 出图 / 来张 / 艺术图 / 写实
图 / 场景图 / 插图 / 封皮 / 头图 ...)。两层联动:一级 prompt 指引到 skill,
二级 description 提匹配概率。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:56:29 +08:00
caoqianming
f2b1ad085b
feat(skill): 新增 imagegen skill 引导用户说清楚生图需求(seedream tool)
...
五步法:诊断模糊度 → 一次性给推断 + 待确认项 → 用户拍板 → 装配 prompt → 调
seedream tool。五维清单(主体/场景/风格/构图/光线)缺 2 维以上就先问;mermaid
vs seedream 选型给"默认倾向 mermaid + 反向选 seedream 信号 + 模糊时主动问用
户"三段式(没在 system prompt 那条流程图优先 mermaid 上一刀切,留 skill 层细
化判断)。size/watermark/search 默认值取舍 + 失败不复发的解药表 + 8 条反模式。
skills/imagegen/SKILL.md 单文件(参考 coding skill 范式);seedream tool 本身
不动,本 skill 仅是流程引导层。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:49:51 +08:00
caoqianming
fe21ca1e8c
ui+api: 登录页加管理员发用户入口 + 删 chat meta 重复的 条/tok 显示
...
- web/auth.py 加 `create_user()` helper(CLI / web 共用)+ `AuthConfig.admin_token` 从
`ZCBOT_ADMIN_TOKEN` env 读,未设 → 接口返 503(功能默关)
- web/app.py 新增 POST /v1/auth/admin/create_user,403/400/409 四分支(口令错 /
邮箱不合法或密码 < 6 / 邮箱占用);main.py user_add CLI 改调同 helper 避免漂移
- web/static/dev.html 登录卡片右下加 ghost link "+ 管理员添加用户" + 弹窗
(email/密码/管理员口令),成功后回填邮箱到登录表单不自动登录;
同时删 chat 顶栏 ${n_messages} 条 · ${tokens} tok 一行(与左 task 列表重复)
- RUN.md 加 ZCBOT_ADMIN_TOKEN env 说明 + 故障表两行;PROGRESS.md 加一条 2026-05-21
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:51:02 +08:00
caoqianming
7bdb6ca5eb
feat(skill): documents skill 接内部材料学科知识库(document_search API)
...
- skills/documents/{SKILL.md, client.py} 4 函数 list_kb / search / download / health
- 走 https://ai.ctc-zc.com:8100/api Bearer 认证;env DOCUMENT_SEARCH_API_KEY + DOCUMENT_SEARCH_URL(可覆盖)
- search 默认返 md_content(整篇 Markdown 50K-200K 字符级),反模式段约束"只 print 前 300 字"防爆上下文
- smoke 实测后校准 SKILL.md:库实质是 7 个材料学科(胶凝/陶瓷/玻璃/晶体/复合/耐火/检验检测,21W+ 文件)预收的英文学术论文 + 跨语言语义检索(原猜"主语料中文"错了)
- 与 research(OpenAlex 全网)互补:documents 已 Markdown 化对 LLM 友好,但仅覆盖材料领域
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:31:21 +08:00
caoqianming
52c25b9404
ui+api: dev SPA SSE 客户端 3 次退避重连 + stream_events 非活跃 task 立即吐 done
...
--reload 重启 / 网络抖时 fetchSse 拆出 consumeSseStream + 包重连壳
(1s/2s/4s,EOF 未见 done/error 触发重连);后端 stream_events 入口检
tasks.run_status,非 running/cancelling 直接关流,避免重连卡在空 broker
无限挂 ping。3 次仍失败 → 卡片末尾红色"请重发"。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:14:51 +08:00
caoqianming
c5dcbb7e24
docs: 精简 PROGRESS.md(每条 1-2 句,~41K → ~7K tokens)
...
已完成关键能力段每条压到 1-2 句:做了啥 + 关键判断;
删掉大段 Tradeoff / 对比方案 / 反方案(git log + DESIGN §7.9 已存档)。
保留全部时间线条目 + 状态表 + 决策表 + 文件清单 + 下一步候选。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 14:40:38 +08:00
caoqianming
f197b06cb4
fix(skill): research fetch_pdf 改走静态直链跟 fetch_xml 对齐(绕开 paper_pdf_view 路径 bug)
...
二次迭代 redeploy + nginx 修 Host 透传后,fetch_xml 5/5 PASS 但 fetch_pdf 仍 5/5 404 —— 同批 paper 同目录 XML 能下 PDF 不能,说明 paper_pdf_view 的 init_paper_path 路径计算 bug(非数据问题)。fetch_pdf 改成读 paper.pdf_url 静态直链 + _stream_to,跟 fetch_xml 同范式;删常量 _PDF。smoke 跑通 5.4MB / 3843ms + 152ms 复用,5/5 候选 100% 成功。
paper_pdf_view 端点本身的 bug 不修(浏览器用户可能还在用),由 paper_server 后续处理;zcbot 不再走它。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 14:30:25 +08:00
caoqianming
a1c0e71703
feat(skill): research list 加 pdf_url / xml_url 直链 + 新增 fetch_xml + smoke 扩 trgm/xml 步
...
paper.py: _LIST_FIELDS 扩到 16(加 publication_date / has_fulltext_xml / pdf_url / xml_url),新加 fetch_xml(id_or_doi, working_dir) helper 走 paper_server media 静态直链(从 paper.xml_url 读,paper_pdf_view 不支持 XML),抽出 _stream_to 共用;fetch_pdf 行为不变。
SKILL.md: 工作流加 "XML 优先 PDF" 原则(已结构化标签 vs OCR 抽取),四函数清单 + 错误处理表更新 fetch_xml / xml_url 空场景。
smoke: 加 step 0 验 pg_trgm 索引速度(>5s 警告 migration 没生效)+ step 4 fetch_xml 多候选轮询 + 复用,step 1 字段集 expected 同步扩到 16。
paper_server 侧改动(serializers pdf_url/xml_url + migration 0006 pg_trgm)见 paper_server 仓库 6a5a5d7b。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:49:34 +08:00
caoqianming
b480147fb2
fix(usage): 顶栏 token 累计修 — sync_task_tokens 改走 messages SUM,删 LLM.TokenCounter
...
5/20 切流式后 LLM.token_counter.add() 只在同步 chat() 里调,流式 chat_stream() 路径从不更新它,
sync_task_tokens 从内存计数器读累计 → tasks.tokens_prompt/completion 一直 0 → 顶栏 0 tok。
DB 验证:5/20 之前 task 数据正常(4568/934),之后 0/0;但 messages.tokens_in/out 一直对
(record_chat_usage 写),source-of-truth 完好,只是冗余汇总列没同步。
改 sync_task_tokens(task_state) 走 SELECT SUM(tokens_in/out) FROM messages WHERE task_id=?,
删 TokenCounter 类 + ConsoleEventSink 的 token_counter 回调 + spinner ctx 尾巴。
一次性 backfill 4 个被影响 task 的累计列。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:39:57 +08:00
caoqianming
d2fd89f3a4
api+ui: 同 wd 并发软警告 banner + /v1/tasks 加 run_status 筛选 + task header @wd 显式化
...
评估 γ(同 wd 单活 gate) / short_id 全产物隔离 / clone task 三方案均判定过度工程 — dogfood 同 wd 基本不并发。走 Claude Code 同款"信任 + 软警告 + 承认 limitation"。后端 /v1/tasks 加 run_status query(逗号分隔 allowlist),前端 selectTask + SSE 收尾两点拉同 wd 活跃 task,有命中挂 #wd-concurrent-warn 黄底 banner(⚠ 项目名 + 邻居 task name + run_status + 等 N 个),不挡发送。renderChatMeta 把 📁 wdName 改为仅 wdName !== taskName 时显示。DESIGN §7.8 风险表文件级悲观锁行(本就未实现)换为 known limitation + 软警告;§7.9 新增取舍条说明三方案为何不选。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:01:21 +08:00
caoqianming
97bcd5ae1e
feat(skill): paper_server → research skill (search / get_paper / fetch_pdf)
...
skills/research/{SKILL.md, paper.py}: 接内部部署 paper_server 的 3 个 helper,LLM 经 load_skill("research") + run_python 调用。范式选 skill 而非 tool/MCP/裸 httpx/lib —— 频次低且 helper 范式让 API 漂移时改一处。tools/run_python.py 注入 PYTHONPATH=base_dir,让子进程能 `from skills.research.paper import ...` 不必折腾 sys.path。base_url 默 http://paper.xxhhcty.xyz:8080,可 PAPER_SERVER_URL env 覆盖。遗留:paper_server 侧 PaperDetailSerializer 加 abstract 字段由用户重新部署。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:00:37 +08:00
caoqianming
5f0f296a23
ui(media): chip 三规则定型 — 工具 I/O 走产物白名单 + 助手正文无条件挂 chip 绕开 seenRels
...
修截图反馈"助手回复 echo 的产物路径没挂 chip"。① 工具 I/O(args/result):chip 抽取只对产物工具(seedream/seedance),通用工具 echo 是引用不该挂;② 产物图/视频:inline 大图;③ 助手正文:永远挂 chip 且 allowInlineMedia=false,只小按钮不重复 inline 大图。SSE 处 upgradeMediaArtifacts 同步 gate 到 isProducer 下。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 08:44:36 +08:00
caoqianming
d402c8771c
ui(media): chip 解绑产物白名单 — 通用工具 echo 路径也挂 chip,图片/视频 inline 仍只对产物开
...
renderArtifactBarHtml 加 allowInlineMedia 参,false 时图片/视频也走 .art-chip 按钮(点开仍弹预览 modal);4 处 tool 调用点解绑 ARTIFACT_PRODUCING_TOOLS chip gate,只透传给第二参控制 inline;SSE 两处 upgradeMediaArtifacts 同步 gate 到 if (inlineMedia);assistant 正文默认 true 不变。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 08:31:39 +08:00
caoqianming
972f36db20
fix(loop): tool message append 补 name 字段 + backfill 历史 — 修历史 task 重开后 seedream banner/chip 不展示
...
session.append 的 tool 消息只存 role/tool_call_id/content,没 name;前端历史渲染依赖 payload.name 判断产物工具白名单 + 抽 elapsed banner,刷新后两者全黑(流式正常因为 SSE event 单独带 name)。scripts/backfill_tool_message_name.py 按 task 走 assistant.tool_calls 建 tool_call_id→name map 回填,dry-run 默认,--apply 真写,幂等。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 08:14:23 +08:00
caoqianming
1e4548dd0c
ui(media): chip 抽取改"产物工具白名单"门控 — grep/read 类工具结果里 echo 的路径不再误挂 chip / 误 inline 图片
...
用户报"生成的图正常预览,但 grep 工具的结果里 figures/ 下另一张老图也被 inline 出来了"。根因:extractArtifactRels + renderArtifactBarHtml 是通用产物展示(image/video → inline / 其他 → 可点 chip),通用工具结果里 echo 的任何带扩展名路径都会被当产物挂出来,seenRels 只能去重同路径挡不住"figures/ 下别的老图首次出现"。修法:加 ARTIFACT_PRODUCING_TOOLS=new Set(["seedream","seedance"]) 白名单,4 处工具 I/O 调用点(renderMessages tool 历史 + assistant tool_calls args + SSE tool_call + SSE tool_result)用 .has(toolName) 三元短路;assistant 正文不门控沿用 seenRels 兜底。chip 本意是"这次工具调用新产出的东西",grep/read 输出里的路径是引用不是产物。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 22:06:01 +08:00
caoqianming
57ac7214e5
refactor(paths): 砍 ROOT 外路径分支 — 写入入口只接 simple name join workspace
...
to_db_path 越界 raise(原 str(pp) 静默存绝对),from_db_path 删 is_absolute 分支只 ROOT/s。ROOT 外分支是防御性死代码:写入入口(POST /v1/tasks → working_dir_from_name)只接 simple name,0002 migration 已清理历史绝对串。DESIGN §7.4 注释同步。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 22:05:32 +08:00
caoqianming
3c2e25d912
api+ui(chat): 删输入框冗余上传按钮 + 加润色按钮 — POST /v1/tasks/{id}/optimize_prompt 走 task 当前模型同步润色,usage_events 新 kind=prompt_optimize 单独记账不污染主对话累计;前端 execCommand insertText 接 textarea 原生 undo 栈,Ctrl+Z 一次回到原文
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 21:20:16 +08:00
caoqianming
febe04a569
ui(media): tool 结果与 assistant 正文同路径 chip/inline 图去重 — Set O(n) + CLAUDE.md 加 "实施前先对方案" 段
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 16:33:47 +08:00
caoqianming
8c9e0d0d3a
api+ui(media): 顶栏生图模型下拉(消息级,不入 DB) + 中间产物图片/视频内联展示
...
- 新加 GET /v1/image_models(扫 config/media/doubao.yaml image 段)
- POST /v1/tasks/{id}/messages body 加可选 image_model 字段,_run_agent_bg
透传到 build_agent(image_variant=...);agent_builder 据此装配 SeedreamTool
variant,不命中 yaml 静默回 fallback,空 → 沿用第一个
- dev SPA:顶栏「模型」旁加「生图」下拉(默认锁第一个 variant,per-session
state 不持久),sendMessage 携 image_model 一起发
- 中间产物 chip 按文件类型分支:图片/视频走 .art-media 异步 fetch blob →
填 <img>/<video controls>(Bearer header 不允许 <img src=> 直 URL);
图片点击仍弹模态放大,视频用浏览器原生 controls;openFilePreview 加
_showVideo + .mp4/.webm/.mov/.mkv/.m4v 进 _EXT_GROUPS;_mediaArtifactCache
按 rel 复用,切 task 时 revoke
- DESIGN 不动(无架构 / schema 变化);PROGRESS / RUN 同步
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 16:18:36 +08:00
caoqianming
a3acb97079
feat(loop+ui): LLM 调用切 streaming — cancel 秒退 + 前端打字机 + 发送/停止合并单按钮
...
- core/llm.py: 加 chat_stream() generator(stream=True + include_usage),
generator finally 关底层 httpx 连接;_build_kwargs 抽出来 chat/chat_stream 共用
- core/loop.py: 主循环 _stream_llm() 流式迭代,chunk 间 poll cancel 命中 break,
litellm.stream_chunk_builder 拼回 response 给 tool_calls 解析 + usage 记账;
content delta 即时 emit text 事件激活前端打字机渲染
- web/static/dev.html: chat-send + chat-cancel 合并 chat-action 单按钮,
setActionMode(idle/streaming/cancelling) 切态;streaming 期间 Enter 不触发停止
- cancel 延迟从「整轮 generation 时长」(几十秒)降到「单 chunk 间隔」(100ms 级)
- 文档:DESIGN §3.1 + API 表 + risks 表翻转 tradeoff;RUN 接口 + 故障兜底同步;
web/app.py docstring 对齐;PROGRESS 加条目 + 文件清单行数
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:46:54 +08:00
caoqianming
c04b8ba05e
feat(media): 接入豆包 Seedream 5.0 图像生成 tool + 0007 cost_usd→cost_cny 全表统一币种
...
- 新 tools/seedream.py:调 ark /images/generations 同步生成,产物落 figures/<ts>-<rand>.png + 同名 .meta.json
- 新 core/ark_client.py:火山方舟 HTTP 封装(base URL + bearer auth + 异常翻译 + download),共享给后续 seedance
- 新 config/media/doubao.yaml:独立命名空间;价格表注释 last_updated + 调价路径说明
- core/storage/usage.py 加 record_image_usage:单价 snapshot 进 units jsonb,防调价污染历史
- agent_builder.py 注册 SeedreamTool:仅当 ARK_API_KEY 设了才挂(无 key 用户无感)
- 0007 migration:tasks/usage_events 双 rename cost_usd → cost_cny,×7.2 一次性折算;
record_chat_usage 内部把 litellm USD 同样 ×7.2 落 CNY,免分类汇总
- prompts/system/general_v1.md 加「媒体生成工具」段,提示按需调用、不主动装饰
- dev SPA tool_result 折叠态显示 banner(model/size/cost/elapsed 徽章),不展开就透明
- scripts/smoke_seedream.py:端到端走通(待 ARK_API_KEY 配齐真跑会产生 ~¥0.22)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:20:34 +08:00
caoqianming
e1f09547e0
api+ui(files): POST /v1/files/delete 加 recursive 字段 — 顶层目录被 task 引用闸 + dev SPA 二次确认显示条目数
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:38:58 +08:00
caoqianming
5ff09b9aca
fs tools 输出 user_root-relative 路径 + dev SPA chip 锚点修正 + assistant 正文也挂 chip
...
- tools/base.Tool: 加 user_root kwarg + _display(p) helper(p 在 user_root 内 → POSIX 相对,外 → 原绝对)
- tools/fs.py: Read/Write/Edit/Glob/Grep 所有结果串里路径都过 _display,不再泄 user_id / 部署根
- core/agent_builder: build_agent 把 user_root 透传给所有 tool(含 ShellTool / RunPythonTool / LoadSkillTool — base 默认 None 不影响)
- tools/skill_tool: __init__ 加 user_root 转传 super
- web/static/dev.html: 新加 _workingDirName helper(从 db 形 working_dir 取末段 + 跳过外部绝对路径);5 个 chip 抽取点统一用它代替原 working_dir 直取 → 根治 chip 点击 404;assistant 正文也接 chip 抽取
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:22:20 +08:00
caoqianming
d1a2961bf4
api+ui(dev SPA): POST /v1/tasks/{id}/clear 清空对话 + 顶栏「清空对话」按钮
...
- 后端: 同事务 SELECT FOR UPDATE 锁 + active run 检查(running/cancelling → 409) +
DELETE messages + reset tasks.tokens_prompt/completion/cost_usd=0 + run_status='idle'
- usage_events 完全不动 — 用户级账单 source of truth 与对话清空解耦;
message_id FK 是 ondelete=SET NULL,task_id/units/cost_usd 全保留可重建累计
- dev SPA 顶栏在导出后插「清空对话」(紫色 hover,介于完成绿/废弃橙/删除红),
running||n_messages==0 → disabled,confirm 二次确认 + 同步刷新 chat-meta / 消息 / 任务列表
- FS 文件保留(沿用 task delete 的"FS 视图可重生"心智)
- RUN.md API 表 + 故障兜底加 409 case;DESIGN.md 不动(无架构 / schema 字段语义变化)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:19:08 +08:00
caoqianming
ecff1d7858
ui(dev SPA): tool_call/result 卡片下加 artifact chip — 点击复用文件预览 modal,免再去右栏找
...
PROGRESS.md 同步。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:08:15 +08:00
caoqianming
c4fac2428b
skills+core(命名约定): task 级宪法文件 <date>-<short_id>-<name>.spec.md + spec_lock → spec 简化
...
同 working_dir 多 task 共享中间产物是设计意图(素材跨本子复用),
但 spec 这种 task 1:1 宪法文件必须隔离 — 否则两本子 spec 直接撞。
文件名三段式:
- task_short_id (task_id.hex[:8],永不变) 主锚 → glob *-<short_id>-*.spec.md 字典序最大 = current
- date 让"重定调"写新文件而非 edit 覆盖,旧版自然成历史快照
- task_name 作建时元数据,改 task.name 不 cascade(由 short_id 兜底定位)
约定由 core/agent_builder.py::_build_system_prompt 单点注入
(task_id / today 实际值嵌入,所有 skill SKILL.md 引用同一份)。
proposal / ppt SKILL.md 阶段一加"glob 检测已有 spec → 询问沿用/重定调"分支。
模板 templates/spec_lock.md → spec.md (git mv 保历史),_lock 后缀无信息量去掉。
未动:DB schema / PATCH /v1/tasks/{id} 改 name 入口 / 其他中间产物扁平共享
/ quality_check.py (--spec 接路径)。反方案(cascade rename / spec 入 PG /
物理 task 子目录)及"何时升级到 DB 化"信号见 DESIGN §7.9。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:03:21 +08:00
caoqianming
775962d68a
ui(dev SPA): 任务行 meta 数字槽位等宽 + 折叠按钮挪 pane-head + rail 模式 + time-ago 锁宽完成跨行对齐
...
- meta 加 tabular-nums + .num 槽位 (min-width:44px + text-align:right) + fmtTokens (1.2k/123k/1.2M)
- .num.right-group 把 [N条][Ntok][time] 整组用 margin-left:auto 推右
- time-ago 加 min-width:64px 锁宽: 整组右锚点稳定后, 跨行"条/tok"后缀才真正垂直对齐
- 折叠按钮挪到 pane-head 紧贴 ↻ 刷新; 折叠态改 VS Code rail 模式 (40px 列 + 只留 toggle 一直可点)
- 删 header #hd-toggle-left 冗余按钮 + header .icon-btn CSS (rail 模式下不需要)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:30:14 +08:00
caoqianming
5b67d29f59
ui(dev SPA): 左 pane 280→320px + header 折叠 toggle + 任务行精简 meta 防 CJK 断行
...
- grid 左列 280→320px (从 chat 借 40px), 任务名 / 描述 / wd 更舒展
- header 最左 toggle 按钮: body.left-collapsed → 列归零 + #pane-left display:none, chevron 翻向, localStorage 持久化
- 任务行 meta 删 id8 (挪到 row title 仍可查) + 各 span white-space:nowrap + badge/time-ago flex-shrink:0
- wd/desc 副行恢复 inline overflow:hidden ellipsis (单文本带不是 flex 子元素)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:18:42 +08:00
caoqianming
afebf25d79
ui(dev SPA): 任务列表 pager bar → 滚动加载(IntersectionObserver sentinel)
...
- 删 #task-pager / renderPager / resetPageAndReload / btn-prev|next 三件套
- 加 #task-sentinel + IO root=#pane-left + rootMargin 200px 提前触发
- loadTaskList({append}) 双语义: reset 抢占(_taskLoadSeq 丢弃过期响应) / append 互斥
- renderTaskList(append=true) 不 clobber 已渲染行, 事件 handler 只挂新行
- 首 pane-head 加 "共 N 个" 总数小字补偿丢失的 pager-info
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:09:46 +08:00
caoqianming
97d838a9ec
ui(dev SPA): 任务列表行加最近操作时间(updated_at 相对显示) + 新建弹框工作目录改 <select> 下拉
...
- 列表行 meta 加 fmtTimeAgo helper(刚刚/N 分钟前/N 小时前/昨天 HH:MM/MM-DD/YYYY-MM-DD), title 出完整 locale 串
- 新建任务工作目录: input + datalist → <select> 既有目录列表 + "+ 新建目录..." 展开 text input(保留新名建目录能力)
- folders-datalist 保留供左 pane filter-wd 继续 autocomplete
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:57:16 +08:00
caoqianming
7fc9570ffe
model: 加 GLM 5.1 档案 (zai provider + 国内站 bigmodel.cn)
...
- config/models/glm.yaml: family=glm, variant=pro, model_id=zai/glm-5.1,
api_base 覆盖到 https://open.bigmodel.cn/api/paas/v4,env ZHIPUAI_API_KEY
- thinking_mode 暂不开:GLM 协议是 body {"type":"enabled"} 而非
reasoning_effort 等级,需 core/llm.py 加 family 分支才能透传,留 TODO
- requirements.txt: litellm 下限 1.50→1.83(zai provider PR #17307
merge 后才内置,旧版不识别 zai/ 前缀会炸)
- RUN.md: .env 例子加 ZHIPUAI_API_KEY,probe 命令加 glm.pro
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:32:15 +08:00
caoqianming
337b8896a6
files(dev SPA): UX 翻面 — 主区去 checkbox / 黄 bar,改 [选入到此处] 弹框 + 拖拽上传 + 修全局 input width bug
...
主区从 select-then-pick-dest 改 at-dest-pull-sources:用户切任务时主
区已自动跳 working_dir,destination-first 比 source-first 少一次心智
切换。pane-head 加 [选入…] → 弹框跨目录勾源(Set<rel> 切换路径保留)
→ 底部 [复制到此处] / [移动到此处] 落到主区当前 state.filesPath;弹
框浏览 == 主区路径时同目录 checkbox 灰禁(挡 409)。整个 pane-right
成 drop zone,Files 类型才响应 + dragenter/leave 计数防子元素冒泡闪
烁,落点用 state.filesPath 沿用 /v1/files/upload。
根因 bug:全局 input{ width: 100% } 把新加的行 checkbox 撑成全行宽,
.name(flex:1; flex-basis:0)被挤成 0 宽 — 用户报"看不到文字"的元
凶。修法 selector 排除 [type=checkbox]/[type=radio]/[type=file]。
按 CLAUDE.md 不留兼容:state.selectedFiles / syncBulkBar / dirPicker
/ files-selall / files-bulkbar / row-cb / .file-row.selected 整套删
干净。后端 /v1/files/copy /v1/files/move 一行没动,前端用同样的
{paths, dest_dir}。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:59:21 +08:00
caoqianming
0c5dd3b176
files(dev SPA): /v1/files/copy + /move 跨目录批量搬动 + 多选 + 目录选择弹框
...
后端两路由共用 _validate_transfer 预检 helper(批量原子校验:同名 409 不
覆盖、不自嵌套、不重名、target 已存);move 加闸"顶层目录是某 task
working_dir → 409"维持 working_dir = top-level invariant,copy 无此闸
(新副本无 task 关联)。dev SPA 文件行加 checkbox + 顶栏全选三态 + 黄底
toolbar(复制到/移动到/取消),目录选择弹框复用 /v1/files 浏览。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:29:59 +08:00
caoqianming
7925dcef54
files: working_dir 视为可重生 FS 视图(DELETE task 顺手清空孤儿 + delete_file 去 task-ref 闸)
...
DB 是 source of truth,FS working_dir 可独立删 / 用户手删 / 跨机器迁移丢失,
下次 build_agent 自动 mkdir 重建。三处改:
- core/agent_builder.py: working_dir.mkdir(exist_ok=True) 从 if not resume:
里挪出,resume 也兜底建目录
- web/app.py DELETE /v1/tasks/{id}: 删完后若同 user 无其他 task 引用 +
FS 空 + ROOT 内相对路径 → best-effort rmdir 清孤儿;外部 --working-dir
(DB 绝对串)静默跳过
- web/app.py POST /v1/files/delete: 顶层目录去掉"有 task 引用 → 409"闸,
允许独立删空目录,task.working_dir 字段不动
smoke case 4 改 200 + working_dir 不变;新增 case 8(空目录自动清)/
case 9(非空保留),全 9 pass。PROGRESS / RUN 跟着更。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 08:10:36 +08:00
caoqianming
781a216ca6
model: 同 task 内切模型(c 模式 task 级 / A 粒度)+ usage_events v2 表(0006); GET /v1/models; 前端顶栏下拉 + 历史 model 切换点小标
...
- DB(0006): messages 加 model_profile 列(assistant 行有值); 重建 usage_events 表 v2 形态(event_id/user_id/task_id/message_id/kind/model_profile/units jsonb/cost_usd + 三索引), 0004 删的旧 schema 字段不够多态; tasks.tokens_prompt/completion/cost_usd 保留作粗概览
- ModelCapabilities 加 display_name; deepseek_v4.yaml flash/pro 各填名
- GET /v1/models: 扫 config/models/*.yaml 列可选项(profile/display_name/family/thinking_mode/is_default); POST /v1/tasks + PATCH 接受 model_profile(不传 → cfg["default_model"]; 校验走 ModelCapabilities.load 失败 400)
- build_agent: resume 时优先 task.model_profile 而非 cfg default; AgentLoop 加 user_id 透传, 每轮 assistant 入库后调 record_chat_usage(litellm cost map 算钱, 失败吞掉 emit warn 不阻 loop)
- core/storage/usage.py 新文件: record_chat_usage(双写 messages.tokens_in/out + model_profile + insert usage_events 一行)
- session.append() 返回 message_id(供 usage 关联)
- 前端 dev.html: chat-meta 加模型下拉(切了 PATCH + running 中提示"跑完后生效"); 新建对话框 modal 加 nt-model select; renderMessages 按 model_profile 切换点画小标 "── DeepSeek V4 Pro ──"
- CLAUDE.md: 加"开发测试期 / 不删现有数据 / DROP COLUMN 两种情况"规则
- DESIGN §7.4 schema 加 messages.model_profile + usage_events v2 段; PROGRESS 加 0006 条目 + 文件清单
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:43:13 +08:00
caoqianming
e7f9b005bd
doc: 精简 DESIGN/PROGRESS/RUN(总 162KB → 66KB,PROGRESS -80%)
...
PROGRESS 每条压 2-4 句去 smoke 列表 / 文件清单 / 行数明细;DESIGN 清重复 + 删反方向已废弃的取舍段;RUN 压命令注释 + 修 db current 应输出 0005(原写 0004)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 14:20:48 +08:00
caoqianming
2baed6894b
auth(dev SPA): 邀请码撤回 邮箱+密码 (users.email/password_hash bcrypt; 0005 加 UNIQUE; user add CLI; 登录两 tab)
...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 13:58:48 +08:00
caoqianming
53f59eb78a
auth(dev SPA): 邀请码登录(invites 表 0005) + SENTINEL user 彻底撤
...
- 新增 POST /v1/auth/login_invite {token}: dev SPA 给同事试用,token → name → uuid5(NS, name) 推导 user_id;原 /v1/auth/login 保留为 platform 机器对机器入口
- 0005 migration 新表 invites(token PK / name UNIQUE / created_at);先用 ZCBOT_INVITES env 试了一版,讨论后升级到 DB 表 — schema 极薄,不入 user_id (uuid5 推导),不入 revoked_at (DELETE 即撤销);管理直接 SQL,后期可加 main.py invite CLI
- web/auth.py: 删 _parse_invites / AuthConfig.invites / env 读取;新模块函数 resolve_invite(token) 每次 SELECT,无缓存避免 DELETE 后还能登
- SENTINEL_USER_ID 常量 + ensure_local_sentinel 函数 + agent_builder fallback 全删 (CLI 撤后无 caller);storage/utils.py 三函数 user_id 改必填;TaskState 加 user_id 字段;build_agent user_id 改 KEYWORD_ONLY 必填;session.py 删多余 ensure_local_task_row (task 行 web 入口已 INSERT)
- DB 清: SENTINEL 行 + 5 个 dev task + 307 messages + workspace/users/00000000.../ 全删
- dev.html: 登录页 2 格 (uuid+key) → 1 格邀请码,header 显示 name·uuid 前 8 位
- 文档全套同步: RUN/DESIGN/PROGRESS
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 13:14:31 +08:00
caoqianming
f61503fbdb
ui(dev SPA): 任务/文件行 ⋯ 下拉菜单 + 顶栏长名截断 + 聊天上传按钮 + 工具调用刷新右侧
...
- 单例浮层菜单 (position: fixed) 避开 pane overflow 裁剪
- 任务行 ⋯:完成/废弃/导出 docx/删除 (4 色, 按 status/消息数 disabled)
- 文件行 ⋯:重命名/下载(仅文件)/删除, 替代原内联按钮
- pane-head .label 加 nowrap+flex-shrink:0;files-proj 长项目名 11 字截断+title 全名
- chat-upload 复用同一 upload-input, 上传到右侧当前目录
- tool_result 触发 scheduleFilesRefresh (debounce 500ms)
- 重构 setTaskStatus/deleteTask/exportTask 接 tid 参数, 中间 pane 按钮共用同组函数
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 10:50:45 +08:00
caoqianming
fafcb14d86
skill(proposal): mermaid 文件名 hash→caption + quality_check 加图相关 4 拦截 + SKILL.md 精简; web cache fix
...
用户报"图没渲染到 docx",诊断后修三件事(同一根因链):
- web/app.py /v1/files/download 加 Cache-Control: no-cache
Starlette FileResponse 只发 ETag/Last-Modified, 浏览器走启发式缓存,
workspace 文件改了 SPA 预览看不到新版
- quality_check 新 check_figures(): 4 条规则
1) figures/ 有 png 但 sections 0 个 ![]() 引用
2) fenced 代码块出现 box-drawing 字符 (┌─┐│└─┘ 等)
3) mermaid 块必须有首行 %% caption: <题>
4) 同 task 内 mermaid caption 不能撞名
- render_diagrams.py: hash → caption 命名
pass-1 验证 caption 完整 + 全 task 唯一, 缺/撞 退 2
pass-2 渲染落 fig_<sanitized>.png, 总是覆盖
- render_docx.py: mermaid 块按 caption 查 fig_<caption>.png
无 caption / 清洗空 / png 缺 → ASCII fallback
- SKILL.md ~193 → ~160 行:
插图段 49→22 行(压 matplotlib 细节 + 删类型选择展开)
反模式合并 ASCII/占位/手写图编号/缺 caption/撞名
删"为什么两段式"长说理段
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 10:19:09 +08:00
caoqianming
3ca37f7041
doc(PROGRESS): 05-19 dev SPA 文件预览弹框
...
加 05-19 条目 + 文件清单更新 dev.html 行数 + 加 web/static/vendor/ 一行。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 08:25:30 +08:00
caoqianming
d6fc004367
skill(proposal): mermaid 管线 + render_docx 图片插入 + 图题自动编号
...
新增 render_diagrams.py 把 ```mermaid``` 块预渲染到 figures/fig_<sha1>.png
(优先本地 mmdc, 回退 mermaid.ink 公网 API, 都失败留 WARN 不阻塞);
render_docx.py 加  识别 + mermaid 缓存查找, 缺缓存自动 ASCII fallback,
图题"图 N <caption>"全局自增, 替换原模板里的 [图 2-2 ...] 裸占位写法。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:37:16 +08:00
caoqianming
9aa2efc335
core(/v1/files): 加 rename + delete 顶层加 task 引用闸
...
- POST /v1/files/rename:任意深度;path 是顶层目录则 DB-aware
(FOR UPDATE 锁 task / 活跃 run 互锁 / check_no_subtask exclude /
UPDATE working_dir 先于 FS rename,FS 失败回滚)
- POST /v1/files/delete:顶层目录 + 有 task 引用 → 409,杜绝悬空
- check_no_subtask 加 exclude_task_ids,rename 平移自己不误判嵌套
- dev SPA:file row 加改名按钮,顶层改名后刷任务列表 + 当前 task header
- smoke 7 case 全绿(scripts/smoke_files_rename.py)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:06:21 +08:00
caoqianming
952a377017
prompt(skill 机制): 永远 load → 可选辅助,通用任务不必硬套
...
skill 字段在新建任务时已可留空(本轮 /v1/skills 下拉落地),原"永远 load 一下"
对简单问答 / 改 bug / 文件操作等通用任务过激;改为"Skill 是可选辅助",明确通用
任务直接用通用工具,匹配到 skill 领域再 load。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 16:27:30 +08:00
caoqianming
b057c3bd06
core(/v1/skills + dev SPA): GET /v1/skills + 新建任务弹窗 skill 字段改下拉
...
- web/app.py: lifespan 启动扫一次 SkillRegistry 挂 app.state;新增 GET /v1/skills(JWT 鉴权)
- web/static/dev.html: nt-skill input → select + loadSkillOptions 缓存到 state.skills
- PROGRESS: 记录
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 16:27:10 +08:00
caoqianming
f9311b069c
ui(dev SPA): 菜单 / 按钮 / 状态 / 弹窗文案全部中文化
...
login / header / 三栏 label / chat 按钮 / new task modal 静态文案 +
renderTaskList / renderChatMeta / fetchSse / 弹窗等动态文案全套本地化。
状态码 active/completed/abandoned 显示为「进行中/已完成/已废弃」,
role user/assistant/error → 我/助手/错误。
技术字段(user_id / platform_key / UUID / tok / CSS class / SSE event 名)
保持原状,不影响 UI 中文。Smoke 13 个中文标签全在 + 8 个英文按钮无残留。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 15:44:50 +08:00
caoqianming
0d127a7261
core(入口归位): cli.py→main.py, 原 main.py→core/agent_builder.py, 删 REPL
...
按 §5 Less Scaffolding + SoC 把混三角色的 main.py 拆开:入口归位到 main.py,
装配 lib 归位到 core/agent_builder.py。dev SPA 落地后 CLI REPL(chat/tasks/
export)与 web /v1 等价,维护双套 task 切换语义只是"对称美",一并撤(§7 E
CLI 双模式路线同样撤)。
- cli.py → main.py(入口,只剩 web/db/probe 三 click 命令组)
- 原 main.py → core/agent_builder.py(build_agent / system prompt /
validate_task_name 装配 lib;顺手删死代码 _resolve_uuid_or_prefix +
resume "last" 分支)
- 删 chat/tasks/export 三 REPL 命令 + _cleanup_if_empty / _list_task_rows
等 CLI-only helpers ~400 行
- web/app.py 5 处 from main import → from core.agent_builder import
- DESIGN §1/§2/§3.3/§3.6/§7.0/§7.6/§7.7/§7.8/§7.9 + RUN + PROGRESS 全套同步
- Smoke 6 case 全绿(in-process TestClient + 子进程 python main.py db current)
- 净减 486 行
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 14:10:59 +08:00
caoqianming
2e519ab8a6
core(0004): 删 runs / usage_events 表 + cancel/SSE 改 task-level
...
usage_events 全代码库零引用,纯死代码;runs 表实质就是"task 当前 in-flight 状态"
影子表,tokens_p/c 写但从未被读,run_id 唯二实用是 broker 频道键 + cancel 参数 —
单活 run 形态下完全冗余,客户端只需 task_id。按"开发期不写兼容层"心智一把切干净。
- alembic 0004:DROP runs / usage_events,tasks 加 run_status (idle/running/cancelling/error) +
run_error 两列;ok/cancelled 终态都回 idle 不留持久标记,只有 error 持久
- ORM 删 Run / UsageEvent class
- Broker 全 task_id 索引,加 start(tid) 在新 run 前清 _done
- /v1/tasks/{tid}/runs/{rid}/{events,cancel} → /v1/tasks/{tid}/{events,cancel}
- POST /messages 返 {events_url} 去掉 run_id
- dev SPA: state.currentRunId → state.streaming(bool),路径去掉 /runs/{rid}/
- Smoke 18 case 全绿
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 11:05:35 +08:00
caoqianming
bf41631437
core(run cancel): POST /runs/{rid}/cancel + AgentLoop 协作式 cancel + dev SPA stop 按钮
...
落地 DESIGN §7.2 原标"待"的 cancel 路由 — 等待回复 / LLM 操作时也能中断。
- broker 加 request_cancel / is_cancelled / clear_cancel(per-run threading.Event)
- AgentLoop 加 cancel_check 回调,每轮 LLM 前 + tool_calls 之间 poll;命中给
未执行 tool_call 补 [cancelled by user] tool result 保 LiteLLM 协议,emit
cancelled event
- 单活 gate + 启动 reaper 扩到 running | cancelling
- BG 装 cancel_check + 终态判 cancelled/ok + finally clear flag
- dev SPA stop 按钮 + cancelled badge + fetchSse try/finally 失败路径复原 UI
LLM 同步 call 本身不可中断,最坏等当前一轮跑完(通常几十秒)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:42:45 +08:00
caoqianming
976ef45e87
core(POST /v1/tasks/{id}/messages): 同 task 单活 run 锁 + 启动 reaper
...
挡住"用户连点 send 两条 → 两个 BG 线程争 messages.idx UniqueConstraint
race"的旧 TODO。POST /messages 把所有权 + 活跃 Run 检查 + 新 Run INSERT
收进一个事务,首步 SELECT Task … FOR UPDATE 锁 task 行,命中 running 已
存在则 409。lifespan 加 stale-run reaper,把进程 crash 留下的孤儿 running
标 error,避免对应 task 被 409 永挂。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:27:48 +08:00
caoqianming
9a7620f704
core(files API): user-rooted /v1/files*,去掉 task_id 前置
...
文件操作语义上只关心"路径 + user 边界",task_id 是多余拐杖;
同时 §7.1 心智模型把 task 和 dir 定义为正交副视图,API 不该混。
- 4 路由 /v1/tasks/{id}/files* → /v1/files*(列/下载/上传/删)
- 边界从 task_dir 改 user_root (workspace/users/<uid>/)
- dotfile 一律过滤(.memory/ 等系统区不暴露)
- dev SPA:登录即拉 user_root,选 task 自动跳到其 working_dir,
crumbs root 标"我的",新增 upload 按钮
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 07:59:19 +08:00
caoqianming
0c577ba0a5
core(GET /v1/tasks): 分页 + 多维筛选 + ordering 排序
...
- 响应壳固定 {page, page_size, count, results}
- 6 个 query 参数:page(1-based) / page_size(1-100 clamp) /
status / skill / working_dir(末段名,后端拼前缀比对) /
q(name + description ILIKE)
- ordering DRF 风格逗号分隔,-field 倒序;allowlist
created_at/updated_at/name/status;非法字段静默丢弃;**默认 -created_at**
- 单次 COUNT + 单次 SELECT LIMIT/OFFSET,无 N+1
- dev SPA:task pane 三段头(status + 刷新 / q + working_dir / ordering),
prev/next 翻页 + "from–to / count (第 P/L 页)" + 输入 debounce 300ms +
默认 -created_at 不发到 URL(参数干净)
- DESIGN §7.2 / RUN 路由表 / PROGRESS 同步
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 19:26:29 +08:00
caoqianming
4a6aaaf34d
core(0003): name + working_dir + skill schema 重构 + per-user .memory
...
- alembic 0003: TRUNCATE tasks CASCADE + task_dir→working_dir + mode→skill + 加 name TEXT NOT NULL
- name(必填,任务显示名,UI / docx 用)与 working_dir(可选,留空 fallback 用 name 作目录)解耦;
同 working_dir 多 task 共享物理目录(§7.1)
- skill 字段对齐 skills/ 注册表语义,后续可下拉强校验
- POST /v1/tasks {name(req), working_dir?, description?, skill?};
PATCH 支持改 name/skill;新增 GET /v1/folders(FS 列表 + n_tasks + last_used)
- DELETE /v1/tasks/{id} 硬删 DB(messages CASCADE)+ FS working_dir 保留;
dev SPA 加 task delete 按钮 + file per-row 删按钮
- 工作目录改 eager mkdir(取代懒创建):用户给 name 即声明项目,目录立刻存在
- dev SPA modal 拆"任务名" + "工作目录"(<datalist> autocomplete 走 /v1/folders +
输入实时提示"复用 / 新建 / fallback");renderTaskList 主行 = t.name,副行 = 📁 + skill + desc
- files 面板 UX:pane-head 显示项目名 + crumbs root 用项目名 + 修 root 处多渲 "." crumb 的 bug
- 顺手:memory 搬 workspace/users/<uid>/.memory/(per-user dotfile 隔离);
CLI --mode → --skill,--name + --working-dir 分开
- DESIGN §3.1 / §3.6 / §7.2 / §7.4 + PROGRESS + RUN 全量同步
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 19:15:37 +08:00
caoqianming
02a69058df
core(§7 D + D'): /v1 JSON API + PLATFORM_KEY→JWT auth + dev SPA
...
整合今日累积的 §7 D 阶段主体工作:
- §7 D /v1 JSON API:删 web/templates/* + web/static/style.css,
web/app.py 重写为纯 /v1 JSON 路由(tasks CRUD + messages +
SSE 事件 JSON 化 + files 4 路由 + export),CORS allow_origins
起步 *,GET / 改 302 → dev SPA(详 DESIGN §7.9)。
- §7 D' 过渡 auth:web/auth.py 新增 — PLATFORM_KEY env(共享密钥)
+ JWT_SECRET env(HS256 签),POST /v1/auth/login 校验 key → 签
JWT(默 7d TTL),所有 /v1/tasks* 走 Depends(require_user) 验签
并按 user_id 隔离数据;豁免 /healthz、/docs、/openapi.json、
/static/*、/v1/auth/login。env 双必填,缺则 fail-fast。
- dev SPA:web/static/dev.html ~600 行 vanilla JS 单文件,login
overlay(user_id 默 sentinel + platform_key)+ 3 栏布局(task
list + chat 流 + files 浏览)+ new-task modal + done/abandon/
export。SSE 走 fetch+ReadableStream(EventSource 不支持 Bearer)。
- task_dir 改相对存储:新增 core/paths.py(to_db_path/from_db_path)
+ alembic 0002 migration 把 ROOT-内绝对路径转 posix 相对,跨 OS
和混合分隔符历史数据天然兼容。check_no_subtask 改 Python 端归一
比对,逻辑更清晰。
- litellm 启动 cost map 网络警告兜底:core/llm.py 在 import 前
setdefault LITELLM_LOCAL_MODEL_COST_MAP=True,墙内冷启动 ~5s →
<1s。
- docs:DESIGN §7.3 改写(过渡 auth + 真 OIDC 路线)+ §7.7 状态表
+ §7.9 dev SPA 取舍;PROGRESS 加多条今日条目 + 文件清单 + 下一
步;RUN env 双 auth env + curl 示例 + 路由表 Auth 列 + 5 条故
障兜底新条目。CLAUDE.md 加"开发期不写兼容层"心智。
Smoke 全绿:env fail-fast / 8 路径无 token 全 401 / login 3 分
支 / 带 token CRUD / 跨 user 4 case 隔离 / token 异常 4 case /
真实 HTTP uvicorn 端到端 login + bearer call + dev.html 服务。
requirements: 加 pyjwt>=2.8.0;删 jinja2 / markdown-it-py /
mdit-py-plugins / pygments(模板路线撤一并清);保留 python-
multipart(files upload 还用)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 16:14:25 +08:00
caoqianming
1035b12847
core(§7 Phase G G6/new): Web 端新建 task 入口
...
提前于 G5 落地 — 用户反馈 Web 没"开启新对话"的地方。
- GET /new 渲染 new_task.html 表单(description / mode / task_dir 三字段);
- POST /new:strip 校验 + description 与 task_dir 至少填一个否则 400 +
check_no_subtask 同 CLI / build_agent 一致拦前缀嵌套 → 409 +
ensure_local_task_row 写占位行 + 303 See Other 跳转 /tasks/{tid};
- task_dir 空 → 默认派生 workspace/tasks/<uuid>/(同 _default_task_dir),
显式 → Path.expanduser().resolve() 同 cli.py --task-dir;
- 模板 new_task.html:三字段表单 + error 渲染(400/409 重渲带 form_state
不丢用户填的值);home.html 加 + new task 主按钮;base.html 默认 nav
也带 tasks/new 链接;
- CSS:.btn-primary 商务红主按钮 / .new-task-form 表单 + focus / .navlinks
.active 当前页高亮 / .head-actions flex 容纳 filter + new 按钮;
- 懒创建保留语义:Web /new 入库占位,后续 build_agent 走 resume(已存在
不冲突);CLI REPL 仍走 build_agent 懒创建路径,两路互不干扰。
Smoke 21 路径全绿:GET 表单 200 + 三字段 / POST happy(description-only
和 custom task_dir)→ 303 + Location 正确 / DB 行字段对 + default-derived
task_dir 含 uuid / 空+空 → 400 重渲表单带 error / no-subtask 父子嵌套 →
409 + 错误文案 / home 页 + new task 按钮 + nav 链接 / /new nav active 标记。
版本 0.4 → 0.5。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 09:51:35 +08:00
caoqianming
7356d25652
core(§7 Phase G G4): chat 发送 + SSE 流式回复
...
- web/broker.py RunBroker:in-process pub/sub,subscribe/emit/close/
unsubscribe;同 run_id 多订阅者 fan-out(刷新 / 多 tab / 桌面+移动
都能同时看流);_done 集合让晚到订阅者立刻收 done(不挂)。
- web/sinks.py WebEventSink:实现 §7 A sink 协议,把 AgentLoop._emit
桥到 broker.emit(run_id, ev),AgentLoop 完全不知 web 存在。
- 异步策略 = asyncio.to_thread(不改 core):POST /tasks/{tid}/messages
async handler → INSERT runs 行 + asyncio.create_task(to_thread(
_run_agent_bg)),_run_agent_bg 工作线程跑 build_agent + agent.run,
sink 通过 loop.call_soon_threadsafe 跨线程把 event 桥回 asyncio queue。
- GET /tasks/{tid}/runs/{rid}/events:StreamingResponse async gen,
响应头 text/event-stream + Cache-Control: no-cache + X-Accel-
Buffering: no(nginx 反代友好);第一帧 retry/connected 让 ES 立
即建立,30s 无 event 发 : ping 心跳。SSE multi-line data 每行加
data: 前缀(SSE spec),客户端 ES 自动还原 \n 拼接的 HTML。
- _render_event_fragment 渲染 text/tool_call/tool_result/error
HTML 片段;run_start/llm_start/llm_end/done 发空 data(只让客户端
识别 event type)。
- 新模板:_frag_text/_frag_tool_call/_frag_tool_result/_frag_error +
_send_response(POST 响应:user msg 卡 + msg-assistant streaming
容器带 sse-connect/sse-swap/sse-close)。
- chat.html 加 send 表单(Enter 发,Shift+Enter 换行,HTMX hx-post /
hx-target=#chat-stream / hx-swap=beforeend / 提交后 reset);chat
section 改 id=chat-stream;非 active task 隐藏表单。
- CSS:.streaming .run-indicator 红点脉冲 / .send-form 输入框 /
.tool-result-inline 追加式样式 / .msg-error 错误卡。
- runs 表写状态:POST 时 status=running,正常完结 ok + tokens_p/c,
异常 error + error 文本(DB 写失败不放大噪声,已 emit error 给前端)。
- lifespan bind_loop(asyncio.get_running_loop()) 让 broker 拿到
loop 引用,emit 跨线程才能 call_soon_threadsafe。
- RUN 故障兜底加 3 条:SSE 经 nginx 卡住、浏览器 send 无反应、并发
POST messages idx 冲突(已知 TODO)。
Smoke 双层全绿:
- broker 单元 8 case (subscribe/emit/get/fan-out/跨 run 隔离/close/
late subscribe instant done/unsubscribe/未 bind silent drop)
- 端到端 24 case (POST 200 + sse-connect/run_id 抽取 + content-type/
x-accel-buffering/cache-control 头对 + event 序列 run_start→done
+ text 片段 <strong> + tool_call <details> + tool_result preview
+ empty body 400 + 各种 404 + late done + runs 行 INSERT)
版本 0.3 → 0.4。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 09:19:25 +08:00
caoqianming
514d36c481
core(§7 Phase G G3): chat 只读页 + markdown + tool 折叠
...
- web/app.py 加 _get_md() 单例 MarkdownIt(gfm-like + linkify +
breaks,html=False 禁内联 HTML 防 XSS),fenced code 走 pygments
_pygments_highlight 回调(codehilite cssclass)。
- load_chat_messages(tid):PG idx asc 读 messages。
build_chat_blocks(messages):system / tool 不入 block(tool 内嵌进
assistant.tool_call.result),user / assistant text 走 md 渲染,
orphan tool_call → [no result]。_args_preview 60 字截断,
_pretty_json 解析失败 fallback 原串。
- /tasks/{id} 渲染 chat.html;删 task_placeholder.html。
- chat.html:.msg 卡片(user 浅蓝 / assistant 白底),tool_call 用
<details> 默认折叠(无 JS,浏览器原生);summary 显示 tool 名 +
args 前 60 字预览,展开看 args_pretty + result。
- CSS 加 .body 内 markdown 元素样式(table / blockquote / code / pre
/ strikethrough)+ .codehilite 浅色 token 配色(keyword/string/
comment/function/number/operator,余下黑色)。
- requirements: markdown-it-py[linkify] / mdit-py-plugins / pygments。
Smoke 28 路径全绿(in-process Starlette TestClient):4 display
blocks aggregation + GFM 特性(table/fence/autolink/strikethrough/
bold)+ tool 配对(命中 + orphan [no result])+ HTML 含 <details>/
tool-badge/codehilite/<s> + 空 task 文案 + invalid UUID 404 + util
单测(args_preview/pretty_json/render_md 边界)。版本 0.2 → 0.3。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:38:31 +08:00
caoqianming
80a658eba4
core(§7 Phase G G2): task list 页 + /tasks/{id} 占位
...
- web/app.py 加 list_tasks(limit, status):PG tasks + messages count,
updated_at 降序,返回模板友好 dict。Web 与 cli.py 数据形状不一致
(CLI 用 tuple,Web 用 dict),不预付抽象,等真有 schema 同步成本
再抽。
- / 路由换成 task 列表,支持 ?status=active|completed|abandoned
filter(无效值静默降级 all)。/tasks/{task_id} 占位路由:UUID 解析
失败 → 404,DB 不存在 → 404,有效则渲 task_placeholder.html(G3 来填
消息流)。
- Linux portability:_norm_path() 显示前 replace('\','/') 把 Win
存的 backslash 归一,Win Path.resolve()-str → "D:/..." 显示;Linux
forward-slash 原路通过。Path.as_posix() 在 Linux 读 Win backslash
串时不归一,所以选 replace 而非 as_posix。
- 模板 home.html 表格(id/updated/status/mode/model/msgs/tokens/desc-dir)
+ status badge 配色(active 绿 / completed 蓝 / abandoned 灰) +
filter 表单 + 空态文案。task_placeholder.html 渲染 G3 提示。CSS
tabular-nums 数字对齐 / hover 高亮 / accent-soft note。
Smoke 18 路径全绿(in-process Starlette TestClient):3 task seed
(active/completed/abandoned)+ Win\Linux 双路径形态 → / 渲染对、
status filter 正反向、garbage status 静默 all、UUID 占位、notauuid
404、ghost UUID 404、limit 生效、/healthz 不退化。版本 0.1 → 0.2。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:52:11 +08:00
caoqianming
91202b6172
core(§7 Phase G G1): Web UI 脚手架 + cli.py web 子命令
...
- web/ 新包:app.py FastAPI 工厂(/ + /healthz + /static),Jinja2
base.html / home.html,minimal style.css。HTMX + HTMX-SSE 走 CDN
(无 node 链路,与 §5 Less Scaffolding 一致)。
- cli.py 加 web --host --port --reload 子命令,默认 127.0.0.1:8765,
本地形态 sentinel user 无 auth(Phase D 才上 OIDC)。
- requirements: fastapi / uvicorn[standard] / jinja2 / python-multipart
(multipart 为 G5 文件上传留)。
- Starlette 新签名踩坑:TemplateResponse(request, name, context),
旧式塞 context 里会让 jinja 用 dict 当 cache key 炸 unhashable,记
RUN.md 故障兜底。
- Linux portability:模板 path 显示约定 .as_posix();SSE 头 G4 上时
带 X-Accel-Buffering: no(nginx 反代友好)。`cli.py web` 在
.venv/Scripts/python.exe(Win)/ .venv/bin/python(Linux)走同一路径。
Smoke 四路径(in-process via Starlette TestClient)全绿:/healthz →
"ok" / / → 1063B(title + static + version)/ /static/style.css →
1624B / /nonexistent → 404。`cli.py web --help` 子命令注册 OK。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:37:54 +08:00
caoqianming
e8dbfa57a5
core(§7 B Step 6): no-subtask 前缀嵌套校验
...
- core/storage/utils.py 加 check_no_subtask + NoSubtaskError;PG LIKE
双向(new LIKE existing/% OR existing LIKE new/%),同 task_dir
允许(同项目多对话),空 / whitespace 跳过。
- 分隔符容差:SQL replace(task_dir, '\', '/') 把存的 Windows 反斜杠
与新值统一到 '/' 再比;backslash 通过 bind 参数传,绕开 SQL 转义。
- main.py::build_agent 在 resolve_task_id 后、TaskState 构造前调,
if not resume 单层闸 —— resume 跳过(改名走未来 Folder API cascade).
- cli.py 三处 build_agent 调用现有 try/except 直接接住 NoSubtaskError.
- PROGRESS / RUN 同步:Step 6 完工,故障兜底加一条 NoSubtaskError 处理.
Smoke(9 路径 + e2e 3 分支)全绿。§7 B 完工(Step 5 取消)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:59:37 +08:00
caoqianming
2b3692c8bf
core(§7 B Step 4): --task-dir 双形态 + RUN.md 运行手册
...
- CLI `chat --task-dir <path>` 让用户显式指定项目目录(§7.1 task-primary +
dir 副视图心智模型落地);留空走默认派生 workspace/tasks/<uuid>/。
- main.py::resolve_task_id 加 task_dir_arg;resume 时从 PG tasks.task_dir
读,空则降级默认派生。新增 is_managed_task_dir(td, ws) 判断 task_dir
是否在默认模板下。
- cli.py::_cleanup_if_empty 拿 workspace_dir 作保护开关 —— 用户自指定的
task_dir 绝不 rmtree(可能含用户已有素材);DB 行该删还是删。
- core/export_docx.py::export_chat_to_docx 重构:task_id 升一等参数(从
task_dir.name 提取改入参传入),task_dir 留空时自动从 PG 读;cli /export
与 cli.py export 子命令均改走 _resolve_uuid_or_prefix + task_id 直传。
- 新建 RUN.md(运行手册):env / 初始化 / 日常命令 / 故障兜底 / 关键路径。
- CLAUDE.md 加 RUN.md 维护规则(三文档边界:DESIGN=为什么 / PROGRESS=做到哪
/ RUN=怎么跑),对外行为改动同步更 RUN。
Smoke 4 路径:default-derived(managed=True, cleanup rmtree)/ --task-dir
(managed=False, FS preserved)/ resume reads DB task_dir / export 自动 PG
查路径,全绿。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:38:05 +08:00
caoqianming
aeecc7f0f3
core(§7 B Step 3): TaskState ORM + Web UI 设计 (Phase G)
...
- TaskState dataclass 改 PG-backed:save() → upsert_task (INSERT ON CONFLICT
DO UPDATE,显式刷 updated_at);load(task_id) → SELECT。state.json 全面
废除,task_dir 只承担 skill 产物。
- TaskState 字段去 cwd / 加 task_dir(对齐 §7 SaaS task_dir-as-identity);
cwd 只在 session.meta 内存视图保留(展示用)。
- core/storage/utils.py 新增 upsert_task / update_task;ORM-level UPDATE
自带 onupdate=func.now(),DO UPDATE 需显式 set。
- session.py Session.append 的 ensure 调用补传 mode/description/
reasoning_effort,避免首次 INSERT 后 _list_task_rows 看到空 meta。
- sync_task_tokens 改成 update_task 单字段 UPDATE,避免无谓全字段 UPSERT。
- cli.py _list_task_rows 全字段从 PG 读,status 过滤走 SQL WHERE;
_cleanup_if_empty 去 state.json 特例(任何 FS 文件/子目录都算实质痕迹)。
- core/export_docx.py meta 走 TaskState.load(tid),CWD 字段从 meta 表移除。
- DESIGN.md 追加 Phase G(Web UI 简洁版,FastAPI + Jinja2 + HTMX + SSE),
排在 §7.7 D 后;§7.9 补 server-render 不上 SPA 的取舍 4 条。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:25:53 +08:00
caoqianming
4f87bf14ee
core(§7 B Step 2): Session ORM — messages 走 PG, task_id 切 UUID
...
Session 重写
- messages 落 PG `messages` 表(append-only, idx 严格递增, jsonb payload)
- system prompt 不入库(每次 build_agent 重建到 messages[0],memory 演化即时生效)
- Session.load(task_id, system_prompt=...) 从 DB 读历史
- Session.task_exists / n_user_msgs 工具
Storage utils
- ensure_local_task_row: 首条消息前 INSERT ... ON CONFLICT DO NOTHING
打底 tasks 行(Step 3 后由 TaskState.save 接管字段更新)
task_id 切 UUID
- resolve_task_id(workspace, arg, resume): UUID + 前缀匹配,'last' 从 PG
按 updated_at 取最近
- 显示一律截前 8 位;完整 UUID 在 /id /status 保留
- 旧 workspace 老 task(时间戳格式)**不做兼容**
CLI 适配
- _cleanup_if_empty 双检查:DB messages count + FS 产物
- _list_task_rows: PG tasks ORDER BY updated_at + state.json 兜底字段
- _task_has_messages: /export 检查改 DB
- core/export_docx.py: messages 从 PG 读,state.json 留作 meta
Step 5 (migrate-from-fs) 取消。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 10:55:50 +08:00
caoqianming
425ea59937
core(§7 B Step 1): Storage 基建 — SQLAlchemy ORM + alembic + db CLI
...
- core/storage/{engine,models}.py: 5 表 ORM(users/tasks/messages/runs/
usage_events)+ session_scope 上下文 + 本地 sentinel user 初始化
- alembic 初版 migration 0001_initial_schema: messages.payload GIN
索引 + tasks (user_id, task_dir) 复合索引 + pgcrypto 扩展兜底
- cli.py: db upgrade/downgrade/current 子命令组;ZCBOT_DB_URL 未设
给 ASCII 报错 + exit 2(避开 Windows GBK 控制台编码问题)
- requirements: +sqlalchemy>=2.0 +psycopg[binary]>=3.1 +alembic>=1.13
- DB URL 来自环境变量 ZCBOT_DB_URL,不引导 docker(用户给测试库地址)
已在远端测试 PG 跑通 db upgrade head + db current。Session/TaskState
ORM 接入留 Step 2-3。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 10:41:44 +08:00
caoqianming
efe4a91c33
design: 精简 DESIGN/PROGRESS (-177 行)
...
DESIGN 520→351,PROGRESS 88→80。砍 §7 内部重复说理与 SQL 示例,
合并 §6/§7.8 风险表,压缩 §3 字段表与启动顺序;load-bearing 细节
(rename `old/%` 前缀、ModelCapabilities 字段、阶段估时)全部保留。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 08:52:23 +08:00
caoqianming
b4df60062e
design: 更新设计
2026-05-14 08:46:53 +08:00
caoqianming
e4a48fbb53
core: Session/TaskState 原子写 + Phase 6 双层记忆
...
- core.session.atomic_write_text (tmp + fsync + os.replace) 接管 Session/
TaskState 落盘, 中途异常不留 0 字节; _cleanup_if_empty 放过 *.tmp 孤儿
- core/memory.py: workspace/memory/{core.md, extended/} 双层记忆.
core.md 注 system prompt, extended/*.md 索引(标题+绝对路径)注 prompt,
内容靠 read 工具按需拉
- _build_system_prompt 从 build_agent 里提出来, new 和 resume 都走同一段,
resume 时覆盖 messages[0] -> memory 演化即时生效
- PROGRESS/DESIGN 同步: §7 platform track 行 + A 阶段完成 + 双层记忆/原子写
+ 文件清单到 2429 行
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:13:56 +08:00
caoqianming
ae93016442
cli: REPL /resume 切 task + 懒创建 task_dir + 切走前空清理
...
- 加 /resume [last|<id>] REPL 命令,无参数列最近 10 个表格让用户挑;
和 /new 对称,都在 REPL 内重建五元组。tasks 命令复用 _list_task_rows
- main.py 新建分支不再 session.save() / task_state.save() 占位 ——
推迟到首条 user 消息触发的 Session.append → save() 才物化 task_dir。
启动 REPL 立刻 /exit 磁盘无痕,跨进程也安全
- _cleanup_if_empty 在 /exit /quit /new /resume + Ctrl-C/EOF 守门:
无 user 消息 + 目录在磁盘上 + 文件集 ⊆ {messages.json} 才删,
state.json 存在(/done /abandon /desc 留下的显式痕迹)就保
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:55:56 +08:00
caoqianming
56e414e046
proposal: 阶段二两段式 + render_docx 透传 fenced 代码块
...
- SKILL.md 阶段二改两段式: 先列 3-6 条要点 → 用户确认 → 再起草 → 用户确认。关键章节 (立项依据/研究方案/技术路线/考核指标) 一段一卡。一次性出全文容易把错方向推到底,要点阶段拦得早
- render_docx.py 支持 ```...``` 围栏: 中文新宋体 + 西文 Consolas + 行距 1.0 + 不缩进 + xml:space=preserve。原先 ASCII 流程图被当散文段落合并,框完全错位
- PROGRESS.md backlog 加 mermaid 预渲染 (mmdc → PNG → add_picture),等 ASCII 透传不够用再做
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 08:58:18 +08:00
caoqianming
a32cb049bc
ppt+proposal: 素材摄取改用 markitdown, 删自研 source_to_md
...
ppt/proposal 的"素材 → Markdown"逻辑此前各写一份 (source_to_md.py
内联 pypdf/python-docx/openpyxl), 改用微软 markitdown CLI 统一替换:
表格/标题/列表保留更好, 同时多覆盖 xlsx/url/html/csv 等格式。
- requirements.txt: 加 markitdown[pdf,docx,pptx,xlsx]
- skills/ppt/SKILL.md: 资源行改成 markitdown 说明
- skills/proposal/SKILL.md: 阶段零 32 行 Python 代码 → 4 行 CLI
- skills/ppt/scripts/source_to_md.py: 删除 (157 行)
- PROGRESS.md: scripts 列表同步
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 08:03:07 +08:00
caoqianming
3869089a44
DESIGN/PROGRESS: 同步 TUI 打磨 + task_dir 落地
...
DESIGN: 目录树补全 task_dir 内的 skill 产物;启动顺序 #5 加 task_dir 注入;
3.1 主循环增补 markdown 渲染、spinner 实时耗时/token、每轮成本行。
PROGRESS: 加 2026-05-07 条目;工具基目录决策更新;loop/main 行数刷新。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:20:59 +08:00
caoqianming
a25fcbc703
精简 DESIGN/PROGRESS:对齐当前实现,去掉落地前的设想
...
- DESIGN.md 1140 → 212 行:删 v1/v2 比较、Eval Suite 章节、Phase
roadmap、长代码草图、技术栈清单(在 requirements.txt)、超长附录
- PROGRESS.md 185 → 82 行:删超细分的 ppt v3 描述、重复测试列表
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 16:26:19 +08:00
caoqianming
dbb778fe10
Phase 4 + 6: capability probe + task 概念 / state.json
...
- core/probe.py + cli.py probe: basic_chat / parallel_tools /
thinking_mode / long_context 四项实测对账 yaml;不进启动路径
- core/task.py + main.py: workspace/tasks/<id>/{state.json, messages.json},
TaskState 跟 mode/desc/status/tokens/timestamps;build_agent 返 5 元组
- cli.py tasks 子命令 + REPL /status /done /abandon /desc;chat 加
--mode/--desc 选项;移除 legacy workspace/sessions/ 兼容
Phase 5 evals 评估后决定不做:个人工具用 dogfooding 判断模型升级,
probe 已覆盖健康检查;造作 case 没区分度,维护成本不划算。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 16:21:17 +08:00
caoqianming
0971a500e7
PPT skill v3: 红色硬约束 + 品牌条 + 强制尾页 + Iconify 图标库
...
修复上一轮生成的实际问题: 模型擅自把红色换成蓝色 / 内页全裸白 / 缺 Q&A 尾页;
并补齐"个性化图标"能力 (此前只有 MSO_SHAPE + unicode 字形,业务概念图标缺位)。
- SKILL.md: 红色主题改硬约束 + ⛔ BLOCKING 八条对齐 (bundled 推荐, 等用户拍板),
封面/尾页改强制项, 不算在 5-8 页正文预算内
- layouts.md: 加 apply_brand(slide, kind) 4 模式品牌条 (cover/inner/section/end),
9 个版式起手必调, 消灭裸白页
- 图标库: 新增 fetch_icon.py 走 Iconify CDN (tabler/lucide/heroicons 等 150+ 集),
主题色染色, 缓存到 assets/icons/, 配 INDEX.md 推荐清单
- icons.md: 移除 MSO_SHAPE 当业务图标的部分 (PENTAGON/LIGHTNING_BOLT 等视觉陈旧),
三层降级 → 两层 (Iconify / unicode 兜底); MSO_SHAPE 退为 layouts.md helper 内部原语
- canvas_presets.md 并入 design_principles.md §0 (减少零碎文件)
- .gitignore: spec_lock.md 与根目录 *.pptx 不入库 (PPT skill 工作产物)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 12:23:06 +08:00
caoqianming
3a66849953
Initial import: zcbot personal task agent
...
DESIGN.md / PROGRESS.md 落地 Phase 1-3:
- core/: LiteLLM 封装 + ReAct loop + 会话持久化 + Anthropic skill registry
- tools/: read/write/edit/glob/grep/shell/run_python/load_skill (Hybrid 范式)
- skills/coding | proposal: WHY+WHAT 风格 SKILL.md
- skills/ppt: 完整渐进披露 (SKILL + 4 references + 3 scripts)
· 借鉴 hugohe3/ppt-master 的两阶段 + spec lock 思路
· MSO_SHAPE 图标体系 + 安全区 + 越界检测
· 默认商务红主题 (#C00000 / #E15554 / #FFC107)
- config/models/: DeepSeek V4 flash/pro 档案
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:02:59 +08:00