@agentclientprotocol/codex-acp 1.8.0 → 1.9.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/dist/index.js +426 -20
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -24051,6 +24051,37 @@ function fallbackName(sessionId) {
|
|
|
24051
24051
|
return `Agent ${suffix}`;
|
|
24052
24052
|
}
|
|
24053
24053
|
|
|
24054
|
+
// src/RateLimitsMap.ts
|
|
24055
|
+
function rateLimitId(snapshot, fallback = "codex") {
|
|
24056
|
+
return snapshot.limitId ?? fallback;
|
|
24057
|
+
}
|
|
24058
|
+
function createRateLimitsMap(response) {
|
|
24059
|
+
const result = /* @__PURE__ */ new Map();
|
|
24060
|
+
const snapshots = Object.entries(response.rateLimitsByLimitId ?? {}).filter((entry) => entry[1] !== void 0);
|
|
24061
|
+
if (snapshots.length === 0) {
|
|
24062
|
+
snapshots.push([rateLimitId(response.rateLimits), response.rateLimits]);
|
|
24063
|
+
}
|
|
24064
|
+
for (const [fallbackId, snapshot] of snapshots) {
|
|
24065
|
+
const limitId = rateLimitId(snapshot, fallbackId);
|
|
24066
|
+
result.set(limitId, {
|
|
24067
|
+
limitId,
|
|
24068
|
+
limitName: snapshot.limitName ?? limitId,
|
|
24069
|
+
snapshot
|
|
24070
|
+
});
|
|
24071
|
+
}
|
|
24072
|
+
return result;
|
|
24073
|
+
}
|
|
24074
|
+
function mergeRateLimitSnapshot(previous, update) {
|
|
24075
|
+
return {
|
|
24076
|
+
...update,
|
|
24077
|
+
limitId: update.limitId ?? "codex",
|
|
24078
|
+
credits: update.credits ?? previous.credits,
|
|
24079
|
+
individualLimit: update.individualLimit ?? previous.individualLimit,
|
|
24080
|
+
spendControlReached: update.spendControlReached ?? previous.spendControlReached,
|
|
24081
|
+
planType: update.planType ?? previous.planType
|
|
24082
|
+
};
|
|
24083
|
+
}
|
|
24084
|
+
|
|
24054
24085
|
// src/CodexEventHandler.ts
|
|
24055
24086
|
var MAX_SESSION_FAILURE_TITLE_LENGTH = 240;
|
|
24056
24087
|
var SESSION_FAILURE_POLICY = {
|
|
@@ -24159,11 +24190,14 @@ var CodexEventHandler = class _CodexEventHandler {
|
|
|
24159
24190
|
terminalCommandOutputIds = /* @__PURE__ */ new Set();
|
|
24160
24191
|
agentMessagePhases = /* @__PURE__ */ new Map();
|
|
24161
24192
|
subagents;
|
|
24193
|
+
/** Connection-level `authStatus` sink; the app-server account push feeds it. */
|
|
24194
|
+
onAccountUpdated;
|
|
24162
24195
|
constructor(connection, sessionState, supportsPlanUpdates = false, supportsTypedSessionFailures = false, sessionFailureEpoch = randomUUID(), subagents = new CodexSubagentEventRouter(
|
|
24163
24196
|
sessionState.sessionId,
|
|
24164
24197
|
false,
|
|
24165
24198
|
new ACPSessionConnection(connection, sessionState.sessionId)
|
|
24166
|
-
)) {
|
|
24199
|
+
), onAccountUpdated) {
|
|
24200
|
+
this.onAccountUpdated = onAccountUpdated;
|
|
24167
24201
|
this.sessionState = sessionState;
|
|
24168
24202
|
this.supportsPlanUpdates = supportsPlanUpdates;
|
|
24169
24203
|
this.supportsTypedSessionFailures = supportsTypedSessionFailures;
|
|
@@ -24387,6 +24421,9 @@ var CodexEventHandler = class _CodexEventHandler {
|
|
|
24387
24421
|
case "account/rateLimits/updated":
|
|
24388
24422
|
this.handleRateLimitsUpdated(notification.params);
|
|
24389
24423
|
return null;
|
|
24424
|
+
case "account/updated":
|
|
24425
|
+
this.onAccountUpdated?.(notification.params);
|
|
24426
|
+
return null;
|
|
24390
24427
|
case "configWarning":
|
|
24391
24428
|
return await this.createConfigWarningEvent(notification.params);
|
|
24392
24429
|
case "warning":
|
|
@@ -24437,7 +24474,6 @@ var CodexEventHandler = class _CodexEventHandler {
|
|
|
24437
24474
|
case "turn/moderationMetadata":
|
|
24438
24475
|
case "item/fileChange/outputDelta":
|
|
24439
24476
|
case "item/fileChange/patchUpdated":
|
|
24440
|
-
case "account/updated":
|
|
24441
24477
|
case "fs/changed":
|
|
24442
24478
|
case "mcpServer/startupStatus/updated":
|
|
24443
24479
|
case "mcpServer/event/stream/notification":
|
|
@@ -25083,11 +25119,13 @@ ${event.stdin}
|
|
|
25083
25119
|
if (!this.sessionState.rateLimits) {
|
|
25084
25120
|
this.sessionState.rateLimits = /* @__PURE__ */ new Map();
|
|
25085
25121
|
}
|
|
25086
|
-
const limitId = params.rateLimits.limitId ??
|
|
25122
|
+
const limitId = params.rateLimits.limitId ?? "codex";
|
|
25123
|
+
const existingEntry = this.sessionState.rateLimits.get(limitId);
|
|
25124
|
+
const snapshot = existingEntry ? mergeRateLimitSnapshot(existingEntry.snapshot, params.rateLimits) : { ...params.rateLimits, limitId };
|
|
25087
25125
|
this.sessionState.rateLimits.set(limitId, {
|
|
25088
25126
|
limitId,
|
|
25089
|
-
limitName:
|
|
25090
|
-
snapshot
|
|
25127
|
+
limitName: snapshot.limitName ?? existingEntry?.limitName ?? limitId,
|
|
25128
|
+
snapshot
|
|
25091
25129
|
});
|
|
25092
25130
|
}
|
|
25093
25131
|
handleFuzzyFileSearchSessionUpdated(params) {
|
|
@@ -27247,7 +27285,7 @@ var package_default = {
|
|
|
27247
27285
|
publishConfig: {
|
|
27248
27286
|
access: "public"
|
|
27249
27287
|
},
|
|
27250
|
-
version: "1.
|
|
27288
|
+
version: "1.9.0",
|
|
27251
27289
|
description: "",
|
|
27252
27290
|
main: "dist/index.js",
|
|
27253
27291
|
bin: {
|
|
@@ -27310,7 +27348,7 @@ var package_default = {
|
|
|
27310
27348
|
},
|
|
27311
27349
|
dependencies: {
|
|
27312
27350
|
"@agentclientprotocol/sdk": "^1.4.0",
|
|
27313
|
-
"@openai/codex": "^0.
|
|
27351
|
+
"@openai/codex": "^0.153.2",
|
|
27314
27352
|
diff: "^9.0.0",
|
|
27315
27353
|
open: "^11.0.1",
|
|
27316
27354
|
"vscode-jsonrpc": "^9.0.1",
|
|
@@ -27908,6 +27946,12 @@ var CodexAcpClient = class {
|
|
|
27908
27946
|
config;
|
|
27909
27947
|
modelProvider;
|
|
27910
27948
|
gatewayConfig;
|
|
27949
|
+
/**
|
|
27950
|
+
* Where the stored gateway routing came from: the `gateway` auth method
|
|
27951
|
+
* (agent-owned authentication) or the ACP `providers/*` API (client-driven
|
|
27952
|
+
* routing). `authStatus` reports only the agent-owned one.
|
|
27953
|
+
*/
|
|
27954
|
+
gatewayConfigSource;
|
|
27911
27955
|
pendingLoginCompleted = null;
|
|
27912
27956
|
pendingAccountUpdated = null;
|
|
27913
27957
|
sessionNotificationQueues = /* @__PURE__ */ new Map();
|
|
@@ -27919,6 +27963,7 @@ var CodexAcpClient = class {
|
|
|
27919
27963
|
this.config = codexConfig ?? {};
|
|
27920
27964
|
this.modelProvider = modelProvider ?? null;
|
|
27921
27965
|
this.gatewayConfig = null;
|
|
27966
|
+
this.gatewayConfigSource = null;
|
|
27922
27967
|
this.subagents = new CodexSubagentSubscriptions(codexClient);
|
|
27923
27968
|
}
|
|
27924
27969
|
get appServerClient() {
|
|
@@ -27951,6 +27996,7 @@ var CodexAcpClient = class {
|
|
|
27951
27996
|
throw RequestError.invalidRequest();
|
|
27952
27997
|
}
|
|
27953
27998
|
this.gatewayConfig = null;
|
|
27999
|
+
this.gatewayConfigSource = null;
|
|
27954
28000
|
switch (authRequest.methodId) {
|
|
27955
28001
|
case "api-key":
|
|
27956
28002
|
return await this.authenticateWithApiKey(authRequest);
|
|
@@ -28034,7 +28080,7 @@ var CodexAcpClient = class {
|
|
|
28034
28080
|
apiType: GatewayAuthMethod._meta.gateway.protocol,
|
|
28035
28081
|
headers: gatewaySettings.headers,
|
|
28036
28082
|
providerName: gatewaySettings.providerName
|
|
28037
|
-
});
|
|
28083
|
+
}, "authentication");
|
|
28038
28084
|
return true;
|
|
28039
28085
|
}
|
|
28040
28086
|
readApiKeyFromEnv() {
|
|
@@ -28080,6 +28126,11 @@ var CodexAcpClient = class {
|
|
|
28080
28126
|
};
|
|
28081
28127
|
}
|
|
28082
28128
|
}
|
|
28129
|
+
/**
|
|
28130
|
+
* The provider that actually serves requests, ACP-configured gateway
|
|
28131
|
+
* routing included. Use {@link getAgentConfiguredModelProvider} instead
|
|
28132
|
+
* when asking what the agent itself is configured with (`authStatus`).
|
|
28133
|
+
*/
|
|
28083
28134
|
async getCurrentModelProvider() {
|
|
28084
28135
|
const sessionModelProvider = this.getModelProvider();
|
|
28085
28136
|
if (sessionModelProvider !== null) {
|
|
@@ -28105,7 +28156,7 @@ var CodexAcpClient = class {
|
|
|
28105
28156
|
* method and the ACP `providers/set` method. Throws `invalid_params` for an
|
|
28106
28157
|
* unsupported protocol or a malformed base URL.
|
|
28107
28158
|
*/
|
|
28108
|
-
applyGatewayConfig(params) {
|
|
28159
|
+
applyGatewayConfig(params, source) {
|
|
28109
28160
|
const apiType = params.apiType;
|
|
28110
28161
|
const wireApi = SUPPORTED_GATEWAY_PROTOCOLS[apiType];
|
|
28111
28162
|
if (!wireApi) {
|
|
@@ -28122,6 +28173,7 @@ var CodexAcpClient = class {
|
|
|
28122
28173
|
"X-Client-Feature-ID": "codex",
|
|
28123
28174
|
...params.headers
|
|
28124
28175
|
};
|
|
28176
|
+
this.gatewayConfigSource = source;
|
|
28125
28177
|
this.gatewayConfig = {
|
|
28126
28178
|
modelProvider: CUSTOM_GATEWAY_PROVIDER_ID,
|
|
28127
28179
|
config: {
|
|
@@ -28186,7 +28238,7 @@ var CodexAcpClient = class {
|
|
|
28186
28238
|
apiType: request.apiType,
|
|
28187
28239
|
baseUrl: request.baseUrl,
|
|
28188
28240
|
headers: request.headers
|
|
28189
|
-
});
|
|
28241
|
+
}, "acpProviders");
|
|
28190
28242
|
logger.log("providers/set applied", {
|
|
28191
28243
|
providerId: request.providerId,
|
|
28192
28244
|
apiType: request.apiType,
|
|
@@ -28202,6 +28254,7 @@ var CodexAcpClient = class {
|
|
|
28202
28254
|
const overrideWasActive = this.gatewayConfig !== null;
|
|
28203
28255
|
if (request.providerId === OPENAI_PROVIDER_ID) {
|
|
28204
28256
|
this.gatewayConfig = null;
|
|
28257
|
+
this.gatewayConfigSource = null;
|
|
28205
28258
|
}
|
|
28206
28259
|
const current = this.gatewayConfig ? {
|
|
28207
28260
|
apiType: gatewayApiTypeFromConfig(this.gatewayConfig),
|
|
@@ -28219,6 +28272,36 @@ var CodexAcpClient = class {
|
|
|
28219
28272
|
async getAccount() {
|
|
28220
28273
|
return this.codexClient.accountRead({ refreshToken: false });
|
|
28221
28274
|
}
|
|
28275
|
+
async getRateLimits() {
|
|
28276
|
+
return this.codexClient.accountRateLimitsRead();
|
|
28277
|
+
}
|
|
28278
|
+
/**
|
|
28279
|
+
* Presentable name of the gateway the agent itself authenticated against
|
|
28280
|
+
* (the `gateway` auth method), or `null`. Routing that the client
|
|
28281
|
+
* configured through `providers/set` is deliberately not reported here:
|
|
28282
|
+
* `authStatus` describes the agent-owned login only.
|
|
28283
|
+
*/
|
|
28284
|
+
getAuthGatewayProviderName() {
|
|
28285
|
+
return this.gatewayConfigSource === "authentication" ? this.gatewayConfig?.config.name ?? null : null;
|
|
28286
|
+
}
|
|
28287
|
+
/** Whether this provider id is client-driven routing set through `providers/set`. */
|
|
28288
|
+
isClientConfiguredProvider(providerId) {
|
|
28289
|
+
return providerId === CUSTOM_GATEWAY_PROVIDER_ID && this.gatewayConfigSource === "acpProviders";
|
|
28290
|
+
}
|
|
28291
|
+
/**
|
|
28292
|
+
* The model provider the agent itself is configured with (launch option or
|
|
28293
|
+
* Codex config), ignoring any ACP-configured gateway routing. The
|
|
28294
|
+
* routing-aware counterpart is {@link getCurrentModelProvider}.
|
|
28295
|
+
*/
|
|
28296
|
+
async getAgentConfiguredModelProvider() {
|
|
28297
|
+
const provider = this.getModelProvider();
|
|
28298
|
+
const agentProvider = this.isClientConfiguredProvider(provider) ? this.modelProvider : provider;
|
|
28299
|
+
if (agentProvider !== null) {
|
|
28300
|
+
return agentProvider;
|
|
28301
|
+
}
|
|
28302
|
+
const settingsModelProvider = await this.codexClient.configRead({ includeLayers: false });
|
|
28303
|
+
return settingsModelProvider?.config?.model_provider ?? null;
|
|
28304
|
+
}
|
|
28222
28305
|
async resumeSession(request, onSubscribed) {
|
|
28223
28306
|
const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
|
|
28224
28307
|
await this.refreshSkills(request.cwd, additionalDirectories);
|
|
@@ -29473,6 +29556,9 @@ var CodexAppServerClient = class {
|
|
|
29473
29556
|
async accountRead(params) {
|
|
29474
29557
|
return await this.sendRequest({ method: "account/read", params });
|
|
29475
29558
|
}
|
|
29559
|
+
async accountRateLimitsRead() {
|
|
29560
|
+
return await this.sendRequest({ method: "account/rateLimits/read", params: void 0 });
|
|
29561
|
+
}
|
|
29476
29562
|
//TODO create type-safe helper
|
|
29477
29563
|
async awaitTurnCompleted(threadId, turnId) {
|
|
29478
29564
|
return await new Promise((resolve) => {
|
|
@@ -30079,6 +30165,7 @@ var CodexCommands = class {
|
|
|
30079
30165
|
return { handled: true, turnCompleted };
|
|
30080
30166
|
}
|
|
30081
30167
|
case "status": {
|
|
30168
|
+
await this.refreshRateLimits(sessionState);
|
|
30082
30169
|
const session = new ACPSessionConnection(this.connection, sessionId);
|
|
30083
30170
|
const message = this.buildStatusMessage(sessionState);
|
|
30084
30171
|
await session.update(createAgentTextMessageChunk(message));
|
|
@@ -30227,6 +30314,16 @@ var CodexCommands = class {
|
|
|
30227
30314
|
];
|
|
30228
30315
|
return lines.join(" \n");
|
|
30229
30316
|
}
|
|
30317
|
+
async refreshRateLimits(sessionState) {
|
|
30318
|
+
try {
|
|
30319
|
+
const response = await this.runWithProcessCheck(() => this.codexAcpClient.getRateLimits());
|
|
30320
|
+
if (response) {
|
|
30321
|
+
sessionState.rateLimits = createRateLimitsMap(response);
|
|
30322
|
+
}
|
|
30323
|
+
} catch (err) {
|
|
30324
|
+
logger.error(`Failed to refresh rate limits for session ${sessionState.sessionId}`, err);
|
|
30325
|
+
}
|
|
30326
|
+
}
|
|
30230
30327
|
formatAccountInfo(account) {
|
|
30231
30328
|
if (!account) {
|
|
30232
30329
|
return "not logged in";
|
|
@@ -30257,10 +30354,10 @@ var CodexCommands = class {
|
|
|
30257
30354
|
return "data not available yet";
|
|
30258
30355
|
}
|
|
30259
30356
|
const used = usage.totalTokens;
|
|
30260
|
-
const
|
|
30357
|
+
const percentUsed = Math.round(used / contextWindow * 100);
|
|
30261
30358
|
const usedFormatted = this.formatTokenCount(used);
|
|
30262
30359
|
const totalFormatted = this.formatTokenCount(contextWindow);
|
|
30263
|
-
return `${
|
|
30360
|
+
return `${percentUsed}% used (${usedFormatted} used / ${totalFormatted})`;
|
|
30264
30361
|
}
|
|
30265
30362
|
formatRateLimitLines(rateLimits) {
|
|
30266
30363
|
if (!rateLimits || rateLimits.size === 0) {
|
|
@@ -30294,8 +30391,31 @@ var CodexCommands = class {
|
|
|
30294
30391
|
lines.push(`**${prefix}Credits:** ${rateLimits.credits.balance}`);
|
|
30295
30392
|
}
|
|
30296
30393
|
}
|
|
30394
|
+
if (rateLimits.individualLimit) {
|
|
30395
|
+
const limit = rateLimits.individualLimit;
|
|
30396
|
+
const used = this.formatCreditAmount(limit.used);
|
|
30397
|
+
const total = this.formatCreditAmount(limit.limit);
|
|
30398
|
+
if (used !== null && total !== null) {
|
|
30399
|
+
const percentLeft = Math.round(Math.min(100, Math.max(0, limit.remainingPercent)));
|
|
30400
|
+
const resetDate = new Date(limit.resetsAt * 1e3).toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
30401
|
+
lines.push(
|
|
30402
|
+
`**${prefix}individual spend limit:** ${percentLeft}% left (${used} of ${total} credits used; resets ${resetDate})`
|
|
30403
|
+
);
|
|
30404
|
+
}
|
|
30405
|
+
}
|
|
30297
30406
|
return lines;
|
|
30298
30407
|
}
|
|
30408
|
+
formatCreditAmount(raw) {
|
|
30409
|
+
const trimmed = raw.trim();
|
|
30410
|
+
if (trimmed.length === 0) {
|
|
30411
|
+
return null;
|
|
30412
|
+
}
|
|
30413
|
+
const value = Number(trimmed);
|
|
30414
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
30415
|
+
return null;
|
|
30416
|
+
}
|
|
30417
|
+
return Math.round(value).toLocaleString("en-US");
|
|
30418
|
+
}
|
|
30299
30419
|
formatWindowLabel(windowDurationMins) {
|
|
30300
30420
|
if (windowDurationMins === null) {
|
|
30301
30421
|
return "Limit";
|
|
@@ -31342,6 +31462,139 @@ function numberValue(value) {
|
|
|
31342
31462
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
31343
31463
|
}
|
|
31344
31464
|
|
|
31465
|
+
// src/AuthStatusMeta.ts
|
|
31466
|
+
var AUTH_STATUS_UPDATE_METHOD = "_auth/status_update";
|
|
31467
|
+
var AUTH_STATUS_META_KEY = "authStatus";
|
|
31468
|
+
function authStatusCapability() {
|
|
31469
|
+
return {};
|
|
31470
|
+
}
|
|
31471
|
+
var NOT_LOGGED_IN_LABEL = "Not logged in";
|
|
31472
|
+
var DEFAULT_GATEWAY_LABEL = "Custom model gateway";
|
|
31473
|
+
var PLAN_DISPLAY_NAMES = {
|
|
31474
|
+
free: "Free",
|
|
31475
|
+
go: "Go",
|
|
31476
|
+
plus: "Plus",
|
|
31477
|
+
pro: "Pro",
|
|
31478
|
+
team: "Team",
|
|
31479
|
+
business: "Business",
|
|
31480
|
+
enterprise: "Enterprise",
|
|
31481
|
+
edu: "Edu"
|
|
31482
|
+
};
|
|
31483
|
+
function planTypePresentable(planType) {
|
|
31484
|
+
if (!planType || planType === "unknown") {
|
|
31485
|
+
return null;
|
|
31486
|
+
}
|
|
31487
|
+
return PLAN_DISPLAY_NAMES[planType] ?? capitalize2(planType);
|
|
31488
|
+
}
|
|
31489
|
+
function capitalize2(value) {
|
|
31490
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
31491
|
+
}
|
|
31492
|
+
function chatGptLabel(planType) {
|
|
31493
|
+
const plan = planTypePresentable(planType);
|
|
31494
|
+
return plan === null ? "ChatGPT" : `ChatGPT ${plan}`;
|
|
31495
|
+
}
|
|
31496
|
+
function gatewayStatus(providerName) {
|
|
31497
|
+
const detail = typeof providerName === "string" && providerName.trim().length > 0 ? providerName.trim() : void 0;
|
|
31498
|
+
return {
|
|
31499
|
+
kind: "gateway",
|
|
31500
|
+
label: DEFAULT_GATEWAY_LABEL,
|
|
31501
|
+
...detail === void 0 ? {} : { detail }
|
|
31502
|
+
};
|
|
31503
|
+
}
|
|
31504
|
+
function unauthenticatedStatus() {
|
|
31505
|
+
return {
|
|
31506
|
+
kind: "none",
|
|
31507
|
+
label: NOT_LOGGED_IN_LABEL
|
|
31508
|
+
};
|
|
31509
|
+
}
|
|
31510
|
+
function fromAccount(account) {
|
|
31511
|
+
if (account === null) {
|
|
31512
|
+
return unauthenticatedStatus();
|
|
31513
|
+
}
|
|
31514
|
+
switch (account.type) {
|
|
31515
|
+
case "chatgpt": {
|
|
31516
|
+
const accountInfo = {};
|
|
31517
|
+
if (account.email) {
|
|
31518
|
+
accountInfo.email = account.email;
|
|
31519
|
+
}
|
|
31520
|
+
if (account.planType) {
|
|
31521
|
+
accountInfo.plan = account.planType;
|
|
31522
|
+
}
|
|
31523
|
+
return {
|
|
31524
|
+
kind: "account",
|
|
31525
|
+
label: chatGptLabel(account.planType),
|
|
31526
|
+
...Object.keys(accountInfo).length > 0 ? { account: accountInfo } : {}
|
|
31527
|
+
};
|
|
31528
|
+
}
|
|
31529
|
+
case "apiKey":
|
|
31530
|
+
return {
|
|
31531
|
+
kind: "api_key",
|
|
31532
|
+
label: "OpenAI API key"
|
|
31533
|
+
};
|
|
31534
|
+
case "amazonBedrock":
|
|
31535
|
+
return {
|
|
31536
|
+
kind: "external",
|
|
31537
|
+
label: "AWS Bedrock"
|
|
31538
|
+
};
|
|
31539
|
+
}
|
|
31540
|
+
}
|
|
31541
|
+
function fromAccountUpdated(notification, previous) {
|
|
31542
|
+
const authMode = notification.authMode;
|
|
31543
|
+
if (authMode === null) {
|
|
31544
|
+
return unauthenticatedStatus();
|
|
31545
|
+
}
|
|
31546
|
+
switch (authMode) {
|
|
31547
|
+
case "chatgpt":
|
|
31548
|
+
case "chatgptAuthTokens": {
|
|
31549
|
+
const status = {
|
|
31550
|
+
kind: "account",
|
|
31551
|
+
label: chatGptLabel(notification.planType)
|
|
31552
|
+
};
|
|
31553
|
+
const accountInfo = {};
|
|
31554
|
+
const previousEmail = previous?.kind === "account" ? previous.account?.email : void 0;
|
|
31555
|
+
if (previousEmail) {
|
|
31556
|
+
accountInfo.email = previousEmail;
|
|
31557
|
+
}
|
|
31558
|
+
if (notification.planType) {
|
|
31559
|
+
accountInfo.plan = notification.planType;
|
|
31560
|
+
}
|
|
31561
|
+
if (Object.keys(accountInfo).length > 0) {
|
|
31562
|
+
status.account = accountInfo;
|
|
31563
|
+
}
|
|
31564
|
+
return status;
|
|
31565
|
+
}
|
|
31566
|
+
case "apikey":
|
|
31567
|
+
return {
|
|
31568
|
+
kind: "api_key",
|
|
31569
|
+
label: "OpenAI API key"
|
|
31570
|
+
};
|
|
31571
|
+
case "personalAccessToken":
|
|
31572
|
+
return {
|
|
31573
|
+
kind: "api_key",
|
|
31574
|
+
label: "OpenAI personal access token"
|
|
31575
|
+
};
|
|
31576
|
+
case "bedrockApiKey":
|
|
31577
|
+
case "bedrockAccessKeys":
|
|
31578
|
+
return {
|
|
31579
|
+
kind: "external",
|
|
31580
|
+
label: "AWS Bedrock"
|
|
31581
|
+
};
|
|
31582
|
+
case "agentIdentity":
|
|
31583
|
+
return {
|
|
31584
|
+
kind: "external",
|
|
31585
|
+
label: "Agent identity"
|
|
31586
|
+
};
|
|
31587
|
+
case "headers":
|
|
31588
|
+
return gatewayStatus();
|
|
31589
|
+
}
|
|
31590
|
+
}
|
|
31591
|
+
function sameAuthStatus(previous, next) {
|
|
31592
|
+
if (!previous) {
|
|
31593
|
+
return false;
|
|
31594
|
+
}
|
|
31595
|
+
return previous.kind === next.kind && previous.label === next.label && previous.detail === next.detail && previous.account?.email === next.account?.email && previous.account?.organization === next.account?.organization && previous.account?.plan === next.account?.plan && JSON.stringify(previous.vendor) === JSON.stringify(next.vendor);
|
|
31596
|
+
}
|
|
31597
|
+
|
|
31345
31598
|
// src/AcpExtensions.ts
|
|
31346
31599
|
var LEGACY_SET_SESSION_MODEL_METHOD = "session/set_model";
|
|
31347
31600
|
var SESSION_STEERING_METHOD = "_session/steering";
|
|
@@ -31525,6 +31778,8 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31525
31778
|
clientCapabilities;
|
|
31526
31779
|
terminalOutputMode;
|
|
31527
31780
|
booleanConfigOptionsSupported;
|
|
31781
|
+
/** Last `authStatus` pushed to the client; used to suppress duplicates. */
|
|
31782
|
+
currentAuthStatus;
|
|
31528
31783
|
sessions;
|
|
31529
31784
|
pendingMcpStartupSessions;
|
|
31530
31785
|
pendingTurnStarts;
|
|
@@ -31561,6 +31816,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31561
31816
|
this.clientCapabilities = null;
|
|
31562
31817
|
this.terminalOutputMode = "terminal_output_delta";
|
|
31563
31818
|
this.booleanConfigOptionsSupported = false;
|
|
31819
|
+
this.currentAuthStatus = null;
|
|
31564
31820
|
this.availableCommands = this.createAvailableCommands(codexAcpClient);
|
|
31565
31821
|
}
|
|
31566
31822
|
createAvailableCommands(client) {
|
|
@@ -31568,7 +31824,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31568
31824
|
this.connection,
|
|
31569
31825
|
client,
|
|
31570
31826
|
(operation) => this.runWithProcessCheck(operation),
|
|
31571
|
-
() => this.
|
|
31827
|
+
() => this.refreshAuthState(null)
|
|
31572
31828
|
);
|
|
31573
31829
|
}
|
|
31574
31830
|
async initialize(_params) {
|
|
@@ -31579,6 +31835,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31579
31835
|
this.terminalOutputMode = resolveTerminalOutputMode(_params.clientCapabilities);
|
|
31580
31836
|
this.booleanConfigOptionsSupported = clientSupportsBooleanConfigOptions(_params.clientCapabilities);
|
|
31581
31837
|
await this.runWithProcessCheck(() => this.codexAcpClient.initialize(_params));
|
|
31838
|
+
this.publishFirstAuthStatusAfterResponse();
|
|
31582
31839
|
const sessionCapabilities = {
|
|
31583
31840
|
resume: {},
|
|
31584
31841
|
list: {},
|
|
@@ -31610,6 +31867,11 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31610
31867
|
acp: false,
|
|
31611
31868
|
http: true,
|
|
31612
31869
|
sse: false
|
|
31870
|
+
},
|
|
31871
|
+
_meta: {
|
|
31872
|
+
// Presence means "this agent pushes `_auth/status_update`". It
|
|
31873
|
+
// never carries a payload, and the client never asks for one.
|
|
31874
|
+
[AUTH_STATUS_META_KEY]: authStatusCapability()
|
|
31613
31875
|
}
|
|
31614
31876
|
},
|
|
31615
31877
|
authMethods: getCodexAuthMethods(_params.clientCapabilities),
|
|
@@ -31741,7 +32003,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31741
32003
|
async handleError(e) {
|
|
31742
32004
|
if (e.message.includes("log out") || e.message.includes("cloud requirements")) {
|
|
31743
32005
|
await this.runWithProcessCheck(() => this.codexAcpClient.logout());
|
|
31744
|
-
await this.
|
|
32006
|
+
await this.refreshAuthState(null);
|
|
31745
32007
|
throw RequestError.internalError(`${e.message}
|
|
31746
32008
|
|
|
31747
32009
|
You have been logged out. Please try again.`);
|
|
@@ -31922,12 +32184,14 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
31922
32184
|
}
|
|
31923
32185
|
async getAuthStateForProvider(authProvider) {
|
|
31924
32186
|
if (!this.authProviderUsesOpenAiAccount(authProvider)) {
|
|
32187
|
+
await this.publishAuthStatus(authProvider, null);
|
|
31925
32188
|
return {
|
|
31926
32189
|
account: null,
|
|
31927
32190
|
authConfigured: true
|
|
31928
32191
|
};
|
|
31929
32192
|
}
|
|
31930
32193
|
const accountResponse = await this.runWithProcessCheck(() => this.codexAcpClient.getAccount());
|
|
32194
|
+
await this.publishAuthStatus(authProvider, accountResponse.account);
|
|
31931
32195
|
return {
|
|
31932
32196
|
account: accountResponse.account,
|
|
31933
32197
|
authConfigured: accountResponse.account !== null || !accountResponse.requiresOpenaiAuth
|
|
@@ -32106,7 +32370,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32106
32370
|
logger.log("Authenticate request failed");
|
|
32107
32371
|
throw RequestError.invalidParams();
|
|
32108
32372
|
}
|
|
32109
|
-
await this.
|
|
32373
|
+
await this.refreshAuthState(this.getAuthProviderForAuthenticateRequest(_params));
|
|
32110
32374
|
logger.log("Authenticate request completed");
|
|
32111
32375
|
return {};
|
|
32112
32376
|
}
|
|
@@ -32137,7 +32401,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32137
32401
|
async logout(_params) {
|
|
32138
32402
|
logger.log("Logout request received");
|
|
32139
32403
|
await this.runWithProcessCheck(() => this.codexAcpClient.logout());
|
|
32140
|
-
await this.
|
|
32404
|
+
await this.refreshAuthState(null);
|
|
32141
32405
|
logger.log("Logout request completed");
|
|
32142
32406
|
}
|
|
32143
32407
|
listProviders(_params) {
|
|
@@ -32240,15 +32504,155 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32240
32504
|
state.modelProvider
|
|
32241
32505
|
);
|
|
32242
32506
|
}
|
|
32507
|
+
/** Returns whether the auth state was read (and thus the auth status pushed). */
|
|
32243
32508
|
async refreshSessionsAuthState(authProvider) {
|
|
32244
|
-
if (this.sessions.size === 0) return;
|
|
32509
|
+
if (this.sessions.size === 0) return false;
|
|
32245
32510
|
const sessionsToRefresh = [...this.sessions.values()].filter((sessionState) => this.authProvidersMatch(sessionState.authProvider, authProvider));
|
|
32246
|
-
if (sessionsToRefresh.length === 0) return;
|
|
32511
|
+
if (sessionsToRefresh.length === 0) return false;
|
|
32247
32512
|
const authState = await this.getAuthStateForProvider(authProvider);
|
|
32248
32513
|
for (const sessionState of sessionsToRefresh) {
|
|
32249
32514
|
sessionState.account = authState.account;
|
|
32250
32515
|
sessionState.authConfigured = authState.authConfigured;
|
|
32251
32516
|
}
|
|
32517
|
+
return true;
|
|
32518
|
+
}
|
|
32519
|
+
/**
|
|
32520
|
+
* Refreshes the sessions of a provider and makes sure the connection-level
|
|
32521
|
+
* `authStatus` is pushed even when no session matched (the empty-screen
|
|
32522
|
+
* login case). Reuses the session refresh read; never adds a second one.
|
|
32523
|
+
*/
|
|
32524
|
+
async refreshAuthState(authProvider) {
|
|
32525
|
+
const refreshed = await this.refreshSessionsAuthState(authProvider);
|
|
32526
|
+
if (refreshed) return;
|
|
32527
|
+
try {
|
|
32528
|
+
await this.getAuthStateForProvider(authProvider ?? this.codexAcpClient.getModelProvider());
|
|
32529
|
+
} catch (error51) {
|
|
32530
|
+
logger.log("Failed to refresh auth status", { error: String(error51) });
|
|
32531
|
+
}
|
|
32532
|
+
}
|
|
32533
|
+
/**
|
|
32534
|
+
* Schedules the connection's first `_auth/status_update`: one account read,
|
|
32535
|
+
* pushed whatever it says, including `none`.
|
|
32536
|
+
*
|
|
32537
|
+
* The push must not overtake the `initialize` response. The JSON-RPC layer
|
|
32538
|
+
* writes that response in the microtask that resolves {@link initialize}, so
|
|
32539
|
+
* the read starts from a check-phase callback, which always runs after it.
|
|
32540
|
+
* `initialize` itself never waits for the read.
|
|
32541
|
+
*
|
|
32542
|
+
* "Unconditional" costs nothing extra here: nothing has been pushed yet on
|
|
32543
|
+
* this connection, so {@link setAuthStatus} cannot suppress this one.
|
|
32544
|
+
*/
|
|
32545
|
+
publishFirstAuthStatusAfterResponse() {
|
|
32546
|
+
setImmediate(() => void this.publishAuthStatusRead());
|
|
32547
|
+
}
|
|
32548
|
+
/**
|
|
32549
|
+
* Reads the agent-owned identity and pushes it.
|
|
32550
|
+
*
|
|
32551
|
+
* Never rejects: an unreadable source means "nothing to report", not an
|
|
32552
|
+
* error. The client then keeps showing the last pushed value, or "not
|
|
32553
|
+
* reported" when there was none.
|
|
32554
|
+
*/
|
|
32555
|
+
async publishAuthStatusRead() {
|
|
32556
|
+
let authStatus;
|
|
32557
|
+
try {
|
|
32558
|
+
authStatus = await this.readAgentAuthIdentity();
|
|
32559
|
+
} catch (error51) {
|
|
32560
|
+
logger.log("Cannot determine auth status", { error: String(error51) });
|
|
32561
|
+
return;
|
|
32562
|
+
}
|
|
32563
|
+
await this.setAuthStatus(authStatus);
|
|
32564
|
+
}
|
|
32565
|
+
/**
|
|
32566
|
+
* Builds the agent-owned auth identity. Routing the client configured
|
|
32567
|
+
* through the ACP `providers/*` API is invisible here: the reported state
|
|
32568
|
+
* is what the agent itself is logged in with. `gateway` stays reserved for
|
|
32569
|
+
* agent-owned gateway state — the `gateway` auth method, or a provider the
|
|
32570
|
+
* user configured in Codex's own config.
|
|
32571
|
+
*/
|
|
32572
|
+
async readAgentAuthIdentity() {
|
|
32573
|
+
const authGatewayName = this.codexAcpClient.getAuthGatewayProviderName();
|
|
32574
|
+
if (authGatewayName !== null) {
|
|
32575
|
+
return gatewayStatus(authGatewayName);
|
|
32576
|
+
}
|
|
32577
|
+
const modelProvider = await this.runWithProcessCheck(() => this.codexAcpClient.getAgentConfiguredModelProvider());
|
|
32578
|
+
if (!this.authProviderUsesOpenAiAccount(modelProvider)) {
|
|
32579
|
+
return gatewayStatus(modelProvider);
|
|
32580
|
+
}
|
|
32581
|
+
const accountResponse = await this.runWithProcessCheck(() => this.codexAcpClient.getAccount());
|
|
32582
|
+
return fromAccount(accountResponse.account);
|
|
32583
|
+
}
|
|
32584
|
+
/**
|
|
32585
|
+
* Pushes `_auth/status_update` for the freshly read account of a provider.
|
|
32586
|
+
* Agent-owned gateway authentication wins; a client-driven provider
|
|
32587
|
+
* override is ignored and the agent-owned login is reported instead.
|
|
32588
|
+
*/
|
|
32589
|
+
async publishAuthStatus(authProvider, account) {
|
|
32590
|
+
const authGatewayName = this.codexAcpClient.getAuthGatewayProviderName();
|
|
32591
|
+
if (authGatewayName !== null) {
|
|
32592
|
+
await this.setAuthStatus(gatewayStatus(authGatewayName));
|
|
32593
|
+
return;
|
|
32594
|
+
}
|
|
32595
|
+
if (this.authProviderUsesOpenAiAccount(authProvider)) {
|
|
32596
|
+
await this.setAuthStatus(fromAccount(account));
|
|
32597
|
+
return;
|
|
32598
|
+
}
|
|
32599
|
+
if (this.codexAcpClient.isClientConfiguredProvider(authProvider)) {
|
|
32600
|
+
await this.publishAuthStatusRead();
|
|
32601
|
+
return;
|
|
32602
|
+
}
|
|
32603
|
+
await this.setAuthStatus(gatewayStatus(authProvider));
|
|
32604
|
+
}
|
|
32605
|
+
/**
|
|
32606
|
+
* Handles the app-server `account/updated` push: the free freshness channel
|
|
32607
|
+
* for logins and logouts happening outside this connection.
|
|
32608
|
+
*/
|
|
32609
|
+
handleAccountUpdated(notification) {
|
|
32610
|
+
void this.applyAccountUpdated(notification);
|
|
32611
|
+
}
|
|
32612
|
+
/**
|
|
32613
|
+
* `account/updated` describes the Codex account only. It must never
|
|
32614
|
+
* overwrite an agent-owned gateway status, which no account event can
|
|
32615
|
+
* invalidate; only a gateway logout or a provider change does.
|
|
32616
|
+
*/
|
|
32617
|
+
async applyAccountUpdated(notification) {
|
|
32618
|
+
try {
|
|
32619
|
+
if (this.codexAcpClient.getAuthGatewayProviderName() !== null) {
|
|
32620
|
+
return;
|
|
32621
|
+
}
|
|
32622
|
+
if (this.currentAuthStatus === null) {
|
|
32623
|
+
await this.publishAuthStatusRead();
|
|
32624
|
+
return;
|
|
32625
|
+
}
|
|
32626
|
+
if (this.currentAuthStatus.kind === "gateway") {
|
|
32627
|
+
return;
|
|
32628
|
+
}
|
|
32629
|
+
await this.setAuthStatus(fromAccountUpdated(notification, this.currentAuthStatus));
|
|
32630
|
+
} catch (error51) {
|
|
32631
|
+
logger.log("Failed to apply account update to auth status", { error: String(error51) });
|
|
32632
|
+
}
|
|
32633
|
+
}
|
|
32634
|
+
/**
|
|
32635
|
+
* Stores `next` and pushes `_auth/status_update`.
|
|
32636
|
+
*
|
|
32637
|
+
* A push goes out only when the payload changed. The identity is read on
|
|
32638
|
+
* many occasions — `initialize`, each session create, each `account/updated`
|
|
32639
|
+
* — and almost all of them see the login already reported.
|
|
32640
|
+
* Clients replace their whole state on each update and tolerate duplicates,
|
|
32641
|
+
* so a repeat is harmless, but it is pure noise all the same.
|
|
32642
|
+
*
|
|
32643
|
+
* The first push of a connection always goes out: nothing was reported yet,
|
|
32644
|
+
* so no payload can equal it.
|
|
32645
|
+
*/
|
|
32646
|
+
async setAuthStatus(next) {
|
|
32647
|
+
if (sameAuthStatus(this.currentAuthStatus, next)) {
|
|
32648
|
+
return;
|
|
32649
|
+
}
|
|
32650
|
+
this.currentAuthStatus = next;
|
|
32651
|
+
try {
|
|
32652
|
+
await this.connection.notify(AUTH_STATUS_UPDATE_METHOD, { authStatus: next });
|
|
32653
|
+
} catch (error51) {
|
|
32654
|
+
logger.log("Failed to send auth status update", { error: String(error51) });
|
|
32655
|
+
}
|
|
32252
32656
|
}
|
|
32253
32657
|
async setSessionMode(_params) {
|
|
32254
32658
|
logger.log("Set session mode requested", {
|
|
@@ -33438,7 +33842,6 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
33438
33842
|
let promptWasCancelled = false;
|
|
33439
33843
|
let recoverableSessionFailure = sessionState.sessionFailure;
|
|
33440
33844
|
sessionState.currentTurnId = null;
|
|
33441
|
-
sessionState.lastTokenUsage = null;
|
|
33442
33845
|
const activePrompt = this.trackActivePrompt(params.sessionId);
|
|
33443
33846
|
let pendingTurnStart = null;
|
|
33444
33847
|
const ensurePendingTurnStart = () => {
|
|
@@ -33471,7 +33874,8 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
33471
33874
|
clientSupportsPlanUpdates(this.clientCapabilities),
|
|
33472
33875
|
clientSupportsTypedSessionFailures(this.clientCapabilities),
|
|
33473
33876
|
this.sessionFailureEpoch,
|
|
33474
|
-
sessionState.subagents
|
|
33877
|
+
sessionState.subagents,
|
|
33878
|
+
(accountUpdated) => this.handleAccountUpdated(accountUpdated)
|
|
33475
33879
|
);
|
|
33476
33880
|
eventHandler = promptEventHandler;
|
|
33477
33881
|
const permissionLifecycle = this.permissionLifecycleContext(sessionState);
|
|
@@ -33516,6 +33920,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
33516
33920
|
}
|
|
33517
33921
|
const commandPromise = this.availableCommands.tryHandleCommand(params.prompt, sessionState, {
|
|
33518
33922
|
onTurnStartPending: () => {
|
|
33923
|
+
sessionState.lastTokenUsage = null;
|
|
33519
33924
|
ensurePendingTurnStart();
|
|
33520
33925
|
},
|
|
33521
33926
|
onTurnStarted: (turnId, threadId) => {
|
|
@@ -33612,6 +34017,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
33612
34017
|
sessionState.fastModeEnabled,
|
|
33613
34018
|
sessionState.currentModelSupportsFast
|
|
33614
34019
|
);
|
|
34020
|
+
sessionState.lastTokenUsage = null;
|
|
33615
34021
|
ensurePendingTurnStart();
|
|
33616
34022
|
const sendPromptPromise = this.runWithProcessCheck(
|
|
33617
34023
|
() => this.codexAcpClient.sendPrompt(
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.9.0",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"bin": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@agentclientprotocol/sdk": "^1.4.0",
|
|
69
|
-
"@openai/codex": "^0.
|
|
69
|
+
"@openai/codex": "^0.153.2",
|
|
70
70
|
"diff": "^9.0.0",
|
|
71
71
|
"open": "^11.0.1",
|
|
72
72
|
"vscode-jsonrpc": "^9.0.1",
|