Skip to content

dsh-plugin-llm-codebuddy

Verified

dsh-plugin-llm-codebuddy · v1.2.1 · MIT · Web UI

Tencent CodeBuddy LLM adapter for DeepSeek Harness

Install

dsh plugin add dsh-plugin-llm-codebuddy

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Readme

dsh-plugin-llm-codebuddy

CI npm license

腾讯 CodeBuddy 的 DeepSeek Harness(dsh)适配插件。

它把 CodeBuddy 的模型目录与对话端点接入 harness,注册为名为 codebuddy 的 LLM 路由,让你的 CodeBuddy 账号额度直接用于 dsh 里的对话。插件不会自行创建 或删除凭据:它读取凭据与目录、流式对话,并在令牌临近过期时刷新后写回同一条 记录(刷新失败不删除凭据)。

装上之后你会多得到这些:

  • 模型选择器上直接看价格与促销——模型名后面挂促销徽章,最右边是积分倍率。
  • 账号额度可视化——侧栏进度条、输入框旁的胶囊、插件详情页的同心圆环,三处同源。
  • 用 CodeBuddy 账号接管网页搜索与抓取——一个开关,不抢别人的位置。
  • 限流与偶发失败有人兜底——429 会告诉你重置时刻,瞬时失败自动退避重试。

安装

dsh plugin 会把参数转交给该 profile 目录下的包管理器,因此安装就是一次普通的包安装:

dsh plugin --profile web add dsh-plugin-llm-codebuddy

装好后重启 dsh web(或让 profile 重新加载)生效。

登录

CodeBuddy 使用浏览器签发的令牌认证。安装后在 设置 → 插件 → CodeBuddy 里点「登录」,浏览器完成授权即可;令牌写入 harness 的凭据存储,不作为配置项,也不会被本插件另存一份。

  • 未登录时模型列表为空,实际发起请求会以「缺少凭据」失败。
  • 访问令牌临近过期会自动刷新;刷新失败不会删除凭据,只有刷新令牌本身也过期 才会提示重新登录。

配置

配置项 默认值 说明
baseURL https://copilot.tencent.com/v2 对话接口地址。
defaultContextWindow 128000 目录未标注容量的模型所用上下文窗口。
defaultMaxTokens 8192 目录未标注容量的模型所用最大输出。
streamIdleTimeoutMs 300000 对话流的空闲超时。

模型目录

模型来自 CodeBuddy 的目录接口,会随服务端编辑自动更新,无需重启。只在目录里 标注了容量(上下文与最大输出均为正)的模型才会出现在选择器中;未标注容量的 条目不出现在列表里,但仍可用模型 id 直接指定,此时按上面的默认值保守处理。

已知问题:部分模型的自动压缩不会触发

CodeBuddy 目录里标注的「最大输出」有时比模型本身的上下文窗口还大:

模型 上下文窗口 目录标注的最大输出
minimax-m3 512,000 524,288
kimi-k3-1 1,000,000 1,048,576

harness 判断「什么时候该自动压缩会话」时,会先把这段输出预留从窗口里减掉。这两个 模型减出来是负数,于是自动压缩永远不触发,上下文一路增长,最后由网关报上下文 超限。

已经验证过:这不是网关的限制(发同样大的 max_tokens 网关照收,而且输出会精确 停在那个数),是客户端按目录声明值算出来的结果。把输出预留压到窗口以内,这两个 模型的判断就恢复正常,其余模型不受影响。

本插件目前没有修这个(仍然直接使用目录声明的值)。如果你常用上面这两个模型、 又遇到了上下文超限,先换一个模型试试。

模型选择器上的价格与促销

CodeBuddy 的模型目录里带着促销与订阅信息,插件把它们直接摆到模型选择器里,省得 每次去官方 IDE 里对一遍:

Hy3 [限时免费] x0
  • 徽章:模型名后面是当前生效的促销(限时免费、夜间折扣、错峰使用……)与订阅 权益(订阅优先)。徽章颜色由 CodeBuddy 服务端给出。
  • 倍率:最右边的 x0.29 是这个模型的积分倍率,倍率越低越便宜(x0 就是 免费)。促销生效时显示的是折后的倍率——限时免费期间是 x0。
  • 悬停看详情:基础倍率 → 当前生效倍率、促销说明、促销的时间窗、模型简介。

促销按 CodeBuddy 给的时间窗自动出现和消失(比如「每晚 23:00–次日 8:00」那种夜间 折扣,白天就没有)。插件每 5 分钟向 CodeBuddy 对一次目录,所以时间窗翻转后最多五 分钟内生效。

