@jacobbd/relay-ai 0.9.8 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,13 @@ type RelayCoreErrorCode = 'INVALID_ROUTE_ID' | 'ROUTE_NOT_FOUND' | 'PROVIDER_DIS
16
16
  */
17
17
  type RelayReasoningLevel = 'off' | 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
18
18
  interface CreateRelayModelOptions {
19
+ /**
20
+ * Optional stable conversation identifier for OpenCode Go. Relay sends this
21
+ * as `x-opencode-session` on every upstream model call for the returned model.
22
+ * The value is treated as opaque and bounded to OpenCode's 256-character
23
+ * header limit. Per-call `headers` supplied to the AI SDK take precedence.
24
+ */
25
+ sessionId?: string;
19
26
  /**
20
27
  * Optional sanitized transport diagnostics. Messages contain event types,
21
28
  * field names, counts, and lengths — never credentials, prompts, or bodies.
@@ -50,7 +50,7 @@ import { join as join2 } from "path";
50
50
  // package.json
51
51
  var package_default = {
52
52
  name: "@jacobbd/relay-ai",
53
- version: "0.9.8",
53
+ version: "0.10.1",
54
54
  publishConfig: {
55
55
  access: "public"
56
56
  },
@@ -2099,7 +2099,9 @@ var PROVIDER_TEMPLATES = [
2099
2099
  {
2100
2100
  id: "antigravity",
2101
2101
  name: "Cloud Code Assist OAuth (Google)",
2102
- shortName: "Cloud Code Assist",
2102
+ // Compact label users actually recognise: every surface that shows this
2103
+ // provider calls it Antigravity, and it is also the shortest of the two.
2104
+ shortName: "Antigravity",
2103
2105
  authType: "oauth",
2104
2106
  npm: "@ai-sdk/openai-compatible",
2105
2107
  signupUrl: "https://antigravity.google",
@@ -2204,6 +2206,55 @@ function isRelayCoreError(err) {
2204
2206
  return err instanceof RelayCoreError;
2205
2207
  }
2206
2208
 
2209
+ // src/opencode-session.ts
2210
+ import { randomUUID as randomUUID2 } from "crypto";
2211
+ var OPENCODE_SESSION_HEADER = "x-opencode-session";
2212
+ var MAX_OPENCODE_SESSION_LENGTH = 256;
2213
+ var RELAY_USER_AGENT = `relay-ai/${VERSION}`;
2214
+ function sanitizeSessionId(value) {
2215
+ if (typeof value !== "string") return void 0;
2216
+ const normalized = value.trim();
2217
+ if (!normalized || normalized.length > MAX_OPENCODE_SESSION_LENGTH) return void 0;
2218
+ if (/[\u0000-\u001f\u007f\r\n]/.test(normalized)) return void 0;
2219
+ return normalized;
2220
+ }
2221
+ function isOpenCodeGoEndpoint(providerId, endpoint) {
2222
+ const id = providerId?.trim().toLowerCase();
2223
+ if (id === "go" || id === "opencode-go") return true;
2224
+ if (!endpoint) return false;
2225
+ try {
2226
+ const url = new URL(endpoint);
2227
+ if (url.hostname.toLowerCase() !== "opencode.ai") return false;
2228
+ return url.pathname.split("/").some((segment) => segment.toLowerCase() === "go");
2229
+ } catch {
2230
+ return false;
2231
+ }
2232
+ }
2233
+ function mergeHeaders(base, overrides) {
2234
+ const result = {};
2235
+ for (const [key, value] of Object.entries(base ?? {})) {
2236
+ if (typeof value === "string") result[key] = value;
2237
+ }
2238
+ for (const [key, value] of Object.entries(overrides ?? {})) {
2239
+ for (const existing of Object.keys(result)) {
2240
+ if (existing.toLowerCase() === key.toLowerCase()) delete result[existing];
2241
+ }
2242
+ result[key] = value;
2243
+ }
2244
+ return result;
2245
+ }
2246
+ function openCodeGoHeaders(providerId, endpoint, sessionId, baseHeaders, options) {
2247
+ if (!isOpenCodeGoEndpoint(providerId, endpoint)) return void 0;
2248
+ let normalizedSessionId = sanitizeSessionId(sessionId);
2249
+ if (!normalizedSessionId && (options?.generateFallbackSession ?? true)) {
2250
+ normalizedSessionId = `relay-${randomUUID2()}`;
2251
+ }
2252
+ return mergeHeaders(baseHeaders, {
2253
+ "User-Agent": RELAY_USER_AGENT,
2254
+ ...normalizedSessionId ? { [OPENCODE_SESSION_HEADER]: normalizedSessionId } : {}
2255
+ });
2256
+ }
2257
+
2207
2258
  // src/core/reasoning.ts
2208
2259
  var RELAY_REASONING_LEVELS = [
2209
2260
  "off",
@@ -2277,6 +2328,22 @@ async function withReasoningProviderOptions(model, providerOptions) {
2277
2328
  }
2278
2329
  });
2279
2330
  }
2331
+ async function withRequestHeaders(model, headers) {
2332
+ const { wrapLanguageModel: wrapLanguageModel2 } = await import("ai");
2333
+ return wrapLanguageModel2({
2334
+ model,
2335
+ middleware: {
2336
+ specificationVersion: "v3",
2337
+ transformParams: async ({ params }) => ({
2338
+ ...params,
2339
+ headers: mergeHeaders(
2340
+ headers,
2341
+ params.headers
2342
+ )
2343
+ })
2344
+ }
2345
+ });
2346
+ }
2280
2347
 
2281
2348
  // src/core/route-id.ts
2282
2349
  var SEPARATOR = "::";
@@ -3509,7 +3576,7 @@ function providerRefreshToken(providerId, authType, authRef) {
3509
3576
  }
3510
3577
 
3511
3578
  // src/core/antigravity-model.ts
3512
- import { randomUUID as randomUUID2 } from "crypto";
3579
+ import { randomUUID as randomUUID3 } from "crypto";
3513
3580
  var CLOUD_CODE_BASES = ANTIGRAVITY_BASE_URLS.map((base) => base.replace(/\/+$/, ""));
3514
3581
  var CLOUD_CODE_BASE = CLOUD_CODE_BASES[0];
3515
3582
  var STREAM_URLS = CLOUD_CODE_BASES.map((base) => `${base}/${ANTIGRAVITY_API_VERSION}:streamGenerateContent?alt=sse`);
@@ -3596,7 +3663,7 @@ function createCloudCodeFetch(options, fetchImpl) {
3596
3663
  const geminiBody = await readJsonBody(input, init);
3597
3664
  const envelope = {
3598
3665
  project: options.projectId,
3599
- requestId: randomUUID2(),
3666
+ requestId: randomUUID3(),
3600
3667
  model: options.modelId,
3601
3668
  userAgent: ANTIGRAVITY_USER_AGENT,
3602
3669
  requestType: "agent",
@@ -3791,7 +3858,21 @@ async function createRelayModel(routeId, options) {
3791
3858
  const registry = loadCoreRegistry();
3792
3859
  const { provider, model } = findRoute(registry, providerId, modelId, routeId);
3793
3860
  const reasoningOptions = options?.reasoning === void 0 ? void 0 : resolveReasoningProviderOptions(options.reasoning, provider, model, routeId);
3794
- const finish = (built) => reasoningOptions ? withReasoningProviderOptions(built, reasoningOptions) : Promise.resolve(built);
3861
+ const transportHeaders = openCodeGoHeaders(
3862
+ provider.id,
3863
+ model.apiUrl ?? provider.api.url,
3864
+ options?.sessionId,
3865
+ provider.api.headers,
3866
+ // The returned model may be reused across conversations, so never fabricate a
3867
+ // session here — a baked-in id would blend histories. The host passes sessionId.
3868
+ { generateFallbackSession: false }
3869
+ );
3870
+ const finish = async (built) => {
3871
+ let finished = built;
3872
+ if (reasoningOptions) finished = await withReasoningProviderOptions(finished, reasoningOptions);
3873
+ if (transportHeaders) finished = await withRequestHeaders(finished, transportHeaders);
3874
+ return finished;
3875
+ };
3795
3876
  if (isAntigravityCloudCodeRoute(provider, model)) {
3796
3877
  const apiKey2 = await resolveCredential(provider, routeId);
3797
3878
  const providerData2 = await resolveProviderOAuthProviderData(provider.authRef);