Skip to content

Releases: Asaiuta/dsh-custom-header

0.4.1 — Zen 免费档工具桩改为追加,不再吞掉调用方工具

Choose a tag to compare

@Asaiuta Asaiuta released this 25 Oct 15:34
  • 修复:opencodeZenFree 把整个 tools 数组替换成两个桩,免费档会话因此拿不到自己的工具。
    Zen 免费档的门只要求「提供」bash 与 read 两个名字(各自带非空 parameters
    schema),并不要求它们是仅有的工具——但 0.4.0 的实现是

    tools: [bashStub, readStub]

    于是每一次 *-free 请求都只带这两个桩出网,调用方(DSH)原有 50 个工具全被丢掉。
    实测后果三类,全部同源:
    ① 会猜名字的模型照旧能干完活——它从系统提示里认出 ops_bash / ff_read 并直接调用
    (观察到 502 / 449 / 73 次工具调用的会话);因为 DSH 按名字派发,工具并未「没注册」;
    ② 照桩文案办事的模型直接拒答——它认定自己没有任何可用工具,0 次调用
    (session-1f6c6fbe、session-74289305 都回「## 结论:本次会话无法执行该分析」);
    ③ 去调桩名 bash 的模型撞上 unknown tool "bash"——这正是此前排查到的
    「DSH 里 bash 失效」34 次未知工具事件的上游来源,不是模型凭空乱调。
    由于模型是否「敢猜」随采样而变,表现为非确定性:同一份配置有时能干、有时摆烂。

  • 行为变更:patchOpencodeZenFreeBody 改为追加——保留调用方 tools,按名补齐缺失的桩;
    调用方已提供同名工具时不重复插入(既不产生重名条目,也不覆盖调用方自己的 schema)。
    ZEN_TOOL_STUB 文案保持不变:它的职责是压制对 bash 这个 DSH 并不提供的名字的调用,
    问题从来不是这句话,而是它当时是模型唯一能看到的东西。

  • 新增命名导出 ZEN_TOOL_STUB / ZEN_BASH_TOOL_NAME / ZEN_READ_TOOL_NAME,供测试与外部核查引用。

  • 设置页 opencodeZenFree 帮助文案(中/英)补一句:工具桩是追加而非取代。

  • tests/smoke.mjs 新增场景 10(16 条断言):调用方工具被保留、桩被追加、无重名、stream
    仍被强制、桩的描述与 schema 未变、调用方已提供 read 时不重复且其 schema 生效、
    非免费模型与开关关闭时请求体原样透传,以及 rewriteOpencodeZenUrl 的纯函数边界
    (/zen → /inference、/zen 根、/zen/go、其它主机、非 zen 路径)。
    反向对照已验证该场景对旧实现报 FAIL s10 caller tools preserved — bash,read。

0.4.0 — 传输层回到 fetch 中间件管道

Choose a tag to compare

@Asaiuta Asaiuta released this 25 Oct 15:34
  • 传输层回到 fetch 中间件管道。 0.2.0 把插件改成了持有 provider 路由的
    原生 LlmAdapter(ctx.llm.registerAdapter),但那个扩展点是按路由独占的:
    想给已被别的插件持有的 provider(如 llm-pi-ai 的 ziyong/moonshotai)
    注入客户端签名,注册直接抛 DUPLICATE_ADAPTER,宿主也没有「嫁接」到别人
    适配器上的通道;llm/stream 瀑布又改不了 HTTP 头。本插件的意图恰恰是
    「让我发往自己网关的请求看起来像客户端 X」——无论这些请求来自哪个插件——
    所以回到不持有任何路由的 fetch 层:globalThis.fetch 上一条 DSH 专用
    symbol 键控(Symbol.for("dsh-custom-header.fetch.pipeline.v1"))的中间件链,
    所有 provider 的请求都从链上流过。这不是回退到 0.1.3:0.3.0 的设置平面
    (ConfigSchema 全 volatile、场景向导配置页、无 default 导出修复、lockfile
    修复)全部保留,只换传输实现。
  • 四个中间件(优先级升序):url-rewrite(5) → header-strip(6) →
    header-inject(7) → body-patch(8)。每个都按需重读一份快照,快照由
    loader/volatile-update 刷新——设置页保存对下一次请求即生效,不重挂插件。
  • 统一主机门控(行为变更):fetch 层识别请求只能靠 URL,永远看不到它属于
    哪个 provider,因此 gatewayHosts 成为唯一的安全边界——所有预设(包括
    codex_official 等固定预设,0.1.x 里它们全局注入)都只作用于命中白名单
    主机的请求;白名单为空即完全无操作。resolveProfileForUrl 统一实现该规则。
  • 配置面收窄:删除适配器时代字段 providers / openaiProviders /
    baseUrl / openaiBaseUrl / apiKey(provider 路由不再由本插件持有,网关
    URL 由 provider 自身配置决定)。gatewayHosts 从「仅 auto 生效」升格为中心开关。
  • 会话 id 恢复按对话隔离:llm/stream 包装器把 GenerateOptions.sessionId
    经 AsyncLocalStorage 传给 fetch 层(session-context.ts 回归);
    x-opencode-session / X-Claude-Code-Session-Id / body metadata.user_id
    均按会话惰性分配缓存,非 LLM 请求退回进程级 default 会话。
  • opencode Zen 免费层移植到 fetch 层:body-patch 中间件先读请求体里的
    模型 id,命中免费模型(*-free / big-pickle / grok-code)时把 /zen
    改写到 /inference 并补 bash/read 工具桩——URL 改写住在 body-patch
    而不是 url-rewrite,正因为判定需要模型 id。
  • 删除 opencodeFreeModels(额外免费模型 id 列表):实际用不上,0.4.0
    未发布、无兼容负担;isOpencodeFreeModel 改为只认内置免费 id。
  • 403 诊断保留:llm/stream 观察器区分 Cloudflare HTML 边缘拦截与 JSON
    client_restriction 拒绝。
  • 代码组织:恢复 fetch-pipeline.ts / session-context.ts /
    header-inject.ts;重写 url-rewrite.ts(新增 UrlRewriteContext /
    withUrl 等)与 body-patch.ts(新增 createBodyPatchMiddleware,保留
    patchAnthropicMessagesPayload);删除 adapter.ts / adapter-common.ts /
    adapter-openai.ts / serialize.ts / serialize-openai.ts / sse.ts /
    sse-openai.ts。
  • 字段改名(按日常读法):autoHosts → gatewayHosts(网关主机)、
    autoCodexProfile → codexFallback(Codex 兜底)、claudeSystemMode →
    claudeSystemBlock、codexVersion → codexCliVersion;配置页为
    opencodeClient / opencodeProject 补上专属提示(原来错用版本提示)。
    0.4.0 未发布过,未做旧名兼容。
  • 配置页重排为「导航栏 + 三个固定页」(外壳参考 dsh-any-background 的
    设置分节:左侧 158px 导航栏与滑动指示条、品牌块、页面切换动画、卡片;
    ≤620px 收成顶部横条)。起因是场景向导的一个真实缺陷:字段按 overridden
    分区(「也已设置,但不属于当前场景」),而 overridden 在暂存时就为真,
    于是编辑 claudeSystemBlock 这类当前场景不列的字段时,正在编辑的行会从
    「全部设置」折叠区跳到页外——行在手下跑掉。新布局里每个字段有唯一
    归属页,切页不再重新归类任何行:
    1. 身份与网关(gatewayHosts / codexFallback / claudeSystemBlock)——
      顶部仍是场景选择器,选中场景只暂存 profile;
    2. 版本与请求头(8 个版本/头字段);
    3. 高级(extraHeaders / urlRewrites)。
      随之删除 scenarios.ts 的 fields / ScenarioFields / scenarioFields、
      fields.tsx 的 GROUPS 导出与「也已设置」分区;styles.ts 与
      index.tsx 按新外壳重写(导出 NAV_ITEM_H / NAV_GAP 供指示条定位),
      locales.ts(中英)新增 brandTag / nav* / overline* / desc* 并清掉
      死键。
  • 配置页随配置面同步:删除网关端点分组与 apiKey 密钥行(连带
    SettingsSecretField 用法与 secret 字段类型);fields.tsx /
    scenarios.ts / locales.ts(中英)同步。
  • 冒烟测试回归并扩展:tests/smoke.mjs 9 场景 31 断言(新增「固定预设同样
    受 gatewayHosts 门控」);支持 SMOKE_LIB 环境变量指向临时构建产物,
    便于在桌面端运行(插件被挂载监视)时不碰 lib/ 验证构建。
  • README(中 / 英)重写「为什么回到 fetch / 工作机制 / 配置 / 配置页 /
    已知残留」章节。