@jacobbd/relay-ai 0.11.1 → 0.12.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/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-VXQ7ZVTW.js → chunk-FD5EDSBM.js} +2 -2
- package/dist/{chunk-YD6A3ZB3.js → chunk-HL3EZ4OU.js} +171 -18
- package/dist/chunk-HL3EZ4OU.js.map +1 -0
- package/dist/{chunk-O2XZFXHG.js → chunk-Z5GL4ALA.js} +34 -6
- package/dist/chunk-Z5GL4ALA.js.map +1 -0
- package/dist/cli.js +241 -234
- 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-A6FSQV3S.js} +3 -3
- package/dist/{provider-templates-JY3NXZK7.js → provider-templates-WXVGL4XE.js} +2 -2
- package/dist/{ui-command-WKPJI2CU.js → ui-command-J7PTTCM5.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-FD5EDSBM.js.map} +0 -0
- /package/dist/{manual-models-VEBTRQFK.js.map → manual-models-A6FSQV3S.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-J7PTTCM5.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-FD5EDSBM.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-HL3EZ4OU.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-Z5GL4ALA.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
|
|
@@ -3430,6 +3434,14 @@ function classifyCodexDispatch(modelId, relayRoutes, nativeModelIds) {
|
|
|
3430
3434
|
if (route) return { kind: "relay", route };
|
|
3431
3435
|
return { kind: "unknown", modelId };
|
|
3432
3436
|
}
|
|
3437
|
+
function classifyCodexMixedDispatch(input) {
|
|
3438
|
+
if (input.subagentRoute) return { kind: "relay", route: input.subagentRoute };
|
|
3439
|
+
const dispatch = classifyCodexDispatch(input.modelId, input.relayRoutes, input.nativeModelIds);
|
|
3440
|
+
if (dispatch.kind === "unknown" && input.markedSubagent) {
|
|
3441
|
+
return { kind: "native", modelId: input.modelId };
|
|
3442
|
+
}
|
|
3443
|
+
return dispatch;
|
|
3444
|
+
}
|
|
3433
3445
|
function createMixedProxyCapability() {
|
|
3434
3446
|
return randomBytes(32).toString("base64url");
|
|
3435
3447
|
}
|
|
@@ -4046,6 +4058,13 @@ function isCodexV2CompactionRequest(body) {
|
|
|
4046
4058
|
);
|
|
4047
4059
|
}
|
|
4048
4060
|
var COMPACTION_MAX_OUTPUT_TOKENS = 4e3;
|
|
4061
|
+
function streamOutcome(failure, okStatus) {
|
|
4062
|
+
if (!failure) return { outcome: "ok", status: okStatus };
|
|
4063
|
+
return {
|
|
4064
|
+
outcome: "error",
|
|
4065
|
+
status: failure.errorMessage ? upstreamHttpStatus(void 0, failure.errorMessage) : "stream-aborted"
|
|
4066
|
+
};
|
|
4067
|
+
}
|
|
4049
4068
|
function protectCodexCompactionParams(body, params, contextWindow) {
|
|
4050
4069
|
if (!isLikelyCodexCompactionRequest(body)) {
|
|
4051
4070
|
return trimToContextLimit(params, contextWindow);
|
|
@@ -4345,74 +4364,68 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4345
4364
|
if (debug && markedSubagent) {
|
|
4346
4365
|
log15(`subagent dispatch: requested=${modelId} route=${subagentRoute?.modelId ?? "(none)"}`);
|
|
4347
4366
|
}
|
|
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
4367
|
if (mixedNative) {
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4368
|
+
const dispatch = classifyCodexMixedDispatch({
|
|
4369
|
+
modelId,
|
|
4370
|
+
markedSubagent,
|
|
4371
|
+
subagentRoute,
|
|
4372
|
+
relayRoutes: routes,
|
|
4373
|
+
nativeModelIds: mixedNative.nativeModelIds
|
|
4374
|
+
});
|
|
4375
|
+
if (dispatch.kind === "unknown") {
|
|
4376
|
+
audit({ transport: "http", requestedModel: modelId, dispatch: "unknown", phase: "complete", outcome: "error", status: 404 });
|
|
4377
|
+
sendJson(res, 404, { error: { message: `Unknown model: ${modelId}`, type: "invalid_request_error" } });
|
|
4378
|
+
return;
|
|
4379
|
+
}
|
|
4380
|
+
if (dispatch.kind === "native") {
|
|
4381
|
+
audit({
|
|
4382
|
+
transport: "http",
|
|
4383
|
+
requestedModel: modelId,
|
|
4384
|
+
dispatch: "native",
|
|
4385
|
+
phase: "dispatch",
|
|
4386
|
+
provider: "openai-native",
|
|
4387
|
+
routeModel: modelId,
|
|
4388
|
+
upstreamModel: modelId
|
|
4389
|
+
});
|
|
4390
|
+
const controller = new AbortController();
|
|
4391
|
+
req.once("aborted", () => controller.abort());
|
|
4392
|
+
try {
|
|
4393
|
+
const nativeResponse = await forwardNativeCodexHttp({
|
|
4394
|
+
body: rawBody,
|
|
4395
|
+
inboundHeaders: req.headers,
|
|
4396
|
+
nativeUrl: mixedNative.nativeBaseUrl ? `${mixedNative.nativeBaseUrl.replace(/\/$/, "")}/responses` : NATIVE_CODEX_RESPONSES_URL,
|
|
4397
|
+
signal: controller.signal,
|
|
4398
|
+
fetchImpl: mixedNative.nativeFetchImpl
|
|
4399
|
+
});
|
|
4400
|
+
const contentType = nativeResponse.headers.get("content-type");
|
|
4401
|
+
res.writeHead(nativeResponse.status, contentType ? { "content-type": contentType } : void 0);
|
|
4402
|
+
res.end(Buffer.from(await nativeResponse.arrayBuffer()));
|
|
4367
4403
|
audit({
|
|
4368
4404
|
transport: "http",
|
|
4369
4405
|
requestedModel: modelId,
|
|
4370
4406
|
dispatch: "native",
|
|
4371
|
-
phase: "
|
|
4407
|
+
phase: "complete",
|
|
4372
4408
|
provider: "openai-native",
|
|
4373
4409
|
routeModel: modelId,
|
|
4374
|
-
upstreamModel: modelId
|
|
4410
|
+
upstreamModel: modelId,
|
|
4411
|
+
outcome: nativeResponse.ok ? "ok" : "error",
|
|
4412
|
+
status: nativeResponse.status
|
|
4375
4413
|
});
|
|
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;
|
|
4414
|
+
} catch (err) {
|
|
4415
|
+
audit({
|
|
4416
|
+
transport: "http",
|
|
4417
|
+
requestedModel: modelId,
|
|
4418
|
+
dispatch: "native",
|
|
4419
|
+
phase: "complete",
|
|
4420
|
+
provider: "openai-native",
|
|
4421
|
+
routeModel: modelId,
|
|
4422
|
+
upstreamModel: modelId,
|
|
4423
|
+
outcome: "error",
|
|
4424
|
+
status: "forward-failed"
|
|
4425
|
+
});
|
|
4426
|
+
if (!res.writableEnded) sendJson(res, 502, { error: { message: "Native Codex request failed", type: "upstream_error" } });
|
|
4415
4427
|
}
|
|
4428
|
+
return;
|
|
4416
4429
|
}
|
|
4417
4430
|
}
|
|
4418
4431
|
let resolved = subagentRoute ? resolveModel(routes, models, subagentRoute.modelId) : resolveModel(routes, models, modelId);
|
|
@@ -4511,11 +4524,13 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4511
4524
|
}
|
|
4512
4525
|
}
|
|
4513
4526
|
};
|
|
4527
|
+
let streamFailure;
|
|
4514
4528
|
try {
|
|
4515
4529
|
if (v2Compaction) {
|
|
4516
4530
|
await streamCompactionResponse(languageModel, params, modelId, write);
|
|
4517
4531
|
} else
|
|
4518
4532
|
await streamResponsesResponse(languageModel, params, modelId, write, (summary) => {
|
|
4533
|
+
if (summary.errorMessage || summary.aborted) streamFailure = summary;
|
|
4519
4534
|
if (debug) {
|
|
4520
4535
|
const failure = `${summary.aborted ? " aborted=yes" : ""}${summary.errorMessage ? ` error=${JSON.stringify(summary.errorMessage)}` : ""}`;
|
|
4521
4536
|
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 +4548,7 @@ async function startCodexProxy(routes, options = {}) {
|
|
|
4533
4548
|
provider: route.providerId ?? "relay",
|
|
4534
4549
|
routeModel: route.modelId,
|
|
4535
4550
|
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
4536
|
-
|
|
4537
|
-
status: 200
|
|
4551
|
+
...streamOutcome(streamFailure, 200)
|
|
4538
4552
|
});
|
|
4539
4553
|
} catch (err) {
|
|
4540
4554
|
const msg = formatUpstreamError(err);
|
|
@@ -4877,190 +4891,182 @@ data: ${JSON.stringify({ error: { message: "Invalid JSON", type: "invalid_reques
|
|
|
4877
4891
|
if (debug && markedSubagent) {
|
|
4878
4892
|
log15(`WS subagent dispatch: requested=${modelId} route=${subagentRoute?.modelId ?? "(none)"}`);
|
|
4879
4893
|
}
|
|
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
4894
|
if (mixedNative) {
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4895
|
+
const dispatch = classifyCodexMixedDispatch({
|
|
4896
|
+
modelId,
|
|
4897
|
+
markedSubagent,
|
|
4898
|
+
subagentRoute,
|
|
4899
|
+
relayRoutes: routes,
|
|
4900
|
+
nativeModelIds: mixedNative.nativeModelIds
|
|
4901
|
+
});
|
|
4902
|
+
if (dispatch.kind === "unknown") {
|
|
4903
|
+
audit({ transport: "ws", requestedModel: modelId, dispatch: "unknown", phase: "complete", outcome: "error", status: 404 });
|
|
4904
|
+
sendWsEvent(`event: error
|
|
4898
4905
|
data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}`, type: "invalid_request_error" } })}
|
|
4899
4906
|
|
|
4900
4907
|
`);
|
|
4901
|
-
|
|
4908
|
+
closeSocket();
|
|
4909
|
+
return;
|
|
4910
|
+
}
|
|
4911
|
+
if (dispatch.kind === "native") {
|
|
4912
|
+
audit({
|
|
4913
|
+
transport: "ws",
|
|
4914
|
+
requestedModel: modelId,
|
|
4915
|
+
dispatch: "native",
|
|
4916
|
+
phase: "dispatch",
|
|
4917
|
+
provider: "openai-native",
|
|
4918
|
+
routeModel: modelId,
|
|
4919
|
+
upstreamModel: modelId
|
|
4920
|
+
});
|
|
4921
|
+
const nativeBody = prepareNativeCodexBody(body);
|
|
4922
|
+
if (debug && nativeBody !== body) {
|
|
4923
|
+
log15(`WS native history normalized: model=${modelId} converted Relay compaction for native verification`);
|
|
4924
|
+
}
|
|
4925
|
+
if (nativeActive && nativeUpstream) {
|
|
4926
|
+
if (nativeUpstream.readyState === WebSocket.OPEN) {
|
|
4927
|
+
if (debug) log15(`WS native forwarding next turn: model=${modelId}`);
|
|
4928
|
+
nativeSendTurn?.(nativeBody, modelId);
|
|
4929
|
+
} else if (debug) {
|
|
4930
|
+
log15(`WS native cannot forward next turn: upstream_state=${nativeUpstream.readyState}`);
|
|
4931
|
+
}
|
|
4902
4932
|
return;
|
|
4903
4933
|
}
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
if (
|
|
4916
|
-
|
|
4934
|
+
const wsTarget = mixedNative.nativeBaseUrl ? `${mixedNative.nativeBaseUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:").replace(/\/$/, "")}/responses` : void 0;
|
|
4935
|
+
const target = nativeResponsesWebSocketOptions({ headers: req.headers, wsUrl: wsTarget });
|
|
4936
|
+
let upstream;
|
|
4937
|
+
let nativeOpened = false;
|
|
4938
|
+
let nativeCompleted = false;
|
|
4939
|
+
let nativeTurnModelId = modelId;
|
|
4940
|
+
let nativeFrameCount = 0;
|
|
4941
|
+
let finished = false;
|
|
4942
|
+
let connectTimer;
|
|
4943
|
+
let firstFrameTimer;
|
|
4944
|
+
const clearTimers = () => {
|
|
4945
|
+
if (connectTimer) clearTimeout(connectTimer);
|
|
4946
|
+
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
4947
|
+
};
|
|
4948
|
+
const sendNativeError = (message) => {
|
|
4949
|
+
if (socket.destroyed) return;
|
|
4950
|
+
socket.write(wsEncodeTextFrame(JSON.stringify({
|
|
4951
|
+
type: "error",
|
|
4952
|
+
error: { type: "upstream_error", message }
|
|
4953
|
+
})));
|
|
4954
|
+
};
|
|
4955
|
+
const closeBoth = (message, closeCode = 1011) => {
|
|
4956
|
+
if (finished) return;
|
|
4957
|
+
finished = true;
|
|
4958
|
+
nativeActive = false;
|
|
4959
|
+
nativeSendTurn = void 0;
|
|
4960
|
+
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
4961
|
+
clearTimers();
|
|
4962
|
+
if (debug && message) {
|
|
4963
|
+
log15(`WS native upstream failed: model=${nativeTurnModelId} opened=${nativeOpened} frames=${nativeFrameCount} message=${message}`);
|
|
4917
4964
|
}
|
|
4918
|
-
if (
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4965
|
+
if (message && !nativeCompleted) {
|
|
4966
|
+
audit({
|
|
4967
|
+
transport: "ws",
|
|
4968
|
+
requestedModel: nativeTurnModelId,
|
|
4969
|
+
dispatch: "native",
|
|
4970
|
+
phase: "complete",
|
|
4971
|
+
provider: "openai-native",
|
|
4972
|
+
routeModel: nativeTurnModelId,
|
|
4973
|
+
upstreamModel: nativeTurnModelId,
|
|
4974
|
+
outcome: "error",
|
|
4975
|
+
status: "upstream-failed"
|
|
4976
|
+
});
|
|
4977
|
+
}
|
|
4978
|
+
if (message && !nativeCompleted) sendNativeError(message);
|
|
4979
|
+
try {
|
|
4980
|
+
upstream?.close();
|
|
4981
|
+
} catch {
|
|
4982
|
+
}
|
|
4983
|
+
closeSocket(closeCode);
|
|
4984
|
+
};
|
|
4985
|
+
const sendNativeTurn = (turnBody, turnModelId) => {
|
|
4986
|
+
if (!upstream || upstream.readyState !== WebSocket.OPEN) {
|
|
4987
|
+
if (debug) log15(`WS native cannot send turn: model=${turnModelId} upstream_state=${upstream?.readyState ?? "missing"}`);
|
|
4925
4988
|
return;
|
|
4926
4989
|
}
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4990
|
+
nativeTurnModelId = turnModelId;
|
|
4991
|
+
nativeCompleted = false;
|
|
4992
|
+
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
4993
|
+
upstream.send(JSON.stringify({ type: "response.create", ...turnBody }));
|
|
4994
|
+
firstFrameTimer = setTimeout(() => closeBoth("Native Codex WebSocket response timed out"), 6e4);
|
|
4995
|
+
};
|
|
4996
|
+
try {
|
|
4997
|
+
if (debug) {
|
|
4998
|
+
log15(`WS native connecting: model=${modelId} url=${target.url} headers=[${Object.keys(target.headers).join(",")}]`);
|
|
4999
|
+
}
|
|
5000
|
+
upstream = new WebSocket(target.url, { headers: target.headers });
|
|
5001
|
+
nativeUpstream = upstream;
|
|
5002
|
+
nativeSendTurn = sendNativeTurn;
|
|
5003
|
+
nativeActive = true;
|
|
5004
|
+
connectTimer = setTimeout(() => closeBoth("Native Codex WebSocket connection timed out"), 15e3);
|
|
5005
|
+
upstream.once("open", () => {
|
|
5006
|
+
nativeOpened = true;
|
|
4938
5007
|
if (connectTimer) clearTimeout(connectTimer);
|
|
4939
|
-
if (
|
|
4940
|
-
|
|
4941
|
-
|
|
5008
|
+
if (debug) log15(`WS native upstream open: model=${modelId}`);
|
|
5009
|
+
sendNativeTurn(nativeBody, modelId);
|
|
5010
|
+
});
|
|
5011
|
+
upstream.once("unexpected-response", (_request, response) => {
|
|
5012
|
+
if (debug) log15(`WS native upstream HTTP rejection: model=${modelId} status=${response.statusCode}`);
|
|
5013
|
+
response.resume();
|
|
5014
|
+
closeBoth(`Native Codex WebSocket rejected (${response.statusCode})`);
|
|
5015
|
+
});
|
|
5016
|
+
upstream.on("message", (data) => {
|
|
4942
5017
|
if (socket.destroyed) return;
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
5018
|
+
nativeFrameCount += 1;
|
|
5019
|
+
if (firstFrameTimer) clearTimeout(firstFrameTimer);
|
|
5020
|
+
const text6 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
|
|
5021
|
+
let eventType = "non-json";
|
|
5022
|
+
try {
|
|
5023
|
+
const parsed = JSON.parse(text6);
|
|
5024
|
+
if (typeof parsed.type === "string") eventType = parsed.type;
|
|
5025
|
+
if (eventType === "response.completed" || eventType === "response.failed" || eventType === "response.incomplete") {
|
|
5026
|
+
nativeCompleted = true;
|
|
5027
|
+
audit({
|
|
5028
|
+
transport: "ws",
|
|
5029
|
+
requestedModel: modelId,
|
|
5030
|
+
dispatch: "native",
|
|
5031
|
+
phase: "complete",
|
|
5032
|
+
provider: "openai-native",
|
|
5033
|
+
routeModel: modelId,
|
|
5034
|
+
upstreamModel: modelId,
|
|
5035
|
+
outcome: eventType === "response.completed" ? "ok" : "error",
|
|
5036
|
+
status: eventType
|
|
5037
|
+
});
|
|
5038
|
+
}
|
|
5039
|
+
} catch {
|
|
5040
|
+
}
|
|
5041
|
+
if (debug && (nativeFrameCount <= 3 || nativeCompleted || eventType === "error" || nativeFrameCount % 25 === 0)) {
|
|
5042
|
+
log15(`WS native frame#${nativeFrameCount}: model=${modelId} type=${eventType} bytes=${text6.length}`);
|
|
5043
|
+
}
|
|
5044
|
+
socket.write(wsEncodeTextFrame(text6));
|
|
5045
|
+
});
|
|
5046
|
+
upstream.once("error", (err) => closeBoth(`Native Codex WebSocket error: ${err.message}`));
|
|
5047
|
+
upstream.once("close", (code, reason) => {
|
|
5048
|
+
const detail = reason?.length ? ` reason=${reason.toString("utf8").slice(0, 200)}` : "";
|
|
5049
|
+
if (debug) log15(`WS native upstream close: model=${modelId} code=${code}${detail} frames=${nativeFrameCount}`);
|
|
5050
|
+
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
5051
|
+
nativeActive = false;
|
|
5052
|
+
if (!finished) closeBoth(nativeCompleted ? void 0 : `Native Codex WebSocket closed before completion (${code})`);
|
|
5053
|
+
});
|
|
5054
|
+
socket.once("close", () => {
|
|
5055
|
+
if (debug) log15(`WS native downstream close: model=${modelId} frames=${nativeFrameCount} completed=${nativeCompleted}`);
|
|
4950
5056
|
finished = true;
|
|
4951
5057
|
nativeActive = false;
|
|
4952
5058
|
nativeSendTurn = void 0;
|
|
4953
5059
|
if (nativeUpstream === upstream) nativeUpstream = void 0;
|
|
4954
5060
|
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
5061
|
try {
|
|
4973
5062
|
upstream?.close();
|
|
4974
5063
|
} catch {
|
|
4975
5064
|
}
|
|
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;
|
|
5065
|
+
});
|
|
5066
|
+
} catch (err) {
|
|
5067
|
+
closeBoth(`Native Codex WebSocket setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5063
5068
|
}
|
|
5069
|
+
return;
|
|
5064
5070
|
}
|
|
5065
5071
|
}
|
|
5066
5072
|
externalActive = true;
|
|
@@ -5095,6 +5101,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
5095
5101
|
currentExternalCompletedResponse = void 0;
|
|
5096
5102
|
currentExternalStateInput = void 0;
|
|
5097
5103
|
currentExternalConsumedResponseId = void 0;
|
|
5104
|
+
let streamFailure;
|
|
5098
5105
|
const continuation = resolveExternalContinuation(body);
|
|
5099
5106
|
if (continuation.orphanedResponseId) {
|
|
5100
5107
|
if (debug) log15(`WS continuation rejected: unknown previous_response_id=${continuation.orphanedResponseId}`);
|
|
@@ -5155,6 +5162,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
5155
5162
|
await streamCompactionResponse(languageModel, params, modelId, sendWsEvent);
|
|
5156
5163
|
} else
|
|
5157
5164
|
await streamResponsesResponse(languageModel, params, modelId, sendWsEvent, (summary) => {
|
|
5165
|
+
if (summary.errorMessage || summary.aborted) streamFailure = summary;
|
|
5158
5166
|
if (debug) {
|
|
5159
5167
|
const failure = `${summary.aborted ? " aborted=yes" : ""}${summary.errorMessage ? ` error=${JSON.stringify(summary.errorMessage)}` : ""}`;
|
|
5160
5168
|
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 +5186,7 @@ data: ${JSON.stringify({ error: { message: `Unknown model: ${modelId}` } })}
|
|
|
5178
5186
|
provider: route.providerId ?? "relay",
|
|
5179
5187
|
routeModel: route.modelId,
|
|
5180
5188
|
upstreamModel: route.auditUpstreamModelId ?? route.upstreamModelId,
|
|
5181
|
-
|
|
5182
|
-
status: "response.completed"
|
|
5189
|
+
...streamOutcome(streamFailure, "response.completed")
|
|
5183
5190
|
});
|
|
5184
5191
|
} catch (err) {
|
|
5185
5192
|
const msg = formatUpstreamError(err);
|
|
@@ -15973,7 +15980,7 @@ Options:
|
|
|
15973
15980
|
--trace Write debug logs under ~/.relay-ai/logs/`);
|
|
15974
15981
|
return 0;
|
|
15975
15982
|
}
|
|
15976
|
-
const { runUiCommand } = await import("./ui-command-
|
|
15983
|
+
const { runUiCommand } = await import("./ui-command-J7PTTCM5.js");
|
|
15977
15984
|
return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
|
|
15978
15985
|
}
|
|
15979
15986
|
if (parsed.command === "models") {
|