@eddyskywalker/dsh-chatgpt-subscription 0.1.27 → 0.1.29
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 +26 -25
- package/LICENSE +21 -21
- package/cordis.patch.yml +6 -6
- package/lib/client.js +385 -453
- package/lib/client.js.map +1 -1
- package/lib/index.js +342 -281
- package/lib/types/client/CodexSubscriptionSection.d.ts +2 -2
- package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
- package/lib/types/client/api.d.ts +1 -2
- package/lib/types/client/api.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +53 -59
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/quota.d.ts.map +1 -1
- package/lib/types/client/styles.d.ts.map +1 -1
- package/lib/types/compat.d.ts +1 -0
- package/lib/types/compat.d.ts.map +1 -1
- package/lib/types/host/codex-fetch.d.ts +10 -0
- package/lib/types/host/codex-fetch.d.ts.map +1 -0
- package/lib/types/host/model-catalog.d.ts.map +1 -1
- package/lib/types/host/preferences.d.ts.map +1 -1
- package/lib/types/host/proxy-manager.d.ts +29 -0
- package/lib/types/host/proxy-manager.d.ts.map +1 -0
- package/lib/types/host/responses-client.d.ts +2 -0
- package/lib/types/host/responses-client.d.ts.map +1 -1
- package/lib/types/host/responses-mapper.d.ts +2 -1
- package/lib/types/host/responses-mapper.d.ts.map +1 -1
- package/lib/types/host/routes.d.ts +2 -1
- package/lib/types/host/routes.d.ts.map +1 -1
- package/lib/types/host/search-provider-switcher.d.ts +2 -1
- package/lib/types/host/search-provider-switcher.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/contracts.d.ts +13 -38
- package/lib/types/shared/contracts.d.ts.map +1 -1
- package/lib/types/shared/preferences.d.ts +2 -1
- package/lib/types/shared/preferences.d.ts.map +1 -1
- package/package.json +10 -11
- package/lib/types/client/SubagentSettingsSection.d.ts +0 -9
- package/lib/types/client/SubagentSettingsSection.d.ts.map +0 -1
- package/lib/types/host/subagent-context-adapter.d.ts +0 -24
- package/lib/types/host/subagent-context-adapter.d.ts.map +0 -1
- package/lib/types/host/subagent-model-preference.d.ts +0 -5
- package/lib/types/host/subagent-model-preference.d.ts.map +0 -1
- package/lib/types/host/subagent-policy.d.ts +0 -17
- package/lib/types/host/subagent-policy.d.ts.map +0 -1
package/lib/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { CallId, HarnessError, LlmAdapter, LlmError, ProviderRequestId, ReasoningEffortId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
2
|
+
import { WebError } from "@deepseek-ai/dsh-web";
|
|
2
3
|
import { createUserMessage } from "@deepseek-ai/dsh-llm/message";
|
|
3
4
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
4
5
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
5
|
-
import { WebError } from "@deepseek-ai/dsh-web";
|
|
6
6
|
import http from "node:http";
|
|
7
|
+
import { execSync, spawn } from "node:child_process";
|
|
8
|
+
import { ProxyAgent, fetch as fetch$1 } from "undici";
|
|
7
9
|
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
8
10
|
import z from "@deepseek-ai/schemastery";
|
|
9
|
-
import { spawn } from "node:child_process";
|
|
10
11
|
import { constants } from "node:fs";
|
|
11
12
|
import { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
|
|
12
13
|
import { homedir } from "node:os";
|
|
13
14
|
import { dirname, join } from "node:path";
|
|
14
|
-
import { delegationDepthOf } from "@deepseek-ai/dsh-subagent";
|
|
15
15
|
//#region src/compat.ts
|
|
16
16
|
/**
|
|
17
17
|
* Compatibility constants for the ChatGPT-backed Codex flow. The backend and
|
|
@@ -39,6 +39,7 @@ const CODEX_ORIGINATOR = "opencode";
|
|
|
39
39
|
const CODEX_IMAGE_TOOL_NAME = "codex_image_generate";
|
|
40
40
|
const CODEX_IMAGE_MODEL = "gpt-image-2";
|
|
41
41
|
const CODEX_SEARCH_PROVIDER_ID = "codex-subscription";
|
|
42
|
+
const CODEX_FETCH_PROVIDER_ID = "codex-subscription";
|
|
42
43
|
const QUOTA_MIN_UPSTREAM_INTERVAL_MS = 15e3;
|
|
43
44
|
const OAUTH_AUTHORIZE_URL = `${CHATGPT_OAUTH_ISSUER}/oauth/authorize`;
|
|
44
45
|
const OAUTH_TOKEN_URL = `${CHATGPT_OAUTH_ISSUER}/oauth/token`;
|
|
@@ -162,7 +163,8 @@ function listCodexModels(preferences) {
|
|
|
162
163
|
}
|
|
163
164
|
function resolveCodexModel(model, preferences) {
|
|
164
165
|
const entry = resolveCodexCatalogEntry(model);
|
|
165
|
-
const
|
|
166
|
+
const status = preferences?.status();
|
|
167
|
+
const configuredContextWindow = isConfigurableContextModelId(model) ? status?.contextWindowOverrides[model] : void 0;
|
|
166
168
|
const efforts = reasoningEffortsForModel(model);
|
|
167
169
|
return {
|
|
168
170
|
provider: PROVIDER_ID,
|
|
@@ -230,6 +232,79 @@ var CodexChatGptAdapter = class extends LlmAdapter {
|
|
|
230
232
|
}
|
|
231
233
|
};
|
|
232
234
|
//#endregion
|
|
235
|
+
//#region src/host/codex-fetch.ts
|
|
236
|
+
const DEFAULT_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
237
|
+
const DEFAULT_MAX_BODY_CHARS = 1e5;
|
|
238
|
+
function createCodexFetchProvider(options = {}) {
|
|
239
|
+
const fetchFn = options.fetchFn ?? fetch;
|
|
240
|
+
const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
|
|
241
|
+
const maxBodyChars = options.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;
|
|
242
|
+
return {
|
|
243
|
+
id: CODEX_FETCH_PROVIDER_ID,
|
|
244
|
+
available: () => true,
|
|
245
|
+
async fetch(request, signal) {
|
|
246
|
+
const targetUrl = request.url.trim();
|
|
247
|
+
let parsedUrl;
|
|
248
|
+
try {
|
|
249
|
+
parsedUrl = new URL(targetUrl);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
throw new WebError(`invalid URL: ${targetUrl}`, "WEB_INVALID_URL", { cause: error });
|
|
252
|
+
}
|
|
253
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new WebError(`unsupported URL scheme "${parsedUrl.protocol}" (only http and https are allowed)`, "WEB_INVALID_URL");
|
|
254
|
+
let response;
|
|
255
|
+
try {
|
|
256
|
+
response = await fetchFn(targetUrl, {
|
|
257
|
+
method: "GET",
|
|
258
|
+
headers: {
|
|
259
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
260
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,text/plain;q=0.8,*/*;q=0.5"
|
|
261
|
+
},
|
|
262
|
+
redirect: "follow",
|
|
263
|
+
signal
|
|
264
|
+
});
|
|
265
|
+
} catch (error) {
|
|
266
|
+
if (signal?.aborted) throw new WebError("web fetch aborted", "WEB_ABORTED", { cause: error });
|
|
267
|
+
throw new WebError(`web fetch failed: ${error instanceof Error ? error.message : String(error)}`, "WEB_PROVIDER_ERROR", { cause: error });
|
|
268
|
+
}
|
|
269
|
+
const contentType = response.headers.get("content-type") || "";
|
|
270
|
+
const mime = contentType.replace(/;.*$/s, "").trim().toLowerCase();
|
|
271
|
+
const kind = mime === "text/html" || mime === "application/xhtml+xml" ? "html" : "text";
|
|
272
|
+
let charset = "utf-8";
|
|
273
|
+
const match = /;\s*charset\s*=\s*"?([^";]+)"?/i.exec(contentType);
|
|
274
|
+
if (match?.[1]) charset = match[1].trim().toLowerCase();
|
|
275
|
+
let decoder;
|
|
276
|
+
try {
|
|
277
|
+
decoder = new TextDecoder(charset);
|
|
278
|
+
} catch {
|
|
279
|
+
decoder = new TextDecoder("utf-8");
|
|
280
|
+
}
|
|
281
|
+
let rawBytes;
|
|
282
|
+
let truncatedByBytes = false;
|
|
283
|
+
try {
|
|
284
|
+
const buffer = await response.arrayBuffer();
|
|
285
|
+
if (buffer.byteLength > maxResponseBytes) {
|
|
286
|
+
rawBytes = new Uint8Array(buffer.slice(0, maxResponseBytes));
|
|
287
|
+
truncatedByBytes = true;
|
|
288
|
+
} else rawBytes = new Uint8Array(buffer);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
throw new WebError(`failed to read response body: ${error instanceof Error ? error.message : String(error)}`, "WEB_PROVIDER_ERROR", { cause: error });
|
|
291
|
+
}
|
|
292
|
+
const decodedText = decoder.decode(rawBytes);
|
|
293
|
+
const truncatedByChars = decodedText.length > maxBodyChars;
|
|
294
|
+
const finalContent = truncatedByChars ? decodedText.slice(0, maxBodyChars) : decodedText;
|
|
295
|
+
return {
|
|
296
|
+
url: response.url || targetUrl,
|
|
297
|
+
statusCode: response.status,
|
|
298
|
+
body: {
|
|
299
|
+
kind,
|
|
300
|
+
content: finalContent
|
|
301
|
+
},
|
|
302
|
+
truncated: truncatedByBytes || truncatedByChars
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
233
308
|
//#region src/host/wire-auth.ts
|
|
234
309
|
function codexHeaders(credentials, sessionId) {
|
|
235
310
|
const dshAgent = attributionHeaders()["user-agent"] ?? "dsh/unknown";
|
|
@@ -502,10 +577,7 @@ function sendSearch(fetchFn, credentials, query, model, id, signal) {
|
|
|
502
577
|
id,
|
|
503
578
|
model,
|
|
504
579
|
input: query,
|
|
505
|
-
commands: {
|
|
506
|
-
search_query: [{ q: query }],
|
|
507
|
-
response_length: "short"
|
|
508
|
-
},
|
|
580
|
+
commands: { search_query: [{ q: query }] },
|
|
509
581
|
settings: {
|
|
510
582
|
allowed_callers: ["direct"],
|
|
511
583
|
external_web_access: true
|
|
@@ -1075,24 +1147,171 @@ async function oauthErrorIdentifier(response) {
|
|
|
1075
1147
|
return null;
|
|
1076
1148
|
}
|
|
1077
1149
|
//#endregion
|
|
1150
|
+
//#region src/host/proxy-manager.ts
|
|
1151
|
+
const SYSTEM_PROXY_CACHE_TTL_MS = 5e3;
|
|
1152
|
+
function normalizeProxyUrl(rawUrl) {
|
|
1153
|
+
const trimmed = rawUrl.trim();
|
|
1154
|
+
if (!trimmed) return "";
|
|
1155
|
+
if (/^https?:\/\//i.test(trimmed) || /^socks5?:\/\//i.test(trimmed)) return trimmed;
|
|
1156
|
+
return `http://${trimmed}`;
|
|
1157
|
+
}
|
|
1158
|
+
function parseWindowsProxyRegistry(stdout) {
|
|
1159
|
+
const enableMatch = stdout.match(/ProxyEnable\s+REG_DWORD\s+(0x[0-9a-fA-F]+|\d+)/i);
|
|
1160
|
+
if (!enableMatch) return null;
|
|
1161
|
+
if ((enableMatch[1].startsWith("0x") ? parseInt(enableMatch[1], 16) : parseInt(enableMatch[1], 10)) !== 1) return null;
|
|
1162
|
+
const serverMatch = stdout.match(/ProxyServer\s+REG_SZ\s+([^\r\n]+)/i);
|
|
1163
|
+
if (!serverMatch) return null;
|
|
1164
|
+
const rawServer = serverMatch[1].trim();
|
|
1165
|
+
if (!rawServer) return null;
|
|
1166
|
+
if (rawServer.includes("=")) {
|
|
1167
|
+
const pairs = rawServer.split(";");
|
|
1168
|
+
const map = {};
|
|
1169
|
+
for (const pair of pairs) {
|
|
1170
|
+
const [proto, addr] = pair.split("=").map((s) => s.trim());
|
|
1171
|
+
if (proto && addr) map[proto.toLowerCase()] = addr;
|
|
1172
|
+
}
|
|
1173
|
+
const target = map.https || map.http || map.socks;
|
|
1174
|
+
if (target) {
|
|
1175
|
+
if (map.socks && !map.https && !map.http) return normalizeProxyUrl(target.startsWith("socks") ? target : `socks5://${target}`);
|
|
1176
|
+
return normalizeProxyUrl(target);
|
|
1177
|
+
}
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
return normalizeProxyUrl(rawServer);
|
|
1181
|
+
}
|
|
1182
|
+
function parseMacOsScutilProxy(stdout) {
|
|
1183
|
+
const httpsEnable = /HTTPSEnable\s*:\s*1/i.test(stdout);
|
|
1184
|
+
const httpEnable = /HTTPEnable\s*:\s*1/i.test(stdout);
|
|
1185
|
+
const socksEnable = /SOCKSEnable\s*:\s*1/i.test(stdout);
|
|
1186
|
+
if (httpsEnable) {
|
|
1187
|
+
const host = stdout.match(/HTTPSProxy\s*:\s*([^\s\r\n]+)/i)?.[1];
|
|
1188
|
+
const port = stdout.match(/HTTPSPort\s*:\s*(\d+)/i)?.[1];
|
|
1189
|
+
if (host && port) return normalizeProxyUrl(`${host}:${port}`);
|
|
1190
|
+
}
|
|
1191
|
+
if (httpEnable) {
|
|
1192
|
+
const host = stdout.match(/HTTPProxy\s*:\s*([^\s\r\n]+)/i)?.[1];
|
|
1193
|
+
const port = stdout.match(/HTTPPort\s*:\s*(\d+)/i)?.[1];
|
|
1194
|
+
if (host && port) return normalizeProxyUrl(`${host}:${port}`);
|
|
1195
|
+
}
|
|
1196
|
+
if (socksEnable) {
|
|
1197
|
+
const host = stdout.match(/SOCKSProxy\s*:\s*([^\s\r\n]+)/i)?.[1];
|
|
1198
|
+
const port = stdout.match(/SOCKSPort\s*:\s*(\d+)/i)?.[1];
|
|
1199
|
+
if (host && port) return normalizeProxyUrl(`socks5://${host}:${port}`);
|
|
1200
|
+
}
|
|
1201
|
+
return null;
|
|
1202
|
+
}
|
|
1203
|
+
function parseEnvProxy(env = process.env) {
|
|
1204
|
+
const proxy = env.HTTPS_PROXY || env.https_proxy || env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
|
|
1205
|
+
if (!proxy || !proxy.trim()) return null;
|
|
1206
|
+
return normalizeProxyUrl(proxy);
|
|
1207
|
+
}
|
|
1208
|
+
function detectSystemProxy(platform = process.platform, env = process.env) {
|
|
1209
|
+
try {
|
|
1210
|
+
if (platform === "win32") {
|
|
1211
|
+
const detected = parseWindowsProxyRegistry(execSync("reg query \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\"", {
|
|
1212
|
+
timeout: 1500,
|
|
1213
|
+
encoding: "utf-8",
|
|
1214
|
+
stdio: [
|
|
1215
|
+
"ignore",
|
|
1216
|
+
"pipe",
|
|
1217
|
+
"ignore"
|
|
1218
|
+
]
|
|
1219
|
+
}));
|
|
1220
|
+
if (detected) return detected;
|
|
1221
|
+
} else if (platform === "darwin") {
|
|
1222
|
+
const detected = parseMacOsScutilProxy(execSync("scutil --proxy", {
|
|
1223
|
+
timeout: 1500,
|
|
1224
|
+
encoding: "utf-8",
|
|
1225
|
+
stdio: [
|
|
1226
|
+
"ignore",
|
|
1227
|
+
"pipe",
|
|
1228
|
+
"ignore"
|
|
1229
|
+
]
|
|
1230
|
+
}));
|
|
1231
|
+
if (detected) return detected;
|
|
1232
|
+
}
|
|
1233
|
+
} catch {}
|
|
1234
|
+
return parseEnvProxy(env);
|
|
1235
|
+
}
|
|
1236
|
+
var ProxyManager = class {
|
|
1237
|
+
getPreferences;
|
|
1238
|
+
baseFetch;
|
|
1239
|
+
systemProxyDetector;
|
|
1240
|
+
logger;
|
|
1241
|
+
cachedSystemProxy = null;
|
|
1242
|
+
lastSystemProxyCheck = 0;
|
|
1243
|
+
agents = /* @__PURE__ */ new Map();
|
|
1244
|
+
constructor(options) {
|
|
1245
|
+
this.getPreferences = options.getPreferences;
|
|
1246
|
+
this.baseFetch = options.baseFetch ?? fetch;
|
|
1247
|
+
this.systemProxyDetector = options.systemProxyDetector ?? (() => detectSystemProxy());
|
|
1248
|
+
this.logger = options.logger;
|
|
1249
|
+
}
|
|
1250
|
+
getSystemProxy(force = false) {
|
|
1251
|
+
const now = Date.now();
|
|
1252
|
+
if (!force && now - this.lastSystemProxyCheck < SYSTEM_PROXY_CACHE_TTL_MS) return this.cachedSystemProxy;
|
|
1253
|
+
this.lastSystemProxyCheck = now;
|
|
1254
|
+
try {
|
|
1255
|
+
this.cachedSystemProxy = this.systemProxyDetector();
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
this.cachedSystemProxy = null;
|
|
1258
|
+
this.logger?.warn?.(`[dsh-chatgpt-subscription] Failed to detect system proxy: ${error instanceof Error ? error.message : String(error)}`);
|
|
1259
|
+
}
|
|
1260
|
+
return this.cachedSystemProxy;
|
|
1261
|
+
}
|
|
1262
|
+
resolveActiveProxyUrl() {
|
|
1263
|
+
const prefs = this.getPreferences();
|
|
1264
|
+
const mode = prefs.proxyMode ?? "auto";
|
|
1265
|
+
if (mode === "direct") return null;
|
|
1266
|
+
if (mode === "custom") return prefs.customProxyUrl ? normalizeProxyUrl(prefs.customProxyUrl) : null;
|
|
1267
|
+
return this.getSystemProxy();
|
|
1268
|
+
}
|
|
1269
|
+
getOrCreateAgent(proxyUrl) {
|
|
1270
|
+
let agent = this.agents.get(proxyUrl);
|
|
1271
|
+
if (!agent) {
|
|
1272
|
+
agent = new ProxyAgent(proxyUrl);
|
|
1273
|
+
this.agents.set(proxyUrl, agent);
|
|
1274
|
+
}
|
|
1275
|
+
return agent;
|
|
1276
|
+
}
|
|
1277
|
+
createFetch() {
|
|
1278
|
+
return async (input, init) => {
|
|
1279
|
+
const activeProxy = this.resolveActiveProxyUrl();
|
|
1280
|
+
if (!activeProxy) return this.baseFetch(input, init);
|
|
1281
|
+
try {
|
|
1282
|
+
const agent = this.getOrCreateAgent(activeProxy);
|
|
1283
|
+
return await fetch$1(input, {
|
|
1284
|
+
...init,
|
|
1285
|
+
dispatcher: agent
|
|
1286
|
+
});
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
throw error;
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
dispose() {
|
|
1293
|
+
for (const agent of this.agents.values()) agent.destroy().catch(() => void 0);
|
|
1294
|
+
this.agents.clear();
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
//#endregion
|
|
1078
1298
|
//#region src/shared/preferences.ts
|
|
1079
1299
|
const PREFERENCES_NAMESPACE = "dsh-chatgpt-subscription";
|
|
1080
1300
|
const DEFAULT_PREFERENCES = {
|
|
1081
1301
|
quickQuotaVisible: false,
|
|
1302
|
+
fastMode: false,
|
|
1082
1303
|
outputVerbosity: null,
|
|
1083
1304
|
visibleModelIds: [...DEFAULT_VISIBLE_CODEX_MODEL_IDS],
|
|
1084
1305
|
searchProvider: "dsh",
|
|
1085
|
-
subagentProvider: "codex-chatgpt",
|
|
1086
|
-
subagentModel: "gpt-5.6-sol",
|
|
1087
|
-
subagentReasoningEffort: "medium",
|
|
1088
|
-
subagentContextWindow: 272e3,
|
|
1089
|
-
subagentMaxDepth: 3,
|
|
1090
|
-
subagentMaxAgents: 8,
|
|
1091
1306
|
contextWindowOverrides: {
|
|
1092
1307
|
"gpt-5.6-sol": 272e3,
|
|
1093
1308
|
"gpt-5.6-terra": 272e3,
|
|
1094
1309
|
"gpt-5.6-luna": 272e3
|
|
1095
|
-
}
|
|
1310
|
+
},
|
|
1311
|
+
subagentContextWindow: null,
|
|
1312
|
+
subagentMaxDepth: null,
|
|
1313
|
+
proxyMode: "auto",
|
|
1314
|
+
customProxyUrl: null
|
|
1096
1315
|
};
|
|
1097
1316
|
const SEARCH_PROVIDER_CODEX = "codex";
|
|
1098
1317
|
function isSearchProviderPreference(value) {
|
|
@@ -1101,11 +1320,15 @@ function isSearchProviderPreference(value) {
|
|
|
1101
1320
|
function isCodexOutputVerbosity(value) {
|
|
1102
1321
|
return value === "low" || value === "medium" || value === "high";
|
|
1103
1322
|
}
|
|
1323
|
+
function isProxyMode(value) {
|
|
1324
|
+
return value === "auto" || value === "custom" || value === "direct";
|
|
1325
|
+
}
|
|
1104
1326
|
//#endregion
|
|
1105
1327
|
//#region src/host/preferences.ts
|
|
1106
1328
|
function registerPreferenceStore(settings) {
|
|
1107
1329
|
return new SettingsPreferenceStore(settings.register(settingsNamespace(PREFERENCES_NAMESPACE), z.object({
|
|
1108
1330
|
quickQuotaVisible: z.boolean().default(DEFAULT_PREFERENCES.quickQuotaVisible),
|
|
1331
|
+
fastMode: z.boolean().default(DEFAULT_PREFERENCES.fastMode),
|
|
1109
1332
|
outputVerbosity: z.union([
|
|
1110
1333
|
z.const("low"),
|
|
1111
1334
|
z.const("medium"),
|
|
@@ -1114,17 +1337,19 @@ function registerPreferenceStore(settings) {
|
|
|
1114
1337
|
]).default(DEFAULT_PREFERENCES.outputVerbosity),
|
|
1115
1338
|
visibleModelIds: z.array(z.string()).default(DEFAULT_PREFERENCES.visibleModelIds),
|
|
1116
1339
|
searchProvider: z.union([z.const("dsh"), z.const(SEARCH_PROVIDER_CODEX)]).default(DEFAULT_PREFERENCES.searchProvider),
|
|
1117
|
-
subagentProvider: z.string().default(DEFAULT_PREFERENCES.subagentProvider),
|
|
1118
|
-
subagentModel: z.string().default(DEFAULT_PREFERENCES.subagentModel),
|
|
1119
|
-
subagentReasoningEffort: z.union([z.string(), z.const(null)]).default(DEFAULT_PREFERENCES.subagentReasoningEffort),
|
|
1120
|
-
subagentContextWindow: z.number().step(1).min(1).default(DEFAULT_PREFERENCES.subagentContextWindow),
|
|
1121
|
-
subagentMaxDepth: z.number().step(1).min(0).max(3).default(DEFAULT_PREFERENCES.subagentMaxDepth),
|
|
1122
|
-
subagentMaxAgents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_PREFERENCES.subagentMaxAgents),
|
|
1123
1340
|
contextWindowOverrides: z.object({
|
|
1124
1341
|
"gpt-5.6-sol": z.number().step(1).min(1).max(GPT_56_MAX_CONTEXT_WINDOW).default(DEFAULT_PREFERENCES.contextWindowOverrides["gpt-5.6-sol"]),
|
|
1125
1342
|
"gpt-5.6-terra": z.number().step(1).min(1).max(GPT_56_MAX_CONTEXT_WINDOW).default(DEFAULT_PREFERENCES.contextWindowOverrides["gpt-5.6-terra"]),
|
|
1126
1343
|
"gpt-5.6-luna": z.number().step(1).min(1).max(GPT_56_MAX_CONTEXT_WINDOW).default(DEFAULT_PREFERENCES.contextWindowOverrides["gpt-5.6-luna"])
|
|
1127
|
-
}).default(DEFAULT_PREFERENCES.contextWindowOverrides)
|
|
1344
|
+
}).default(DEFAULT_PREFERENCES.contextWindowOverrides),
|
|
1345
|
+
subagentContextWindow: z.union([z.number().step(1).min(1), z.const(null)]).default(DEFAULT_PREFERENCES.subagentContextWindow),
|
|
1346
|
+
subagentMaxDepth: z.union([z.number().step(1).min(0).max(3), z.const(null)]).default(DEFAULT_PREFERENCES.subagentMaxDepth),
|
|
1347
|
+
proxyMode: z.union([
|
|
1348
|
+
z.const("auto"),
|
|
1349
|
+
z.const("custom"),
|
|
1350
|
+
z.const("direct")
|
|
1351
|
+
]).default(DEFAULT_PREFERENCES.proxyMode),
|
|
1352
|
+
customProxyUrl: z.union([z.string(), z.const(null)]).default(DEFAULT_PREFERENCES.customProxyUrl)
|
|
1128
1353
|
})));
|
|
1129
1354
|
}
|
|
1130
1355
|
var SettingsPreferenceStore = class {
|
|
@@ -1138,6 +1363,7 @@ var SettingsPreferenceStore = class {
|
|
|
1138
1363
|
async update(patch) {
|
|
1139
1364
|
const normalized = {};
|
|
1140
1365
|
if (patch.quickQuotaVisible !== void 0) normalized.quickQuotaVisible = patch.quickQuotaVisible;
|
|
1366
|
+
if (patch.fastMode !== void 0) normalized.fastMode = patch.fastMode;
|
|
1141
1367
|
if (patch.outputVerbosity !== void 0) {
|
|
1142
1368
|
if (patch.outputVerbosity !== null && !isCodexOutputVerbosity(patch.outputVerbosity)) throw new PreferenceError("Unsupported output verbosity preference.");
|
|
1143
1369
|
normalized.outputVerbosity = patch.outputVerbosity;
|
|
@@ -1150,16 +1376,21 @@ var SettingsPreferenceStore = class {
|
|
|
1150
1376
|
if (!isSearchProviderPreference(patch.searchProvider)) throw new PreferenceError("Unsupported search provider preference.");
|
|
1151
1377
|
normalized.searchProvider = patch.searchProvider;
|
|
1152
1378
|
}
|
|
1153
|
-
if (patch.subagentProvider !== void 0) normalized.subagentProvider = patch.subagentProvider;
|
|
1154
|
-
if (patch.subagentModel !== void 0) normalized.subagentModel = patch.subagentModel;
|
|
1155
|
-
if (patch.subagentReasoningEffort !== void 0) normalized.subagentReasoningEffort = patch.subagentReasoningEffort;
|
|
1156
|
-
if (patch.subagentContextWindow !== void 0) normalized.subagentContextWindow = patch.subagentContextWindow;
|
|
1157
|
-
if (patch.subagentMaxDepth !== void 0) normalized.subagentMaxDepth = patch.subagentMaxDepth;
|
|
1158
|
-
if (patch.subagentMaxAgents !== void 0) normalized.subagentMaxAgents = patch.subagentMaxAgents;
|
|
1159
1379
|
if (patch.contextWindowOverrides !== void 0) normalized.contextWindowOverrides = {
|
|
1160
1380
|
...this.scope.get().contextWindowOverrides,
|
|
1161
1381
|
...patch.contextWindowOverrides
|
|
1162
1382
|
};
|
|
1383
|
+
if (patch.subagentContextWindow !== void 0) normalized.subagentContextWindow = patch.subagentContextWindow;
|
|
1384
|
+
if (patch.subagentMaxDepth !== void 0) normalized.subagentMaxDepth = patch.subagentMaxDepth;
|
|
1385
|
+
if (patch.proxyMode !== void 0) {
|
|
1386
|
+
if (!isProxyMode(patch.proxyMode)) throw new PreferenceError("Unsupported proxy mode preference.");
|
|
1387
|
+
normalized.proxyMode = patch.proxyMode;
|
|
1388
|
+
}
|
|
1389
|
+
if (patch.customProxyUrl !== void 0) if (patch.customProxyUrl !== null) {
|
|
1390
|
+
const trimmed = patch.customProxyUrl.trim();
|
|
1391
|
+
if (trimmed.length > 0 && !/^https?:\/\//i.test(trimmed) && !/^socks5?:\/\//i.test(trimmed)) normalized.customProxyUrl = `http://${trimmed}`;
|
|
1392
|
+
else normalized.customProxyUrl = trimmed.length === 0 ? null : trimmed;
|
|
1393
|
+
} else normalized.customProxyUrl = null;
|
|
1163
1394
|
await this.scope.update(normalized);
|
|
1164
1395
|
return this.status();
|
|
1165
1396
|
}
|
|
@@ -1213,13 +1444,14 @@ function normalizeReplayCallIds(items, normalizeCallId) {
|
|
|
1213
1444
|
call_id: normalizeCallId(item.call_id)
|
|
1214
1445
|
} : item);
|
|
1215
1446
|
}
|
|
1216
|
-
async function buildResponsesPayload(options, attachments, localRawImages = {}, outputVerbosity = null) {
|
|
1447
|
+
async function buildResponsesPayload(options, attachments, localRawImages = {}, outputVerbosity = null, fastMode = false) {
|
|
1217
1448
|
const sandboxRetryTools = recentSandboxRetryToolNames(options.messages);
|
|
1218
1449
|
const resolveLocalRawImages = supportsImageInput(options);
|
|
1219
1450
|
const normalizeCallId = createCallIdNormalizer();
|
|
1220
1451
|
const instructionParts = [
|
|
1221
1452
|
options.system?.trim(),
|
|
1222
1453
|
...options.messages.filter((message) => message.role === "system").map((message) => blocksToText(message.content).trim()),
|
|
1454
|
+
progressExplanationInstruction(options.tools),
|
|
1223
1455
|
sandboxToolInstruction(options.tools, sandboxRetryTools),
|
|
1224
1456
|
commandToolInstruction(options.tools),
|
|
1225
1457
|
runCodeInstruction(options.tools)
|
|
@@ -1295,12 +1527,17 @@ async function buildResponsesPayload(options, attachments, localRawImages = {},
|
|
|
1295
1527
|
payload.parallel_tool_calls = true;
|
|
1296
1528
|
}
|
|
1297
1529
|
if (outputVerbosity !== null) payload.text = { verbosity: outputVerbosity };
|
|
1530
|
+
if (fastMode) payload.service_tier = "priority";
|
|
1298
1531
|
if (options.reasoningEffort !== void 0) payload.reasoning = codexModelSupportsReasoningSummary(options.model) ? {
|
|
1299
1532
|
effort: options.reasoningEffort,
|
|
1300
1533
|
summary: "auto"
|
|
1301
1534
|
} : { effort: options.reasoningEffort };
|
|
1302
1535
|
return payload;
|
|
1303
1536
|
}
|
|
1537
|
+
function progressExplanationInstruction(tools) {
|
|
1538
|
+
if (!tools?.length) return void 0;
|
|
1539
|
+
return "Progress and tool execution rule: when executing multi-step tasks or invoking tools, output 1-2 concise sentences of progress, intent, or intermediate findings before each tool call. Keep progress text brief, professional, and factual. Only present the comprehensive final answer and summary in the final turn after all tool operations are completed.";
|
|
1540
|
+
}
|
|
1304
1541
|
function runCodeInstruction(tools) {
|
|
1305
1542
|
if (!tools?.some((tool) => tool.name === "run_code")) return void 0;
|
|
1306
1543
|
return "run_code compatibility rule: its code is parsed as strict JavaScript/TypeScript before execution. Shell commands are nested string data: JavaScript template literals may consume ${...}, backticks, backslashes, and escape sequences before PowerShell, Bash, or POSIX sh sees them. On Windows, avoid embedding PowerShell containing $, ${...}, backslashes, or here-strings in template literals; String.raw does not disable ${...} interpolation. On Linux, prefer ordinary quoted strings or write a script file before invoking bash/sh, especially for commands containing backticks or ${...}. Prefer arrays of ordinary quoted strings joined with \"\\n\", escaping backslashes, or use a file-write tool for large scripts.";
|
|
@@ -1595,6 +1832,7 @@ var ResponsesClient = class {
|
|
|
1595
1832
|
fetchFn;
|
|
1596
1833
|
onGenerationFinished;
|
|
1597
1834
|
outputVerbosity;
|
|
1835
|
+
fastMode;
|
|
1598
1836
|
constructor(oauth, attachments, options = {}) {
|
|
1599
1837
|
this.oauth = oauth;
|
|
1600
1838
|
this.attachments = attachments;
|
|
@@ -1602,10 +1840,11 @@ var ResponsesClient = class {
|
|
|
1602
1840
|
this.localRawImages = options.localRawImages ?? {};
|
|
1603
1841
|
this.onGenerationFinished = options.onGenerationFinished ?? (() => void 0);
|
|
1604
1842
|
this.outputVerbosity = options.outputVerbosity ?? (() => null);
|
|
1843
|
+
this.fastMode = options.fastMode ?? (() => false);
|
|
1605
1844
|
}
|
|
1606
1845
|
localRawImages;
|
|
1607
1846
|
async *stream(options) {
|
|
1608
|
-
const payload = await buildResponsesPayload(options, this.attachments, this.localRawImages, this.outputVerbosity());
|
|
1847
|
+
const payload = await buildResponsesPayload(options, this.attachments, this.localRawImages, this.outputVerbosity(), this.fastMode());
|
|
1609
1848
|
const hiddenSandboxControls = hiddenSandboxControlToolNames(options);
|
|
1610
1849
|
const sessionId = stableSessionId(options.sessionId);
|
|
1611
1850
|
try {
|
|
@@ -2373,61 +2612,11 @@ function identityKey(credentials) {
|
|
|
2373
2612
|
return credentials.accountId ?? credentials.email ?? credentials.planType ?? "signed-in";
|
|
2374
2613
|
}
|
|
2375
2614
|
//#endregion
|
|
2376
|
-
//#region src/host/subagent-context-adapter.ts
|
|
2377
|
-
/**
|
|
2378
|
-
* A private provider route that delegates streaming to a selected registered
|
|
2379
|
-
* provider while exposing a user-selected effective context window to DSH.
|
|
2380
|
-
*/
|
|
2381
|
-
var SubagentContextAdapter = class SubagentContextAdapter extends LlmAdapter {
|
|
2382
|
-
llm;
|
|
2383
|
-
selection;
|
|
2384
|
-
static provider = "dsh-subagent-model-override";
|
|
2385
|
-
constructor(llm, selection) {
|
|
2386
|
-
super();
|
|
2387
|
-
this.llm = llm;
|
|
2388
|
-
this.selection = selection;
|
|
2389
|
-
}
|
|
2390
|
-
providerInfo() {
|
|
2391
|
-
return {
|
|
2392
|
-
id: SubagentContextAdapter.provider,
|
|
2393
|
-
name: "Subagent model override"
|
|
2394
|
-
};
|
|
2395
|
-
}
|
|
2396
|
-
async listModels() {
|
|
2397
|
-
return [];
|
|
2398
|
-
}
|
|
2399
|
-
async resolveModel(_provider, _model, signal) {
|
|
2400
|
-
const selected = this.selection();
|
|
2401
|
-
return {
|
|
2402
|
-
...await this.llm.resolveModelInfo(selected.provider, selected.model, signal),
|
|
2403
|
-
provider: SubagentContextAdapter.provider,
|
|
2404
|
-
id: selected.model,
|
|
2405
|
-
context: { contextWindow: selected.contextWindow }
|
|
2406
|
-
};
|
|
2407
|
-
}
|
|
2408
|
-
stream(options) {
|
|
2409
|
-
const selected = this.selection();
|
|
2410
|
-
const { preparedCall: _preparedCall, ...forwarded } = options;
|
|
2411
|
-
return this.llm.stream({
|
|
2412
|
-
...forwarded,
|
|
2413
|
-
provider: selected.provider,
|
|
2414
|
-
model: selected.model
|
|
2415
|
-
});
|
|
2416
|
-
}
|
|
2417
|
-
};
|
|
2418
|
-
//#endregion
|
|
2419
2615
|
//#region src/host/routes.ts
|
|
2420
2616
|
const MAX_BODY_BYTES = 64 * 1024;
|
|
2421
|
-
function registerRoutes(ctx, oauth, usage, preferences) {
|
|
2617
|
+
function registerRoutes(ctx, oauth, usage, preferences, proxyManager) {
|
|
2422
2618
|
const handler = async (request, response) => {
|
|
2423
2619
|
const url = new URL(request.url ?? "/", "http://dsh.local");
|
|
2424
|
-
if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/models`) {
|
|
2425
|
-
json(response, {
|
|
2426
|
-
ok: true,
|
|
2427
|
-
value: await subagentModelCatalog(ctx)
|
|
2428
|
-
});
|
|
2429
|
-
return;
|
|
2430
|
-
}
|
|
2431
2620
|
if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/status`) {
|
|
2432
2621
|
const oauthStatus = await oauth.status();
|
|
2433
2622
|
json(response, {
|
|
@@ -2435,7 +2624,9 @@ function registerRoutes(ctx, oauth, usage, preferences) {
|
|
|
2435
2624
|
value: {
|
|
2436
2625
|
...oauthStatus,
|
|
2437
2626
|
quota: await usage.status(oauthStatus.authenticated),
|
|
2438
|
-
preferences: preferences.status()
|
|
2627
|
+
preferences: preferences.status(),
|
|
2628
|
+
detectedProxy: proxyManager?.getSystemProxy() ?? null,
|
|
2629
|
+
activeProxy: proxyManager?.resolveActiveProxyUrl() ?? null
|
|
2439
2630
|
}
|
|
2440
2631
|
});
|
|
2441
2632
|
return;
|
|
@@ -2656,6 +2847,10 @@ function readPreferencesUpdate(value, current) {
|
|
|
2656
2847
|
if (typeof value.quickQuotaVisible !== "boolean") throw new PreferenceError("quickQuotaVisible must be a boolean.");
|
|
2657
2848
|
patch.quickQuotaVisible = value.quickQuotaVisible;
|
|
2658
2849
|
}
|
|
2850
|
+
if ("fastMode" in value) {
|
|
2851
|
+
if (typeof value.fastMode !== "boolean") throw new PreferenceError("fastMode must be a boolean.");
|
|
2852
|
+
patch.fastMode = value.fastMode;
|
|
2853
|
+
}
|
|
2659
2854
|
if ("outputVerbosity" in value) {
|
|
2660
2855
|
if (value.outputVerbosity !== null && value.outputVerbosity !== "low" && value.outputVerbosity !== "medium" && value.outputVerbosity !== "high") throw new PreferenceError("outputVerbosity must be null, low, medium, or high.");
|
|
2661
2856
|
patch.outputVerbosity = value.outputVerbosity;
|
|
@@ -2664,30 +2859,6 @@ function readPreferencesUpdate(value, current) {
|
|
|
2664
2859
|
if (value.searchProvider !== "dsh" && value.searchProvider !== "codex") throw new PreferenceError("searchProvider must be dsh or codex.");
|
|
2665
2860
|
patch.searchProvider = value.searchProvider;
|
|
2666
2861
|
}
|
|
2667
|
-
if ("subagentProvider" in value) {
|
|
2668
|
-
if (typeof value.subagentProvider !== "string" || value.subagentProvider === "") throw new PreferenceError("subagentProvider must be a non-empty string.");
|
|
2669
|
-
patch.subagentProvider = value.subagentProvider;
|
|
2670
|
-
}
|
|
2671
|
-
if ("subagentModel" in value) {
|
|
2672
|
-
if (typeof value.subagentModel !== "string" || value.subagentModel === "") throw new PreferenceError("subagentModel must be a non-empty string.");
|
|
2673
|
-
patch.subagentModel = value.subagentModel;
|
|
2674
|
-
}
|
|
2675
|
-
if ("subagentReasoningEffort" in value) {
|
|
2676
|
-
if (value.subagentReasoningEffort !== null && (typeof value.subagentReasoningEffort !== "string" || value.subagentReasoningEffort === "")) throw new PreferenceError("subagentReasoningEffort must be null or a non-empty string.");
|
|
2677
|
-
patch.subagentReasoningEffort = value.subagentReasoningEffort;
|
|
2678
|
-
}
|
|
2679
|
-
if ("subagentContextWindow" in value) {
|
|
2680
|
-
if (!Number.isSafeInteger(value.subagentContextWindow) || value.subagentContextWindow < 1) throw new PreferenceError("subagentContextWindow must be a positive integer.");
|
|
2681
|
-
patch.subagentContextWindow = value.subagentContextWindow;
|
|
2682
|
-
}
|
|
2683
|
-
if ("subagentMaxDepth" in value) {
|
|
2684
|
-
if (!Number.isSafeInteger(value.subagentMaxDepth) || value.subagentMaxDepth < 0 || value.subagentMaxDepth > 3) throw new PreferenceError(`subagentMaxDepth must be an integer from 0 to 3.`);
|
|
2685
|
-
patch.subagentMaxDepth = value.subagentMaxDepth;
|
|
2686
|
-
}
|
|
2687
|
-
if ("subagentMaxAgents" in value) {
|
|
2688
|
-
if (!Number.isSafeInteger(value.subagentMaxAgents) || value.subagentMaxAgents < 1) throw new PreferenceError("subagentMaxAgents must be a positive integer.");
|
|
2689
|
-
patch.subagentMaxAgents = value.subagentMaxAgents;
|
|
2690
|
-
}
|
|
2691
2862
|
if ("contextWindowOverrides" in value) {
|
|
2692
2863
|
if (!isRecord(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
|
|
2693
2864
|
const overrides = {};
|
|
@@ -2698,55 +2869,24 @@ function readPreferencesUpdate(value, current) {
|
|
|
2698
2869
|
}
|
|
2699
2870
|
patch.contextWindowOverrides = overrides;
|
|
2700
2871
|
}
|
|
2872
|
+
if ("subagentContextWindow" in value) {
|
|
2873
|
+
if (value.subagentContextWindow !== null && (!Number.isSafeInteger(value.subagentContextWindow) || value.subagentContextWindow < 1)) throw new PreferenceError("subagentContextWindow must be null or a positive integer.");
|
|
2874
|
+
patch.subagentContextWindow = value.subagentContextWindow;
|
|
2875
|
+
}
|
|
2876
|
+
if ("subagentMaxDepth" in value) {
|
|
2877
|
+
if (value.subagentMaxDepth !== null && (!Number.isSafeInteger(value.subagentMaxDepth) || value.subagentMaxDepth < 0 || value.subagentMaxDepth > 3)) throw new PreferenceError(`subagentMaxDepth must be null or an integer from 0 to 3.`);
|
|
2878
|
+
patch.subagentMaxDepth = value.subagentMaxDepth;
|
|
2879
|
+
}
|
|
2880
|
+
if ("proxyMode" in value) {
|
|
2881
|
+
if (value.proxyMode !== "auto" && value.proxyMode !== "custom" && value.proxyMode !== "direct") throw new PreferenceError("proxyMode must be auto, custom, or direct.");
|
|
2882
|
+
patch.proxyMode = value.proxyMode;
|
|
2883
|
+
}
|
|
2884
|
+
if ("customProxyUrl" in value) {
|
|
2885
|
+
if (value.customProxyUrl !== null && typeof value.customProxyUrl !== "string") throw new PreferenceError("customProxyUrl must be a string or null.");
|
|
2886
|
+
patch.customProxyUrl = value.customProxyUrl;
|
|
2887
|
+
}
|
|
2701
2888
|
return patch;
|
|
2702
2889
|
}
|
|
2703
|
-
async function subagentModelCatalog(ctx) {
|
|
2704
|
-
const results = await Promise.all(ctx.llm.listProviders().filter((provider) => provider.id !== SubagentContextAdapter.provider).map(async (provider) => {
|
|
2705
|
-
try {
|
|
2706
|
-
const models = await ctx.llm.listModels(provider.id);
|
|
2707
|
-
return {
|
|
2708
|
-
kind: "provider",
|
|
2709
|
-
provider: {
|
|
2710
|
-
id: provider.id,
|
|
2711
|
-
name: provider.name,
|
|
2712
|
-
models: await Promise.all(models.map(async (model) => {
|
|
2713
|
-
const resolved = await ctx.llm.resolveModelInfo(provider.id, model.id);
|
|
2714
|
-
return {
|
|
2715
|
-
id: model.id,
|
|
2716
|
-
name: model.name,
|
|
2717
|
-
...model.description === void 0 ? {} : { description: model.description },
|
|
2718
|
-
...resolved.context === void 0 ? {} : {
|
|
2719
|
-
contextWindow: resolved.context.contextWindow,
|
|
2720
|
-
maxContextWindow: provider.id === "codex-chatgpt" && isConfigurableContextModelId(model.id) ? GPT_56_MAX_CONTEXT_WINDOW : resolved.context.contextWindow
|
|
2721
|
-
},
|
|
2722
|
-
...resolved.reasoning === void 0 ? {} : { reasoning: {
|
|
2723
|
-
efforts: resolved.reasoning.efforts.map((effort) => ({
|
|
2724
|
-
id: effort.id,
|
|
2725
|
-
name: effort.name,
|
|
2726
|
-
...effort.description === void 0 ? {} : { description: effort.description }
|
|
2727
|
-
})),
|
|
2728
|
-
...resolved.reasoning.defaultEffort === void 0 ? {} : { defaultEffort: resolved.reasoning.defaultEffort }
|
|
2729
|
-
} }
|
|
2730
|
-
};
|
|
2731
|
-
}))
|
|
2732
|
-
}
|
|
2733
|
-
};
|
|
2734
|
-
} catch (error) {
|
|
2735
|
-
return {
|
|
2736
|
-
kind: "failure",
|
|
2737
|
-
failure: {
|
|
2738
|
-
id: provider.id,
|
|
2739
|
-
name: provider.name,
|
|
2740
|
-
message: error instanceof Error ? error.message : String(error)
|
|
2741
|
-
}
|
|
2742
|
-
};
|
|
2743
|
-
}
|
|
2744
|
-
}));
|
|
2745
|
-
return {
|
|
2746
|
-
providers: results.filter((result) => result.kind === "provider").map((result) => result.provider),
|
|
2747
|
-
failures: results.filter((result) => result.kind === "failure").map((result) => result.failure)
|
|
2748
|
-
};
|
|
2749
|
-
}
|
|
2750
2890
|
function json(response, envelope, status = 200) {
|
|
2751
2891
|
response.writeHead(status, {
|
|
2752
2892
|
"content-type": "application/json; charset=utf-8",
|
|
@@ -3108,7 +3248,8 @@ function createPlatformTokenStore(platform = process.platform) {
|
|
|
3108
3248
|
//#region src/host/search-provider-switcher.ts
|
|
3109
3249
|
var SearchProviderSwitcher = class {
|
|
3110
3250
|
loader;
|
|
3111
|
-
|
|
3251
|
+
originalSearchProvider;
|
|
3252
|
+
originalFetchProvider;
|
|
3112
3253
|
initialized = false;
|
|
3113
3254
|
constructor(loader) {
|
|
3114
3255
|
this.loader = loader;
|
|
@@ -3118,14 +3259,19 @@ var SearchProviderSwitcher = class {
|
|
|
3118
3259
|
if (entry === null) return;
|
|
3119
3260
|
const config = currentConfig(entry);
|
|
3120
3261
|
if (!this.initialized) {
|
|
3121
|
-
this.
|
|
3262
|
+
this.originalSearchProvider = typeof config.searchProvider === "string" && config.searchProvider !== "codex-subscription" ? config.searchProvider : void 0;
|
|
3263
|
+
this.originalFetchProvider = typeof config.fetchProvider === "string" && config.fetchProvider !== "codex-subscription" ? config.fetchProvider : void 0;
|
|
3122
3264
|
this.initialized = true;
|
|
3123
3265
|
}
|
|
3124
|
-
const selected = preference === "codex" ? CODEX_SEARCH_PROVIDER_ID :
|
|
3125
|
-
|
|
3266
|
+
const selected = preference === "codex" ? CODEX_SEARCH_PROVIDER_ID : void 0;
|
|
3267
|
+
const nextSearch = selected ?? this.originalSearchProvider;
|
|
3268
|
+
const nextFetch = selected ? CODEX_FETCH_PROVIDER_ID : this.originalFetchProvider;
|
|
3269
|
+
if (config.searchProvider === nextSearch && config.fetchProvider === nextFetch) return;
|
|
3126
3270
|
const nextConfig = { ...config };
|
|
3127
|
-
if (
|
|
3128
|
-
else nextConfig.searchProvider =
|
|
3271
|
+
if (nextSearch === void 0) delete nextConfig.searchProvider;
|
|
3272
|
+
else nextConfig.searchProvider = nextSearch;
|
|
3273
|
+
if (nextFetch === void 0) delete nextConfig.fetchProvider;
|
|
3274
|
+
else nextConfig.fetchProvider = nextFetch;
|
|
3129
3275
|
await entry.update({ config: nextConfig }, true);
|
|
3130
3276
|
}
|
|
3131
3277
|
findWebEntry() {
|
|
@@ -3141,100 +3287,6 @@ function currentConfig(entry) {
|
|
|
3141
3287
|
return typeof config === "object" && config !== null && !Array.isArray(config) ? config : {};
|
|
3142
3288
|
}
|
|
3143
3289
|
//#endregion
|
|
3144
|
-
//#region src/host/subagent-model-preference.ts
|
|
3145
|
-
/** Apply the configured provider/model/reasoning route to every delegated Agent. */
|
|
3146
|
-
function installSubagentModelPreference(ctx, preferences) {
|
|
3147
|
-
return ctx.on("agent/request", async (payload, next) => {
|
|
3148
|
-
const resolved = await next();
|
|
3149
|
-
if (payload.agent.session.header.origin !== "subagent") return resolved;
|
|
3150
|
-
const selection = preferences.status();
|
|
3151
|
-
return {
|
|
3152
|
-
...resolved,
|
|
3153
|
-
provider: SubagentContextAdapter.provider,
|
|
3154
|
-
model: selection.subagentModel,
|
|
3155
|
-
...selection.subagentReasoningEffort === null ? { reasoningEffort: void 0 } : { reasoningEffort: ReasoningEffortId(selection.subagentReasoningEffort) }
|
|
3156
|
-
};
|
|
3157
|
-
});
|
|
3158
|
-
}
|
|
3159
|
-
//#endregion
|
|
3160
|
-
//#region src/host/subagent-policy.ts
|
|
3161
|
-
const MANAGED_DELEGATION_TOOLS = /* @__PURE__ */ new Set(["subagent", "subagent_fork"]);
|
|
3162
|
-
/**
|
|
3163
|
-
* Enforce global limits with a user-facing preflight on built-in delegation
|
|
3164
|
-
* tools and an authoritative synchronous Agent-publication gate. The latter
|
|
3165
|
-
* also covers workflows, Ralph, and nested subagents without depending on the
|
|
3166
|
-
* parent's provider or model. Throwing from `agent/created` vetoes and rolls
|
|
3167
|
-
* back the new child before its loop starts; already-running children remain.
|
|
3168
|
-
*/
|
|
3169
|
-
function installSubagentPolicy(ctx, preferences) {
|
|
3170
|
-
const disposePreflight = ctx.on("tools/pre-execute", async (exec, next) => {
|
|
3171
|
-
const parent = exec.agent;
|
|
3172
|
-
if (parent === void 0 || !MANAGED_DELEGATION_TOOLS.has(exec.name)) return next();
|
|
3173
|
-
const { subagentMaxDepth, subagentMaxAgents } = preferences.status();
|
|
3174
|
-
const attemptedDepth = delegationDepthOf(parent) + 1;
|
|
3175
|
-
if (attemptedDepth > subagentMaxDepth) return {
|
|
3176
|
-
kind: "deny",
|
|
3177
|
-
reason: `Subagent nesting depth limit reached: attempted depth ${attemptedDepth}, configured maximum ${subagentMaxDepth}.`
|
|
3178
|
-
};
|
|
3179
|
-
const activeAgents = activeDescendantCount(ctx, subagentTreeRoot(ctx, parent).id);
|
|
3180
|
-
if (activeAgents >= subagentMaxAgents) return {
|
|
3181
|
-
kind: "deny",
|
|
3182
|
-
reason: `Subagent limit reached: ${activeAgents} active descendants in the Agent tree, configured maximum ${subagentMaxAgents}. Wait for one to finish or raise the global limit in Subagents settings.`
|
|
3183
|
-
};
|
|
3184
|
-
return next();
|
|
3185
|
-
});
|
|
3186
|
-
const disposePublicationGate = ctx.on("agent/created", ({ agent }) => {
|
|
3187
|
-
if (agent.session.header.origin !== "subagent") return;
|
|
3188
|
-
const { subagentMaxDepth, subagentMaxAgents } = preferences.status();
|
|
3189
|
-
const depth = delegationDepthOf(agent);
|
|
3190
|
-
if (depth > subagentMaxDepth) throw new Error(`Subagent nesting depth limit reached: attempted depth ${depth}, configured maximum ${subagentMaxDepth}.`);
|
|
3191
|
-
const activeAgents = activeDescendantCount(ctx, subagentTreeRoot(ctx, agent).id);
|
|
3192
|
-
if (activeAgents > subagentMaxAgents) throw new Error(`Subagent limit reached: attempted ${activeAgents} active descendants in the Agent tree, configured maximum ${subagentMaxAgents}. Wait for one to finish or raise the global limit in Subagents settings.`);
|
|
3193
|
-
});
|
|
3194
|
-
return () => {
|
|
3195
|
-
disposePublicationGate();
|
|
3196
|
-
disposePreflight();
|
|
3197
|
-
};
|
|
3198
|
-
}
|
|
3199
|
-
/** Return the highest currently live ancestor that owns this subagent tree. */
|
|
3200
|
-
function subagentTreeRoot(ctx, agent) {
|
|
3201
|
-
let current = agent;
|
|
3202
|
-
const visited = /* @__PURE__ */ new Set([agent.id]);
|
|
3203
|
-
while (current.session.header.origin === "subagent") {
|
|
3204
|
-
const parentId = current.session.header.parentSession;
|
|
3205
|
-
if (parentId === void 0 || visited.has(parentId)) break;
|
|
3206
|
-
const parent = ctx.agents.get(parentId);
|
|
3207
|
-
if (parent === void 0) break;
|
|
3208
|
-
visited.add(parent.id);
|
|
3209
|
-
current = parent;
|
|
3210
|
-
}
|
|
3211
|
-
return current;
|
|
3212
|
-
}
|
|
3213
|
-
/** Return currently live descendants in one caller's subagent tree. */
|
|
3214
|
-
function activeDescendantIds(ctx, parentId) {
|
|
3215
|
-
const childrenByParent = /* @__PURE__ */ new Map();
|
|
3216
|
-
for (const agent of ctx.agents.list()) {
|
|
3217
|
-
if (agent.session.header.origin !== "subagent") continue;
|
|
3218
|
-
const directParent = agent.session.header.parentSession;
|
|
3219
|
-
if (directParent === void 0) continue;
|
|
3220
|
-
const children = childrenByParent.get(directParent) ?? [];
|
|
3221
|
-
children.push(agent.id);
|
|
3222
|
-
childrenByParent.set(directParent, children);
|
|
3223
|
-
}
|
|
3224
|
-
const descendants = /* @__PURE__ */ new Set();
|
|
3225
|
-
const pending = [...childrenByParent.get(parentId) ?? []];
|
|
3226
|
-
while (pending.length > 0) {
|
|
3227
|
-
const id = pending.pop();
|
|
3228
|
-
if (descendants.has(id)) continue;
|
|
3229
|
-
descendants.add(id);
|
|
3230
|
-
pending.push(...childrenByParent.get(id) ?? []);
|
|
3231
|
-
}
|
|
3232
|
-
return descendants;
|
|
3233
|
-
}
|
|
3234
|
-
function activeDescendantCount(ctx, parentId) {
|
|
3235
|
-
return activeDescendantIds(ctx, parentId).size;
|
|
3236
|
-
}
|
|
3237
|
-
//#endregion
|
|
3238
3290
|
//#region src/index.ts
|
|
3239
3291
|
const inject = [
|
|
3240
3292
|
"webServer",
|
|
@@ -3243,52 +3295,61 @@ const inject = [
|
|
|
3243
3295
|
"tools",
|
|
3244
3296
|
"web",
|
|
3245
3297
|
"settings",
|
|
3246
|
-
"loader"
|
|
3247
|
-
"agents"
|
|
3298
|
+
"loader"
|
|
3248
3299
|
];
|
|
3249
3300
|
function apply(ctx) {
|
|
3250
|
-
const
|
|
3251
|
-
const usage = new UsageService(oauth);
|
|
3301
|
+
const store = createPlatformTokenStore();
|
|
3252
3302
|
const preferences = registerPreferenceStore(ctx.settings);
|
|
3253
|
-
const adapter = new CodexChatGptAdapter(new ResponsesClient(oauth, ctx.attachments, {
|
|
3254
|
-
localRawImages: { baseUrl: localWebServerBaseUrl(ctx.webServer.host, ctx.webServer.port) },
|
|
3255
|
-
onGenerationFinished: () => usage.invalidate(),
|
|
3256
|
-
outputVerbosity: () => preferences.status().outputVerbosity
|
|
3257
|
-
}), preferences);
|
|
3258
3303
|
ctx.effect(() => {
|
|
3304
|
+
const proxyManager = new ProxyManager({
|
|
3305
|
+
getPreferences: () => preferences.status(),
|
|
3306
|
+
logger: ctx.logger
|
|
3307
|
+
});
|
|
3308
|
+
const proxyFetch = proxyManager.createFetch();
|
|
3309
|
+
const oauth = new OAuthService(store, {
|
|
3310
|
+
fetchFn: proxyFetch,
|
|
3311
|
+
logger: ctx.logger
|
|
3312
|
+
});
|
|
3313
|
+
const usage = new UsageService(oauth, { fetchFn: proxyFetch });
|
|
3314
|
+
const adapter = new CodexChatGptAdapter(new ResponsesClient(oauth, ctx.attachments, {
|
|
3315
|
+
fetchFn: proxyFetch,
|
|
3316
|
+
localRawImages: { baseUrl: localWebServerBaseUrl(ctx.webServer.host, ctx.webServer.port) },
|
|
3317
|
+
onGenerationFinished: () => usage.invalidate(),
|
|
3318
|
+
outputVerbosity: () => preferences.status().outputVerbosity,
|
|
3319
|
+
fastMode: () => preferences.status().fastMode
|
|
3320
|
+
}), preferences);
|
|
3259
3321
|
const searchSwitcher = new SearchProviderSwitcher(ctx.loader);
|
|
3260
3322
|
const applySearchPreference = (searchProvider = preferences.status().searchProvider) => {
|
|
3261
3323
|
searchSwitcher.select(searchProvider).catch((error) => {
|
|
3262
3324
|
ctx.logger.warn(`[dsh-chatgpt-subscription] Search provider preference could not be applied: ${error instanceof Error ? error.message : String(error)}`);
|
|
3263
3325
|
});
|
|
3264
3326
|
};
|
|
3265
|
-
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences);
|
|
3327
|
+
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager);
|
|
3266
3328
|
const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID], adapter);
|
|
3267
|
-
const
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3329
|
+
const disposeImageTool = ctx.tools.register(createCodexImageTool(oauth, ctx.attachments, { fetchFn: proxyFetch }));
|
|
3330
|
+
let disposeWebProviders = () => {};
|
|
3331
|
+
const registerWebProviders = () => {
|
|
3332
|
+
disposeWebProviders();
|
|
3333
|
+
if (ctx.web) {
|
|
3334
|
+
const d1 = ctx.web.registerSearchProvider(createCodexSearchProvider(oauth, { fetchFn: proxyFetch }));
|
|
3335
|
+
const d2 = ctx.web.registerFetchProvider(createCodexFetchProvider({ fetchFn: proxyFetch }));
|
|
3336
|
+
disposeWebProviders = () => {
|
|
3337
|
+
d1();
|
|
3338
|
+
d2();
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
registerWebProviders();
|
|
3280
3343
|
const disposePreferenceWatch = preferences.watch((next) => applySearchPreference(next.searchProvider));
|
|
3281
3344
|
applySearchPreference();
|
|
3282
3345
|
return () => {
|
|
3283
3346
|
disposePreferenceWatch();
|
|
3284
|
-
|
|
3285
|
-
disposeSubagentModelPreference();
|
|
3286
|
-
disposeSearchProvider();
|
|
3347
|
+
disposeWebProviders();
|
|
3287
3348
|
disposeImageTool();
|
|
3288
|
-
disposeSubagentContextAdapter();
|
|
3289
3349
|
disposeAdapter();
|
|
3290
3350
|
disposeRoutes();
|
|
3291
3351
|
oauth.dispose();
|
|
3352
|
+
proxyManager.dispose();
|
|
3292
3353
|
};
|
|
3293
3354
|
}, "dsh-chatgpt-subscription: adapter, routes, and lifecycle");
|
|
3294
3355
|
}
|
|
@@ -3296,4 +3357,4 @@ function localWebServerBaseUrl(host, port) {
|
|
|
3296
3357
|
return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
|
|
3297
3358
|
}
|
|
3298
3359
|
//#endregion
|
|
3299
|
-
export { CodexChatGptAdapter, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, inject, mapCodexUsage, parseCodexUsage, parseResponsesStream };
|
|
3360
|
+
export { CodexChatGptAdapter, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, detectSystemProxy, inject, mapCodexUsage, parseCodexUsage, parseResponsesStream };
|