@eddyskywalker/dsh-chatgpt-subscription 0.1.28 → 0.1.30
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/lib/client.js +373 -454
- package/lib/client.js.map +1 -1
- package/lib/index.js +330 -280
- 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 +49 -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-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 +11 -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,6 +1147,154 @@ 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 = {
|
|
@@ -1083,17 +1303,15 @@ const DEFAULT_PREFERENCES = {
|
|
|
1083
1303
|
outputVerbosity: null,
|
|
1084
1304
|
visibleModelIds: [...DEFAULT_VISIBLE_CODEX_MODEL_IDS],
|
|
1085
1305
|
searchProvider: "dsh",
|
|
1086
|
-
subagentProvider: "codex-chatgpt",
|
|
1087
|
-
subagentModel: "gpt-5.6-sol",
|
|
1088
|
-
subagentReasoningEffort: "medium",
|
|
1089
|
-
subagentContextWindow: 272e3,
|
|
1090
|
-
subagentMaxDepth: 3,
|
|
1091
|
-
subagentMaxAgents: 8,
|
|
1092
1306
|
contextWindowOverrides: {
|
|
1093
1307
|
"gpt-5.6-sol": 272e3,
|
|
1094
1308
|
"gpt-5.6-terra": 272e3,
|
|
1095
1309
|
"gpt-5.6-luna": 272e3
|
|
1096
|
-
}
|
|
1310
|
+
},
|
|
1311
|
+
subagentContextWindow: null,
|
|
1312
|
+
subagentMaxDepth: null,
|
|
1313
|
+
proxyMode: "auto",
|
|
1314
|
+
customProxyUrl: null
|
|
1097
1315
|
};
|
|
1098
1316
|
const SEARCH_PROVIDER_CODEX = "codex";
|
|
1099
1317
|
function isSearchProviderPreference(value) {
|
|
@@ -1102,6 +1320,9 @@ function isSearchProviderPreference(value) {
|
|
|
1102
1320
|
function isCodexOutputVerbosity(value) {
|
|
1103
1321
|
return value === "low" || value === "medium" || value === "high";
|
|
1104
1322
|
}
|
|
1323
|
+
function isProxyMode(value) {
|
|
1324
|
+
return value === "auto" || value === "custom" || value === "direct";
|
|
1325
|
+
}
|
|
1105
1326
|
//#endregion
|
|
1106
1327
|
//#region src/host/preferences.ts
|
|
1107
1328
|
function registerPreferenceStore(settings) {
|
|
@@ -1116,17 +1337,19 @@ function registerPreferenceStore(settings) {
|
|
|
1116
1337
|
]).default(DEFAULT_PREFERENCES.outputVerbosity),
|
|
1117
1338
|
visibleModelIds: z.array(z.string()).default(DEFAULT_PREFERENCES.visibleModelIds),
|
|
1118
1339
|
searchProvider: z.union([z.const("dsh"), z.const(SEARCH_PROVIDER_CODEX)]).default(DEFAULT_PREFERENCES.searchProvider),
|
|
1119
|
-
subagentProvider: z.string().default(DEFAULT_PREFERENCES.subagentProvider),
|
|
1120
|
-
subagentModel: z.string().default(DEFAULT_PREFERENCES.subagentModel),
|
|
1121
|
-
subagentReasoningEffort: z.union([z.string(), z.const(null)]).default(DEFAULT_PREFERENCES.subagentReasoningEffort),
|
|
1122
|
-
subagentContextWindow: z.number().step(1).min(1).default(DEFAULT_PREFERENCES.subagentContextWindow),
|
|
1123
|
-
subagentMaxDepth: z.number().step(1).min(0).max(3).default(DEFAULT_PREFERENCES.subagentMaxDepth),
|
|
1124
|
-
subagentMaxAgents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_PREFERENCES.subagentMaxAgents),
|
|
1125
1340
|
contextWindowOverrides: z.object({
|
|
1126
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"]),
|
|
1127
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"]),
|
|
1128
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"])
|
|
1129
|
-
}).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)
|
|
1130
1353
|
})));
|
|
1131
1354
|
}
|
|
1132
1355
|
var SettingsPreferenceStore = class {
|
|
@@ -1153,16 +1376,21 @@ var SettingsPreferenceStore = class {
|
|
|
1153
1376
|
if (!isSearchProviderPreference(patch.searchProvider)) throw new PreferenceError("Unsupported search provider preference.");
|
|
1154
1377
|
normalized.searchProvider = patch.searchProvider;
|
|
1155
1378
|
}
|
|
1156
|
-
if (patch.subagentProvider !== void 0) normalized.subagentProvider = patch.subagentProvider;
|
|
1157
|
-
if (patch.subagentModel !== void 0) normalized.subagentModel = patch.subagentModel;
|
|
1158
|
-
if (patch.subagentReasoningEffort !== void 0) normalized.subagentReasoningEffort = patch.subagentReasoningEffort;
|
|
1159
|
-
if (patch.subagentContextWindow !== void 0) normalized.subagentContextWindow = patch.subagentContextWindow;
|
|
1160
|
-
if (patch.subagentMaxDepth !== void 0) normalized.subagentMaxDepth = patch.subagentMaxDepth;
|
|
1161
|
-
if (patch.subagentMaxAgents !== void 0) normalized.subagentMaxAgents = patch.subagentMaxAgents;
|
|
1162
1379
|
if (patch.contextWindowOverrides !== void 0) normalized.contextWindowOverrides = {
|
|
1163
1380
|
...this.scope.get().contextWindowOverrides,
|
|
1164
1381
|
...patch.contextWindowOverrides
|
|
1165
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;
|
|
1166
1394
|
await this.scope.update(normalized);
|
|
1167
1395
|
return this.status();
|
|
1168
1396
|
}
|
|
@@ -1223,6 +1451,7 @@ async function buildResponsesPayload(options, attachments, localRawImages = {},
|
|
|
1223
1451
|
const instructionParts = [
|
|
1224
1452
|
options.system?.trim(),
|
|
1225
1453
|
...options.messages.filter((message) => message.role === "system").map((message) => blocksToText(message.content).trim()),
|
|
1454
|
+
progressExplanationInstruction(options.tools),
|
|
1226
1455
|
sandboxToolInstruction(options.tools, sandboxRetryTools),
|
|
1227
1456
|
commandToolInstruction(options.tools),
|
|
1228
1457
|
runCodeInstruction(options.tools)
|
|
@@ -1305,6 +1534,10 @@ async function buildResponsesPayload(options, attachments, localRawImages = {},
|
|
|
1305
1534
|
} : { effort: options.reasoningEffort };
|
|
1306
1535
|
return payload;
|
|
1307
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
|
+
}
|
|
1308
1541
|
function runCodeInstruction(tools) {
|
|
1309
1542
|
if (!tools?.some((tool) => tool.name === "run_code")) return void 0;
|
|
1310
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.";
|
|
@@ -2379,61 +2612,11 @@ function identityKey(credentials) {
|
|
|
2379
2612
|
return credentials.accountId ?? credentials.email ?? credentials.planType ?? "signed-in";
|
|
2380
2613
|
}
|
|
2381
2614
|
//#endregion
|
|
2382
|
-
//#region src/host/subagent-context-adapter.ts
|
|
2383
|
-
/**
|
|
2384
|
-
* A private provider route that delegates streaming to a selected registered
|
|
2385
|
-
* provider while exposing a user-selected effective context window to DSH.
|
|
2386
|
-
*/
|
|
2387
|
-
var SubagentContextAdapter = class SubagentContextAdapter extends LlmAdapter {
|
|
2388
|
-
llm;
|
|
2389
|
-
selection;
|
|
2390
|
-
static provider = "dsh-subagent-model-override";
|
|
2391
|
-
constructor(llm, selection) {
|
|
2392
|
-
super();
|
|
2393
|
-
this.llm = llm;
|
|
2394
|
-
this.selection = selection;
|
|
2395
|
-
}
|
|
2396
|
-
providerInfo() {
|
|
2397
|
-
return {
|
|
2398
|
-
id: SubagentContextAdapter.provider,
|
|
2399
|
-
name: "Subagent model override"
|
|
2400
|
-
};
|
|
2401
|
-
}
|
|
2402
|
-
async listModels() {
|
|
2403
|
-
return [];
|
|
2404
|
-
}
|
|
2405
|
-
async resolveModel(_provider, _model, signal) {
|
|
2406
|
-
const selected = this.selection();
|
|
2407
|
-
return {
|
|
2408
|
-
...await this.llm.resolveModelInfo(selected.provider, selected.model, signal),
|
|
2409
|
-
provider: SubagentContextAdapter.provider,
|
|
2410
|
-
id: selected.model,
|
|
2411
|
-
context: { contextWindow: selected.contextWindow }
|
|
2412
|
-
};
|
|
2413
|
-
}
|
|
2414
|
-
stream(options) {
|
|
2415
|
-
const selected = this.selection();
|
|
2416
|
-
const { preparedCall: _preparedCall, ...forwarded } = options;
|
|
2417
|
-
return this.llm.stream({
|
|
2418
|
-
...forwarded,
|
|
2419
|
-
provider: selected.provider,
|
|
2420
|
-
model: selected.model
|
|
2421
|
-
});
|
|
2422
|
-
}
|
|
2423
|
-
};
|
|
2424
|
-
//#endregion
|
|
2425
2615
|
//#region src/host/routes.ts
|
|
2426
2616
|
const MAX_BODY_BYTES = 64 * 1024;
|
|
2427
|
-
function registerRoutes(ctx, oauth, usage, preferences) {
|
|
2617
|
+
function registerRoutes(ctx, oauth, usage, preferences, proxyManager) {
|
|
2428
2618
|
const handler = async (request, response) => {
|
|
2429
2619
|
const url = new URL(request.url ?? "/", "http://dsh.local");
|
|
2430
|
-
if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/models`) {
|
|
2431
|
-
json(response, {
|
|
2432
|
-
ok: true,
|
|
2433
|
-
value: await subagentModelCatalog(ctx)
|
|
2434
|
-
});
|
|
2435
|
-
return;
|
|
2436
|
-
}
|
|
2437
2620
|
if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/status`) {
|
|
2438
2621
|
const oauthStatus = await oauth.status();
|
|
2439
2622
|
json(response, {
|
|
@@ -2441,7 +2624,9 @@ function registerRoutes(ctx, oauth, usage, preferences) {
|
|
|
2441
2624
|
value: {
|
|
2442
2625
|
...oauthStatus,
|
|
2443
2626
|
quota: await usage.status(oauthStatus.authenticated),
|
|
2444
|
-
preferences: preferences.status()
|
|
2627
|
+
preferences: preferences.status(),
|
|
2628
|
+
detectedProxy: proxyManager?.getSystemProxy() ?? null,
|
|
2629
|
+
activeProxy: proxyManager?.resolveActiveProxyUrl() ?? null
|
|
2445
2630
|
}
|
|
2446
2631
|
});
|
|
2447
2632
|
return;
|
|
@@ -2674,30 +2859,6 @@ function readPreferencesUpdate(value, current) {
|
|
|
2674
2859
|
if (value.searchProvider !== "dsh" && value.searchProvider !== "codex") throw new PreferenceError("searchProvider must be dsh or codex.");
|
|
2675
2860
|
patch.searchProvider = value.searchProvider;
|
|
2676
2861
|
}
|
|
2677
|
-
if ("subagentProvider" in value) {
|
|
2678
|
-
if (typeof value.subagentProvider !== "string" || value.subagentProvider === "") throw new PreferenceError("subagentProvider must be a non-empty string.");
|
|
2679
|
-
patch.subagentProvider = value.subagentProvider;
|
|
2680
|
-
}
|
|
2681
|
-
if ("subagentModel" in value) {
|
|
2682
|
-
if (typeof value.subagentModel !== "string" || value.subagentModel === "") throw new PreferenceError("subagentModel must be a non-empty string.");
|
|
2683
|
-
patch.subagentModel = value.subagentModel;
|
|
2684
|
-
}
|
|
2685
|
-
if ("subagentReasoningEffort" in value) {
|
|
2686
|
-
if (value.subagentReasoningEffort !== null && (typeof value.subagentReasoningEffort !== "string" || value.subagentReasoningEffort === "")) throw new PreferenceError("subagentReasoningEffort must be null or a non-empty string.");
|
|
2687
|
-
patch.subagentReasoningEffort = value.subagentReasoningEffort;
|
|
2688
|
-
}
|
|
2689
|
-
if ("subagentContextWindow" in value) {
|
|
2690
|
-
if (!Number.isSafeInteger(value.subagentContextWindow) || value.subagentContextWindow < 1) throw new PreferenceError("subagentContextWindow must be a positive integer.");
|
|
2691
|
-
patch.subagentContextWindow = value.subagentContextWindow;
|
|
2692
|
-
}
|
|
2693
|
-
if ("subagentMaxDepth" in value) {
|
|
2694
|
-
if (!Number.isSafeInteger(value.subagentMaxDepth) || value.subagentMaxDepth < 0 || value.subagentMaxDepth > 3) throw new PreferenceError(`subagentMaxDepth must be an integer from 0 to 3.`);
|
|
2695
|
-
patch.subagentMaxDepth = value.subagentMaxDepth;
|
|
2696
|
-
}
|
|
2697
|
-
if ("subagentMaxAgents" in value) {
|
|
2698
|
-
if (!Number.isSafeInteger(value.subagentMaxAgents) || value.subagentMaxAgents < 1) throw new PreferenceError("subagentMaxAgents must be a positive integer.");
|
|
2699
|
-
patch.subagentMaxAgents = value.subagentMaxAgents;
|
|
2700
|
-
}
|
|
2701
2862
|
if ("contextWindowOverrides" in value) {
|
|
2702
2863
|
if (!isRecord(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
|
|
2703
2864
|
const overrides = {};
|
|
@@ -2708,55 +2869,24 @@ function readPreferencesUpdate(value, current) {
|
|
|
2708
2869
|
}
|
|
2709
2870
|
patch.contextWindowOverrides = overrides;
|
|
2710
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
|
+
}
|
|
2711
2888
|
return patch;
|
|
2712
2889
|
}
|
|
2713
|
-
async function subagentModelCatalog(ctx) {
|
|
2714
|
-
const results = await Promise.all(ctx.llm.listProviders().filter((provider) => provider.id !== SubagentContextAdapter.provider).map(async (provider) => {
|
|
2715
|
-
try {
|
|
2716
|
-
const models = await ctx.llm.listModels(provider.id);
|
|
2717
|
-
return {
|
|
2718
|
-
kind: "provider",
|
|
2719
|
-
provider: {
|
|
2720
|
-
id: provider.id,
|
|
2721
|
-
name: provider.name,
|
|
2722
|
-
models: await Promise.all(models.map(async (model) => {
|
|
2723
|
-
const resolved = await ctx.llm.resolveModelInfo(provider.id, model.id);
|
|
2724
|
-
return {
|
|
2725
|
-
id: model.id,
|
|
2726
|
-
name: model.name,
|
|
2727
|
-
...model.description === void 0 ? {} : { description: model.description },
|
|
2728
|
-
...resolved.context === void 0 ? {} : {
|
|
2729
|
-
contextWindow: resolved.context.contextWindow,
|
|
2730
|
-
maxContextWindow: provider.id === "codex-chatgpt" && isConfigurableContextModelId(model.id) ? GPT_56_MAX_CONTEXT_WINDOW : resolved.context.contextWindow
|
|
2731
|
-
},
|
|
2732
|
-
...resolved.reasoning === void 0 ? {} : { reasoning: {
|
|
2733
|
-
efforts: resolved.reasoning.efforts.map((effort) => ({
|
|
2734
|
-
id: effort.id,
|
|
2735
|
-
name: effort.name,
|
|
2736
|
-
...effort.description === void 0 ? {} : { description: effort.description }
|
|
2737
|
-
})),
|
|
2738
|
-
...resolved.reasoning.defaultEffort === void 0 ? {} : { defaultEffort: resolved.reasoning.defaultEffort }
|
|
2739
|
-
} }
|
|
2740
|
-
};
|
|
2741
|
-
}))
|
|
2742
|
-
}
|
|
2743
|
-
};
|
|
2744
|
-
} catch (error) {
|
|
2745
|
-
return {
|
|
2746
|
-
kind: "failure",
|
|
2747
|
-
failure: {
|
|
2748
|
-
id: provider.id,
|
|
2749
|
-
name: provider.name,
|
|
2750
|
-
message: error instanceof Error ? error.message : String(error)
|
|
2751
|
-
}
|
|
2752
|
-
};
|
|
2753
|
-
}
|
|
2754
|
-
}));
|
|
2755
|
-
return {
|
|
2756
|
-
providers: results.filter((result) => result.kind === "provider").map((result) => result.provider),
|
|
2757
|
-
failures: results.filter((result) => result.kind === "failure").map((result) => result.failure)
|
|
2758
|
-
};
|
|
2759
|
-
}
|
|
2760
2890
|
function json(response, envelope, status = 200) {
|
|
2761
2891
|
response.writeHead(status, {
|
|
2762
2892
|
"content-type": "application/json; charset=utf-8",
|
|
@@ -3118,7 +3248,8 @@ function createPlatformTokenStore(platform = process.platform) {
|
|
|
3118
3248
|
//#region src/host/search-provider-switcher.ts
|
|
3119
3249
|
var SearchProviderSwitcher = class {
|
|
3120
3250
|
loader;
|
|
3121
|
-
|
|
3251
|
+
originalSearchProvider;
|
|
3252
|
+
originalFetchProvider;
|
|
3122
3253
|
initialized = false;
|
|
3123
3254
|
constructor(loader) {
|
|
3124
3255
|
this.loader = loader;
|
|
@@ -3128,14 +3259,19 @@ var SearchProviderSwitcher = class {
|
|
|
3128
3259
|
if (entry === null) return;
|
|
3129
3260
|
const config = currentConfig(entry);
|
|
3130
3261
|
if (!this.initialized) {
|
|
3131
|
-
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;
|
|
3132
3264
|
this.initialized = true;
|
|
3133
3265
|
}
|
|
3134
|
-
const selected = preference === "codex" ? CODEX_SEARCH_PROVIDER_ID :
|
|
3135
|
-
|
|
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;
|
|
3136
3270
|
const nextConfig = { ...config };
|
|
3137
|
-
if (
|
|
3138
|
-
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;
|
|
3139
3275
|
await entry.update({ config: nextConfig }, true);
|
|
3140
3276
|
}
|
|
3141
3277
|
findWebEntry() {
|
|
@@ -3151,100 +3287,6 @@ function currentConfig(entry) {
|
|
|
3151
3287
|
return typeof config === "object" && config !== null && !Array.isArray(config) ? config : {};
|
|
3152
3288
|
}
|
|
3153
3289
|
//#endregion
|
|
3154
|
-
//#region src/host/subagent-model-preference.ts
|
|
3155
|
-
/** Apply the configured provider/model/reasoning route to every delegated Agent. */
|
|
3156
|
-
function installSubagentModelPreference(ctx, preferences) {
|
|
3157
|
-
return ctx.on("agent/request", async (payload, next) => {
|
|
3158
|
-
const resolved = await next();
|
|
3159
|
-
if (payload.agent.session.header.origin !== "subagent") return resolved;
|
|
3160
|
-
const selection = preferences.status();
|
|
3161
|
-
return {
|
|
3162
|
-
...resolved,
|
|
3163
|
-
provider: SubagentContextAdapter.provider,
|
|
3164
|
-
model: selection.subagentModel,
|
|
3165
|
-
...selection.subagentReasoningEffort === null ? { reasoningEffort: void 0 } : { reasoningEffort: ReasoningEffortId(selection.subagentReasoningEffort) }
|
|
3166
|
-
};
|
|
3167
|
-
});
|
|
3168
|
-
}
|
|
3169
|
-
//#endregion
|
|
3170
|
-
//#region src/host/subagent-policy.ts
|
|
3171
|
-
const MANAGED_DELEGATION_TOOLS = /* @__PURE__ */ new Set(["subagent", "subagent_fork"]);
|
|
3172
|
-
/**
|
|
3173
|
-
* Enforce global limits with a user-facing preflight on built-in delegation
|
|
3174
|
-
* tools and an authoritative synchronous Agent-publication gate. The latter
|
|
3175
|
-
* also covers workflows, Ralph, and nested subagents without depending on the
|
|
3176
|
-
* parent's provider or model. Throwing from `agent/created` vetoes and rolls
|
|
3177
|
-
* back the new child before its loop starts; already-running children remain.
|
|
3178
|
-
*/
|
|
3179
|
-
function installSubagentPolicy(ctx, preferences) {
|
|
3180
|
-
const disposePreflight = ctx.on("tools/pre-execute", async (exec, next) => {
|
|
3181
|
-
const parent = exec.agent;
|
|
3182
|
-
if (parent === void 0 || !MANAGED_DELEGATION_TOOLS.has(exec.name)) return next();
|
|
3183
|
-
const { subagentMaxDepth, subagentMaxAgents } = preferences.status();
|
|
3184
|
-
const attemptedDepth = delegationDepthOf(parent) + 1;
|
|
3185
|
-
if (attemptedDepth > subagentMaxDepth) return {
|
|
3186
|
-
kind: "deny",
|
|
3187
|
-
reason: `Subagent nesting depth limit reached: attempted depth ${attemptedDepth}, configured maximum ${subagentMaxDepth}.`
|
|
3188
|
-
};
|
|
3189
|
-
const activeAgents = activeDescendantCount(ctx, subagentTreeRoot(ctx, parent).id);
|
|
3190
|
-
if (activeAgents >= subagentMaxAgents) return {
|
|
3191
|
-
kind: "deny",
|
|
3192
|
-
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.`
|
|
3193
|
-
};
|
|
3194
|
-
return next();
|
|
3195
|
-
});
|
|
3196
|
-
const disposePublicationGate = ctx.on("agent/created", ({ agent }) => {
|
|
3197
|
-
if (agent.session.header.origin !== "subagent") return;
|
|
3198
|
-
const { subagentMaxDepth, subagentMaxAgents } = preferences.status();
|
|
3199
|
-
const depth = delegationDepthOf(agent);
|
|
3200
|
-
if (depth > subagentMaxDepth) throw new Error(`Subagent nesting depth limit reached: attempted depth ${depth}, configured maximum ${subagentMaxDepth}.`);
|
|
3201
|
-
const activeAgents = activeDescendantCount(ctx, subagentTreeRoot(ctx, agent).id);
|
|
3202
|
-
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.`);
|
|
3203
|
-
});
|
|
3204
|
-
return () => {
|
|
3205
|
-
disposePublicationGate();
|
|
3206
|
-
disposePreflight();
|
|
3207
|
-
};
|
|
3208
|
-
}
|
|
3209
|
-
/** Return the highest currently live ancestor that owns this subagent tree. */
|
|
3210
|
-
function subagentTreeRoot(ctx, agent) {
|
|
3211
|
-
let current = agent;
|
|
3212
|
-
const visited = /* @__PURE__ */ new Set([agent.id]);
|
|
3213
|
-
while (current.session.header.origin === "subagent") {
|
|
3214
|
-
const parentId = current.session.header.parentSession;
|
|
3215
|
-
if (parentId === void 0 || visited.has(parentId)) break;
|
|
3216
|
-
const parent = ctx.agents.get(parentId);
|
|
3217
|
-
if (parent === void 0) break;
|
|
3218
|
-
visited.add(parent.id);
|
|
3219
|
-
current = parent;
|
|
3220
|
-
}
|
|
3221
|
-
return current;
|
|
3222
|
-
}
|
|
3223
|
-
/** Return currently live descendants in one caller's subagent tree. */
|
|
3224
|
-
function activeDescendantIds(ctx, parentId) {
|
|
3225
|
-
const childrenByParent = /* @__PURE__ */ new Map();
|
|
3226
|
-
for (const agent of ctx.agents.list()) {
|
|
3227
|
-
if (agent.session.header.origin !== "subagent") continue;
|
|
3228
|
-
const directParent = agent.session.header.parentSession;
|
|
3229
|
-
if (directParent === void 0) continue;
|
|
3230
|
-
const children = childrenByParent.get(directParent) ?? [];
|
|
3231
|
-
children.push(agent.id);
|
|
3232
|
-
childrenByParent.set(directParent, children);
|
|
3233
|
-
}
|
|
3234
|
-
const descendants = /* @__PURE__ */ new Set();
|
|
3235
|
-
const pending = [...childrenByParent.get(parentId) ?? []];
|
|
3236
|
-
while (pending.length > 0) {
|
|
3237
|
-
const id = pending.pop();
|
|
3238
|
-
if (descendants.has(id)) continue;
|
|
3239
|
-
descendants.add(id);
|
|
3240
|
-
pending.push(...childrenByParent.get(id) ?? []);
|
|
3241
|
-
}
|
|
3242
|
-
return descendants;
|
|
3243
|
-
}
|
|
3244
|
-
function activeDescendantCount(ctx, parentId) {
|
|
3245
|
-
return activeDescendantIds(ctx, parentId).size;
|
|
3246
|
-
}
|
|
3247
|
-
//#endregion
|
|
3248
3290
|
//#region src/index.ts
|
|
3249
3291
|
const inject = [
|
|
3250
3292
|
"webServer",
|
|
@@ -3253,53 +3295,61 @@ const inject = [
|
|
|
3253
3295
|
"tools",
|
|
3254
3296
|
"web",
|
|
3255
3297
|
"settings",
|
|
3256
|
-
"loader"
|
|
3257
|
-
"agents"
|
|
3298
|
+
"loader"
|
|
3258
3299
|
];
|
|
3259
3300
|
function apply(ctx) {
|
|
3260
|
-
const
|
|
3261
|
-
const usage = new UsageService(oauth);
|
|
3301
|
+
const store = createPlatformTokenStore();
|
|
3262
3302
|
const preferences = registerPreferenceStore(ctx.settings);
|
|
3263
|
-
const adapter = new CodexChatGptAdapter(new ResponsesClient(oauth, ctx.attachments, {
|
|
3264
|
-
localRawImages: { baseUrl: localWebServerBaseUrl(ctx.webServer.host, ctx.webServer.port) },
|
|
3265
|
-
onGenerationFinished: () => usage.invalidate(),
|
|
3266
|
-
outputVerbosity: () => preferences.status().outputVerbosity,
|
|
3267
|
-
fastMode: () => preferences.status().fastMode
|
|
3268
|
-
}), preferences);
|
|
3269
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);
|
|
3270
3321
|
const searchSwitcher = new SearchProviderSwitcher(ctx.loader);
|
|
3271
3322
|
const applySearchPreference = (searchProvider = preferences.status().searchProvider) => {
|
|
3272
3323
|
searchSwitcher.select(searchProvider).catch((error) => {
|
|
3273
3324
|
ctx.logger.warn(`[dsh-chatgpt-subscription] Search provider preference could not be applied: ${error instanceof Error ? error.message : String(error)}`);
|
|
3274
3325
|
});
|
|
3275
3326
|
};
|
|
3276
|
-
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences);
|
|
3327
|
+
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager);
|
|
3277
3328
|
const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID], adapter);
|
|
3278
|
-
const
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
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();
|
|
3291
3343
|
const disposePreferenceWatch = preferences.watch((next) => applySearchPreference(next.searchProvider));
|
|
3292
3344
|
applySearchPreference();
|
|
3293
3345
|
return () => {
|
|
3294
3346
|
disposePreferenceWatch();
|
|
3295
|
-
|
|
3296
|
-
disposeSubagentModelPreference();
|
|
3297
|
-
disposeSearchProvider();
|
|
3347
|
+
disposeWebProviders();
|
|
3298
3348
|
disposeImageTool();
|
|
3299
|
-
disposeSubagentContextAdapter();
|
|
3300
3349
|
disposeAdapter();
|
|
3301
3350
|
disposeRoutes();
|
|
3302
3351
|
oauth.dispose();
|
|
3352
|
+
proxyManager.dispose();
|
|
3303
3353
|
};
|
|
3304
3354
|
}, "dsh-chatgpt-subscription: adapter, routes, and lifecycle");
|
|
3305
3355
|
}
|
|
@@ -3307,4 +3357,4 @@ function localWebServerBaseUrl(host, port) {
|
|
|
3307
3357
|
return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
|
|
3308
3358
|
}
|
|
3309
3359
|
//#endregion
|
|
3310
|
-
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 };
|