徽章只属于 CodeBuddy 提供的模型:用别的 provider 时,模型名照常显示,不会有徽章 或倍率。

用量额度

插件提供三个入口查看账号额度,数据同源,因此任何两处显示的数字都不会互相矛盾。

侧栏指示器(默认关闭)

侧栏底部用两条进度条回答「还剩多少、还能自由花多少」:

  • 第一条「本月剩余」:灰轨是总剩余,高亮段是本月内到期的那部分,条长就是 本月剩余占总剩余的比例——一眼看出「总剩看着不少,本月能自由花的只是一小段」。 右侧数字是本月消耗率,消耗率越高颜色在同一灰蓝色系内越深。
  • 第二条「最近剩余」:把未来到期的额度按 ≤1 天 / 1–2 天 / 2–7 天分成三段, 每段宽度按该档额度分配,段内实心部分是该档的已用比例。没有任何短期到期额度时 整条隐藏。

悬停可看到本月已用/额度、本月剩余、总剩余,以及每个到期段的精确数值。

默认关闭是因为侧栏底部是常驻区域,加进度条会改变默认界面。

输入框额度胶囊(默认开启)

输入框工具行、模型选择器左侧显示 CodeBuddy · 本月 xx%,点击打开完整明细。 只在当前选中 CodeBuddy 模型时出现——额度是 CodeBuddy 账号的属性,用别的 provider 时它既无意义也不该占位。

同心圆环

插件详情页与胶囊弹窗展示同一套同心圆环,中心与三环由内向外分别是:

位置 内容
中心 总剩余积分
内环 已用积分(占总额度的比例)
中环 本月到期(占本月额度的比例)
外环 未来 7 天,按天分段

三个环是同一种读法:空轨表示未使用,彩色弧表示已使用,弧长就是已用的比例。 分段环也不例外——每一天未用的部分与另外两环同一个底色,只有已用的部分才是 当天自己的颜色,所以七个环段彼此可分辨,一眼能看出「哪几天有额度、各用了多少」。

按天分段的宽度按当天发放的额度分配,段与段之间留有空隙。当天没有发放额度 是特殊情况:它不画彩色弧,只留一小段很淡的占位,既让七个位置保持稳定(便于记住 「第几天」),又不会让人误以为那天有额度可花;图例里也会显式写「—」而不是 0 / 0。

打开或数据刷新时,三个环会从零顺时针生长出来,并逐环错开;如果你在系统里设置了 「减少动态效果」,动画会被跳过,静态图形照常显示。

每日签到

圆心是签到按钮:点它就给 CodeBuddy 账号签到,领当日积分(Buddy 加油站)。

  • 未签到时圆心是干净的,鼠标移上去会浮起一圈淡底,提示「这里可点」。
  • 签到成功后圆心铺上一层灰绿,中心数字换成深墨色——铺色的过程与三环的生长 动画同一个节奏(同一条曲线、同一时长),读起来是一次动作。
  • 已签到时圆心再点不动:按钮是真的 disabled,键盘也到不了。
  • 签到失败才会弹一条提示(成功不打扰,因为「颜色变了」本身就是结果);提示里 会带上服务端给的原因,例如「活动未开启」。

签到成功会顺带刷新一次额度读数——签到加了积分,数字得跟着变。

签到只在你点的时候发生,插件不会定时替你签。

读数时效

额度读数由宿主向 CodeBuddy 拉取,每 60 秒刷新一次。

同账号的重复读取由宿主缓存吸收:多个入口、多次重开面板或刷新页面都不会各自再打 一次 CodeBuddy,只有缓存过期后才在后台刷新一次。

用 CodeBuddy 接管网页搜索与抓取

开启后,harness 内置的 web_search 与 web_fetch 会改走 CodeBuddy 网关, 复用同一个账号,不需要额外凭据。

开关在 设置 → 插件 → CodeBuddy → 「使用 CodeBuddy 搜索」,且只在已登录时 出现(未登录时插件根本不注册相关后端,开关无从生效)。

行为 说明
默认值 关闭。安装插件不会静默抢走搜索位。
生效时机 即时,无需重启。
范围 一个开关同时控制搜索与抓取。
退出登录 自动释放,不影响其他搜索提供方。
重新登录 开关仍为开时自动恢复接管。

开关旁有一枚生效态徽标,用来区分「开关想怎样」和「实际怎样」:

