Skip to content

Send x-opencode-session header on API requests (required by OpenCode Go from 09/05) #2317

Closed

Description

@thdxr

Hi — we operate the OpenCode Go managed-inference API, and 280 of your users' orgs call it through MiMoCode (UA mimocode/0.1.13 ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.14).

Starting 09/05, requests without an x-opencode-session header will error on our side — we need a stable per-conversation ID for routing/optimization. MiMoCode doesn't send it in any version we've seen.

Ask: include x-opencode-session: <stable-id-per-conversation> on all outbound inference HTTP requests. Any stable UUID per conversation works. Happy to confirm from our telemetry once a build ships it. Thanks!

Activity

  1. romeo88845 commented on Sep 3, 2026

    @romeo88845

    Is there a work around if MimoCode hasn't fixed this issue before 09/05?

  2. Moviw commented on Sep 4, 2026

    @Moviw

    PR is up: #2327.

    It adds x-opencode-session next to the existing x-session-affinity in session/llm.ts, carrying the same stable sessionID under the same non-ephemeral condition — so it's a real per-conversation ID, not one per process or per install. Tests cover both the normal and the ephemeral path.

    @thdxr happy to have you confirm from your telemetry once a build ships it.

    Heads up that the PR is still waiting on maintainer approval to run CI (first-time contributor), so it hasn't been reviewed yet — flagging because of the 09/05 date.

    @romeo88845 as a stopgap you can set the header yourself, per model:

    {
      "provider": {
        "<providerID>": {
          "models": {
            "<modelID>": {
              "headers": { "x-opencode-session": "<any-uuid>" }
            }
          }
        }
      }
    }

    That clears the hard error, but it's one fixed ID for every conversation, so you lose the per-conversation routing the header exists for.

  3. est commented on Sep 5, 2026

    @est

    雷军!金凡! 萝福莉!

    还是优先看看这个issue吧!!影响天才程序员发挥了!!

  4. DaweiTian commented on Sep 10, 2026

    @DaweiTian

    @yanyihan-xiaomi 请尽快合并这个,opencode go没法用了,这里合并后,mimo desktop里面应该也就可以用了吧。

  5. OToNaShi-4 commented on Sep 12, 2026

    @OToNaShi-4

    @yanyihan-xiaomi 请尽快合并这个,影响我使用了,本来就是从 opencode 分支出来的不能忘本

  6. Pegasus-lt commented on Sep 15, 2026

    @Pegasus-lt

    User-side workaround for this issue (works on the released build, no binary patch)

    Confirmed on v0.1.14: MiMoCode already computes the stable per-conversation ID and sends it as x-session-affinity — it just never sends the second name OpenCode Go asks for:

    $ grep -a -c 'x-session-affinity' "$(command -v mimo)"   # 1
    $ grep -a -c 'x-opencode-session' "$(command -v mimo)"   # 0

    So the minimal fix is putting the same value under a second header name — exactly what #2327 does in session/llm.ts. Until that merges, MiMoCode's documented chat.headers hook covers it from userland.

    Install. Save as ~/.config/mimocode/hooks/opencode-session.ts (or <your-project>/.mimocode/hooks/). File hooks hot-reload on the next turn — no restart. Zero dependencies, nothing else to install.

    // Workaround for XiaomiMiMo/MiMo-Code#2317 until #2327 ships.
    // MiMoCode already sends the stable per-conversation ID as `x-session-affinity`;
    // OpenCode Go additionally requires it as `x-opencode-session`.
    const TARGET_PROVIDERS = new Set(["opencode-go", "opencode"])
    
    export default {
      "chat.headers": async (input, output) => {
        if (!input.sessionID) return
        if (!TARGET_PROVIDERS.has(input.model?.providerID)) return
        output.headers["x-opencode-session"] = input.sessionID
      },
    }

    Verify.

    $ mimo run -m opencode-go/deepseek-v4-flash "reply ok"
    • Before: Request is missing x-opencode-session and cannot be routed efficiently (hard reject).
    • After: normal response. Other providers are untouched — the hook returns early.

    Caveats

    • Ephemeral requests (session title / summary) do not pass through chat.headers, so they still lack the header. That matches fix(session): send x-opencode-session on outbound requests #2327's non-ephemeral condition, and this issue's ask is per-conversation.

    • The value is input.sessionID — the same opaque ID already sent as x-session-affinity, so no new identifier is invented and no routing assumption changes.

    • Retire it once a release ships the fix — and check, don't assume an upgrade fixed it:

      $ grep -a -c 'x-opencode-session' "$(command -v mimo)"   # ≥1 ⇒ built into the client, delete the hook

      To disable, rename the extension (opencode-session.ts.bak) or delete the file. The loader glob is {hook,hooks}/*.{js,ts}, so a .disabled.ts name would still be loaded.


    中文说明(同上一节)

    根因:在 v0.1.14 实测,MiMoCode 已经把稳定的「每会话 ID」以 x-session-affinity 发出去了,只是没发 OpenCode Go 要求的第二个名字 x-opencode-session。所以最小修复就是把同一个值再加一个头名 —— 正是 #2327 在 session/llm.ts 里做的事。在该 PR 合并前,用 MiMoCode 官方的 chat.headers 钩子即可从用户侧补上。

    安装:存为 ~/.config/mimocode/hooks/opencode-session.ts(或 <项目>/.mimocode/hooks/)。下一回合热加载,无需重启,零依赖,代码见上。

    验证:mimo run -m opencode-go/<模型> "回复ok"。装之前报 Request is missing x-opencode-session and cannot be routed efficiently(硬拦截),装之后正常返回;非 opencode* provider 不受影响(钩子里提前 return)。

    注意:

    • 瞬时请求(会话标题、摘要)不走 chat.headers,仍无该头 —— 与 fix(session): send x-opencode-session on outbound requests #2327 的 non-ephemeral 条件一致。
    • 头值取 input.sessionID,与已在发的 x-session-affinity 同源,不新造 ID、不改变路由假设。
    • 上游发版带修复后即可退役:先 grep -a -c 'x-opencode-session' "$(command -v mimo)" 确认 ≥1,别默认「升级了=修好了」;停用须改扩展名或删除(加载 glob 是 {hook,hooks}/*.{js,ts},*.disabled.ts 照样会被加载)。
  7. DaweiTian commented on Sep 15, 2026

    @DaweiTian

    牛逼,感谢大哥,终于可以用了

  8. MeiHuaGuangShuo commented on Sep 15, 2026

    @MeiHuaGuangShuo

    刚让dsv4.1f做了一个,没想到有人已经做出来了。不过我是v0.1.12,我这个版本的使用下面的插件可以正常让会话标题带要求的请求头以正常请求实现短标题生成

    /**
     * opencode-go-session —— 给 OpenCode Go 补上它要求的 x-opencode-session 请求头喵
     *
     * 背景
     *   OpenCode Go(https://opencode.ai/zen/go/v1)自 2026-09-06 起要求每个请求都带上
     *   一个"每个会话稳定"的 ID,用来把同一段对话的请求固定路由到同一台后端,好复用
     *   prompt cache。MiMoCode 是 OpenCode 的 fork,但它只发自己那套 x-session-affinity,
     *   不带 x-opencode-session,于是被网关直接挡回来:
     *
     *     Error from provider (Console Go): Request is missing x-opencode-session
     *     and cannot be routed efficiently.
     *
     * 覆盖面(2026-09-15 实测)
     *   MiMoCode 在「所有 LLM 请求发出前」的统一位置触发 chat.headers,参数里的 agent
     *   是动态的。实测已确认以下请求都会经过本插件并带头:
     *     - 主对话   agent=build / plan ...
     *     - 会话标题 agent=title(自动选用 small 模型,如 deepseek-v4.1-flash)
     *   而上下文压缩、checkpoint 写入等走的是同一个入口,同样会被覆盖。
     *   同一个会话内,标题请求与主对话共用同一 session ID —— 正是上游要的路由亲和语义。
     *
     * 做法
     *   只挂 MiMoCode 官方的插件钩子 chat.headers,往出站请求头上补一层就完事。
     *   不改 MiMoCode 本体、不动它 130MB 的二进制、不改 provider / auth 配置,
     *   删掉这个文件即可完全复原。
     *
     * 安放位置(两者都已在 v0.1.12 上实测会被自动加载)
     *   全局:~/.config/mimocode/plugin/opencode-go-session.ts
     *   单项目:<项目根>/.mimocode/plugin/opencode-go-session.ts
     *
     * 停用
     *   把下面 ENABLED 改成 false,或者直接删掉文件。
     */
    
    /** 总开关喵 */
    const ENABLED = true
    
    /** 按 provider id 认(MiMoCode 内置的 OpenCode Go provider id 就是 opencode-go) */
    const PROVIDER_IDS = ["opencode-go"]
    
    /** provider id 全都读不到时,用 URL 兜底再认一次 */
    const BASE_URL_MARKERS = ["opencode.ai/zen/go"]
    
    /**
     * 客户端标识头(可选,默认关闭)。
     *
     * OpenCode Go 官方文档只要求两件事:
     *   1. 用能识别客户端的 User-Agent(要像 my-coding-agent/1.0,不能是通用 SDK 名)
     *   2. 用 x-opencode-session 携带每会话稳定的 ID
     * 文档通篇没有要求 x-opencode-client,实测把它去掉后请求照常成功。
     * 既然非必需,默认就不发:少一个上游未定义的头,也避免被当成伪装客户端。
     * 想发的话填个字符串即可(例如 "mimocode"),会以 x-opencode-client 发出。
     */
    const CLIENT_HEADER = ""
    
    /** 会话 ID 的取值来源,默认用 MiMoCode 自己的 sessionID(形如 ses_xxx),天然每会话稳定 */
    const SESSION_FIELD = "sessionID"
    
    const pickStrings = (list) => list.filter((x) => typeof x === "string" && x.length > 0)
    
    /**
     * provider id 的三条来路,按可靠度排序喵:
     *   1. input.model.providerID —— MiMoCode 内置插件(Xiaomi / Copilot 那几个)就用这个字段,
     *      实测 v0.1.12 稳定可读,最可靠
     *   2. input.provider.id      —— 实测可读;注意运行时 provider 是**扁平**对象,
     *      并没有 SDK 类型声明里的 info 那一层
     *   3. input.provider.info.id —— 类型声明里的形状,当前版本取不到,留着兼容将来
     */
    const readProviderIds = (input) =>
      pickStrings([input?.model?.providerID, input?.provider?.id, input?.provider?.info?.id])
    
    /**
     * URL 兜底。优先用 input.model.api.url —— 它是模型自带的上游地址,
     * 即使有人在配置里覆盖了 provider.options.baseURL 也不会跟着变,比读 baseURL 准。
     */
    const readUrls = (input) =>
      pickStrings([
        input?.model?.api?.url,
        input?.provider?.options?.baseURL,
        input?.provider?.options?.baseUrl,
        input?.provider?.info?.options?.baseURL,
        input?.provider?.info?.options?.baseUrl,
      ])
    
    const isGoRequest = (input) => {
      if (readProviderIds(input).some((id) => PROVIDER_IDS.includes(id))) return true
      return readUrls(input).some((url) => BASE_URL_MARKERS.some((marker) => url.includes(marker)))
    }
    
    export const OpenCodeGoSession = async () => ({
      // chat.params 与 chat.headers 是同一处触发的,二者入参一致;
      // 这里只挂 headers 就够,params 不碰,免得掺和采样参数喵
      "chat.headers": async (input, output) => {
        if (!ENABLED) return
        if (!isGoRequest(input)) return
    
        // 拿不到会话 id 就什么都不塞,免得把上游的路由搅乱
        const sessionID = String(input?.[SESSION_FIELD] ?? "").trim()
        if (!sessionID) return
    
        output.headers["x-opencode-session"] = sessionID
        if (CLIENT_HEADER) output.headers["x-opencode-client"] = CLIENT_HEADER
      },
    })
  9. DaweiTian commented on Sep 15, 2026

    @DaweiTian

    [

    opencode-session.js

    ](url)
    Linux 上面的tui用前面Pegasus-lt老哥的hooks就行,如果用Xiaomi Mimo desktop 内测版的同学,可以用这个插件,Windows下,MAC不太清楚hooks能不能加载,反正Windows客户端没法加载hooks。

  10. MeiHuaGuangShuo commented on Sep 16, 2026

    @MeiHuaGuangShuo

    [

    opencode-session.js

    ](url) Linux 上面的tui用前面Pegasus-lt老哥的hooks就行,如果用Xiaomi Mimo desktop 内测版的同学,可以用这个插件,Windows下,MAC不太清楚hooks能不能加载,反正Windows客户端没法加载hooks。

    我是Windows下是v0.1.12 bun二进制编译版,实测是支持hook的。

  11. Pegasus-lt commented on Sep 16, 2026

    @Pegasus-lt

    Windows 上怎么用这个 workaround

    一、文件放哪里

    原生 Windows 上别放 hooks/——那个加载器在 Windows 上是坏的(原因见最后一节)。要放 plugin/ 目录,二选一:

    作用范围 路径
    全局(所有项目生效) %USERPROFILE%\.config\mimocode\plugin\opencode-session.ts
    单个项目 <项目目录>\.mimocode\plugin\opencode-session.ts

    目录名写 plugin 还是 plugins 都行,两个都会被扫到。

    如果 MiMoCode 跑在 WSL 里,本节不适用——直接按上面第一条评论的 ~/.config/mimocode/hooks/opencode-session.ts 做即可。

    二、文件内容

    // MiMoCode #2317 的临时方案。上游把 x-opencode-session 做进客户端后,本文件即可删除。
    // 注意两点:要放在 plugin/ 目录,并且必须导出「工厂函数」(不能直接 export default {...})。
    const TARGET_PROVIDERS = new Set(["opencode-go", "opencode"])
    
    export const OpenCodeSessionPlugin = async () => ({
      "chat.headers": async (input, output) => {
        if (!input.sessionID) return
        if (!TARGET_PROVIDERS.has(input.model?.providerID)) return
        output.headers["x-opencode-session"] = input.sessionID
      },
    })

    三、怎么确认生效

    跑一条 opencode-go 的请求:

    mimo run -m opencode-go/deepseek-v4-flash "回复 ok"

    装之前会被上游硬拦,报 Request is missing x-opencode-session and cannot be routed efficiently;装之后正常返回。

    想更确定,可以看日志里有没有加载成功那一行:

    Select-String -Path "$env:USERPROFILE\.local\share\mimocode\log\*.log" -Pattern "loading plugin"

    四、两个容易踩的坑

    1. 必须是工厂函数。 直接写 export default { "chat.headers": ... } 会抛 Plugin export is not a function——那是 hook 文件的写法,在 Windows 上用不了。
    2. 启动可能被依赖安装卡住。 只要 plugin/ 目录里有文件,MiMoCode 启动时会先等 @mimo-ai/plugin@<对应版本> 的后台安装完成,才开始加载插件;装不上(离线、代理不通)就会卡在启动阶段。先手动装好可以避免:
    cd $env:USERPROFILE\.config\mimocode
    npm i @mimo-ai/plugin@0.1.14

    五、什么时候可以删

    上游把 x-opencode-session 做进客户端之后(PR #2327 合入并升级到带修复的版本),本文件就没用了,直接删掉。判断办法:在 mimo 可执行文件里搜一下 x-opencode-session,能搜到就说明客户端已经自带这个头。

    附:为什么不能用 hooks/

    Windows 上 hooks/ 目录里的文件永远加载不了。加载器先把钩子转译成临时文件,再用裸 Windows 路径去 import,没有走 pathToFileURL;E:\...\xxx.mjs 会被当成模块名去解析,必然失败,日志里表现为 Cannot find module ... failed to load file hook。

    这是上游已知问题,7 月就报了:#1813、#1753、#2359;v0.1.12 与 v0.1.14 的加载器逐字节相同,所以不是某个版本才坏的。修复是一行 import(pathToFileURL(tmpFile).href);在它合入之前,Windows 上只能走上面的 plugin/ 路线。

  12. Pegasus-lt commented on Sep 16, 2026

    @Pegasus-lt

    [
    opencode-session.js
    ](url) Linux 上面的tui用前面Pegasus-lt老哥的hooks就行,如果用Xiaomi Mimo desktop 内测版的同学,可以用这个插件,Windows下,MAC不太清楚hooks能不能加载,反正Windows客户端没法加载hooks。

    我是Windows下是v0.1.12 bun二进制编译版,实测是支持hook的。

    我测试好像不太行啊……

  13. Pegasus-lt commented on Sep 16, 2026

    @Pegasus-lt

    牛逼,感谢大哥,终于可以用了

    我也是拿Deepseek烧了一块钱搞的,用AI解决AI!

  14. MeiHuaGuangShuo commented on Sep 16, 2026

    @MeiHuaGuangShuo

    [
    opencode-session.js
    ](url) Linux 上面的tui用前面Pegasus-lt老哥的hooks就行,如果用Xiaomi Mimo desktop 内测版的同学,可以用这个插件,Windows下,MAC不太清楚hooks能不能加载,反正Windows客户端没法加载hooks。

    我是Windows下是v0.1.12 bun二进制编译版,实测是支持hook的。

    我测试好像不太行啊……

    我是使用插件的方式加载的,不是hook,参考我之前发的。在你这条消息之前有个人解释了为什么使用hook不行而插件可以,但是他删掉了

  15. Pegasus-lt commented on Sep 16, 2026

    @Pegasus-lt
  16. Kassell commented on Sep 18, 2026

    @Kassell

    不是我没看懂啊,是什么操作opencode,让生态去兼容你????自己不准守标准。在我看来这种pr就是给项目带来技术债,我是维护者的话我是不会合的

  17. lcthe commented on Sep 23, 2026

    @lcthe

    macOS 桌面版(Xiaomi MiMo Desktop)已用本 issue 里的插件方案解决,确认可用 ✅

    环境

    • Xiaomi MiMo Desktop 26.922.220226(macOS 26.2, Apple Silicon, Mac mini),内嵌 MiMoCode 引擎
    • 错误现象与楼主一致:Request is missing x-opencode-session and cannot be routed efficiently. Please see https://opencode.ai/docs/go/#where-can-i-use-it
    • 引擎日志确认报错来自 provider opencode-go-chat → https://opencode.ai/zen/go/v1/chat/completions

    完整流程(在 macOS 桌面版上验证通过)

    1. 下载本 issue 中 @MeiHuaGuangShuo 提供的 opencode-session.js(桌面版适用:桌面版通过原生 import() 加载 plugin,不走 hooks 加载器)。

    2. 通读源码审计后(32 行,只做补头,无任何多余行为),放到全局插件目录:

      ~/.config/mimocode/plugin/opencode-session.js
      

      (桌面版引擎实际读写 ~/.config/mimocode/,与 CLI 共用配置目录;mimocode.jsonc 里的 opencode-go provider 不用改。)

    3. 完全退出桌面版(Cmd+Q)后重新启动,让内嵌引擎重新加载插件。

    4. 用 opencode-go 模型发消息测试:修复前必报上述错误,修复后正常回复。

    确认两点

    • 之前评论里不确定“MAC 不太清楚 hooks 能不能加载”——结论是:macOS 桌面版同样走 plugin 方案即可,本插件实测有效。
    • 瞬时请求(会话标题生成等)仍不走 chat.headers,标题生成可能偶尔报错,主体对话不受影响,与 fix(session): send x-opencode-session on outbound requests #2327 的 non-ephemeral 条件一致。

    桌面版单独立了个跟踪 issue:#2477。等 #2327 合并发版后就删掉这个插件文件。

  18. Ruandou commented on Sep 29, 2026

    @Ruandou

    要是每家中转站都来提一个这种SB 要求???

  19. added a commit that references this issue on Oct 13, 2026
    597e710
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions