@code-yeongyu/senpi 2026.6.16 → 2026.6.17
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/CHANGELOG.md +28 -0
- package/dist/bun/restore-sandbox-env.d.ts +4 -0
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
- package/dist/bun/restore-sandbox-env.js +4 -0
- package/dist/bun/restore-sandbox-env.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +8 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +5 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +8 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +2 -2
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -4
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -10
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/export-html/vendor/marked.min.js +75 -3
- package/dist/core/http-dispatcher.d.ts +1 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +17 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +10 -4
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +10 -0
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts +10 -6
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +32 -17
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +17 -5
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +0 -1
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/docs/providers.md +20 -2
- package/docs/settings.md +12 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/README.md +19 -0
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts +5 -5
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +21 -57
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +149 -3
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +135 -25
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +52 -33
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +28 -12
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +7 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare.d.ts +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare.js +4 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js +13 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +107 -57
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +15 -7
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +8 -7
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/stream.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +8 -0
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/node-http-proxy.d.ts +2 -8
- package/node_modules/@earendil-works/pi-ai/dist/utils/node-http-proxy.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/node-http-proxy.js +17 -22
- package/node_modules/@earendil-works/pi-ai/dist/utils/node-http-proxy.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/anthropic.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/anthropic.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-env.d.ts +7 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-env.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-env.js +44 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-env.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts +3 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/box.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js +18 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/box.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js +3 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/loader.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +5 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +57 -15
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +5 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +22 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +2 -2
- package/npm-shrinkwrap.json +11 -11
- package/package.json +5 -5
|
@@ -21,6 +21,7 @@ import { combineAbortSignals } from "../utils/abort-signals.js";
|
|
|
21
21
|
import { appendAssistantMessageDiagnostic, createAssistantMessageDiagnostic, formatThrownValue, } from "../utils/diagnostics.js";
|
|
22
22
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
23
23
|
import { headersToRecord } from "../utils/headers.js";
|
|
24
|
+
import { resolveHttpProxyUrlForTarget } from "../utils/node-http-proxy.js";
|
|
24
25
|
import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
|
|
25
26
|
import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
|
|
26
27
|
import { applyExtraBody, buildBaseOptions, clampMaxForOpenAI, OPENAI_RESPONSES_RESERVED_BODY_KEYS, } from "./simple-options.js";
|
|
@@ -38,6 +39,7 @@ const DEFAULT_SSE_HEADER_TIMEOUT_MS = 20_000;
|
|
|
38
39
|
const DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS = 15_000;
|
|
39
40
|
const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
40
41
|
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
|
42
|
+
const CODEX_PREVIOUS_RESPONSE_STALE_CODES = new Set(["previous_response_not_found", "codex_previous_response_stale"]);
|
|
41
43
|
const CODEX_RESPONSE_STATUSES = new Set([
|
|
42
44
|
"completed",
|
|
43
45
|
"incomplete",
|
|
@@ -426,23 +428,46 @@ class CodexProtocolError extends Error {
|
|
|
426
428
|
function isCodexNonTransportError(error) {
|
|
427
429
|
return error instanceof CodexApiError || error instanceof CodexProtocolError;
|
|
428
430
|
}
|
|
431
|
+
function asRecord(value) {
|
|
432
|
+
return value && typeof value === "object" ? value : null;
|
|
433
|
+
}
|
|
434
|
+
function getString(value) {
|
|
435
|
+
return typeof value === "string" ? value : undefined;
|
|
436
|
+
}
|
|
437
|
+
function getCodexEventError(event) {
|
|
438
|
+
const response = asRecord(event.response);
|
|
439
|
+
return asRecord(event.error) ?? (response ? asRecord(response.error) : null);
|
|
440
|
+
}
|
|
441
|
+
function getCodexEventErrorCode(event) {
|
|
442
|
+
const error = getCodexEventError(event);
|
|
443
|
+
return getString(error?.code) ?? getString(error?.type) ?? getString(event.code) ?? "";
|
|
444
|
+
}
|
|
445
|
+
function getCodexEventErrorMessage(event) {
|
|
446
|
+
const response = asRecord(event.response);
|
|
447
|
+
const error = getCodexEventError(event);
|
|
448
|
+
return getString(error?.message) ?? getString(event.message) ?? getString(response?.message) ?? "";
|
|
449
|
+
}
|
|
450
|
+
function isCodexPreviousResponseStale(error) {
|
|
451
|
+
return (error instanceof CodexApiError &&
|
|
452
|
+
typeof error.code === "string" &&
|
|
453
|
+
CODEX_PREVIOUS_RESPONSE_STALE_CODES.has(error.code));
|
|
454
|
+
}
|
|
429
455
|
async function* mapCodexEvents(events) {
|
|
430
456
|
for await (const event of events) {
|
|
431
457
|
const type = typeof event.type === "string" ? event.type : undefined;
|
|
432
458
|
if (!type)
|
|
433
459
|
continue;
|
|
434
460
|
if (type === "error") {
|
|
435
|
-
const code = event
|
|
436
|
-
const message = event
|
|
461
|
+
const code = getCodexEventErrorCode(event);
|
|
462
|
+
const message = getCodexEventErrorMessage(event);
|
|
437
463
|
throw new CodexApiError(`Codex error: ${message || code || JSON.stringify(event)}`, {
|
|
438
464
|
code: code || undefined,
|
|
439
465
|
payload: event,
|
|
440
466
|
});
|
|
441
467
|
}
|
|
442
468
|
if (type === "response.failed") {
|
|
443
|
-
const
|
|
444
|
-
const
|
|
445
|
-
const message = response?.error?.message;
|
|
469
|
+
const code = getCodexEventErrorCode(event);
|
|
470
|
+
const message = getCodexEventErrorMessage(event);
|
|
446
471
|
throw new CodexApiError(message || "Codex response failed", { code, payload: event });
|
|
447
472
|
}
|
|
448
473
|
if (type === "response.done" || type === "response.completed" || type === "response.incomplete") {
|
|
@@ -604,16 +629,13 @@ function recordWebSocketFailure(sessionId, error) {
|
|
|
604
629
|
stats.websocketFallbackActive = true;
|
|
605
630
|
}
|
|
606
631
|
let _cachedWebsocket = null;
|
|
607
|
-
async function getWebSocketConstructor() {
|
|
608
|
-
if (_cachedWebsocket)
|
|
632
|
+
async function getWebSocketConstructor(env) {
|
|
633
|
+
if (!env && _cachedWebsocket)
|
|
609
634
|
return _cachedWebsocket;
|
|
610
635
|
// bun doesn't respect http proxy envs, ref: https://github.com/oven-sh/bun/issues/15489
|
|
611
636
|
// TODO: remove this when bun supports proxy envs in websocket.
|
|
612
|
-
if (process
|
|
613
|
-
|
|
614
|
-
const m = await dynamicImport("proxy-from-env");
|
|
615
|
-
const getProxyForUrl = m.getProxyForUrl;
|
|
616
|
-
_cachedWebsocket = class {
|
|
637
|
+
if (typeof process !== "undefined" && process.versions?.bun) {
|
|
638
|
+
const WebSocketWithProxy = class {
|
|
617
639
|
constructor(url, options) {
|
|
618
640
|
let _opts = {};
|
|
619
641
|
if (Array.isArray(options) || typeof options === "string") {
|
|
@@ -622,10 +644,10 @@ async function getWebSocketConstructor() {
|
|
|
622
644
|
else {
|
|
623
645
|
_opts = { ...options };
|
|
624
646
|
}
|
|
625
|
-
const
|
|
647
|
+
const proxyUrl = resolveHttpProxyUrlForTarget(url.toString().replace(/^wss:/, "https:").replace(/^ws:/, "http:"), env);
|
|
626
648
|
this.socket = Reflect.construct(WebSocket, [
|
|
627
649
|
url,
|
|
628
|
-
{ ..._opts, ...(
|
|
650
|
+
{ ..._opts, ...(proxyUrl ? { proxy: proxyUrl.toString() } : {}) },
|
|
629
651
|
]);
|
|
630
652
|
}
|
|
631
653
|
close(code, reason) {
|
|
@@ -641,7 +663,10 @@ async function getWebSocketConstructor() {
|
|
|
641
663
|
this.socket.removeEventListener(type, listener);
|
|
642
664
|
}
|
|
643
665
|
};
|
|
644
|
-
|
|
666
|
+
if (!env) {
|
|
667
|
+
_cachedWebsocket = WebSocketWithProxy;
|
|
668
|
+
}
|
|
669
|
+
return WebSocketWithProxy;
|
|
645
670
|
}
|
|
646
671
|
const ctor = globalThis.WebSocket;
|
|
647
672
|
if (!isWebSocketConstructor(ctor))
|
|
@@ -686,8 +711,8 @@ function scheduleSessionWebSocketExpiry(sessionId, entry) {
|
|
|
686
711
|
websocketSessionCache.delete(sessionId);
|
|
687
712
|
}, SESSION_WEBSOCKET_CACHE_TTL_MS);
|
|
688
713
|
}
|
|
689
|
-
async function connectWebSocket(url, headers, signal, connectTimeoutMs = DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS) {
|
|
690
|
-
const WebSocketCtor = await getWebSocketConstructor();
|
|
714
|
+
async function connectWebSocket(url, headers, signal, connectTimeoutMs = DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS, env) {
|
|
715
|
+
const WebSocketCtor = await getWebSocketConstructor(env);
|
|
691
716
|
if (!WebSocketCtor) {
|
|
692
717
|
throw new Error("WebSocket transport is not available in this runtime");
|
|
693
718
|
}
|
|
@@ -754,9 +779,9 @@ async function connectWebSocket(url, headers, signal, connectTimeoutMs = DEFAULT
|
|
|
754
779
|
}
|
|
755
780
|
});
|
|
756
781
|
}
|
|
757
|
-
async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutMs) {
|
|
782
|
+
async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutMs, env) {
|
|
758
783
|
if (!sessionId) {
|
|
759
|
-
const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs);
|
|
784
|
+
const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
|
|
760
785
|
return {
|
|
761
786
|
socket,
|
|
762
787
|
reused: false,
|
|
@@ -787,7 +812,7 @@ async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutM
|
|
|
787
812
|
};
|
|
788
813
|
}
|
|
789
814
|
if (cached.busy) {
|
|
790
|
-
const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs);
|
|
815
|
+
const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
|
|
791
816
|
return {
|
|
792
817
|
socket,
|
|
793
818
|
reused: false,
|
|
@@ -801,7 +826,7 @@ async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutM
|
|
|
801
826
|
websocketSessionCache.delete(sessionId);
|
|
802
827
|
}
|
|
803
828
|
}
|
|
804
|
-
const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs);
|
|
829
|
+
const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
|
|
805
830
|
const entry = { socket, busy: true };
|
|
806
831
|
websocketSessionCache.set(sessionId, entry);
|
|
807
832
|
return {
|
|
@@ -1029,6 +1054,30 @@ function buildCachedWebSocketRequestBody(entry, body) {
|
|
|
1029
1054
|
input: delta,
|
|
1030
1055
|
};
|
|
1031
1056
|
}
|
|
1057
|
+
function recordWebSocketRequestStats(stats, requestBody, reused, useCachedContext) {
|
|
1058
|
+
if (!stats)
|
|
1059
|
+
return;
|
|
1060
|
+
stats.requests++;
|
|
1061
|
+
if (reused)
|
|
1062
|
+
stats.connectionsReused++;
|
|
1063
|
+
else
|
|
1064
|
+
stats.connectionsCreated++;
|
|
1065
|
+
if (useCachedContext)
|
|
1066
|
+
stats.cachedContextRequests++;
|
|
1067
|
+
if (requestBody.store === true)
|
|
1068
|
+
stats.storeTrueRequests++;
|
|
1069
|
+
stats.lastInputItems = requestBody.input?.length ?? 0;
|
|
1070
|
+
if (requestBody.previous_response_id) {
|
|
1071
|
+
stats.deltaRequests++;
|
|
1072
|
+
stats.lastDeltaInputItems = requestBody.input?.length ?? 0;
|
|
1073
|
+
stats.lastPreviousResponseId = requestBody.previous_response_id;
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
stats.fullContextRequests++;
|
|
1077
|
+
stats.lastDeltaInputItems = undefined;
|
|
1078
|
+
stats.lastPreviousResponseId = undefined;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1032
1081
|
async function* startWebSocketOutputOnFirstEvent(events, output, stream, onStart) {
|
|
1033
1082
|
let started = false;
|
|
1034
1083
|
for await (const event of events) {
|
|
@@ -1041,43 +1090,41 @@ async function* startWebSocketOutputOnFirstEvent(events, output, stream, onStart
|
|
|
1041
1090
|
}
|
|
1042
1091
|
}
|
|
1043
1092
|
async function processWebSocketStream(url, body, headers, output, stream, model, onStart, idleTimeoutMs, websocketConnectTimeoutMs, options) {
|
|
1044
|
-
const { socket, entry, reused, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal, websocketConnectTimeoutMs);
|
|
1093
|
+
const { socket, entry, reused, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal, websocketConnectTimeoutMs, options?.env);
|
|
1045
1094
|
let keepConnection = true;
|
|
1046
1095
|
const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
|
|
1047
1096
|
// ChatGPT Codex Responses rejects `store: true` ("Store must be set to false").
|
|
1048
1097
|
// WebSocket continuation still works via connection-scoped previous_response_id state.
|
|
1049
1098
|
const fullBody = body;
|
|
1050
|
-
|
|
1099
|
+
let requestBody = useCachedContext && entry ? buildCachedWebSocketRequestBody(entry, fullBody) : fullBody;
|
|
1051
1100
|
const stats = options?.sessionId ? getOrCreateWebSocketDebugStats(options.sessionId) : undefined;
|
|
1052
|
-
|
|
1053
|
-
stats.requests++;
|
|
1054
|
-
if (reused)
|
|
1055
|
-
stats.connectionsReused++;
|
|
1056
|
-
else
|
|
1057
|
-
stats.connectionsCreated++;
|
|
1058
|
-
if (useCachedContext)
|
|
1059
|
-
stats.cachedContextRequests++;
|
|
1060
|
-
if (requestBody.store === true)
|
|
1061
|
-
stats.storeTrueRequests++;
|
|
1062
|
-
stats.lastInputItems = requestBody.input?.length ?? 0;
|
|
1063
|
-
if (requestBody.previous_response_id) {
|
|
1064
|
-
stats.deltaRequests++;
|
|
1065
|
-
stats.lastDeltaInputItems = requestBody.input?.length ?? 0;
|
|
1066
|
-
stats.lastPreviousResponseId = requestBody.previous_response_id;
|
|
1067
|
-
}
|
|
1068
|
-
else {
|
|
1069
|
-
stats.fullContextRequests++;
|
|
1070
|
-
stats.lastDeltaInputItems = undefined;
|
|
1071
|
-
stats.lastPreviousResponseId = undefined;
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1101
|
+
let recoveredStalePreviousResponse = false;
|
|
1074
1102
|
try {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1103
|
+
while (true) {
|
|
1104
|
+
recordWebSocketRequestStats(stats, requestBody, reused && !recoveredStalePreviousResponse, useCachedContext);
|
|
1105
|
+
try {
|
|
1106
|
+
socket.send(JSON.stringify({ type: "response.create", ...requestBody }));
|
|
1107
|
+
await processResponsesStream(startWebSocketOutputOnFirstEvent(mapCodexEvents(parseWebSocket(socket, options?.signal, idleTimeoutMs)), output, stream, onStart), output, stream, model, {
|
|
1108
|
+
serviceTier: options?.serviceTier,
|
|
1109
|
+
resolveServiceTier: resolveCodexServiceTier,
|
|
1110
|
+
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),
|
|
1111
|
+
});
|
|
1112
|
+
break;
|
|
1113
|
+
}
|
|
1114
|
+
catch (error) {
|
|
1115
|
+
if (!recoveredStalePreviousResponse &&
|
|
1116
|
+
entry &&
|
|
1117
|
+
requestBody.previous_response_id &&
|
|
1118
|
+
isCodexPreviousResponseStale(error) &&
|
|
1119
|
+
!options?.signal?.aborted) {
|
|
1120
|
+
entry.continuation = undefined;
|
|
1121
|
+
requestBody = fullBody;
|
|
1122
|
+
recoveredStalePreviousResponse = true;
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
throw error;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1081
1128
|
if (options?.signal?.aborted) {
|
|
1082
1129
|
keepConnection = false;
|
|
1083
1130
|
}
|
|
@@ -1136,15 +1183,13 @@ function extractAccountId(token) {
|
|
|
1136
1183
|
try {
|
|
1137
1184
|
const parts = token.split(".");
|
|
1138
1185
|
if (parts.length !== 3)
|
|
1139
|
-
|
|
1186
|
+
return undefined;
|
|
1140
1187
|
const payload = JSON.parse(atob(parts[1]));
|
|
1141
1188
|
const accountId = payload?.[JWT_CLAIM_PATH]?.chatgpt_account_id;
|
|
1142
|
-
|
|
1143
|
-
throw new Error("No account ID in token");
|
|
1144
|
-
return accountId;
|
|
1189
|
+
return typeof accountId === "string" && accountId.length > 0 ? accountId : undefined;
|
|
1145
1190
|
}
|
|
1146
1191
|
catch {
|
|
1147
|
-
|
|
1192
|
+
return undefined;
|
|
1148
1193
|
}
|
|
1149
1194
|
}
|
|
1150
1195
|
function createCodexRequestId() {
|
|
@@ -1159,7 +1204,12 @@ function buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token)
|
|
|
1159
1204
|
headers.set(key, value);
|
|
1160
1205
|
}
|
|
1161
1206
|
headers.set("Authorization", `Bearer ${token}`);
|
|
1162
|
-
|
|
1207
|
+
if (accountId) {
|
|
1208
|
+
headers.set("chatgpt-account-id", accountId);
|
|
1209
|
+
}
|
|
1210
|
+
else {
|
|
1211
|
+
headers.delete("chatgpt-account-id");
|
|
1212
|
+
}
|
|
1163
1213
|
headers.set("originator", "senpi");
|
|
1164
1214
|
const userAgent = _os ? `senpi (${_os.platform()} ${_os.release()}; ${_os.arch()})` : "senpi (browser)";
|
|
1165
1215
|
headers.set("User-Agent", userAgent);
|