Repository navigation
Releases: zdk119746/dsh-llm-workbuddy
Release list
v0.1.26
Changed
- 修复图片输入的一个 bug
具体修了什么
两处 bug 都会让只要历史里出现过图片的每一轮请求失败,并误报成
WorkBuddy proxy stream from http://127.0.0.1:8787/v1 failed:
-
传给附件服务的 target 形状错了。
serializeMessages传的是
{maxPixels, maxBytes},但attachments.readImageRequest要的是
ImageRequestTarget——{width, height, maxBytes}。真实后端以
INVALID_ATTACHMENT_REF("Image request width must be a positive
integer.")拒绝,而AttachmentError不是LlmError,于是被兜底重标成
TRANSPORT。代理其实一条请求都没收到,重试重发同一历史必然再失败。
现在按官方适配器(dsh-llm-pi-ai/dsh-llm-deepseek)的做法,用
requestImageDimensions投影出目标尺寸。 -
tool 结果里的图片被丢掉了。
read_image把图片字节放在 tool 结果内部,
而serializeToolResult把它 flatten 成纯文本,模型只会回"没有图片"。
现在保留文本并追加image_url部分。
验证
- 新增
test/image-serialize.test.mjs,用真实的dsh-attachment-local
校验 target 形状;把修复回退后该测试确实失败(6 项挂 2 项),确认能捕获此 bug。 - 端到端:用真实的
app_home_qrcode.png走真实附件服务 + 真实代理,
HTTP 200,模型正确描述出"二维码 / 中央有白色方块和星芒状图标"。
本 bug 自 0.1.9 引入(0.1.11 进一步确认了错误形状),此前
readImageRequest
从未被测试覆盖。桌面端安装的是同一份代码,升级到 0.1.26 即修复。
v0.1.23
v0.1.22
v0.1.21
v0.1.20
v0.1.18 — 上游错误不再被吞掉 / 不再假掉登录
安装
dsh plugin --profile web add dsh-llm-workbuddy
# 或
npm i dsh-llm-workbuddy@0.1.18本版含插件侧与本地代理侧两类修复,正文里用
[插件]/[本地代理]标注了范围。
修复「任务跑着跑着就死、显示得像断联、之后再怎么重试也起不来」这一类问题。
根因是三条,都只在 WorkBuddy 这条链路上(原生 DeepSeek 不受影响)。
改动范围:
[插件]的随本包发布,装了就生效;[本地代理]的在本机
.workbuddy-src(start-workbuddy.sh用的那份源码)里,使用 PyPI 版
workbuddy2api的用户不受影响。不过[插件]的错误帧解析对任何代理都有效,
所以"一次 429 就让整场任务死掉"这个问题对所有用户都修好了。
Fixed
- [插件] 上游的 HTTP 错误被吞掉,任务当场死亡且不可重试(主因)。
代理在流式请求里先发 200 响应头、之后才知道上游状态,于是上游的
429 / 400 / 5xx只能作为data: {"error": …}事件写进 SSE,而且写完直接
return,不补[DONE]。适配器只看到"流结束了却没有 [DONE]",抛出
STREAM_CLOSED;而STREAM_CLOSED不在 harness 的重试策略里
(EMPTY_RESPONSE / RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT)—— 一次限流
就让整个任务终结,且同样的请求下次还是被拒,用户看到的就是"卡死、无法继续"。
实测证据:09-16 上游 4 次429 code 6004 您的使用量已超出频率限制(含重置时间)
↔ 会话记录里 5 次STREAM_CLOSED;09-04 上游 6 次400 code 11133。
现在:- 适配器把
error帧翻成真正的LlmError:配额类(6004/ "使用量已超出"
/quota)→QUOTA,429 →RATE_LIMIT,400 →INVALID_REQUEST/
CONTEXT_WINDOW_EXCEEDED,5xx →SERVER,并把上游原文(含"将在 … 重置")
带进错误信息; - 代理在所有结束路径(含错误、超时、内部异常)都补
data: [DONE],
并给错误帧带上code/type/ 可读原因; - "一个字节都没收到就断开"映射为可重试的
EMPTY_RESPONSE,已经有内容再断开
仍保持STREAM_CLOSED(截断的回答不能当完整回答用)。
- 适配器把
- [本地代理] 60 个工具被静默砍到 30 个(上下文自相矛盾)。DSH 一次发 60 个工具,代理
硬编码MAX_TOOLS = 30且按顺序截断;DSH 的工具表是字母序,前 30 个恰好
全是ego_*浏览器工具,被丢掉的正是read/write/grep/glob/
todo_write/web_search/subagent/present…,而 system prompt 里
仍然写着这些工具。模型于是调用"没声明"的工具、或者干脆长时间只出推理不出
工具调用(实测一次卡住的 step:9727 字符推理、116 秒、无工具调用,最后被用户
中止)。实测上游接受 60 个工具(HTTP 200),所以默认上限提到 64,可用
--max-tools调整,真发生截断时会把被丢掉的工具名写进日志(不再静默)。 - [插件] 探测抖动被当成掉登录(胶囊假红)。状态胶囊的判定是
status.authenticated && status.proxyUp,而proxyUp来自一次
2 秒硬超时、只试一次 的/health探测 —— 本机任何一次短暂卡顿都会让
胶囊直接显示WorkBuddy · 未登录,哪怕会话文件完全有效。现改为:
每次探测 8 秒、失败重试 1 次、连续 2 次失败才报告proxyUp: false
(连接被拒绝时立即判定,因为那确实说明代理没了);/status与诊断面板
额外返回lastProbeError/consecutiveProbeFailures/degraded。 - [插件] 胶囊只有两态,把"代理没响应"也说成"未登录"。现在三态:绿=已登录、
黄=代理未响应(登录态仍有效,不提示点登录,避免多占一次上游)、
红=未登录(只有会话真的无效时才出现)。 - [插件] 中止任务后上游仍在生成,WorkBuddy 账号被继续占用。适配器现在监听调用方
的中止信号,用户一按停止就立刻 abort 上游请求(不再依赖 finally 的时机);
本地代理侧也新增客户端断开轮询与client_disconnected记录作为兜底。 - [本地代理] 请求路径上的同步设备流登录会冻住整个代理。代理原来在每个 chat 请求里
同步调用ensure_authenticated(),一旦判定 token 过期且刷新失败,就会在
async handler 里跑login()(内部time.sleep轮询、最多 300 秒),把
uvicorn 单事件循环彻底冻住:所有工作区的模型请求一起卡死,/health也
不响应,于是胶囊同时显示"未登录"。现改为:认证检查/刷新放到
asyncio.to_thread里执行并加锁(refresh token 是轮换式的,并发刷新会
互相作废),请求路径绝不允许触发交互式登录 —— 刷新失败就快速返回
401auth_error,交互式登录仍走POST /api/workbuddy/login。 - [本地代理] 手动启动与 launchd 抢 8787。
start-workbuddy.sh增加端口占用护栏:
手动执行时发现端口已占用就直接退出;launchd 用--wait驻留等待旧进程退出
后再接管(配套 plist 加--wait与ThrottleInterval),不再刷
address already in use。
Verified (not a bug)
- DSH 的 system prompt 不会被压缩/裁剪。代理的
--desensitize+
compact_harness只针对 Codex CLI / Claude Code 的模板(Codex CLI、
# How you work、<permissions instructions>等标记),实测拿 DSH 真实的
6874 字符 system prompt 跑一遍:只多了 15 个零宽空格(敏感词表命中),
长度与内容不变。所以"数据压缩把上下文搞坏"不成立。
0.1.17
[0.1.17] - 2026-09-11
Fixed
- 登录态永远判定为有效,且「登录」按钮无法恢复(issue #3)。
代理(workbuddy2api)写入的~/.codebuddy-session.json里没有
auth.expiresAt,而插件把"字段缺失"当作"未过期",于是:
authenticated恒为true、POST /api/workbuddy/login恒返回
alreadyLoggedIn、诊断面板的「登录状态/登录令牌」恒显示"有效";一旦 token
真失效,用户点登录没有任何反应,只能回终端手跑脚本。
现改为多源推导过期时间(按优先级):auth.expiresAt(本插件脚本写入;秒/毫秒自动归一化);refreshToken的 JWTexp——代理会自动 refresh,所以 refreshToken 的
有效期才是会话真正的边界;accessToken的 JWTexp(解码 payload,不校验签名);auth.expiresIn+ 会话文件 mtime(兜底,只会低估、不会高估)。
全部失败时返回expiryKnown: false+reloginRecommended: true,
不再假装有效。/status与诊断面板都会带上expiresAtSource说明来源。
authUrl永远为null,点「登录」在新标签页打不开任何页面(issue #3 相关)。
登录路由spawn后立刻res.end(),而设备流链接是子进程稍后才打印到
stdout 的,因此返回的authUrl恒为null,Web 登录实际上从来没成功打开过
授权页。现改为等待链接出现(或脚本早退/15 秒超时)再应答,失败时返回结构化
error,前端弹窗提示而不是静默无反应。子进程 stdout 在应答后继续被 drain,
避免缓冲写满阻塞。- 「🎁 签到」必然 404,且每天写入一条失败记录(issue #4)。
插件请求代理的/checkin-status与/checkin,但 workbuddy2api 从未实现
这两个端点(已核对 PyPI 2.0.0–2.0.4 与main/dsh/codex全部分支),
且autoCheckin默认true,于是每天 10 点后必然失败一次并把失败结果持久化。
现改为探测 + 优雅降级:/checkin-status返回 404/405 时判定该代理
不支持签到,隐藏签到入口、自动签到直接跳过(不写任何状态)。探测结果
缓存 30 分钟,/status通过checkin.supported三态(true/false/null)
暴露给胶囊。代理补齐端点后入口会自动出现,无需升级插件。
Added
- 🔑 重新登录(强制重登):
POST /api/workbuddy/login?force=1忽略本地会话
判定直接重跑设备流;胶囊始终提供该入口(不做条件显示),「判定错误 →
无法恢复」的耦合从此断开。
Changed
- 诊断面板新增「令牌过期」(含来源)与重新登录提示;
login失败会弹窗说明原因。 - README 代理安装指引更正:原先要求"必须用 GitHub 主分支源码、不能用 PyPI
2.0.3",实测 PyPI 2.0.4(2026-09-10 发布)已包含该登录修复(
X-Product-Code/_enterprise_headers/X-Domain均在包内),且 GitHub
main与 2.0.4 内容完全一致;而 2.0.3 确实缺少该修复。现改为推荐
uv tool install -U workbuddy2api(>= 2.0.4),源码方式作为等价备选。 - 诊断面板的「重启命令」同步改为
uv tool install方式。 - README 补充「登录态判定」章节与签到依赖说明。
v0.1.16 — 适配 DeepSeek Harness 0.1.5-rc.1
[0.1.16] - 2026-09-10
Fixed
- 适配 DeepSeek Harness
0.1.5-rc.1(此前 0.1.15 在该版本上完全无法加载)。
0.1.5-rc.1 移动了两个导出符号,而具名导入一个已不存在的符号会触发 ESM
加载期SyntaxError,整个插件在import阶段即崩溃(provider、状态胶囊、
用量面板全部消失),不是运行期告警。现改为经命名空间对象取值并带兜底:@deepseek-ai/dsh-llm:CallId在 0.1.5-rc.1 改名为ToolCallId。@deepseek-ai/dsh-settings:settingsNamespace已移除(改由注册内部
parseSettingsNamespace校验,规则同为/^[a-z][a-z0-9-]*$/),
installSettingsSection改为 settings 服务的installSection方法。settings分区改走ctx.inject(["settings"], (c) => c.settings.installSection(...)),
与官方dsh-llm-deepseek/dsh-llm-pi-ai在 0.1.5-rc.1 上的写法一致。
Changed
peerDependencies放宽为^0.1.1-rc.2 || ^0.1.5-rc.1(dsh-llm/
dsh-settings/dsh-timeout),@deepseek-ai/cordis放宽为^4.0.1 || ^4.0.2,
以显式声明对 0.1.5-rc.1 的支持。修复为运行时取值,同一个包在
0.1.1-rc.2 与 0.1.5-rc.1 上均可加载。
0.1.15
Fixed
- 刷新模型看不到新上架模型:内置目录此前同时充当白名单,代理
/v1/models里凡不在目录中的模型一律被丢弃,所以平台新上架的模型(如deepseek-v4.1-flash)刷新多少次都不会出现。现改为黑名单RETIRED_MODEL_IDS:只隐藏实测不可用(上游service info not found)的旧模型 id,其余照单显示。顺带放出 7 个别名仍在服务、却被旧白名单误藏的模型(minimax-m2.7、glm-5.0-turbo、hy3-preview、deepseek-v3-1-lkeap、deepseek-v3-0324-lkeap、deepseek-r1-0528-lkeap、hunyuan-2.0-instruct)。 - 内置目录新增
deepseek-v4.1-flash,并在代理不可达时作为兜底条目。 - 🔍 诊断的「模型出字」误报修复:探针用
hy3+max_tokens: 4发请求,而推理模型会把这 4 个 token 全花在思考上、content返回空串,于是"代理正常"被误判成「代理返回了空响应」。现承认"有计费 completion token + 有 choice"即为正常。
Added
- 模型名后显示积分消耗倍率:读取代理
/v1/models的credits字段("x0.06 credits")并拼成Deepseek-V4-Flash ×0.06;实时值优先,内置目录credits兜底。平台未声明倍率的模型只显示模型名——不会用 0 或猜测值冒充。 - 内置目录补齐各模型
credits(取自 WorkBuddy 应用product.json)。
Changed(代理侧,workbuddy2api)
/v1/models透出credits字段,倍率不再由各客户端各自维护。- 官方应用
product.json与本地models_config.json合并时倍率单独回填:官方条目为空则沿用本地倍率,避免官方合并把倍率抹平。 models_config.json补充deepseek-v4.1-flash(1M 上下文 / 50k 输出)。
完整对比:v0.1.14...v0.1.15