@jacobbd/relay-ai 0.9.1 → 0.9.2

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.
package/dist/cli.js CHANGED
@@ -197,7 +197,7 @@ import {
197
197
  validateCustomEndpointUrl,
198
198
  writeSecureLogLine,
199
199
  zenRegistryStub
200
- } from "./chunk-MFKBK6YL.js";
200
+ } from "./chunk-GQCFLSEM.js";
201
201
  import {
202
202
  filterTemplates,
203
203
  getTemplateById,
@@ -14869,7 +14869,7 @@ Options:
14869
14869
  --trace Write debug logs under ~/.relay-ai/logs/`);
14870
14870
  return 0;
14871
14871
  }
14872
- const { runUiCommand } = await import("./ui-command-ZJBZZZ4X.js");
14872
+ const { runUiCommand } = await import("./ui-command-SDMYDYT6.js");
14873
14873
  return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
14874
14874
  }
14875
14875
  if (parsed.command === "models") {
@@ -3,6 +3,13 @@ import { LanguageModel } from 'ai';
3
3
  /** Unconditionally-scoped route id: `${providerId}::${modelId}`. Never bare. */
4
4
  type RelayRouteId = `${string}::${string}`;
5
5
  type RelayCoreErrorCode = 'INVALID_ROUTE_ID' | 'ROUTE_NOT_FOUND' | 'PROVIDER_DISABLED' | 'CREDENTIAL_UNAVAILABLE' | 'OAUTH_REFRESH_FAILED' | 'UNSUPPORTED_MODEL' | 'UNSUPPORTED_REGISTRY_VERSION' | 'PROVIDER_LOAD_FAILED';
6
+ interface CreateRelayModelOptions {
7
+ /**
8
+ * Optional sanitized transport diagnostics. Messages contain event types,
9
+ * field names, counts, and lengths — never credentials, prompts, or bodies.
10
+ */
11
+ onDebug?: (message: string) => void;
12
+ }
6
13
  interface RelayModelDescriptor {
7
14
  routeId: RelayRouteId;
8
15
  providerId: string;
@@ -50,7 +57,7 @@ declare function listRelayModels(registryPath?: string): RelayModelDescriptor[];
50
57
  * resolved (and OAuth tokens refreshed) by Relay's existing machinery; the
51
58
  * credential and the intermediate spec never leave this function.
52
59
  */
53
- declare function createRelayModel(routeId: RelayRouteId): Promise<LanguageModel>;
60
+ declare function createRelayModel(routeId: RelayRouteId, options?: CreateRelayModelOptions): Promise<LanguageModel>;
54
61
 
55
62
  /**
56
63
  * Build a route id from a provider id and a model id. The provider id must pass
@@ -88,4 +95,4 @@ declare class RelayCoreError extends Error {
88
95
  }
89
96
  declare function isRelayCoreError(err: unknown): err is RelayCoreError;
90
97
 
91
- export { RelayCoreError, type RelayCoreErrorCode, type RelayModelDescriptor, type RelayRouteId, createRelayModel, isRelayCoreError, listRelayModels, parseRelayRouteId, toRelayRouteId };
98
+ export { type CreateRelayModelOptions, RelayCoreError, type RelayCoreErrorCode, type RelayModelDescriptor, type RelayRouteId, createRelayModel, isRelayCoreError, listRelayModels, parseRelayRouteId, toRelayRouteId };
@@ -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.1",
53
+ version: "0.9.2",
54
54
  publishConfig: {
55
55
  access: "public"
56
56
  },
@@ -288,7 +288,229 @@ async function refreshOpenAiAccessToken(refreshToken) {
288
288
 
289
289
  // src/oauth/responses-websocket.ts
290
290
  var RESPONSES_LITE_HEADER = "x-openai-internal-codex-responses-lite";
291
- var TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set(["response.completed", "response.failed", "response.incomplete"]);
291
+ var TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set(["response.completed", "response.failed", "response.incomplete", "error"]);
292
+ function isRecord(value) {
293
+ return !!value && typeof value === "object" && !Array.isArray(value);
294
+ }
295
+ function recordKeys(value) {
296
+ return isRecord(value) ? Object.keys(value).sort().join(",") : "";
297
+ }
298
+ function summarizeResponsesLiteEvent(event) {
299
+ if (!isRecord(event)) return `kind=${event == null ? "null" : typeof event}`;
300
+ const parts = [`type=${typeof event.type === "string" ? event.type : "unknown"}`, `keys=${recordKeys(event)}`];
301
+ if (typeof event.delta === "string") parts.push(`deltaChars=${event.delta.length}`);
302
+ if (typeof event.output_index === "number") parts.push(`hasOutputIndex=1`);
303
+ if (typeof event.item_id === "string") parts.push(`hasItemId=1`);
304
+ if (isRecord(event.item)) {
305
+ parts.push(`itemType=${typeof event.item.type === "string" ? event.item.type : "unknown"}`);
306
+ parts.push(`itemKeys=${recordKeys(event.item)}`);
307
+ if (typeof event.item.arguments === "string") parts.push(`argumentsChars=${event.item.arguments.length}`);
308
+ }
309
+ if (isRecord(event.response)) {
310
+ parts.push(`responseKeys=${recordKeys(event.response)}`);
311
+ if (Array.isArray(event.response.output)) {
312
+ parts.push(`outputCount=${event.response.output.length}`);
313
+ parts.push(`outputTypes=${event.response.output.map((item) => isRecord(item) && typeof item.type === "string" ? item.type : "unknown").join(",")}`);
314
+ }
315
+ if (isRecord(event.response.usage)) parts.push(`usageKeys=${recordKeys(event.response.usage)}`);
316
+ if (typeof event.response.status === "string") parts.push(`status=${event.response.status}`);
317
+ }
318
+ if (isRecord(event.error)) {
319
+ parts.push(`errorKeys=${recordKeys(event.error)}`);
320
+ if (typeof event.error.message === "string") parts.push(`messageChars=${event.error.message.length}`);
321
+ }
322
+ return parts.join(" ");
323
+ }
324
+ function createResponsesLiteNormalizeState() {
325
+ return {
326
+ nextId: 1,
327
+ lastOutputIndex: 0,
328
+ textDeltaForwarded: false,
329
+ messageAddedIds: /* @__PURE__ */ new Set(),
330
+ messageDoneIds: /* @__PURE__ */ new Set(),
331
+ functionAddedIndexes: /* @__PURE__ */ new Set(),
332
+ functionDeltaIndexes: /* @__PURE__ */ new Set(),
333
+ functionDoneCallIds: /* @__PURE__ */ new Set()
334
+ };
335
+ }
336
+ function nextId(state, prefix) {
337
+ const id = `${prefix}_${state.nextId}`;
338
+ state.nextId += 1;
339
+ return id;
340
+ }
341
+ function asString(value) {
342
+ return typeof value === "string" && value.length > 0 ? value : void 0;
343
+ }
344
+ function normalizeErrorEvent(event) {
345
+ const raw = isRecord(event.error) ? event.error : { message: typeof event.error === "string" ? event.error : "upstream error" };
346
+ return {
347
+ type: "error",
348
+ sequence_number: typeof event.sequence_number === "number" ? event.sequence_number : 0,
349
+ error: {
350
+ type: asString(raw.type) ?? "server_error",
351
+ code: asString(raw.code) ?? "unknown",
352
+ message: asString(raw.message) ?? "upstream error",
353
+ ...raw.param == null ? {} : { param: raw.param }
354
+ }
355
+ };
356
+ }
357
+ function normalizeFunctionItem(item, state, forDone = false) {
358
+ const callId = asString(item.call_id) ?? asString(item.id) ?? nextId(state, "call");
359
+ const id = asString(item.id) ?? nextId(state, "fc");
360
+ state.lastFunctionItemId = id;
361
+ return {
362
+ ...item,
363
+ type: "function_call",
364
+ id,
365
+ call_id: callId,
366
+ name: asString(item.name) ?? "",
367
+ arguments: typeof item.arguments === "string" ? item.arguments : "",
368
+ ...forDone ? { status: "completed" } : {}
369
+ };
370
+ }
371
+ function messageText(item) {
372
+ if (typeof item.text === "string") return item.text;
373
+ if (!Array.isArray(item.content)) return "";
374
+ let out = "";
375
+ for (const part of item.content) {
376
+ if (isRecord(part) && typeof part.text === "string" && (part.type === "output_text" || part.type === "text")) {
377
+ out += part.text;
378
+ }
379
+ }
380
+ return out;
381
+ }
382
+ function synthesizeMessage(item, outputIndex, state) {
383
+ const text = messageText(item);
384
+ if (!text) return [];
385
+ const id = asString(item.id) ?? nextId(state, "msg");
386
+ state.lastMessageItemId = id;
387
+ state.textDeltaForwarded = true;
388
+ state.messageAddedIds.add(id);
389
+ state.messageDoneIds.add(id);
390
+ return [
391
+ { type: "response.output_item.added", output_index: outputIndex, item: { type: "message", id } },
392
+ { type: "response.output_text.delta", item_id: id, delta: text },
393
+ { type: "response.output_item.done", output_index: outputIndex, item: { type: "message", id } }
394
+ ];
395
+ }
396
+ function synthesizeFunctionCall(item, outputIndex, state) {
397
+ const normalized = normalizeFunctionItem(item, state);
398
+ const callId = String(normalized.call_id);
399
+ if (state.functionDoneCallIds.has(callId)) return [];
400
+ const events = [];
401
+ if (!state.functionAddedIndexes.has(outputIndex)) {
402
+ events.push({
403
+ type: "response.output_item.added",
404
+ output_index: outputIndex,
405
+ item: { ...normalized, arguments: "" }
406
+ });
407
+ state.functionAddedIndexes.add(outputIndex);
408
+ }
409
+ if (!state.functionDeltaIndexes.has(outputIndex) && typeof normalized.arguments === "string" && normalized.arguments.length > 0) {
410
+ events.push({
411
+ type: "response.function_call_arguments.delta",
412
+ item_id: normalized.id,
413
+ output_index: outputIndex,
414
+ delta: normalized.arguments
415
+ });
416
+ state.functionDeltaIndexes.add(outputIndex);
417
+ }
418
+ events.push({
419
+ type: "response.output_item.done",
420
+ output_index: outputIndex,
421
+ item: { ...normalized, status: "completed" }
422
+ });
423
+ state.functionDoneCallIds.add(callId);
424
+ state.lastOutputIndex = outputIndex;
425
+ return events;
426
+ }
427
+ function recoverFromCompletedOutput(response, state) {
428
+ if (!Array.isArray(response.output)) return [];
429
+ const recovered = [];
430
+ response.output.forEach((item, index) => {
431
+ if (!isRecord(item) || typeof item.type !== "string") return;
432
+ if (item.type === "message" && !state.textDeltaForwarded) {
433
+ recovered.push(...synthesizeMessage(item, index, state));
434
+ } else if (item.type === "function_call") {
435
+ recovered.push(...synthesizeFunctionCall(item, index, state));
436
+ }
437
+ });
438
+ return recovered;
439
+ }
440
+ function normalizeResponsesLiteEvent(event, state) {
441
+ if (!isRecord(event) || typeof event.type !== "string") return [event];
442
+ if (event.type === "error") return [normalizeErrorEvent(event)];
443
+ if (event.type === "response.output_item.added" && isRecord(event.item)) {
444
+ const outputIndex = typeof event.output_index === "number" ? event.output_index : state.lastOutputIndex;
445
+ state.lastOutputIndex = outputIndex;
446
+ if (event.item.type === "message") {
447
+ const id = asString(event.item.id) ?? nextId(state, "msg");
448
+ state.lastMessageItemId = id;
449
+ state.messageAddedIds.add(id);
450
+ return [{ ...event, output_index: outputIndex, item: { ...event.item, id } }];
451
+ }
452
+ if (event.item.type === "function_call") {
453
+ const item = normalizeFunctionItem(event.item, state);
454
+ state.functionAddedIndexes.add(outputIndex);
455
+ return [{ ...event, output_index: outputIndex, item }];
456
+ }
457
+ return [{ ...event, output_index: outputIndex }];
458
+ }
459
+ if (event.type === "response.output_item.done" && isRecord(event.item)) {
460
+ const outputIndex = typeof event.output_index === "number" ? event.output_index : state.lastOutputIndex;
461
+ if (event.item.type === "function_call") {
462
+ const item = normalizeFunctionItem(event.item, state, true);
463
+ const callId = String(item.call_id);
464
+ state.functionDoneCallIds.add(callId);
465
+ return [{ ...event, output_index: outputIndex, item }];
466
+ }
467
+ if (event.item.type === "message") {
468
+ const id = asString(event.item.id) ?? state.lastMessageItemId ?? nextId(state, "msg");
469
+ state.lastMessageItemId = id;
470
+ state.messageDoneIds.add(id);
471
+ return [{ ...event, output_index: outputIndex, item: { ...event.item, id } }];
472
+ }
473
+ return [{ ...event, output_index: outputIndex }];
474
+ }
475
+ if (event.type === "response.output_text.delta") {
476
+ const itemId = asString(event.item_id) ?? state.lastMessageItemId ?? nextId(state, "msg");
477
+ state.lastMessageItemId = itemId;
478
+ state.textDeltaForwarded = true;
479
+ const events = [];
480
+ if (!state.messageAddedIds.has(itemId)) {
481
+ events.push({
482
+ type: "response.output_item.added",
483
+ output_index: state.lastOutputIndex,
484
+ item: { type: "message", id: itemId }
485
+ });
486
+ state.messageAddedIds.add(itemId);
487
+ }
488
+ events.push({ ...event, item_id: itemId, delta: typeof event.delta === "string" ? event.delta : "" });
489
+ return events;
490
+ }
491
+ if (event.type === "response.function_call_arguments.delta") {
492
+ const itemId = asString(event.item_id) ?? state.lastFunctionItemId ?? nextId(state, "fc");
493
+ const outputIndex = typeof event.output_index === "number" ? event.output_index : state.lastOutputIndex;
494
+ state.lastFunctionItemId = itemId;
495
+ state.lastOutputIndex = outputIndex;
496
+ state.functionDeltaIndexes.add(outputIndex);
497
+ return [{ ...event, item_id: itemId, output_index: outputIndex, delta: typeof event.delta === "string" ? event.delta : "" }];
498
+ }
499
+ if (event.type === "response.completed" || event.type === "response.incomplete") {
500
+ const response = isRecord(event.response) ? event.response : {};
501
+ const recovered = recoverFromCompletedOutput(response, state);
502
+ if (state.lastMessageItemId && state.textDeltaForwarded && !state.messageDoneIds.has(state.lastMessageItemId)) {
503
+ recovered.push({
504
+ type: "response.output_item.done",
505
+ output_index: state.lastOutputIndex,
506
+ item: { type: "message", id: state.lastMessageItemId }
507
+ });
508
+ state.messageDoneIds.add(state.lastMessageItemId);
509
+ }
510
+ return [...recovered, event];
511
+ }
512
+ return [event];
513
+ }
292
514
  function toHeaderRecord(headers) {
293
515
  const out = {};
294
516
  if (!headers) return out;
@@ -346,10 +568,14 @@ function createResponsesWebSocketFetch(wsUrl, log) {
346
568
  if (hasResponsesLiteHeader(headers)) {
347
569
  payload = applyResponsesLiteShape(payload);
348
570
  }
571
+ debug(
572
+ `request type=response.create keys=${Object.keys(payload).sort().join(",")} toolCount=${Array.isArray(payload.tools) ? payload.tools.length : 0} store=${String(payload.store)} parallelToolCalls=${String(payload.parallel_tool_calls)} reasoningKeys=${recordKeys(payload.reasoning)}`
573
+ );
349
574
  const outgoing = JSON.stringify({ type: "response.create", ...payload });
350
575
  const encoder = new TextEncoder();
351
576
  let socket;
352
577
  let frameCount = 0;
578
+ const normalizeState = createResponsesLiteNormalizeState();
353
579
  const stream = new ReadableStream({
354
580
  start(controller) {
355
581
  let closed = false;
@@ -367,13 +593,12 @@ function createResponsesWebSocketFetch(wsUrl, log) {
367
593
  };
368
594
  const fail = (message) => {
369
595
  if (closed) return;
370
- debug(`fail: ${message}`);
596
+ debug(`fail messageChars=${message.length}`);
371
597
  try {
372
- controller.enqueue(encoder.encode(
373
- `data: ${JSON.stringify({ type: "error", error: { message } })}
598
+ const [errorEvent] = normalizeResponsesLiteEvent({ type: "error", error: { message } }, normalizeState);
599
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(errorEvent)}
374
600
 
