{"version":3,"file":"opencode-headers.d.ts","sourceRoot":"","sources":["../../src/providers/opencode-headers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAK5D;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,GAAG,OAAO,CASxF;AAQD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,SAAS,aAAa,GAAG,SAAS,EACnF,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC,EAC3D,OAAO,EAAE,QAAQ,GACf,QAAQ,CAYV","sourcesContent":["import type { Api, Model, StreamOptions } from \"../types.js\";\n\n/**\n * HTTP header OpenCode uses for per-conversation routing and optimization.\n * Must be a stable identifier for the lifetime of one conversation (issue 500).\n */\nexport const OPENCODE_SESSION_HEADER = \"x-opencode-session\";\n\n/** Provider IDs of the built-in OpenCode endpoints. */\nconst OPENCODE_PROVIDER_IDS = new Set([\"opencode\", \"opencode-go\"]);\n\n/**\n * Returns true when the model points at an OpenCode endpoint: either one of the\n * built-in OpenCode providers, or a custom provider whose base URL parses to\n * exactly the `opencode.ai` hostname. Hostname equality is exact (case-insensitive,\n * no substring matching) so lookalike domains never receive session IDs.\n */\nexport function isOpenCodeModel(model: Pick<Model<Api>, \"provider\" | \"baseUrl\">): boolean {\n\tif (OPENCODE_PROVIDER_IDS.has(model.provider)) {\n\t\treturn true;\n\t}\n\ttry {\n\t\treturn new URL(model.baseUrl).hostname.toLowerCase() === \"opencode.ai\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction hasHeader(headers: Record<string, string> | undefined, name: string): boolean {\n\tif (!headers) return false;\n\tconst wanted = name.toLowerCase();\n\treturn Object.keys(headers).some((key) => key.toLowerCase() === wanted);\n}\n\n/**\n * Returns options with `x-opencode-session` added for OpenCode models when a\n * session ID is present. The input is never mutated; a new options object is\n * returned when the header is added.\n *\n * Precedence: an explicit header in `model.headers` or `options.headers` wins\n * case-insensitively, so no differently-cased duplicate of the generated header\n * can be sent. OpenCode models without a session ID, missing options, and\n * non-OpenCode models are returned unchanged.\n */\nexport function withOpenCodeSessionHeader<TOptions extends StreamOptions | undefined>(\n\tmodel: Pick<Model<Api>, \"provider\" | \"baseUrl\" | \"headers\">,\n\toptions: TOptions,\n): TOptions {\n\tif (!options) return options;\n\tconst sessionId = options.sessionId;\n\tif (!sessionId) return options;\n\tif (!isOpenCodeModel(model)) return options;\n\tif (hasHeader(model.headers, OPENCODE_SESSION_HEADER) || hasHeader(options.headers, OPENCODE_SESSION_HEADER)) {\n\t\treturn options;\n\t}\n\treturn {\n\t\t...options,\n\t\theaders: { ...options.headers, [OPENCODE_SESSION_HEADER]: sessionId },\n\t} as TOptions;\n}\n"]}