@jacobbd/relay-ai 0.15.2 → 0.15.3

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
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  addManualModel,
4
4
  removeManualModel
5
- } from "./chunk-NZYRIU7L.js";
5
+ } from "./chunk-CCRCZB7R.js";
6
6
  import {
7
7
  CODEX_APP_AUTO_COMPACT_RATIO,
8
8
  CODEX_APP_PROVIDER_ID,
@@ -146,7 +146,7 @@ import {
146
146
  waitForCodexAppQuit,
147
147
  writeSecureLogLine,
148
148
  zenRegistryStub
149
- } from "./chunk-IGGQQDV7.js";
149
+ } from "./chunk-J5ZT7M4R.js";
150
150
  import {
151
151
  filterTemplates,
152
152
  getTemplateById,
@@ -226,7 +226,7 @@ import {
226
226
  thinkingProviderOptions,
227
227
  upstreamHttpStatus,
228
228
  validateCustomEndpointUrl
229
- } from "./chunk-G6CR3AKM.js";
229
+ } from "./chunk-QPUBSC2V.js";
230
230
  import "./chunk-JIDIH7DS.js";
231
231
 
232
232
  // src/cli.ts
@@ -16387,7 +16387,7 @@ Options:
16387
16387
  --trace Write debug logs under ~/.relay-ai/logs/`);
16388
16388
  return 0;
16389
16389
  }
16390
- const { runUiCommand } = await import("./ui-command-AVBR5ALW.js");
16390
+ const { runUiCommand } = await import("./ui-command-YKOW6JIG.js");
16391
16391
  return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
16392
16392
  }
16393
16393
  if (parsed.command === "models") {
@@ -31,6 +31,13 @@ interface CreateRelayModelOptions {
31
31
  * specialized Cloud Code Assist path.
32
32
  */
33
33
  onDebug?: (message: string) => void;
34
+ /**
35
+ * Custom upstream fetch for Antigravity Cloud Code routes (other routes ignore it).
36
+ * Used for every inference attempt, including failover and OAuth retries.
37
+ * Trusted transport: receives OAuth credentials, project metadata and full bodies.
38
+ * Credential refresh itself still uses Relay's OAuth machinery.
39
+ */
40
+ fetchImpl?: typeof globalThis.fetch;
34
41
  /**
35
42
  * Reasoning level to apply to every call made with the returned model.
36
43
  *
@@ -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.15.2",
53
+ version: "0.15.3",
54
54
  publishConfig: {
55
55
  access: "public"
56
56
  },
@@ -1099,15 +1099,15 @@ function resolveProtocolAlternative(input) {
1099
1099
  function errorText(value) {
1100
1100
  if (typeof value === "string") return value;
1101
1101
  if (!value || typeof value !== "object") return "";
1102
- const record = value;
1103
- const pieces = [record.message, record.responseBody, record.code].filter((item) => typeof item === "string");
1102
+ const record2 = value;
1103
+ const pieces = [record2.message, record2.responseBody, record2.code].filter((item) => typeof item === "string");
1104
1104
  return pieces.join(" ");
1105
1105
  }
1106
1106
  function findErrorField(error, field, depth = 0) {
1107
1107
  if (!error || typeof error !== "object" || depth > 3) return void 0;
1108
- const record = error;
1109
- if (record[field] !== void 0) return record[field];
1110
- return findErrorField(record.cause, field, depth + 1) ?? findErrorField(record.lastError, field, depth + 1);
1108
+ const record2 = error;
1109
+ if (record2[field] !== void 0) return record2[field];
1110
+ return findErrorField(record2.cause, field, depth + 1) ?? findErrorField(record2.lastError, field, depth + 1);
1111
1111
  }
1112
1112
  function classifyProtocolFailure(error) {
1113
1113
  const statusValue = findErrorField(error, "statusCode") ?? findErrorField(error, "status");
@@ -4615,6 +4615,137 @@ function reconcileCachedModelProtocol(model, provider, metadata = loadModelsDevC
4615
4615
 
4616
4616
  // src/core/antigravity-model.ts
4617
4617
  import { randomUUID as randomUUID3 } from "crypto";
4618
+
4619
+ // src/core/antigravity-diagnostics.ts
4620
+ import { createHash as createHash3 } from "crypto";
4621
+ var SECTION_NAMES = ["systemInstruction", "tools", "toolConfig", "generationConfig"];
4622
+ var USAGE_NAMES = ["promptTokenCount", "cachedContentTokenCount", "thoughtsTokenCount"];
4623
+ function record(value) {
4624
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4625
+ }
4626
+ function requestFingerprint(value) {
4627
+ const body = record(value);
4628
+ const sections = Object.fromEntries(SECTION_NAMES.map((name) => {
4629
+ const text = JSON.stringify(body[name]);
4630
+ return [name, text === void 0 ? { present: false } : {
4631
+ present: true,
4632
+ bytes: Buffer.byteLength(text),
4633
+ hash: createHash3("sha256").update(text).digest("hex").slice(0, 32)
4634
+ }];
4635
+ }));
4636
+ const prefix = createHash3("sha256");
4637
+ const contents = (Array.isArray(body.contents) ? body.contents : []).map((item) => {
4638
+ const text = JSON.stringify(item);
4639
+ const bytes = Buffer.byteLength(text);
4640
+ prefix.update(`${bytes}:`).update(text);
4641
+ return { bytes, prefixHash: prefix.copy().digest("hex").slice(0, 32) };
4642
+ });
4643
+ const tools = Array.isArray(body.tools) ? body.tools : [];
4644
+ return {
4645
+ // Unknown property names can contain caller data. Keep their position but hide their names.
4646
+ keyOrder: Object.keys(body).map((key) => [...SECTION_NAMES, "contents", "safetySettings", "cachedContent", "labels"].includes(key) ? key : "[other]"),
4647
+ sections,
4648
+ contents,
4649
+ contentsCount: contents.length,
4650
+ toolCount: tools.reduce((count, tool) => {
4651
+ const declarations = record(tool).functionDeclarations;
4652
+ return count + (Array.isArray(declarations) ? declarations.length : 0);
4653
+ }, 0)
4654
+ };
4655
+ }
4656
+ function observeCloudCodeBody(body, streaming, log, startedAt, signal) {
4657
+ const decoder = new TextDecoder();
4658
+ let pending = "";
4659
+ let usage;
4660
+ let firstBodyByteMs = null;
4661
+ let finished = false;
4662
+ const inspect = (text) => {
4663
+ try {
4664
+ const parsed = record(JSON.parse(text));
4665
+ const response = "response" in parsed ? record(parsed.response) : parsed;
4666
+ if (response.usageMetadata && typeof response.usageMetadata === "object") {
4667
+ usage = record(response.usageMetadata);
4668
+ }
4669
+ } catch {
4670
+ }
4671
+ };
4672
+ const inspectEvent = (event) => {
4673
+ const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
4674
+ if (data) inspect(data);
4675
+ };
4676
+ const scan = (chunk) => {
4677
+ pending += chunk ? decoder.decode(chunk, { stream: true }) : decoder.decode();
4678
+ if (!streaming) {
4679
+ if (!chunk) inspect(pending);
4680
+ return;
4681
+ }
4682
+ while (true) {
4683
+ const separator = /\r?\n\r?\n/.exec(pending);
4684
+ if (!separator) break;
4685
+ inspectEvent(pending.slice(0, separator.index));
4686
+ pending = pending.slice(separator.index + separator[0].length);
4687
+ }
4688
+ if (!chunk && pending) inspectEvent(pending);
4689
+ };
4690
+ const finish = (outcome) => {
4691
+ if (finished) return;
4692
+ finished = true;
4693
+ signal?.removeEventListener("abort", onAbort);
4694
+ log(`end ${JSON.stringify({
4695
+ outcome,
4696
+ elapsedMs: performance.now() - startedAt,
4697
+ firstBodyByteMs,
4698
+ usageMetadataPresent: usage !== void 0,
4699
+ usage: Object.fromEntries(USAGE_NAMES.map((name) => {
4700
+ if (!usage || !Object.hasOwn(usage, name)) return [name, { present: false }];
4701
+ const value = usage[name];
4702
+ return [name, typeof value === "number" && Number.isFinite(value) ? { present: true, value } : { present: true, valid: false }];
4703
+ }))
4704
+ })}`);
4705
+ pending = "";
4706
+ };
4707
+ const onAbort = () => finish("cancelled");
4708
+ if (!body) {
4709
+ finish("completed");
4710
+ return null;
4711
+ }
4712
+ signal?.addEventListener("abort", onAbort, { once: true });
4713
+ if (signal?.aborted) onAbort();
4714
+ const reader = body.getReader();
4715
+ return new ReadableStream({
4716
+ async pull(controller) {
4717
+ try {
4718
+ const { value, done } = await reader.read();
4719
+ if (done) {
4720
+ if (!finished) scan();
4721
+ finish("completed");
4722
+ reader.releaseLock();
4723
+ controller.close();
4724
+ } else {
4725
+ if (!finished) {
4726
+ firstBodyByteMs ??= performance.now() - startedAt;
4727
+ scan(value);
4728
+ }
4729
+ controller.enqueue(value);
4730
+ }
4731
+ } catch (err) {
4732
+ finish(signal?.aborted ? "cancelled" : "failed");
4733
+ reader.releaseLock();
4734
+ controller.error(err);
4735
+ }
4736
+ },
4737
+ async cancel(reason) {
4738
+ finish("cancelled");
4739
+ try {
4740
+ await reader.cancel(reason);
4741
+ } finally {
4742
+ reader.releaseLock();
4743
+ }
4744
+ }
4745
+ }, { highWaterMark: 0 });
4746
+ }
4747
+
4748
+ // src/core/antigravity-model.ts
4618
4749
  var CLOUD_CODE_BASES = ANTIGRAVITY_BASE_URLS.map((base) => base.replace(/\/+$/, ""));
4619
4750
  var CLOUD_CODE_BASE = CLOUD_CODE_BASES[0];
4620
4751
  var STREAM_URLS = CLOUD_CODE_BASES.map((base) => `${base}/${ANTIGRAVITY_API_VERSION}:streamGenerateContent?alt=sse`);
@@ -4688,17 +4819,21 @@ function createCloudCodeSseUnwrapper() {
4688
4819
  }
4689
4820
  function createCloudCodeFetch(options, fetchImpl) {
4690
4821
  let accessToken = options.accessToken;
4691
- const debug = (msg) => {
4692
- try {
4693
- options.onDebug?.(`cloud-code: ${msg}`);
4694
- } catch {
4695
- }
4696
- };
4697
4822
  return async (input, init) => {
4823
+ const startedAt = options.onDebug ? performance.now() : 0;
4824
+ const diagnosticId = options.onDebug ? randomUUID3() : "";
4825
+ let attempt = 0;
4826
+ const debug = (msg) => {
4827
+ try {
4828
+ options.onDebug?.(`cloud-code: call=${diagnosticId} attempt=${attempt} ${msg}`);
4829
+ } catch {
4830
+ }
4831
+ };
4698
4832
  const url = requestUrl(input);
4699
4833
  const streaming = url.includes("streamGenerateContent");
4700
4834
  const signal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
4701
4835
  const geminiBody = await readJsonBody(input, init);
4836
+ if (options.onDebug) debug(`fingerprint ${JSON.stringify(requestFingerprint(geminiBody))}`);
4702
4837
  const envelope = {
4703
4838
  project: options.projectId,
4704
4839
  requestId: randomUUID3(),
@@ -4711,7 +4846,7 @@ function createCloudCodeFetch(options, fetchImpl) {
4711
4846
  const body = JSON.stringify(envelope);
4712
4847
  const bodyByteLength = Buffer.byteLength(body, "utf8");
4713
4848
  const upstreamUrls = streaming ? STREAM_URLS : UNARY_URLS;
4714
- const doFetch = fetchImpl ?? ((input2, init2) => globalThis.fetch(input2, init2));
4849
+ const doFetch = fetchImpl ?? options.fetchImpl ?? ((input2, init2) => globalThis.fetch(input2, init2));
4715
4850
  const send = async (url2, token) => {
4716
4851
  try {
4717
4852
  return await doFetch(url2, {
@@ -4732,6 +4867,8 @@ function createCloudCodeFetch(options, fetchImpl) {
4732
4867
  const sendWithFailover = async (token, startIndex = 0) => {
4733
4868
  let lastError;
4734
4869
  for (let i = startIndex; i < upstreamUrls.length; i += 1) {
4870
+ attempt += 1;
4871
+ const attemptStartedAt = options.onDebug ? performance.now() : 0;
4735
4872
  const url2 = upstreamUrls[i];
4736
4873
  const isLast = i === upstreamUrls.length - 1;
4737
4874
  const where = `endpoint=${i + 1}/${upstreamUrls.length} host=${endpointHost(url2)}`;
@@ -4739,8 +4876,12 @@ function createCloudCodeFetch(options, fetchImpl) {
4739
4876
  try {
4740
4877
  debug(`request ${where} kind=${streaming ? "stream" : "unary"} payloadBytes=${bodyByteLength}`);
4741
4878
  response2 = await send(url2, token);
4879
+ if (options.onDebug) debug(`timing ${where} headersMs=${performance.now() - attemptStartedAt} sendMs=${attemptStartedAt - startedAt} httpVersion=unavailable socketReused=unavailable`);
4742
4880
  } catch (err) {
4743
- if (isAbortError(err, signal)) throw err;
4881
+ if (isAbortError(err, signal)) {
4882
+ debug('end {"outcome":"cancelled","phase":"fetch"}');
4883
+ throw err;
4884
+ }
4744
4885
  lastError = err;
4745
4886
  debug(`network failure ${where} errorName=${errorName(err)}`);
4746
4887
  }
@@ -4753,8 +4894,12 @@ function createCloudCodeFetch(options, fetchImpl) {
4753
4894
  lastError = new Error(`Cloud Code Assist endpoint returned ${response2.status}`);
4754
4895
  debug(`retryable status=${response2.status} ${where} \u2014 trying next endpoint`);
4755
4896
  }
4756
- if (signal?.aborted) throw abortError(signal);
4897
+ if (signal?.aborted) {
4898
+ debug('end {"outcome":"cancelled","phase":"fetch"}');
4899
+ throw abortError(signal);
4900
+ }
4757
4901
  }
4902
+ debug('end {"outcome":"failed","phase":"fetch"}');
4758
4903
  throw lastError ?? new Error("All Cloud Code Assist endpoints failed");
4759
4904
  };
4760
4905
  const tokenUsed = accessToken;
@@ -4771,6 +4916,10 @@ function createCloudCodeFetch(options, fetchImpl) {
4771
4916
  debug(`refresh did not yield a new credential (refreshed=${refreshed ? "same" : "none"})`);
4772
4917
  }
4773
4918
  }
4919
+ if (options.onDebug) {
4920
+ const observed = observeCloudCodeBody(response.body, streaming && response.ok, debug, startedAt, signal);
4921
+ response = new Response(observed, { status: response.status, statusText: response.statusText, headers: response.headers });
4922
+ }
4774
4923
  return adaptUpstreamResponse(response, streaming);
4775
4924
  };
4776
4925
  }
@@ -5076,7 +5225,8 @@ async function createRelayModel(routeId, options) {
5076
5225
  accessToken: apiKey2,
5077
5226
  projectId,
5078
5227
  refreshToken: providerRefreshToken(provider.id, provider.authType, provider.authRef),
5079
- ...options?.onDebug ? { onDebug: options.onDebug } : {}
5228
+ ...options?.onDebug ? { onDebug: options.onDebug } : {},
5229
+ ...options?.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
5080
5230
  }), reasoningNpmForRoute(provider, model));
5081
5231
  } catch (err) {
5082
5232
  if (isRelayCoreError(err)) throw err;