@jacobbd/relay-ai 0.11.1 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{chunk-TRM2WGI6.js → chunk-3R25QO5X.js} +11 -1
- package/dist/chunk-3R25QO5X.js.map +1 -0
- package/dist/{chunk-O2XZFXHG.js → chunk-7ASGPECN.js} +34 -6
- package/dist/chunk-7ASGPECN.js.map +1 -0
- package/dist/{chunk-YD6A3ZB3.js → chunk-OYE6YZYP.js} +171 -18
- package/dist/chunk-OYE6YZYP.js.map +1 -0
- package/dist/{chunk-VXQ7ZVTW.js → chunk-UQKYSNDM.js} +2 -2
- package/dist/cli.js +243 -235
- package/dist/cli.js.map +1 -1
- package/dist/core/index.js +11 -1
- package/dist/core/index.js.map +1 -1
- package/dist/{manual-models-VEBTRQFK.js → manual-models-FJZEOQSO.js} +3 -3
- package/dist/{provider-templates-JY3NXZK7.js → provider-templates-WXVGL4XE.js} +2 -2
- package/dist/{ui-command-WKPJI2CU.js → ui-command-QFXKL6HH.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-O2XZFXHG.js.map +0 -1
- package/dist/chunk-TRM2WGI6.js.map +0 -1
- package/dist/chunk-YD6A3ZB3.js.map +0 -1
- /package/dist/{chunk-VXQ7ZVTW.js.map → chunk-UQKYSNDM.js.map} +0 -0
- /package/dist/{manual-models-VEBTRQFK.js.map → manual-models-FJZEOQSO.js.map} +0 -0
- /package/dist/{provider-templates-JY3NXZK7.js.map → provider-templates-WXVGL4XE.js.map} +0 -0
- /package/dist/{ui-command-WKPJI2CU.js.map → ui-command-QFXKL6HH.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
addManualModel,
|
|
4
4
|
removeManualModel
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UQKYSNDM.js";
|
|
6
6
|
import {
|
|
7
7
|
CODEX_APP_AUTO_COMPACT_RATIO,
|
|
8
8
|
CODEX_APP_PROVIDER_ID,
|
|
@@ -148,7 +148,7 @@ import {
|
|
|
148
148
|
waitForCodexAppQuit,
|
|
149
149
|
writeSecureLogLine,
|
|
150
150
|
zenRegistryStub
|
|
151
|
-
} from "./chunk-
|
|
151
|
+
} from "./chunk-OYE6YZYP.js";
|
|
152
152
|
import {
|
|
153
153
|
filterTemplates,
|
|
154
154
|
getTemplateById,
|
|
@@ -156,7 +156,7 @@ import {
|
|
|
156
156
|
listAddableTemplates,
|
|
157
157
|
listSupportedTemplates,
|
|
158
158
|
listVisibleOAuthTemplates
|
|
159
|
-
} from "./chunk-
|
|
159
|
+
} from "./chunk-3R25QO5X.js";
|
|
160
160
|
import {
|
|
161
161
|
ANTIGRAVITY_BASE_URLS,
|
|
162
162
|
BACKENDS,
|
|
@@ -196,6 +196,7 @@ import {
|
|
|
196
196
|
migrateGlobalOpencodeCredential,
|
|
197
197
|
migrateLegacyCloudProviders,
|
|
198
198
|
modelIdError,
|
|
199
|
+
normalizeToolSchemaForNpm,
|
|
199
200
|
oauthCredentialToKeychainJson,
|
|
200
201
|
parseDsmlToolCalls,
|
|
201
202
|
parseToolArguments,
|
|
@@ -221,7 +222,7 @@ import {
|
|
|
221
222
|
thinkingProviderOptions,
|
|
222
223
|
upstreamHttpStatus,
|
|
223
224
|
validateCustomEndpointUrl
|
|
224
|
-
} from "./chunk-
|
|
225
|
+
} from "./chunk-7ASGPECN.js";
|
|
225
226
|
import "./chunk-JIDIH7DS.js";
|
|
226
227
|
|
|
227
228
|
// src/cli.ts
|
|
@@ -2661,7 +2662,7 @@ function translateResponsesTools(tools, options = {}) {
|
|
|
2661
2662
|
if (options.maxTools !== void 0 && toolCount >= options.maxTools) return;
|
|
2662
2663
|
out[name] = tool({
|
|
2663
2664
|
description: description ?? "",
|
|
2664
|
-
inputSchema: jsonSchema(parameters ?? { type: "object", properties: {} })
|
|
2665
|
+
inputSchema: jsonSchema(normalizeToolSchemaForNpm(parameters ?? { type: "object", properties: {} }, options.npm))
|
|
2665
2666
|
});
|
|
2666
2667
|
toolCount++;
|
|
2667
2668
|
};
|
|
@@ -2688,6 +2689,9 @@ function translateResponsesTools(tools, options = {}) {
|
|
|
2688
2689
|
}
|
|
2689
2690
|
return Object.keys(out).length ? out : void 0;
|
|
2690
2691
|
}
|
|
2692
|
+
function omitsMaxOutputTokens(metadata) {
|
|
2693
|
+
return metadata?.providerId === "openrouter" || (metadata?.apiBaseUrl?.includes("openrouter.ai") ?? false);
|
|
2694
|
+
}
|
|
2691
2695
|
function translateResponsesRequest(body, npm, metadata, options = {}) {
|
|
2692
2696
|
const toolContext = createCodexToolContext();
|
|
2693
2697
|
let effectiveTools = body.tools ?? [];
|
|
@@ -2704,13 +2708,13 @@ function translateResponsesRequest(body, npm, metadata, options = {}) {
|
|
|
2704
2708
|
thinkingProviderOptions(npm),
|
|
2705
2709
|
effortProviderOptions(npm, effort, metadata?.upstreamModelId ?? body.model, metadata)
|
|
2706
2710
|
);
|
|
2707
|
-
const tools = translateResponsesTools([...effectiveTools, ...deferredTools], options);
|
|
2711
|
+
const tools = translateResponsesTools([...effectiveTools, ...deferredTools], { ...options, npm });
|
|
2708
2712
|
return {
|
|
2709
2713
|
instructions: system,
|
|
2710
2714
|
messages,
|
|
2711
2715
|
tools,
|
|
2712
2716
|
toolContext,
|
|
2713
|
-
maxOutputTokens: body.max_output_tokens,
|
|
2717
|
+
maxOutputTokens: omitsMaxOutputTokens(metadata) ? void 0 : body.max_output_tokens,
|
|
2714
2718
|
temperature: body.temperature,
|
|
2715
2719
|
providerOptions,
|
|
2716
2720
|
headers: options.requestHeaders
|
|
@@ -2986,7 +2990,8 @@ async function writeResponsesStream(fullStream, modelId, write, onDone, onProgre
|
|
|
2986
2990
|
toolCallCount: toolStates.length,
|
|
2987
2991
|
toolNames: toolStates.map((t) => t.name),
|
|
2988
2992
|
loopDetected,
|
|
2989
|
-
errorMessage: msg
|
|
2993
|
+
errorMessage: msg,
|
|
2994
|
+
errorStatus: part.error?.statusCode
|
|
2990
2995
|
});
|
|
2991
2996
|
if (is429) {
|
|
2992
2997
|
writeResponsesRateLimitStream(modelId, msg, write);
|
|
@@ -3430,6 +3435,14 @@ function classifyCodexDispatch(modelId, relayRoutes, nativeModelIds) {
|
|
|
3430
3435
|
if (route) return { kind: "relay", route };
|
|
3431
3436
|
return { kind: "unknown", modelId };
|
|
3432
3437
|
}
|
|
3438
|
+
function classifyCodexMixedDispatch(input) {
|
|
3439
|
+
if (input.subagentRoute) return { kind: "relay", route: input.subagentRoute };
|
|
3440
|
+
const dispatch = classifyCodexDispatch(input.modelId, input.relayRoutes, input.nativeModelIds);
|
|
3441
|
+
if (dispatch.kind === "unknown" && input.markedSubagent) {
|
|
3442
|
+
return { kind: "native", modelId: input.modelId };
|
|
3443
|
+
}
|
|
3444
|
+
return dispatch;
|
|
3445
|
+
}
|
|
3433
3446
|
function createMixedProxyCapability() {
|
|
3434
3447
|
return randomBytes(32).toString("base64url");
|
|
3435
3448
|
}
|
|
@@ -4046,6 +4059,13 @@ function isCodexV2CompactionRequest(body) {
|
|
|
4046
4059
|
);
|
|
4047
4060
|
}
|
|
4048
4061
|
var COMPACTION_MAX_OUTPUT_TOKENS = 4e3;
|
|
4062
|
+
function streamOutcome(failure, okStatus) {
|
|
4063
|
+
if (!failure) return { outcome: "ok", status: okStatus };
|
|
4064
|
+
return {
|
|
4065
|
+
outcome: "error",
|
|
4066
|
+
status: failure.errorStatus ?? (failure.errorMessage ? upstreamHttpStatus(void 0, failure.errorMessage) : "stream-aborted")
|
|
4067
|
+
};
|
|
4068
|
+
}
|
|
4049
4069
|
function protectCodexCompactionParams(body, params, contextWindow) {
|
|
4050
4070
|
if (!isLikelyCodexCompactionRequest(body)) {
|
|
4051
4071
|
return trimToContextLimit(params, contextWindow);
|
|
@@ -4345,74 +4365,68 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4345
4365
|
if (debug && markedSubagent) {
|
|
4346
4366
|
log15(`subagent dispatch: requested=${modelId} route=${subagentRoute?.modelId ?? "(none)"}`);
|
|
4347
4367
|
}
|
|
4348
|
-
if (mixedNative && markedSubagent && !subagentRoute) {
|
|
4349
|
-
audit({ transport: "http", requestedModel: modelId, dispatch: "relay-subagent", phase: "complete", outcome: "error", status: 503 });
|
|
4350
|
-
sendJson(res, 503, {
|
|
4351
|
-
error: {
|
|
4352
|
-
message: "Codex marked this request as a Sub-agent, but no configured Codex Sub-agent route is available.",
|
|
4353
|
-
type: "service_unavailable"
|
|
4354
|
-
}
|
|
4355
|
-
});
|
|
4356
|
-
return;
|
|
4357
|
-
}
|
|
4358
4368
|
if (mixedNative) {
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4369
|
+
const dispatch = classifyCodexMixedDispatch({
|
|
4370
|
+
modelId,
|
|
4371
|
+
markedSubagent,
|
|
4372
|
+
subagentRoute,
|
|
4373
|
+
relayRoutes: routes,
|
|
4374
|
+
nativeModelIds: mixedNative.nativeModelIds
|
|
4375
|
+
});
|
|
4376
|
+
if (dispatch.kind === "unknown") {
|
|
4377
|
+
audit({ transport: "http", requestedModel: modelId, dispatch: "unknown", phase: "complete", outcome: "error", status: 404 });
|
|
4378
|
+
sendJson(res, 404, { error: { message: `Unknown model: ${modelId}`, type: "invalid_request_error" } });
|
|
4379
|
+
return;
|
|
4380
|
+
}
|
|
4381
|
+
if (dispatch.kind === "native") {
|
|
4382
|
+
audit({
|
|
4383
|
+
transport: "http",
|
|
4384
|
+
requestedModel: modelId,
|
|
4385
|
+
dispatch: "native",
|
|
4386
|
+
phase: "dispatch",
|
|
4387
|
+
provider: "openai-native",
|
|
4388
|
+
routeModel: modelId,
|
|
4389
|
+
upstreamModel: modelId
|
|
4390
|
+
});
|
|
4391
|
+
const controller = new AbortController();
|
|
4392
|
+
req.once("aborted", () => controller.abort());
|
|
4393
|
+
try {
|
|
4394
|
+
const nativeResponse = await forwardNativeCodexHttp({
|
|
4395
|
+
body: rawBody,
|
|
4396
|
+
inboundHeaders: req.headers,
|
|
4397
|
+
nativeUrl: mixedNative.nativeBaseUrl ? `${mixedNative.nativeBaseUrl.replace(/\/$/, "")}/responses` : NATIVE_CODEX_RESPONSES_URL,
|
|
4398
|
+
signal: controller.signal,
|
|
4399
|
+
fetchImpl: mixedNative.nativeFetchImpl
|
|
4400
|
+
});
|
|
4401
|
+
const contentType = nativeResponse.headers.get("content-type");
|
|
4402
|
+
res.writeHead(nativeResponse.status, contentType ? { "content-type": contentType } : void 0);
|
|
4403
|
+
res.end(Buffer.from(await nativeResponse.arrayBuffer()));
|
|
4367
4404
|
audit({
|
|
4368
4405
|
transport: "http",
|
|
4369
4406
|
requestedModel: modelId,
|
|
4370
4407
|
dispatch: "native",
|
|
4371
|
-
phase: "
|
|
4408
|
+
phase: "complete",
|
|
4372
4409
|
provider: "openai-native",
|
|
4373
4410
|
routeModel: modelId,
|
|
4374
|
-
upstreamModel: modelId
|
|
4411
|
+
upstreamModel: modelId,
|
|
4412
|
+
outcome: nativeResponse.ok ? "ok" : "error",
|
|
4413
|
+
status: nativeResponse.status
|
|
4375
4414
|
});
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
audit({
|
|
4390
|
-
transport: "http",
|
|
4391
|
-
requestedModel: modelId,
|
|
4392
|
-
dispatch: "native",
|
|
4393
|
-
phase: "complete",
|
|
4394
|
-
provider: "openai-native",
|
|
4395
|
-
routeModel: modelId,
|
|
4396
|
-
upstreamModel: modelId,
|
|
4397
|
-
outcome: nativeResponse.ok ? "ok" : "error",
|
|
4398
|
-
status: nativeResponse.status
|
|
4399
|
-
});
|
|
4400
|
-
} catch (err) {
|
|
4401
|
-
audit({
|
|
4402
|
-
transport: "http",
|
|
4403
|
-
requestedModel: modelId,
|
|
4404
|
-
dispatch: "native",
|
|
4405
|
-
phase: "complete",
|
|
4406
|
-
provider: "openai-native",
|
|
4407
|
-
routeModel: modelId,
|
|
4408
|
-
upstreamModel: modelId,
|
|
4409
|
-
outcome: "error",
|
|
4410
|
-
status: "forward-failed"
|
|
4411
|
-
});
|
|
4412
|
-
if (!res.writableEnded) sendJson(res, 502, { error: { message: "Native Codex request failed", type: "upstream_error" } });
|
|
4413
|
-
}
|
|
4414
|
-
return;
|
|
4415
|
+
} catch (err) {
|
|
4416
|
+
audit({
|
|
4417
|
+
transport: "http",
|
|
4418
|
+
requestedModel: modelId,
|
|
4419
|
+
dispatch: "native",
|
|
4420
|
+
phase: "complete",
|
|
4421
|
+
provider: "openai-native",
|
|
4422
|
+
routeModel: modelId,
|
|
4423
|
+
upstreamModel: modelId,
|
|
4424
|
+
outcome: "error",
|
|
4425
|
+
status: "forward-failed"
|
|
4426
|
+
});
|
|
4427
|
+
if (!res.writableEnded) sendJson(res, 502, { error: { message: "Native Codex request failed", type: "upstream_error" } });
|
|
4415
4428
|
}
|
|
4429
|
+
return;
|
|
4416
4430
|
}
|
|
4417
4431
|
}
|
|
4418
4432
|
let resolved = subagentRoute ? resolveModel(routes, models, subagentRoute.modelId) : resolveModel(routes, models, modelId);
|
|
@@ -4511,11 +4525,13 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4511
4525
|
}
|
|
4512
4526
|
}
|
|
4513
4527
|
};
|
|
4528
|
+
let streamFailure;
|
|
4514
4529
|
try {
|
|
4515
4530
|
if (v2Compaction) {
|
|
4516
4531
|
await streamCompactionResponse(languageModel, params, modelId, write);
|
|
4517
4532
|
} else
|
|
4518
4533
|
await streamResponsesResponse(languageModel, params, modelId, write, (summary) => {
|
|
4534
|
+
if (summary.errorMessage || summary.aborted) streamFailure = summary;
|
|
4519
4535
|
if (debug) {
|
|
4520
4536
|
const failure = `${summary.aborted ? " aborted=yes" : ""}${summary.errorMessage ? ` error=${JSON.stringify(summary.errorMessage)}` : ""}`;
|
|
4521
4537
|
log15(`response done: model=${route.modelId} reasoningChars=${summary.reasoningChars} textChars=${summary.textChars} toolCalls=${summary.toolCallCount} toolNames=[${summary.toolNames.join(",")}] loopDetected=${summary.loopDetected ?? "no"} dsmlRecovered=${summary.dsmlToolCallsRecovered ?? 0}${failure} reasoningPreview=${JSON.stringify(summary.reasoningPreview)}`);
|
|
@@ -4533,8 +4549,7 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4533
4549
|
provider: route.providerId ?? "relay",
|
|
4534
4550
|
routeModel: route.modelId,
|
|
4535
4551
|
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
4536
|
-
|
|
4537
|
-
status: 200
|
|
4552
|
+
...streamOutcome(streamFailure, 200)
|
|
4538
4553
|
});
|
|
4539
4554
|
} catch (err) {
|
|
4540
4555
|
const msg = formatUpstreamError(err);
|
|
@@ -4877,190 +4892,182 @@ data: ${JSON.stringify({ error: { message: "Invalid JSON", type: "invalid_reques
|
|
|
4877
4892
|
if (debug && markedSubagent) {
|
|
4878
4893
|
log15(`WS subagent dispatch: requested=${modelId} route=${subagentRoute?.modelId ?? "(none)"}`);
|
|
4879
4894
|
}
|
|
4880
|
-
if (mixedNative && markedSubagent && !subagentRoute) {
|
|
4881
|
-
audit({ transport: "ws", requestedModel: modelId, dispatch: "relay-subagent", phase: "complete", outcome: "error", status: 503 });
|
|
4882
|
-
sendWsEvent(`event: error
|
|
4883
|
-
data: ${JSON.stringify({ error: {
|
|
4884
|
-
message: "Codex marked this request as a Sub-agent, but no configured Codex Sub-agent route is available.",
|
|
4885
|
-
type: "service_unavailable"
|
|
4886
|
-
} })}
|
|
4887
|
-
|
|
4888
|
-
`);
|
|
4889
|
-
closeSocket();
|
|
4890
|
-
return;
|
|
4891
|
-
}
|
|
4892
4895
|
if (mixedNative) {
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4896
|
+
const dispatch = classifyCodexMixedDispatch({
|
|
4897
|
+
modelId,
|
|
4898
|
+
markedSubagent,
|
|
4899
|
+
subagentRoute,
|
|
4900
|
+
relayRoutes: routes,
|
|
4901
|
+
nativeModelIds: mixedNative.nativeModelIds
|
|
4902
|
+
});
|
|
4903
|
+
if (dispatch.kind === "unknown") {
|
|
4904
|
+
audit({ transport: "ws", requestedModel: modelId, dispatch: "unknown", phase: "complete", outcome: "error", status: 404 });
|
|
4905
|
+
sendWsEvent(`event: error
|
|
4898
4906
|
data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "invalid_request_error" } })}
|
|
4899
4907
|
|
|
4900
4908
|
`);
|
|
4901
|
-
|
|
4909
|
+
closeSocket();
|
|
4910
|
+
return;
|
|
4911
|
+
}
|
|
4912
|
+
if (dispatch.kind === "native") {
|
|
4913
|
+
audit({
|
|
4914
|
+
transport: "ws",
|
|
4915
|
+
requestedModel: modelId,
|
|
4916
|
+
dispatch: "native",
|
|
4917
|
+
phase: "dispatch",
|
|
4918
|
+
provider: "openai-native",
|
|
4919
|
+
routeModel: modelId,
|
|
4920
|
+
upstreamModel: modelId
|
|
4921
|
+
});
|
|
4922
|
+
const nativeBody = prepareNativeCodexBody(body);
|
|
4923
|
+
if (debug && nativeBody !== body) {
|
|
4924
|
+
log15(`WS native history normalized: model=${modelId} converted Relay compaction for native verification`);
|
|
4925
|
+
}
|
|
4926
|
+
if (nativeActive && nativeUpstream) {
|
|
4927
|
+
if (nativeUpstream.readyState === WebSocket.OPEN) {
|
|
4928
|
+
if (debug) log15(`WS native forwarding next turn: model=${modelId}`);
|
|
4929
|
+
nativeSendTurn?.(nativeBody, modelId);
|
|
4930
|
+
} else if (debug) {
|
|
4931
|
+
log15(`WS native cannot forward next turn: upstream_state=${nativeUpstream.readyState}`);
|
|
4932
|
+
}
|
|
4902
4933
|
return;
|
|
4903
4934
|
}
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
if (
|
|
4916
|
-
|
|
4935
|
+
const wsTarget = mixedNative.nativeBaseUrl ? `${mixedNative.nativeBaseUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:").replace(/\/$/, "")}/responses` : void 0;
|
|
4936
|
+
const target = nativeResponsesWebSocketOptions({ headers: req.headers, wsUrl: wsTarget });
|
|
4937
|
+
let upstream;
|
|
4938
|
+
let nativeOpened = false;
|
|
4939
|
+
let nativeCompleted = false;
|
|
4940
|
+
let nativeTurnModelId = modelId;
|
|
4941
|
+
let nativeFrameCount = 0;
|
|
4942
|
+
let finished = false;
|
|
4943
|
+
let connectTimer;
|
|
4944
|
+
let firstFrameTimer;
|
|
4945
|
+
const clearTimers = () => {
|
|
4946
|
+
if (connectTimer) clearTimeout(connectTimer);
|
|
4947
|
+
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
4948
|
+
};
|
|
4949
|
+
const sendNativeError = (message) => {
|
|
4950
|
+
if (socket.destroyed) return;
|
|
4951
|
+
socket.write(wsEncodeTextFrame(JSON.stringify({
|
|
4952
|
+
type: "error",
|
|
4953
|
+
error: { type: "upstream_error", message }
|
|
4954
|
+
})));
|
|
4955
|
+
};
|
|
4956
|
+
const closeBoth = (message, closeCode = 1011) => {
|
|
4957
|
+
if (finished) return;
|
|
4958
|
+
finished = true;
|
|
4959
|
+
nativeActive = false;
|
|
4960
|
+
nativeSendTurn = void 0;
|
|
4961
|
+
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
4962
|
+
clearTimers();
|
|
4963
|
+
if (debug && message) {
|
|
4964
|
+
log15(`WS native upstream failed: model=${nativeTurnModelId} opened=${nativeOpened} frames=${nativeFrameCount} message=${message}`);
|
|
4917
4965
|
}
|
|
4918
|
-
if (
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4966
|
+
if (message && !nativeCompleted) {
|
|
4967
|
+
audit({
|
|
4968
|
+
transport: "ws",
|
|
4969
|
+
requestedModel: nativeTurnModelId,
|
|
4970
|
+
dispatch: "native",
|
|
4971
|
+
phase: "complete",
|
|
4972
|
+
provider: "openai-native",
|
|
4973
|
+
routeModel: nativeTurnModelId,
|
|
4974
|
+
upstreamModel: nativeTurnModelId,
|
|
4975
|
+
outcome: "error",
|
|
4976
|
+
status: "upstream-failed"
|
|
4977
|
+
});
|
|
4978
|
+
}
|
|
4979
|
+
if (message && !nativeCompleted) sendNativeError(message);
|
|
4980
|
+
try {
|
|
4981
|
+
upstream?.close();
|
|
4982
|
+
} catch {
|
|
4983
|
+
}
|
|
4984
|
+
closeSocket(closeCode);
|
|
4985
|
+
};
|
|
4986
|
+
const sendNativeTurn = (turnBody, turnModelId) => {
|
|
4987
|
+
if (!upstream || upstream.readyState !== WebSocket.OPEN) {
|
|
4988
|
+
if (debug) log15(`WS native cannot send turn: model=${turnModelId} upstream_state=${upstream?.readyState ?? "missing"}`);
|
|
4925
4989
|
return;
|
|
4926
4990
|
}
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4991
|
+
nativeTurnModelId = turnModelId;
|
|
4992
|
+
nativeCompleted = false;
|
|
4993
|
+
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
4994
|
+
upstream.send(JSON.stringify({ type: "response.create", ...turnBody }));
|
|
4995
|
+
firstFrameTimer = setTimeout(() => closeBoth("Native Codex WebSocket response timed out"), 6e4);
|
|
4996
|
+
};
|
|
4997
|
+
try {
|
|
4998
|
+
if (debug) {
|
|
4999
|
+
log15(`WS native connecting: model=${modelId} url=${target.url} headers=[${Object.keys(target.headers).join(",")}]`);
|
|
5000
|
+
}
|
|
5001
|
+
upstream = new WebSocket(target.url, { headers: target.headers });
|
|
5002
|
+
nativeUpstream = upstream;
|
|
5003
|
+
nativeSendTurn = sendNativeTurn;
|
|
5004
|
+
nativeActive = true;
|
|
5005
|
+
connectTimer = setTimeout(() => closeBoth("Native Codex WebSocket connection timed out"), 15e3);
|
|
5006
|
+
upstream.once("open", () => {
|
|
5007
|
+
nativeOpened = true;
|
|
4938
5008
|
if (connectTimer) clearTimeout(connectTimer);
|
|
4939
|
-
if (
|
|
4940
|
-
|
|
4941
|
-
|
|
5009
|
+
if (debug) log15(`WS native upstream open: model=${modelId}`);
|
|
5010
|
+
sendNativeTurn(nativeBody, modelId);
|
|
5011
|
+
});
|
|
5012
|
+
upstream.once("unexpected-response", (_request, response) => {
|
|
5013
|
+
if (debug) log15(`WS native upstream HTTP rejection: model=${modelId} status=${response.statusCode}`);
|
|
5014
|
+
response.resume();
|
|
5015
|
+
closeBoth(`Native Codex WebSocket rejected (${response.statusCode})`);
|
|
5016
|
+
});
|
|
5017
|
+
upstream.on("message", (data) => {
|
|
4942
5018
|
if (socket.destroyed) return;
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
5019
|
+
nativeFrameCount += 1;
|
|
5020
|
+
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
5021
|
+
const text6 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
|
|
5022
|
+
let eventType = "non-json";
|
|
5023
|
+
try {
|
|
5024
|
+
const parsed = JSON.parse(text6);
|
|
5025
|
+
if (typeof parsed.type === "string") eventType = parsed.type;
|
|
5026
|
+
if (eventType === "response.completed" || eventType === "response.failed" || eventType === "response.incomplete") {
|
|
5027
|
+
nativeCompleted = true;
|
|
5028
|
+
audit({
|
|
5029
|
+
transport: "ws",
|
|
5030
|
+
requestedModel: modelId,
|
|
5031
|
+
dispatch: "native",
|
|
5032
|
+
phase: "complete",
|
|
5033
|
+
provider: "openai-native",
|
|
5034
|
+
routeModel: modelId,
|
|
5035
|
+
upstreamModel: modelId,
|
|
5036
|
+
outcome: eventType === "response.completed" ? "ok" : "error",
|
|
5037
|
+
status: eventType
|
|
5038
|
+
});
|
|
5039
|
+
}
|
|
5040
|
+
} catch {
|
|
5041
|
+
}
|
|
5042
|
+
if (debug && (nativeFrameCount <= 3 || nativeCompleted || eventType === "error" || nativeFrameCount % 25 === 0)) {
|
|
5043
|
+
log15(`WS native frame#${nativeFrameCount}: model=${modelId} type=${eventType} bytes=${text6.length}`);
|
|
5044
|
+
}
|
|
5045
|
+
socket.write(wsEncodeTextFrame(text6));
|
|
5046
|
+
});
|
|
5047
|
+
upstream.once("error", (err) => closeBoth(`Native Codex WebSocket error: ${err.message}`));
|
|
5048
|
+
upstream.once("close", (code, reason) => {
|
|
5049
|
+
const detail = reason?.length ? ` reason=${reason.toString("utf8").slice(0, 200)}` : "";
|
|
5050
|
+
if (debug) log15(`WS native upstream close: model=${modelId} code=${code}${detail} frames=${nativeFrameCount}`);
|
|
5051
|
+
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
5052
|
+
nativeActive = false;
|
|
5053
|
+
if (!finished) closeBoth(nativeCompleted ? void 0 : `Native Codex WebSocket closed before completion (${code})`);
|
|
5054
|
+
});
|
|
5055
|
+
socket.once("close", () => {
|
|
5056
|
+
if (debug) log15(`WS native downstream close: model=${modelId} frames=${nativeFrameCount} completed=${nativeCompleted}`);
|
|
4950
5057
|
finished = true;
|
|
4951
5058
|
nativeActive = false;
|
|
4952
5059
|
nativeSendTurn = void 0;
|
|
4953
5060
|
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
4954
5061
|
clearTimers();
|
|
4955
|
-
if (debug && message) {
|
|
4956
|
-
log15(`WS native upstream failed: model=${nativeTurnModelId} opened=${nativeOpened} frames=${nativeFrameCount} message=${message}`);
|
|
4957
|
-
}
|
|
4958
|
-
if (message && !nativeCompleted) {
|
|
4959
|
-
audit({
|
|
4960
|
-
transport: "ws",
|
|
4961
|
-
requestedModel: nativeTurnModelId,
|
|
4962
|
-
dispatch: "native",
|
|
4963
|
-
phase: "complete",
|
|
4964
|
-
provider: "openai-native",
|
|
4965
|
-
routeModel: nativeTurnModelId,
|
|
4966
|
-
upstreamModel: nativeTurnModelId,
|
|
4967
|
-
outcome: "error",
|
|
4968
|
-
status: "upstream-failed"
|
|
4969
|
-
});
|
|
4970
|
-
}
|
|
4971
|
-
if (message && !nativeCompleted) sendNativeError(message);
|
|
4972
5062
|
try {
|
|
4973
5063
|
upstream?.close();
|
|
4974
5064
|
} catch {
|
|
4975
5065
|
}
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
if (!upstream || upstream.readyState !== WebSocket.OPEN) {
|
|
4980
|
-
if (debug) log15(`WS native cannot send turn: model=${turnModelId} upstream_state=${upstream?.readyState ?? "missing"}`);
|
|
4981
|
-
return;
|
|
4982
|
-
}
|
|
4983
|
-
nativeTurnModelId = turnModelId;
|
|
4984
|
-
nativeCompleted = false;
|
|
4985
|
-
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
4986
|
-
upstream.send(JSON.stringify({ type: "response.create", ...turnBody }));
|
|
4987
|
-
firstFrameTimer = setTimeout(() => closeBoth("Native Codex WebSocket response timed out"), 6e4);
|
|
4988
|
-
};
|
|
4989
|
-
try {
|
|
4990
|
-
if (debug) {
|
|
4991
|
-
log15(`WS native connecting: model=${modelId} url=${target.url} headers=[${Object.keys(target.headers).join(",")}]`);
|
|
4992
|
-
}
|
|
4993
|
-
upstream = new WebSocket(target.url, { headers: target.headers });
|
|
4994
|
-
nativeUpstream = upstream;
|
|
4995
|
-
nativeSendTurn = sendNativeTurn;
|
|
4996
|
-
nativeActive = true;
|
|
4997
|
-
connectTimer = setTimeout(() => closeBoth("Native Codex WebSocket connection timed out"), 15e3);
|
|
4998
|
-
upstream.once("open", () => {
|
|
4999
|
-
nativeOpened = true;
|
|
5000
|
-
if (connectTimer) clearTimeout(connectTimer);
|
|
5001
|
-
if (debug) log15(`WS native upstream open: model=${modelId}`);
|
|
5002
|
-
sendNativeTurn(nativeBody, modelId);
|
|
5003
|
-
});
|
|
5004
|
-
upstream.once("unexpected-response", (_request, response) => {
|
|
5005
|
-
if (debug) log15(`WS native upstream HTTP rejection: model=${modelId} status=${response.statusCode}`);
|
|
5006
|
-
response.resume();
|
|
5007
|
-
closeBoth(`Native Codex WebSocket rejected (${response.statusCode})`);
|
|
5008
|
-
});
|
|
5009
|
-
upstream.on("message", (data) => {
|
|
5010
|
-
if (socket.destroyed) return;
|
|
5011
|
-
nativeFrameCount += 1;
|
|
5012
|
-
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
5013
|
-
const text6 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
|
|
5014
|
-
let eventType = "non-json";
|
|
5015
|
-
try {
|
|
5016
|
-
const parsed = JSON.parse(text6);
|
|
5017
|
-
if (typeof parsed.type === "string") eventType = parsed.type;
|
|
5018
|
-
if (eventType === "response.completed" || eventType === "response.failed" || eventType === "response.incomplete") {
|
|
5019
|
-
nativeCompleted = true;
|
|
5020
|
-
audit({
|
|
5021
|
-
transport: "ws",
|
|
5022
|
-
requestedModel: modelId,
|
|
5023
|
-
dispatch: "native",
|
|
5024
|
-
phase: "complete",
|
|
5025
|
-
provider: "openai-native",
|
|
5026
|
-
routeModel: modelId,
|
|
5027
|
-
upstreamModel: modelId,
|
|
5028
|
-
outcome: eventType === "response.completed" ? "ok" : "error",
|
|
5029
|
-
status: eventType
|
|
5030
|
-
});
|
|
5031
|
-
}
|
|
5032
|
-
} catch {
|
|
5033
|
-
}
|
|
5034
|
-
if (debug && (nativeFrameCount <= 3 || nativeCompleted || eventType === "error" || nativeFrameCount % 25 === 0)) {
|
|
5035
|
-
log15(`WS native frame#${nativeFrameCount}: model=${modelId} type=${eventType} bytes=${text6.length}`);
|
|
5036
|
-
}
|
|
5037
|
-
socket.write(wsEncodeTextFrame(text6));
|
|
5038
|
-
});
|
|
5039
|
-
upstream.once("error", (err) => closeBoth(`Native Codex WebSocket error: ${err.message}`));
|
|
5040
|
-
upstream.once("close", (code, reason) => {
|
|
5041
|
-
const detail = reason?.length ? ` reason=${reason.toString("utf8").slice(0, 200)}` : "";
|
|
5042
|
-
if (debug) log15(`WS native upstream close: model=${modelId} code=${code}${detail} frames=${nativeFrameCount}`);
|
|
5043
|
-
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
5044
|
-
nativeActive = false;
|
|
5045
|
-
if (!finished) closeBoth(nativeCompleted ? void 0 : `Native Codex WebSocket closed before completion (${code})`);
|
|
5046
|
-
});
|
|
5047
|
-
socket.once("close", () => {
|
|
5048
|
-
if (debug) log15(`WS native downstream close: model=${modelId} frames=${nativeFrameCount} completed=${nativeCompleted}`);
|
|
5049
|
-
finished = true;
|
|
5050
|
-
nativeActive = false;
|
|
5051
|
-
nativeSendTurn = void 0;
|
|
5052
|
-
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
5053
|
-
clearTimers();
|
|
5054
|
-
try {
|
|
5055
|
-
upstream?.close();
|
|
5056
|
-
} catch {
|
|
5057
|
-
}
|
|
5058
|
-
});
|
|
5059
|
-
} catch (err) {
|
|
5060
|
-
closeBoth(`Native Codex WebSocket setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5061
|
-
}
|
|
5062
|
-
return;
|
|
5066
|
+
});
|
|
5067
|
+
} catch (err) {
|
|
5068
|
+
closeBoth(`Native Codex WebSocket setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5063
5069
|
}
|
|
5070
|
+
return;
|
|
5064
5071
|
}
|
|
5065
5072
|
}
|
|
5066
5073
|
externalActive = true;
|
|
@@ -5095,6 +5102,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
5095
5102
|
currentExternalCompletedResponse = void 0;
|
|
5096
5103
|
currentExternalStateInput = void 0;
|
|
5097
5104
|
currentExternalConsumedResponseId = void 0;
|
|
5105
|
+
let streamFailure;
|
|
5098
5106
|
const continuation = resolveExternalContinuation(body);
|
|
5099
5107
|
if (continuation.orphanedResponseId) {
|
|
5100
5108
|
if (debug) log15(`WS continuation rejected: unknown previous_response_id=${continuation.orphanedResponseId}`);
|
|
@@ -5155,6 +5163,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
5155
5163
|
await streamCompactionResponse(languageModel, params, modelId, sendWsEvent);
|
|
5156
5164
|
} else
|
|
5157
5165
|
await streamResponsesResponse(languageModel, params, modelId, sendWsEvent, (summary) => {
|
|
5166
|
+
if (summary.errorMessage || summary.aborted) streamFailure = summary;
|
|
5158
5167
|
if (debug) {
|
|
5159
5168
|
const failure = `${summary.aborted ? " aborted=yes" : ""}${summary.errorMessage ? ` error=${JSON.stringify(summary.errorMessage)}` : ""}`;
|
|
5160
5169
|
log15(`WS response done: model=${route.modelId} reasoningChars=${summary.reasoningChars} textChars=${summary.textChars} toolCalls=${summary.toolCallCount} toolNames=[${summary.toolNames.join(",")}] loopDetected=${summary.loopDetected ?? "no"} dsmlRecovered=${summary.dsmlToolCallsRecovered ?? 0}${failure} reasoningPreview=${JSON.stringify(summary.reasoningPreview)}`);
|
|
@@ -5178,8 +5187,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
5178
5187
|
provider: route.providerId ?? "relay",
|
|
5179
5188
|
routeModel: route.modelId,
|
|
5180
5189
|
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
5181
|
-
|
|
5182
|
-
status: "response.completed"
|
|
5190
|
+
...streamOutcome(streamFailure, "response.completed")
|
|
5183
5191
|
});
|
|
5184
5192
|
} catch (err) {
|
|
5185
5193
|
const msg = formatUpstreamError(err);
|
|
@@ -15973,7 +15981,7 @@ Options:
|
|
|
15973
15981
|
--trace Write debug logs under ~/.relay-ai/logs/`);
|
|
15974
15982
|
return 0;
|
|
15975
15983
|
}
|
|
15976
|
-
const { runUiCommand } = await import("./ui-command-
|
|
15984
|
+
const { runUiCommand } = await import("./ui-command-QFXKL6HH.js");
|
|
15977
15985
|
return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
|
|
15978
15986
|
}
|
|
15979
15987
|
if (parsed.command === "models") {
|