@jacobbd/relay-ai 0.9.4 → 0.9.6

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.
@@ -11,7 +11,7 @@ import { join } from "path";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@jacobbd/relay-ai",
14
- version: "0.9.4",
14
+ version: "0.9.6",
15
15
  publishConfig: {
16
16
  access: "public"
17
17
  },
@@ -1693,10 +1693,86 @@ function thinkingProviderOptions(npm) {
1693
1693
  }
1694
1694
 
1695
1695
  // src/codex/multi-agent.ts
1696
- import { execFileSync } from "child_process";
1697
1696
  import { mkdtempSync, rmSync, writeFileSync } from "fs";
1698
1697
  import { tmpdir } from "os";
1699
1698
  import { join as join2 } from "path";
1699
+
1700
+ // src/codex/process.ts
1701
+ import spawn from "cross-spawn";
1702
+ function commandError(binaryPath, args, stdout, stderr, detail) {
1703
+ const error = new Error(`Command failed: ${binaryPath} ${args.join(" ")} (${detail})`);
1704
+ return Object.assign(error, { stdout, stderr });
1705
+ }
1706
+ function runCodexCommandSync(binaryPath, args, options = {}) {
1707
+ const result = spawn.sync(binaryPath, args, {
1708
+ encoding: "utf8",
1709
+ stdio: ["ignore", "pipe", "pipe"],
1710
+ env: options.env,
1711
+ timeout: options.timeout,
1712
+ maxBuffer: options.maxBuffer
1713
+ });
1714
+ const stdout = result.stdout ?? "";
1715
+ const stderr = result.stderr ?? "";
1716
+ if (result.error) {
1717
+ throw Object.assign(result.error, { stdout, stderr });
1718
+ }
1719
+ if (result.status !== 0) {
1720
+ throw commandError(binaryPath, args, stdout, stderr, `exit ${result.status ?? result.signal ?? "unknown"}`);
1721
+ }
1722
+ return { stdout, stderr };
1723
+ }
1724
+ function runCodexCommand(binaryPath, args, options = {}) {
1725
+ return new Promise((resolve, reject) => {
1726
+ const child = spawn(binaryPath, args, {
1727
+ stdio: ["ignore", "pipe", "pipe"],
1728
+ env: options.env
1729
+ });
1730
+ const stdoutChunks = [];
1731
+ const stderrChunks = [];
1732
+ let outputBytes = 0;
1733
+ let settled = false;
1734
+ const finishError = (error) => {
1735
+ if (settled) return;
1736
+ settled = true;
1737
+ reject(Object.assign(error, {
1738
+ stdout: Buffer.concat(stdoutChunks).toString("utf8"),
1739
+ stderr: Buffer.concat(stderrChunks).toString("utf8")
1740
+ }));
1741
+ };
1742
+ const collect = (target, chunk) => {
1743
+ target.push(chunk);
1744
+ outputBytes += chunk.length;
1745
+ if (options.maxBuffer !== void 0 && outputBytes > options.maxBuffer) {
1746
+ child.kill();
1747
+ finishError(commandError(binaryPath, args, "", "", `output exceeded ${options.maxBuffer} bytes`));
1748
+ }
1749
+ };
1750
+ child.stdout?.on("data", (chunk) => collect(stdoutChunks, chunk));
1751
+ child.stderr?.on("data", (chunk) => collect(stderrChunks, chunk));
1752
+ child.on("error", finishError);
1753
+ let timer;
1754
+ if (options.timeout !== void 0) {
1755
+ timer = setTimeout(() => {
1756
+ child.kill();
1757
+ finishError(commandError(binaryPath, args, "", "", `timed out after ${options.timeout}ms`));
1758
+ }, options.timeout);
1759
+ }
1760
+ child.on("close", (code, signal) => {
1761
+ if (timer) clearTimeout(timer);
1762
+ if (settled) return;
1763
+ const stdout = Buffer.concat(stdoutChunks).toString("utf8");
1764
+ const stderr = Buffer.concat(stderrChunks).toString("utf8");
1765
+ if (code !== 0) {
1766
+ finishError(commandError(binaryPath, args, stdout, stderr, `exit ${code ?? signal ?? "unknown"}`));
1767
+ return;
1768
+ }
1769
+ settled = true;
1770
+ resolve({ stdout, stderr });
1771
+ });
1772
+ });
1773
+ }
1774
+
1775
+ // src/codex/multi-agent.ts
1700
1776
  var CODEX_MULTI_AGENT_V2 = Object.freeze({
1701
1777
  enabled: true,
1702
1778
  max_concurrent_threads_per_session: 6,
@@ -1711,12 +1787,7 @@ function probeText(value) {
1711
1787
  return `${result.stdout ?? ""}
1712
1788
  ${result.stderr ?? ""}`;
1713
1789
  }
1714
- function supportsMultiAgentV2(binaryPath, run3 = (path, args, env) => execFileSync(path, args, {
1715
- encoding: "utf8",
1716
- stdio: ["ignore", "pipe", "pipe"],
1717
- timeout: 1e4,
1718
- env
1719
- })) {
1790
+ function supportsMultiAgentV2(binaryPath, run3 = (path, args, env) => runCodexCommandSync(path, args, { timeout: 1e4, env })) {
1720
1791
  const probeHome = mkdtempSync(join2(tmpdir(), "relay-codex-v2-probe-"));
1721
1792
  try {
1722
1793
  writeFileSync(join2(probeHome, "config.toml"), renderMultiAgentV2Feature(), { encoding: "utf8", mode: 384 });
@@ -2410,18 +2481,18 @@ function resolveServerAutostart(env = process.env) {
2410
2481
 
2411
2482
  // src/launch.ts
2412
2483
  import { execSync } from "child_process";
2413
- import spawn from "cross-spawn";
2484
+ import spawn2 from "cross-spawn";
2414
2485
  import { existsSync as existsSync3, appendFileSync } from "fs";
2415
2486
  import { homedir as homedir3 } from "os";
2416
2487
  import { join as join5 } from "path";
2417
2488
 
2418
2489
  // src/binary-lookup.ts
2419
- import { execFileSync as execFileSync2 } from "child_process";
2490
+ import { execFileSync } from "child_process";
2420
2491
  import { existsSync as existsSync2 } from "fs";
2421
2492
  function findBinaryOnPath(name, fallbackPaths, options = {}) {
2422
2493
  const isWindows2 = options.isWindows ?? process.platform === "win32";
2423
2494
  const exists = options.exists ?? existsSync2;
2424
- const runWhich = options.runWhich ?? ((binary, win) => execFileSync2(win ? "where.exe" : "which", [binary], {
2495
+ const runWhich = options.runWhich ?? ((binary, win) => execFileSync(win ? "where.exe" : "which", [binary], {
2425
2496
  encoding: "utf8",
2426
2497
  stdio: ["pipe", "pipe", "pipe"]
2427
2498
  }));
@@ -2499,7 +2570,7 @@ function launchClaude(env, model, extraArgs) {
2499
2570
  process.stdout.write = originalStdoutWrite;
2500
2571
  process.stderr.write = originalStderrWrite;
2501
2572
  };
2502
- const child = spawn(claudePath, args, {
2573
+ const child = spawn2(claudePath, args, {
2503
2574
  stdio: "inherit",
2504
2575
  env
2505
2576
  });
@@ -5141,9 +5212,9 @@ function claudeModelFamily(modelId) {
5141
5212
  if (!normalized.startsWith("claude-")) return void 0;
5142
5213
  return CLAUDE_MODEL_FAMILIES.find((family) => normalized.includes(family));
5143
5214
  }
5144
- function isClaudeAgentTool(tool4) {
5145
- if (tool4.name !== "Agent" || !isRecord2(tool4.input_schema)) return false;
5146
- const properties = tool4.input_schema.properties;
5215
+ function isClaudeAgentTool(tool3) {
5216
+ if (tool3.name !== "Agent" || !isRecord2(tool3.input_schema)) return false;
5217
+ const properties = tool3.input_schema.properties;
5147
5218
  if (!isRecord2(properties)) return false;
5148
5219
  return ["description", "prompt", "subagent_type"].every((name) => isRecord2(properties[name]));
5149
5220
  }
@@ -5233,8 +5304,8 @@ function prepareClaudeAgentInput(input, routing) {
5233
5304
  clientInput.prompt = appendSubagentRouteMarker(prompt, token);
5234
5305
  return { input: clientInput, decision };
5235
5306
  }
5236
- function augmentClaudeAgentTool(tool4, routing) {
5237
- const inputSchema = isRecord2(tool4.input_schema) ? tool4.input_schema : {};
5307
+ function augmentClaudeAgentTool(tool3, routing) {
5308
+ const inputSchema = isRecord2(tool3.input_schema) ? tool3.input_schema : {};
5238
5309
  const properties = isRecord2(inputSchema.properties) ? inputSchema.properties : {};
5239
5310
  const originalModel = isRecord2(properties.model) ? properties.model : {};
5240
5311
  const smallCatalog = routing.models.length <= MAX_MODEL_CATALOG;
@@ -5250,8 +5321,8 @@ function augmentClaudeAgentTool(tool4, routing) {
5250
5321
  }
5251
5322
  const guidance = smallCatalog ? `Relay AI subagent model routing (default: ${routing.parentModelId}). ` + routing.models.map((model) => `${model.displayName}: ${model.id}`).join("; ") : `Relay AI subagent model routing (default: ${routing.parentModelId}). Other explicit model values must be exact ids from the current session catalog.`;
5252
5323
  return {
5253
- ...tool4,
5254
- description: [tool4.description?.trim(), guidance].filter(Boolean).join("\n\n"),
5324
+ ...tool3,
5325
+ description: [tool3.description?.trim(), guidance].filter(Boolean).join("\n\n"),
5255
5326
  input_schema: {
5256
5327
  ...inputSchema,
5257
5328
  properties: {
@@ -5583,11 +5654,7 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
5583
5654
  }
5584
5655
 
5585
5656
  // src/antigravity/anthropic-to-cloudcode.ts
5586
- import { randomUUID as randomUUID4 } from "crypto";
5587
-
5588
- // src/antigravity/request-adapter.ts
5589
5657
  import { randomUUID as randomUUID3 } from "crypto";
5590
- import { tool, jsonSchema } from "ai";
5591
5658
 
5592
5659
  // src/proxy-shared.ts
5593
5660
  function grabRoundTripSignature(part) {
@@ -5695,265 +5762,6 @@ function serializeToolResultContent(content) {
5695
5762
  return JSON.stringify(content);
5696
5763
  }
5697
5764
 
5698
- // src/antigravity/request-adapter.ts
5699
- var UNSUPPORTED_VOICE_MESSAGE = "Voice transcription isn\u2019t supported by Relay AI yet. Please type your message. Your coding session remains active.";
5700
- var OMITTED_VOICE_TEXT = "[Voice recording omitted because transcription is not supported by Relay AI.]";
5701
- function isSupportedImage(part) {
5702
- return part.inlineData?.mimeType.toLowerCase().startsWith("image/") ?? false;
5703
- }
5704
- function isUnsupportedInlineData(part) {
5705
- return !!part.inlineData && !isSupportedImage(part);
5706
- }
5707
- function sanitizeUnsupportedInlineData(ccReq) {
5708
- const contents = ccReq.request?.contents ?? [];
5709
- let latestUserIndex = -1;
5710
- for (let i = contents.length - 1; i >= 0; i--) {
5711
- if (contents[i].role === "user") {
5712
- latestUserIndex = i;
5713
- break;
5714
- }
5715
- }
5716
- let latestUserTurnHasUnsupportedMedia = false;
5717
- const sanitizedContents = contents.map((message, index) => ({
5718
- ...message,
5719
- parts: message.parts.map((part) => {
5720
- if (!isUnsupportedInlineData(part)) return part;
5721
- if (index === latestUserIndex) latestUserTurnHasUnsupportedMedia = true;
5722
- return { text: OMITTED_VOICE_TEXT };
5723
- })
5724
- }));
5725
- return {
5726
- request: {
5727
- ...ccReq,
5728
- request: {
5729
- ...ccReq.request,
5730
- contents: sanitizedContents
5731
- }
5732
- },
5733
- latestUserTurnHasUnsupportedMedia
5734
- };
5735
- }
5736
- function tracePartChars(part) {
5737
- if (typeof part.text === "string") return part.text.length;
5738
- if (part.type !== "tool-result") return void 0;
5739
- const output = part.output;
5740
- if (typeof output === "string") return output.length;
5741
- if (output && typeof output === "object" && typeof output.value === "string") {
5742
- return output.value.length;
5743
- }
5744
- try {
5745
- return output === void 0 ? void 0 : JSON.stringify(output).length;
5746
- } catch {
5747
- return void 0;
5748
- }
5749
- }
5750
- function summarizeSdkRequestForTrace(request) {
5751
- const messages = request.messages.map((message) => {
5752
- const content = message.content;
5753
- if (typeof content === "string") {
5754
- return { role: message.role, parts: [{ type: "text", chars: content.length }] };
5755
- }
5756
- const parts = Array.isArray(content) ? content.map((rawPart) => {
5757
- const part = rawPart;
5758
- const summary = {
5759
- type: typeof part.type === "string" ? part.type : typeof rawPart
5760
- };
5761
- const chars = tracePartChars(part);
5762
- if (chars !== void 0) summary.chars = chars;
5763
- if (typeof part.toolName === "string") summary.toolName = part.toolName;
5764
- if (typeof part.toolCallId === "string") summary.toolCallId = part.toolCallId;
5765
- return summary;
5766
- }) : [{ type: typeof content }];
5767
- return { role: message.role, parts };
5768
- });
5769
- return {
5770
- systemChars: request.system?.length ?? 0,
5771
- messages,
5772
- toolNames: Object.keys(request.tools ?? {}),
5773
- ...request.toolChoice ? { toolChoice: request.toolChoice } : {}
5774
- };
5775
- }
5776
- var JSON_SCHEMA_TYPES = /* @__PURE__ */ new Map([
5777
- ["ARRAY", "array"],
5778
- ["BOOLEAN", "boolean"],
5779
- ["INTEGER", "integer"],
5780
- ["NULL", "null"],
5781
- ["NUMBER", "number"],
5782
- ["OBJECT", "object"],
5783
- ["STRING", "string"]
5784
- ]);
5785
- function expandTextWithThinking(text4) {
5786
- if (!text4.includes("<thinking>")) {
5787
- return [{ type: "text", text: text4 }];
5788
- }
5789
- const out = [];
5790
- const tokens = text4.split(/<thinking>([\s\S]*?)<\/thinking>/);
5791
- for (let i = 0; i < tokens.length; i++) {
5792
- const token = tokens[i] ?? "";
5793
- if (!token.trim()) continue;
5794
- out.push({ type: i % 2 === 1 ? "reasoning" : "text", text: token });
5795
- }
5796
- return out.length > 0 ? out : [{ type: "text", text: text4 }];
5797
- }
5798
- function normalizeSchemaType(value) {
5799
- if (typeof value === "string") {
5800
- return JSON_SCHEMA_TYPES.get(value) ?? value;
5801
- }
5802
- if (Array.isArray(value)) {
5803
- return value.map(normalizeSchemaType);
5804
- }
5805
- return value;
5806
- }
5807
- function normalizeJsonSchema(value) {
5808
- if (Array.isArray(value)) {
5809
- return value.map(normalizeJsonSchema);
5810
- }
5811
- if (!value || typeof value !== "object") {
5812
- return value;
5813
- }
5814
- return Object.fromEntries(
5815
- Object.entries(value).map(([key, child]) => [
5816
- key,
5817
- key === "type" ? normalizeSchemaType(child) : normalizeJsonSchema(child)
5818
- ])
5819
- );
5820
- }
5821
- function translateTools(ccTools, options = {}) {
5822
- if (!ccTools?.length) return void 0;
5823
- const tools = {};
5824
- let toolCount = 0;
5825
- for (const t of ccTools) {
5826
- if (t.functionDeclarations) {
5827
- for (const fd of t.functionDeclarations) {
5828
- if (options.maxTools !== void 0 && toolCount >= options.maxTools) break;
5829
- tools[fd.name] = tool({
5830
- description: fd.description || "",
5831
- inputSchema: jsonSchema(
5832
- normalizeJsonSchema(fd.parameters || { type: "object", properties: {} })
5833
- )
5834
- });
5835
- toolCount++;
5836
- }
5837
- }
5838
- }
5839
- return Object.keys(tools).length > 0 ? tools : void 0;
5840
- }
5841
- function translateRequest(ccReq, options = {}) {
5842
- const systemInstructions = [];
5843
- const sdkMessages = [];
5844
- const nameToIdList = /* @__PURE__ */ new Map();
5845
- const fallbackAssistantReasoning = [...options.fallbackAssistantReasoning ?? []];
5846
- const request = ccReq.request || {};
5847
- if (request.systemInstruction?.parts) {
5848
- for (const part of request.systemInstruction.parts) {
5849
- if (part.text) {
5850
- systemInstructions.push(part.text);
5851
- }
5852
- }
5853
- }
5854
- const contents = request.contents || [];
5855
- for (const msg of contents) {
5856
- const role = msg.role;
5857
- if (role === "system") {
5858
- for (const part of msg.parts) {
5859
- if (part.text) {
5860
- systemInstructions.push(part.text);
5861
- }
5862
- }
5863
- continue;
5864
- }
5865
- const sdkRole = role === "model" ? "assistant" : "user";
5866
- const hasFunctionCall = msg.parts.some((p8) => p8.functionCall);
5867
- const hasAssistantReasoning = role === "model" && msg.parts.some((p8) => p8.thought || p8.text?.includes("<thinking>"));
5868
- const hasComplexParts = msg.parts.some((p8) => p8.thought || p8.inlineData || p8.functionCall || p8.functionResponse);
5869
- const singleText = msg.parts.length === 1 ? msg.parts[0]?.text : void 0;
5870
- if (!hasComplexParts && singleText !== void 0 && !singleText.includes("<thinking>")) {
5871
- sdkMessages.push({
5872
- role: sdkRole,
5873
- content: singleText
5874
- });
5875
- continue;
5876
- }
5877
- const contentParts = [];
5878
- const toolResults = [];
5879
- if (role === "model" && hasFunctionCall && !hasAssistantReasoning) {
5880
- const fallback = fallbackAssistantReasoning.shift();
5881
- if (fallback?.trim()) {
5882
- contentParts.push({ type: "reasoning", text: fallback });
5883
- }
5884
- }
5885
- for (const part of msg.parts) {
5886
- if (part.text !== void 0) {
5887
- if (part.thought) {
5888
- contentParts.push({ type: "reasoning", text: part.text });
5889
- } else {
5890
- for (const piece of expandTextWithThinking(part.text)) {
5891
- contentParts.push(piece);
5892
- }
5893
- }
5894
- } else if (part.inlineData) {
5895
- if (isSupportedImage(part)) {
5896
- contentParts.push({
5897
- type: "image",
5898
- image: part.inlineData.data,
5899
- mimeType: part.inlineData.mimeType
5900
- });
5901
- } else {
5902
- contentParts.push({ type: "text", text: OMITTED_VOICE_TEXT });
5903
- }
5904
- } else if (part.functionCall) {
5905
- const id = "call_" + randomUUID3().replace(/-/g, "");
5906
- const name = part.functionCall.name;
5907
- if (!nameToIdList.has(name)) nameToIdList.set(name, []);
5908
- nameToIdList.get(name).push(id);
5909
- contentParts.push({
5910
- type: "tool-call",
5911
- toolCallId: id,
5912
- toolName: name,
5913
- input: part.functionCall.args || {}
5914
- });
5915
- } else if (part.functionResponse) {
5916
- const name = part.functionResponse.name;
5917
- const idList = nameToIdList.get(name) || [];
5918
- const id = idList.shift() || "call_" + randomUUID3().replace(/-/g, "");
5919
- toolResults.push({
5920
- type: "tool-result",
5921
- toolCallId: id,
5922
- toolName: name,
5923
- output: { type: "text", value: serializeToolResultContent(part.functionResponse.response) }
5924
- });
5925
- }
5926
- }
5927
- if (toolResults.length > 0) {
5928
- sdkMessages.push({
5929
- role: "tool",
5930
- content: toolResults
5931
- });
5932
- }
5933
- if (contentParts.length > 0) {
5934
- sdkMessages.push({
5935
- role: sdkRole,
5936
- content: contentParts
5937
- });
5938
- }
5939
- }
5940
- const system = systemInstructions.length > 0 ? systemInstructions.join("\n\n") : void 0;
5941
- const tools = translateTools(request.tools, options);
5942
- let toolChoice;
5943
- const mode = request.toolConfig?.functionCallingConfig?.mode;
5944
- if (mode === "ANY") {
5945
- toolChoice = "required";
5946
- } else if (mode === "AUTO" || tools) {
5947
- toolChoice = "auto";
5948
- }
5949
- return {
5950
- system,
5951
- messages: sdkMessages,
5952
- tools,
5953
- toolChoice
5954
- };
5955
- }
5956
-
5957
5765
  // src/antigravity/anthropic-to-cloudcode.ts
5958
5766
  var DEFAULT_SAFETY_SETTINGS = [
5959
5767
  { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "OFF" },
@@ -5963,7 +5771,6 @@ var DEFAULT_SAFETY_SETTINGS = [
5963
5771
  ];
5964
5772
  var ANTIGRAVITY_USER_AGENT2 = "vscode/1.X.X (Antigravity/4.2.0)";
5965
5773
  var MIN_ANTIGRAVITY_OUTPUT_TOKENS = 1024;
5966
- var DISABLE_CLOUD_CODE_THINKING = { thinkingBudget: 0, includeThoughts: false };
5967
5774
  var STRIP_KEYS = /* @__PURE__ */ new Set([
5968
5775
  "$schema",
5969
5776
  "$defs",
@@ -5973,6 +5780,7 @@ var STRIP_KEYS = /* @__PURE__ */ new Set([
5973
5780
  "additionalProperties",
5974
5781
  "propertyNames",
5975
5782
  "patternProperties",
5783
+ "prefixItems",
5976
5784
  "title",
5977
5785
  "exclusiveMinimum",
5978
5786
  "exclusiveMaximum",
@@ -6006,15 +5814,105 @@ var STRIP_KEYS = /* @__PURE__ */ new Set([
6006
5814
  "examples",
6007
5815
  "readOnly",
6008
5816
  "writeOnly",
6009
- "deprecated"
5817
+ "deprecated",
5818
+ // Codex app tool schemas can include this internal annotation. It is not a
5819
+ // JSON Schema keyword and Cloud Code's Schema protobuf rejects it.
5820
+ "encrypted"
6010
5821
  ]);
6011
- function stripDraftMeta(obj) {
5822
+ var CLOUD_CODE_SCHEMA_TYPES = /* @__PURE__ */ new Map([
5823
+ ["array", "ARRAY"],
5824
+ ["boolean", "BOOLEAN"],
5825
+ ["integer", "INTEGER"],
5826
+ ["null", "NULL"],
5827
+ ["number", "NUMBER"],
5828
+ ["object", "OBJECT"],
5829
+ ["string", "STRING"]
5830
+ ]);
5831
+ function normalizeCloudCodeSchemaType(value) {
5832
+ if (typeof value === "string") {
5833
+ return CLOUD_CODE_SCHEMA_TYPES.get(value.toLowerCase()) ?? value;
5834
+ }
5835
+ return value;
5836
+ }
5837
+ function isNullSchema(obj) {
5838
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return false;
5839
+ const type = obj.type;
5840
+ return type === "null" || type === "NULL" || Array.isArray(type) && type.every((value) => value === "null" || value === "NULL");
5841
+ }
5842
+ function resolveLocalSchemaRef(ref, root) {
5843
+ if (!ref.startsWith("#/$defs/")) return void 0;
5844
+ const name = ref.slice("#/$defs/".length).replace(/~1/g, "/").replace(/~0/g, "~");
5845
+ const defs = root.$defs;
5846
+ if (!defs || typeof defs !== "object" || Array.isArray(defs)) return void 0;
5847
+ return defs[name];
5848
+ }
5849
+ function stripDraftMeta(obj, root = void 0, resolvingRefs = /* @__PURE__ */ new Set()) {
6012
5850
  if (!obj || typeof obj !== "object") return obj;
6013
- if (Array.isArray(obj)) return obj.map(stripDraftMeta);
5851
+ if (Array.isArray(obj)) return obj.map((value) => stripDraftMeta(value, root, resolvingRefs));
5852
+ const source = obj;
5853
+ const schemaRoot = root ?? source;
5854
+ if (typeof source.$ref === "string") {
5855
+ const resolved = resolveLocalSchemaRef(source.$ref, schemaRoot);
5856
+ if (resolved !== void 0) {
5857
+ if (resolvingRefs.has(source.$ref)) return { type: "OBJECT" };
5858
+ const nextRefs = new Set(resolvingRefs);
5859
+ nextRefs.add(source.$ref);
5860
+ const dereferenced = stripDraftMeta(resolved, schemaRoot, nextRefs);
5861
+ if (dereferenced && typeof dereferenced === "object" && !Array.isArray(dereferenced)) {
5862
+ const siblings = { ...source };
5863
+ delete siblings.$ref;
5864
+ const sanitizedSiblings = stripDraftMeta(siblings, schemaRoot, resolvingRefs);
5865
+ return {
5866
+ ...dereferenced,
5867
+ ...sanitizedSiblings
5868
+ };
5869
+ }
5870
+ return dereferenced;
5871
+ }
5872
+ }
6014
5873
  const out = {};
6015
- for (const [k, v] of Object.entries(obj)) {
5874
+ for (const [k, v] of Object.entries(source)) {
6016
5875
  if (STRIP_KEYS.has(k) || k.startsWith("x-")) continue;
6017
- out[k] = stripDraftMeta(v);
5876
+ if (k === "properties" && v && typeof v === "object" && !Array.isArray(v)) {
5877
+ out.properties = Object.fromEntries(
5878
+ Object.entries(v).map(([name, schema]) => [
5879
+ name,
5880
+ stripDraftMeta(schema, schemaRoot, resolvingRefs)
5881
+ ])
5882
+ );
5883
+ continue;
5884
+ }
5885
+ if (k === "type") {
5886
+ const types = (Array.isArray(v) ? v : [v]).map(normalizeCloudCodeSchemaType).filter((type) => typeof type === "string");
5887
+ const concreteType = types.find((type) => type !== "NULL");
5888
+ out.type = concreteType ?? "STRING";
5889
+ if (types.includes("NULL")) out.nullable = true;
5890
+ continue;
5891
+ }
5892
+ if (k === "enum" && Array.isArray(v)) {
5893
+ out.enum = v.filter((value) => value !== null && value !== void 0).map(String);
5894
+ continue;
5895
+ }
5896
+ if (k === "items" && Array.isArray(v)) {
5897
+ out.items = stripDraftMeta(v[0] ?? {}, schemaRoot, resolvingRefs);
5898
+ continue;
5899
+ }
5900
+ out[k] = stripDraftMeta(v, schemaRoot, resolvingRefs);
5901
+ }
5902
+ const union = Array.isArray(source.anyOf) ? source.anyOf : Array.isArray(source.oneOf) ? source.oneOf : void 0;
5903
+ if (union) {
5904
+ const nullable = union.some(isNullSchema);
5905
+ const alternatives = union.filter((branch) => !isNullSchema(branch)).map((branch) => stripDraftMeta(branch, schemaRoot, resolvingRefs)).filter((branch) => Boolean(branch) && typeof branch === "object" && !Array.isArray(branch));
5906
+ if (alternatives.length === 1) Object.assign(out, alternatives[0], out);
5907
+ else if (alternatives.length > 1) out.anyOf = alternatives;
5908
+ if (nullable) out.nullable = true;
5909
+ }
5910
+ if (!out.type) {
5911
+ if (out.properties && typeof out.properties === "object" && !Array.isArray(out.properties)) {
5912
+ out.type = "OBJECT";
5913
+ } else if (out.items && typeof out.items === "object" && !Array.isArray(out.items)) {
5914
+ out.type = "ARRAY";
5915
+ }
6018
5916
  }
6019
5917
  if (Array.isArray(out.required) && out.properties && typeof out.properties === "object") {
6020
5918
  const props = out.properties;
@@ -6075,15 +5973,18 @@ function extractSystem(system) {
6075
5973
  }
6076
5974
  return void 0;
6077
5975
  }
6078
- function translateTools2(tools) {
5976
+ function translateTools(tools) {
6079
5977
  if (!Array.isArray(tools) || tools.length === 0) return void 0;
6080
5978
  const decls = [];
6081
5979
  for (const t of tools) {
6082
5980
  if (typeof t.name !== "string") continue;
5981
+ const translated = stripDraftMeta(t.input_schema ?? { type: "object", properties: {} });
5982
+ const parameters = translated && typeof translated === "object" && !Array.isArray(translated) ? translated : { type: "OBJECT", properties: {} };
5983
+ if (!parameters.type) parameters.type = "OBJECT";
6083
5984
  decls.push({
6084
5985
  name: t.name,
6085
5986
  description: typeof t.description === "string" ? t.description : "",
6086
- parameters: stripDraftMeta(normalizeJsonSchema(t.input_schema ?? { type: "object", properties: {} }))
5987
+ parameters
6087
5988
  });
6088
5989
  }
6089
5990
  return decls.length > 0 ? [{ functionDeclarations: decls }] : void 0;
@@ -6109,8 +6010,7 @@ function anthropicToCloudCode(body, realModelId, projectId) {
6109
6010
  }
6110
6011
  if (typeof body.temperature === "number") generationConfig.temperature = body.temperature;
6111
6012
  if (typeof body.top_p === "number") generationConfig.topP = body.top_p;
6112
- generationConfig.thinkingConfig = DISABLE_CLOUD_CODE_THINKING;
6113
- const ccTools = translateTools2(body.tools);
6013
+ const ccTools = translateTools(body.tools);
6114
6014
  const systemInstruction = extractSystem(body.system);
6115
6015
  const request = {
6116
6016
  contents,
@@ -6124,7 +6024,7 @@ function anthropicToCloudCode(body, realModelId, projectId) {
6124
6024
  }
6125
6025
  return {
6126
6026
  project: projectId,
6127
- requestId: randomUUID4(),
6027
+ requestId: randomUUID3(),
6128
6028
  model: realModelId,
6129
6029
  userAgent: ANTIGRAVITY_USER_AGENT2,
6130
6030
  requestType: "agent",
@@ -6134,7 +6034,7 @@ function anthropicToCloudCode(body, realModelId, projectId) {
6134
6034
  }
6135
6035
 
6136
6036
  // src/antigravity/cloudcode-to-anthropic.ts
6137
- import { randomUUID as randomUUID5 } from "crypto";
6037
+ import { randomUUID as randomUUID4 } from "crypto";
6138
6038
  function writeEvent(res, event, data) {
6139
6039
  res.write(`event: ${event}
6140
6040
  data: ${JSON.stringify(data)}
@@ -6224,7 +6124,7 @@ function closeBlock(res, state) {
6224
6124
  }
6225
6125
  async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
6226
6126
  const state = {
6227
- messageId: `msg_${randomUUID5().replace(/-/g, "").slice(0, 24)}`,
6127
+ messageId: `msg_${randomUUID4().replace(/-/g, "").slice(0, 24)}`,
6228
6128
  model,
6229
6129
  blockIdx: 0,
6230
6130
  textBlockOpen: false,
@@ -6313,7 +6213,7 @@ async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
6313
6213
  closeBlock(res, state);
6314
6214
  }
6315
6215
  for (const tc of state.toolCalls) {
6316
- const rawToolId = `toolu_${randomUUID5().replace(/-/g, "").slice(0, 16)}`;
6216
+ const rawToolId = `toolu_${randomUUID4().replace(/-/g, "").slice(0, 16)}`;
6317
6217
  const toolId = encodeToolUseId(rawToolId, tc.signature);
6318
6218
  writeEvent(res, "content_block_start", {
6319
6219
  type: "content_block_start",
@@ -6364,7 +6264,7 @@ async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
6364
6264
  }
6365
6265
  async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
6366
6266
  const text4 = await upstreamRes.text();
6367
- const messageId = `msg_${randomUUID5().replace(/-/g, "").slice(0, 24)}`;
6267
+ const messageId = `msg_${randomUUID4().replace(/-/g, "").slice(0, 24)}`;
6368
6268
  const content = [];
6369
6269
  let stopReason = "end_turn";
6370
6270
  let inputTokens = 0;
@@ -6393,7 +6293,7 @@ async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
6393
6293
  else content.push({ type: "text", text: part.text });
6394
6294
  } else if (part.functionCall && typeof part.functionCall === "object") {
6395
6295
  const fc = part.functionCall;
6396
- const rawToolId = `toolu_${randomUUID5().replace(/-/g, "").slice(0, 16)}`;
6296
+ const rawToolId = `toolu_${randomUUID4().replace(/-/g, "").slice(0, 16)}`;
6397
6297
  content.push({
6398
6298
  type: "tool_use",
6399
6299
  id: encodeToolUseId(rawToolId, signature ?? pendingThoughtSignature),
@@ -6426,16 +6326,16 @@ async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
6426
6326
  }
6427
6327
 
6428
6328
  // src/proxy.ts
6429
- import { randomUUID as randomUUID6 } from "crypto";
6329
+ import { randomUUID as randomUUID5 } from "crypto";
6430
6330
 
6431
6331
  // src/sdk-adapter.ts
6432
- import { streamText, generateText, tool as tool2, jsonSchema as jsonSchema2 } from "ai";
6332
+ import { streamText, generateText, tool, jsonSchema } from "ai";
6433
6333
 
6434
6334
  // src/tool-search.ts
6435
6335
  var TOOL_SEARCH_TYPE_PREFIX = "tool_search_tool";
6436
- function isToolSearchTool(tool4) {
6437
- if (typeof tool4.type === "string" && tool4.type.startsWith(TOOL_SEARCH_TYPE_PREFIX)) return true;
6438
- const name = tool4.name ?? "";
6336
+ function isToolSearchTool(tool3) {
6337
+ if (typeof tool3.type === "string" && tool3.type.startsWith(TOOL_SEARCH_TYPE_PREFIX)) return true;
6338
+ const name = tool3.name ?? "";
6439
6339
  return name.includes("tool_search") || name === "ToolSearch";
6440
6340
  }
6441
6341
  function extractReferencedToolNames(messages) {
@@ -6474,16 +6374,16 @@ function resolveUpstreamTools(tools, messages) {
6474
6374
  if (!tools?.length) return [];
6475
6375
  const referenced = extractReferencedToolNames(messages);
6476
6376
  const upstream = [];
6477
- for (const tool4 of tools) {
6478
- if (isToolSearchTool(tool4)) {
6479
- upstream.push(tool4);
6377
+ for (const tool3 of tools) {
6378
+ if (isToolSearchTool(tool3)) {
6379
+ upstream.push(tool3);
6480
6380
  continue;
6481
6381
  }
6482
- if (tool4.defer_loading === true) {
6483
- if (referenced.has(tool4.name)) upstream.push(tool4);
6382
+ if (tool3.defer_loading === true) {
6383
+ if (referenced.has(tool3.name)) upstream.push(tool3);
6484
6384
  continue;
6485
6385
  }
6486
- upstream.push(tool4);
6386
+ upstream.push(tool3);
6487
6387
  }
6488
6388
  return upstream;
6489
6389
  }
@@ -6730,12 +6630,12 @@ function stripNullInputs(input) {
6730
6630
  }
6731
6631
  return out;
6732
6632
  }
6733
- function translateTools3(anthropicTools) {
6633
+ function translateTools2(anthropicTools) {
6734
6634
  if (!anthropicTools?.length) return void 0;
6735
6635
  const tools = {};
6736
6636
  for (const t of anthropicTools) {
6737
6637
  if (!t.name || !t.input_schema) continue;
6738
- tools[t.name] = tool2({ description: t.description ?? "", inputSchema: jsonSchema2(t.input_schema) });
6638
+ tools[t.name] = tool({ description: t.description ?? "", inputSchema: jsonSchema(t.input_schema) });
6739
6639
  }
6740
6640
  return Object.keys(tools).length ? tools : void 0;
6741
6641
  }
@@ -6746,7 +6646,7 @@ function translateToolChoice(tc) {
6746
6646
  if (tc.type === "tool" && tc.name) return { type: "tool", toolName: tc.name };
6747
6647
  return void 0;
6748
6648
  }
6749
- function translateRequest2(body, npm, options) {
6649
+ function translateRequest(body, npm, options) {
6750
6650
  const messages = body.messages ?? [];
6751
6651
  annotateToolNames(messages);
6752
6652
  const baseSystem = systemToString(body.system);
@@ -6783,7 +6683,7 @@ function translateRequest2(body, npm, options) {
6783
6683
  return {
6784
6684
  system: options?.openAiOAuth ? void 0 : systemText,
6785
6685
  messages: translateMessages(messages, npm, options?.onDebug),
6786
- tools: translateTools3(upstreamTools.length ? upstreamTools : void 0),
6686
+ tools: translateTools2(upstreamTools.length ? upstreamTools : void 0),
6787
6687
  toolChoice: translateToolChoice(body.tool_choice),
6788
6688
  maxOutputTokens: options?.openAiOAuth ? void 0 : body.max_tokens,
6789
6689
  temperature: body.temperature,
@@ -7119,7 +7019,7 @@ function lookupRoute(byAlias, id) {
7119
7019
  return void 0;
7120
7020
  }
7121
7021
  function startProxyCatalog(routes, defaultAliasId, debug = false) {
7122
- const proxyToken = randomUUID6();
7022
+ const proxyToken = randomUUID5();
7123
7023
  silenceSdkWarnings();
7124
7024
  if (routes.length === 0) {
7125
7025
  return Promise.reject(new Error("Proxy catalog requires at least one route"));
@@ -7246,7 +7146,7 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
7246
7146
  if (sessionId) {
7247
7147
  subagentRouting.registerSubagentRoute = (modelId) => subagentRouteRegistry.register(sessionId, modelId);
7248
7148
  }
7249
- const params = translateRequest2(anthropicBody, route.npm, {
7149
+ const params = translateRequest(anthropicBody, route.npm, {
7250
7150
  openAiOAuth,
7251
7151
  maxTools: maxToolsForNpm(route.npm),
7252
7152
  onDebug: (msg) => plog(() => msg),
@@ -10397,6 +10297,20 @@ async function refreshAntigravityOAuthModels(accessToken) {
10397
10297
  }).finally(() => clearTimeout(timer));
10398
10298
  if (!res.ok) continue;
10399
10299
  const body = await res.json();
10300
+ const deprecatedModelIds = body.deprecatedModelIds && typeof body.deprecatedModelIds === "object" && !Array.isArray(body.deprecatedModelIds) ? body.deprecatedModelIds : {};
10301
+ const resolveUpstreamModelId = (id) => {
10302
+ let current = id;
10303
+ const seen = /* @__PURE__ */ new Set();
10304
+ while (!seen.has(current)) {
10305
+ seen.add(current);
10306
+ const alias = deprecatedModelIds[current];
10307
+ if (!alias || typeof alias !== "object" || Array.isArray(alias)) break;
10308
+ const next = alias.newModelId;
10309
+ if (typeof next !== "string" || next.length === 0) break;
10310
+ current = next;
10311
+ }
10312
+ return current;
10313
+ };
10400
10314
  const raw = body.models && typeof body.models === "object" && !Array.isArray(body.models) ? Object.entries(body.models).map(([id, model]) => ({ id, ...model })) : Array.isArray(body.models) ? body.models.filter((m) => typeof m.id === "string" && m.id.length > 0) : [];
10401
10315
  if (raw.length === 0) continue;
10402
10316
  const models = raw.filter((m) => typeof m.id === "string" && m.id.length > 0 && !isAntigravityCloudCodeHelperSlot(m.id)).map((m) => {
@@ -10409,7 +10323,7 @@ async function refreshAntigravityOAuthModels(accessToken) {
10409
10323
  return {
10410
10324
  id,
10411
10325
  name,
10412
- upstreamModelId: id,
10326
+ upstreamModelId: resolveUpstreamModelId(id),
10413
10327
  family: isGemini ? "gemini" : id.split("-")[0] ?? id,
10414
10328
  brand: isGemini ? "Google" : isClaude ? "Anthropic" : isOpenAi ? "OpenAI" : "Other",
10415
10329
  contextWindow: maxTokens ?? resolveContextWindow(id),
@@ -11150,7 +11064,7 @@ async function askSaveServerPassword() {
11150
11064
  import { createServer as createServer2 } from "http";
11151
11065
 
11152
11066
  // src/openai-adapter.ts
11153
- import { tool as tool3, jsonSchema as jsonSchema3, streamText as streamText2, generateText as generateText2 } from "ai";
11067
+ import { tool as tool2, jsonSchema as jsonSchema2, streamText as streamText2, generateText as generateText2 } from "ai";
11154
11068
  function translateOpenAiRequest(body) {
11155
11069
  const toolNameById = /* @__PURE__ */ new Map();
11156
11070
  for (const msg of body.messages) {
@@ -11216,10 +11130,10 @@ function translateOpenAiRequest(body) {
11216
11130
  tools = {};
11217
11131
  for (const t of body.tools) {
11218
11132
  if (t.type === "function" && t.function.name) {
11219
- const schema = t.function.parameters ? jsonSchema3(t.function.parameters) : void 0;
11220
- tools[t.function.name] = tool3({
11133
+ const schema = t.function.parameters ? jsonSchema2(t.function.parameters) : void 0;
11134
+ tools[t.function.name] = tool2({
11221
11135
  description: t.function.description ?? "",
11222
- inputSchema: schema ?? jsonSchema3({ type: "object", properties: {} })
11136
+ inputSchema: schema ?? jsonSchema2({ type: "object", properties: {} })
11223
11137
  });
11224
11138
  }
11225
11139
  }
@@ -11509,7 +11423,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog, suba
11509
11423
  if (sessionId) {
11510
11424
  subagentRouting.registerSubagentRoute = (modelId) => subagentRouteRegistry.register(sessionId, modelId);
11511
11425
  }
11512
- const params = translateRequest2(body, model.npm, {
11426
+ const params = translateRequest(body, model.npm, {
11513
11427
  defaultEffort: anthropicEffortFromRequest(body) ? void 0 : model.defaultEffort,
11514
11428
  openAiOAuth: model.npm === "@ai-sdk/openai" && model.authType === "oauth",
11515
11429
  onDebug: plog,
@@ -12974,6 +12888,7 @@ ${pc6.bold("Usage:")}
12974
12888
  relay-ai providers auth openai-oauth
12975
12889
  relay-ai providers auth github-copilot
12976
12890
  relay-ai providers auth cline-pass
12891
+ relay-ai providers auth antigravity
12977
12892
 
12978
12893
  ${pc6.bold("Device code (works on SSH/VPS):")}
12979
12894
  xai-oauth SuperGrok / X Premium (device code at x.ai/device)
@@ -12981,22 +12896,25 @@ ${pc6.bold("Device code (works on SSH/VPS):")}
12981
12896
  github-copilot GitHub Copilot Free or paid (device code at github.com/login/device)
12982
12897
  cline-pass ClinePass account (device code at app.cline.bot)
12983
12898
 
12899
+ ${pc6.bold("Browser sign-in:")}
12900
+ antigravity Google Cloud Code Assist OAuth (opens Google sign-in)
12901
+
12984
12902
  ${pc6.dim("OpenCode CLI configs: use")} relay-ai providers import${pc6.dim(" (optional one-time migration).")}`;
12985
12903
  }
12986
12904
 
12987
12905
  // src/codex/app-launch.ts
12988
- import { execSync as execSync2, spawn as spawn2 } from "child_process";
12989
- import { existsSync as existsSync11, readdirSync as readdirSync2, realpathSync, statSync as statSync3 } from "fs";
12906
+ import { execFileSync as execFileSync3, execSync as execSync2, spawn as spawn3 } from "child_process";
12907
+ import { copyFileSync as copyFileSync3, existsSync as existsSync11, mkdirSync as mkdirSync8, readdirSync as readdirSync2, realpathSync, statSync as statSync3 } from "fs";
12990
12908
  import { homedir as homedir7 } from "os";
12991
- import { dirname as dirname5, join as join12 } from "path";
12909
+ import { dirname as dirname5, join as join12, win32 as winPath } from "path";
12992
12910
  import * as p6 from "@clack/prompts";
12993
12911
 
12994
12912
  // src/linux-display.ts
12995
- import { execFileSync as execFileSync3 } from "child_process";
12913
+ import { execFileSync as execFileSync2 } from "child_process";
12996
12914
  import { readdirSync } from "fs";
12997
12915
  function displayHasWindow(display, windowId) {
12998
12916
  try {
12999
- const output = execFileSync3("xprop", ["-display", display, "-id", windowId, "WM_CLASS"], {
12917
+ const output = execFileSync2("xprop", ["-display", display, "-id", windowId, "WM_CLASS"], {
13000
12918
  encoding: "utf8",
13001
12919
  stdio: ["ignore", "pipe", "ignore"]
13002
12920
  });
@@ -13074,6 +12992,54 @@ function linuxEmbeddedCodexCandidates(appPath) {
13074
12992
  function winLocalAppData() {
13075
12993
  return process.env.LOCALAPPDATA ?? join12(homedir7(), "AppData", "Local");
13076
12994
  }
12995
+ function windowsEmbeddedCodexCandidates(appPath, packageInstallLocations) {
12996
+ const candidates = [];
12997
+ if (appPath && !appPath.startsWith("shell:AppsFolder\\")) {
12998
+ const appDir = winPath.dirname(appPath);
12999
+ candidates.push(
13000
+ winPath.join(appDir, "resources", "codex.exe"),
13001
+ winPath.join(appDir, "app", "resources", "codex.exe")
13002
+ );
13003
+ }
13004
+ for (const installLocation of packageInstallLocations) {
13005
+ if (!installLocation.trim()) continue;
13006
+ candidates.push(
13007
+ winPath.join(installLocation, "app", "resources", "codex.exe"),
13008
+ winPath.join(installLocation, "resources", "codex.exe")
13009
+ );
13010
+ }
13011
+ return [...new Set(candidates)];
13012
+ }
13013
+ function windowsEmbeddedCodexCachePath(sourcePath, home = homedir7()) {
13014
+ const normalized = sourcePath.replaceAll("/", "\\");
13015
+ const match = normalized.match(/\\WindowsApps\\([^\\]+)\\/i);
13016
+ if (!match) return null;
13017
+ const packageDirectory = match[1].replace(/[^a-zA-Z0-9._-]/g, "_");
13018
+ return winPath.join(home, ".relay-ai", "codex", "embedded-runtime", packageDirectory, "codex.exe");
13019
+ }
13020
+ function executableWindowsEmbeddedCodexPath(sourcePath) {
13021
+ const cachePath2 = windowsEmbeddedCodexCachePath(sourcePath);
13022
+ if (!cachePath2) return sourcePath;
13023
+ try {
13024
+ const sourceSize = statSync3(sourcePath).size;
13025
+ if (existsSync11(cachePath2) && statSync3(cachePath2).size === sourceSize) return cachePath2;
13026
+ mkdirSync8(winPath.dirname(cachePath2), { recursive: true });
13027
+ copyFileSync3(sourcePath, cachePath2);
13028
+ return statSync3(cachePath2).size === sourceSize ? cachePath2 : null;
13029
+ } catch {
13030
+ return null;
13031
+ }
13032
+ }
13033
+ function winCodexPackageInstallLocations() {
13034
+ try {
13035
+ const out = runPowerShell(
13036
+ "Get-AppxPackage -Name 'OpenAI.Codex' | Sort-Object Version -Descending | Select-Object -ExpandProperty InstallLocation"
13037
+ );
13038
+ return out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
13039
+ } catch {
13040
+ return [];
13041
+ }
13042
+ }
13077
13043
  function winCodexExeCandidates() {
13078
13044
  const local = winLocalAppData();
13079
13045
  const bases = WIN_APP_NAMES.flatMap((name) => [
@@ -13143,14 +13109,25 @@ function findCodexApp(platform = process.platform) {
13143
13109
  }
13144
13110
  function findEmbeddedCodexBinary(platform = process.platform) {
13145
13111
  const appPath = findCodexApp(platform);
13146
- if (!appPath) return null;
13147
13112
  if (platform === "darwin") {
13113
+ if (!appPath) return null;
13148
13114
  const binary = join12(appPath, "Contents", "Resources", "codex");
13149
13115
  return existsSync11(binary) ? binary : null;
13150
13116
  }
13151
13117
  if (platform === "linux") {
13118
+ if (!appPath) return null;
13152
13119
  return linuxEmbeddedCodexCandidates(appPath).find((path) => existsSync11(path)) ?? null;
13153
13120
  }
13121
+ if (platform === "win32") {
13122
+ const sourcePath = windowsEmbeddedCodexCandidates(appPath, winCodexPackageInstallLocations()).find((path) => {
13123
+ try {
13124
+ return existsSync11(path) && statSync3(path).isFile();
13125
+ } catch {
13126
+ return false;
13127
+ }
13128
+ });
13129
+ return sourcePath ? executableWindowsEmbeddedCodexPath(sourcePath) : null;
13130
+ }
13154
13131
  return null;
13155
13132
  }
13156
13133
  function darwinIsRunning() {
@@ -13163,6 +13140,51 @@ function darwinIsRunning() {
13163
13140
  }
13164
13141
  });
13165
13142
  }
13143
+ function pgrepExact(names) {
13144
+ const pids = /* @__PURE__ */ new Set();
13145
+ for (const name of names) {
13146
+ try {
13147
+ for (const raw of run(`pgrep -x ${JSON.stringify(name)}`).split(/\s+/)) {
13148
+ const pid = Number.parseInt(raw, 10);
13149
+ if (Number.isFinite(pid) && pid > 0 && pid !== process.pid) pids.add(pid);
13150
+ }
13151
+ } catch {
13152
+ }
13153
+ }
13154
+ return [...pids];
13155
+ }
13156
+ function darwinMainExecutableCandidates(appPath) {
13157
+ return DARWIN_APP_NAMES.map((name) => join12(appPath, "Contents", "MacOS", name));
13158
+ }
13159
+ function darwinMainPidsFromProcessList(processList, commands, currentPid = process.pid) {
13160
+ const pids = /* @__PURE__ */ new Set();
13161
+ for (const line of processList.split("\n")) {
13162
+ const match = line.match(/^\s*(\d+)\s+(.+?)\s*$/);
13163
+ if (!match) continue;
13164
+ const pid = Number.parseInt(match[1], 10);
13165
+ const command = match[2];
13166
+ if (Number.isFinite(pid) && pid > 0 && pid !== currentPid && commands.some((candidate) => command === candidate || command.startsWith(`${candidate} `))) {
13167
+ pids.add(pid);
13168
+ }
13169
+ }
13170
+ return [...pids];
13171
+ }
13172
+ function darwinMatchingPids() {
13173
+ const appPath = findCodexApp("darwin");
13174
+ if (!appPath) return [];
13175
+ try {
13176
+ const processList = execFileSync3("ps", ["-axo", "pid=,command="], {
13177
+ encoding: "utf8",
13178
+ stdio: ["pipe", "pipe", "pipe"]
13179
+ });
13180
+ return darwinMainPidsFromProcessList(
13181
+ processList,
13182
+ darwinMainExecutableCandidates(appPath)
13183
+ );
13184
+ } catch {
13185
+ return [];
13186
+ }
13187
+ }
13166
13188
  function linuxIsRunning() {
13167
13189
  for (const name of ["ChatGPT", "chatgpt"]) {
13168
13190
  try {
@@ -13172,6 +13194,9 @@ function linuxIsRunning() {
13172
13194
  }
13173
13195
  return false;
13174
13196
  }
13197
+ function linuxMatchingPids() {
13198
+ return pgrepExact(["ChatGPT", "chatgpt"]);
13199
+ }
13175
13200
  function winMatchingPids() {
13176
13201
  try {
13177
13202
  const nameFilter = WIN_APP_NAMES.map((name) => `Name = '${name}.exe'`).join(" OR ");
@@ -13200,21 +13225,30 @@ function isCodexAppRunning() {
13200
13225
  if (process.platform === "linux") return linuxIsRunning();
13201
13226
  return false;
13202
13227
  }
13228
+ function codexAppMainPids(platform = process.platform) {
13229
+ if (platform === "darwin") return darwinMatchingPids();
13230
+ if (platform === "win32") return winMatchingPids();
13231
+ if (platform === "linux") return linuxMatchingPids();
13232
+ return [];
13233
+ }
13234
+ function pidIsAlive(pid) {
13235
+ try {
13236
+ process.kill(pid, 0);
13237
+ return true;
13238
+ } catch (err) {
13239
+ return err.code === "EPERM";
13240
+ }
13241
+ }
13203
13242
  function sleep(ms) {
13204
13243
  return new Promise((resolve) => setTimeout(resolve, ms));
13205
13244
  }
13206
- async function waitForQuit(timeoutMs) {
13245
+ async function waitForOriginalCodexPids(originalPids, timeoutMs, alive = pidIsAlive) {
13207
13246
  const deadline = Date.now() + timeoutMs;
13208
13247
  while (Date.now() < deadline) {
13209
- if (process.platform === "win32") {
13210
- if (winMatchingPids().length === 0) return true;
13211
- } else if (process.platform === "linux" ? !linuxIsRunning() : !darwinIsRunning()) {
13212
- return true;
13213
- }
13248
+ if (originalPids.every((pid) => !alive(pid))) return true;
13214
13249
  await sleep(200);
13215
13250
  }
13216
- if (process.platform === "win32") return winMatchingPids().length === 0;
13217
- return process.platform === "linux" ? !linuxIsRunning() : !darwinIsRunning();
13251
+ return originalPids.every((pid) => !alive(pid));
13218
13252
  }
13219
13253
  function openCodexAppAt(path) {
13220
13254
  if (process.platform === "darwin") {
@@ -13227,14 +13261,14 @@ function openCodexAppAt(path) {
13227
13261
  }
13228
13262
  if (process.platform === "win32") {
13229
13263
  if (path.startsWith("shell:AppsFolder\\")) {
13230
- spawn2("cmd.exe", ["/c", "start", "", path], { stdio: "ignore", detached: true }).unref();
13264
+ spawn3("cmd.exe", ["/c", "start", "", path], { stdio: "ignore", detached: true }).unref();
13231
13265
  } else {
13232
13266
  runPowerShell(`Start-Process -FilePath '${path.replace(/'/g, "''")}'`);
13233
13267
  }
13234
13268
  return;
13235
13269
  }
13236
13270
  if (process.platform === "linux") {
13237
- spawn2(path, [], { stdio: "ignore", detached: true, env: linuxLaunchEnv() }).unref();
13271
+ spawn3(path, [], { stdio: "ignore", detached: true, env: linuxLaunchEnv() }).unref();
13238
13272
  }
13239
13273
  }
13240
13274
  function openCodexApp() {
@@ -13246,12 +13280,11 @@ function openCodexApp() {
13246
13280
  }
13247
13281
  openCodexAppAt(path);
13248
13282
  }
13283
+ function darwinQuitAppleScript() {
13284
+ return `tell application id "${CODEX_BUNDLE_ID}" to quit`;
13285
+ }
13249
13286
  function darwinQuit() {
13250
- try {
13251
- execSync2(`osascript -e 'tell application "Codex" to quit'`, { stdio: "pipe" });
13252
- } catch {
13253
- execSync2(`osascript -e 'tell application id "${CODEX_BUNDLE_ID}" to quit'`, { stdio: "pipe" });
13254
- }
13287
+ execFileSync3("osascript", ["-e", darwinQuitAppleScript()], { stdio: "pipe" });
13255
13288
  }
13256
13289
  function winQuitGraceful() {
13257
13290
  const nameFilter = WIN_APP_NAMES.map((name) => `'${name}'`).join(",");
@@ -13273,13 +13306,19 @@ function quitCodexAppGracefully() {
13273
13306
  else if (process.platform === "win32") winQuitGraceful();
13274
13307
  else if (process.platform === "linux") linuxQuitGraceful();
13275
13308
  }
13276
- function winForceQuit() {
13277
- const pids = winMatchingPids();
13309
+ function winForceQuit(pids = winMatchingPids()) {
13278
13310
  if (pids.length === 0) return;
13279
13311
  runPowerShell(`Stop-Process -Id ${pids.join(",")} -Force -ErrorAction SilentlyContinue`);
13280
13312
  }
13281
- async function launchOrRestartCodexApp(prompt = "Restart ChatGPT Desktop to apply relay-ai settings?") {
13313
+ function restartTimeoutAction(platform) {
13314
+ return platform === "win32" ? "force-quit" : "fail-closed";
13315
+ }
13316
+ function gracefulQuitTimeoutMs(platform) {
13317
+ return platform === "darwin" ? 3e4 : 5e3;
13318
+ }
13319
+ async function launchOrRestartCodexApp(prompt = "Restart ChatGPT Desktop to apply relay-ai settings?", assumeYes = false) {
13282
13320
  const appPath = findCodexApp();
13321
+ const originalPids = codexAppMainPids();
13283
13322
  if (!isCodexAppRunning()) {
13284
13323
  if (!appPath) {
13285
13324
  throw new Error(
@@ -13289,9 +13328,15 @@ async function launchOrRestartCodexApp(prompt = "Restart ChatGPT Desktop to appl
13289
13328
  openCodexAppAt(appPath);
13290
13329
  return;
13291
13330
  }
13331
+ if (originalPids.length === 0) {
13332
+ throw new Error("ChatGPT Desktop is running but Relay could not identify its main process; refusing an unsafe restart.");
13333
+ }
13292
13334
  if (process.platform === "linux") {
13293
13335
  p6.log.info("Restarting ChatGPT Desktop to apply relay-ai settings...");
13294
13336
  linuxQuitGraceful();
13337
+ } else if (assumeYes) {
13338
+ if (process.platform === "darwin") darwinQuit();
13339
+ else if (process.platform === "win32") winQuitGraceful();
13295
13340
  } else {
13296
13341
  const restart = await p6.confirm({ message: prompt, initialValue: true });
13297
13342
  if (p6.isCancel(restart) || !restart) {
@@ -13301,10 +13346,18 @@ async function launchOrRestartCodexApp(prompt = "Restart ChatGPT Desktop to appl
13301
13346
  if (process.platform === "darwin") darwinQuit();
13302
13347
  else if (process.platform === "win32") winQuitGraceful();
13303
13348
  }
13304
- if (!await waitForQuit(5e3)) {
13305
- if (process.platform === "win32") winForceQuit();
13306
- await waitForQuit(5e3);
13349
+ const gracefulTimeout = gracefulQuitTimeoutMs(process.platform);
13350
+ if (!await waitForOriginalCodexPids(originalPids, gracefulTimeout)) {
13351
+ if (restartTimeoutAction(process.platform) === "force-quit") {
13352
+ winForceQuit(originalPids);
13353
+ if (!await waitForOriginalCodexPids(originalPids, 5e3)) {
13354
+ throw new Error("ChatGPT Desktop did not exit after its force-quit timeout; refusing to launch a duplicate process.");
13355
+ }
13356
+ } else {
13357
+ throw new Error("ChatGPT Desktop did not exit after graceful shutdown; refusing to relaunch or force-quit it.");
13358
+ }
13307
13359
  }
13360
+ if (isCodexAppRunning()) return;
13308
13361
  if (appPath) openCodexAppAt(appPath);
13309
13362
  else openCodexApp();
13310
13363
  }
@@ -13313,7 +13366,7 @@ function codexAppInstallHint() {
13313
13366
  }
13314
13367
 
13315
13368
  // src/claude-desktop/app-launch.ts
13316
- import { execSync as execSync3, spawn as spawn3 } from "child_process";
13369
+ import { execSync as execSync3, spawn as spawn4 } from "child_process";
13317
13370
  import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
13318
13371
  import { homedir as homedir8 } from "os";
13319
13372
  import { join as join13 } from "path";
@@ -13380,7 +13433,7 @@ function linuxWhichClaude() {
13380
13433
  return null;
13381
13434
  }
13382
13435
  }
13383
- function linuxMatchingPids() {
13436
+ function linuxMatchingPids2() {
13384
13437
  try {
13385
13438
  const out = run2("pgrep -x claude-desktop");
13386
13439
  return out.split(/\s+/).map((s) => Number.parseInt(s, 10)).filter((n) => Number.isFinite(n) && n > 0);
@@ -13389,7 +13442,7 @@ function linuxMatchingPids() {
13389
13442
  }
13390
13443
  }
13391
13444
  function linuxMainPid() {
13392
- const pids = linuxMatchingPids();
13445
+ const pids = linuxMatchingPids2();
13393
13446
  for (const pid of pids) {
13394
13447
  try {
13395
13448
  const cmdline = readFileSync12(`/proc/${pid}/cmdline`, "utf8");
@@ -13408,7 +13461,7 @@ function linuxQuit() {
13408
13461
  }
13409
13462
  }
13410
13463
  function linuxForceQuit() {
13411
- for (const pid of linuxMatchingPids()) {
13464
+ for (const pid of linuxMatchingPids2()) {
13412
13465
  try {
13413
13466
  process.kill(pid, "SIGKILL");
13414
13467
  } catch {
@@ -13487,26 +13540,26 @@ function winHasWindow2() {
13487
13540
  function isClaudeAppRunning() {
13488
13541
  if (process.platform === "darwin") return darwinIsRunning2();
13489
13542
  if (process.platform === "win32") return winMatchingPids2().length > 0 || winHasWindow2();
13490
- if (process.platform === "linux") return linuxMatchingPids().length > 0;
13543
+ if (process.platform === "linux") return linuxMatchingPids2().length > 0;
13491
13544
  return false;
13492
13545
  }
13493
13546
  function sleep2(ms) {
13494
13547
  return new Promise((resolve) => setTimeout(resolve, ms));
13495
13548
  }
13496
- async function waitForQuit2(timeoutMs) {
13549
+ async function waitForQuit(timeoutMs) {
13497
13550
  const deadline = Date.now() + timeoutMs;
13498
13551
  while (Date.now() < deadline) {
13499
13552
  if (process.platform === "win32") {
13500
13553
  if (winMatchingPids2().length === 0) return true;
13501
13554
  } else if (process.platform === "linux") {
13502
- if (linuxMatchingPids().length === 0) return true;
13555
+ if (linuxMatchingPids2().length === 0) return true;
13503
13556
  } else if (!darwinIsRunning2()) {
13504
13557
  return true;
13505
13558
  }
13506
13559
  await sleep2(200);
13507
13560
  }
13508
13561
  if (process.platform === "win32") return winMatchingPids2().length === 0;
13509
- if (process.platform === "linux") return linuxMatchingPids().length === 0;
13562
+ if (process.platform === "linux") return linuxMatchingPids2().length === 0;
13510
13563
  return !darwinIsRunning2();
13511
13564
  }
13512
13565
  function openClaudeAppAt(path) {
@@ -13520,14 +13573,14 @@ function openClaudeAppAt(path) {
13520
13573
  }
13521
13574
  if (process.platform === "win32") {
13522
13575
  if (path.startsWith("shell:AppsFolder\\")) {
13523
- spawn3("cmd.exe", ["/c", "start", "", path], { stdio: "ignore", detached: true }).unref();
13576
+ spawn4("cmd.exe", ["/c", "start", "", path], { stdio: "ignore", detached: true }).unref();
13524
13577
  } else {
13525
13578
  runPowerShell2(`Start-Process -FilePath '${path.replace(/'/g, "''")}'`);
13526
13579
  }
13527
13580
  return;
13528
13581
  }
13529
13582
  if (process.platform === "linux") {
13530
- spawn3(path, [], { stdio: "ignore", detached: true, env: linuxLaunchEnv() }).unref();
13583
+ spawn4(path, [], { stdio: "ignore", detached: true, env: linuxLaunchEnv() }).unref();
13531
13584
  }
13532
13585
  }
13533
13586
  function openClaudeApp() {
@@ -13582,10 +13635,10 @@ async function launchOrRestartClaudeApp(prompt = "Restart Claude Desktop to appl
13582
13635
  if (process.platform === "darwin") darwinQuit2();
13583
13636
  else if (process.platform === "win32") winQuitGraceful2();
13584
13637
  }
13585
- if (!await waitForQuit2(5e3)) {
13638
+ if (!await waitForQuit(5e3)) {
13586
13639
  if (process.platform === "win32") winForceQuit2();
13587
13640
  else if (process.platform === "linux") linuxForceQuit();
13588
- await waitForQuit2(5e3);
13641
+ await waitForQuit(5e3);
13589
13642
  }
13590
13643
  if (appPath) openClaudeAppAt(appPath);
13591
13644
  else openClaudeApp();
@@ -13613,6 +13666,8 @@ export {
13613
13666
  effortProviderOptions,
13614
13667
  deepMergeProviderOptions,
13615
13668
  thinkingProviderOptions,
13669
+ runCodexCommandSync,
13670
+ runCodexCommand,
13616
13671
  renderMultiAgentV2Feature,
13617
13672
  supportsMultiAgentV2,
13618
13673
  CODEX_APP_PROVIDER_ID,
@@ -13653,6 +13708,7 @@ export {
13653
13708
  getAppHome,
13654
13709
  getConfigPath,
13655
13710
  getProvidersPath,
13711
+ getLogsPath,
13656
13712
  loadPreferences,
13657
13713
  savePreferences,
13658
13714
  getAppPathOverride,
@@ -13763,10 +13819,6 @@ export {
13763
13819
  splitToolUseId,
13764
13820
  encodeToolUseId,
13765
13821
  serializeToolResultContent,
13766
- UNSUPPORTED_VOICE_MESSAGE,
13767
- sanitizeUnsupportedInlineData,
13768
- summarizeSdkRequestForTrace,
13769
- translateRequest,
13770
13822
  formatUpstreamErrorTrace,
13771
13823
  formatUpstreamError,
13772
13824
  upstreamHttpStatus,
@@ -13844,4 +13896,4 @@ export {
13844
13896
  supportsClaudeTransparentMode,
13845
13897
  buildHttpProxyRoutes
13846
13898
  };
13847
- //# sourceMappingURL=chunk-KDIY732Q.js.map
13899
+ //# sourceMappingURL=chunk-PYJQMEJD.js.map