dsh-gpt-adapter 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -7
- package/README.zh-CN.md +48 -7
- package/dist/src/client/context-window.d.ts +9 -0
- package/dist/src/codex-config.d.ts +2 -1
- package/dist/src/codex-mcp-metadata.d.ts +12 -0
- package/dist/src/context-window.d.ts +25 -0
- package/dist/src/host.d.ts +15 -0
- package/dist/src/settings-overlay.d.ts +14 -2
- package/lib/client.js +282 -61
- package/lib/core.js +28 -1
- package/lib/index.js +514 -51
- package/package.json +5 -3
package/lib/index.js
CHANGED
|
@@ -196,6 +196,84 @@ function createRequestBridge(provider) {
|
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
// src/context-window.ts
|
|
200
|
+
var officialModels = [
|
|
201
|
+
["gpt-6-astra", 105e4],
|
|
202
|
+
["gpt-6.1-sol", 105e4],
|
|
203
|
+
["gpt-6.1-terra", 105e4],
|
|
204
|
+
["gpt-6-luna", 105e4],
|
|
205
|
+
["gpt-5.4", 105e4],
|
|
206
|
+
["gpt-4.1", 1047576],
|
|
207
|
+
["gpt-4o", 128e3],
|
|
208
|
+
["gpt-5.4-2026-03-05", 105e4, "gpt-5.4"],
|
|
209
|
+
["gpt-4.1-2025-04-14", 1047576, "gpt-4.1"],
|
|
210
|
+
["gpt-4o-2024-08-06", 128e3, "gpt-4o"]
|
|
211
|
+
];
|
|
212
|
+
var OFFICIAL_CONTEXT_WINDOWS = Object.fromEntries(
|
|
213
|
+
officialModels.map(([id, tokens, page]) => [
|
|
214
|
+
id,
|
|
215
|
+
{
|
|
216
|
+
tokens,
|
|
217
|
+
url: `https://developers.openai.com/api/docs/models/${page ?? id}`,
|
|
218
|
+
verifiedAt: "2026-11-13"
|
|
219
|
+
}
|
|
220
|
+
])
|
|
221
|
+
);
|
|
222
|
+
function validContextWindow(value) {
|
|
223
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
224
|
+
}
|
|
225
|
+
function contextWindowMode(tokens) {
|
|
226
|
+
return tokens === 516e3 ? "516k" : tokens === 1e6 ? "1m" : "custom";
|
|
227
|
+
}
|
|
228
|
+
function contextWindowTokens(mode, customTokens) {
|
|
229
|
+
if (!["default", "516k", "1m", "custom"].includes(mode))
|
|
230
|
+
throw new Error("\u8BF7\u9009\u62E9\u6709\u6548\u7684\u4E0A\u4E0B\u6587\u9884\u8BBE");
|
|
231
|
+
if (mode !== "custom" && customTokens !== void 0)
|
|
232
|
+
throw new Error("\u53EA\u6709\u81EA\u5B9A\u4E49\u9884\u8BBE\u63A5\u53D7\u7A97\u53E3\u6570\u503C");
|
|
233
|
+
if (mode === "custom") {
|
|
234
|
+
if (!validContextWindow(customTokens))
|
|
235
|
+
throw new Error("\u4E0A\u4E0B\u6587\u7A97\u53E3\u5FC5\u987B\u662F\u6B63\u7684\u5B89\u5168\u6574\u6570");
|
|
236
|
+
return customTokens;
|
|
237
|
+
}
|
|
238
|
+
return mode === "516k" ? 516e3 : mode === "1m" ? 1e6 : void 0;
|
|
239
|
+
}
|
|
240
|
+
function resolveContextWindow(options) {
|
|
241
|
+
const fallback = [
|
|
242
|
+
[options.providerDefault, "provider-default"],
|
|
243
|
+
[options.upstream, "upstream"],
|
|
244
|
+
[options.catalog, "capability-catalog"],
|
|
245
|
+
[
|
|
246
|
+
Object.hasOwn(OFFICIAL_CONTEXT_WINDOWS, options.modelId) ? OFFICIAL_CONTEXT_WINDOWS[options.modelId]?.tokens : void 0,
|
|
247
|
+
"official-preset"
|
|
248
|
+
],
|
|
249
|
+
[options.nativeDefault, "native-default"],
|
|
250
|
+
[1e6, "native-default"]
|
|
251
|
+
];
|
|
252
|
+
const [defaultTokens, source] = fallback.find(
|
|
253
|
+
([tokens]) => validContextWindow(tokens)
|
|
254
|
+
);
|
|
255
|
+
for (const [tokens, manualSource] of [
|
|
256
|
+
[options.legacy, "legacy-override"],
|
|
257
|
+
[options.native, "native-user"]
|
|
258
|
+
]) {
|
|
259
|
+
if (tokens === void 0) continue;
|
|
260
|
+
if (!validContextWindow(tokens))
|
|
261
|
+
throw new Error("\u4E0A\u4E0B\u6587\u7A97\u53E3\u5FC5\u987B\u662F\u6B63\u7684\u5B89\u5168\u6574\u6570");
|
|
262
|
+
return {
|
|
263
|
+
mode: contextWindowMode(tokens),
|
|
264
|
+
effectiveTokens: tokens,
|
|
265
|
+
defaultTokens,
|
|
266
|
+
source: manualSource
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
mode: "default",
|
|
271
|
+
effectiveTokens: defaultTokens,
|
|
272
|
+
defaultTokens,
|
|
273
|
+
source
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
199
277
|
// src/sync.ts
|
|
200
278
|
import { createHash } from "node:crypto";
|
|
201
279
|
var MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
@@ -373,7 +451,7 @@ function parseCapabilityHints(row, modelId) {
|
|
|
373
451
|
...typeof defaultReasoningValue === "string" ? { defaultReasoningId: defaultReasoningValue } : {},
|
|
374
452
|
...hasTierSignal ? { serviceTiers: tiers } : {},
|
|
375
453
|
...defaultTier ? { defaultServiceTier: defaultTier } : {},
|
|
376
|
-
...
|
|
454
|
+
...validContextWindow(contextWindow) ? { contextWindow } : {},
|
|
377
455
|
...typeof maxOutputTokens === "number" && maxOutputTokens > 0 ? { maxOutputTokens } : {}
|
|
378
456
|
};
|
|
379
457
|
return Object.keys(hints).length === 0 ? void 0 : hints;
|
|
@@ -735,6 +813,7 @@ import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
|
735
813
|
import { createRequire } from "node:module";
|
|
736
814
|
import { readFileSync } from "node:fs";
|
|
737
815
|
import { createHash as createHash6 } from "node:crypto";
|
|
816
|
+
import { getBuiltinModels } from "@earendil-works/pi-ai/providers/all";
|
|
738
817
|
|
|
739
818
|
// src/native-adapter.ts
|
|
740
819
|
function installNativeAdapterBridge(prototype, capture) {
|
|
@@ -759,6 +838,12 @@ function installNativeAdapterBridge(prototype, capture) {
|
|
|
759
838
|
});
|
|
760
839
|
const models = new Proxy(snapshot.models, {
|
|
761
840
|
get(target, key) {
|
|
841
|
+
if (key === "getModel" && typeof target.getModel === "function")
|
|
842
|
+
return (provider, modelId) => {
|
|
843
|
+
const model = target.getModel(provider, modelId);
|
|
844
|
+
const capacity = extension.model.context;
|
|
845
|
+
return model && provider === options.provider && modelId === options.model && capacity?.contextWindow ? { ...model, contextWindow: capacity.contextWindow } : model;
|
|
846
|
+
};
|
|
762
847
|
if (key === "streamSimple")
|
|
763
848
|
return (model, context, settings) => {
|
|
764
849
|
const call = extension.prepare(options);
|
|
@@ -853,11 +938,12 @@ function installNativeAdapterBridge(prototype, capture) {
|
|
|
853
938
|
// src/settings-overlay.ts
|
|
854
939
|
import z from "@deepseek-ai/schemastery";
|
|
855
940
|
import { symbols } from "@deepseek-ai/cordis";
|
|
856
|
-
function projectModel(provider, model, resolve4, profile) {
|
|
941
|
+
function projectModel(provider, model, resolve4, profile, includeContext = true) {
|
|
857
942
|
const capability = resolve4(provider, model, profile);
|
|
858
943
|
if (!capability) return model;
|
|
859
944
|
return {
|
|
860
945
|
...model,
|
|
946
|
+
...includeContext && validContextWindow(capability.contextWindow) ? { contextWindow: capability.contextWindow } : {},
|
|
861
947
|
reasoningEfforts: capability.reasoning.status === "unsupported" ? false : Object.fromEntries(
|
|
862
948
|
capability.reasoning.choices.map((choice) => [
|
|
863
949
|
choice.id,
|
|
@@ -903,7 +989,7 @@ function projectSchema(json, preserveNativeLevels) {
|
|
|
903
989
|
keys.list = offered.map((level) => z.const(level));
|
|
904
990
|
return schema.toJSON();
|
|
905
991
|
}
|
|
906
|
-
function nativeWrite(next, current, resolve4) {
|
|
992
|
+
function nativeWrite(next, current, resolve4, contextEdits) {
|
|
907
993
|
if (!next.providers) return next;
|
|
908
994
|
return {
|
|
909
995
|
...next,
|
|
@@ -932,6 +1018,21 @@ function nativeWrite(next, current, resolve4) {
|
|
|
932
1018
|
"GPT \u601D\u8003\u7B49\u7EA7\u7531\u63D2\u4EF6\u9884\u8BBE\u7BA1\u7406\uFF1B\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\uFF0C\u6216\u5728\u6A21\u578B\u80FD\u529B\u8986\u76D6\u4E2D\u4FEE\u6539\uFF0C\u505C\u7528\u81EA\u52A8\u9002\u914D\u53EF\u6062\u590D\u539F\u751F\u7F16\u8F91\u3002"
|
|
933
1019
|
);
|
|
934
1020
|
const restored = { ...model };
|
|
1021
|
+
const projectedContext = original ? projectModel(provider, original, resolve4, ownershipProfile).contextWindow : void 0;
|
|
1022
|
+
const editedContext = original ? model.contextWindow !== projectedContext : Object.hasOwn(model, "contextWindow");
|
|
1023
|
+
if (!editedContext) {
|
|
1024
|
+
if (original && Object.hasOwn(original, "contextWindow"))
|
|
1025
|
+
restored.contextWindow = original.contextWindow;
|
|
1026
|
+
else delete restored.contextWindow;
|
|
1027
|
+
} else {
|
|
1028
|
+
if (model.contextWindow !== void 0 && !validContextWindow(model.contextWindow))
|
|
1029
|
+
throw new Error("\u4E0A\u4E0B\u6587\u7A97\u53E3\u5FC5\u987B\u662F\u6B63\u7684\u5B89\u5168\u6574\u6570");
|
|
1030
|
+
contextEdits.push({
|
|
1031
|
+
provider,
|
|
1032
|
+
modelId: model.id,
|
|
1033
|
+
previousTokens: original?.contextWindow
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
935
1036
|
if (original && Object.hasOwn(original, "reasoningEfforts"))
|
|
936
1037
|
restored.reasoningEfforts = structuredClone(
|
|
937
1038
|
original.reasoningEfforts
|
|
@@ -945,10 +1046,11 @@ function nativeWrite(next, current, resolve4) {
|
|
|
945
1046
|
)
|
|
946
1047
|
};
|
|
947
1048
|
}
|
|
948
|
-
function installSettingsOverlay(settings, resolve4) {
|
|
1049
|
+
function installSettingsOverlay(settings, resolve4, contextWrites) {
|
|
949
1050
|
settings = settings[symbols.original] ?? settings;
|
|
950
1051
|
const originalDescribe = settings.describe;
|
|
951
1052
|
const originalWrite = settings.write;
|
|
1053
|
+
let viewRevision;
|
|
952
1054
|
function describe(options) {
|
|
953
1055
|
return originalDescribe.call(this, options).map((row) => {
|
|
954
1056
|
if (row.ns !== "llm-pi-ai") return row;
|
|
@@ -960,18 +1062,46 @@ function installSettingsOverlay(settings, resolve4) {
|
|
|
960
1062
|
for (const model of profile.models ?? [])
|
|
961
1063
|
if (resolve4(provider, model, profile)) managed = true;
|
|
962
1064
|
else other = true;
|
|
963
|
-
|
|
1065
|
+
const value = managed ? projectConfiguration(row.value, resolve4) : row.value;
|
|
1066
|
+
const contexts = JSON.stringify(
|
|
1067
|
+
Object.entries(value?.providers ?? {}).map(([provider, profile]) => [
|
|
1068
|
+
provider,
|
|
1069
|
+
profile.models?.map((model) => [
|
|
1070
|
+
model.id,
|
|
1071
|
+
model.contextWindow
|
|
1072
|
+
])
|
|
1073
|
+
])
|
|
1074
|
+
);
|
|
1075
|
+
if (typeof row.revision === "number") {
|
|
1076
|
+
if (!viewRevision)
|
|
1077
|
+
viewRevision = {
|
|
1078
|
+
native: row.revision,
|
|
1079
|
+
contexts,
|
|
1080
|
+
revision: row.revision
|
|
1081
|
+
};
|
|
1082
|
+
else if (viewRevision.native !== row.revision || viewRevision.contexts !== contexts)
|
|
1083
|
+
viewRevision = {
|
|
1084
|
+
native: row.revision,
|
|
1085
|
+
contexts,
|
|
1086
|
+
revision: Math.max(row.revision, viewRevision.revision + 1)
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
964
1089
|
return {
|
|
965
1090
|
...row,
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1091
|
+
...managed ? {
|
|
1092
|
+
schema: projectSchema(row.schema, other),
|
|
1093
|
+
value,
|
|
1094
|
+
base: projectConfiguration(row.base, resolve4),
|
|
1095
|
+
user: projectConfiguration(row.user, resolve4)
|
|
1096
|
+
} : {},
|
|
1097
|
+
...typeof row.revision === "number" ? { revision: viewRevision.revision } : {}
|
|
970
1098
|
};
|
|
971
1099
|
});
|
|
972
1100
|
}
|
|
973
1101
|
async function write(ns, change, expected, paths) {
|
|
974
|
-
|
|
1102
|
+
const edits = [];
|
|
1103
|
+
const legacyRevision = contextWrites?.revision();
|
|
1104
|
+
await originalWrite.call(
|
|
975
1105
|
this,
|
|
976
1106
|
ns,
|
|
977
1107
|
ns !== "llm-pi-ai" ? change : (current, base, schema) => nativeWrite(
|
|
@@ -981,19 +1111,88 @@ function installSettingsOverlay(settings, resolve4) {
|
|
|
981
1111
|
schema
|
|
982
1112
|
),
|
|
983
1113
|
current,
|
|
984
|
-
resolve4
|
|
1114
|
+
resolve4,
|
|
1115
|
+
edits
|
|
985
1116
|
),
|
|
986
1117
|
expected,
|
|
987
1118
|
paths
|
|
988
1119
|
);
|
|
1120
|
+
await finishContextWrite.call(this, ns, edits, legacyRevision, paths);
|
|
1121
|
+
}
|
|
1122
|
+
async function finishContextWrite(ns, edits, legacyRevision, paths) {
|
|
1123
|
+
if (ns !== "llm-pi-ai" || !edits.length || !contextWrites) return;
|
|
1124
|
+
const committedRevision = describe.call(this).find((row) => row.ns === ns)?.revision;
|
|
1125
|
+
try {
|
|
1126
|
+
await contextWrites.saved(edits, legacyRevision);
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
try {
|
|
1129
|
+
await originalWrite.call(
|
|
1130
|
+
this,
|
|
1131
|
+
ns,
|
|
1132
|
+
(current) => {
|
|
1133
|
+
const restored = structuredClone(current);
|
|
1134
|
+
for (const edit of edits) {
|
|
1135
|
+
const row = restored.providers?.[edit.provider]?.models?.find(
|
|
1136
|
+
(model) => model.id === edit.modelId
|
|
1137
|
+
);
|
|
1138
|
+
if (!row) continue;
|
|
1139
|
+
if (edit.previousTokens === void 0) delete row.contextWindow;
|
|
1140
|
+
else row.contextWindow = edit.previousTokens;
|
|
1141
|
+
}
|
|
1142
|
+
return restored;
|
|
1143
|
+
},
|
|
1144
|
+
committedRevision,
|
|
1145
|
+
paths
|
|
1146
|
+
);
|
|
1147
|
+
} catch (rollbackError) {
|
|
1148
|
+
throw new AggregateError(
|
|
1149
|
+
[error, rollbackError],
|
|
1150
|
+
"\u4E0A\u4E0B\u6587\u4FDD\u5B58\u672A\u5B8C\u6210\uFF0C\u6062\u590D\u5931\u8D25\uFF1B\u8BF7\u5237\u65B0\u67E5\u770B\u5F53\u524D\u8BBE\u7F6E"
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
throw error;
|
|
1154
|
+
}
|
|
989
1155
|
}
|
|
990
1156
|
settings.describe = describe;
|
|
991
1157
|
if (originalWrite) settings.write = write;
|
|
992
|
-
|
|
1158
|
+
const dispose = () => {
|
|
993
1159
|
if (settings.describe === describe) settings.describe = originalDescribe;
|
|
994
1160
|
if (originalWrite && settings.write === write)
|
|
995
1161
|
settings.write = originalWrite;
|
|
996
1162
|
};
|
|
1163
|
+
const setContextWindow = async (provider, modelId, tokens, expected) => {
|
|
1164
|
+
if (!originalWrite) throw new Error("\u4E0A\u4E0B\u6587\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528");
|
|
1165
|
+
if (tokens !== void 0 && !validContextWindow(tokens))
|
|
1166
|
+
throw new Error("\u4E0A\u4E0B\u6587\u7A97\u53E3\u5FC5\u987B\u662F\u6B63\u7684\u5B89\u5168\u6574\u6570");
|
|
1167
|
+
const edits = [];
|
|
1168
|
+
const legacyRevision = contextWrites?.revision();
|
|
1169
|
+
const paths = [["providers", provider, "models"]];
|
|
1170
|
+
await originalWrite.call(
|
|
1171
|
+
settings,
|
|
1172
|
+
"llm-pi-ai",
|
|
1173
|
+
(current) => {
|
|
1174
|
+
const next = structuredClone(current);
|
|
1175
|
+
const model = next.providers?.[provider]?.models?.find(
|
|
1176
|
+
(row) => row.id === modelId
|
|
1177
|
+
);
|
|
1178
|
+
if (!model) throw new Error("\u6A21\u578B\u5DF2\u53D8\u66F4\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5");
|
|
1179
|
+
edits.push({ provider, modelId, previousTokens: model.contextWindow });
|
|
1180
|
+
if (tokens === void 0) delete model.contextWindow;
|
|
1181
|
+
else model.contextWindow = tokens;
|
|
1182
|
+
return next;
|
|
1183
|
+
},
|
|
1184
|
+
expected,
|
|
1185
|
+
paths
|
|
1186
|
+
);
|
|
1187
|
+
await finishContextWrite.call(
|
|
1188
|
+
settings,
|
|
1189
|
+
"llm-pi-ai",
|
|
1190
|
+
edits,
|
|
1191
|
+
legacyRevision,
|
|
1192
|
+
paths
|
|
1193
|
+
);
|
|
1194
|
+
};
|
|
1195
|
+
return Object.assign(dispose, { setContextWindow });
|
|
997
1196
|
}
|
|
998
1197
|
|
|
999
1198
|
// src/codex.ts
|
|
@@ -1082,7 +1281,7 @@ function serverName(name2) {
|
|
|
1082
1281
|
const hash = createHash3("sha256").update(name2).digest("hex").slice(0, 8);
|
|
1083
1282
|
return `${name2.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 23)}-${hash}`;
|
|
1084
1283
|
}
|
|
1085
|
-
function normalizeCodexMcp(name2, value, source, base, environment = process.env) {
|
|
1284
|
+
function normalizeCodexMcp(name2, value, source, base, environment = process.env, defaultCwd = base) {
|
|
1086
1285
|
const raw = object(value);
|
|
1087
1286
|
if (raw.enabled === false) return void 0;
|
|
1088
1287
|
const headers = record2(raw.http_headers ?? raw.headers);
|
|
@@ -1131,8 +1330,8 @@ function normalizeCodexMcp(name2, value, source, base, environment = process.env
|
|
|
1131
1330
|
if (environment[variable] !== void 0 && env[variable] === void 0)
|
|
1132
1331
|
env[variable] = environment[variable];
|
|
1133
1332
|
}
|
|
1134
|
-
const cwd = typeof raw.cwd === "string" ? resolve(base, raw.cwd) :
|
|
1135
|
-
const command = raw.command.includes("/") && !isAbsolute(raw.command) ? resolve(cwd, raw.command) : raw.command;
|
|
1333
|
+
const cwd = typeof raw.cwd === "string" ? resolve(base, raw.cwd) : defaultCwd;
|
|
1334
|
+
const command = raw.command.includes("/") && !isAbsolute(raw.command) ? resolve(typeof raw.cwd === "string" ? cwd : base, raw.command) : raw.command;
|
|
1136
1335
|
return {
|
|
1137
1336
|
...common,
|
|
1138
1337
|
transport: "stdio",
|
|
@@ -1151,9 +1350,29 @@ async function directories(path) {
|
|
|
1151
1350
|
}
|
|
1152
1351
|
}
|
|
1153
1352
|
async function manifestAt(root) {
|
|
1353
|
+
const text = await optionalText(join2(root, "plugin.json"));
|
|
1354
|
+
if (text !== void 0) {
|
|
1355
|
+
const manifest = object(JSON.parse(text));
|
|
1356
|
+
if (manifest.$schema === "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json") {
|
|
1357
|
+
const inline = manifest.extensions?.["com.openai"];
|
|
1358
|
+
const overlay = inline !== null && typeof inline === "object" && !Array.isArray(inline) ? inline : object(
|
|
1359
|
+
JSON.parse(
|
|
1360
|
+
await optionalText(
|
|
1361
|
+
join2(root, ".codex-plugin", "plugin.json")
|
|
1362
|
+
) ?? "{}"
|
|
1363
|
+
)
|
|
1364
|
+
);
|
|
1365
|
+
return {
|
|
1366
|
+
root,
|
|
1367
|
+
manifest: { ...manifest, ...overlay, name: manifest.name },
|
|
1368
|
+
portable: true
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1154
1372
|
for (const name2 of [".codex-plugin", ".claude-plugin"]) {
|
|
1155
|
-
const
|
|
1156
|
-
if (
|
|
1373
|
+
const text2 = await optionalText(join2(root, name2, "plugin.json"));
|
|
1374
|
+
if (text2 !== void 0)
|
|
1375
|
+
return { root, manifest: object(JSON.parse(text2)), portable: false };
|
|
1157
1376
|
}
|
|
1158
1377
|
return void 0;
|
|
1159
1378
|
}
|
|
@@ -1182,7 +1401,7 @@ async function resolvePlugin(home, id, config) {
|
|
|
1182
1401
|
}
|
|
1183
1402
|
function expandPlugin(value, root, home) {
|
|
1184
1403
|
if (typeof value === "string")
|
|
1185
|
-
return value.replaceAll("${CLAUDE_PLUGIN_ROOT}", root).replaceAll("${CODEX_PLUGIN_ROOT}", root).replaceAll("${CODEX_HOME}", home);
|
|
1404
|
+
return value.replaceAll("${PLUGIN_ROOT}", root).replaceAll("${CLAUDE_PLUGIN_ROOT}", root).replaceAll("${CODEX_PLUGIN_ROOT}", root).replaceAll("${CODEX_HOME}", home);
|
|
1186
1405
|
if (Array.isArray(value)) return value.map((v) => expandPlugin(v, root, home));
|
|
1187
1406
|
if (value && typeof value === "object")
|
|
1188
1407
|
return Object.fromEntries(
|
|
@@ -1190,6 +1409,22 @@ function expandPlugin(value, root, home) {
|
|
|
1190
1409
|
);
|
|
1191
1410
|
return value;
|
|
1192
1411
|
}
|
|
1412
|
+
function restrictMcpFallback(entry, policy) {
|
|
1413
|
+
if (policy.enabled === false) return void 0;
|
|
1414
|
+
const requested = Array.isArray(policy.enabled_tools) ? strings(policy.enabled_tools) : void 0;
|
|
1415
|
+
const enabledTools = requested && entry.enabledTools ? entry.enabledTools.filter((name2) => requested.includes(name2)) : requested ?? entry.enabledTools;
|
|
1416
|
+
return {
|
|
1417
|
+
...entry,
|
|
1418
|
+
...enabledTools ? { enabledTools } : {},
|
|
1419
|
+
disabledTools: [
|
|
1420
|
+
.../* @__PURE__ */ new Set([
|
|
1421
|
+
...entry.disabledTools,
|
|
1422
|
+
...strings(policy.disabled_tools),
|
|
1423
|
+
...Object.entries(object(policy.tools)).filter(([, tool]) => object(tool).enabled === false).map(([name2]) => name2)
|
|
1424
|
+
])
|
|
1425
|
+
]
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1193
1428
|
async function discoverCodexSnapshot(home, options, context = {}) {
|
|
1194
1429
|
const config = await readCodexConfig(
|
|
1195
1430
|
home,
|
|
@@ -1207,24 +1442,38 @@ async function discoverCodexSnapshot(home, options, context = {}) {
|
|
|
1207
1442
|
items: []
|
|
1208
1443
|
};
|
|
1209
1444
|
const names = /* @__PURE__ */ new Set();
|
|
1210
|
-
const addServers = (servers, source, base, plugin = "") => {
|
|
1211
|
-
for (const [name2,
|
|
1445
|
+
const addServers = (servers, source, base, plugin = "", policies = {}) => {
|
|
1446
|
+
for (const [name2, declaration] of Object.entries(servers)) {
|
|
1447
|
+
const policy = Object.fromEntries(
|
|
1448
|
+
Object.entries(object(policies[name2])).filter(
|
|
1449
|
+
([key]) => ["enabled", "enabled_tools", "disabled_tools", "tools"].includes(key)
|
|
1450
|
+
)
|
|
1451
|
+
);
|
|
1452
|
+
const raw = mergeConfig(object(declaration), policy);
|
|
1212
1453
|
const publicName = plugin ? `${plugin}-${name2}` : name2;
|
|
1213
1454
|
if (object(raw).enabled === false || options.mcpAllowlist.length && !options.mcpAllowlist.includes(name2)) {
|
|
1214
1455
|
result.items.push({ name: publicName, kind: "mcp", state: "disabled" });
|
|
1215
1456
|
continue;
|
|
1216
1457
|
}
|
|
1217
1458
|
try {
|
|
1218
|
-
const value = normalizeCodexMcp(
|
|
1459
|
+
const value = normalizeCodexMcp(
|
|
1460
|
+
publicName,
|
|
1461
|
+
raw,
|
|
1462
|
+
source,
|
|
1463
|
+
base,
|
|
1464
|
+
process.env,
|
|
1465
|
+
context.cwd ?? base
|
|
1466
|
+
);
|
|
1219
1467
|
if (!value) continue;
|
|
1220
1468
|
if (names.has(value.serverName)) throw new Error("namespace-conflict");
|
|
1221
1469
|
names.add(value.serverName);
|
|
1222
|
-
result.mcp.push(value);
|
|
1470
|
+
result.mcp.push({ ...value, originalName: name2 });
|
|
1223
1471
|
} catch (error) {
|
|
1224
1472
|
const previous = context.previous?.mcp.find(
|
|
1225
1473
|
(entry) => entry.serverName === serverName(publicName)
|
|
1226
1474
|
);
|
|
1227
|
-
|
|
1475
|
+
const retained = previous && restrictMcpFallback(previous, raw);
|
|
1476
|
+
if (retained) result.mcp.push(retained);
|
|
1228
1477
|
const reason = [
|
|
1229
1478
|
"missing-environment",
|
|
1230
1479
|
"unsupported-transport",
|
|
@@ -1258,9 +1507,9 @@ async function discoverCodexSnapshot(home, options, context = {}) {
|
|
|
1258
1507
|
});
|
|
1259
1508
|
continue;
|
|
1260
1509
|
}
|
|
1261
|
-
const { root, manifest } = plugin;
|
|
1510
|
+
const { root, manifest, portable } = plugin;
|
|
1262
1511
|
const name2 = String(manifest.name ?? id.split("@")[0]);
|
|
1263
|
-
const declared = manifest.skills ?? "./skills";
|
|
1512
|
+
const declared = portable ? "./skills" : manifest.skills ?? "./skills";
|
|
1264
1513
|
const skillRoots = [];
|
|
1265
1514
|
for (const entry of Array.isArray(declared) ? declared : [declared]) {
|
|
1266
1515
|
if (typeof entry === "string")
|
|
@@ -1271,7 +1520,10 @@ async function discoverCodexSnapshot(home, options, context = {}) {
|
|
|
1271
1520
|
});
|
|
1272
1521
|
}
|
|
1273
1522
|
let servers = manifest.mcpServers;
|
|
1274
|
-
if (
|
|
1523
|
+
if (portable) {
|
|
1524
|
+
const text = await optionalText(join2(root, "mcp.json"));
|
|
1525
|
+
servers = text === void 0 ? void 0 : JSON.parse(text);
|
|
1526
|
+
} else if (typeof servers === "string") {
|
|
1275
1527
|
const text = await optionalText(resolve(root, servers));
|
|
1276
1528
|
if (text === void 0) throw new Error("missing-plugin-mcp-file");
|
|
1277
1529
|
servers = JSON.parse(text);
|
|
@@ -1285,7 +1537,13 @@ async function discoverCodexSnapshot(home, options, context = {}) {
|
|
|
1285
1537
|
home
|
|
1286
1538
|
);
|
|
1287
1539
|
result.skillRoots.push(...skillRoots);
|
|
1288
|
-
addServers(
|
|
1540
|
+
addServers(
|
|
1541
|
+
object(expanded),
|
|
1542
|
+
`plugin:${id}`,
|
|
1543
|
+
root,
|
|
1544
|
+
name2,
|
|
1545
|
+
object(object(setting).mcp_servers)
|
|
1546
|
+
);
|
|
1289
1547
|
result.items.push({ name: id, kind: "plugin", state: "loaded" });
|
|
1290
1548
|
for (const field of ["hooks", "apps", "commands"]) {
|
|
1291
1549
|
if (manifest[field])
|
|
@@ -1297,11 +1555,16 @@ async function discoverCodexSnapshot(home, options, context = {}) {
|
|
|
1297
1555
|
});
|
|
1298
1556
|
}
|
|
1299
1557
|
} catch {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
)
|
|
1304
|
-
|
|
1558
|
+
const policies = object(object(setting).mcp_servers);
|
|
1559
|
+
for (const entry of context.previous?.mcp ?? []) {
|
|
1560
|
+
if (entry.source !== `plugin:${id}`) continue;
|
|
1561
|
+
if (!entry.originalName && Object.keys(policies).length) continue;
|
|
1562
|
+
const retained = restrictMcpFallback(
|
|
1563
|
+
entry,
|
|
1564
|
+
object(policies[entry.originalName ?? ""])
|
|
1565
|
+
);
|
|
1566
|
+
if (retained) result.mcp.push(retained);
|
|
1567
|
+
}
|
|
1305
1568
|
result.skillRoots.push(
|
|
1306
1569
|
...context.previous?.skillRoots.filter(
|
|
1307
1570
|
(entry) => entry.pluginId === id
|
|
@@ -1548,6 +1811,49 @@ function installCodexInstructions(ctx, home) {
|
|
|
1548
1811
|
);
|
|
1549
1812
|
}
|
|
1550
1813
|
|
|
1814
|
+
// src/codex-mcp-metadata.ts
|
|
1815
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1816
|
+
import { Client } from "@modelcontextprotocol/client";
|
|
1817
|
+
var scope = new AsyncLocalStorage();
|
|
1818
|
+
var users = 0;
|
|
1819
|
+
var restore;
|
|
1820
|
+
function installCodexMcpMetadata() {
|
|
1821
|
+
if (users++ === 0) {
|
|
1822
|
+
const original = Client.prototype.callTool;
|
|
1823
|
+
const callTool = function(params, options) {
|
|
1824
|
+
const call = scope.getStore();
|
|
1825
|
+
const metadata = call?.active() ? call.metadata : void 0;
|
|
1826
|
+
return original.call(
|
|
1827
|
+
this,
|
|
1828
|
+
metadata ? {
|
|
1829
|
+
...params,
|
|
1830
|
+
_meta: { ...params._meta, "x-codex-turn-metadata": metadata }
|
|
1831
|
+
} : params,
|
|
1832
|
+
options
|
|
1833
|
+
);
|
|
1834
|
+
};
|
|
1835
|
+
Client.prototype.callTool = callTool;
|
|
1836
|
+
restore = () => {
|
|
1837
|
+
if (Client.prototype.callTool === callTool)
|
|
1838
|
+
Client.prototype.callTool = original;
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
let active = true;
|
|
1842
|
+
return {
|
|
1843
|
+
run(metadata, operation) {
|
|
1844
|
+
return scope.run({ active: () => active, metadata }, operation);
|
|
1845
|
+
},
|
|
1846
|
+
dispose() {
|
|
1847
|
+
if (!active) return;
|
|
1848
|
+
active = false;
|
|
1849
|
+
if (--users === 0) {
|
|
1850
|
+
restore?.();
|
|
1851
|
+
restore = void 0;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1551
1857
|
// src/codex.ts
|
|
1552
1858
|
var fingerprint = (v) => createHash5("sha256").update(JSON.stringify(v)).digest("hex");
|
|
1553
1859
|
function publicToolName(server, name2) {
|
|
@@ -1629,6 +1935,7 @@ function installCodexCompatibility(ctx, config) {
|
|
|
1629
1935
|
dispose: async () => {
|
|
1630
1936
|
}
|
|
1631
1937
|
};
|
|
1938
|
+
const metadata = config.importMcpServers ? installCodexMcpMetadata() : void 0;
|
|
1632
1939
|
const scopedSnapshot = async (cwd, signal, fallback) => {
|
|
1633
1940
|
const key = resolve3(cwd);
|
|
1634
1941
|
const previous = scopedSnapshots.get(key) ?? fallback ?? latest;
|
|
@@ -1841,7 +2148,22 @@ function installCodexCompatibility(ctx, config) {
|
|
|
1841
2148
|
if (enabledTools && !enabledTools.includes(definition.name) || disabledTools.includes(definition.name))
|
|
1842
2149
|
return () => {
|
|
1843
2150
|
};
|
|
1844
|
-
return target.register(
|
|
2151
|
+
return target.register({
|
|
2152
|
+
...definition,
|
|
2153
|
+
execute(args, execution) {
|
|
2154
|
+
const agent = execution.agent ?? owner.agent;
|
|
2155
|
+
const boundary = agent.session ? child.agents.turnBoundaryOf?.(agent.session) : void 0;
|
|
2156
|
+
const turn = boundary?.openTurnStartSeq;
|
|
2157
|
+
const turnMetadata = typeof agent.id === "string" && agent.id && turn != null ? {
|
|
2158
|
+
session_id: agent.id,
|
|
2159
|
+
turn_id: `${agent.id}:${turn}`
|
|
2160
|
+
} : void 0;
|
|
2161
|
+
return metadata.run(
|
|
2162
|
+
turnMetadata,
|
|
2163
|
+
() => definition.execute(args, execution)
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
1845
2167
|
};
|
|
1846
2168
|
const value = Reflect.get(target, key);
|
|
1847
2169
|
return typeof value === "function" ? value.bind(target) : value;
|
|
@@ -1984,9 +2306,15 @@ function installCodexCompatibility(ctx, config) {
|
|
|
1984
2306
|
agents.delete(agent);
|
|
1985
2307
|
await owner.tail.catch(() => {
|
|
1986
2308
|
});
|
|
1987
|
-
await Promise.
|
|
1988
|
-
[...owner.clients.values()].map((client) => client.fiber.dispose())
|
|
2309
|
+
const results = await Promise.allSettled(
|
|
2310
|
+
[...owner.clients.values()].map(async (client) => client.fiber.dispose())
|
|
2311
|
+
);
|
|
2312
|
+
const errors = results.flatMap(
|
|
2313
|
+
(result) => result.status === "rejected" ? [result.reason] : []
|
|
1989
2314
|
);
|
|
2315
|
+
if (errors.length === 1) throw errors[0];
|
|
2316
|
+
if (errors.length > 1)
|
|
2317
|
+
throw new AggregateError(errors, "Codex MCP client cleanup failed");
|
|
1990
2318
|
};
|
|
1991
2319
|
const reconcile = async () => {
|
|
1992
2320
|
if (closed) return;
|
|
@@ -2082,11 +2410,25 @@ function installCodexCompatibility(ctx, config) {
|
|
|
2082
2410
|
const dispose = () => stopping ??= (async () => {
|
|
2083
2411
|
closed = true;
|
|
2084
2412
|
if (timer) clearInterval(timer);
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2413
|
+
const errors = [];
|
|
2414
|
+
const settle = async (actions) => {
|
|
2415
|
+
const results = await Promise.allSettled(
|
|
2416
|
+
actions.map(async (action) => action())
|
|
2417
|
+
);
|
|
2418
|
+
for (const result of results)
|
|
2419
|
+
if (result.status === "rejected") errors.push(result.reason);
|
|
2420
|
+
};
|
|
2421
|
+
await settle(injections.map((fiber) => () => fiber.dispose()));
|
|
2422
|
+
await settle([() => tail]);
|
|
2423
|
+
await settle([...agents.keys()].map((agent) => () => disposeAgent(agent)));
|
|
2424
|
+
await settle([
|
|
2425
|
+
() => metadata?.dispose(),
|
|
2426
|
+
() => skillDispose?.(),
|
|
2427
|
+
...skills.map((provider) => () => provider.dispose())
|
|
2428
|
+
]);
|
|
2429
|
+
if (errors.length === 1) throw errors[0];
|
|
2430
|
+
if (errors.length > 1)
|
|
2431
|
+
throw new AggregateError(errors, "Codex compatibility cleanup failed");
|
|
2090
2432
|
})();
|
|
2091
2433
|
ctx.effect(() => dispose, "codex-compatibility");
|
|
2092
2434
|
return {
|
|
@@ -2156,14 +2498,15 @@ var Config2 = z2.object({
|
|
|
2156
2498
|
mcpAllowlist: z2.array(String).default([])
|
|
2157
2499
|
}).default({})
|
|
2158
2500
|
});
|
|
2159
|
-
var _speed_dec, _setSpeed_dec, _removeStale_dec, _refresh_dec, _updateOptions_dec, _configure_dec, _status_dec, _refreshCodex_dec, _codexStatus_dec, _a, _init;
|
|
2160
|
-
var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refreshCodex_dec = [Remote], _status_dec = [Remote], _configure_dec = [Remote], _updateOptions_dec = [Remote], _refresh_dec = [Remote], _removeStale_dec = [Remote], _setSpeed_dec = [Remote({ resources: { session: (sessionId) => sessionId } })], _speed_dec = [Remote({ resources: { session: (sessionId) => sessionId } })], _a) {
|
|
2501
|
+
var _speed_dec, _setSpeed_dec, _setContextWindow_dec, _removeStale_dec, _refresh_dec, _updateOptions_dec, _configure_dec, _status_dec, _refreshCodex_dec, _codexStatus_dec, _a, _init;
|
|
2502
|
+
var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refreshCodex_dec = [Remote], _status_dec = [Remote], _configure_dec = [Remote], _updateOptions_dec = [Remote], _refresh_dec = [Remote], _removeStale_dec = [Remote], _setContextWindow_dec = [Remote], _setSpeed_dec = [Remote({ resources: { session: (sessionId) => sessionId } })], _speed_dec = [Remote({ resources: { session: (sessionId) => sessionId } })], _a) {
|
|
2161
2503
|
constructor(ctx, options, settingsNs) {
|
|
2162
2504
|
super(ctx, "gptModels");
|
|
2163
2505
|
this.options = options;
|
|
2164
2506
|
this.settingsNs = settingsNs;
|
|
2165
2507
|
__runInitializers(_init, 5, this);
|
|
2166
2508
|
__publicField(this, "codex");
|
|
2509
|
+
__publicField(this, "contextWriter");
|
|
2167
2510
|
__publicField(this, "typertRemote");
|
|
2168
2511
|
__publicField(this, "runners", /* @__PURE__ */ new Map());
|
|
2169
2512
|
__publicField(this, "snapshots", /* @__PURE__ */ new Map());
|
|
@@ -2291,11 +2634,11 @@ var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refre
|
|
|
2291
2634
|
}
|
|
2292
2635
|
async publish(provider, snapshot) {
|
|
2293
2636
|
if (this.closed) return;
|
|
2294
|
-
const {
|
|
2637
|
+
const { profile } = this.profile(provider);
|
|
2295
2638
|
const existing = profile.models ?? [];
|
|
2296
2639
|
const ids = new Set(existing.map((model) => model.id));
|
|
2297
2640
|
const added = snapshot.models.filter((model) => !model.stale && !ids.has(model.id)).map((model) => ({ id: model.id, name: model.displayName ?? model.id }));
|
|
2298
|
-
if (added.length) {
|
|
2641
|
+
if (added.length && !Object.keys(profile.modelOverrides ?? {}).length) {
|
|
2299
2642
|
await this.ctx.settings.mutate(
|
|
2300
2643
|
"llm-pi-ai",
|
|
2301
2644
|
[
|
|
@@ -2306,12 +2649,14 @@ var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refre
|
|
|
2306
2649
|
(model) => projectModel(
|
|
2307
2650
|
provider,
|
|
2308
2651
|
model,
|
|
2309
|
-
(route, row) => this.capability(route, row.id, row.name)
|
|
2652
|
+
(route, row) => this.capability(route, row.id, row.name),
|
|
2653
|
+
void 0,
|
|
2654
|
+
ids.has(model.id)
|
|
2310
2655
|
)
|
|
2311
2656
|
)
|
|
2312
2657
|
}
|
|
2313
2658
|
],
|
|
2314
|
-
|
|
2659
|
+
this.modelsRevision()
|
|
2315
2660
|
);
|
|
2316
2661
|
}
|
|
2317
2662
|
if (this.closed) return;
|
|
@@ -2347,6 +2692,15 @@ var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refre
|
|
|
2347
2692
|
stale: model.stale ?? false
|
|
2348
2693
|
})) ?? [],
|
|
2349
2694
|
capabilities,
|
|
2695
|
+
contextWindows: Object.fromEntries(
|
|
2696
|
+
this.routeOptions(provider)?.enabled ? (this.profile(provider).profile.models ?? []).filter(
|
|
2697
|
+
(model) => this.capability(provider, model.id) !== void 0
|
|
2698
|
+
).map((model) => [
|
|
2699
|
+
model.id,
|
|
2700
|
+
this.contextWindow(provider, model.id)
|
|
2701
|
+
]) : []
|
|
2702
|
+
),
|
|
2703
|
+
contextRevision: this.modelsRevision(),
|
|
2350
2704
|
options: this.routeOptions(provider) ?? {
|
|
2351
2705
|
enabled: false,
|
|
2352
2706
|
refreshIntervalMs: 18e5,
|
|
@@ -2387,6 +2741,10 @@ var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refre
|
|
|
2387
2741
|
const overrides = JSON.parse(overridesJSON);
|
|
2388
2742
|
if (!overrides || typeof overrides !== "object" || Array.isArray(overrides))
|
|
2389
2743
|
throw new Error("\u6A21\u578B\u8986\u76D6\u9700\u8981\u662F JSON \u5BF9\u8C61");
|
|
2744
|
+
for (const entry of Object.values(overrides)) {
|
|
2745
|
+
if (entry && typeof entry === "object" && "contextWindow" in entry && !validContextWindow(entry.contextWindow))
|
|
2746
|
+
throw new Error("\u4E0A\u4E0B\u6587\u7A97\u53E3\u5FC5\u987B\u662F\u6B63\u7684\u5B89\u5168\u6574\u6570");
|
|
2747
|
+
}
|
|
2390
2748
|
const descriptor = this.ctx.settings.describe().find((entry) => entry.ns === this.settingsNs);
|
|
2391
2749
|
if (!descriptor) throw new Error("GPT plugin settings unavailable");
|
|
2392
2750
|
await this.ctx.settings.mutate(
|
|
@@ -2416,7 +2774,7 @@ var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refre
|
|
|
2416
2774
|
const runner = this.runners.get(provider);
|
|
2417
2775
|
if (!runner) throw new Error("Enable GPT adaptation first");
|
|
2418
2776
|
const staleIds = runner.current()?.models.filter((model) => model.stale).map((model) => model.id) ?? [];
|
|
2419
|
-
const {
|
|
2777
|
+
const { profile } = this.profile(provider);
|
|
2420
2778
|
const nextModels = profile.models?.filter(
|
|
2421
2779
|
(model) => !staleIds.includes(model.id)
|
|
2422
2780
|
);
|
|
@@ -2431,15 +2789,101 @@ var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refre
|
|
|
2431
2789
|
(model) => projectModel(
|
|
2432
2790
|
provider,
|
|
2433
2791
|
model,
|
|
2434
|
-
(route, row) => this.capability(route, row.id, row.name)
|
|
2792
|
+
(route, row) => this.capability(route, row.id, row.name),
|
|
2793
|
+
void 0
|
|
2435
2794
|
)
|
|
2436
2795
|
)
|
|
2437
2796
|
}
|
|
2438
2797
|
],
|
|
2439
|
-
|
|
2798
|
+
this.modelsRevision()
|
|
2440
2799
|
);
|
|
2441
2800
|
await runner.removeStale(staleIds);
|
|
2442
2801
|
}
|
|
2802
|
+
/** Resolve raw metadata separately from merged capabilities and cached overrides. */
|
|
2803
|
+
contextWindow(provider, modelId, nativeDefault) {
|
|
2804
|
+
const { descriptor, profile } = this.profile(provider);
|
|
2805
|
+
const overrides = JSON.parse(
|
|
2806
|
+
this.routeOptions(provider, profile)?.overridesJSON ?? "{}"
|
|
2807
|
+
);
|
|
2808
|
+
const row = profile.models?.length ? profile.models.find((model) => model.id === modelId) : profile.modelOverrides?.[modelId];
|
|
2809
|
+
const snapshot = this.snapshots.get(provider);
|
|
2810
|
+
const raw = snapshot?.models.find((model) => model.id === modelId);
|
|
2811
|
+
const userProfile = descriptor.user?.providers?.[provider];
|
|
2812
|
+
const providerDefault = userProfile?.defaultContextWindow ?? (profile.defaultContextWindow !== 1e6 || descriptor.base === void 0 && descriptor.user === void 0 ? profile.defaultContextWindow : void 0);
|
|
2813
|
+
const catalogModel = getBuiltinModels(provider).find(
|
|
2814
|
+
(model) => model.id === modelId
|
|
2815
|
+
);
|
|
2816
|
+
return resolveContextWindow({
|
|
2817
|
+
modelId,
|
|
2818
|
+
legacy: overrides[modelId]?.contextWindow,
|
|
2819
|
+
native: row?.contextWindow,
|
|
2820
|
+
providerDefault,
|
|
2821
|
+
upstream: raw?.capabilities?.contextWindow,
|
|
2822
|
+
catalog: snapshot?.catalog?.models[modelId]?.contextWindow,
|
|
2823
|
+
nativeDefault: nativeDefault ?? catalogModel?.contextWindow ?? profile.defaultContextWindow
|
|
2824
|
+
});
|
|
2825
|
+
}
|
|
2826
|
+
contextWriteRevision() {
|
|
2827
|
+
return this.nativeDescribe().find((row) => row.ns === this.settingsNs)?.revision;
|
|
2828
|
+
}
|
|
2829
|
+
/** Public form revisions also cover changes to projected model capacities. */
|
|
2830
|
+
modelsRevision() {
|
|
2831
|
+
const descriptor = this.ctx.settings.describe().find((row) => row.ns === "llm-pi-ai");
|
|
2832
|
+
if (!descriptor) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528");
|
|
2833
|
+
return descriptor.revision;
|
|
2834
|
+
}
|
|
2835
|
+
/** Remove only legacy context fields after the matching native save commits. */
|
|
2836
|
+
async clearLegacyContext(edits, expected) {
|
|
2837
|
+
const operations = [];
|
|
2838
|
+
for (const provider of new Set(edits.map((edit) => edit.provider))) {
|
|
2839
|
+
const text = this.options.routes[provider]?.overridesJSON;
|
|
2840
|
+
if (!text) continue;
|
|
2841
|
+
const overrides = JSON.parse(text);
|
|
2842
|
+
let changed = false;
|
|
2843
|
+
for (const edit of edits.filter((entry) => entry.provider === provider)) {
|
|
2844
|
+
if (overrides[edit.modelId] && Object.hasOwn(overrides[edit.modelId], "contextWindow")) {
|
|
2845
|
+
delete overrides[edit.modelId].contextWindow;
|
|
2846
|
+
changed = true;
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
if (changed)
|
|
2850
|
+
operations.push({
|
|
2851
|
+
op: "set",
|
|
2852
|
+
path: ["routes", provider, "overridesJSON"],
|
|
2853
|
+
value: JSON.stringify(overrides)
|
|
2854
|
+
});
|
|
2855
|
+
}
|
|
2856
|
+
if (operations.length)
|
|
2857
|
+
await this.ctx.settings.mutate(this.settingsNs, operations, expected);
|
|
2858
|
+
}
|
|
2859
|
+
async setContextWindow(provider, modelId, mode, customTokens, expectedRevision) {
|
|
2860
|
+
const tokens = contextWindowTokens(mode, customTokens);
|
|
2861
|
+
const { profile } = this.profile(provider);
|
|
2862
|
+
if (!this.capability(provider, modelId))
|
|
2863
|
+
throw new Error("\u8BF7\u5148\u542F\u7528\u8BE5 GPT \u6A21\u578B\u7684\u81EA\u52A8\u9002\u914D");
|
|
2864
|
+
const index = profile.models?.findIndex((model) => model.id === modelId) ?? -1;
|
|
2865
|
+
if (index < 0) throw new Error("\u6A21\u578B\u5DF2\u53D8\u66F4\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5");
|
|
2866
|
+
const effective = tokens ?? this.contextWindow(provider, modelId).defaultTokens;
|
|
2867
|
+
const row = profile.models[index];
|
|
2868
|
+
const compaction = this.nativeDescribe().find(
|
|
2869
|
+
(entry) => entry.ns === "compaction-basic"
|
|
2870
|
+
)?.value;
|
|
2871
|
+
const policy = compaction?.modelPolicies?.find(
|
|
2872
|
+
(entry) => entry.provider === provider && entry.model === modelId
|
|
2873
|
+
);
|
|
2874
|
+
const headroom = policy?.headroomTokens ?? compaction?.headroomTokens ?? 0;
|
|
2875
|
+
const output = validContextWindow(row.maxTokens) ? row.maxTokens : 0;
|
|
2876
|
+
if (effective <= output + headroom)
|
|
2877
|
+
throw new Error("\u4E0A\u4E0B\u6587\u7A97\u53E3\u5FC5\u987B\u5927\u4E8E\u5F53\u524D\u6700\u5927\u8F93\u51FA\u548C\u538B\u7F29\u9884\u7559\u7684\u603B\u548C");
|
|
2878
|
+
if (!this.contextWriter) throw new Error("\u4E0A\u4E0B\u6587\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528");
|
|
2879
|
+
await this.contextWriter(
|
|
2880
|
+
provider,
|
|
2881
|
+
modelId,
|
|
2882
|
+
tokens,
|
|
2883
|
+
expectedRevision ?? this.modelsRevision()
|
|
2884
|
+
);
|
|
2885
|
+
this.ctx.emit("llm/models-updated", provider);
|
|
2886
|
+
}
|
|
2443
2887
|
async setSpeed(sessionId, provider, modelId, tier) {
|
|
2444
2888
|
const capability = this.capability(provider, modelId);
|
|
2445
2889
|
if (!capability?.serviceTiers.some((choice) => choice.id === tier))
|
|
@@ -2476,11 +2920,15 @@ var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refre
|
|
|
2476
2920
|
...overrides[modelId]
|
|
2477
2921
|
};
|
|
2478
2922
|
const current = this.snapshots.get(provider)?.capabilities[modelId];
|
|
2479
|
-
|
|
2923
|
+
const result = current ? mergeCapabilities(current, override, current.capabilityRevision) : resolveCapabilities(
|
|
2480
2924
|
{ id: modelId, ...displayName ? { displayName } : {} },
|
|
2481
2925
|
"gpt-preset",
|
|
2482
2926
|
override
|
|
2483
2927
|
);
|
|
2928
|
+
return {
|
|
2929
|
+
...result,
|
|
2930
|
+
contextWindow: this.contextWindow(provider, modelId).effectiveTokens
|
|
2931
|
+
};
|
|
2484
2932
|
}
|
|
2485
2933
|
/** Capture request controls for the version-checked native runtime extension. */
|
|
2486
2934
|
capture(provider, modelId, base) {
|
|
@@ -2493,6 +2941,14 @@ var GptModels = class extends (_a = Service, _codexStatus_dec = [Remote], _refre
|
|
|
2493
2941
|
const protocol = profile.api ?? "openai-responses";
|
|
2494
2942
|
const model = {
|
|
2495
2943
|
...base,
|
|
2944
|
+
context: {
|
|
2945
|
+
...base.context ?? {},
|
|
2946
|
+
contextWindow: this.contextWindow(
|
|
2947
|
+
provider,
|
|
2948
|
+
modelId,
|
|
2949
|
+
base.context?.contextWindow
|
|
2950
|
+
).effectiveTokens
|
|
2951
|
+
},
|
|
2496
2952
|
name: capabilities.displayName === modelId ? base.name : capabilities.displayName
|
|
2497
2953
|
};
|
|
2498
2954
|
if (!preserveReasoning) delete model.reasoning;
|
|
@@ -2543,6 +2999,7 @@ __decorateElement(_init, 1, "configure", _configure_dec, GptModels);
|
|
|
2543
2999
|
__decorateElement(_init, 1, "updateOptions", _updateOptions_dec, GptModels);
|
|
2544
3000
|
__decorateElement(_init, 1, "refresh", _refresh_dec, GptModels);
|
|
2545
3001
|
__decorateElement(_init, 1, "removeStale", _removeStale_dec, GptModels);
|
|
3002
|
+
__decorateElement(_init, 1, "setContextWindow", _setContextWindow_dec, GptModels);
|
|
2546
3003
|
__decorateElement(_init, 1, "setSpeed", _setSpeed_dec, GptModels);
|
|
2547
3004
|
__decorateElement(_init, 1, "speed", _speed_dec, GptModels);
|
|
2548
3005
|
__decoratorMetadata(_init, GptModels);
|
|
@@ -2582,9 +3039,15 @@ async function apply2(ctx, config) {
|
|
|
2582
3039
|
const providers = service.enabledProviders();
|
|
2583
3040
|
const dispose = installSettingsOverlay(
|
|
2584
3041
|
ctx.settings,
|
|
2585
|
-
(provider, model, profile) => typeof model.id === "string" ? service.capability(provider, model.id, model.name, profile) : void 0
|
|
3042
|
+
(provider, model, profile) => typeof model.id === "string" ? service.capability(provider, model.id, model.name, profile) : void 0,
|
|
3043
|
+
{
|
|
3044
|
+
revision: () => service.contextWriteRevision(),
|
|
3045
|
+
saved: (edits, revision) => service.clearLegacyContext(edits, revision)
|
|
3046
|
+
}
|
|
2586
3047
|
);
|
|
3048
|
+
service.contextWriter = dispose.setContextWindow;
|
|
2587
3049
|
return () => {
|
|
3050
|
+
service.contextWriter = void 0;
|
|
2588
3051
|
dispose();
|
|
2589
3052
|
for (const provider of providers) ctx.emit("llm/models-updated", provider);
|
|
2590
3053
|
};
|