徽标 含义
CodeBuddy 搜索生效中(绿) 搜索确实走 CodeBuddy 网关。
未接管搜索(灰) 你主动关掉了,或未登录。
接管未生效(琥珀) 开关已开且已登录,但接管没成立(宿主未提供相关能力等)。

没有这枚徽标时,「开关开着却没生效」与「开关是关的」看起来一模一样。接管通过 运行时选择实现,关闭时会还原成原来那个提供方,因此可以和其他搜索插件共存。

遇到频率限制(429)时的行为

当账号额度被限流,CodeBuddy 返回 429,并在提示里给出重置时刻(例如 「将在 2026-09-27 17:30:18 UTC+8 重置」)。插件会读出这个时刻,接下来怎么走 由一个开关决定:

「遇到 429 等待重置」开关 行为
关(默认) 429 按普通失败处理:快速重试几次(约 5 次、15 秒内)后抛出错误,报文里的重置时刻随错误消息原样保留,可据此切换模型继续。
开 等到服务给出的重置时刻再自动重试;但最多只等 5 小时。
  • 5 小时是硬上限,与开关无关:重置时刻更远时,插件也不会挂上这个等待——一个 回合为一个可能长到隔夜的重置默默挂住,不如立刻失败让人接手。
  • 等待路径(开关开且 ≤5 小时)在对话里有一条可展开的倒计时提示,说明第几次 重试与失败原因,随时可中断。
  • 报文里读不出重置时刻时(服务抽风,少见):与开关无关,按普通失败重试几次后 失败;宿主日志会留一份报文原文供核对。
  • 提示里的重置时间会原样保留,不会因为错误分类而丢失(分类特意不用 QUOTA—— 界面会把它换成一句通用文案,把那句话吃掉)。

开关在 设置 → 插件 → CodeBuddy →「遇到 429 等待重置」,即时生效,无需重启。

与 CodeBuddy 网关的两处兼容

CodeBuddy 网关对请求的写法有自己的规矩,harness 默认发出来的样子过不去。插件在 请求出门前做了两处调整——不调整的话,每一轮对话都会被网关当场拒收。

改写系统提示开头的身份声明

harness 给每轮对话加的系统提示,开头固定有一句「You are an AI agent powered by DeepSeek Harness…」。CodeBuddy 网关按渠道校验调用方,以这句原文开头的请求一律 拒收:

HTTP 400 · code=11128 · Illegal API invocation from an unapproved channel

界面上表现为一句「Request blocked. Please send it again」。这看着像登录出了问题, 其实不是:同一份凭据、同一组请求头,只要把这句改掉就立刻正常返回。

网关只认开头那一句(原句出现在别处不拦),也不区分大小写。所以插件只在开头 那句里补一个逗号——You are an AI agent powered by DeepSeek Harness. 变成 You are an AI agent, powered by DeepSeek Harness.,句子仍然成立、模型读到的身份 说明不变,但上游的整句比对失效。系统提示其余位置的产品名换成中性代称 (the DeepSeek Harness Web GUI → this harness Web GUI)。

改写 @ 路径说明那句

系统提示中段还有一句由 @deepseek-ai/dsh-file-reference 贡献的说明:

Tokens prefixed with @ are paths the user explicitly referenced. Relative paths resolve from the workspace root; …

它与产品名无关,所以上面「补逗号 + 产品名代称」两条规则都盖不到它。2026-10-29 用 真实 system 正文二分定位确认:把这句原样放进请求就 400 · code=11128,把 prefixed with 改成 starting with 立刻 200(只改一个词 explicitly → directly 也不再命中,说明上游比对的是长字面量,不是语义)。插件因此把这两个词 换成等义说法,其余文字逐个保留。

把你这一轮说的话放在最后

harness 在你的消息后面还会附上运行时上下文(AGENTS.md、运行时快照、技能清单、 当前时间),而 CodeBuddy 把一轮里最后一条用户消息当成你这次的提问。不重排的话, 网关读到的是那些附带内容,不是你问的话。

所以插件把每一轮里你的话挪到该轮用户消息的末尾。每一轮都这么排(包括已经结束的 轮次),这样一轮对话无论出现在哪都长得一样,请求前缀保持稳定,网关那边的缓存照旧 命中。

渠道自检与 CHANNEL_BLOCKED

网关按渠道校验调用方时,用的是这一句:

HTTP 400 · code=11128 · Illegal API invocation from an unapproved channel

