@nyssance/codex-acp-v2 0.3.5 → 0.5.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 +34 -0
- package/README.md +6 -0
- package/dist/index.js +389 -80
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 — 2026-09-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `providers/set` hints `_meta.codex.bearerToken` (written as `experimental_bearer_token`)
|
|
8
|
+
and `_meta.codex.config` (top-level Codex thread-config overrides that ride with the
|
|
9
|
+
gateway; a `model_catalog_json` file is read so gateway models carry their real reasoning
|
|
10
|
+
levels and modalities). `providers/disable` removes both from live threads.
|
|
11
|
+
- Catalog mode (`_meta.codex.mode: "catalog"`): the gateway's models are offered next to
|
|
12
|
+
Codex's in every session's `model` option as their own select group; selecting one moves
|
|
13
|
+
only that session to the gateway (unsubscribe + resume, materializing an empty thread
|
|
14
|
+
first), selecting a Codex model moves it back. `session/new` / `session/fork` honour
|
|
15
|
+
`_meta.alwith.model`; `session/resume` follows the provider Codex recorded for the thread.
|
|
16
|
+
Advertised as `capabilities._meta.codex.providerCatalog`.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- A thread another Codex client is writing ("already has an active writer", a file lock
|
|
21
|
+
across the Codex home) opens as a read-only session through `thread/read`, like Codex's
|
|
22
|
+
TUI: history replays, the response carries `_meta.codex.readOnly`, prompts and config
|
|
23
|
+
changes are refused with `-32600`, and a later resume retries the real thing.
|
|
24
|
+
|
|
25
|
+
## 0.4.0 — 2026-09-10
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Skills and plugins extension surface: `_codex/skills_list`, `_codex/skills_config_write`,
|
|
30
|
+
`_codex/plugin_list`, `_codex/plugin_installed`, `_codex/plugin_install`,
|
|
31
|
+
`_codex/plugin_uninstall`, `_codex/plugin_read`, `_codex/marketplace_add`,
|
|
32
|
+
`_codex/marketplace_remove`, `_codex/marketplace_upgrade` pass the Codex v2 shapes
|
|
33
|
+
through verbatim; advertised as `capabilities._meta.codex.skills` / `.plugins`.
|
|
34
|
+
- `_codex/skills_changed` notification: forwarded from Codex `skills/changed` and sent
|
|
35
|
+
after every catalog mutation made through the adapter.
|
|
36
|
+
|
|
3
37
|
## 0.3.5 — 2026-09-07
|
|
4
38
|
|
|
5
39
|
### Fixed
|
package/README.md
CHANGED
|
@@ -21,6 +21,12 @@ compatibility layer: everything is written against `@agentclientprotocol/sdk/exp
|
|
|
21
21
|
hiding is the `_codex/session_archive` / `_codex/session_unarchive` extension pair
|
|
22
22
|
(declared as `capabilities._meta.codex.archive`), and `session/list` pages the archive
|
|
23
23
|
when called with `_meta: {codex: {archived: true}}`.
|
|
24
|
+
- **Skills and plugins**: `_codex/skills_list`, `_codex/skills_config_write`,
|
|
25
|
+
`_codex/plugin_list` / `_codex/plugin_installed` / `_codex/plugin_install` /
|
|
26
|
+
`_codex/plugin_uninstall` / `_codex/plugin_read` and `_codex/marketplace_add` /
|
|
27
|
+
`_codex/marketplace_remove` / `_codex/marketplace_upgrade` pass Codex's own request
|
|
28
|
+
and response shapes through (declared as `capabilities._meta.codex.skills` / `.plugins`);
|
|
29
|
+
`_codex/skills_changed` tells the client to refetch either catalog.
|
|
24
30
|
- **Asynchronous prompts**: `session/prompt` returns immediately; the turn is
|
|
25
31
|
reported through `state_update` frames (`running`, `requires_action` while a
|
|
26
32
|
permission or form is open, `idle` with `stopReason` and token `usage`).
|
package/dist/index.js
CHANGED
|
@@ -3406,8 +3406,8 @@ var require_main = __commonJS(function(exports) {
|
|
|
3406
3406
|
exports.createMessageConnection = createMessageConnection;
|
|
3407
3407
|
var ril_1 = __importDefault(require_ril());
|
|
3408
3408
|
ril_1.default.install();
|
|
3409
|
-
var
|
|
3410
|
-
var
|
|
3409
|
+
var path8 = __importStar(__require("path"));
|
|
3410
|
+
var os3 = __importStar(__require("os"));
|
|
3411
3411
|
var fs7 = __importStar(__require("fs"));
|
|
3412
3412
|
var crypto_1 = __require("crypto");
|
|
3413
3413
|
var net_1 = __require("net");
|
|
@@ -3555,7 +3555,7 @@ var require_main = __commonJS(function(exports) {
|
|
|
3555
3555
|
}
|
|
3556
3556
|
let randomLength = 32;
|
|
3557
3557
|
const fixedLength = "/lsp-.sock".length;
|
|
3558
|
-
const tmpDir = fs7.realpathSync(XDG_RUNTIME_DIR ??
|
|
3558
|
+
const tmpDir = fs7.realpathSync(XDG_RUNTIME_DIR ?? os3.tmpdir());
|
|
3559
3559
|
const limit = safeIpcPathLengths.get(process.platform);
|
|
3560
3560
|
if (limit !== undefined) {
|
|
3561
3561
|
randomLength = Math.min(limit - tmpDir.length - fixedLength, randomLength);
|
|
@@ -3564,7 +3564,7 @@ var require_main = __commonJS(function(exports) {
|
|
|
3564
3564
|
throw new Error(`Unable to generate a random pipe name with ${randomLength} characters.`);
|
|
3565
3565
|
}
|
|
3566
3566
|
const randomSuffix = (0, crypto_1.randomBytes)(Math.floor(randomLength / 2)).toString("hex");
|
|
3567
|
-
return
|
|
3567
|
+
return path8.join(tmpDir, `lsp-${randomSuffix}.sock`);
|
|
3568
3568
|
}
|
|
3569
3569
|
function createClientPipeTransport(pipeName, encoding = "utf-8") {
|
|
3570
3570
|
let connectResolve;
|
|
@@ -21721,7 +21721,7 @@ var package_default = {
|
|
|
21721
21721
|
publishConfig: {
|
|
21722
21722
|
access: "public"
|
|
21723
21723
|
},
|
|
21724
|
-
version: "0.
|
|
21724
|
+
version: "0.5.0",
|
|
21725
21725
|
description: "Native ACP v2 agent for the OpenAI Codex app-server",
|
|
21726
21726
|
packageManager: "bun@1.4.0",
|
|
21727
21727
|
engines: {
|
|
@@ -21838,7 +21838,7 @@ function classifyTurnError(info) {
|
|
|
21838
21838
|
}
|
|
21839
21839
|
|
|
21840
21840
|
// src/agent/CodexAgent.ts
|
|
21841
|
-
import
|
|
21841
|
+
import path7 from "path";
|
|
21842
21842
|
|
|
21843
21843
|
// src/util/logger.ts
|
|
21844
21844
|
import fs from "fs";
|
|
@@ -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, gatewayGroup = null) {
|
|
23986
23986
|
const visible = catalog.filter((model) => !model.hidden || model.id === current);
|
|
23987
23987
|
const options = visible.map((model) => ({
|
|
23988
23988
|
value: model.id,
|
|
@@ -23992,14 +23992,25 @@ function modelConfigOption(catalog, current) {
|
|
|
23992
23992
|
if (!options.some((option) => option.value === current)) {
|
|
23993
23993
|
options.unshift({ value: current, name: current, description: null });
|
|
23994
23994
|
}
|
|
23995
|
-
|
|
23995
|
+
const base = {
|
|
23996
23996
|
configId: MODEL_CONFIG_ID,
|
|
23997
23997
|
name: "Model",
|
|
23998
23998
|
description: "Model Codex uses for this session",
|
|
23999
23999
|
category: "model",
|
|
24000
24000
|
type: "select",
|
|
24001
|
-
currentValue: current
|
|
24002
|
-
|
|
24001
|
+
currentValue: current
|
|
24002
|
+
};
|
|
24003
|
+
if (gatewayGroup === null)
|
|
24004
|
+
return { ...base, options };
|
|
24005
|
+
const gatewayIds = new Set(gatewayGroup.modelIds);
|
|
24006
|
+
const native = options.filter((option) => !gatewayIds.has(option.value));
|
|
24007
|
+
const gateway = options.filter((option) => gatewayIds.has(option.value));
|
|
24008
|
+
return {
|
|
24009
|
+
...base,
|
|
24010
|
+
options: [
|
|
24011
|
+
{ groupId: "codex", name: "Codex", options: native },
|
|
24012
|
+
{ groupId: "custom-gateway", name: gatewayGroup.name, options: gateway }
|
|
24013
|
+
]
|
|
24003
24014
|
};
|
|
24004
24015
|
}
|
|
24005
24016
|
function effortConfigOption(efforts, current) {
|
|
@@ -25955,7 +25966,7 @@ function sessionConfigOptions(session) {
|
|
|
25955
25966
|
const model = findModel(session.catalog, session.model.model);
|
|
25956
25967
|
const options = [
|
|
25957
25968
|
modeConfigOption(session.mode),
|
|
25958
|
-
modelConfigOption(session.catalog, session.model.model),
|
|
25969
|
+
modelConfigOption(session.catalog, session.model.model, session.gatewayGroup),
|
|
25959
25970
|
effortConfigOption(supportedEfforts(model), session.model.effort),
|
|
25960
25971
|
collaborationModeConfigOption(session.collaborationMode)
|
|
25961
25972
|
];
|
|
@@ -26057,10 +26068,12 @@ function firstLine(text2) {
|
|
|
26057
26068
|
}
|
|
26058
26069
|
|
|
26059
26070
|
// src/agent/providers.ts
|
|
26071
|
+
import { readFileSync } from "fs";
|
|
26072
|
+
import os2 from "os";
|
|
26073
|
+
import path6 from "path";
|
|
26060
26074
|
var OPENAI_PROVIDER_ID = "openai";
|
|
26061
26075
|
var GATEWAY_MODEL_PROVIDER = "custom-gateway";
|
|
26062
26076
|
var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
26063
|
-
|
|
26064
26077
|
class ProviderRouting {
|
|
26065
26078
|
baseConfig;
|
|
26066
26079
|
configuredProvider;
|
|
@@ -26078,12 +26091,14 @@ class ProviderRouting {
|
|
|
26078
26091
|
return this.gateway;
|
|
26079
26092
|
}
|
|
26080
26093
|
list() {
|
|
26094
|
+
const routed = this.gateway !== null && this.gateway.mode === "route";
|
|
26081
26095
|
return {
|
|
26082
26096
|
providers: [{
|
|
26083
26097
|
providerId: OPENAI_PROVIDER_ID,
|
|
26084
26098
|
supported: ["openai"],
|
|
26085
26099
|
required: false,
|
|
26086
|
-
current: this.gateway ? { apiType: "openai", baseUrl: this.gateway.baseUrl } : { apiType: "openai", baseUrl: this.nativeBaseUrl() }
|
|
26100
|
+
current: routed && this.gateway ? { apiType: "openai", baseUrl: this.gateway.baseUrl } : { apiType: "openai", baseUrl: this.nativeBaseUrl() },
|
|
26101
|
+
...this.gateway && !routed ? { _meta: { codex: { mode: "catalog", gateway: { name: this.gateway.name, baseUrl: this.gateway.baseUrl, models: this.gateway.models.map((model) => model.id) } } } } : {}
|
|
26087
26102
|
}]
|
|
26088
26103
|
};
|
|
26089
26104
|
}
|
|
@@ -26104,37 +26119,83 @@ class ProviderRouting {
|
|
|
26104
26119
|
headers: { ...request.headers ?? {} },
|
|
26105
26120
|
name: hints.name ?? "Client-configured gateway",
|
|
26106
26121
|
models: hints.models,
|
|
26107
|
-
model: hints.model
|
|
26122
|
+
model: hints.model,
|
|
26123
|
+
bearerToken: hints.bearerToken,
|
|
26124
|
+
config: hints.config,
|
|
26125
|
+
mode: hints.mode,
|
|
26126
|
+
catalogEntries: hints.catalogEntries
|
|
26108
26127
|
};
|
|
26109
26128
|
}
|
|
26129
|
+
get mode() {
|
|
26130
|
+
return this.gateway?.mode ?? "route";
|
|
26131
|
+
}
|
|
26132
|
+
isGatewayModel(modelId) {
|
|
26133
|
+
return this.gateway !== null && this.gateway.models.some((model) => model.id === modelId);
|
|
26134
|
+
}
|
|
26135
|
+
gatewayGroup() {
|
|
26136
|
+
if (!this.gateway || this.gateway.mode !== "catalog" || this.gateway.models.length === 0)
|
|
26137
|
+
return null;
|
|
26138
|
+
return { name: this.gateway.name, modelIds: this.gateway.models.map((model) => model.id) };
|
|
26139
|
+
}
|
|
26110
26140
|
disable(request) {
|
|
26111
26141
|
if (request.providerId === OPENAI_PROVIDER_ID)
|
|
26112
26142
|
this.gateway = null;
|
|
26113
26143
|
}
|
|
26114
26144
|
modelProvider() {
|
|
26115
|
-
return this.gateway ? GATEWAY_MODEL_PROVIDER : this.configuredProvider;
|
|
26145
|
+
return this.gateway && this.gateway.mode === "route" ? GATEWAY_MODEL_PROVIDER : this.configuredProvider;
|
|
26116
26146
|
}
|
|
26117
|
-
|
|
26118
|
-
|
|
26147
|
+
routesByDefault() {
|
|
26148
|
+
return this.gateway !== null && this.gateway.mode === "route";
|
|
26149
|
+
}
|
|
26150
|
+
threadConfig(onGateway = this.routesByDefault()) {
|
|
26151
|
+
if (!this.gateway || !onGateway)
|
|
26119
26152
|
return this.baseConfig;
|
|
26120
|
-
|
|
26153
|
+
return this.applyGateway(this.baseConfig, this.gateway);
|
|
26154
|
+
}
|
|
26155
|
+
routeConfig(config2, onGateway) {
|
|
26156
|
+
return this.rebind(config2, this, onGateway);
|
|
26157
|
+
}
|
|
26158
|
+
overrideKeys() {
|
|
26159
|
+
return this.gateway ? Object.keys(this.gateway.config) : [];
|
|
26160
|
+
}
|
|
26161
|
+
rebind(config2, previous, onGateway = this.routesByDefault()) {
|
|
26162
|
+
const next = { ...config2 };
|
|
26163
|
+
for (const key of previous.overrideKeys()) {
|
|
26164
|
+
if (key in this.baseConfig)
|
|
26165
|
+
next[key] = this.baseConfig[key];
|
|
26166
|
+
else
|
|
26167
|
+
delete next[key];
|
|
26168
|
+
}
|
|
26169
|
+
if (!this.gateway || !onGateway) {
|
|
26170
|
+
delete next["model_providers"];
|
|
26171
|
+
if (isRecord2(this.baseConfig["model_providers"]))
|
|
26172
|
+
next["model_providers"] = this.baseConfig["model_providers"];
|
|
26173
|
+
return next;
|
|
26174
|
+
}
|
|
26175
|
+
return this.applyGateway(next, this.gateway);
|
|
26176
|
+
}
|
|
26177
|
+
applyGateway(config2, gateway) {
|
|
26178
|
+
const existing = isRecord2(config2["model_providers"]) ? config2["model_providers"] : {};
|
|
26179
|
+
const entry = {
|
|
26180
|
+
name: gateway.name,
|
|
26181
|
+
base_url: gateway.baseUrl,
|
|
26182
|
+
wire_api: "responses",
|
|
26183
|
+
http_headers: { "X-Client-Feature-ID": "codex", ...gateway.headers }
|
|
26184
|
+
};
|
|
26185
|
+
if (gateway.bearerToken !== null)
|
|
26186
|
+
entry["experimental_bearer_token"] = gateway.bearerToken;
|
|
26121
26187
|
return {
|
|
26122
|
-
...
|
|
26123
|
-
|
|
26124
|
-
|
|
26125
|
-
[GATEWAY_MODEL_PROVIDER]: {
|
|
26126
|
-
name: this.gateway.name,
|
|
26127
|
-
base_url: this.gateway.baseUrl,
|
|
26128
|
-
wire_api: "responses",
|
|
26129
|
-
http_headers: { "X-Client-Feature-ID": "codex", ...this.gateway.headers }
|
|
26130
|
-
}
|
|
26131
|
-
}
|
|
26188
|
+
...config2,
|
|
26189
|
+
...gateway.config,
|
|
26190
|
+
model_providers: { ...existing, [GATEWAY_MODEL_PROVIDER]: entry }
|
|
26132
26191
|
};
|
|
26133
26192
|
}
|
|
26134
26193
|
catalog(codexCatalog) {
|
|
26135
26194
|
if (!this.gateway || this.gateway.models.length === 0)
|
|
26136
26195
|
return codexCatalog;
|
|
26137
|
-
|
|
26196
|
+
const gateway = this.gateway;
|
|
26197
|
+
const own = gateway.models.map((model, index) => gatewayModel(model, gateway.mode === "route" && index === 0, codexCatalog[0], gateway.catalogEntries.get(model.id)));
|
|
26198
|
+
return gateway.mode === "catalog" ? [...codexCatalog, ...own] : own;
|
|
26138
26199
|
}
|
|
26139
26200
|
nativeBaseUrl() {
|
|
26140
26201
|
const providers = this.baseConfig["model_providers"];
|
|
@@ -26165,22 +26226,80 @@ function readHints(meta) {
|
|
|
26165
26226
|
}
|
|
26166
26227
|
const model = typeof alwith["model"] === "string" && alwith["model"].length > 0 ? alwith["model"] : null;
|
|
26167
26228
|
const name = typeof codex["name"] === "string" && codex["name"].length > 0 ? codex["name"] : null;
|
|
26168
|
-
|
|
26229
|
+
const bearerToken = typeof codex["bearerToken"] === "string" && codex["bearerToken"].length > 0 ? codex["bearerToken"] : null;
|
|
26230
|
+
const rawConfig = codex["config"];
|
|
26231
|
+
if (rawConfig !== undefined && !isRecord2(rawConfig)) {
|
|
26232
|
+
throw RequestError.invalidParams({ config: rawConfig }, "_meta.codex.config must be an object of top-level Codex config keys");
|
|
26233
|
+
}
|
|
26234
|
+
const config2 = {};
|
|
26235
|
+
if (isRecord2(rawConfig)) {
|
|
26236
|
+
for (const [key, value] of Object.entries(rawConfig)) {
|
|
26237
|
+
if (key === "model_providers")
|
|
26238
|
+
throw RequestError.invalidParams({ config: rawConfig }, "_meta.codex.config cannot set model_providers; that entry is derived from baseUrl and headers");
|
|
26239
|
+
config2[key] = value;
|
|
26240
|
+
}
|
|
26241
|
+
}
|
|
26242
|
+
const rawMode = codex["mode"];
|
|
26243
|
+
if (rawMode !== undefined && rawMode !== "route" && rawMode !== "catalog") {
|
|
26244
|
+
throw RequestError.invalidParams({ mode: rawMode }, '_meta.codex.mode must be "route" or "catalog"');
|
|
26245
|
+
}
|
|
26246
|
+
const mode = rawMode === "catalog" ? "catalog" : "route";
|
|
26247
|
+
if (mode === "catalog" && models.length === 0) {
|
|
26248
|
+
throw RequestError.invalidParams(undefined, `_meta.codex.mode "catalog" needs _meta.alwith.models: the models offered next to Codex's`);
|
|
26249
|
+
}
|
|
26250
|
+
const catalogPath = config2["model_catalog_json"];
|
|
26251
|
+
const catalogEntries = typeof catalogPath === "string" ? readCatalogFile(catalogPath) : new Map;
|
|
26252
|
+
return { models, model, name, bearerToken, config: config2, mode, catalogEntries };
|
|
26253
|
+
}
|
|
26254
|
+
function readCatalogFile(catalogPath) {
|
|
26255
|
+
const resolved = catalogPath.startsWith("~/") ? path6.join(os2.homedir(), catalogPath.slice(2)) : catalogPath;
|
|
26256
|
+
let parsed;
|
|
26257
|
+
try {
|
|
26258
|
+
parsed = JSON.parse(readFileSync(resolved, "utf8"));
|
|
26259
|
+
} catch (error40) {
|
|
26260
|
+
throw RequestError.invalidParams({ model_catalog_json: catalogPath }, `model_catalog_json could not be read as JSON: ${error40 instanceof Error ? error40.message : String(error40)}`);
|
|
26261
|
+
}
|
|
26262
|
+
const models = isRecord2(parsed) && Array.isArray(parsed["models"]) ? parsed["models"] : null;
|
|
26263
|
+
if (models === null)
|
|
26264
|
+
throw RequestError.invalidParams({ model_catalog_json: catalogPath }, "model_catalog_json must be {models: [...]} as Codex expects");
|
|
26265
|
+
const entries = new Map;
|
|
26266
|
+
for (const entry of models) {
|
|
26267
|
+
if (!isRecord2(entry) || typeof entry["slug"] !== "string")
|
|
26268
|
+
continue;
|
|
26269
|
+
const levels = Array.isArray(entry["supported_reasoning_levels"]) ? entry["supported_reasoning_levels"] : [];
|
|
26270
|
+
const efforts = [];
|
|
26271
|
+
for (const level of levels) {
|
|
26272
|
+
if (!isRecord2(level) || typeof level["effort"] !== "string")
|
|
26273
|
+
continue;
|
|
26274
|
+
efforts.push({ reasoningEffort: level["effort"], description: typeof level["description"] === "string" ? level["description"] : "" });
|
|
26275
|
+
}
|
|
26276
|
+
const modalities = Array.isArray(entry["input_modalities"]) ? entry["input_modalities"].filter((value) => value === "text" || value === "image") : null;
|
|
26277
|
+
entries.set(entry["slug"], {
|
|
26278
|
+
displayName: typeof entry["display_name"] === "string" ? entry["display_name"] : null,
|
|
26279
|
+
description: typeof entry["description"] === "string" ? entry["description"] : null,
|
|
26280
|
+
efforts,
|
|
26281
|
+
defaultEffort: typeof entry["default_reasoning_level"] === "string" ? entry["default_reasoning_level"] : null,
|
|
26282
|
+
inputModalities: modalities
|
|
26283
|
+
});
|
|
26284
|
+
}
|
|
26285
|
+
return entries;
|
|
26169
26286
|
}
|
|
26170
|
-
function gatewayModel(model, isDefault, template) {
|
|
26287
|
+
function gatewayModel(model, isDefault, template, entry) {
|
|
26288
|
+
const efforts = entry && entry.efforts.length > 0 ? entry.efforts : template?.supportedReasoningEfforts ?? [];
|
|
26289
|
+
const defaultEffort = entry?.defaultEffort ?? template?.defaultReasoningEffort ?? "medium";
|
|
26171
26290
|
return {
|
|
26172
26291
|
id: model.id,
|
|
26173
26292
|
model: model.id,
|
|
26174
26293
|
upgrade: null,
|
|
26175
26294
|
upgradeInfo: null,
|
|
26176
26295
|
availabilityNux: null,
|
|
26177
|
-
displayName: model.name,
|
|
26178
|
-
description: model.description ?? "",
|
|
26296
|
+
displayName: model.name !== model.id ? model.name : entry?.displayName ?? model.name,
|
|
26297
|
+
description: model.description ?? entry?.description ?? "",
|
|
26179
26298
|
modelSpecialty: null,
|
|
26180
26299
|
hidden: false,
|
|
26181
|
-
supportedReasoningEfforts:
|
|
26182
|
-
defaultReasoningEffort:
|
|
26183
|
-
inputModalities: ["text", "image"],
|
|
26300
|
+
supportedReasoningEfforts: efforts,
|
|
26301
|
+
defaultReasoningEffort: defaultEffort,
|
|
26302
|
+
inputModalities: entry?.inputModalities ?? ["text", "image"],
|
|
26184
26303
|
supportsPersonality: false,
|
|
26185
26304
|
multiAgentVersion: null,
|
|
26186
26305
|
additionalSpeedTiers: [],
|
|
@@ -26283,6 +26402,7 @@ class CodexAgent {
|
|
|
26283
26402
|
if (!this.changingSkillRoots)
|
|
26284
26403
|
for (const runtime of this.sessions.values())
|
|
26285
26404
|
this.refreshAvailableCommands(runtime);
|
|
26405
|
+
this.notifySkillsChanged();
|
|
26286
26406
|
}
|
|
26287
26407
|
if (notification.method === "account/updated")
|
|
26288
26408
|
this.refreshAccounts().catch((error40) => logger.error("refreshing account failed", error40));
|
|
@@ -26322,7 +26442,7 @@ class CodexAgent {
|
|
|
26322
26442
|
additionalDirectories: {}
|
|
26323
26443
|
},
|
|
26324
26444
|
providers: {},
|
|
26325
|
-
_meta: { codex: { archive: true, seedHistory: true } }
|
|
26445
|
+
_meta: { codex: { archive: true, seedHistory: true, skills: true, plugins: true, providerCatalog: true } }
|
|
26326
26446
|
},
|
|
26327
26447
|
authMethods: authMethods(this.capabilities, this.env)
|
|
26328
26448
|
};
|
|
@@ -26407,23 +26527,27 @@ class CodexAgent {
|
|
|
26407
26527
|
try {
|
|
26408
26528
|
change(candidate);
|
|
26409
26529
|
const catalog = await this.withCodex(() => this.codex.allModels(true));
|
|
26410
|
-
const
|
|
26411
|
-
|
|
26530
|
+
const nativeProvider = await this.resolveModelProvider(candidate);
|
|
26531
|
+
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 === GATEWAY_MODEL_PROVIDER);
|
|
26533
|
+
const targets = new Map(moving.map((runtime) => [runtime, candidate.active !== null && (candidate.mode === "route" || runtime.modelProvider === GATEWAY_MODEL_PROVIDER)]));
|
|
26534
|
+
for (const runtime of moving) {
|
|
26412
26535
|
try {
|
|
26413
26536
|
await this.codex.threadTurnsList({ threadId: runtime.session.id, limit: 1, itemsView: "notLoaded" });
|
|
26414
26537
|
} catch (error40) {
|
|
26415
|
-
|
|
26538
|
+
if (wholesale) {
|
|
26539
|
+
throw RequestError.invalidRequest({ sessionId: runtime.session.id, details: errorMessage(error40) }, "Cannot reload this session's history for a provider change; close empty sessions and configure the provider before creating them, or retry after history is available");
|
|
26540
|
+
}
|
|
26541
|
+
await this.withCodex(() => this.codex.threadInjectItems({ threadId: runtime.session.id, items: [providerChangeNote()] }));
|
|
26416
26542
|
}
|
|
26417
26543
|
}
|
|
26418
|
-
|
|
26419
|
-
|
|
26420
|
-
const
|
|
26421
|
-
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
|
|
26425
|
-
const nextCatalog = candidate.catalog(catalog);
|
|
26426
|
-
const requestedModel = candidate.active?.model ?? (findModel(nextCatalog, runtime.session.model.model) ? runtime.session.model.model : null);
|
|
26544
|
+
const nextCatalog = candidate.catalog(catalog);
|
|
26545
|
+
for (const runtime of moving) {
|
|
26546
|
+
const onGateway = targets.get(runtime) === true;
|
|
26547
|
+
const config2 = candidate.rebind(runtime.config, this.providers, onGateway);
|
|
26548
|
+
const modelProvider = onGateway ? GATEWAY_MODEL_PROVIDER : nativeProvider;
|
|
26549
|
+
const keep = findModel(nextCatalog, runtime.session.model.model) && candidate.isGatewayModel(runtime.session.model.model) === onGateway;
|
|
26550
|
+
const requestedModel = candidate.mode === "route" ? candidate.active?.model ?? (keep ? runtime.session.model.model : null) : keep ? runtime.session.model.model : null;
|
|
26427
26551
|
const selection = resolveModelSelection(nextCatalog, requestedModel, requestedModel === runtime.session.model.model ? runtime.session.model.effort : null);
|
|
26428
26552
|
attempted.push(runtime);
|
|
26429
26553
|
await this.codex.threadUnsubscribe({ threadId: runtime.session.id });
|
|
@@ -26435,15 +26559,21 @@ class CodexAgent {
|
|
|
26435
26559
|
model: selection.model,
|
|
26436
26560
|
excludeTurns: true
|
|
26437
26561
|
}));
|
|
26438
|
-
staged.set(runtime, { config: config2, catalog: nextCatalog, model: resolveModelSelection(nextCatalog,
|
|
26562
|
+
staged.set(runtime, { config: config2, catalog: nextCatalog, model: resolveModelSelection(nextCatalog, requestedModel ?? thread.model, thread.reasoningEffort) });
|
|
26439
26563
|
}
|
|
26440
26564
|
this.providers = candidate;
|
|
26441
|
-
for (const
|
|
26442
|
-
|
|
26443
|
-
|
|
26444
|
-
|
|
26445
|
-
|
|
26446
|
-
|
|
26565
|
+
for (const runtime of this.sessions.values()) {
|
|
26566
|
+
const next = staged.get(runtime);
|
|
26567
|
+
if (next) {
|
|
26568
|
+
runtime.config = next.config;
|
|
26569
|
+
runtime.modelProvider = targets.get(runtime) === true ? GATEWAY_MODEL_PROVIDER : nativeProvider;
|
|
26570
|
+
runtime.session.model = next.model;
|
|
26571
|
+
runtime.stale = false;
|
|
26572
|
+
}
|
|
26573
|
+
runtime.session.catalog = nextCatalog;
|
|
26574
|
+
runtime.session.gatewayGroup = candidate.gatewayGroup();
|
|
26575
|
+
if (!next)
|
|
26576
|
+
runtime.session.model = resolveModelSelection(nextCatalog, findModel(nextCatalog, runtime.session.model.model) ? runtime.session.model.model : null, runtime.session.model.effort);
|
|
26447
26577
|
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));
|
|
26448
26578
|
}
|
|
26449
26579
|
} catch (error40) {
|
|
@@ -26482,7 +26612,8 @@ class CodexAgent {
|
|
|
26482
26612
|
}
|
|
26483
26613
|
async resumeSession(params, signal) {
|
|
26484
26614
|
const runtime = await this.openSession({ kind: "resume", request: params }, signal);
|
|
26485
|
-
|
|
26615
|
+
const configOptions = sessionConfigOptions(runtime.session);
|
|
26616
|
+
return runtime.readOnly ? { configOptions, _meta: { codex: { readOnly: true, reason: "active_writer" } } } : { configOptions };
|
|
26486
26617
|
}
|
|
26487
26618
|
async forkSession(params, signal) {
|
|
26488
26619
|
const runtime = await this.openSession({ kind: "fork", request: params }, signal);
|
|
@@ -26518,7 +26649,7 @@ class CodexAgent {
|
|
|
26518
26649
|
if (open2.kind !== "new")
|
|
26519
26650
|
this.assertUnsubscribeSettled(open2.request.sessionId);
|
|
26520
26651
|
const { request } = open2;
|
|
26521
|
-
if (typeof request.cwd !== "string" || !
|
|
26652
|
+
if (typeof request.cwd !== "string" || !path7.isAbsolute(request.cwd)) {
|
|
26522
26653
|
throw RequestError.invalidParams({ cwd: request.cwd }, "cwd must be an absolute path");
|
|
26523
26654
|
}
|
|
26524
26655
|
if (open2.kind === "resume" && open2.request.replayFrom != null && open2.request.replayFrom.type !== "start") {
|
|
@@ -26531,25 +26662,44 @@ class CodexAgent {
|
|
|
26531
26662
|
this.assertUnsubscribeSettled(open2.request.sessionId);
|
|
26532
26663
|
}
|
|
26533
26664
|
const mcpServers = request.mcpServers ?? [];
|
|
26665
|
+
const requestedModel = requestedModelOf(request._meta);
|
|
26666
|
+
let onGateway = this.providers.routesByDefault();
|
|
26667
|
+
if (this.providers.active !== null && this.providers.mode === "catalog") {
|
|
26668
|
+
if (requestedModel !== null)
|
|
26669
|
+
onGateway = this.providers.isGatewayModel(requestedModel);
|
|
26670
|
+
else if (open2.kind === "resume") {
|
|
26671
|
+
const known = await this.withCodex(() => this.codex.threadRead({ threadId: open2.request.sessionId, includeTurns: false }));
|
|
26672
|
+
onGateway = known.thread.modelProvider === GATEWAY_MODEL_PROVIDER;
|
|
26673
|
+
}
|
|
26674
|
+
}
|
|
26534
26675
|
let accountVersion = this.accountGeneration;
|
|
26535
26676
|
const account = await this.withCodex(() => this.codex.accountRead({ refreshToken: false }));
|
|
26536
|
-
if (
|
|
26677
|
+
if (!onGateway && account.requiresOpenaiAuth && account.account === null) {
|
|
26537
26678
|
throw RequestError.authRequired(undefined, "Log in to Codex first (auth/login)");
|
|
26538
26679
|
}
|
|
26539
26680
|
const existingMcp = mcpServers.length > 0 ? await this.withCodex(() => this.configuredMcpServerNames(request.cwd)) : new Set;
|
|
26540
|
-
const config2 = buildThreadConfig(this.providers.threadConfig(), request.cwd, additionalDirectories, mcpServers, existingMcp);
|
|
26681
|
+
const config2 = buildThreadConfig(this.providers.threadConfig(onGateway), request.cwd, additionalDirectories, mcpServers, existingMcp);
|
|
26541
26682
|
const mcpStartupGeneration = this.codex.mcpStartupGeneration;
|
|
26542
|
-
const modelProvider = await this.withCodex(() => this.resolveModelProvider());
|
|
26683
|
+
const modelProvider = onGateway ? GATEWAY_MODEL_PROVIDER : await this.withCodex(() => this.resolveModelProvider());
|
|
26684
|
+
const startModel = onGateway ? requestedModel ?? this.providers.active?.model ?? null : requestedModel;
|
|
26685
|
+
let readOnly = false;
|
|
26543
26686
|
const { thread, skills } = await this.withSkillsContext(request.cwd, additionalDirectories, async (skills2) => ({ skills: skills2, thread: await this.withCodex(async () => {
|
|
26544
26687
|
if (signal?.aborted)
|
|
26545
26688
|
throw RequestError.requestCancelled(undefined, "Session opening was cancelled");
|
|
26546
26689
|
switch (open2.kind) {
|
|
26547
26690
|
case "new":
|
|
26548
|
-
return await this.codex.threadStart({ config: config2, cwd: request.cwd, modelProvider });
|
|
26691
|
+
return await this.codex.threadStart({ config: config2, cwd: request.cwd, modelProvider, model: startModel });
|
|
26549
26692
|
case "resume":
|
|
26550
|
-
|
|
26693
|
+
try {
|
|
26694
|
+
return await this.codex.threadResume({ threadId: open2.request.sessionId, config: config2, cwd: request.cwd, modelProvider, model: startModel, excludeTurns: true });
|
|
26695
|
+
} catch (error40) {
|
|
26696
|
+
if (!errorMessage(error40).includes("already has an active writer"))
|
|
26697
|
+
throw error40;
|
|
26698
|
+
readOnly = true;
|
|
26699
|
+
return readOnlyThread((await this.codex.threadRead({ threadId: open2.request.sessionId, includeTurns: false })).thread, request.cwd);
|
|
26700
|
+
}
|
|
26551
26701
|
case "fork":
|
|
26552
|
-
return await this.codex.threadFork({ threadId: open2.request.sessionId, config: config2, cwd: request.cwd, modelProvider, excludeTurns: true });
|
|
26702
|
+
return await this.codex.threadFork({ threadId: open2.request.sessionId, config: config2, cwd: request.cwd, modelProvider, model: startModel, excludeTurns: true });
|
|
26553
26703
|
}
|
|
26554
26704
|
}) }));
|
|
26555
26705
|
const sessionId = thread.thread.id;
|
|
@@ -26577,15 +26727,15 @@ class CodexAgent {
|
|
|
26577
26727
|
accountVersion = this.accountGeneration;
|
|
26578
26728
|
openedAccount = (await this.withCodex(() => this.codex.accountRead({ refreshToken: false }))).account;
|
|
26579
26729
|
}
|
|
26580
|
-
const gateway = this.providers.active;
|
|
26581
26730
|
const catalog = this.providers.catalog(codexCatalog);
|
|
26582
|
-
const model = resolveModelSelection(catalog,
|
|
26731
|
+
const model = resolveModelSelection(catalog, startModel ?? thread.model, thread.reasoningEffort);
|
|
26583
26732
|
const session = {
|
|
26584
26733
|
id: sessionId,
|
|
26585
26734
|
cwd: request.cwd,
|
|
26586
26735
|
additionalDirectories,
|
|
26587
26736
|
mcpServerNames: mcpServerNames(mcpServers).filter((name) => !existingMcp.has(name)),
|
|
26588
26737
|
catalog,
|
|
26738
|
+
gatewayGroup: this.providers.gatewayGroup(),
|
|
26589
26739
|
model,
|
|
26590
26740
|
mode: initialAgentMode(this.env),
|
|
26591
26741
|
collaborationMode: DEFAULT_COLLABORATION_MODE,
|
|
@@ -26598,7 +26748,7 @@ class CodexAgent {
|
|
|
26598
26748
|
contextWindow: null,
|
|
26599
26749
|
closed: false
|
|
26600
26750
|
};
|
|
26601
|
-
const runtime = this.installRuntime(session, capabilities, config2, thread.modelProvider);
|
|
26751
|
+
const runtime = this.installRuntime(session, capabilities, config2, onGateway ? GATEWAY_MODEL_PROVIDER : thread.modelProvider, readOnly);
|
|
26602
26752
|
const cancelOpening = () => {
|
|
26603
26753
|
runtime.session.closed = true;
|
|
26604
26754
|
runtime.lifetime.abort();
|
|
@@ -26645,7 +26795,7 @@ class CodexAgent {
|
|
|
26645
26795
|
stopObservingOpen();
|
|
26646
26796
|
}
|
|
26647
26797
|
}
|
|
26648
|
-
installRuntime(session, capabilities, config2, modelProvider) {
|
|
26798
|
+
installRuntime(session, capabilities, config2, modelProvider, readOnly = false) {
|
|
26649
26799
|
const client = new ClientSession(session.id, this.link, capabilities);
|
|
26650
26800
|
const bridge = new EventBridge(client, session);
|
|
26651
26801
|
const turnContext = new TurnContext(session.id);
|
|
@@ -26655,7 +26805,7 @@ class CodexAgent {
|
|
|
26655
26805
|
};
|
|
26656
26806
|
const approval = new CodexApprovalHandler(client, turnContext, signal);
|
|
26657
26807
|
const elicitation = new CodexElicitationHandler(client, turnContext, signal);
|
|
26658
|
-
const runtime = { config: config2, modelProvider, stale: false, session, client, bridge, turnContext, elicitation, lifetime: new AbortController, queue: Promise.resolve() };
|
|
26808
|
+
const runtime = { config: config2, modelProvider, stale: false, readOnly, session, client, bridge, turnContext, elicitation, lifetime: new AbortController, queue: Promise.resolve() };
|
|
26659
26809
|
const drained = (operation) => async (params) => {
|
|
26660
26810
|
await abortable(this.drain(runtime), this.codex.disconnectSignal);
|
|
26661
26811
|
return await operation(params);
|
|
@@ -26841,6 +26991,61 @@ class CodexAgent {
|
|
|
26841
26991
|
await this.withCodex(() => this.codex.threadUnarchive({ threadId: params.sessionId }));
|
|
26842
26992
|
return {};
|
|
26843
26993
|
}
|
|
26994
|
+
async skillsList(params) {
|
|
26995
|
+
this.requireInitialized("_codex/skills_list");
|
|
26996
|
+
return await this.withCodex(() => this.codex.skillsList(params));
|
|
26997
|
+
}
|
|
26998
|
+
async skillsConfigWrite(params) {
|
|
26999
|
+
this.requireInitialized("_codex/skills_config_write");
|
|
27000
|
+
const response = await this.withCodex(() => this.codex.skillsConfigWrite(params));
|
|
27001
|
+
this.notifySkillsChanged();
|
|
27002
|
+
return response;
|
|
27003
|
+
}
|
|
27004
|
+
async pluginList(params) {
|
|
27005
|
+
this.requireInitialized("_codex/plugin_list");
|
|
27006
|
+
return await this.withCodex(() => this.codex.pluginList(params));
|
|
27007
|
+
}
|
|
27008
|
+
async pluginInstalled(params) {
|
|
27009
|
+
this.requireInitialized("_codex/plugin_installed");
|
|
27010
|
+
return await this.withCodex(() => this.codex.pluginInstalled(params));
|
|
27011
|
+
}
|
|
27012
|
+
async pluginInstall(params) {
|
|
27013
|
+
this.requireInitialized("_codex/plugin_install");
|
|
27014
|
+
const response = await this.withCodex(() => this.codex.pluginInstall(params));
|
|
27015
|
+
this.notifySkillsChanged();
|
|
27016
|
+
return response;
|
|
27017
|
+
}
|
|
27018
|
+
async pluginUninstall(params) {
|
|
27019
|
+
this.requireInitialized("_codex/plugin_uninstall");
|
|
27020
|
+
await this.withCodex(() => this.codex.pluginUninstall(params));
|
|
27021
|
+
this.notifySkillsChanged();
|
|
27022
|
+
return {};
|
|
27023
|
+
}
|
|
27024
|
+
async pluginRead(params) {
|
|
27025
|
+
this.requireInitialized("_codex/plugin_read");
|
|
27026
|
+
return await this.withCodex(() => this.codex.pluginRead(params));
|
|
27027
|
+
}
|
|
27028
|
+
async marketplaceAdd(params) {
|
|
27029
|
+
this.requireInitialized("_codex/marketplace_add");
|
|
27030
|
+
const response = await this.withCodex(() => this.codex.marketplaceAdd(params));
|
|
27031
|
+
this.notifySkillsChanged();
|
|
27032
|
+
return response;
|
|
27033
|
+
}
|
|
27034
|
+
async marketplaceRemove(params) {
|
|
27035
|
+
this.requireInitialized("_codex/marketplace_remove");
|
|
27036
|
+
await this.withCodex(() => this.codex.marketplaceRemove(params));
|
|
27037
|
+
this.notifySkillsChanged();
|
|
27038
|
+
return {};
|
|
27039
|
+
}
|
|
27040
|
+
async marketplaceUpgrade(params) {
|
|
27041
|
+
this.requireInitialized("_codex/marketplace_upgrade");
|
|
27042
|
+
const response = await this.withCodex(() => this.codex.marketplaceUpgrade(params));
|
|
27043
|
+
this.notifySkillsChanged();
|
|
27044
|
+
return response;
|
|
27045
|
+
}
|
|
27046
|
+
notifySkillsChanged() {
|
|
27047
|
+
this.link.notify("_codex/skills_changed", {}).catch((error40) => logger.error("skills change notification failed", error40));
|
|
27048
|
+
}
|
|
26844
27049
|
async closeRuntime(sessionId) {
|
|
26845
27050
|
const deadline = performance.now() + this.closeGraceMs;
|
|
26846
27051
|
const runtime = this.sessions.get(sessionId);
|
|
@@ -26867,7 +27072,7 @@ class CodexAgent {
|
|
|
26867
27072
|
runtime.session.activeTurn = null;
|
|
26868
27073
|
turn.resolveFinished();
|
|
26869
27074
|
}
|
|
26870
|
-
const unsubscribe = this.unsubscribeThread(sessionId);
|
|
27075
|
+
const unsubscribe = runtime.readOnly ? Promise.resolve() : this.unsubscribeThread(sessionId);
|
|
26871
27076
|
let remoteUnsubscribe = "confirmed";
|
|
26872
27077
|
try {
|
|
26873
27078
|
if (!await within(unsubscribe, Math.max(0, deadline - performance.now()))) {
|
|
@@ -26896,11 +27101,46 @@ class CodexAgent {
|
|
|
26896
27101
|
}
|
|
26897
27102
|
async setSessionConfigOptionInternal(params) {
|
|
26898
27103
|
const runtime = this.runtime(params.sessionId, "session/set_config_option");
|
|
27104
|
+
assertWritable(runtime);
|
|
27105
|
+
if (params.configId === MODEL_CONFIG_ID && this.providers.mode === "catalog" && typeof params.value === "string") {
|
|
27106
|
+
const toGateway = this.providers.isGatewayModel(params.value);
|
|
27107
|
+
if (toGateway !== (runtime.modelProvider === GATEWAY_MODEL_PROVIDER))
|
|
27108
|
+
await this.rerouteSession(runtime, toGateway, params.value);
|
|
27109
|
+
}
|
|
26899
27110
|
await this.withCodex(() => applyConfigOption(runtime.session, this.codex, params));
|
|
26900
27111
|
const configOptions = sessionConfigOptions(runtime.session);
|
|
26901
27112
|
await runtime.client.update({ sessionUpdate: "config_option_update", configOptions });
|
|
26902
27113
|
return { configOptions };
|
|
26903
27114
|
}
|
|
27115
|
+
async rerouteSession(runtime, toGateway, modelId) {
|
|
27116
|
+
const { session } = runtime;
|
|
27117
|
+
this.assertRoutingAvailable();
|
|
27118
|
+
if (session.activeTurn !== null) {
|
|
27119
|
+
throw RequestError.invalidRequest({ sessionId: session.id }, "The model provider cannot change while a turn is running; cancel it or wait for idle");
|
|
27120
|
+
}
|
|
27121
|
+
try {
|
|
27122
|
+
await this.codex.threadTurnsList({ threadId: session.id, limit: 1, itemsView: "notLoaded" });
|
|
27123
|
+
} catch {
|
|
27124
|
+
await this.withCodex(() => this.codex.threadInjectItems({ threadId: session.id, items: [providerChangeNote()] }));
|
|
27125
|
+
}
|
|
27126
|
+
const config2 = this.providers.routeConfig(runtime.config, toGateway);
|
|
27127
|
+
const modelProvider = toGateway ? GATEWAY_MODEL_PROVIDER : await this.withCodex(() => this.resolveModelProvider());
|
|
27128
|
+
await this.codex.threadUnsubscribe({ threadId: session.id });
|
|
27129
|
+
try {
|
|
27130
|
+
await this.withCodex(() => this.codex.threadResume({ threadId: session.id, cwd: session.cwd, config: config2, modelProvider, model: modelId, excludeTurns: true }));
|
|
27131
|
+
} catch (error40) {
|
|
27132
|
+
try {
|
|
27133
|
+
await this.codex.threadResume({ threadId: session.id, cwd: session.cwd, config: runtime.config, modelProvider: runtime.modelProvider, model: session.model.model, excludeTurns: true });
|
|
27134
|
+
} catch (rollbackError) {
|
|
27135
|
+
runtime.stale = true;
|
|
27136
|
+
logger.error("Provider rollback failed", rollbackError, { sessionId: session.id });
|
|
27137
|
+
}
|
|
27138
|
+
throw RequestError.internalError({ sessionId: session.id, stale: runtime.stale }, `Could not move the session to ${toGateway ? "the gateway" : "Codex's provider"}: ${errorMessage(error40)}`);
|
|
27139
|
+
}
|
|
27140
|
+
runtime.config = config2;
|
|
27141
|
+
runtime.modelProvider = modelProvider;
|
|
27142
|
+
runtime.stale = false;
|
|
27143
|
+
}
|
|
26904
27144
|
runtime(sessionId, method) {
|
|
26905
27145
|
this.requireInitialized(method);
|
|
26906
27146
|
const runtime = this.sessions.get(sessionId);
|
|
@@ -26909,16 +27149,20 @@ class CodexAgent {
|
|
|
26909
27149
|
return runtime;
|
|
26910
27150
|
}
|
|
26911
27151
|
async prompt(params) {
|
|
27152
|
+
return await this.promptInternal(params);
|
|
27153
|
+
}
|
|
27154
|
+
async promptInternal(params, allowReentry = false) {
|
|
26912
27155
|
this.assertRoutingAvailable();
|
|
26913
|
-
if (this.sessionMutations.has(params.sessionId))
|
|
27156
|
+
if (!allowReentry && this.sessionMutations.has(params.sessionId))
|
|
26914
27157
|
throw RequestError.invalidRequest({ sessionId: params.sessionId }, "Session lifecycle or configuration work is in progress; retry when it finishes");
|
|
26915
27158
|
const runtime = this.runtime(params.sessionId, "session/prompt");
|
|
27159
|
+
assertWritable(runtime);
|
|
26916
27160
|
if (runtime.stale)
|
|
26917
27161
|
throw RequestError.invalidRequest({ sessionId: params.sessionId, _meta: { codex: { routing: { stale: true } } } }, "Session routing is stale; resume the session or successfully change providers before prompting");
|
|
26918
|
-
const { session } = runtime;
|
|
26919
27162
|
if (!Array.isArray(params.prompt) || params.prompt.length === 0) {
|
|
26920
27163
|
throw RequestError.invalidParams(undefined, "prompt must contain at least one content block");
|
|
26921
27164
|
}
|
|
27165
|
+
const { session } = runtime;
|
|
26922
27166
|
const model = findModel(session.catalog, session.model.model);
|
|
26923
27167
|
if (!modelSupportsImages(model) && params.prompt.some((block) => block.type === "image")) {
|
|
26924
27168
|
throw RequestError.invalidParams({ model: session.model.model }, "The current model does not support image input");
|
|
@@ -26943,7 +27187,7 @@ class CodexAgent {
|
|
|
26943
27187
|
} catch (error40) {
|
|
26944
27188
|
if (this.completedTurns.get(turn) === turnId || runtime.session.activeTurn !== turn) {
|
|
26945
27189
|
await turn.finished;
|
|
26946
|
-
return await this.
|
|
27190
|
+
return await this.promptInternal(params, true);
|
|
26947
27191
|
}
|
|
26948
27192
|
throw RequestError.invalidRequest({ sessionId: params.sessionId, turnId }, `Could not steer the running turn: ${errorMessage(error40)}`);
|
|
26949
27193
|
}
|
|
@@ -27211,7 +27455,7 @@ ${error40.additionalDetails}` : error40.message;
|
|
|
27211
27455
|
}
|
|
27212
27456
|
withSkillsContext(cwd, additionalDirectories, operation) {
|
|
27213
27457
|
const run = async () => {
|
|
27214
|
-
const roots = additionalDirectories.map((root) =>
|
|
27458
|
+
const roots = additionalDirectories.map((root) => path7.join(root, ".agents", "skills"));
|
|
27215
27459
|
if (roots.length !== this.skillRoots.length || roots.some((root, index) => root !== this.skillRoots[index])) {
|
|
27216
27460
|
this.changingSkillRoots = true;
|
|
27217
27461
|
try {
|
|
@@ -27245,7 +27489,7 @@ ${error40.additionalDetails}` : error40.message;
|
|
|
27245
27489
|
}
|
|
27246
27490
|
async refreshAvailableCommands(runtime) {
|
|
27247
27491
|
const run = async () => {
|
|
27248
|
-
const roots = runtime.session.additionalDirectories.map((root) =>
|
|
27492
|
+
const roots = runtime.session.additionalDirectories.map((root) => path7.join(root, ".agents", "skills"));
|
|
27249
27493
|
if (runtime.session.closed || roots.length !== this.skillRoots.length || roots.some((root, i) => root !== this.skillRoots[i]))
|
|
27250
27494
|
return;
|
|
27251
27495
|
return await this.loadSkills(runtime.session.cwd, runtime.session.additionalDirectories);
|
|
@@ -27348,6 +27592,16 @@ function interruptedTurn(threadId) {
|
|
|
27348
27592
|
turn: { id: "", items: [], itemsView: "notLoaded", status: "interrupted", error: null, startedAt: null, completedAt: null, durationMs: null }
|
|
27349
27593
|
};
|
|
27350
27594
|
}
|
|
27595
|
+
function objectParams() {
|
|
27596
|
+
return (raw) => {
|
|
27597
|
+
if (raw === undefined || raw === null)
|
|
27598
|
+
return {};
|
|
27599
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
27600
|
+
throw RequestError.invalidParams({ params: raw }, "expected a params object");
|
|
27601
|
+
}
|
|
27602
|
+
return raw;
|
|
27603
|
+
};
|
|
27604
|
+
}
|
|
27351
27605
|
function parseSessionIdParams(raw) {
|
|
27352
27606
|
const sessionId = raw?.sessionId;
|
|
27353
27607
|
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
@@ -27359,6 +27613,34 @@ function archivedFilter(meta) {
|
|
|
27359
27613
|
const codex = meta?.codex;
|
|
27360
27614
|
return codex?.archived === true;
|
|
27361
27615
|
}
|
|
27616
|
+
function providerChangeNote() {
|
|
27617
|
+
return { type: "message", role: "developer", content: [{ type: "input_text", text: "The user changed this conversation's model before sending a message." }] };
|
|
27618
|
+
}
|
|
27619
|
+
function assertWritable(runtime) {
|
|
27620
|
+
if (!runtime.readOnly)
|
|
27621
|
+
return;
|
|
27622
|
+
throw RequestError.invalidRequest({ codex: { readOnly: true } }, "Thread is open in another Codex client");
|
|
27623
|
+
}
|
|
27624
|
+
function readOnlyThread(thread, cwd) {
|
|
27625
|
+
return {
|
|
27626
|
+
thread,
|
|
27627
|
+
model: thread.model ?? "",
|
|
27628
|
+
modelProvider: thread.modelProvider,
|
|
27629
|
+
serviceTier: null,
|
|
27630
|
+
cwd,
|
|
27631
|
+
instructionSources: [],
|
|
27632
|
+
approvalPolicy: "on-request",
|
|
27633
|
+
approvalsReviewer: "user",
|
|
27634
|
+
sandbox: { type: "readOnly", networkAccess: false },
|
|
27635
|
+
reasoningEffort: thread.reasoningEffort,
|
|
27636
|
+
turnsBackwardsCursor: null,
|
|
27637
|
+
itemsBackwardsCursor: null
|
|
27638
|
+
};
|
|
27639
|
+
}
|
|
27640
|
+
function requestedModelOf(meta) {
|
|
27641
|
+
const raw = meta?.alwith?.model;
|
|
27642
|
+
return typeof raw === "string" && raw.length > 0 ? raw : null;
|
|
27643
|
+
}
|
|
27362
27644
|
function seedHistoryOf(meta) {
|
|
27363
27645
|
const raw = meta?.codex?.seedHistory;
|
|
27364
27646
|
if (raw === undefined || raw === null)
|
|
@@ -27405,7 +27687,7 @@ function createAgentApp(options) {
|
|
|
27405
27687
|
};
|
|
27406
27688
|
return agent({ name: options.info.name }).onConnect((connection) => {
|
|
27407
27689
|
agent2 = new CodexAgent(connection.client, options);
|
|
27408
|
-
}).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(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));
|
|
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));
|
|
27409
27691
|
}
|
|
27410
27692
|
|
|
27411
27693
|
// src/codex/AppServerClient.ts
|
|
@@ -27788,6 +28070,33 @@ class AppServerClient {
|
|
|
27788
28070
|
skillsExtraRootsSet(params) {
|
|
27789
28071
|
return this.send({ method: "skills/extraRoots/set", params });
|
|
27790
28072
|
}
|
|
28073
|
+
skillsConfigWrite(params) {
|
|
28074
|
+
return this.send({ method: "skills/config/write", params });
|
|
28075
|
+
}
|
|
28076
|
+
pluginList(params) {
|
|
28077
|
+
return this.send({ method: "plugin/list", params });
|
|
28078
|
+
}
|
|
28079
|
+
pluginInstalled(params) {
|
|
28080
|
+
return this.send({ method: "plugin/installed", params });
|
|
28081
|
+
}
|
|
28082
|
+
pluginInstall(params) {
|
|
28083
|
+
return this.send({ method: "plugin/install", params });
|
|
28084
|
+
}
|
|
28085
|
+
pluginUninstall(params) {
|
|
28086
|
+
return this.send({ method: "plugin/uninstall", params });
|
|
28087
|
+
}
|
|
28088
|
+
pluginRead(params) {
|
|
28089
|
+
return this.send({ method: "plugin/read", params });
|
|
28090
|
+
}
|
|
28091
|
+
marketplaceAdd(params) {
|
|
28092
|
+
return this.send({ method: "marketplace/add", params });
|
|
28093
|
+
}
|
|
28094
|
+
marketplaceRemove(params) {
|
|
28095
|
+
return this.send({ method: "marketplace/remove", params });
|
|
28096
|
+
}
|
|
28097
|
+
marketplaceUpgrade(params) {
|
|
28098
|
+
return this.send({ method: "marketplace/upgrade", params });
|
|
28099
|
+
}
|
|
27791
28100
|
accountRead(params) {
|
|
27792
28101
|
return this.send({ method: "account/read", params });
|
|
27793
28102
|
}
|
|
@@ -27826,7 +28135,7 @@ function threadIdOf(notification) {
|
|
|
27826
28135
|
|
|
27827
28136
|
// src/codex/process.ts
|
|
27828
28137
|
import { existsSync } from "fs";
|
|
27829
|
-
import
|
|
28138
|
+
import path8 from "path";
|
|
27830
28139
|
import { spawn, spawnSync } from "child_process";
|
|
27831
28140
|
import { createRequire } from "module";
|
|
27832
28141
|
|
|
@@ -28119,11 +28428,11 @@ function resolveCodexLauncher(codexPath) {
|
|
|
28119
28428
|
const target = targets[platform2];
|
|
28120
28429
|
if (!target)
|
|
28121
28430
|
throw new Error(`Unsupported Codex platform: ${platform2}`);
|
|
28122
|
-
let vendor =
|
|
28431
|
+
let vendor = path8.resolve(path8.dirname(launcher), "..", "vendor");
|
|
28123
28432
|
try {
|
|
28124
|
-
vendor =
|
|
28433
|
+
vendor = path8.join(path8.dirname(platformRequire.resolve(`@openai/codex-${platform2}/package.json`)), "vendor");
|
|
28125
28434
|
} catch {}
|
|
28126
|
-
const command =
|
|
28435
|
+
const command = path8.join(vendor, target, "bin", process.platform === "win32" ? "codex.exe" : "codex");
|
|
28127
28436
|
if (!existsSync(command))
|
|
28128
28437
|
throw new Error(`Missing Codex platform binary: ${command}`);
|
|
28129
28438
|
return { command, prefixArgs: [], shell: false };
|