@nyssance/codex-acp-v2 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/index.js +206 -71
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 — 2026-09-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Several gateways at once in catalog mode: `providers/set` takes `_meta.codex.id`
|
|
8
|
+
(default `custom-gateway`); each gateway keeps its own base URL, key, config and models,
|
|
9
|
+
writes its own `model_providers.<id>` entry, and gets its own select group in every
|
|
10
|
+
session's `model` option. Model ids must be unique across gateways (`-32602` otherwise).
|
|
11
|
+
`providers/disable` with `_meta.codex.id` removes one gateway; `providers/list` reports
|
|
12
|
+
`_meta.codex.gateways`. Route mode is unchanged (one gateway).
|
|
13
|
+
- Pass-throughs: `_codex/session_rename` (`thread/name/set`), `_codex/account_read`
|
|
14
|
+
(`account/read`), `_codex/rate_limits` (`account/rateLimits/read`) and
|
|
15
|
+
`_codex/fuzzy_file_search` (`fuzzyFileSearch`); notifications
|
|
16
|
+
`_codex/rate_limits_updated`, `_codex/fuzzy_file_search_updated` and
|
|
17
|
+
`_codex/fuzzy_file_search_completed` carry Codex's payloads verbatim. Advertised as
|
|
18
|
+
`capabilities._meta.codex.rename` / `.account` / `.fuzzyFileSearch`.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- A thread moved from one gateway to another no longer keeps the previous gateway's
|
|
23
|
+
`model_providers` entry (and bearer token) in its config.
|
|
24
|
+
- Re-registering one gateway no longer re-resumes sessions that sit on other gateways.
|
|
25
|
+
|
|
3
26
|
## 0.5.0 — 2026-09-11
|
|
4
27
|
|
|
5
28
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -21721,7 +21721,7 @@ var package_default = {
|
|
|
21721
21721
|
publishConfig: {
|
|
21722
21722
|
access: "public"
|
|
21723
21723
|
},
|
|
21724
|
-
version: "0.
|
|
21724
|
+
version: "0.6.0",
|
|
21725
21725
|
description: "Native ACP v2 agent for the OpenAI Codex app-server",
|
|
21726
21726
|
packageManager: "bun@1.4.0",
|
|
21727
21727
|
engines: {
|
|
@@ -23982,7 +23982,7 @@ function modelLacksReasoning(model) {
|
|
|
23982
23982
|
const options = supportedEfforts(model);
|
|
23983
23983
|
return options.length > 0 && options.every((option) => option.reasoningEffort === "none");
|
|
23984
23984
|
}
|
|
23985
|
-
function modelConfigOption(catalog, current,
|
|
23985
|
+
function modelConfigOption(catalog, current, gatewayGroups = []) {
|
|
23986
23986
|
const visible = catalog.filter((model) => !model.hidden || model.id === current);
|
|
23987
23987
|
const options = visible.map((model) => ({
|
|
23988
23988
|
value: model.id,
|
|
@@ -24000,16 +24000,18 @@ function modelConfigOption(catalog, current, gatewayGroup = null) {
|
|
|
24000
24000
|
type: "select",
|
|
24001
24001
|
currentValue: current
|
|
24002
24002
|
};
|
|
24003
|
-
if (
|
|
24003
|
+
if (gatewayGroups.length === 0)
|
|
24004
24004
|
return { ...base, options };
|
|
24005
|
-
const
|
|
24006
|
-
const
|
|
24007
|
-
|
|
24005
|
+
const owner = new Map;
|
|
24006
|
+
for (const group of gatewayGroups)
|
|
24007
|
+
for (const id of group.modelIds)
|
|
24008
|
+
owner.set(id, group.id);
|
|
24009
|
+
const native = options.filter((option) => !owner.has(option.value));
|
|
24008
24010
|
return {
|
|
24009
24011
|
...base,
|
|
24010
24012
|
options: [
|
|
24011
24013
|
{ groupId: "codex", name: "Codex", options: native },
|
|
24012
|
-
{ groupId:
|
|
24014
|
+
...gatewayGroups.map((group) => ({ groupId: group.id, name: group.name, options: options.filter((option) => owner.get(option.value) === group.id) }))
|
|
24013
24015
|
]
|
|
24014
24016
|
};
|
|
24015
24017
|
}
|
|
@@ -25966,7 +25968,7 @@ function sessionConfigOptions(session) {
|
|
|
25966
25968
|
const model = findModel(session.catalog, session.model.model);
|
|
25967
25969
|
const options = [
|
|
25968
25970
|
modeConfigOption(session.mode),
|
|
25969
|
-
modelConfigOption(session.catalog, session.model.model, session.
|
|
25971
|
+
modelConfigOption(session.catalog, session.model.model, session.gatewayGroups),
|
|
25970
25972
|
effortConfigOption(supportedEfforts(model), session.model.effort),
|
|
25971
25973
|
collaborationModeConfigOption(session.collaborationMode)
|
|
25972
25974
|
];
|
|
@@ -26074,31 +26076,41 @@ import path6 from "path";
|
|
|
26074
26076
|
var OPENAI_PROVIDER_ID = "openai";
|
|
26075
26077
|
var GATEWAY_MODEL_PROVIDER = "custom-gateway";
|
|
26076
26078
|
var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
26079
|
+
var GATEWAY_ID_PATTERN = /^[a-z0-9-]+$/;
|
|
26080
|
+
|
|
26077
26081
|
class ProviderRouting {
|
|
26078
26082
|
baseConfig;
|
|
26079
26083
|
configuredProvider;
|
|
26080
|
-
|
|
26084
|
+
gateways = new Map;
|
|
26081
26085
|
constructor(baseConfig, configuredProvider) {
|
|
26082
26086
|
this.baseConfig = baseConfig;
|
|
26083
26087
|
this.configuredProvider = configuredProvider;
|
|
26084
26088
|
}
|
|
26085
26089
|
copy() {
|
|
26086
26090
|
const copy = new ProviderRouting(this.baseConfig, this.configuredProvider);
|
|
26087
|
-
copy.
|
|
26091
|
+
copy.gateways = new Map(this.gateways);
|
|
26088
26092
|
return copy;
|
|
26089
26093
|
}
|
|
26090
26094
|
get active() {
|
|
26091
|
-
return this.
|
|
26095
|
+
return this.gateways.values().next().value ?? null;
|
|
26096
|
+
}
|
|
26097
|
+
gateway(id) {
|
|
26098
|
+
return id === null ? null : this.gateways.get(id) ?? null;
|
|
26099
|
+
}
|
|
26100
|
+
hasGateway(id) {
|
|
26101
|
+
return id !== null && this.gateways.has(id);
|
|
26092
26102
|
}
|
|
26093
26103
|
list() {
|
|
26094
|
-
const
|
|
26104
|
+
const first = this.active;
|
|
26105
|
+
const routed = first !== null && first.mode === "route";
|
|
26106
|
+
const summary = (gateway) => ({ id: gateway.id, name: gateway.name, baseUrl: gateway.baseUrl, models: gateway.models.map((model) => model.id) });
|
|
26095
26107
|
return {
|
|
26096
26108
|
providers: [{
|
|
26097
26109
|
providerId: OPENAI_PROVIDER_ID,
|
|
26098
26110
|
supported: ["openai"],
|
|
26099
26111
|
required: false,
|
|
26100
|
-
current: routed &&
|
|
26101
|
-
...
|
|
26112
|
+
current: routed && first ? { apiType: "openai", baseUrl: first.baseUrl } : { apiType: "openai", baseUrl: this.nativeBaseUrl() },
|
|
26113
|
+
...first && !routed ? { _meta: { codex: { mode: "catalog", gateway: summary(first), gateways: [...this.gateways.values()].map(summary) } } } : {}
|
|
26102
26114
|
}]
|
|
26103
26115
|
};
|
|
26104
26116
|
}
|
|
@@ -26114,7 +26126,21 @@ class ProviderRouting {
|
|
|
26114
26126
|
throw RequestError.invalidParams({ baseUrl: request.baseUrl }, "baseUrl must be an http(s) URL");
|
|
26115
26127
|
}
|
|
26116
26128
|
const hints = readHints(request._meta);
|
|
26117
|
-
this.
|
|
26129
|
+
if (hints.id === OPENAI_PROVIDER_ID || hints.id === this.configuredProvider) {
|
|
26130
|
+
throw RequestError.invalidParams({ id: hints.id }, `_meta.codex.id "${hints.id}" is Codex's own provider; pick another gateway id`);
|
|
26131
|
+
}
|
|
26132
|
+
if (hints.mode === "route" || this.mode !== hints.mode)
|
|
26133
|
+
this.gateways.clear();
|
|
26134
|
+
for (const other of this.gateways.values()) {
|
|
26135
|
+
if (other.id === hints.id)
|
|
26136
|
+
continue;
|
|
26137
|
+
const duplicate = hints.models.find((model) => other.models.some((candidate) => candidate.id === model.id));
|
|
26138
|
+
if (duplicate) {
|
|
26139
|
+
throw RequestError.invalidParams({ id: hints.id, model: duplicate.id }, `Model "${duplicate.id}" is already served by gateway "${other.id}"; model ids must be unique across gateways`);
|
|
26140
|
+
}
|
|
26141
|
+
}
|
|
26142
|
+
this.gateways.set(hints.id, {
|
|
26143
|
+
id: hints.id,
|
|
26118
26144
|
baseUrl,
|
|
26119
26145
|
headers: { ...request.headers ?? {} },
|
|
26120
26146
|
name: hints.name ?? "Client-configured gateway",
|
|
@@ -26124,41 +26150,65 @@ class ProviderRouting {
|
|
|
26124
26150
|
config: hints.config,
|
|
26125
26151
|
mode: hints.mode,
|
|
26126
26152
|
catalogEntries: hints.catalogEntries
|
|
26127
|
-
};
|
|
26153
|
+
});
|
|
26128
26154
|
}
|
|
26129
26155
|
get mode() {
|
|
26130
|
-
return this.
|
|
26156
|
+
return this.active?.mode ?? "route";
|
|
26157
|
+
}
|
|
26158
|
+
gatewayFor(modelId) {
|
|
26159
|
+
for (const gateway of this.gateways.values()) {
|
|
26160
|
+
if (gateway.models.some((model) => model.id === modelId))
|
|
26161
|
+
return gateway;
|
|
26162
|
+
}
|
|
26163
|
+
return null;
|
|
26164
|
+
}
|
|
26165
|
+
gatewayIdFor(modelId) {
|
|
26166
|
+
return this.gatewayFor(modelId)?.id ?? null;
|
|
26131
26167
|
}
|
|
26132
26168
|
isGatewayModel(modelId) {
|
|
26133
|
-
return this.
|
|
26169
|
+
return this.gatewayFor(modelId) !== null;
|
|
26134
26170
|
}
|
|
26135
|
-
|
|
26136
|
-
if (
|
|
26137
|
-
return
|
|
26138
|
-
return { name:
|
|
26171
|
+
gatewayGroups() {
|
|
26172
|
+
if (this.mode !== "catalog")
|
|
26173
|
+
return [];
|
|
26174
|
+
return [...this.gateways.values()].filter((gateway) => gateway.models.length > 0).map((gateway) => ({ id: gateway.id, name: gateway.name, modelIds: gateway.models.map((model) => model.id) }));
|
|
26139
26175
|
}
|
|
26140
26176
|
disable(request) {
|
|
26141
|
-
if (request.providerId
|
|
26142
|
-
|
|
26177
|
+
if (request.providerId !== OPENAI_PROVIDER_ID)
|
|
26178
|
+
return;
|
|
26179
|
+
const id = readGatewayId(request._meta);
|
|
26180
|
+
if (id === null)
|
|
26181
|
+
this.gateways.clear();
|
|
26182
|
+
else
|
|
26183
|
+
this.gateways.delete(id);
|
|
26143
26184
|
}
|
|
26144
26185
|
modelProvider() {
|
|
26145
|
-
return this.
|
|
26186
|
+
return this.defaultGatewayId() ?? this.configuredProvider;
|
|
26146
26187
|
}
|
|
26147
26188
|
routesByDefault() {
|
|
26148
|
-
return this.
|
|
26189
|
+
return this.defaultGatewayId() !== null;
|
|
26190
|
+
}
|
|
26191
|
+
defaultGatewayId() {
|
|
26192
|
+
const first = this.active;
|
|
26193
|
+
return first !== null && first.mode === "route" ? first.id : null;
|
|
26149
26194
|
}
|
|
26150
|
-
threadConfig(
|
|
26151
|
-
|
|
26195
|
+
threadConfig(gatewayId = this.defaultGatewayId()) {
|
|
26196
|
+
const gateway = this.gateway(gatewayId);
|
|
26197
|
+
if (!gateway)
|
|
26152
26198
|
return this.baseConfig;
|
|
26153
|
-
return this.applyGateway(this.baseConfig,
|
|
26199
|
+
return this.applyGateway(this.baseConfig, gateway);
|
|
26154
26200
|
}
|
|
26155
|
-
routeConfig(config2,
|
|
26156
|
-
return this.rebind(config2, this,
|
|
26201
|
+
routeConfig(config2, gatewayId) {
|
|
26202
|
+
return this.rebind(config2, this, gatewayId);
|
|
26157
26203
|
}
|
|
26158
26204
|
overrideKeys() {
|
|
26159
|
-
|
|
26205
|
+
const keys = new Set;
|
|
26206
|
+
for (const gateway of this.gateways.values())
|
|
26207
|
+
for (const key of Object.keys(gateway.config))
|
|
26208
|
+
keys.add(key);
|
|
26209
|
+
return [...keys];
|
|
26160
26210
|
}
|
|
26161
|
-
rebind(config2, previous,
|
|
26211
|
+
rebind(config2, previous, gatewayId = this.defaultGatewayId()) {
|
|
26162
26212
|
const next = { ...config2 };
|
|
26163
26213
|
for (const key of previous.overrideKeys()) {
|
|
26164
26214
|
if (key in this.baseConfig)
|
|
@@ -26166,13 +26216,11 @@ class ProviderRouting {
|
|
|
26166
26216
|
else
|
|
26167
26217
|
delete next[key];
|
|
26168
26218
|
}
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
}
|
|
26175
|
-
return this.applyGateway(next, this.gateway);
|
|
26219
|
+
delete next["model_providers"];
|
|
26220
|
+
if (isRecord2(this.baseConfig["model_providers"]))
|
|
26221
|
+
next["model_providers"] = this.baseConfig["model_providers"];
|
|
26222
|
+
const gateway = this.gateway(gatewayId);
|
|
26223
|
+
return gateway ? this.applyGateway(next, gateway) : next;
|
|
26176
26224
|
}
|
|
26177
26225
|
applyGateway(config2, gateway) {
|
|
26178
26226
|
const existing = isRecord2(config2["model_providers"]) ? config2["model_providers"] : {};
|
|
@@ -26187,15 +26235,24 @@ class ProviderRouting {
|
|
|
26187
26235
|
return {
|
|
26188
26236
|
...config2,
|
|
26189
26237
|
...gateway.config,
|
|
26190
|
-
model_providers: { ...existing, [
|
|
26238
|
+
model_providers: { ...existing, [gateway.id]: entry }
|
|
26191
26239
|
};
|
|
26192
26240
|
}
|
|
26193
26241
|
catalog(codexCatalog) {
|
|
26194
|
-
|
|
26242
|
+
const first = this.active;
|
|
26243
|
+
if (first === null)
|
|
26195
26244
|
return codexCatalog;
|
|
26196
|
-
|
|
26197
|
-
|
|
26198
|
-
|
|
26245
|
+
if (first.mode === "route") {
|
|
26246
|
+
if (first.models.length === 0)
|
|
26247
|
+
return codexCatalog;
|
|
26248
|
+
return first.models.map((model, index) => gatewayModel(model, index === 0, codexCatalog[0], first.catalogEntries.get(model.id)));
|
|
26249
|
+
}
|
|
26250
|
+
const own = [];
|
|
26251
|
+
for (const gateway of this.gateways.values()) {
|
|
26252
|
+
for (const model of gateway.models)
|
|
26253
|
+
own.push(gatewayModel(model, false, codexCatalog[0], gateway.catalogEntries.get(model.id)));
|
|
26254
|
+
}
|
|
26255
|
+
return [...codexCatalog, ...own];
|
|
26199
26256
|
}
|
|
26200
26257
|
nativeBaseUrl() {
|
|
26201
26258
|
const providers = this.baseConfig["model_providers"];
|
|
@@ -26212,6 +26269,7 @@ function readHints(meta) {
|
|
|
26212
26269
|
const root = isRecord2(meta) ? meta : {};
|
|
26213
26270
|
const alwith = isRecord2(root["alwith"]) ? root["alwith"] : {};
|
|
26214
26271
|
const codex = isRecord2(root["codex"]) ? root["codex"] : {};
|
|
26272
|
+
const id = readGatewayId(meta) ?? GATEWAY_MODEL_PROVIDER;
|
|
26215
26273
|
const models = [];
|
|
26216
26274
|
if (Array.isArray(alwith["models"])) {
|
|
26217
26275
|
for (const entry of alwith["models"]) {
|
|
@@ -26249,7 +26307,18 @@ function readHints(meta) {
|
|
|
26249
26307
|
}
|
|
26250
26308
|
const catalogPath = config2["model_catalog_json"];
|
|
26251
26309
|
const catalogEntries = typeof catalogPath === "string" ? readCatalogFile(catalogPath) : new Map;
|
|
26252
|
-
return { models, model, name, bearerToken, config: config2, mode, catalogEntries };
|
|
26310
|
+
return { id, models, model, name, bearerToken, config: config2, mode, catalogEntries };
|
|
26311
|
+
}
|
|
26312
|
+
function readGatewayId(meta) {
|
|
26313
|
+
const root = isRecord2(meta) ? meta : {};
|
|
26314
|
+
const codex = isRecord2(root["codex"]) ? root["codex"] : {};
|
|
26315
|
+
const raw = codex["id"];
|
|
26316
|
+
if (raw === undefined)
|
|
26317
|
+
return null;
|
|
26318
|
+
if (typeof raw !== "string" || !GATEWAY_ID_PATTERN.test(raw)) {
|
|
26319
|
+
throw RequestError.invalidParams({ id: raw }, "_meta.codex.id must match [a-z0-9-]+");
|
|
26320
|
+
}
|
|
26321
|
+
return raw;
|
|
26253
26322
|
}
|
|
26254
26323
|
function readCatalogFile(catalogPath) {
|
|
26255
26324
|
const resolved = catalogPath.startsWith("~/") ? path6.join(os2.homedir(), catalogPath.slice(2)) : catalogPath;
|
|
@@ -26406,6 +26475,12 @@ class CodexAgent {
|
|
|
26406
26475
|
}
|
|
26407
26476
|
if (notification.method === "account/updated")
|
|
26408
26477
|
this.refreshAccounts().catch((error40) => logger.error("refreshing account failed", error40));
|
|
26478
|
+
if (notification.method === "account/rateLimits/updated")
|
|
26479
|
+
this.forward("_codex/rate_limits_updated", notification.params);
|
|
26480
|
+
if (notification.method === "fuzzyFileSearch/sessionUpdated")
|
|
26481
|
+
this.forward("_codex/fuzzy_file_search_updated", notification.params);
|
|
26482
|
+
if (notification.method === "fuzzyFileSearch/sessionCompleted")
|
|
26483
|
+
this.forward("_codex/fuzzy_file_search_completed", notification.params);
|
|
26409
26484
|
});
|
|
26410
26485
|
this.codex.disconnectSignal.addEventListener("abort", stopObserving, { once: true });
|
|
26411
26486
|
}
|
|
@@ -26442,7 +26517,7 @@ class CodexAgent {
|
|
|
26442
26517
|
additionalDirectories: {}
|
|
26443
26518
|
},
|
|
26444
26519
|
providers: {},
|
|
26445
|
-
_meta: { codex: { archive: true, seedHistory: true, skills: true, plugins: true, providerCatalog: true } }
|
|
26520
|
+
_meta: { codex: { archive: true, seedHistory: true, skills: true, plugins: true, providerCatalog: true, rename: true, account: true, fuzzyFileSearch: true } }
|
|
26446
26521
|
},
|
|
26447
26522
|
authMethods: authMethods(this.capabilities, this.env)
|
|
26448
26523
|
};
|
|
@@ -26529,8 +26604,14 @@ class CodexAgent {
|
|
|
26529
26604
|
const catalog = await this.withCodex(() => this.codex.allModels(true));
|
|
26530
26605
|
const nativeProvider = await this.resolveModelProvider(candidate);
|
|
26531
26606
|
const wholesale = this.providers.active !== null && this.providers.mode === "route" || candidate.active !== null && candidate.mode === "route";
|
|
26532
|
-
const moving = [...this.sessions.values()].filter((runtime) => wholesale || runtime.modelProvider
|
|
26533
|
-
const targets = new Map(moving.map((runtime) =>
|
|
26607
|
+
const moving = [...this.sessions.values()].filter((runtime) => wholesale || this.providers.hasGateway(runtime.modelProvider) && candidate.gateway(runtime.modelProvider) !== this.providers.gateway(runtime.modelProvider));
|
|
26608
|
+
const targets = new Map(moving.map((runtime) => {
|
|
26609
|
+
if (candidate.active === null)
|
|
26610
|
+
return [runtime, null];
|
|
26611
|
+
if (candidate.mode === "route")
|
|
26612
|
+
return [runtime, candidate.defaultGatewayId()];
|
|
26613
|
+
return [runtime, candidate.hasGateway(runtime.modelProvider) ? runtime.modelProvider : null];
|
|
26614
|
+
}));
|
|
26534
26615
|
for (const runtime of moving) {
|
|
26535
26616
|
try {
|
|
26536
26617
|
await this.codex.threadTurnsList({ threadId: runtime.session.id, limit: 1, itemsView: "notLoaded" });
|
|
@@ -26543,10 +26624,10 @@ class CodexAgent {
|
|
|
26543
26624
|
}
|
|
26544
26625
|
const nextCatalog = candidate.catalog(catalog);
|
|
26545
26626
|
for (const runtime of moving) {
|
|
26546
|
-
const
|
|
26547
|
-
const config2 = candidate.rebind(runtime.config, this.providers,
|
|
26548
|
-
const modelProvider =
|
|
26549
|
-
const keep = findModel(nextCatalog, runtime.session.model.model) && candidate.
|
|
26627
|
+
const target = targets.get(runtime) ?? null;
|
|
26628
|
+
const config2 = candidate.rebind(runtime.config, this.providers, target);
|
|
26629
|
+
const modelProvider = target ?? nativeProvider;
|
|
26630
|
+
const keep = findModel(nextCatalog, runtime.session.model.model) && candidate.gatewayIdFor(runtime.session.model.model) === target;
|
|
26550
26631
|
const requestedModel = candidate.mode === "route" ? candidate.active?.model ?? (keep ? runtime.session.model.model : null) : keep ? runtime.session.model.model : null;
|
|
26551
26632
|
const selection = resolveModelSelection(nextCatalog, requestedModel, requestedModel === runtime.session.model.model ? runtime.session.model.effort : null);
|
|
26552
26633
|
attempted.push(runtime);
|
|
@@ -26566,12 +26647,12 @@ class CodexAgent {
|
|
|
26566
26647
|
const next = staged.get(runtime);
|
|
26567
26648
|
if (next) {
|
|
26568
26649
|
runtime.config = next.config;
|
|
26569
|
-
runtime.modelProvider = targets.get(runtime)
|
|
26650
|
+
runtime.modelProvider = targets.get(runtime) ?? nativeProvider;
|
|
26570
26651
|
runtime.session.model = next.model;
|
|
26571
26652
|
runtime.stale = false;
|
|
26572
26653
|
}
|
|
26573
26654
|
runtime.session.catalog = nextCatalog;
|
|
26574
|
-
runtime.session.
|
|
26655
|
+
runtime.session.gatewayGroups = candidate.gatewayGroups();
|
|
26575
26656
|
if (!next)
|
|
26576
26657
|
runtime.session.model = resolveModelSelection(nextCatalog, findModel(nextCatalog, runtime.session.model.model) ? runtime.session.model.model : null, runtime.session.model.effort);
|
|
26577
26658
|
runtime.client.update({ sessionUpdate: "config_option_update", configOptions: sessionConfigOptions(runtime.session), _meta: { codex: { routing: { stale: false } } } }).catch((error40) => logger.error("Provider config notification failed", error40));
|
|
@@ -26663,25 +26744,26 @@ class CodexAgent {
|
|
|
26663
26744
|
}
|
|
26664
26745
|
const mcpServers = request.mcpServers ?? [];
|
|
26665
26746
|
const requestedModel = requestedModelOf(request._meta);
|
|
26666
|
-
let
|
|
26747
|
+
let gatewayId = this.providers.defaultGatewayId();
|
|
26667
26748
|
if (this.providers.active !== null && this.providers.mode === "catalog") {
|
|
26668
26749
|
if (requestedModel !== null)
|
|
26669
|
-
|
|
26750
|
+
gatewayId = this.providers.gatewayIdFor(requestedModel);
|
|
26670
26751
|
else if (open2.kind === "resume") {
|
|
26671
26752
|
const known = await this.withCodex(() => this.codex.threadRead({ threadId: open2.request.sessionId, includeTurns: false }));
|
|
26672
|
-
|
|
26753
|
+
gatewayId = this.providers.hasGateway(known.thread.modelProvider) ? known.thread.modelProvider : null;
|
|
26673
26754
|
}
|
|
26674
26755
|
}
|
|
26756
|
+
const onGateway = gatewayId !== null;
|
|
26675
26757
|
let accountVersion = this.accountGeneration;
|
|
26676
26758
|
const account = await this.withCodex(() => this.codex.accountRead({ refreshToken: false }));
|
|
26677
26759
|
if (!onGateway && account.requiresOpenaiAuth && account.account === null) {
|
|
26678
26760
|
throw RequestError.authRequired(undefined, "Log in to Codex first (auth/login)");
|
|
26679
26761
|
}
|
|
26680
26762
|
const existingMcp = mcpServers.length > 0 ? await this.withCodex(() => this.configuredMcpServerNames(request.cwd)) : new Set;
|
|
26681
|
-
const config2 = buildThreadConfig(this.providers.threadConfig(
|
|
26763
|
+
const config2 = buildThreadConfig(this.providers.threadConfig(gatewayId), request.cwd, additionalDirectories, mcpServers, existingMcp);
|
|
26682
26764
|
const mcpStartupGeneration = this.codex.mcpStartupGeneration;
|
|
26683
|
-
const modelProvider =
|
|
26684
|
-
const startModel = onGateway ? requestedModel ?? this.providers.
|
|
26765
|
+
const modelProvider = gatewayId ?? await this.withCodex(() => this.resolveModelProvider());
|
|
26766
|
+
const startModel = onGateway ? requestedModel ?? this.providers.gateway(gatewayId)?.model ?? null : requestedModel;
|
|
26685
26767
|
let readOnly = false;
|
|
26686
26768
|
const { thread, skills } = await this.withSkillsContext(request.cwd, additionalDirectories, async (skills2) => ({ skills: skills2, thread: await this.withCodex(async () => {
|
|
26687
26769
|
if (signal?.aborted)
|
|
@@ -26735,7 +26817,7 @@ class CodexAgent {
|
|
|
26735
26817
|
additionalDirectories,
|
|
26736
26818
|
mcpServerNames: mcpServerNames(mcpServers).filter((name) => !existingMcp.has(name)),
|
|
26737
26819
|
catalog,
|
|
26738
|
-
|
|
26820
|
+
gatewayGroups: this.providers.gatewayGroups(),
|
|
26739
26821
|
model,
|
|
26740
26822
|
mode: initialAgentMode(this.env),
|
|
26741
26823
|
collaborationMode: DEFAULT_COLLABORATION_MODE,
|
|
@@ -26748,7 +26830,7 @@ class CodexAgent {
|
|
|
26748
26830
|
contextWindow: null,
|
|
26749
26831
|
closed: false
|
|
26750
26832
|
};
|
|
26751
|
-
const runtime = this.installRuntime(session, capabilities, config2,
|
|
26833
|
+
const runtime = this.installRuntime(session, capabilities, config2, gatewayId ?? thread.modelProvider, readOnly);
|
|
26752
26834
|
const cancelOpening = () => {
|
|
26753
26835
|
runtime.session.closed = true;
|
|
26754
26836
|
runtime.lifetime.abort();
|
|
@@ -27046,6 +27128,27 @@ class CodexAgent {
|
|
|
27046
27128
|
notifySkillsChanged() {
|
|
27047
27129
|
this.link.notify("_codex/skills_changed", {}).catch((error40) => logger.error("skills change notification failed", error40));
|
|
27048
27130
|
}
|
|
27131
|
+
forward(method, params) {
|
|
27132
|
+
this.link.notify(method, params).catch((error40) => logger.error(`${method} notification failed`, error40));
|
|
27133
|
+
}
|
|
27134
|
+
async sessionRename(params) {
|
|
27135
|
+
this.requireInitialized("_codex/session_rename");
|
|
27136
|
+
await this.withCodex(() => this.codex.threadSetName({ threadId: params.sessionId, name: params.name }));
|
|
27137
|
+
return {};
|
|
27138
|
+
}
|
|
27139
|
+
async accountRead() {
|
|
27140
|
+
this.requireInitialized("_codex/account_read");
|
|
27141
|
+
return await this.withCodex(() => this.codex.accountRead({ refreshToken: false }));
|
|
27142
|
+
}
|
|
27143
|
+
async rateLimits() {
|
|
27144
|
+
this.requireInitialized("_codex/rate_limits");
|
|
27145
|
+
return await this.withCodex(() => this.codex.accountRateLimitsRead());
|
|
27146
|
+
}
|
|
27147
|
+
async fuzzyFileSearch(params) {
|
|
27148
|
+
this.requireInitialized("_codex/fuzzy_file_search");
|
|
27149
|
+
const request = { query: params.query, roots: params.roots, cancellationToken: params.cancellationToken };
|
|
27150
|
+
return await this.withCodex(() => this.codex.fuzzyFileSearch(request));
|
|
27151
|
+
}
|
|
27049
27152
|
async closeRuntime(sessionId) {
|
|
27050
27153
|
const deadline = performance.now() + this.closeGraceMs;
|
|
27051
27154
|
const runtime = this.sessions.get(sessionId);
|
|
@@ -27103,16 +27206,17 @@ class CodexAgent {
|
|
|
27103
27206
|
const runtime = this.runtime(params.sessionId, "session/set_config_option");
|
|
27104
27207
|
assertWritable(runtime);
|
|
27105
27208
|
if (params.configId === MODEL_CONFIG_ID && this.providers.mode === "catalog" && typeof params.value === "string") {
|
|
27106
|
-
const
|
|
27107
|
-
|
|
27108
|
-
|
|
27209
|
+
const target = this.providers.gatewayIdFor(params.value);
|
|
27210
|
+
const current = this.providers.hasGateway(runtime.modelProvider) ? runtime.modelProvider : null;
|
|
27211
|
+
if (target !== current)
|
|
27212
|
+
await this.rerouteSession(runtime, target, params.value);
|
|
27109
27213
|
}
|
|
27110
27214
|
await this.withCodex(() => applyConfigOption(runtime.session, this.codex, params));
|
|
27111
27215
|
const configOptions = sessionConfigOptions(runtime.session);
|
|
27112
27216
|
await runtime.client.update({ sessionUpdate: "config_option_update", configOptions });
|
|
27113
27217
|
return { configOptions };
|
|
27114
27218
|
}
|
|
27115
|
-
async rerouteSession(runtime,
|
|
27219
|
+
async rerouteSession(runtime, gatewayId, modelId) {
|
|
27116
27220
|
const { session } = runtime;
|
|
27117
27221
|
this.assertRoutingAvailable();
|
|
27118
27222
|
if (session.activeTurn !== null) {
|
|
@@ -27123,8 +27227,8 @@ class CodexAgent {
|
|
|
27123
27227
|
} catch {
|
|
27124
27228
|
await this.withCodex(() => this.codex.threadInjectItems({ threadId: session.id, items: [providerChangeNote()] }));
|
|
27125
27229
|
}
|
|
27126
|
-
const config2 = this.providers.routeConfig(runtime.config,
|
|
27127
|
-
const modelProvider =
|
|
27230
|
+
const config2 = this.providers.routeConfig(runtime.config, gatewayId);
|
|
27231
|
+
const modelProvider = gatewayId ?? await this.withCodex(() => this.resolveModelProvider());
|
|
27128
27232
|
await this.codex.threadUnsubscribe({ threadId: session.id });
|
|
27129
27233
|
try {
|
|
27130
27234
|
await this.withCodex(() => this.codex.threadResume({ threadId: session.id, cwd: session.cwd, config: config2, modelProvider, model: modelId, excludeTurns: true }));
|
|
@@ -27135,7 +27239,7 @@ class CodexAgent {
|
|
|
27135
27239
|
runtime.stale = true;
|
|
27136
27240
|
logger.error("Provider rollback failed", rollbackError, { sessionId: session.id });
|
|
27137
27241
|
}
|
|
27138
|
-
throw RequestError.internalError({ sessionId: session.id, stale: runtime.stale }, `Could not move the session to ${
|
|
27242
|
+
throw RequestError.internalError({ sessionId: session.id, stale: runtime.stale }, `Could not move the session to ${gatewayId !== null ? `gateway "${gatewayId}"` : "Codex's provider"}: ${errorMessage(error40)}`);
|
|
27139
27243
|
}
|
|
27140
27244
|
runtime.config = config2;
|
|
27141
27245
|
runtime.modelProvider = modelProvider;
|
|
@@ -27592,6 +27696,28 @@ function interruptedTurn(threadId) {
|
|
|
27592
27696
|
turn: { id: "", items: [], itemsView: "notLoaded", status: "interrupted", error: null, startedAt: null, completedAt: null, durationMs: null }
|
|
27593
27697
|
};
|
|
27594
27698
|
}
|
|
27699
|
+
function parseSessionRenameParams(raw) {
|
|
27700
|
+
const params = objectParams()(raw);
|
|
27701
|
+
if (typeof params.sessionId !== "string" || params.sessionId.length === 0) {
|
|
27702
|
+
throw RequestError.invalidParams({ sessionId: params.sessionId }, "sessionId must be a non-empty string");
|
|
27703
|
+
}
|
|
27704
|
+
if (typeof params.name !== "string")
|
|
27705
|
+
throw RequestError.invalidParams({ name: params.name }, "name must be a string");
|
|
27706
|
+
return { sessionId: params.sessionId, name: params.name };
|
|
27707
|
+
}
|
|
27708
|
+
function parseFuzzyFileSearchParams(raw) {
|
|
27709
|
+
const params = objectParams()(raw);
|
|
27710
|
+
if (typeof params.query !== "string")
|
|
27711
|
+
throw RequestError.invalidParams({ query: params.query }, "query must be a string");
|
|
27712
|
+
if (!Array.isArray(params.roots) || !params.roots.every((root) => typeof root === "string")) {
|
|
27713
|
+
throw RequestError.invalidParams({ roots: params.roots }, "roots must be an array of directory paths");
|
|
27714
|
+
}
|
|
27715
|
+
const token = params.cancellationToken;
|
|
27716
|
+
if (token !== undefined && token !== null && typeof token !== "string") {
|
|
27717
|
+
throw RequestError.invalidParams({ cancellationToken: token }, "cancellationToken must be a string when given");
|
|
27718
|
+
}
|
|
27719
|
+
return { query: params.query, roots: params.roots, cancellationToken: typeof token === "string" ? token : null };
|
|
27720
|
+
}
|
|
27595
27721
|
function objectParams() {
|
|
27596
27722
|
return (raw) => {
|
|
27597
27723
|
if (raw === undefined || raw === null)
|
|
@@ -27687,7 +27813,7 @@ function createAgentApp(options) {
|
|
|
27687
27813
|
};
|
|
27688
27814
|
return agent({ name: options.info.name }).onConnect((connection) => {
|
|
27689
27815
|
agent2 = new CodexAgent(connection.client, options);
|
|
27690
|
-
}).onRequest(methods.agent.initialize, (ctx) => current().initialize(ctx.params)).onRequest(methods.agent.auth.login, (ctx) => current().login(ctx.params, ctx.requestId)).onRequest(methods.agent.auth.logout, (ctx) => current().logout(ctx.params)).onRequest(methods.agent.providers.list, (ctx) => current().listProviders(ctx.params)).onRequest(methods.agent.providers.set, (ctx) => current().setProvider(ctx.params)).onRequest(methods.agent.providers.disable, (ctx) => current().disableProvider(ctx.params)).onRequest(methods.agent.session.new, (ctx) => current().newSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.resume, (ctx) => current().resumeSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.fork, (ctx) => current().forkSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.list, (ctx) => current().listSessions(ctx.params)).onRequest(methods.agent.session.close, (ctx) => current().closeSession(ctx.params)).onRequest(methods.agent.session.delete, (ctx) => current().deleteSession(ctx.params)).onRequest("_codex/session_archive", { parse: parseSessionIdParams }, (ctx) => current().archiveSession(ctx.params)).onRequest("_codex/session_unarchive", { parse: parseSessionIdParams }, (ctx) => current().unarchiveSession(ctx.params)).onRequest("_codex/skills_list", { parse: objectParams() }, (ctx) => current().skillsList(ctx.params)).onRequest("_codex/skills_config_write", { parse: objectParams() }, (ctx) => current().skillsConfigWrite(ctx.params)).onRequest("_codex/plugin_list", { parse: objectParams() }, (ctx) => current().pluginList(ctx.params)).onRequest("_codex/plugin_installed", { parse: objectParams() }, (ctx) => current().pluginInstalled(ctx.params)).onRequest("_codex/plugin_install", { parse: objectParams() }, (ctx) => current().pluginInstall(ctx.params)).onRequest("_codex/plugin_uninstall", { parse: objectParams() }, (ctx) => current().pluginUninstall(ctx.params)).onRequest("_codex/plugin_read", { parse: objectParams() }, (ctx) => current().pluginRead(ctx.params)).onRequest("_codex/marketplace_add", { parse: objectParams() }, (ctx) => current().marketplaceAdd(ctx.params)).onRequest("_codex/marketplace_remove", { parse: objectParams() }, (ctx) => current().marketplaceRemove(ctx.params)).onRequest("_codex/marketplace_upgrade", { parse: objectParams() }, (ctx) => current().marketplaceUpgrade(ctx.params)).onRequest(methods.agent.session.setConfigOption, (ctx) => current().setSessionConfigOption(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => current().prompt(ctx.params)).onNotification(methods.agent.session.cancel, (ctx) => current().cancel(ctx.params));
|
|
27816
|
+
}).onRequest(methods.agent.initialize, (ctx) => current().initialize(ctx.params)).onRequest(methods.agent.auth.login, (ctx) => current().login(ctx.params, ctx.requestId)).onRequest(methods.agent.auth.logout, (ctx) => current().logout(ctx.params)).onRequest(methods.agent.providers.list, (ctx) => current().listProviders(ctx.params)).onRequest(methods.agent.providers.set, (ctx) => current().setProvider(ctx.params)).onRequest(methods.agent.providers.disable, (ctx) => current().disableProvider(ctx.params)).onRequest(methods.agent.session.new, (ctx) => current().newSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.resume, (ctx) => current().resumeSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.fork, (ctx) => current().forkSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.list, (ctx) => current().listSessions(ctx.params)).onRequest(methods.agent.session.close, (ctx) => current().closeSession(ctx.params)).onRequest(methods.agent.session.delete, (ctx) => current().deleteSession(ctx.params)).onRequest("_codex/session_archive", { parse: parseSessionIdParams }, (ctx) => current().archiveSession(ctx.params)).onRequest("_codex/session_unarchive", { parse: parseSessionIdParams }, (ctx) => current().unarchiveSession(ctx.params)).onRequest("_codex/skills_list", { parse: objectParams() }, (ctx) => current().skillsList(ctx.params)).onRequest("_codex/skills_config_write", { parse: objectParams() }, (ctx) => current().skillsConfigWrite(ctx.params)).onRequest("_codex/plugin_list", { parse: objectParams() }, (ctx) => current().pluginList(ctx.params)).onRequest("_codex/plugin_installed", { parse: objectParams() }, (ctx) => current().pluginInstalled(ctx.params)).onRequest("_codex/plugin_install", { parse: objectParams() }, (ctx) => current().pluginInstall(ctx.params)).onRequest("_codex/plugin_uninstall", { parse: objectParams() }, (ctx) => current().pluginUninstall(ctx.params)).onRequest("_codex/plugin_read", { parse: objectParams() }, (ctx) => current().pluginRead(ctx.params)).onRequest("_codex/marketplace_add", { parse: objectParams() }, (ctx) => current().marketplaceAdd(ctx.params)).onRequest("_codex/marketplace_remove", { parse: objectParams() }, (ctx) => current().marketplaceRemove(ctx.params)).onRequest("_codex/marketplace_upgrade", { parse: objectParams() }, (ctx) => current().marketplaceUpgrade(ctx.params)).onRequest("_codex/session_rename", { parse: parseSessionRenameParams }, (ctx) => current().sessionRename(ctx.params)).onRequest("_codex/account_read", { parse: objectParams() }, () => current().accountRead()).onRequest("_codex/rate_limits", { parse: objectParams() }, () => current().rateLimits()).onRequest("_codex/fuzzy_file_search", { parse: parseFuzzyFileSearchParams }, (ctx) => current().fuzzyFileSearch(ctx.params)).onRequest(methods.agent.session.setConfigOption, (ctx) => current().setSessionConfigOption(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => current().prompt(ctx.params)).onNotification(methods.agent.session.cancel, (ctx) => current().cancel(ctx.params));
|
|
27691
27817
|
}
|
|
27692
27818
|
|
|
27693
27819
|
// src/codex/AppServerClient.ts
|
|
@@ -28100,6 +28226,15 @@ class AppServerClient {
|
|
|
28100
28226
|
accountRead(params) {
|
|
28101
28227
|
return this.send({ method: "account/read", params });
|
|
28102
28228
|
}
|
|
28229
|
+
accountRateLimitsRead() {
|
|
28230
|
+
return this.send({ method: "account/rateLimits/read", params: undefined });
|
|
28231
|
+
}
|
|
28232
|
+
threadSetName(params) {
|
|
28233
|
+
return this.send({ method: "thread/name/set", params });
|
|
28234
|
+
}
|
|
28235
|
+
fuzzyFileSearch(params) {
|
|
28236
|
+
return this.send({ method: "fuzzyFileSearch", params });
|
|
28237
|
+
}
|
|
28103
28238
|
accountLogin(params) {
|
|
28104
28239
|
return this.send({ method: "account/login/start", params });
|
|
28105
28240
|
}
|