@jacobbd/relay-ai 0.9.7 → 0.10.0
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/{chunk-BJ4AE3PS.js → chunk-2NXLK3O6.js} +174 -20
- package/dist/chunk-2NXLK3O6.js.map +1 -0
- package/dist/{chunk-NYKVDBQC.js → chunk-P4IS6537.js} +7 -1
- package/dist/chunk-P4IS6537.js.map +1 -0
- package/dist/cli.js +67 -13
- package/dist/cli.js.map +1 -1
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.js +510 -4
- package/dist/core/index.js.map +1 -1
- package/dist/{provider-templates-CGWE66TD.js → provider-templates-7M24FAAI.js} +2 -2
- package/dist/{ui-command-NM4MNZLO.js → ui-command-6ZMM5K5K.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-BJ4AE3PS.js.map +0 -1
- package/dist/chunk-NYKVDBQC.js.map +0 -1
- /package/dist/{provider-templates-CGWE66TD.js.map → provider-templates-7M24FAAI.js.map} +0 -0
- /package/dist/{ui-command-NM4MNZLO.js.map → ui-command-6ZMM5K5K.js.map} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getTemplateById,
|
|
4
4
|
init_provider_templates
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-P4IS6537.js";
|
|
6
6
|
|
|
7
7
|
// src/constants.ts
|
|
8
8
|
import { homedir } from "os";
|
|
@@ -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.
|
|
14
|
+
version: "0.10.0",
|
|
15
15
|
publishConfig: {
|
|
16
16
|
access: "public"
|
|
17
17
|
},
|
|
@@ -6326,7 +6326,7 @@ async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
|
|
|
6326
6326
|
}
|
|
6327
6327
|
|
|
6328
6328
|
// src/proxy.ts
|
|
6329
|
-
import { randomUUID as
|
|
6329
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
6330
6330
|
|
|
6331
6331
|
// src/sdk-adapter.ts
|
|
6332
6332
|
import { streamText, generateText, tool, jsonSchema } from "ai";
|
|
@@ -6688,6 +6688,7 @@ function translateRequest(body, npm, options) {
|
|
|
6688
6688
|
maxOutputTokens: options?.openAiOAuth ? void 0 : body.max_tokens,
|
|
6689
6689
|
temperature: body.temperature,
|
|
6690
6690
|
providerOptions,
|
|
6691
|
+
headers: options?.requestHeaders,
|
|
6691
6692
|
subagentRouting: responseSubagentRouting
|
|
6692
6693
|
};
|
|
6693
6694
|
}
|
|
@@ -6956,6 +6957,109 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
6956
6957
|
};
|
|
6957
6958
|
}
|
|
6958
6959
|
|
|
6960
|
+
// src/opencode-session.ts
|
|
6961
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
6962
|
+
var OPENCODE_SESSION_HEADER = "x-opencode-session";
|
|
6963
|
+
var MAX_OPENCODE_SESSION_LENGTH = 256;
|
|
6964
|
+
var RELAY_USER_AGENT = `relay-ai/${VERSION}`;
|
|
6965
|
+
var NATIVE_CONVERSATION_HEADERS = [
|
|
6966
|
+
"x-claude-code-session-id",
|
|
6967
|
+
"session_id",
|
|
6968
|
+
"session-id",
|
|
6969
|
+
"x-session-id",
|
|
6970
|
+
"thread_id",
|
|
6971
|
+
"thread-id",
|
|
6972
|
+
"x-thread-id",
|
|
6973
|
+
"conversation_id",
|
|
6974
|
+
"conversation-id",
|
|
6975
|
+
"x-conversation-id"
|
|
6976
|
+
];
|
|
6977
|
+
function sanitizeSessionId(value) {
|
|
6978
|
+
if (typeof value !== "string") return void 0;
|
|
6979
|
+
const normalized = value.trim();
|
|
6980
|
+
if (!normalized || normalized.length > MAX_OPENCODE_SESSION_LENGTH) return void 0;
|
|
6981
|
+
if (/[\u0000-\u001f\u007f\r\n]/.test(normalized)) return void 0;
|
|
6982
|
+
return normalized;
|
|
6983
|
+
}
|
|
6984
|
+
function headerValue(headers, name) {
|
|
6985
|
+
if (!headers) return void 0;
|
|
6986
|
+
if (headers instanceof Headers) {
|
|
6987
|
+
return sanitizeSessionId(headers.get(name));
|
|
6988
|
+
}
|
|
6989
|
+
const lowerName = name.toLowerCase();
|
|
6990
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
6991
|
+
if (key.toLowerCase() !== lowerName) continue;
|
|
6992
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
6993
|
+
return sanitizeSessionId(first);
|
|
6994
|
+
}
|
|
6995
|
+
return void 0;
|
|
6996
|
+
}
|
|
6997
|
+
function metadataSessionId(body) {
|
|
6998
|
+
if (!body || typeof body !== "object") return void 0;
|
|
6999
|
+
const record = body;
|
|
7000
|
+
const metadata = record.metadata;
|
|
7001
|
+
if (metadata && typeof metadata === "object") {
|
|
7002
|
+
const metadataRecord = metadata;
|
|
7003
|
+
const direct = sanitizeSessionId(metadataRecord.session_id ?? metadataRecord.sessionId);
|
|
7004
|
+
if (direct) return direct;
|
|
7005
|
+
const userId = metadataRecord.user_id;
|
|
7006
|
+
if (typeof userId === "string") {
|
|
7007
|
+
try {
|
|
7008
|
+
const parsed = JSON.parse(userId);
|
|
7009
|
+
const parsedId = sanitizeSessionId(parsed.session_id ?? parsed.sessionId);
|
|
7010
|
+
if (parsedId) return parsedId;
|
|
7011
|
+
} catch {
|
|
7012
|
+
}
|
|
7013
|
+
}
|
|
7014
|
+
}
|
|
7015
|
+
return sanitizeSessionId(record.session_id ?? record.sessionId ?? record.thread_id ?? record.threadId);
|
|
7016
|
+
}
|
|
7017
|
+
function extractConversationId(headers, body) {
|
|
7018
|
+
const explicit = headerValue(headers, OPENCODE_SESSION_HEADER);
|
|
7019
|
+
if (explicit) return explicit;
|
|
7020
|
+
for (const name of NATIVE_CONVERSATION_HEADERS) {
|
|
7021
|
+
const native = headerValue(headers, name);
|
|
7022
|
+
if (native) return native;
|
|
7023
|
+
}
|
|
7024
|
+
return metadataSessionId(body);
|
|
7025
|
+
}
|
|
7026
|
+
function isOpenCodeGoEndpoint(providerId, endpoint) {
|
|
7027
|
+
const id = providerId?.trim().toLowerCase();
|
|
7028
|
+
if (id === "go" || id === "opencode-go") return true;
|
|
7029
|
+
if (!endpoint) return false;
|
|
7030
|
+
try {
|
|
7031
|
+
const url = new URL(endpoint);
|
|
7032
|
+
if (url.hostname.toLowerCase() !== "opencode.ai") return false;
|
|
7033
|
+
return url.pathname.split("/").some((segment) => segment.toLowerCase() === "go");
|
|
7034
|
+
} catch {
|
|
7035
|
+
return false;
|
|
7036
|
+
}
|
|
7037
|
+
}
|
|
7038
|
+
function mergeHeaders(base, overrides) {
|
|
7039
|
+
const result = {};
|
|
7040
|
+
for (const [key, value] of Object.entries(base ?? {})) {
|
|
7041
|
+
if (typeof value === "string") result[key] = value;
|
|
7042
|
+
}
|
|
7043
|
+
for (const [key, value] of Object.entries(overrides ?? {})) {
|
|
7044
|
+
for (const existing of Object.keys(result)) {
|
|
7045
|
+
if (existing.toLowerCase() === key.toLowerCase()) delete result[existing];
|
|
7046
|
+
}
|
|
7047
|
+
result[key] = value;
|
|
7048
|
+
}
|
|
7049
|
+
return result;
|
|
7050
|
+
}
|
|
7051
|
+
function openCodeGoHeaders(providerId, endpoint, sessionId, baseHeaders, options) {
|
|
7052
|
+
if (!isOpenCodeGoEndpoint(providerId, endpoint)) return void 0;
|
|
7053
|
+
let normalizedSessionId = sanitizeSessionId(sessionId);
|
|
7054
|
+
if (!normalizedSessionId && (options?.generateFallbackSession ?? true)) {
|
|
7055
|
+
normalizedSessionId = `relay-${randomUUID5()}`;
|
|
7056
|
+
}
|
|
7057
|
+
return mergeHeaders(baseHeaders, {
|
|
7058
|
+
"User-Agent": RELAY_USER_AGENT,
|
|
7059
|
+
...normalizedSessionId ? { [OPENCODE_SESSION_HEADER]: normalizedSessionId } : {}
|
|
7060
|
+
});
|
|
7061
|
+
}
|
|
7062
|
+
|
|
6959
7063
|
// src/proxy.ts
|
|
6960
7064
|
function appendSecureLog(logPath, line) {
|
|
6961
7065
|
const redacted = redactTraceLine(line);
|
|
@@ -7019,7 +7123,7 @@ function lookupRoute(byAlias, id) {
|
|
|
7019
7123
|
return void 0;
|
|
7020
7124
|
}
|
|
7021
7125
|
function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
7022
|
-
const proxyToken =
|
|
7126
|
+
const proxyToken = randomUUID6();
|
|
7023
7127
|
silenceSdkWarnings();
|
|
7024
7128
|
if (routes.length === 0) {
|
|
7025
7129
|
return Promise.reject(new Error("Proxy catalog requires at least one route"));
|
|
@@ -7102,6 +7206,12 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
7102
7206
|
const forwardBody = { ...anthropicBody, model: route.realModelId };
|
|
7103
7207
|
const targetUrl = `${upstreamUrl}/v1/messages`;
|
|
7104
7208
|
const isOAuth = route.authType === "oauth";
|
|
7209
|
+
const upstreamHeaders = openCodeGoHeaders(
|
|
7210
|
+
route.providerId,
|
|
7211
|
+
route.baseURL ?? upstreamUrl,
|
|
7212
|
+
extractConversationId(req.headers, anthropicBody),
|
|
7213
|
+
route.headers
|
|
7214
|
+
) ?? route.headers;
|
|
7105
7215
|
let effectiveBeta = inboundBeta;
|
|
7106
7216
|
let claudeCodeSessionId;
|
|
7107
7217
|
if (isOAuth) {
|
|
@@ -7126,7 +7236,7 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
7126
7236
|
isOAuth ? "oauth" : "api",
|
|
7127
7237
|
(message) => plog(message),
|
|
7128
7238
|
claudeCodeSessionId,
|
|
7129
|
-
|
|
7239
|
+
upstreamHeaders,
|
|
7130
7240
|
route.refreshToken,
|
|
7131
7241
|
(refreshed) => {
|
|
7132
7242
|
route.apiKey = refreshed;
|
|
@@ -7143,6 +7253,12 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
7143
7253
|
const openAiOAuth = route.npm === "@ai-sdk/openai" && route.authType === "oauth";
|
|
7144
7254
|
const subagentRouting = buildProxySubagentModelRouting(routes, route);
|
|
7145
7255
|
const sessionId = extractClaudeSessionId(req.headers, anthropicBody);
|
|
7256
|
+
const requestHeaders = openCodeGoHeaders(
|
|
7257
|
+
route.providerId,
|
|
7258
|
+
route.baseURL ?? upstreamUrl,
|
|
7259
|
+
extractConversationId(req.headers, anthropicBody),
|
|
7260
|
+
route.headers
|
|
7261
|
+
);
|
|
7146
7262
|
if (sessionId) {
|
|
7147
7263
|
subagentRouting.registerSubagentRoute = (modelId) => subagentRouteRegistry.register(sessionId, modelId);
|
|
7148
7264
|
}
|
|
@@ -7151,6 +7267,7 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
7151
7267
|
maxTools: maxToolsForNpm(route.npm),
|
|
7152
7268
|
onDebug: (msg) => plog(() => msg),
|
|
7153
7269
|
subagentRouting,
|
|
7270
|
+
...requestHeaders ? { requestHeaders } : {},
|
|
7154
7271
|
reasoningMetadata: {
|
|
7155
7272
|
providerId: route.providerId,
|
|
7156
7273
|
apiBaseUrl: route.baseURL,
|
|
@@ -8768,14 +8885,9 @@ function evaluateAgySwitchCompatibility(opts) {
|
|
|
8768
8885
|
};
|
|
8769
8886
|
}
|
|
8770
8887
|
if (opts.version && !KNOWN_COMPATIBLE_AGY_VERSIONS.has(opts.version)) {
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
warnings: [
|
|
8775
|
-
...warnings,
|
|
8776
|
-
`Unvalidated AGY version ${opts.version}; falling back to single-model mode for maximum stability.`
|
|
8777
|
-
]
|
|
8778
|
-
};
|
|
8888
|
+
warnings.push(
|
|
8889
|
+
`AGY version ${opts.version} is not in the explicitly validated set, but its slot config shape matches; multi-model switching is enabled.`
|
|
8890
|
+
);
|
|
8779
8891
|
} else if (!opts.version && !opts.versionReadError) {
|
|
8780
8892
|
warnings.push("AGY version is unknown; fixture shape matches, so multi-model switching remains enabled.");
|
|
8781
8893
|
}
|
|
@@ -11065,7 +11177,7 @@ import { createServer as createServer2 } from "http";
|
|
|
11065
11177
|
|
|
11066
11178
|
// src/openai-adapter.ts
|
|
11067
11179
|
import { tool as tool2, jsonSchema as jsonSchema2, streamText as streamText2, generateText as generateText2 } from "ai";
|
|
11068
|
-
function translateOpenAiRequest(body) {
|
|
11180
|
+
function translateOpenAiRequest(body, requestHeaders) {
|
|
11069
11181
|
const toolNameById = /* @__PURE__ */ new Map();
|
|
11070
11182
|
for (const msg of body.messages) {
|
|
11071
11183
|
if (msg.role === "assistant" && msg.tool_calls) {
|
|
@@ -11144,7 +11256,8 @@ function translateOpenAiRequest(body) {
|
|
|
11144
11256
|
tools,
|
|
11145
11257
|
toolChoice: sdkToolChoice,
|
|
11146
11258
|
temperature: body.temperature,
|
|
11147
|
-
maxOutputTokens: body.max_completion_tokens ?? body.max_tokens
|
|
11259
|
+
maxOutputTokens: body.max_completion_tokens ?? body.max_tokens,
|
|
11260
|
+
headers: requestHeaders
|
|
11148
11261
|
};
|
|
11149
11262
|
}
|
|
11150
11263
|
function toOpenAiFinishReason(reason) {
|
|
@@ -11365,6 +11478,12 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog, suba
|
|
|
11365
11478
|
const clientWantsStream = Boolean(body.stream);
|
|
11366
11479
|
const forwardBody = { ...body, model: upstreamModelId(model) };
|
|
11367
11480
|
const isOAuth = model.authType === "oauth";
|
|
11481
|
+
const upstreamHeaders = openCodeGoHeaders(
|
|
11482
|
+
model.providerId ?? model.sourceBackend,
|
|
11483
|
+
model.baseUrl ?? messagesUrl,
|
|
11484
|
+
extractConversationId(req.headers, body),
|
|
11485
|
+
model.headers
|
|
11486
|
+
) ?? model.headers;
|
|
11368
11487
|
let effectiveBeta = inboundBeta;
|
|
11369
11488
|
let claudeCodeSessionId;
|
|
11370
11489
|
if (isOAuth) {
|
|
@@ -11386,7 +11505,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog, suba
|
|
|
11386
11505
|
isOAuth ? "oauth" : "api",
|
|
11387
11506
|
(message) => plog(message),
|
|
11388
11507
|
claudeCodeSessionId,
|
|
11389
|
-
|
|
11508
|
+
upstreamHeaders,
|
|
11390
11509
|
refreshToken,
|
|
11391
11510
|
(refreshed) => {
|
|
11392
11511
|
model.apiKey = refreshed;
|
|
@@ -11436,7 +11555,16 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog, suba
|
|
|
11436
11555
|
interleavedReasoningField: model.interleavedReasoningField,
|
|
11437
11556
|
upstreamModelId: upstreamModelId(model)
|
|
11438
11557
|
},
|
|
11439
|
-
maxTools: npmMaxTools
|
|
11558
|
+
maxTools: npmMaxTools,
|
|
11559
|
+
...(() => {
|
|
11560
|
+
const requestHeaders = openCodeGoHeaders(
|
|
11561
|
+
model.providerId ?? model.sourceBackend,
|
|
11562
|
+
model.apiBaseUrl ?? model.baseUrl,
|
|
11563
|
+
extractConversationId(req.headers, body),
|
|
11564
|
+
model.headers
|
|
11565
|
+
);
|
|
11566
|
+
return requestHeaders ? { requestHeaders } : {};
|
|
11567
|
+
})()
|
|
11440
11568
|
});
|
|
11441
11569
|
const clientWantsStream = Boolean(body.stream);
|
|
11442
11570
|
const responseModelId = getResponseModelId(body.model, model, options);
|
|
@@ -11513,8 +11641,25 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
|
|
|
11513
11641
|
const completionsUrl = model.completionsUrl ? model.completionsUrl : `${backendFor(options, model).baseUrl}/v1/chat/completions`;
|
|
11514
11642
|
const apiKey2 = model.apiKey ?? options.apiKey;
|
|
11515
11643
|
const forwardBody = { ...body, model: upstreamModelId(model) };
|
|
11644
|
+
const upstreamHeaders = openCodeGoHeaders(
|
|
11645
|
+
model.providerId ?? model.sourceBackend,
|
|
11646
|
+
model.completionsUrl ?? model.apiBaseUrl,
|
|
11647
|
+
extractConversationId(req.headers, body),
|
|
11648
|
+
model.headers
|
|
11649
|
+
) ?? model.headers;
|
|
11516
11650
|
plog(() => `openai-direct-passthrough \u2192 ${completionsUrl} model=${forwardBody.model} stream=${Boolean(body.stream)}`);
|
|
11517
|
-
await relayAnthropicMessages(
|
|
11651
|
+
await relayAnthropicMessages(
|
|
11652
|
+
res,
|
|
11653
|
+
completionsUrl,
|
|
11654
|
+
forwardBody,
|
|
11655
|
+
apiKey2,
|
|
11656
|
+
Boolean(body.stream),
|
|
11657
|
+
void 0,
|
|
11658
|
+
void 0,
|
|
11659
|
+
(message) => plog(message),
|
|
11660
|
+
void 0,
|
|
11661
|
+
upstreamHeaders
|
|
11662
|
+
);
|
|
11518
11663
|
return;
|
|
11519
11664
|
}
|
|
11520
11665
|
const npm = model.npm || (model.modelFormat === "anthropic" ? "@ai-sdk/anthropic" : void 0);
|
|
@@ -11533,7 +11678,13 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
|
|
|
11533
11678
|
options.vertex,
|
|
11534
11679
|
providerRefreshToken(model.providerId, model.authType)
|
|
11535
11680
|
);
|
|
11536
|
-
const
|
|
11681
|
+
const requestHeaders = openCodeGoHeaders(
|
|
11682
|
+
model.providerId ?? model.sourceBackend,
|
|
11683
|
+
baseURL,
|
|
11684
|
+
extractConversationId(req.headers, body),
|
|
11685
|
+
model.headers
|
|
11686
|
+
);
|
|
11687
|
+
const params = translateOpenAiRequest(body, requestHeaders);
|
|
11537
11688
|
const clientWantsStream = Boolean(body.stream);
|
|
11538
11689
|
const responseModelId = getResponseModelId(body.model, model, options);
|
|
11539
11690
|
plog(() => `sdk-openai npm=${npm} upstream=${upstreamModelId(model)} responseModel=${responseModelId} stream=${clientWantsStream}`);
|
|
@@ -13835,6 +13986,9 @@ export {
|
|
|
13835
13986
|
formatUpstreamErrorTrace,
|
|
13836
13987
|
formatUpstreamError,
|
|
13837
13988
|
upstreamHttpStatus,
|
|
13989
|
+
OPENCODE_SESSION_HEADER,
|
|
13990
|
+
extractConversationId,
|
|
13991
|
+
openCodeGoHeaders,
|
|
13838
13992
|
aliasModelId,
|
|
13839
13993
|
startProxyCatalog,
|
|
13840
13994
|
startProxy,
|
|
@@ -13910,4 +14064,4 @@ export {
|
|
|
13910
14064
|
supportsClaudeTransparentMode,
|
|
13911
14065
|
buildHttpProxyRoutes
|
|
13912
14066
|
};
|
|
13913
|
-
//# sourceMappingURL=chunk-
|
|
14067
|
+
//# sourceMappingURL=chunk-2NXLK3O6.js.map
|