375
- `
376
- ));
601
+ `));
377
602
  } catch {
378
603
  }
379
604
  close();
@@ -389,28 +614,31 @@ function createResponsesWebSocketFetch(wsUrl, log) {
389
614
  socket.on("message", (data) => {
390
615
  const text = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
391
616
  frameCount += 1;
392
- if (frameCount <= 3) debug(`frame#${frameCount}: ${text.slice(0, 200)}`);
393
617
  let event;
394
618
  try {
395
619
  event = JSON.parse(text);
396
620
  } catch {
621
+ debug(`frame#${frameCount} non-json chars=${text.length}`);
397
622
  controller.enqueue(encoder.encode(`data: ${text.replace(/\r?\n/g, " ")}
398
623
 
399
624
  `));
400
625
  return;
401
626
  }
402
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}
627
+ if (frameCount <= 8) debug(`frame#${frameCount} ${summarizeResponsesLiteEvent(event)}`);
628
+ for (const next of normalizeResponsesLiteEvent(event, normalizeState)) {
629
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(next)}
403
630
 
404
631
  `));
405
- const type = event.type;
406
- if (typeof type === "string" && TERMINAL_EVENT_TYPES.has(type)) {
632
+ }
633
+ const type = isRecord(event) && typeof event.type === "string" ? event.type : void 0;
634
+ if (type && TERMINAL_EVENT_TYPES.has(type)) {
407
635
  debug(`terminal event: ${type} (after ${frameCount} frames)`);
408
636
  close();
409
637
  }
410
638
  });
411
639
  socket.on("error", (err) => fail(err.message));
412
640
  socket.on("close", (code, reason) => {
413
- debug(`close code=${code} frames=${frameCount}${reason?.length ? ` reason=${reason.toString("utf8").slice(0, 200)}` : ""}`);
641
+ debug(`close code=${code} frames=${frameCount}${reason?.length ? ` reasonChars=${reason.length}` : ""}`);
414
642
  if (closed) return;
415
643
  if (code === 1e3 || code === 1005) {
416
644
  close();
@@ -1513,6 +1741,12 @@ var SCOPES = [
1513
1741
  ].join(" ");
1514
1742
  var ANTIGRAVITY_VERSION = "4.2.0";
1515
1743
  var ANTIGRAVITY_USER_AGENT = `vscode/1.X.X (Antigravity/${ANTIGRAVITY_VERSION})`;
1744
+ var ANTIGRAVITY_BASE_URLS = [
1745
+ "https://daily-cloudcode-pa.googleapis.com",
1746
+ "https://cloudcode-pa.googleapis.com",
1747
+ "https://daily-cloudcode-pa.sandbox.googleapis.com"
1748
+ ];
1749
+ var ANTIGRAVITY_API_VERSION = "v1internal";
1516
1750
  async function refreshAntigravityToken(refreshToken) {
1517
1751
  return postOAuthRefresh(
1518
1752
  TOKEN_URL3,
@@ -2401,7 +2635,185 @@ function providerRefreshToken(providerId, authType, authRef) {
2401
2635
  return () => forceRefreshProviderCredential(providerId, authRef ?? oauthAuthRef(providerId));
2402
2636
  }
2403
2637
 
2638
+ // src/core/antigravity-model.ts
2639
+ import { randomUUID as randomUUID2 } from "crypto";
2640
+ var CLOUD_CODE_BASE = ANTIGRAVITY_BASE_URLS[0].replace(/\/+$/, "");
2641
+ var STREAM_URL = `${CLOUD_CODE_BASE}/${ANTIGRAVITY_API_VERSION}:streamGenerateContent?alt=sse`;
2642
+ var UNARY_URL = `${CLOUD_CODE_BASE}/${ANTIGRAVITY_API_VERSION}:generateContent`;
2643
+ var SDK_BASE_URL = `${CLOUD_CODE_BASE}/v1beta`;
2644
+ function unwrapCloudCodeSsePayload(payload) {
2645
+ const trimmed = payload.trim();
2646
+ if (trimmed === "" || trimmed === "[DONE]") return payload;
2647
+ try {
2648
+ const parsed = JSON.parse(trimmed);
2649
+ if (isWrappedCloudCodeBody(parsed)) {
2650
+ return JSON.stringify(parsed.response);
2651
+ }
2652
+ } catch {
2653
+ }
2654
+ return payload;
2655
+ }
2656
+ function unwrapCloudCodeJsonBody(text) {
2657
+ try {
2658
+ const parsed = JSON.parse(text);
2659
+ if (isWrappedCloudCodeBody(parsed)) {
2660
+ return JSON.stringify(parsed.response);
2661
+ }
2662
+ } catch {
2663
+ }
2664
+ return text;
2665
+ }
2666
+ function consumeCloudCodeSseBuffer(buffer) {
2667
+ const separator = /\r?\n\r?\n/;
2668
+ let rest = buffer;
2669
+ let emitted = "";
2670
+ while (true) {
2671
+ const match = separator.exec(rest);
2672
+ if (!match || match.index === void 0) break;
2673
+ const rawEvent = rest.slice(0, match.index);
2674
+ const sep = match[0];
2675
+ rest = rest.slice(match.index + sep.length);
2676
+ emitted += transformSseEvent(rawEvent) + sep;
2677
+ }
2678
+ return { emitted, rest };
2679
+ }
2680
+ function createCloudCodeSseUnwrapper() {
2681
+ const decoder = new TextDecoder();
2682
+ const encoder = new TextEncoder();
2683
+ let pending = "";
2684
+ return new TransformStream({
2685
+ transform(chunk, controller) {
2686
+ pending += decoder.decode(chunk, { stream: true });
2687
+ const { emitted, rest } = consumeCloudCodeSseBuffer(pending);
2688
+ pending = rest;
2689
+ if (emitted) controller.enqueue(encoder.encode(emitted));
2690
+ },
2691
+ flush(controller) {
2692
+ pending += decoder.decode();
2693
+ if (!pending) return;
2694
+ const { emitted, rest } = consumeCloudCodeSseBuffer(pending);
2695
+ const tail = emitted + (rest ? transformSseEvent(rest) : "");
2696
+ if (tail) controller.enqueue(encoder.encode(tail));
2697
+ }
2698
+ });
2699
+ }
2700
+ function createCloudCodeFetch(options, fetchImpl) {
2701
+ let accessToken = options.accessToken;
2702
+ return async (input, init) => {
2703
+ const url = requestUrl(input);
2704
+ const streaming = url.includes("streamGenerateContent");
2705
+ const signal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
2706
+ const geminiBody = await readJsonBody(input, init);
2707
+ const envelope = {
2708
+ project: options.projectId,
2709
+ requestId: randomUUID2(),
2710
+ model: options.modelId,
2711
+ userAgent: ANTIGRAVITY_USER_AGENT,
2712
+ requestType: "agent",
2713
+ enabledCreditTypes: ["GOOGLE_ONE_AI"],
2714
+ request: geminiBody
2715
+ };
2716
+ const body = JSON.stringify(envelope);
2717
+ const upstreamUrl = streaming ? STREAM_URL : UNARY_URL;
2718
+ const doFetch = fetchImpl ?? ((input2, init2) => globalThis.fetch(input2, init2));
2719
+ const send = (token) => doFetch(upstreamUrl, {
2720
+ method: "POST",
2721
+ headers: {
2722
+ "Content-Type": "application/json",
2723
+ Authorization: `Bearer ${token}`,
2724
+ "User-Agent": ANTIGRAVITY_USER_AGENT
2725
+ },
2726
+ body,
2727
+ signal
2728
+ });
2729
+ let response;
2730
+ try {
2731
+ response = await send(accessToken);
2732
+ } catch (err) {
2733
+ if (isAbortError(err, signal)) throw abortError(signal, err);
2734
+ throw err;
2735
+ }
2736
+ if (response.status === 401 && options.refreshToken && !signal?.aborted) {
2737
+ const refreshed = await options.refreshToken().catch(() => null);
2738
+ if (refreshed && refreshed !== accessToken && !signal?.aborted) {
2739
+ accessToken = refreshed;
2740
+ try {
2741
+ response = await send(accessToken);
2742
+ } catch (err) {
2743
+ if (isAbortError(err, signal)) throw abortError(signal, err);
2744
+ throw err;
2745
+ }
2746
+ }
2747
+ }
2748
+ return adaptUpstreamResponse(response, streaming);
2749
+ };
2750
+ }
2751
+ async function createAntigravityCloudCodeModel(options) {
2752
+ const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
2753
+ const google = createGoogleGenerativeAI({
2754
+ apiKey: "relay-cloud-code",
2755
+ baseURL: SDK_BASE_URL,
2756
+ fetch: createCloudCodeFetch(options)
2757
+ });
2758
+ return google(options.modelId);
2759
+ }
2760
+ function isWrappedCloudCodeBody(parsed) {
2761
+ return !!parsed && typeof parsed === "object" && !Array.isArray(parsed) && "response" in parsed && parsed.response !== null && typeof parsed.response === "object";
2762
+ }
2763
+ function transformSseEvent(event) {
2764
+ return event.replace(/^(data:[ \t]*)(.*)$/gm, (_all, prefix, payload) => `${prefix}${unwrapCloudCodeSsePayload(payload)}`);
2765
+ }
2766
+ function requestUrl(input) {
2767
+ if (typeof input === "string") return input;
2768
+ if (input instanceof URL) return input.href;
2769
+ return input.url;
2770
+ }
2771
+ async function readJsonBody(input, init) {
2772
+ const body = init?.body;
2773
+ if (typeof body === "string") return JSON.parse(body);
2774
+ if (body instanceof Uint8Array) return JSON.parse(new TextDecoder().decode(body));
2775
+ if (body instanceof ArrayBuffer) return JSON.parse(new TextDecoder().decode(body));
2776
+ const request = input instanceof Request ? input.clone() : new Request(input, init);
2777
+ return request.json();
2778
+ }
2779
+ async function adaptUpstreamResponse(upstream, streaming) {
2780
+ const fallbackType = streaming ? "text/event-stream" : "application/json";
2781
+ const contentType = upstream.headers.get("content-type") ?? fallbackType;
2782
+ const headers = new Headers({ "Content-Type": contentType });
2783
+ if (!upstream.ok) {
2784
+ const errBody = await upstream.text();
2785
+ return new Response(errBody, {
2786
+ status: upstream.status,
2787
+ statusText: upstream.statusText,
2788
+ headers
2789
+ });
2790
+ }
2791
+ if (streaming) {
2792
+ const body = upstream.body ? upstream.body.pipeThrough(createCloudCodeSseUnwrapper()) : null;
2793
+ return new Response(body, {
2794
+ status: upstream.status,
2795
+ statusText: upstream.statusText,
2796
+ headers
2797
+ });
2798
+ }
2799
+ const text = await upstream.text();
2800
+ headers.set("Content-Type", "application/json");
2801
+ return new Response(unwrapCloudCodeJsonBody(text), { status: 200, headers });
2802
+ }
2803
+ function isAbortError(err, signal) {
2804
+ if (signal?.aborted) return true;
2805
+ return !!err && typeof err === "object" && err.name === "AbortError";
2806
+ }
2807
+ function abortError(signal, cause) {
2808
+ if (signal?.reason instanceof Error) return signal.reason;
2809
+ if (cause instanceof Error) return cause;
2810
+ return new DOMException("This operation was aborted", "AbortError");
2811
+ }
2812
+
2404
2813
  // src/core/model.ts
2814
+ function isAntigravityCloudCodeRoute(provider, model) {
2815
+ return provider.id === "antigravity" && provider.authType === "oauth" && model.modelFormat === "cloud-code";
2816
+ }
2405
2817
  function findRoute(registry, providerId, modelId, routeId) {
2406
2818
  const provider = registry.providers.find((p) => p.id === providerId);
2407
2819
  if (!provider) {
@@ -2442,10 +2854,37 @@ async function resolveCredential(provider, routeId) {
2442
2854
  );
2443
2855
  }
2444
2856
  }
2445
- async function createRelayModel(routeId) {
2857
+ async function createRelayModel(routeId, options) {
2446
2858
  const { providerId, modelId } = parseRelayRouteId(routeId);
2447
2859
  const registry = loadCoreRegistry();
2448
2860
  const { provider, model } = findRoute(registry, providerId, modelId, routeId);
2861
+ if (isAntigravityCloudCodeRoute(provider, model)) {
2862
+ const apiKey2 = await resolveCredential(provider, routeId);
2863
+ const providerData2 = await resolveProviderOAuthProviderData(provider.authRef);
2864
+ const projectId = typeof providerData2?.projectId === "string" ? providerData2.projectId.trim() : "";
2865
+ if (!projectId) {
2866
+ throw new RelayCoreError(
2867
+ "CREDENTIAL_UNAVAILABLE",
2868
+ `Provider "${provider.name}" is missing project metadata \u2014 re-authenticate in relay-ai ui.`,
2869
+ { providerId: provider.id, routeId }
2870
+ );
2871
+ }
2872
+ try {
2873
+ return await createAntigravityCloudCodeModel({
2874
+ modelId: model.upstreamModelId ?? model.id,
2875
+ accessToken: apiKey2,
2876
+ projectId,
2877
+ refreshToken: providerRefreshToken(provider.id, provider.authType, provider.authRef)
2878
+ });
2879
+ } catch (err) {
2880
+ if (isRelayCoreError(err)) throw err;
2881
+ throw new RelayCoreError(
2882
+ "PROVIDER_LOAD_FAILED",
2883
+ `Failed to construct model "${modelId}" for provider "${provider.name}".`,
2884
+ { providerId, routeId, cause: err }
2885
+ );
2886
+ }
2887
+ }
2449
2888
  const npm = model.npm ?? provider.api.npm;
2450
2889
  if (!npm) {
2451
2890
  throw new RelayCoreError("UNSUPPORTED_MODEL", `Model "${modelId}" has no SDK provider package \u2014 refresh the provider's models in relay-ai ui.`, { providerId, routeId });
@@ -2469,7 +2908,8 @@ async function createRelayModel(routeId) {
2469
2908
  headers: provider.api.headers,
2470
2909
  refreshToken: providerRefreshToken(provider.id, provider.authType, provider.authRef),
2471
2910
  useResponsesLite: model.useResponsesLite,
2472
- preferWebSockets: model.preferWebSockets
2911
+ preferWebSockets: model.preferWebSockets,
2912
+ ...options?.onDebug ? { onDebug: options.onDebug } : {}
2473
2913
  };
2474
2914
  try {
2475
2915
  return await createLanguageModel(spec);