以前它被归进 INVALID_REQUEST,读起来像「请求参数写错了」。这两件事的处置正好相反: 参数错了改请求就有救;渠道校验拒收是确定性的——同一份请求重发多少次都是同一个结果, 只能等上游放开或更新插件。所以插件把它单独归成 CHANNEL_BLOCKED,并且默认策略下不重试 (mode: 'always' 的策略会重试一切):重发只是把一次失败拖成几十秒。

每次真实请求撞上它,插件都把现场追加到 $DSH_HOME/codebuddy-channel-health.json (最多留 20 条),卡片上的「上次真实拦截」读的就是这份。文件里只有诊断——时间、 endpoint、请求 id、当时发出的 X-User-Id、系统提示的长度、一条关于指纹形状的猜测—— 不含凭据,也不含提示词正文;文件是插件自己写的 UTF-8(不带 BOM),读取时容忍开头 的 BOM(Windows 编辑器加的),不会因此把整份记录当成不存在;UTF-16 不自动识别,手改成 UTF-16 会按「无记录」处理。文件权限 0600;目录权限 0700 只在本插件创建它时生效, 已存在的 $DSH_HOME 保持原有权限;这两项在 Windows 上只映射到只读位,不对应 POSIX 语义。 删掉它只是丢掉读数,不影响任何请求。

自检按钮

设置 → 插件 → CodeBuddy →「渠道自检」→「运行自检」。

三发最小请求(max_tokens: 1),依次量:不带系统提示的裸请求能不能过(这一发只考请求头 与客户端身份)、原样带上已知指纹的请求会不会被拦、当前中和过的那份会不会被拦。三发的 差别只在系统提示,所以结论能把「请求头不合规」与「指纹被拦」分开——这正是 11128 的 报文本身分不出来的。

结论 含义
通道正常 已知指纹规则仍在,当前中和有效。
上游已不再按已知指纹拦截 三发全过;中和规则可以简化了。
中和失效 带上指纹的那发与中和过的那发都被拦:上游改了匹配方式,需要更新插件。
结果自相矛盾 未中和的放行、中和过的被拦。按上游灰度对待,隔一会儿再跑一次。
请求头/客户端身份不合规 裸请求就被拒,与提示词无关。
凭据失效 / 频率受限 / 额度用尽 / 连不上网关 与渠道校验无关的前置问题,先解决它。

会消耗一点点额度:三发都是最小请求,但确实是三次真实调用。因此自检只在你点按钮 时发生——插件没有定时检查,打开设置页时「渠道自检」这一块只读盘、零外呼;连点会被 并成同一轮,不会重复扣。 自检那一行只在已登录、且对话地址是官方网关时出现:自建网关没有渠道校验,三发会恒报 「上游已不再拦截」,那是误导,不如不显示。

自检自己撞上的 11128 不写进「上次真实拦截」:那是主动探测的结果,混进去会把一次 自检说成你的请求被拒。

已知盲区:探针只认识仓库里有证据的那条指纹

自检的 B、C 两发,用的都是仓库里确证过的那一条指纹(系统提示开头那句身份声明)。 它回答的是「这条规则还在不在、当前的中和是否还有效」,不是「上游以后还认不认我们」。

上游若新增一条与这句话无关的规则,探针完全可能三发全过、结论写着「通道正常」,而真实 对话照样被拒:真实请求带着完整系统提示、工具定义与多轮历史,比三发 ping 多得多,新的 判据就藏在探针根本不发的东西里。

这条盲区已经兑现过一次。 2026-10-29 的拦截就属于这一类:真实请求全被拦,自检三发 却报「通道正常」。事后拿拦截会话里 system/message 的 14,342 字正文做二分,才定位到 @ 路径说明那句(见上文「改写 @ 路径说明那句」)。探针至今仍只测身份声明那一条, 所以它现在覆盖的是两条已知规则里的第一条——报「通道正常」依然不等于「真实请求 能过」。

这种规则只能由被动记录发现——下一次真实请求撞上 11128,「上次真实拦截」那一行就会 更新,留下时间与指纹猜测、以及拦截会话的 system/message(会话日志里就有完整正文, 报障时 session-<id> 就是二分的原料)。所以两处读数是互补的:探针回答已知规则的 状态,拦截记录回答有没有出现未知规则。任何一处单独亮着,都不能当作「一切正常」。

尚未支持

  • 命令行登录命令。
  • 目录与用户自定义模型条目的合并。

许可证

MIT,见 LICENSE。