cd /news/developer-tools/opencode-zen-model-id-strip-proxy-se… · home topics developer-tools article
[ARTICLE · art-90789] src=gist.github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

OpenCode Zen model_id strip proxy + SessionStart hook for Grok Build (mise shim)

A developer has created a local proxy for OpenCode Zen that strips the non-standard `model_id` field from Grok CLI assistant history before forwarding requests to the upstream Chat Completions API. The proxy, written in Bun, sanitizes JSON bodies for `/chat/completions`, `/responses`, and `/messages` endpoints, ensuring compatibility with OpenCode Zen's API.

read5 min views16 publishedAug 6, 2026

| /** | |

  • OpenCode Zen 向けローカルプロキシ | |
  • | |
  • Grok CLI が assistant 履歴に付ける非標準フィールド model_id を | |
  • Chat Completions 送信前に削除してから upstream へ転送する | |
  • | |
  • 起動: bun ~/.grok/proxies/opencode-strip-model-id/proxy.ts | |
  • 設定: base_url = "http://127.0.0.1:8787/v1" | | / | | | | const LISTEN_HOST = process.env.PROXY_HOST ?? "127.0.0.1" | | const LISTEN_PORT = Number(process.env.PROXY_PORT ?? "8787") | | const UPSTREAM_ORIGIN = ( | | process.env.OPENCODE_UPSTREAM ?? "https://opencode.ai/zen" | | ).replace(//$/, "") | | const LOG = process.env.PROXY_LOG === "1" | | | | type JsonObject = Record<string, unknown> | | | | function isObject(value: unknown): value is JsonObject { | | return value != null && typeof value === "object" && !Array.isArray(value) | | } | | | | /* messages / input 配列内の model_id を削除する / | | function stripModelIdFromMessages(messages: unknown): { | | stripped: number | | value: unknown | | } { | | if (!Array.isArray(messages)) { | | return { stripped: 0, value: messages } | | } | | | | let stripped = 0 | | const next = messages.map((message) => { | | if (!isObject(message) || !("model_id" in message)) { | | return message | | } | | stripped += 1 | | const { model_id: _modelId, ...rest } = message | | return rest | | }) | | return { stripped, value: next } | | } | | | | /* Chat Completions / Responses など JSON body から model_id を除去 */ | | function sanitizeRequestBody(bodyText: string): { | | body: string | | stripped: number | | } { | | if (bodyText.trim() === "") { | | return { body: bodyText, stripped: 0 } | | } | | | | let parsed: unknown | | try { | | parsed = JSON.parse(bodyText) | | } catch { | | return { body: bodyText, stripped: 0 } | | } | | | | if (!isObject(parsed)) { | | return { body: bodyText, stripped: 0 } | | } | | | | let stripped = 0 | | | | if ("messages" in parsed) { | | const result = stripModelIdFromMessages(parsed.messages) | | parsed.messages = result.value | | stripped += result.stripped | | } | | | | // Responses API 互換 (将来用) | | if ("input" in parsed) { | | const result = stripModelIdFromMessages(parsed.input) | | parsed.input = result.value | | stripped += result.stripped | | } | | | | if (stripped === 0) { | | return { body: bodyText, stripped: 0 } | | } | | | | return { body: JSON.stringify(parsed), stripped } | | } | | | | function hopByHopHeaders(): Set<string> { | | return new Set([ | | "connection", | | "keep-alive", | | "proxy-authenticate", | | "proxy-authorization", | | "te", | | "trailers", | | "transfer-encoding", | | "upgrade", | | "host", | | "content-length", | | ]) | | } | | | | function copyRequestHeaders(req: Request): Headers { | | const headers = new Headers() | | const skip = hopByHopHeaders() | | req.headers.forEach((value, key) => { | | if (skip.has(key.toLowerCase())) { | | return | | } | | headers.set(key, value) | | }) | | return headers | | } | | | | function copyResponseHeaders(upstream: Response): Headers { | | const headers = new Headers() | | const skip = hopByHopHeaders() | | upstream.headers.forEach((value, key) => { | | if (skip.has(key.toLowerCase())) { | | return | | } | | headers.set(key, value) | | }) | | return headers | | } | | | | function log(...args: unknown[]): void { | | if (LOG) { | | console.log(new Date().toISOString(), ...args) | | } | | } | | | | const server = Bun.serve({ | | hostname: LISTEN_HOST, | | port: LISTEN_PORT, | | idleTimeout: 255, | | | | async fetch(req: Request): Promise<Response> { | | const url = new URL(req.url) | | const targetUrl = ${UPSTREAM_ORIGIN}${url.pathname}${url.search} | | const method = req.method.toUpperCase() | | | | try { | | const requestHeaders = copyRequestHeaders(req) | | let body: string | Uint8Array | undefined | | let stripped = 0 | | | | if (method !== "GET" && method !== "HEAD") { | | const raw = await req.text() | | const shouldSanitize = | | url.pathname.includes("/chat/completions") || | | url.pathname.includes("/responses") || | | url.pathname.endsWith("/messages") | | | | if (shouldSanitize && raw !== "") { | | const sanitized = sanitizeRequestBody(raw) | | body = sanitized.body | | stripped = sanitized.stripped | | if (stripped > 0) { | | requestHeaders.set( | | "content-type", | | requestHeaders.get("content-type") ?? "application/json" | | ) | | } | | } else { | | body = raw | | } | | } | | | | log( | | method, | | url.pathname, | | stripped > 0 ? stripped_model_id=${stripped} : "passthrough" | | ) | | | | const upstream = await fetch(targetUrl, { | | method, | | headers: requestHeaders, | | body, | | // @ts-expect-error Bun は duplex を受け付ける | | duplex: "half", | | }) | | | | // ストリームはそのままパイプ (SSE / chat streaming) | | return new Response(upstream.body, { | | status: upstream.status, | | statusText: upstream.statusText, | | headers: copyResponseHeaders(upstream), | | }) | | } catch (error) { | | const message = error instanceof Error ? error.message : String(error) | | console.error("[proxy] upstream error:", message) | | return new Response( | | JSON.stringify({ | | error: { | | message: proxy upstream failed: ${message}, | | type: "proxy_error", | | }, | | }), | | { | | status: 502, | | headers: { "content-type": "application/json" }, | | } | | ) | | } | | }, | | }) | | | | console.log( | | [opencode-strip-model-id] listening on http://${server.hostname}:${server.port} | | ) | | console.log([opencode-strip-model-id] upstream ${UPSTREAM_ORIGIN}) | | console.log( | | [opencode-strip-model-id] set base_url = "http://${server.hostname}:${server.port}/v1" | | ) | | console.log([opencode-strip-model-id] debug log: PROXY_LOG=1) |
── more in #developer-tools 4 stories · sorted by recency
── more on @opencode zen 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/opencode-zen-model-i…] indexed:0 read:5min 2026-08-06 ·