@nyssance/codex-acp-v2 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +1 -0
- package/dist/index.js +2264 -2214
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -21844,7 +21844,7 @@ var package_default = {
|
|
|
21844
21844
|
publishConfig: {
|
|
21845
21845
|
access: "public"
|
|
21846
21846
|
},
|
|
21847
|
-
version: "0.7.
|
|
21847
|
+
version: "0.7.4",
|
|
21848
21848
|
description: "Native ACP v2 agent for the OpenAI Codex app-server",
|
|
21849
21849
|
packageManager: "bun@1.4.0",
|
|
21850
21850
|
engines: {
|
|
@@ -21916,7 +21916,7 @@ var package_default = {
|
|
|
21916
21916
|
"vscode-jsonrpc": "^9.0.2"
|
|
21917
21917
|
},
|
|
21918
21918
|
optionalDependencies: {
|
|
21919
|
-
"@openai/codex": "^0.
|
|
21919
|
+
"@openai/codex": "^0.159.2"
|
|
21920
21920
|
}
|
|
21921
21921
|
};
|
|
21922
21922
|
|
|
@@ -22076,6 +22076,8 @@ function classifyTurnError(info) {
|
|
|
22076
22076
|
case "cyberPolicy":
|
|
22077
22077
|
case "misalignmentPolicyViolation":
|
|
22078
22078
|
return failure2("policy", false, "refusal");
|
|
22079
|
+
case "tooManyDenials":
|
|
22080
|
+
return failure2("too_many_denials");
|
|
22079
22081
|
case "sessionBudgetExceeded":
|
|
22080
22082
|
return failure2("budget");
|
|
22081
22083
|
case "usageLimitExceeded":
|
|
@@ -24204,138 +24206,10 @@ function withWritableRoots(policy, roots) {
|
|
|
24204
24206
|
return { ...policy, writableRoots: [...new Set([...policy.writableRoots, ...roots])] };
|
|
24205
24207
|
}
|
|
24206
24208
|
|
|
24207
|
-
// src/
|
|
24208
|
-
|
|
24209
|
-
|
|
24210
|
-
|
|
24211
|
-
var COLLABORATION_MODE_CONFIG_ID = "collaboration_mode";
|
|
24212
|
-
var DEFAULT_EFFORT = "medium";
|
|
24213
|
-
var FAST_SERVICE_TIER = "fast";
|
|
24214
|
-
function resolveModelSelection(catalog, modelId, effort) {
|
|
24215
|
-
const listed = modelId === null ? undefined : catalog.find((model) => model.id === modelId);
|
|
24216
|
-
if (listed)
|
|
24217
|
-
return { model: listed.id, effort: effort ?? listed.defaultReasoningEffort };
|
|
24218
|
-
if (modelId)
|
|
24219
|
-
return { model: modelId, effort: effort ?? DEFAULT_EFFORT };
|
|
24220
|
-
const fallback = catalog.find((model) => model.isDefault) ?? catalog[0];
|
|
24221
|
-
if (!fallback)
|
|
24222
|
-
throw new Error("Codex did not return any models");
|
|
24223
|
-
return { model: fallback.id, effort: effort ?? fallback.defaultReasoningEffort };
|
|
24224
|
-
}
|
|
24225
|
-
function findModel(catalog, modelId) {
|
|
24226
|
-
return catalog.find((model) => model.id === modelId);
|
|
24227
|
-
}
|
|
24228
|
-
function supportedEfforts(model) {
|
|
24229
|
-
return model?.supportedReasoningEfforts ?? [];
|
|
24230
|
-
}
|
|
24231
|
-
function coerceEffort(model, effort) {
|
|
24232
|
-
const options = supportedEfforts(model);
|
|
24233
|
-
if (options.length === 0)
|
|
24234
|
-
return effort;
|
|
24235
|
-
if (options.some((option) => option.reasoningEffort === effort))
|
|
24236
|
-
return effort;
|
|
24237
|
-
return model?.defaultReasoningEffort ?? options[0].reasoningEffort;
|
|
24238
|
-
}
|
|
24239
|
-
function modelSupportsFast(model) {
|
|
24240
|
-
if (!model)
|
|
24241
|
-
return false;
|
|
24242
|
-
return model.serviceTiers.some((tier) => tier.id === FAST_SERVICE_TIER) || model.additionalSpeedTiers.includes(FAST_SERVICE_TIER);
|
|
24243
|
-
}
|
|
24244
|
-
function modelSupportsImages(model) {
|
|
24245
|
-
return model?.inputModalities.includes("image") ?? true;
|
|
24246
|
-
}
|
|
24247
|
-
function modelLacksReasoning(model) {
|
|
24248
|
-
const options = supportedEfforts(model);
|
|
24249
|
-
return options.length > 0 && options.every((option) => option.reasoningEffort === "none");
|
|
24250
|
-
}
|
|
24251
|
-
function modelConfigOption(catalog, current, gatewayGroups = []) {
|
|
24252
|
-
const visible = catalog.filter((model) => !model.hidden || model.id === current);
|
|
24253
|
-
const options = visible.map((model) => ({
|
|
24254
|
-
value: model.id,
|
|
24255
|
-
name: model.displayName,
|
|
24256
|
-
description: model.description || null
|
|
24257
|
-
}));
|
|
24258
|
-
if (!options.some((option) => option.value === current)) {
|
|
24259
|
-
options.unshift({ value: current, name: current, description: null });
|
|
24260
|
-
}
|
|
24261
|
-
const base = {
|
|
24262
|
-
configId: MODEL_CONFIG_ID,
|
|
24263
|
-
name: "Model",
|
|
24264
|
-
description: "Model Codex uses for this session",
|
|
24265
|
-
category: "model",
|
|
24266
|
-
type: "select",
|
|
24267
|
-
currentValue: current
|
|
24268
|
-
};
|
|
24269
|
-
if (gatewayGroups.length === 0)
|
|
24270
|
-
return { ...base, options };
|
|
24271
|
-
const owner = new Map;
|
|
24272
|
-
for (const group of gatewayGroups)
|
|
24273
|
-
for (const id of group.modelIds)
|
|
24274
|
-
owner.set(id, group.id);
|
|
24275
|
-
const native = options.filter((option) => !owner.has(option.value));
|
|
24276
|
-
return {
|
|
24277
|
-
...base,
|
|
24278
|
-
options: [
|
|
24279
|
-
{ groupId: "codex", name: "Codex", options: native },
|
|
24280
|
-
...gatewayGroups.map((group) => ({ groupId: group.id, name: group.name, options: options.filter((option) => owner.get(option.value) === group.id) }))
|
|
24281
|
-
]
|
|
24282
|
-
};
|
|
24283
|
-
}
|
|
24284
|
-
function effortConfigOption(efforts, current) {
|
|
24285
|
-
const options = efforts.map((option) => ({
|
|
24286
|
-
value: option.reasoningEffort,
|
|
24287
|
-
name: capitalize(option.reasoningEffort),
|
|
24288
|
-
description: option.description || null
|
|
24289
|
-
}));
|
|
24290
|
-
if (!options.some((option) => option.value === current)) {
|
|
24291
|
-
options.unshift({ value: current, name: capitalize(current), description: null });
|
|
24292
|
-
}
|
|
24293
|
-
return {
|
|
24294
|
-
configId: EFFORT_CONFIG_ID,
|
|
24295
|
-
name: "Reasoning effort",
|
|
24296
|
-
description: "How much reasoning effort the model should use",
|
|
24297
|
-
category: "thought_level",
|
|
24298
|
-
type: "select",
|
|
24299
|
-
currentValue: current,
|
|
24300
|
-
options
|
|
24301
|
-
};
|
|
24302
|
-
}
|
|
24303
|
-
function fastModeConfigOption(enabled) {
|
|
24304
|
-
return {
|
|
24305
|
-
configId: FAST_MODE_CONFIG_ID,
|
|
24306
|
-
name: "Fast mode",
|
|
24307
|
-
description: "1.5x speed, increased usage",
|
|
24308
|
-
category: "model_config",
|
|
24309
|
-
type: "boolean",
|
|
24310
|
-
currentValue: enabled
|
|
24311
|
-
};
|
|
24312
|
-
}
|
|
24313
|
-
var DEFAULT_COLLABORATION_MODE = "default";
|
|
24314
|
-
var PLAN_COLLABORATION_MODE = "plan";
|
|
24315
|
-
function collaborationModeConfigOption(current) {
|
|
24316
|
-
return {
|
|
24317
|
-
configId: COLLABORATION_MODE_CONFIG_ID,
|
|
24318
|
-
name: "Collaboration mode",
|
|
24319
|
-
description: "How Codex collaborates on subsequent turns",
|
|
24320
|
-
category: "model_config",
|
|
24321
|
-
type: "select",
|
|
24322
|
-
currentValue: current,
|
|
24323
|
-
options: [
|
|
24324
|
-
{ value: DEFAULT_COLLABORATION_MODE, name: "Default", description: "Make changes directly" },
|
|
24325
|
-
{ value: PLAN_COLLABORATION_MODE, name: "Plan", description: "Plan before making changes" }
|
|
24326
|
-
]
|
|
24327
|
-
};
|
|
24328
|
-
}
|
|
24329
|
-
function parseCollaborationMode(value) {
|
|
24330
|
-
if (value === DEFAULT_COLLABORATION_MODE)
|
|
24331
|
-
return DEFAULT_COLLABORATION_MODE;
|
|
24332
|
-
if (value === PLAN_COLLABORATION_MODE)
|
|
24333
|
-
return PLAN_COLLABORATION_MODE;
|
|
24334
|
-
return;
|
|
24335
|
-
}
|
|
24336
|
-
function capitalize(value) {
|
|
24337
|
-
return value.length === 0 ? value : value[0].toUpperCase() + value.slice(1);
|
|
24338
|
-
}
|
|
24209
|
+
// src/agent/providers.ts
|
|
24210
|
+
import { readFileSync } from "fs";
|
|
24211
|
+
import os from "os";
|
|
24212
|
+
import path4 from "path";
|
|
24339
24213
|
|
|
24340
24214
|
// src/permissions/json.ts
|
|
24341
24215
|
function isRecord2(value) {
|
|
@@ -24362,2289 +24236,2424 @@ function normalizeJsonObject(value) {
|
|
|
24362
24236
|
return Object.fromEntries(Object.entries(value).filter(([, nested]) => nested !== undefined).map(([key, nested]) => [key, normalizeJsonValue(nested)]));
|
|
24363
24237
|
}
|
|
24364
24238
|
|
|
24365
|
-
// src/
|
|
24366
|
-
|
|
24367
|
-
|
|
24368
|
-
|
|
24369
|
-
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
const decision = parseDecision(candidate, params);
|
|
24375
|
-
if (!decision)
|
|
24376
|
-
return;
|
|
24377
|
-
decisions.push(decision);
|
|
24378
|
-
}
|
|
24379
|
-
return decisions;
|
|
24239
|
+
// src/agent/providers.ts
|
|
24240
|
+
var OPENAI_PROVIDER_ID = "openai";
|
|
24241
|
+
var GATEWAY_MODEL_PROVIDER = "custom-gateway";
|
|
24242
|
+
var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
24243
|
+
var NATIVE_GROUP_ID = "chatgpt";
|
|
24244
|
+
var NATIVE_GROUP_NAME = "ChatGPT";
|
|
24245
|
+
var GATEWAY_ID_PATTERN = /^[a-z0-9-]+$/;
|
|
24246
|
+
function gatewayModelId(connectionId, modelId) {
|
|
24247
|
+
return `gateway:${encodeURIComponent(connectionId)}:${encodeURIComponent(modelId)}`;
|
|
24380
24248
|
}
|
|
24381
|
-
|
|
24382
|
-
|
|
24383
|
-
|
|
24384
|
-
|
|
24385
|
-
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
}
|
|
24390
|
-
if (params.additionalPermissions)
|
|
24391
|
-
return ["accept", "cancel"];
|
|
24392
|
-
const decisions = ["accept", "acceptForSession"];
|
|
24393
|
-
if (params.proposedExecpolicyAmendment && params.proposedExecpolicyAmendment.length > 0) {
|
|
24394
|
-
decisions.push({ acceptWithExecpolicyAmendment: { execpolicy_amendment: params.proposedExecpolicyAmendment } });
|
|
24249
|
+
|
|
24250
|
+
class ProviderRouting {
|
|
24251
|
+
baseConfig;
|
|
24252
|
+
configuredProvider;
|
|
24253
|
+
gateways = new Map;
|
|
24254
|
+
constructor(baseConfig, configuredProvider) {
|
|
24255
|
+
this.baseConfig = baseConfig;
|
|
24256
|
+
this.configuredProvider = configuredProvider;
|
|
24395
24257
|
}
|
|
24396
|
-
|
|
24397
|
-
|
|
24398
|
-
|
|
24399
|
-
|
|
24400
|
-
if (candidate === "accept" || candidate === "acceptForSession" || candidate === "decline" || candidate === "cancel") {
|
|
24401
|
-
return candidate;
|
|
24258
|
+
copy() {
|
|
24259
|
+
const copy = new ProviderRouting(this.baseConfig, this.configuredProvider);
|
|
24260
|
+
copy.gateways = new Map(this.gateways);
|
|
24261
|
+
return copy;
|
|
24402
24262
|
}
|
|
24403
|
-
|
|
24404
|
-
return;
|
|
24405
|
-
if ("acceptWithExecpolicyAmendment" in candidate) {
|
|
24406
|
-
const value = candidate["acceptWithExecpolicyAmendment"];
|
|
24407
|
-
if (!isRecord2(value))
|
|
24408
|
-
return;
|
|
24409
|
-
const amendment = value["execpolicy_amendment"];
|
|
24410
|
-
if (!isStringArray(amendment) || amendment.length === 0)
|
|
24411
|
-
return;
|
|
24412
|
-
if (!sameStrings(amendment, params.proposedExecpolicyAmendment))
|
|
24413
|
-
return;
|
|
24414
|
-
return { acceptWithExecpolicyAmendment: { execpolicy_amendment: [...amendment] } };
|
|
24263
|
+
get active() {
|
|
24264
|
+
return this.gateways.values().next().value ?? null;
|
|
24415
24265
|
}
|
|
24416
|
-
|
|
24417
|
-
|
|
24418
|
-
if (!isRecord2(value))
|
|
24419
|
-
return;
|
|
24420
|
-
const amendment = parseNetworkAmendment(value["network_policy_amendment"]);
|
|
24421
|
-
if (!amendment || !params.networkApprovalContext)
|
|
24422
|
-
return;
|
|
24423
|
-
if (amendment.host !== params.networkApprovalContext.host)
|
|
24424
|
-
return;
|
|
24425
|
-
const proposed = params.proposedNetworkPolicyAmendments ?? [];
|
|
24426
|
-
if (!proposed.some((entry) => entry.host === amendment.host && entry.action === amendment.action))
|
|
24427
|
-
return;
|
|
24428
|
-
return { applyNetworkPolicyAmendment: { network_policy_amendment: amendment } };
|
|
24266
|
+
gateway(id) {
|
|
24267
|
+
return id === null ? null : this.gateways.get(id) ?? null;
|
|
24429
24268
|
}
|
|
24430
|
-
|
|
24431
|
-
|
|
24432
|
-
function parseNetworkAmendment(value) {
|
|
24433
|
-
if (!isRecord2(value) || typeof value["host"] !== "string")
|
|
24434
|
-
return;
|
|
24435
|
-
const action = value["action"];
|
|
24436
|
-
if (action !== "allow" && action !== "deny")
|
|
24437
|
-
return;
|
|
24438
|
-
return { host: value["host"], action };
|
|
24439
|
-
}
|
|
24440
|
-
function isStringArray(value) {
|
|
24441
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
24442
|
-
}
|
|
24443
|
-
function sameStrings(left, right) {
|
|
24444
|
-
return !!right && left.length === right.length && left.every((value, index) => value === right[index]);
|
|
24445
|
-
}
|
|
24446
|
-
function renderExecPolicyPrefix(command) {
|
|
24447
|
-
const script = extractWrappedScript(command);
|
|
24448
|
-
if (script !== undefined)
|
|
24449
|
-
return script;
|
|
24450
|
-
if (command.some((value) => value.includes("\x00")))
|
|
24451
|
-
return command.join(" ");
|
|
24452
|
-
return command.map(shlexQuote).join(" ");
|
|
24453
|
-
}
|
|
24454
|
-
function extractWrappedScript(command) {
|
|
24455
|
-
const executable = executableName(command[0]);
|
|
24456
|
-
if ((executable === "bash" || executable === "zsh" || executable === "sh") && command.length === 3 && (command[1] === "-lc" || command[1] === "-c")) {
|
|
24457
|
-
return command[2];
|
|
24269
|
+
hasGateway(id) {
|
|
24270
|
+
return id !== null && this.gateways.has(id);
|
|
24458
24271
|
}
|
|
24459
|
-
|
|
24460
|
-
|
|
24461
|
-
|
|
24462
|
-
|
|
24463
|
-
|
|
24464
|
-
|
|
24465
|
-
|
|
24466
|
-
|
|
24467
|
-
|
|
24272
|
+
list() {
|
|
24273
|
+
const first = this.active;
|
|
24274
|
+
const routed = first !== null && first.mode === "route";
|
|
24275
|
+
const summary = (gateway) => ({ id: gateway.id, name: gateway.name, baseUrl: gateway.baseUrl, models: gateway.models.map((model) => model.id) });
|
|
24276
|
+
return {
|
|
24277
|
+
providers: [{
|
|
24278
|
+
providerId: OPENAI_PROVIDER_ID,
|
|
24279
|
+
supported: ["openai"],
|
|
24280
|
+
required: false,
|
|
24281
|
+
current: routed && first ? { apiType: "openai", baseUrl: first.baseUrl } : { apiType: "openai", baseUrl: this.nativeBaseUrl() },
|
|
24282
|
+
...first && !routed ? { _meta: { codex: { mode: "catalog", gateway: summary(first), gateways: [...this.gateways.values()].map(summary) } } } : {}
|
|
24283
|
+
}]
|
|
24284
|
+
};
|
|
24468
24285
|
}
|
|
24469
|
-
|
|
24470
|
-
|
|
24471
|
-
|
|
24472
|
-
|
|
24473
|
-
|
|
24474
|
-
|
|
24475
|
-
|
|
24476
|
-
const
|
|
24477
|
-
if (
|
|
24478
|
-
|
|
24479
|
-
|
|
24286
|
+
set(request) {
|
|
24287
|
+
if (request.providerId !== OPENAI_PROVIDER_ID) {
|
|
24288
|
+
throw RequestError.invalidParams({ providerId: request.providerId }, `Unknown providerId "${request.providerId}"; only "${OPENAI_PROVIDER_ID}" is configurable`);
|
|
24289
|
+
}
|
|
24290
|
+
if (request.apiType !== "openai") {
|
|
24291
|
+
throw RequestError.invalidParams({ apiType: request.apiType }, `Codex only speaks the OpenAI protocol; got apiType "${request.apiType}"`);
|
|
24292
|
+
}
|
|
24293
|
+
const baseUrl = typeof request.baseUrl === "string" ? request.baseUrl.trim() : "";
|
|
24294
|
+
if (!/^https?:\/\//.test(baseUrl)) {
|
|
24295
|
+
throw RequestError.invalidParams({ baseUrl: request.baseUrl }, "baseUrl must be an http(s) URL");
|
|
24296
|
+
}
|
|
24297
|
+
const hints = readHints(request._meta);
|
|
24298
|
+
if (hints.id === OPENAI_PROVIDER_ID || hints.id === this.configuredProvider) {
|
|
24299
|
+
throw RequestError.invalidParams({ id: hints.id }, `_meta.codex.id "${hints.id}" is Codex's own provider; pick another gateway id`);
|
|
24300
|
+
}
|
|
24301
|
+
if (hints.mode === "route" || this.mode !== hints.mode)
|
|
24302
|
+
this.gateways.clear();
|
|
24303
|
+
this.gateways.set(hints.id, {
|
|
24304
|
+
id: hints.id,
|
|
24305
|
+
baseUrl,
|
|
24306
|
+
headers: { ...request.headers ?? {} },
|
|
24307
|
+
name: hints.name ?? "Client-configured gateway",
|
|
24308
|
+
models: hints.models,
|
|
24309
|
+
model: hints.model,
|
|
24310
|
+
bearerToken: hints.bearerToken,
|
|
24311
|
+
config: hints.config,
|
|
24312
|
+
mode: hints.mode,
|
|
24313
|
+
catalogEntries: hints.catalogEntries
|
|
24314
|
+
});
|
|
24480
24315
|
}
|
|
24481
|
-
|
|
24482
|
-
|
|
24483
|
-
|
|
24484
|
-
|
|
24485
|
-
|
|
24486
|
-
|
|
24487
|
-
|
|
24488
|
-
if (!value.includes("'"))
|
|
24489
|
-
return `'${value}'`;
|
|
24490
|
-
return `"${value.replace(/["\\$`!]/g, "\\$&")}"`;
|
|
24491
|
-
}
|
|
24492
|
-
|
|
24493
|
-
// src/permissions/options.ts
|
|
24494
|
-
var OptionId = {
|
|
24495
|
-
AllowOnce: "allow_once",
|
|
24496
|
-
AllowForSession: "allow_for_session",
|
|
24497
|
-
Decline: "decline",
|
|
24498
|
-
Cancel: "cancel",
|
|
24499
|
-
AcceptWithExecpolicyAmendment: "accept_execpolicy_amendment",
|
|
24500
|
-
ApplyNetworkPolicyAmendment: "apply_network_policy_amendment",
|
|
24501
|
-
AllowPermissionsForTurn: "allow_permissions_turn",
|
|
24502
|
-
AllowPermissionsForTurnStrict: "allow_permissions_turn_strict_auto_review",
|
|
24503
|
-
AllowPermissionsForSession: "allow_permissions_session",
|
|
24504
|
-
RejectPermissions: "reject_permissions"
|
|
24505
|
-
};
|
|
24506
|
-
function commandDecisionOptions(params) {
|
|
24507
|
-
const decisions = availableCommandDecisions(params);
|
|
24508
|
-
if (!decisions)
|
|
24509
|
-
return;
|
|
24510
|
-
const options = [];
|
|
24511
|
-
let networkIndex = 0;
|
|
24512
|
-
for (const decision of decisions) {
|
|
24513
|
-
if (decision === "accept") {
|
|
24514
|
-
options.push(decisionOption(OptionId.AllowOnce, params.networkApprovalContext ? "Yes, just this once" : "Yes, proceed", "allow_once", decision));
|
|
24515
|
-
} else if (decision === "acceptForSession") {
|
|
24516
|
-
const name = params.networkApprovalContext ? "Yes, and allow this host for this conversation" : params.additionalPermissions ? "Yes, and allow these permissions for this session" : "Yes, and don't ask again for this command in this session";
|
|
24517
|
-
options.push(decisionOption(OptionId.AllowForSession, name, "allow_always", decision));
|
|
24518
|
-
} else if (decision === "decline") {
|
|
24519
|
-
options.push(decisionOption(OptionId.Decline, "No, continue without running it", "reject_once", decision));
|
|
24520
|
-
} else if (decision === "cancel") {
|
|
24521
|
-
options.push(decisionOption(OptionId.Cancel, "No, and tell Codex what to do differently", "reject_once", decision));
|
|
24522
|
-
} else if ("acceptWithExecpolicyAmendment" in decision) {
|
|
24523
|
-
const prefix = renderExecPolicyPrefix(decision.acceptWithExecpolicyAmendment.execpolicy_amendment);
|
|
24524
|
-
if (prefix.includes(`
|
|
24525
|
-
`) || prefix.includes("\r"))
|
|
24526
|
-
continue;
|
|
24527
|
-
options.push(decisionOption(OptionId.AcceptWithExecpolicyAmendment, `Yes, and don't ask again for commands that start with \`${prefix}\``, "allow_always", decision));
|
|
24528
|
-
} else {
|
|
24529
|
-
const amendment = decision.applyNetworkPolicyAmendment.network_policy_amendment;
|
|
24530
|
-
options.push(decisionOption(`${OptionId.ApplyNetworkPolicyAmendment}:${networkIndex++}`, amendment.action === "allow" ? "Yes, and allow this host in the future" : "No, and block this host in the future", amendment.action === "allow" ? "allow_always" : "reject_always", decision));
|
|
24316
|
+
get mode() {
|
|
24317
|
+
return this.active?.mode ?? "route";
|
|
24318
|
+
}
|
|
24319
|
+
gatewayFor(modelId) {
|
|
24320
|
+
for (const gateway of this.gateways.values()) {
|
|
24321
|
+
if (gateway.models.some((model) => (this.mode === "catalog" ? gatewayModelId(gateway.id, model.id) : model.id) === modelId))
|
|
24322
|
+
return gateway;
|
|
24531
24323
|
}
|
|
24324
|
+
return null;
|
|
24532
24325
|
}
|
|
24533
|
-
|
|
24534
|
-
|
|
24535
|
-
const hasReject = ordered.some(({ option }) => option.kind === "reject_once" || option.kind === "reject_always");
|
|
24536
|
-
const ids = ordered.map(({ option }) => option.optionId);
|
|
24537
|
-
return hasAllow && hasReject && new Set(ids).size === ids.length ? ordered : undefined;
|
|
24538
|
-
}
|
|
24539
|
-
function order(option) {
|
|
24540
|
-
if (option.kind === "allow_once")
|
|
24541
|
-
return 0;
|
|
24542
|
-
if (option.kind === "allow_always")
|
|
24543
|
-
return 1;
|
|
24544
|
-
return 2;
|
|
24545
|
-
}
|
|
24546
|
-
function fileChangeDecisionOptions() {
|
|
24547
|
-
return [
|
|
24548
|
-
decisionOption(OptionId.AllowOnce, "Yes, proceed", "allow_once", "accept"),
|
|
24549
|
-
decisionOption(OptionId.AllowForSession, "Yes, and don't ask again for these files", "allow_always", "acceptForSession"),
|
|
24550
|
-
decisionOption(OptionId.Cancel, "No, and tell Codex what to do differently", "reject_once", "cancel")
|
|
24551
|
-
];
|
|
24552
|
-
}
|
|
24553
|
-
function permissionProfileOptions() {
|
|
24554
|
-
return [
|
|
24555
|
-
permissionOption(OptionId.AllowPermissionsForTurn, "Yes, grant these permissions for this turn", "allow_once"),
|
|
24556
|
-
permissionOption(OptionId.AllowPermissionsForTurnStrict, "Yes, grant for this turn with strict auto review", "allow_once"),
|
|
24557
|
-
permissionOption(OptionId.AllowPermissionsForSession, "Yes, grant these permissions for this session", "allow_always"),
|
|
24558
|
-
permissionOption(OptionId.RejectPermissions, "No, continue without permissions", "reject_once")
|
|
24559
|
-
];
|
|
24560
|
-
}
|
|
24561
|
-
function decisionOption(optionId, name, kind, decision) {
|
|
24562
|
-
return { option: permissionOption(optionId, name, kind), decision };
|
|
24563
|
-
}
|
|
24564
|
-
function permissionOption(optionId, name, kind, description) {
|
|
24565
|
-
return { optionId, name, kind, ...description ? { _meta: { codex: { description } } } : {} };
|
|
24566
|
-
}
|
|
24567
|
-
function selectedDecision(response, decisions) {
|
|
24568
|
-
if (response.outcome.outcome !== "selected")
|
|
24569
|
-
return;
|
|
24570
|
-
const optionId = response.outcome.optionId;
|
|
24571
|
-
return decisions.find(({ option }) => option.optionId === optionId)?.decision;
|
|
24572
|
-
}
|
|
24573
|
-
|
|
24574
|
-
// src/permissions/presentation.ts
|
|
24575
|
-
var COMMAND_TITLE = "Run command?";
|
|
24576
|
-
var NETWORK_TITLE = "Allow network access?";
|
|
24577
|
-
var FILE_CHANGE_TITLE = "Make edits?";
|
|
24578
|
-
var PERMISSIONS_TITLE = "Grant permissions?";
|
|
24579
|
-
function commandSubject(params) {
|
|
24580
|
-
const network = params.networkApprovalContext;
|
|
24581
|
-
const rawInput = {
|
|
24582
|
-
...params.command ? { command: stripShellPrefix(params.command) } : {},
|
|
24583
|
-
...params.cwd ? { cwd: params.cwd } : {},
|
|
24584
|
-
...network?.protocol === "http" || network?.protocol === "https" ? { url: `${network.protocol}://${network.host}` } : {},
|
|
24585
|
-
...params.additionalPermissions ? { additionalPermissions: params.additionalPermissions } : {}
|
|
24586
|
-
};
|
|
24587
|
-
const content = [];
|
|
24588
|
-
if (network)
|
|
24589
|
-
content.push(text(`${network.protocol} access to ${network.host}`));
|
|
24590
|
-
if (params.additionalPermissions)
|
|
24591
|
-
content.push(...profileContent(params.additionalPermissions));
|
|
24592
|
-
const locations = unique([...commandActionPaths(params.commandActions), ...profilePaths(params.additionalPermissions)]);
|
|
24593
|
-
return {
|
|
24594
|
-
toolCallId: params.itemId,
|
|
24595
|
-
name: ToolName.Shell,
|
|
24596
|
-
kind: "execute",
|
|
24597
|
-
status: "pending",
|
|
24598
|
-
title: network ? `${network.protocol} network access to ${network.host}` : commandTitle(params.command, params.commandActions),
|
|
24599
|
-
...Object.keys(rawInput).length > 0 ? { rawInput } : {},
|
|
24600
|
-
...locations.length > 0 ? { locations: locations.map((path4) => ({ path: path4 })) } : {},
|
|
24601
|
-
...content.length > 0 ? { content } : {}
|
|
24602
|
-
};
|
|
24603
|
-
}
|
|
24604
|
-
function fileChangeSubject(params, item) {
|
|
24605
|
-
const paths = item ? changePaths(item.changes) : [];
|
|
24606
|
-
return {
|
|
24607
|
-
toolCallId: params.itemId,
|
|
24608
|
-
name: ToolName.ApplyPatch,
|
|
24609
|
-
kind: "edit",
|
|
24610
|
-
status: "pending",
|
|
24611
|
-
title: paths.length === 1 ? `Edit ${paths[0]}` : paths.length > 1 ? `Edit ${paths.length} files` : "Edit files",
|
|
24612
|
-
...paths.length > 0 ? { locations: paths.map((path4) => ({ path: path4 })) } : {},
|
|
24613
|
-
...item ? { content: item.changes.map(diffContent) } : {},
|
|
24614
|
-
...params.grantRoot ? { rawInput: { grantRoot: params.grantRoot } } : {}
|
|
24615
|
-
};
|
|
24616
|
-
}
|
|
24617
|
-
function permissionsSubject(itemId, cwd, environmentId, permissions) {
|
|
24618
|
-
const content = profileContent(permissions);
|
|
24619
|
-
const paths = profilePaths(permissions);
|
|
24620
|
-
return {
|
|
24621
|
-
toolCallId: itemId,
|
|
24622
|
-
name: ToolName.Shell,
|
|
24623
|
-
kind: "other",
|
|
24624
|
-
status: "pending",
|
|
24625
|
-
title: "Additional sandbox permissions",
|
|
24626
|
-
rawInput: { permissions, cwd, environmentId },
|
|
24627
|
-
...paths.length > 0 ? { locations: paths.map((path4) => ({ path: path4 })) } : {},
|
|
24628
|
-
...content.length > 0 ? { content } : {}
|
|
24629
|
-
};
|
|
24630
|
-
}
|
|
24631
|
-
function commandTitle(command, actions) {
|
|
24632
|
-
const first = actions?.[0];
|
|
24633
|
-
if (first?.type === "read")
|
|
24634
|
-
return actions?.length === 1 ? `Read ${first.path}` : "Run command with file reads";
|
|
24635
|
-
if (first?.type === "listFiles")
|
|
24636
|
-
return "List files";
|
|
24637
|
-
if (first?.type === "search")
|
|
24638
|
-
return "Search files";
|
|
24639
|
-
return command ? stripShellPrefix(command) : "Run command";
|
|
24640
|
-
}
|
|
24641
|
-
function profilePaths(permissions) {
|
|
24642
|
-
const fileSystem = permissions?.fileSystem;
|
|
24643
|
-
return unique([
|
|
24644
|
-
...fileSystem?.read ?? [],
|
|
24645
|
-
...fileSystem?.write ?? [],
|
|
24646
|
-
...(fileSystem?.entries ?? []).flatMap((entry) => entry.path.type === "path" ? [entry.path.path] : [])
|
|
24647
|
-
]);
|
|
24648
|
-
}
|
|
24649
|
-
function profileContent(permissions) {
|
|
24650
|
-
const lines = [];
|
|
24651
|
-
const networkEnabled = permissions.network?.enabled;
|
|
24652
|
-
if (networkEnabled !== null && networkEnabled !== undefined) {
|
|
24653
|
-
lines.push(networkEnabled ? "Enable network access" : "Disable network access");
|
|
24326
|
+
gatewayIdFor(modelId) {
|
|
24327
|
+
return this.gatewayFor(modelId)?.id ?? null;
|
|
24654
24328
|
}
|
|
24655
|
-
|
|
24656
|
-
|
|
24657
|
-
lines.push(`${entry.access} filesystem pattern ${entry.path.pattern}`);
|
|
24658
|
-
else if (entry.path.type === "special")
|
|
24659
|
-
lines.push(`${entry.access} Codex filesystem scope ${JSON.stringify(entry.path.value)}`);
|
|
24329
|
+
isGatewayModel(modelId) {
|
|
24330
|
+
return this.gatewayFor(modelId) !== null;
|
|
24660
24331
|
}
|
|
24661
|
-
|
|
24662
|
-
|
|
24663
|
-
|
|
24664
|
-
|
|
24665
|
-
return { type: "content", content: { type: "text", text: value } };
|
|
24666
|
-
}
|
|
24667
|
-
function unique(values) {
|
|
24668
|
-
return [...new Set(values)];
|
|
24669
|
-
}
|
|
24670
|
-
|
|
24671
|
-
// src/permissions/ApprovalHandler.ts
|
|
24672
|
-
class CodexApprovalHandler {
|
|
24673
|
-
client;
|
|
24674
|
-
turn;
|
|
24675
|
-
signal;
|
|
24676
|
-
constructor(client, turn, signal = () => {
|
|
24677
|
-
return;
|
|
24678
|
-
}) {
|
|
24679
|
-
this.client = client;
|
|
24680
|
-
this.turn = turn;
|
|
24681
|
-
this.signal = signal;
|
|
24332
|
+
gatewayGroups() {
|
|
24333
|
+
if (this.mode !== "catalog")
|
|
24334
|
+
return [];
|
|
24335
|
+
return [...this.gateways.values()].filter((gateway) => gateway.models.length > 0).map((gateway) => ({ id: gateway.id, name: gateway.name, modelIds: gateway.models.map((model) => gatewayModelId(gateway.id, model.id)) }));
|
|
24682
24336
|
}
|
|
24683
|
-
|
|
24684
|
-
|
|
24685
|
-
|
|
24686
|
-
|
|
24687
|
-
|
|
24688
|
-
return
|
|
24689
|
-
|
|
24690
|
-
|
|
24691
|
-
|
|
24692
|
-
|
|
24693
|
-
|
|
24694
|
-
|
|
24695
|
-
|
|
24696
|
-
|
|
24697
|
-
return
|
|
24698
|
-
|
|
24699
|
-
|
|
24700
|
-
|
|
24701
|
-
|
|
24337
|
+
selectionId(modelId, provider) {
|
|
24338
|
+
return modelId !== null && this.mode === "catalog" && this.hasGateway(provider) ? gatewayModelId(provider, modelId) : modelId;
|
|
24339
|
+
}
|
|
24340
|
+
upstreamModel(selectionId) {
|
|
24341
|
+
if (selectionId === null || !selectionId.startsWith("gateway:"))
|
|
24342
|
+
return selectionId;
|
|
24343
|
+
const gateway = this.gatewayFor(selectionId);
|
|
24344
|
+
const model = gateway?.models.find((model2) => gatewayModelId(gateway.id, model2.id) === selectionId);
|
|
24345
|
+
if (!model)
|
|
24346
|
+
throw RequestError.invalidParams({ model: selectionId }, "The selected provider connection or model is unavailable");
|
|
24347
|
+
return model.id;
|
|
24348
|
+
}
|
|
24349
|
+
disable(request) {
|
|
24350
|
+
if (request.providerId !== OPENAI_PROVIDER_ID)
|
|
24351
|
+
return;
|
|
24352
|
+
const id = readGatewayId(request._meta);
|
|
24353
|
+
if (id === null)
|
|
24354
|
+
this.gateways.clear();
|
|
24355
|
+
else
|
|
24356
|
+
this.gateways.delete(id);
|
|
24357
|
+
}
|
|
24358
|
+
modelProvider() {
|
|
24359
|
+
return this.defaultGatewayId() ?? this.configuredProvider;
|
|
24702
24360
|
}
|
|
24703
|
-
|
|
24704
|
-
|
|
24705
|
-
try {
|
|
24706
|
-
const response = await this.client.requestPermission({
|
|
24707
|
-
title: FILE_CHANGE_TITLE,
|
|
24708
|
-
description: nonBlank(params.reason),
|
|
24709
|
-
subject: { type: "tool_call", toolCall: fileChangeSubject(params, this.turn.fileChange(params.itemId)) },
|
|
24710
|
-
options: decisions.map(({ option }) => option)
|
|
24711
|
-
}, this.signal());
|
|
24712
|
-
return { decision: selectedDecision(response, decisions) ?? "cancel" };
|
|
24713
|
-
} catch (error40) {
|
|
24714
|
-
logger.error("file change approval failed", error40, { itemId: params.itemId });
|
|
24715
|
-
return { decision: "cancel" };
|
|
24716
|
-
}
|
|
24361
|
+
routesByDefault() {
|
|
24362
|
+
return this.defaultGatewayId() !== null;
|
|
24717
24363
|
}
|
|
24718
|
-
|
|
24719
|
-
|
|
24720
|
-
|
|
24721
|
-
title: PERMISSIONS_TITLE,
|
|
24722
|
-
description: nonBlank(params.reason),
|
|
24723
|
-
subject: { type: "tool_call", toolCall: permissionsSubject(params.itemId, params.cwd, params.environmentId, params.permissions) },
|
|
24724
|
-
options: permissionProfileOptions()
|
|
24725
|
-
}, this.signal());
|
|
24726
|
-
return permissionsResponse(params.permissions, response);
|
|
24727
|
-
} catch (error40) {
|
|
24728
|
-
logger.error("permissions approval failed", error40, { itemId: params.itemId });
|
|
24729
|
-
return rejected();
|
|
24730
|
-
}
|
|
24364
|
+
defaultGatewayId() {
|
|
24365
|
+
const first = this.active;
|
|
24366
|
+
return first !== null && first.mode === "route" ? first.id : null;
|
|
24731
24367
|
}
|
|
24732
|
-
|
|
24733
|
-
|
|
24734
|
-
|
|
24735
|
-
|
|
24736
|
-
|
|
24737
|
-
case OptionId.AllowPermissionsForTurn:
|
|
24738
|
-
return { permissions: granted(permissions), scope: "turn", strictAutoReview: false };
|
|
24739
|
-
case OptionId.AllowPermissionsForTurnStrict:
|
|
24740
|
-
return { permissions: granted(permissions), scope: "turn", strictAutoReview: true };
|
|
24741
|
-
case OptionId.AllowPermissionsForSession:
|
|
24742
|
-
return { permissions: granted(permissions), scope: "session", strictAutoReview: false };
|
|
24743
|
-
default:
|
|
24744
|
-
return rejected();
|
|
24368
|
+
threadConfig(gatewayId = this.defaultGatewayId()) {
|
|
24369
|
+
const gateway = this.gateway(gatewayId);
|
|
24370
|
+
if (!gateway)
|
|
24371
|
+
return this.baseConfig;
|
|
24372
|
+
return this.applyGateway(this.baseConfig, gateway);
|
|
24745
24373
|
}
|
|
24746
|
-
|
|
24747
|
-
|
|
24748
|
-
return {
|
|
24749
|
-
...permissions.network ? { network: permissions.network } : {},
|
|
24750
|
-
...permissions.fileSystem ? { fileSystem: permissions.fileSystem } : {}
|
|
24751
|
-
};
|
|
24752
|
-
}
|
|
24753
|
-
function rejected() {
|
|
24754
|
-
return { permissions: {}, scope: "turn", strictAutoReview: false };
|
|
24755
|
-
}
|
|
24756
|
-
function nonBlank(value) {
|
|
24757
|
-
const trimmed = value?.trim();
|
|
24758
|
-
return trimmed ? trimmed : null;
|
|
24759
|
-
}
|
|
24760
|
-
|
|
24761
|
-
// src/permissions/mcp.ts
|
|
24762
|
-
var McpOptionId = {
|
|
24763
|
-
AllowOnce: "allow_once",
|
|
24764
|
-
Accept: "accept",
|
|
24765
|
-
AllowSession: "allow_session",
|
|
24766
|
-
AllowAlways: "allow_always",
|
|
24767
|
-
Decline: "decline",
|
|
24768
|
-
Cancel: "cancel"
|
|
24769
|
-
};
|
|
24770
|
-
function parsePersistOptions(meta) {
|
|
24771
|
-
const result = new Set;
|
|
24772
|
-
if (!isRecord2(meta))
|
|
24773
|
-
return result;
|
|
24774
|
-
const persist = meta["persist"];
|
|
24775
|
-
const values = Array.isArray(persist) ? persist : [persist];
|
|
24776
|
-
if (values.includes("session"))
|
|
24777
|
-
result.add("session");
|
|
24778
|
-
if (values.includes("always"))
|
|
24779
|
-
result.add("always");
|
|
24780
|
-
return result;
|
|
24781
|
-
}
|
|
24782
|
-
function isMcpToolCallApproval(meta) {
|
|
24783
|
-
return isRecord2(meta) && meta["codex_approval_kind"] === "mcp_tool_call";
|
|
24784
|
-
}
|
|
24785
|
-
function mcpPermissionOptions(isToolApproval, persist) {
|
|
24786
|
-
const options = [permissionOption(isToolApproval ? McpOptionId.AllowOnce : McpOptionId.Accept, "Allow", "allow_once", isToolApproval ? "Run the tool and continue." : "Allow this request and continue.")];
|
|
24787
|
-
if (persist.has("session")) {
|
|
24788
|
-
options.push(permissionOption(McpOptionId.AllowSession, "Allow for this session", "allow_always", isToolApproval ? "Run the tool and remember this choice for this session." : "Allow this request and remember this choice for this session."));
|
|
24374
|
+
routeConfig(config2, gatewayId) {
|
|
24375
|
+
return this.rebind(config2, this, gatewayId);
|
|
24789
24376
|
}
|
|
24790
|
-
|
|
24791
|
-
|
|
24377
|
+
overrideKeys() {
|
|
24378
|
+
const keys = new Set;
|
|
24379
|
+
for (const gateway of this.gateways.values())
|
|
24380
|
+
for (const key of Object.keys(gateway.config))
|
|
24381
|
+
keys.add(key);
|
|
24382
|
+
return [...keys];
|
|
24792
24383
|
}
|
|
24793
|
-
|
|
24794
|
-
|
|
24384
|
+
rebind(config2, previous, gatewayId = this.defaultGatewayId()) {
|
|
24385
|
+
const next = { ...config2 };
|
|
24386
|
+
for (const key of previous.overrideKeys()) {
|
|
24387
|
+
if (key in this.baseConfig)
|
|
24388
|
+
next[key] = this.baseConfig[key];
|
|
24389
|
+
else
|
|
24390
|
+
delete next[key];
|
|
24391
|
+
}
|
|
24392
|
+
delete next["model_providers"];
|
|
24393
|
+
if (isRecord2(this.baseConfig["model_providers"]))
|
|
24394
|
+
next["model_providers"] = this.baseConfig["model_providers"];
|
|
24395
|
+
const gateway = this.gateway(gatewayId);
|
|
24396
|
+
return gateway ? this.applyGateway(next, gateway) : next;
|
|
24795
24397
|
}
|
|
24796
|
-
|
|
24797
|
-
|
|
24798
|
-
|
|
24799
|
-
|
|
24800
|
-
|
|
24801
|
-
|
|
24802
|
-
|
|
24803
|
-
if (params.mode === "url") {
|
|
24804
|
-
return {
|
|
24805
|
-
title,
|
|
24806
|
-
description: params.message,
|
|
24807
|
-
subject: {
|
|
24808
|
-
type: "tool_call",
|
|
24809
|
-
toolCall: {
|
|
24810
|
-
toolCallId: `elicitation:${params.elicitationId}`,
|
|
24811
|
-
name: ToolName.Mcp,
|
|
24812
|
-
kind: "fetch",
|
|
24813
|
-
status: "pending",
|
|
24814
|
-
title: `Open ${params.url}`,
|
|
24815
|
-
content: [message],
|
|
24816
|
-
rawInput: { serverName: params.serverName, url: params.url }
|
|
24817
|
-
}
|
|
24818
|
-
},
|
|
24819
|
-
options
|
|
24398
|
+
applyGateway(config2, gateway) {
|
|
24399
|
+
const existing = isRecord2(config2["model_providers"]) ? config2["model_providers"] : {};
|
|
24400
|
+
const entry = {
|
|
24401
|
+
name: gateway.name,
|
|
24402
|
+
base_url: gateway.baseUrl,
|
|
24403
|
+
wire_api: "responses",
|
|
24404
|
+
http_headers: { "X-Client-Feature-ID": "codex", ...gateway.headers }
|
|
24820
24405
|
};
|
|
24821
|
-
|
|
24822
|
-
|
|
24406
|
+
if (gateway.bearerToken !== null)
|
|
24407
|
+
entry["experimental_bearer_token"] = gateway.bearerToken;
|
|
24823
24408
|
return {
|
|
24824
|
-
|
|
24825
|
-
|
|
24826
|
-
|
|
24827
|
-
options
|
|
24409
|
+
...config2,
|
|
24410
|
+
...gateway.config,
|
|
24411
|
+
model_providers: { ...existing, [gateway.id]: entry }
|
|
24828
24412
|
};
|
|
24829
24413
|
}
|
|
24830
|
-
|
|
24831
|
-
|
|
24832
|
-
|
|
24833
|
-
|
|
24834
|
-
|
|
24835
|
-
|
|
24836
|
-
|
|
24837
|
-
|
|
24838
|
-
|
|
24839
|
-
|
|
24840
|
-
|
|
24841
|
-
|
|
24842
|
-
|
|
24843
|
-
|
|
24844
|
-
|
|
24845
|
-
options
|
|
24846
|
-
};
|
|
24847
|
-
}
|
|
24848
|
-
function mcpPermissionResponse(response, isToolApproval, persist) {
|
|
24849
|
-
if (response.outcome.outcome !== "selected")
|
|
24850
|
-
return cancelled();
|
|
24851
|
-
switch (response.outcome.optionId) {
|
|
24852
|
-
case McpOptionId.AllowSession:
|
|
24853
|
-
return persist.has("session") ? accepted({ persist: "session" }) : cancelled();
|
|
24854
|
-
case McpOptionId.AllowAlways:
|
|
24855
|
-
return persist.has("always") ? accepted({ persist: "always" }) : cancelled();
|
|
24856
|
-
case McpOptionId.AllowOnce:
|
|
24857
|
-
return isToolApproval ? accepted(null) : cancelled();
|
|
24858
|
-
case McpOptionId.Accept:
|
|
24859
|
-
return isToolApproval ? cancelled() : accepted(null);
|
|
24860
|
-
case McpOptionId.Decline:
|
|
24861
|
-
return isToolApproval ? cancelled() : { action: "decline", content: null, _meta: null };
|
|
24862
|
-
default:
|
|
24863
|
-
return cancelled();
|
|
24414
|
+
catalog(codexCatalog) {
|
|
24415
|
+
const first = this.active;
|
|
24416
|
+
if (first === null)
|
|
24417
|
+
return codexCatalog;
|
|
24418
|
+
if (first.mode === "route") {
|
|
24419
|
+
if (first.models.length === 0)
|
|
24420
|
+
return codexCatalog;
|
|
24421
|
+
return first.models.map((model, index) => gatewayModel(model, index === 0, codexCatalog[0], first.catalogEntries.get(model.id)));
|
|
24422
|
+
}
|
|
24423
|
+
const own = [];
|
|
24424
|
+
for (const gateway of this.gateways.values()) {
|
|
24425
|
+
for (const model of gateway.models)
|
|
24426
|
+
own.push({ ...gatewayModel(model, false, codexCatalog[0], gateway.catalogEntries.get(model.id)), id: gatewayModelId(gateway.id, model.id) });
|
|
24427
|
+
}
|
|
24428
|
+
return [...codexCatalog, ...own];
|
|
24864
24429
|
}
|
|
24865
|
-
|
|
24866
|
-
|
|
24867
|
-
|
|
24868
|
-
|
|
24869
|
-
|
|
24870
|
-
|
|
24871
|
-
|
|
24872
|
-
|
|
24873
|
-
|
|
24874
|
-
var OTHER_SUFFIX = "__other";
|
|
24875
|
-
|
|
24876
|
-
class CodexElicitationHandler {
|
|
24877
|
-
client;
|
|
24878
|
-
turn;
|
|
24879
|
-
signal;
|
|
24880
|
-
pendingUrlElicitations = new Set;
|
|
24881
|
-
constructor(client, turn, signal = () => {
|
|
24882
|
-
return;
|
|
24883
|
-
}) {
|
|
24884
|
-
this.client = client;
|
|
24885
|
-
this.turn = turn;
|
|
24886
|
-
this.signal = signal;
|
|
24430
|
+
nativeBaseUrl() {
|
|
24431
|
+
const providers = this.baseConfig["model_providers"];
|
|
24432
|
+
if (this.configuredProvider && isRecord2(providers)) {
|
|
24433
|
+
const entry = providers[this.configuredProvider];
|
|
24434
|
+
const baseUrl = isRecord2(entry) ? entry["base_url"] : undefined;
|
|
24435
|
+
if (typeof baseUrl === "string" && baseUrl.length > 0)
|
|
24436
|
+
return baseUrl;
|
|
24437
|
+
}
|
|
24438
|
+
return DEFAULT_OPENAI_BASE_URL;
|
|
24887
24439
|
}
|
|
24888
|
-
|
|
24889
|
-
|
|
24890
|
-
|
|
24891
|
-
|
|
24892
|
-
|
|
24893
|
-
|
|
24894
|
-
|
|
24440
|
+
}
|
|
24441
|
+
function readHints(meta) {
|
|
24442
|
+
const root = isRecord2(meta) ? meta : {};
|
|
24443
|
+
const alwith = isRecord2(root["alwith"]) ? root["alwith"] : {};
|
|
24444
|
+
const codex = isRecord2(root["codex"]) ? root["codex"] : {};
|
|
24445
|
+
const id = readGatewayId(meta) ?? GATEWAY_MODEL_PROVIDER;
|
|
24446
|
+
const models = [];
|
|
24447
|
+
if (Array.isArray(alwith["models"])) {
|
|
24448
|
+
for (const entry of alwith["models"]) {
|
|
24449
|
+
if (!isRecord2(entry) || typeof entry["id"] !== "string" || entry["id"].length === 0)
|
|
24450
|
+
continue;
|
|
24451
|
+
models.push({
|
|
24452
|
+
id: entry["id"],
|
|
24453
|
+
name: typeof entry["label"] === "string" && entry["label"].length > 0 ? entry["label"] : entry["id"],
|
|
24454
|
+
description: typeof entry["description"] === "string" ? entry["description"] : null
|
|
24455
|
+
});
|
|
24895
24456
|
}
|
|
24896
24457
|
}
|
|
24897
|
-
|
|
24898
|
-
|
|
24899
|
-
|
|
24900
|
-
|
|
24901
|
-
|
|
24902
|
-
|
|
24903
|
-
const context = this.mcpContext(params);
|
|
24904
|
-
const messageOnly = isMessageOnlyForm(params);
|
|
24905
|
-
if (!messageOnly && this.useAcpElicitation(params)) {
|
|
24906
|
-
const response2 = await this.client.createElicitation(this.elicitationRequest(params, context), this.signal());
|
|
24907
|
-
const result2 = convertMcpElicitationResponse(response2, context);
|
|
24908
|
-
if (params.mode === "url" && result2.action === "accept")
|
|
24909
|
-
this.pendingUrlElicitations.add(params.elicitationId);
|
|
24910
|
-
if (result2.action === "accept")
|
|
24911
|
-
await this.markCorrelatedCallRunning(context);
|
|
24912
|
-
return result2;
|
|
24913
|
-
}
|
|
24914
|
-
if (params.mode !== "url" && !messageOnly) {
|
|
24915
|
-
return cancelled();
|
|
24916
|
-
}
|
|
24917
|
-
const request = mcpPermissionRequest(params, context, () => this.turn.nextStandaloneToolCallId(params.serverName));
|
|
24918
|
-
const response = await this.client.requestPermission(request, this.signal());
|
|
24919
|
-
const result = mcpPermissionResponse(response, context.isToolApproval, context.persistOptions);
|
|
24920
|
-
if (result.action === "accept")
|
|
24921
|
-
await this.markCorrelatedCallRunning(context);
|
|
24922
|
-
return result;
|
|
24923
|
-
} catch (error40) {
|
|
24924
|
-
logger.error("MCP elicitation failed", error40, { server: params.serverName });
|
|
24925
|
-
return cancelled();
|
|
24926
|
-
}
|
|
24458
|
+
const model = typeof alwith["model"] === "string" && alwith["model"].length > 0 ? alwith["model"] : null;
|
|
24459
|
+
const name = typeof codex["name"] === "string" && codex["name"].length > 0 ? codex["name"] : null;
|
|
24460
|
+
const bearerToken = typeof codex["bearerToken"] === "string" && codex["bearerToken"].length > 0 ? codex["bearerToken"] : null;
|
|
24461
|
+
const rawConfig = codex["config"];
|
|
24462
|
+
if (rawConfig !== undefined && !isRecord2(rawConfig)) {
|
|
24463
|
+
throw RequestError.invalidParams({ config: rawConfig }, "_meta.codex.config must be an object of top-level Codex config keys");
|
|
24927
24464
|
}
|
|
24928
|
-
|
|
24929
|
-
|
|
24930
|
-
|
|
24931
|
-
|
|
24932
|
-
|
|
24933
|
-
|
|
24934
|
-
} catch (error40) {
|
|
24935
|
-
logger.error("user input request failed", error40, { itemId: params.itemId });
|
|
24936
|
-
return { answers: {} };
|
|
24465
|
+
const config2 = {};
|
|
24466
|
+
if (isRecord2(rawConfig)) {
|
|
24467
|
+
for (const [key, value] of Object.entries(rawConfig)) {
|
|
24468
|
+
if (key === "model_providers")
|
|
24469
|
+
throw RequestError.invalidParams({ config: rawConfig }, "_meta.codex.config cannot set model_providers; that entry is derived from baseUrl and headers");
|
|
24470
|
+
config2[key] = value;
|
|
24937
24471
|
}
|
|
24938
24472
|
}
|
|
24939
|
-
|
|
24940
|
-
|
|
24941
|
-
|
|
24942
|
-
isToolApproval,
|
|
24943
|
-
persistOptions: parsePersistOptions(params._meta),
|
|
24944
|
-
correlatedCallId: isToolApproval ? this.turn.popPendingMcpCall(params.serverName) : undefined
|
|
24945
|
-
};
|
|
24473
|
+
const rawMode = codex["mode"];
|
|
24474
|
+
if (rawMode !== undefined && rawMode !== "route" && rawMode !== "catalog") {
|
|
24475
|
+
throw RequestError.invalidParams({ mode: rawMode }, '_meta.codex.mode must be "route" or "catalog"');
|
|
24946
24476
|
}
|
|
24947
|
-
|
|
24948
|
-
|
|
24949
|
-
|
|
24950
|
-
if (params.mode === "url")
|
|
24951
|
-
return this.client.capabilities.urlElicitation;
|
|
24952
|
-
return false;
|
|
24477
|
+
const mode = rawMode === "catalog" ? "catalog" : "route";
|
|
24478
|
+
if (mode === "catalog" && models.length === 0) {
|
|
24479
|
+
throw RequestError.invalidParams(undefined, `_meta.codex.mode "catalog" needs _meta.alwith.models: the models offered next to Codex's`);
|
|
24953
24480
|
}
|
|
24954
|
-
|
|
24955
|
-
|
|
24956
|
-
|
|
24957
|
-
|
|
24958
|
-
|
|
24959
|
-
|
|
24960
|
-
|
|
24961
|
-
|
|
24962
|
-
|
|
24481
|
+
const catalogPath = config2["model_catalog_json"];
|
|
24482
|
+
const catalogEntries = typeof catalogPath === "string" ? readCatalogFile(catalogPath) : new Map;
|
|
24483
|
+
return { id, models, model, name, bearerToken, config: config2, mode, catalogEntries };
|
|
24484
|
+
}
|
|
24485
|
+
function readGatewayId(meta) {
|
|
24486
|
+
const root = isRecord2(meta) ? meta : {};
|
|
24487
|
+
const codex = isRecord2(root["codex"]) ? root["codex"] : {};
|
|
24488
|
+
const raw = codex["id"];
|
|
24489
|
+
if (raw === undefined)
|
|
24490
|
+
return null;
|
|
24491
|
+
if (typeof raw !== "string" || !GATEWAY_ID_PATTERN.test(raw)) {
|
|
24492
|
+
throw RequestError.invalidParams({ id: raw }, "_meta.codex.id must match [a-z0-9-]+");
|
|
24493
|
+
}
|
|
24494
|
+
if (raw === NATIVE_GROUP_ID) {
|
|
24495
|
+
throw RequestError.invalidParams({ id: raw }, `_meta.codex.id "${NATIVE_GROUP_ID}" is the native model group`);
|
|
24496
|
+
}
|
|
24497
|
+
return raw;
|
|
24498
|
+
}
|
|
24499
|
+
function readCatalogFile(catalogPath) {
|
|
24500
|
+
const resolved = catalogPath.startsWith("~/") ? path4.join(os.homedir(), catalogPath.slice(2)) : catalogPath;
|
|
24501
|
+
let parsed;
|
|
24502
|
+
try {
|
|
24503
|
+
parsed = JSON.parse(readFileSync(resolved, "utf8"));
|
|
24504
|
+
} catch (error40) {
|
|
24505
|
+
throw RequestError.invalidParams({ model_catalog_json: catalogPath }, `model_catalog_json could not be read as JSON: ${error40 instanceof Error ? error40.message : String(error40)}`);
|
|
24506
|
+
}
|
|
24507
|
+
const models = isRecord2(parsed) && Array.isArray(parsed["models"]) ? parsed["models"] : null;
|
|
24508
|
+
if (models === null)
|
|
24509
|
+
throw RequestError.invalidParams({ model_catalog_json: catalogPath }, "model_catalog_json must be {models: [...]} as Codex expects");
|
|
24510
|
+
const entries = new Map;
|
|
24511
|
+
for (const entry of models) {
|
|
24512
|
+
if (!isRecord2(entry) || typeof entry["slug"] !== "string")
|
|
24513
|
+
continue;
|
|
24514
|
+
const levels = Array.isArray(entry["supported_reasoning_levels"]) ? entry["supported_reasoning_levels"] : [];
|
|
24515
|
+
const efforts = [];
|
|
24516
|
+
for (const level of levels) {
|
|
24517
|
+
if (!isRecord2(level) || typeof level["effort"] !== "string")
|
|
24518
|
+
continue;
|
|
24519
|
+
efforts.push({ reasoningEffort: level["effort"], description: typeof level["description"] === "string" ? level["description"] : "" });
|
|
24963
24520
|
}
|
|
24964
|
-
|
|
24521
|
+
const modalities = Array.isArray(entry["input_modalities"]) ? entry["input_modalities"].filter((value) => value === "text" || value === "image") : null;
|
|
24522
|
+
entries.set(entry["slug"], {
|
|
24523
|
+
displayName: typeof entry["display_name"] === "string" ? entry["display_name"] : null,
|
|
24524
|
+
description: typeof entry["description"] === "string" ? entry["description"] : null,
|
|
24525
|
+
efforts,
|
|
24526
|
+
defaultEffort: typeof entry["default_reasoning_level"] === "string" ? entry["default_reasoning_level"] : null,
|
|
24527
|
+
inputModalities: modalities
|
|
24528
|
+
});
|
|
24965
24529
|
}
|
|
24966
|
-
|
|
24967
|
-
|
|
24968
|
-
|
|
24969
|
-
|
|
24530
|
+
return entries;
|
|
24531
|
+
}
|
|
24532
|
+
function gatewayModel(model, isDefault, template, entry) {
|
|
24533
|
+
const efforts = entry && entry.efforts.length > 0 ? entry.efforts : template?.supportedReasoningEfforts ?? [];
|
|
24534
|
+
const defaultEffort = entry?.defaultEffort ?? template?.defaultReasoningEffort ?? "medium";
|
|
24535
|
+
return {
|
|
24536
|
+
id: model.id,
|
|
24537
|
+
model: model.id,
|
|
24538
|
+
upgrade: null,
|
|
24539
|
+
upgradeInfo: null,
|
|
24540
|
+
availabilityNux: null,
|
|
24541
|
+
displayName: model.name !== model.id ? model.name : entry?.displayName ?? model.name,
|
|
24542
|
+
description: model.description ?? entry?.description ?? "",
|
|
24543
|
+
modelSpecialty: null,
|
|
24544
|
+
hidden: false,
|
|
24545
|
+
supportedReasoningEfforts: efforts,
|
|
24546
|
+
defaultReasoningEffort: defaultEffort,
|
|
24547
|
+
inputModalities: entry?.inputModalities ?? ["text", "image"],
|
|
24548
|
+
supportsPersonality: false,
|
|
24549
|
+
multiAgentVersion: null,
|
|
24550
|
+
additionalSpeedTiers: [],
|
|
24551
|
+
serviceTiers: [],
|
|
24552
|
+
defaultServiceTier: null,
|
|
24553
|
+
availableAccessPrograms: null,
|
|
24554
|
+
isDefault
|
|
24555
|
+
};
|
|
24556
|
+
}
|
|
24557
|
+
|
|
24558
|
+
// src/codex/models.ts
|
|
24559
|
+
var MODEL_CONFIG_ID = "model";
|
|
24560
|
+
var EFFORT_CONFIG_ID = "effort";
|
|
24561
|
+
var FAST_MODE_CONFIG_ID = "fast_mode";
|
|
24562
|
+
var COLLABORATION_MODE_CONFIG_ID = "collaboration_mode";
|
|
24563
|
+
var DEFAULT_EFFORT = "medium";
|
|
24564
|
+
var FAST_SERVICE_TIER = "fast";
|
|
24565
|
+
function resolveModelSelection(catalog, modelId, effort) {
|
|
24566
|
+
const listed = modelId === null ? undefined : catalog.find((model) => model.id === modelId);
|
|
24567
|
+
if (listed)
|
|
24568
|
+
return { model: listed.id, effort: effort ?? listed.defaultReasoningEffort };
|
|
24569
|
+
if (modelId)
|
|
24570
|
+
return { model: modelId, effort: effort ?? DEFAULT_EFFORT };
|
|
24571
|
+
const fallback = catalog.find((model) => model.isDefault) ?? catalog[0];
|
|
24572
|
+
if (!fallback)
|
|
24573
|
+
throw new Error("Codex did not return any models");
|
|
24574
|
+
return { model: fallback.id, effort: effort ?? fallback.defaultReasoningEffort };
|
|
24575
|
+
}
|
|
24576
|
+
function findModel(catalog, modelId) {
|
|
24577
|
+
return catalog.find((model) => model.id === modelId);
|
|
24578
|
+
}
|
|
24579
|
+
function supportedEfforts(model) {
|
|
24580
|
+
return model?.supportedReasoningEfforts ?? [];
|
|
24581
|
+
}
|
|
24582
|
+
function coerceEffort(model, effort) {
|
|
24583
|
+
const options = supportedEfforts(model);
|
|
24584
|
+
if (options.length === 0)
|
|
24585
|
+
return effort;
|
|
24586
|
+
if (options.some((option) => option.reasoningEffort === effort))
|
|
24587
|
+
return effort;
|
|
24588
|
+
return model?.defaultReasoningEffort ?? options[0].reasoningEffort;
|
|
24589
|
+
}
|
|
24590
|
+
function modelSupportsFast(model) {
|
|
24591
|
+
if (!model)
|
|
24592
|
+
return false;
|
|
24593
|
+
return model.serviceTiers.some((tier) => tier.id === FAST_SERVICE_TIER) || model.additionalSpeedTiers.includes(FAST_SERVICE_TIER);
|
|
24594
|
+
}
|
|
24595
|
+
function modelSupportsImages(model) {
|
|
24596
|
+
return model?.inputModalities.includes("image") ?? true;
|
|
24597
|
+
}
|
|
24598
|
+
function modelLacksReasoning(model) {
|
|
24599
|
+
const options = supportedEfforts(model);
|
|
24600
|
+
return options.length > 0 && options.every((option) => option.reasoningEffort === "none");
|
|
24601
|
+
}
|
|
24602
|
+
function modelConfigOption(catalog, current, gatewayGroups = []) {
|
|
24603
|
+
const visible = catalog.filter((model) => !model.hidden || model.id === current);
|
|
24604
|
+
const options = visible.map((model) => ({
|
|
24605
|
+
value: model.id,
|
|
24606
|
+
name: model.displayName,
|
|
24607
|
+
description: model.description || null
|
|
24608
|
+
}));
|
|
24609
|
+
if (!options.some((option) => option.value === current)) {
|
|
24610
|
+
options.unshift({ value: current, name: current, description: null });
|
|
24611
|
+
}
|
|
24612
|
+
const base = {
|
|
24613
|
+
configId: MODEL_CONFIG_ID,
|
|
24614
|
+
name: "Model",
|
|
24615
|
+
description: "Model Codex uses for this session",
|
|
24616
|
+
category: "model",
|
|
24617
|
+
type: "select",
|
|
24618
|
+
currentValue: current
|
|
24619
|
+
};
|
|
24620
|
+
if (gatewayGroups.length === 0)
|
|
24621
|
+
return { ...base, options };
|
|
24622
|
+
const owner = new Map;
|
|
24623
|
+
for (const group of gatewayGroups)
|
|
24624
|
+
for (const id of group.modelIds)
|
|
24625
|
+
owner.set(id, group.id);
|
|
24626
|
+
const native = options.filter((option) => !owner.has(option.value));
|
|
24627
|
+
return {
|
|
24628
|
+
...base,
|
|
24629
|
+
options: [
|
|
24630
|
+
{ groupId: NATIVE_GROUP_ID, name: NATIVE_GROUP_NAME, options: native },
|
|
24631
|
+
...gatewayGroups.map((group) => ({ groupId: group.id, name: group.name, options: options.filter((option) => owner.get(option.value) === group.id) }))
|
|
24632
|
+
]
|
|
24633
|
+
};
|
|
24634
|
+
}
|
|
24635
|
+
function effortConfigOption(efforts, current) {
|
|
24636
|
+
const options = efforts.map((option) => ({
|
|
24637
|
+
value: option.reasoningEffort,
|
|
24638
|
+
name: capitalize(option.reasoningEffort),
|
|
24639
|
+
description: option.description || null
|
|
24640
|
+
}));
|
|
24641
|
+
if (!options.some((option) => option.value === current)) {
|
|
24642
|
+
options.unshift({ value: current, name: capitalize(current), description: null });
|
|
24970
24643
|
}
|
|
24644
|
+
return {
|
|
24645
|
+
configId: EFFORT_CONFIG_ID,
|
|
24646
|
+
name: "Reasoning effort",
|
|
24647
|
+
description: "How much reasoning effort the model should use",
|
|
24648
|
+
category: "thought_level",
|
|
24649
|
+
type: "select",
|
|
24650
|
+
currentValue: current,
|
|
24651
|
+
options
|
|
24652
|
+
};
|
|
24971
24653
|
}
|
|
24972
|
-
function
|
|
24973
|
-
|
|
24974
|
-
|
|
24975
|
-
|
|
24976
|
-
|
|
24977
|
-
|
|
24978
|
-
|
|
24979
|
-
|
|
24980
|
-
|
|
24654
|
+
function fastModeConfigOption(enabled) {
|
|
24655
|
+
return {
|
|
24656
|
+
configId: FAST_MODE_CONFIG_ID,
|
|
24657
|
+
name: "Fast mode",
|
|
24658
|
+
description: "1.5x speed, increased usage",
|
|
24659
|
+
category: "model_config",
|
|
24660
|
+
type: "boolean",
|
|
24661
|
+
currentValue: enabled
|
|
24662
|
+
};
|
|
24981
24663
|
}
|
|
24982
|
-
|
|
24983
|
-
|
|
24984
|
-
|
|
24985
|
-
|
|
24986
|
-
|
|
24664
|
+
var DEFAULT_COLLABORATION_MODE = "default";
|
|
24665
|
+
var PLAN_COLLABORATION_MODE = "plan";
|
|
24666
|
+
function collaborationModeConfigOption(current) {
|
|
24667
|
+
return {
|
|
24668
|
+
configId: COLLABORATION_MODE_CONFIG_ID,
|
|
24669
|
+
name: "Collaboration mode",
|
|
24670
|
+
description: "How Codex collaborates on subsequent turns",
|
|
24671
|
+
category: "model_config",
|
|
24672
|
+
type: "select",
|
|
24673
|
+
currentValue: current,
|
|
24674
|
+
options: [
|
|
24675
|
+
{ value: DEFAULT_COLLABORATION_MODE, name: "Default", description: "Make changes directly" },
|
|
24676
|
+
{ value: PLAN_COLLABORATION_MODE, name: "Plan", description: "Plan before making changes" }
|
|
24677
|
+
]
|
|
24678
|
+
};
|
|
24987
24679
|
}
|
|
24988
|
-
function
|
|
24989
|
-
if (
|
|
24990
|
-
return
|
|
24991
|
-
if (
|
|
24992
|
-
return
|
|
24993
|
-
|
|
24994
|
-
return value;
|
|
24995
|
-
const result = Object.fromEntries(Object.entries(value).filter(([, nested]) => nested !== undefined).map(([key, nested]) => [key, normalizeSchemaValue(nested)]));
|
|
24996
|
-
if (result["type"] === "string" && Array.isArray(result["enum"]) && Array.isArray(result["enumNames"]) && !Array.isArray(result["oneOf"])) {
|
|
24997
|
-
const values = result["enum"];
|
|
24998
|
-
const names = result["enumNames"];
|
|
24999
|
-
result["oneOf"] = values.map((entry, index) => ({ const: String(entry), title: String(names[index] ?? entry) }));
|
|
25000
|
-
delete result["enum"];
|
|
25001
|
-
delete result["enumNames"];
|
|
25002
|
-
}
|
|
25003
|
-
return result;
|
|
24680
|
+
function parseCollaborationMode(value) {
|
|
24681
|
+
if (value === DEFAULT_COLLABORATION_MODE)
|
|
24682
|
+
return DEFAULT_COLLABORATION_MODE;
|
|
24683
|
+
if (value === PLAN_COLLABORATION_MODE)
|
|
24684
|
+
return PLAN_COLLABORATION_MODE;
|
|
24685
|
+
return;
|
|
25004
24686
|
}
|
|
25005
|
-
function
|
|
25006
|
-
|
|
25007
|
-
|
|
25008
|
-
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
return { action: "accept", content: contentOrNull(content), _meta: responseMeta(response, context, persist) };
|
|
25014
|
-
}
|
|
25015
|
-
if (CreateElicitationResponse.isDecline(response)) {
|
|
25016
|
-
return context.isToolApproval ? { action: "cancel", content: null, _meta: responseMeta(response, context) } : { action: "decline", content: null, _meta: responseMeta(response, context) };
|
|
24687
|
+
function capitalize(value) {
|
|
24688
|
+
return value.length === 0 ? value : value[0].toUpperCase() + value.slice(1);
|
|
24689
|
+
}
|
|
24690
|
+
|
|
24691
|
+
// src/permissions/commandDecisions.ts
|
|
24692
|
+
function availableCommandDecisions(params) {
|
|
24693
|
+
if (params.availableDecisions === undefined || params.availableDecisions === null) {
|
|
24694
|
+
return defaultDecisions(params);
|
|
25017
24695
|
}
|
|
25018
|
-
if (
|
|
25019
|
-
return
|
|
24696
|
+
if (!Array.isArray(params.availableDecisions) || params.availableDecisions.length === 0)
|
|
24697
|
+
return;
|
|
24698
|
+
const decisions = [];
|
|
24699
|
+
for (const candidate of params.availableDecisions) {
|
|
24700
|
+
const decision = parseDecision(candidate, params);
|
|
24701
|
+
if (!decision)
|
|
24702
|
+
return;
|
|
24703
|
+
decisions.push(decision);
|
|
25020
24704
|
}
|
|
25021
|
-
return
|
|
25022
|
-
}
|
|
25023
|
-
function persistAllowed(persist, options) {
|
|
25024
|
-
return persist === undefined || persist === "once" || persist === "session" && options.has("session") || persist === "always" && options.has("always");
|
|
25025
|
-
}
|
|
25026
|
-
function contentOrNull(content) {
|
|
25027
|
-
const entries = Object.entries(content);
|
|
25028
|
-
if (entries.length === 0)
|
|
25029
|
-
return null;
|
|
25030
|
-
return Object.fromEntries(entries.map(([key, value]) => [key, normalizeJsonValue(value)]));
|
|
25031
|
-
}
|
|
25032
|
-
function responseMeta(response, context, persist) {
|
|
25033
|
-
const source = recordOrNull(response._meta);
|
|
25034
|
-
const meta = source ? normalizeJsonObject(source) : {};
|
|
25035
|
-
if (context.isToolApproval)
|
|
25036
|
-
delete meta["persist"];
|
|
25037
|
-
if (persist === "session" || persist === "always")
|
|
25038
|
-
meta["persist"] = persist;
|
|
25039
|
-
return Object.keys(meta).length === 0 ? null : meta;
|
|
24705
|
+
return decisions;
|
|
25040
24706
|
}
|
|
25041
|
-
function
|
|
25042
|
-
|
|
25043
|
-
|
|
25044
|
-
|
|
25045
|
-
|
|
25046
|
-
const options = question.options ?? [];
|
|
25047
|
-
const hasOther = question.isOther && options.length > 0;
|
|
25048
|
-
const base = {
|
|
25049
|
-
title: question.header || question.id,
|
|
25050
|
-
description: question.question,
|
|
25051
|
-
_meta: { codex: { isOther: question.isOther, isSecret: question.isSecret } }
|
|
25052
|
-
};
|
|
25053
|
-
if (!hasOther)
|
|
25054
|
-
required2.push(question.id);
|
|
25055
|
-
properties[question.id] = options.length > 0 ? { ...base, type: "string", oneOf: options.map((option) => ({ const: option.label, title: option.label, description: option.description })) } : { ...base, type: "string" };
|
|
25056
|
-
if (hasOther) {
|
|
25057
|
-
properties[otherFieldId(question.id, ids)] = {
|
|
25058
|
-
type: "string",
|
|
25059
|
-
title: "Other",
|
|
25060
|
-
description: "Type your own answer instead of choosing an option above.",
|
|
25061
|
-
_meta: { codex: { questionId: question.id, isOtherAnswer: true, isSecret: question.isSecret } }
|
|
25062
|
-
};
|
|
24707
|
+
function defaultDecisions(params) {
|
|
24708
|
+
if (params.networkApprovalContext) {
|
|
24709
|
+
const decisions2 = ["accept", "acceptForSession"];
|
|
24710
|
+
for (const amendment of params.proposedNetworkPolicyAmendments ?? []) {
|
|
24711
|
+
decisions2.push({ applyNetworkPolicyAmendment: { network_policy_amendment: amendment } });
|
|
25063
24712
|
}
|
|
24713
|
+
decisions2.push("decline", "cancel");
|
|
24714
|
+
return decisions2;
|
|
25064
24715
|
}
|
|
25065
|
-
|
|
25066
|
-
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
|
|
25070
|
-
|
|
25071
|
-
|
|
25072
|
-
|
|
25073
|
-
};
|
|
24716
|
+
if (params.additionalPermissions)
|
|
24717
|
+
return ["accept", "cancel"];
|
|
24718
|
+
const decisions = ["accept", "acceptForSession"];
|
|
24719
|
+
if (params.proposedExecpolicyAmendment && params.proposedExecpolicyAmendment.length > 0) {
|
|
24720
|
+
decisions.push({ acceptWithExecpolicyAmendment: { execpolicy_amendment: params.proposedExecpolicyAmendment } });
|
|
24721
|
+
}
|
|
24722
|
+
decisions.push("decline", "cancel");
|
|
24723
|
+
return decisions;
|
|
25074
24724
|
}
|
|
25075
|
-
function
|
|
25076
|
-
if (
|
|
25077
|
-
return
|
|
25078
|
-
const content = isRecord2(response.content) ? response.content : {};
|
|
25079
|
-
const ids = new Set(params.questions.map((question) => question.id));
|
|
25080
|
-
const answers = {};
|
|
25081
|
-
for (const question of params.questions) {
|
|
25082
|
-
const hasOther = question.isOther && (question.options?.length ?? 0) > 0;
|
|
25083
|
-
const value = (hasOther ? answerValue(content, otherFieldId(question.id, ids)) : undefined) ?? answerValue(content, question.id);
|
|
25084
|
-
if (value === undefined)
|
|
25085
|
-
continue;
|
|
25086
|
-
answers[question.id] = { answers: Array.isArray(value) ? value.map(String) : [String(value)] };
|
|
24725
|
+
function parseDecision(candidate, params) {
|
|
24726
|
+
if (candidate === "accept" || candidate === "acceptForSession" || candidate === "decline" || candidate === "cancel") {
|
|
24727
|
+
return candidate;
|
|
25087
24728
|
}
|
|
25088
|
-
|
|
24729
|
+
if (!isRecord2(candidate))
|
|
24730
|
+
return;
|
|
24731
|
+
if ("acceptWithExecpolicyAmendment" in candidate) {
|
|
24732
|
+
const value = candidate["acceptWithExecpolicyAmendment"];
|
|
24733
|
+
if (!isRecord2(value))
|
|
24734
|
+
return;
|
|
24735
|
+
const amendment = value["execpolicy_amendment"];
|
|
24736
|
+
if (!isStringArray(amendment) || amendment.length === 0)
|
|
24737
|
+
return;
|
|
24738
|
+
if (!sameStrings(amendment, params.proposedExecpolicyAmendment))
|
|
24739
|
+
return;
|
|
24740
|
+
return { acceptWithExecpolicyAmendment: { execpolicy_amendment: [...amendment] } };
|
|
24741
|
+
}
|
|
24742
|
+
if ("applyNetworkPolicyAmendment" in candidate) {
|
|
24743
|
+
const value = candidate["applyNetworkPolicyAmendment"];
|
|
24744
|
+
if (!isRecord2(value))
|
|
24745
|
+
return;
|
|
24746
|
+
const amendment = parseNetworkAmendment(value["network_policy_amendment"]);
|
|
24747
|
+
if (!amendment || !params.networkApprovalContext)
|
|
24748
|
+
return;
|
|
24749
|
+
if (amendment.host !== params.networkApprovalContext.host)
|
|
24750
|
+
return;
|
|
24751
|
+
const proposed = params.proposedNetworkPolicyAmendments ?? [];
|
|
24752
|
+
if (!proposed.some((entry) => entry.host === amendment.host && entry.action === amendment.action))
|
|
24753
|
+
return;
|
|
24754
|
+
return { applyNetworkPolicyAmendment: { network_policy_amendment: amendment } };
|
|
24755
|
+
}
|
|
24756
|
+
return;
|
|
25089
24757
|
}
|
|
25090
|
-
function
|
|
25091
|
-
|
|
25092
|
-
if (typeof value === "string" && value.trim() === "")
|
|
24758
|
+
function parseNetworkAmendment(value) {
|
|
24759
|
+
if (!isRecord2(value) || typeof value["host"] !== "string")
|
|
25093
24760
|
return;
|
|
25094
|
-
|
|
24761
|
+
const action = value["action"];
|
|
24762
|
+
if (action !== "allow" && action !== "deny")
|
|
25095
24763
|
return;
|
|
25096
|
-
return value;
|
|
24764
|
+
return { host: value["host"], action };
|
|
25097
24765
|
}
|
|
25098
|
-
function
|
|
25099
|
-
|
|
25100
|
-
if (!ids.has(base))
|
|
25101
|
-
return base;
|
|
25102
|
-
let index = 1;
|
|
25103
|
-
while (ids.has(`${base}${index}`))
|
|
25104
|
-
index += 1;
|
|
25105
|
-
return `${base}${index}`;
|
|
24766
|
+
function isStringArray(value) {
|
|
24767
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
25106
24768
|
}
|
|
25107
|
-
|
|
25108
|
-
|
|
25109
|
-
|
|
25110
|
-
|
|
25111
|
-
|
|
25112
|
-
|
|
25113
|
-
|
|
25114
|
-
|
|
25115
|
-
|
|
25116
|
-
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
|
|
25122
|
-
case "item/completed":
|
|
25123
|
-
this.itemCompleted(notification.params.item);
|
|
25124
|
-
return;
|
|
25125
|
-
case "turn/completed":
|
|
25126
|
-
this.fileChanges.clear();
|
|
25127
|
-
this.pendingMcpCalls.clear();
|
|
25128
|
-
return;
|
|
25129
|
-
case "serverRequest/resolved":
|
|
25130
|
-
this.pendingMcpCalls.clear();
|
|
25131
|
-
return;
|
|
25132
|
-
default:
|
|
25133
|
-
return;
|
|
25134
|
-
}
|
|
25135
|
-
}
|
|
25136
|
-
fileChange(itemId) {
|
|
25137
|
-
return this.fileChanges.get(itemId);
|
|
24769
|
+
function sameStrings(left, right) {
|
|
24770
|
+
return !!right && left.length === right.length && left.every((value, index) => value === right[index]);
|
|
24771
|
+
}
|
|
24772
|
+
function renderExecPolicyPrefix(command) {
|
|
24773
|
+
const script = extractWrappedScript(command);
|
|
24774
|
+
if (script !== undefined)
|
|
24775
|
+
return script;
|
|
24776
|
+
if (command.some((value) => value.includes("\x00")))
|
|
24777
|
+
return command.join(" ");
|
|
24778
|
+
return command.map(shlexQuote).join(" ");
|
|
24779
|
+
}
|
|
24780
|
+
function extractWrappedScript(command) {
|
|
24781
|
+
const executable = executableName(command[0]);
|
|
24782
|
+
if ((executable === "bash" || executable === "zsh" || executable === "sh") && command.length === 3 && (command[1] === "-lc" || command[1] === "-c")) {
|
|
24783
|
+
return command[2];
|
|
25138
24784
|
}
|
|
25139
|
-
|
|
25140
|
-
|
|
25141
|
-
|
|
24785
|
+
if (executable !== "pwsh" && executable !== "powershell")
|
|
24786
|
+
return;
|
|
24787
|
+
const allowedFlags = new Set(["-nologo", "-noprofile", "-command", "-c"]);
|
|
24788
|
+
for (let index = 1;index + 1 < command.length; index++) {
|
|
24789
|
+
const flag = command[index]?.toLowerCase();
|
|
24790
|
+
if (flag === undefined || !allowedFlags.has(flag))
|
|
25142
24791
|
return;
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
}
|
|
25146
|
-
nextStandaloneToolCallId(serverName) {
|
|
25147
|
-
this.sequence += 1;
|
|
25148
|
-
return `elicitation:${this.sessionId}:${serverName}:${this.sequence}`;
|
|
24792
|
+
if (flag === "-command" || flag === "-c")
|
|
24793
|
+
return command[index + 1];
|
|
25149
24794
|
}
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
|
|
25153
|
-
|
|
25154
|
-
|
|
25155
|
-
|
|
25156
|
-
|
|
25157
|
-
|
|
24795
|
+
return;
|
|
24796
|
+
}
|
|
24797
|
+
function executableName(command) {
|
|
24798
|
+
let filename = command?.replaceAll("\\", "/").split("/").at(-1);
|
|
24799
|
+
while (filename !== undefined) {
|
|
24800
|
+
if (["bash", "zsh", "sh", "pwsh", "powershell"].includes(filename))
|
|
24801
|
+
return filename;
|
|
24802
|
+
const dot = filename.lastIndexOf(".");
|
|
24803
|
+
if (dot <= 0)
|
|
24804
|
+
return;
|
|
24805
|
+
filename = filename.slice(0, dot);
|
|
25158
24806
|
}
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25169
|
-
|
|
25170
|
-
|
|
24807
|
+
return;
|
|
24808
|
+
}
|
|
24809
|
+
function shlexQuote(value) {
|
|
24810
|
+
if (value.length === 0)
|
|
24811
|
+
return "''";
|
|
24812
|
+
if (/^[0-9A-Za-z+\-./:@_]+$/.test(value))
|
|
24813
|
+
return value;
|
|
24814
|
+
if (!value.includes("'"))
|
|
24815
|
+
return `'${value}'`;
|
|
24816
|
+
return `"${value.replace(/["\\$`!]/g, "\\$&")}"`;
|
|
24817
|
+
}
|
|
24818
|
+
|
|
24819
|
+
// src/permissions/options.ts
|
|
24820
|
+
var OptionId = {
|
|
24821
|
+
AllowOnce: "allow_once",
|
|
24822
|
+
AllowForSession: "allow_for_session",
|
|
24823
|
+
Decline: "decline",
|
|
24824
|
+
Cancel: "cancel",
|
|
24825
|
+
AcceptWithExecpolicyAmendment: "accept_execpolicy_amendment",
|
|
24826
|
+
ApplyNetworkPolicyAmendment: "apply_network_policy_amendment",
|
|
24827
|
+
AllowPermissionsForTurn: "allow_permissions_turn",
|
|
24828
|
+
AllowPermissionsForTurnStrict: "allow_permissions_turn_strict_auto_review",
|
|
24829
|
+
AllowPermissionsForSession: "allow_permissions_session",
|
|
24830
|
+
RejectPermissions: "reject_permissions"
|
|
24831
|
+
};
|
|
24832
|
+
function commandDecisionOptions(params) {
|
|
24833
|
+
const decisions = availableCommandDecisions(params);
|
|
24834
|
+
if (!decisions)
|
|
24835
|
+
return;
|
|
24836
|
+
const options = [];
|
|
24837
|
+
let networkIndex = 0;
|
|
24838
|
+
for (const decision of decisions) {
|
|
24839
|
+
if (decision === "accept") {
|
|
24840
|
+
options.push(decisionOption(OptionId.AllowOnce, params.networkApprovalContext ? "Yes, just this once" : "Yes, proceed", "allow_once", decision));
|
|
24841
|
+
} else if (decision === "acceptForSession") {
|
|
24842
|
+
const name = params.networkApprovalContext ? "Yes, and allow this host for this conversation" : params.additionalPermissions ? "Yes, and allow these permissions for this session" : "Yes, and don't ask again for this command in this session";
|
|
24843
|
+
options.push(decisionOption(OptionId.AllowForSession, name, "allow_always", decision));
|
|
24844
|
+
} else if (decision === "decline") {
|
|
24845
|
+
options.push(decisionOption(OptionId.Decline, "No, continue without running it", "reject_once", decision));
|
|
24846
|
+
} else if (decision === "cancel") {
|
|
24847
|
+
options.push(decisionOption(OptionId.Cancel, "No, and tell Codex what to do differently", "reject_once", decision));
|
|
24848
|
+
} else if ("acceptWithExecpolicyAmendment" in decision) {
|
|
24849
|
+
const prefix = renderExecPolicyPrefix(decision.acceptWithExecpolicyAmendment.execpolicy_amendment);
|
|
24850
|
+
if (prefix.includes(`
|
|
24851
|
+
`) || prefix.includes("\r"))
|
|
24852
|
+
continue;
|
|
24853
|
+
options.push(decisionOption(OptionId.AcceptWithExecpolicyAmendment, `Yes, and don't ask again for commands that start with \`${prefix}\``, "allow_always", decision));
|
|
24854
|
+
} else {
|
|
24855
|
+
const amendment = decision.applyNetworkPolicyAmendment.network_policy_amendment;
|
|
24856
|
+
options.push(decisionOption(`${OptionId.ApplyNetworkPolicyAmendment}:${networkIndex++}`, amendment.action === "allow" ? "Yes, and allow this host in the future" : "No, and block this host in the future", amendment.action === "allow" ? "allow_always" : "reject_always", decision));
|
|
25171
24857
|
}
|
|
25172
24858
|
}
|
|
24859
|
+
const ordered = [...options].sort((left, right) => order(left.option) - order(right.option));
|
|
24860
|
+
const hasAllow = ordered.some(({ option }) => option.kind === "allow_once" || option.kind === "allow_always");
|
|
24861
|
+
const hasReject = ordered.some(({ option }) => option.kind === "reject_once" || option.kind === "reject_always");
|
|
24862
|
+
const ids = ordered.map(({ option }) => option.optionId);
|
|
24863
|
+
return hasAllow && hasReject && new Set(ids).size === ids.length ? ordered : undefined;
|
|
24864
|
+
}
|
|
24865
|
+
function order(option) {
|
|
24866
|
+
if (option.kind === "allow_once")
|
|
24867
|
+
return 0;
|
|
24868
|
+
if (option.kind === "allow_always")
|
|
24869
|
+
return 1;
|
|
24870
|
+
return 2;
|
|
24871
|
+
}
|
|
24872
|
+
function fileChangeDecisionOptions() {
|
|
24873
|
+
return [
|
|
24874
|
+
decisionOption(OptionId.AllowOnce, "Yes, proceed", "allow_once", "accept"),
|
|
24875
|
+
decisionOption(OptionId.AllowForSession, "Yes, and don't ask again for these files", "allow_always", "acceptForSession"),
|
|
24876
|
+
decisionOption(OptionId.Cancel, "No, and tell Codex what to do differently", "reject_once", "cancel")
|
|
24877
|
+
];
|
|
24878
|
+
}
|
|
24879
|
+
function permissionProfileOptions() {
|
|
24880
|
+
return [
|
|
24881
|
+
permissionOption(OptionId.AllowPermissionsForTurn, "Yes, grant these permissions for this turn", "allow_once"),
|
|
24882
|
+
permissionOption(OptionId.AllowPermissionsForTurnStrict, "Yes, grant for this turn with strict auto review", "allow_once"),
|
|
24883
|
+
permissionOption(OptionId.AllowPermissionsForSession, "Yes, grant these permissions for this session", "allow_always"),
|
|
24884
|
+
permissionOption(OptionId.RejectPermissions, "No, continue without permissions", "reject_once")
|
|
24885
|
+
];
|
|
24886
|
+
}
|
|
24887
|
+
function decisionOption(optionId, name, kind, decision) {
|
|
24888
|
+
return { option: permissionOption(optionId, name, kind), decision };
|
|
24889
|
+
}
|
|
24890
|
+
function permissionOption(optionId, name, kind, description) {
|
|
24891
|
+
return { optionId, name, kind, ...description ? { _meta: { codex: { description } } } : {} };
|
|
24892
|
+
}
|
|
24893
|
+
function selectedDecision(response, decisions) {
|
|
24894
|
+
if (response.outcome.outcome !== "selected")
|
|
24895
|
+
return;
|
|
24896
|
+
const optionId = response.outcome.optionId;
|
|
24897
|
+
return decisions.find(({ option }) => option.optionId === optionId)?.decision;
|
|
25173
24898
|
}
|
|
25174
24899
|
|
|
25175
|
-
//
|
|
25176
|
-
|
|
25177
|
-
|
|
25178
|
-
|
|
25179
|
-
|
|
25180
|
-
|
|
25181
|
-
|
|
25182
|
-
|
|
25183
|
-
|
|
25184
|
-
|
|
25185
|
-
|
|
25186
|
-
|
|
25187
|
-
|
|
25188
|
-
|
|
25189
|
-
|
|
25190
|
-
|
|
25191
|
-
|
|
25192
|
-
|
|
25193
|
-
|
|
25194
|
-
|
|
25195
|
-
|
|
25196
|
-
|
|
25197
|
-
|
|
25198
|
-
|
|
25199
|
-
|
|
25200
|
-
|
|
25201
|
-
|
|
25202
|
-
|
|
25203
|
-
}
|
|
25204
|
-
return false;
|
|
25205
|
-
}
|
|
24900
|
+
// src/permissions/presentation.ts
|
|
24901
|
+
var COMMAND_TITLE = "Run command?";
|
|
24902
|
+
var NETWORK_TITLE = "Allow network access?";
|
|
24903
|
+
var FILE_CHANGE_TITLE = "Make edits?";
|
|
24904
|
+
var PERMISSIONS_TITLE = "Grant permissions?";
|
|
24905
|
+
function commandSubject(params) {
|
|
24906
|
+
const network = params.networkApprovalContext;
|
|
24907
|
+
const rawInput = {
|
|
24908
|
+
...params.command ? { command: stripShellPrefix(params.command) } : {},
|
|
24909
|
+
...params.cwd ? { cwd: params.cwd } : {},
|
|
24910
|
+
...network?.protocol === "http" || network?.protocol === "https" ? { url: `${network.protocol}://${network.host}` } : {},
|
|
24911
|
+
...params.additionalPermissions ? { additionalPermissions: params.additionalPermissions } : {}
|
|
24912
|
+
};
|
|
24913
|
+
const content = [];
|
|
24914
|
+
if (network)
|
|
24915
|
+
content.push(text(`${network.protocol} access to ${network.host}`));
|
|
24916
|
+
if (params.additionalPermissions)
|
|
24917
|
+
content.push(...profileContent(params.additionalPermissions));
|
|
24918
|
+
const locations = unique([...commandActionPaths(params.commandActions), ...profilePaths(params.additionalPermissions)]);
|
|
24919
|
+
return {
|
|
24920
|
+
toolCallId: params.itemId,
|
|
24921
|
+
name: ToolName.Shell,
|
|
24922
|
+
kind: "execute",
|
|
24923
|
+
status: "pending",
|
|
24924
|
+
title: network ? `${network.protocol} network access to ${network.host}` : commandTitle(params.command, params.commandActions),
|
|
24925
|
+
...Object.keys(rawInput).length > 0 ? { rawInput } : {},
|
|
24926
|
+
...locations.length > 0 ? { locations: locations.map((path5) => ({ path: path5 })) } : {},
|
|
24927
|
+
...content.length > 0 ? { content } : {}
|
|
24928
|
+
};
|
|
25206
24929
|
}
|
|
25207
|
-
function
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
|
|
25212
|
-
|
|
24930
|
+
function fileChangeSubject(params, item) {
|
|
24931
|
+
const paths = item ? changePaths(item.changes) : [];
|
|
24932
|
+
return {
|
|
24933
|
+
toolCallId: params.itemId,
|
|
24934
|
+
name: ToolName.ApplyPatch,
|
|
24935
|
+
kind: "edit",
|
|
24936
|
+
status: "pending",
|
|
24937
|
+
title: paths.length === 1 ? `Edit ${paths[0]}` : paths.length > 1 ? `Edit ${paths.length} files` : "Edit files",
|
|
24938
|
+
...paths.length > 0 ? { locations: paths.map((path5) => ({ path: path5 })) } : {},
|
|
24939
|
+
...item ? { content: item.changes.map(diffContent) } : {},
|
|
24940
|
+
...params.grantRoot ? { rawInput: { grantRoot: params.grantRoot } } : {}
|
|
24941
|
+
};
|
|
25213
24942
|
}
|
|
25214
|
-
function
|
|
25215
|
-
|
|
25216
|
-
|
|
25217
|
-
|
|
25218
|
-
|
|
24943
|
+
function permissionsSubject(itemId, cwd, environmentId, permissions) {
|
|
24944
|
+
const content = profileContent(permissions);
|
|
24945
|
+
const paths = profilePaths(permissions);
|
|
24946
|
+
return {
|
|
24947
|
+
toolCallId: itemId,
|
|
24948
|
+
name: ToolName.Shell,
|
|
24949
|
+
kind: "other",
|
|
24950
|
+
status: "pending",
|
|
24951
|
+
title: "Additional sandbox permissions",
|
|
24952
|
+
rawInput: { permissions, cwd, environmentId },
|
|
24953
|
+
...paths.length > 0 ? { locations: paths.map((path5) => ({ path: path5 })) } : {},
|
|
24954
|
+
...content.length > 0 ? { content } : {}
|
|
24955
|
+
};
|
|
24956
|
+
}
|
|
24957
|
+
function commandTitle(command, actions) {
|
|
24958
|
+
const first = actions?.[0];
|
|
24959
|
+
if (first?.type === "read")
|
|
24960
|
+
return actions?.length === 1 ? `Read ${first.path}` : "Run command with file reads";
|
|
24961
|
+
if (first?.type === "listFiles")
|
|
24962
|
+
return "List files";
|
|
24963
|
+
if (first?.type === "search")
|
|
24964
|
+
return "Search files";
|
|
24965
|
+
return command ? stripShellPrefix(command) : "Run command";
|
|
24966
|
+
}
|
|
24967
|
+
function profilePaths(permissions) {
|
|
24968
|
+
const fileSystem = permissions?.fileSystem;
|
|
24969
|
+
return unique([
|
|
24970
|
+
...fileSystem?.read ?? [],
|
|
24971
|
+
...fileSystem?.write ?? [],
|
|
24972
|
+
...(fileSystem?.entries ?? []).flatMap((entry) => entry.path.type === "path" ? [entry.path.path] : [])
|
|
24973
|
+
]);
|
|
25219
24974
|
}
|
|
25220
|
-
|
|
25221
|
-
|
|
25222
|
-
|
|
25223
|
-
|
|
25224
|
-
|
|
25225
|
-
fs3.statSync("/run/.containerenv");
|
|
25226
|
-
return true;
|
|
25227
|
-
} catch {
|
|
25228
|
-
return false;
|
|
24975
|
+
function profileContent(permissions) {
|
|
24976
|
+
const lines = [];
|
|
24977
|
+
const networkEnabled = permissions.network?.enabled;
|
|
24978
|
+
if (networkEnabled !== null && networkEnabled !== undefined) {
|
|
24979
|
+
lines.push(networkEnabled ? "Enable network access" : "Disable network access");
|
|
25229
24980
|
}
|
|
25230
|
-
|
|
25231
|
-
|
|
25232
|
-
|
|
25233
|
-
|
|
24981
|
+
for (const entry of permissions.fileSystem?.entries ?? []) {
|
|
24982
|
+
if (entry.path.type === "glob_pattern")
|
|
24983
|
+
lines.push(`${entry.access} filesystem pattern ${entry.path.pattern}`);
|
|
24984
|
+
else if (entry.path.type === "special")
|
|
24985
|
+
lines.push(`${entry.access} Codex filesystem scope ${JSON.stringify(entry.path.value)}`);
|
|
25234
24986
|
}
|
|
25235
|
-
return
|
|
24987
|
+
return lines.length > 0 ? [text(lines.join(`
|
|
24988
|
+
`))] : [];
|
|
25236
24989
|
}
|
|
25237
|
-
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
}
|
|
25243
|
-
if (os.release().toLowerCase().includes("microsoft")) {
|
|
25244
|
-
if (isInsideContainer()) {
|
|
25245
|
-
return false;
|
|
25246
|
-
}
|
|
25247
|
-
return true;
|
|
25248
|
-
}
|
|
25249
|
-
try {
|
|
25250
|
-
if (fs4.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
|
|
25251
|
-
return !isInsideContainer();
|
|
25252
|
-
}
|
|
25253
|
-
} catch {}
|
|
25254
|
-
if (fs4.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs4.existsSync("/run/WSL")) {
|
|
25255
|
-
return !isInsideContainer();
|
|
25256
|
-
}
|
|
25257
|
-
return false;
|
|
25258
|
-
};
|
|
25259
|
-
var is_wsl_default = process2.env.__IS_WSL_TEST__ ? isWsl : isWsl();
|
|
25260
|
-
|
|
25261
|
-
// node_modules/wsl-utils/node_modules/powershell-utils/index.js
|
|
25262
|
-
import process3 from "process";
|
|
25263
|
-
import { Buffer as Buffer2 } from "buffer";
|
|
25264
|
-
import { promisify } from "util";
|
|
25265
|
-
import childProcess from "child_process";
|
|
25266
|
-
var execFile = promisify(childProcess.execFile);
|
|
25267
|
-
var powerShellPath = () => `${process3.env.SYSTEMROOT || process3.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
25268
|
-
var executePowerShell = async (command, options = {}) => {
|
|
25269
|
-
const {
|
|
25270
|
-
powerShellPath: psPath,
|
|
25271
|
-
...execFileOptions
|
|
25272
|
-
} = options;
|
|
25273
|
-
const encodedCommand = executePowerShell.encodeCommand(command);
|
|
25274
|
-
return execFile(psPath ?? powerShellPath(), [
|
|
25275
|
-
...executePowerShell.argumentsPrefix,
|
|
25276
|
-
encodedCommand
|
|
25277
|
-
], {
|
|
25278
|
-
encoding: "utf8",
|
|
25279
|
-
...execFileOptions
|
|
25280
|
-
});
|
|
25281
|
-
};
|
|
25282
|
-
executePowerShell.argumentsPrefix = [
|
|
25283
|
-
"-NoProfile",
|
|
25284
|
-
"-NonInteractive",
|
|
25285
|
-
"-ExecutionPolicy",
|
|
25286
|
-
"Bypass",
|
|
25287
|
-
"-EncodedCommand"
|
|
25288
|
-
];
|
|
25289
|
-
executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
|
|
25290
|
-
executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
|
|
25291
|
-
|
|
25292
|
-
// node_modules/wsl-utils/utilities.js
|
|
25293
|
-
function parseMountPointFromConfig(content) {
|
|
25294
|
-
for (const line of content.split(`
|
|
25295
|
-
`)) {
|
|
25296
|
-
if (/^\s*#/.test(line)) {
|
|
25297
|
-
continue;
|
|
25298
|
-
}
|
|
25299
|
-
const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
|
|
25300
|
-
if (!match) {
|
|
25301
|
-
continue;
|
|
25302
|
-
}
|
|
25303
|
-
return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
|
|
25304
|
-
}
|
|
24990
|
+
function text(value) {
|
|
24991
|
+
return { type: "content", content: { type: "text", text: value } };
|
|
24992
|
+
}
|
|
24993
|
+
function unique(values) {
|
|
24994
|
+
return [...new Set(values)];
|
|
25305
24995
|
}
|
|
25306
24996
|
|
|
25307
|
-
//
|
|
25308
|
-
|
|
25309
|
-
|
|
25310
|
-
|
|
25311
|
-
|
|
25312
|
-
|
|
25313
|
-
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
|
|
25317
|
-
|
|
25318
|
-
|
|
25319
|
-
|
|
25320
|
-
|
|
25321
|
-
|
|
25322
|
-
if (!
|
|
25323
|
-
|
|
25324
|
-
|
|
25325
|
-
const configContent = await fs5.readFile(configFilePath, { encoding: "utf8" });
|
|
25326
|
-
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
25327
|
-
if (parsedMountPoint === undefined) {
|
|
25328
|
-
return defaultMountPoint;
|
|
24997
|
+
// src/permissions/ApprovalHandler.ts
|
|
24998
|
+
class CodexApprovalHandler {
|
|
24999
|
+
client;
|
|
25000
|
+
turn;
|
|
25001
|
+
signal;
|
|
25002
|
+
constructor(client, turn, signal = () => {
|
|
25003
|
+
return;
|
|
25004
|
+
}) {
|
|
25005
|
+
this.client = client;
|
|
25006
|
+
this.turn = turn;
|
|
25007
|
+
this.signal = signal;
|
|
25008
|
+
}
|
|
25009
|
+
async handleCommandExecution(params) {
|
|
25010
|
+
const approvalParams = params;
|
|
25011
|
+
const decisions = commandDecisionOptions(approvalParams);
|
|
25012
|
+
if (!decisions) {
|
|
25013
|
+
logger.log("cancelling command approval: no complete decision set", { itemId: params.itemId });
|
|
25014
|
+
return { decision: "cancel" };
|
|
25329
25015
|
}
|
|
25330
|
-
mountPoint = parsedMountPoint;
|
|
25331
|
-
mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
|
|
25332
|
-
return mountPoint;
|
|
25333
|
-
};
|
|
25334
|
-
})();
|
|
25335
|
-
var powerShellPathFromWsl = async () => {
|
|
25336
|
-
const mountPoint = await wslDrivesMountPoint();
|
|
25337
|
-
return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
|
|
25338
|
-
};
|
|
25339
|
-
var powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
|
|
25340
|
-
var canAccessPowerShellPromise;
|
|
25341
|
-
var canAccessPowerShell = async () => {
|
|
25342
|
-
canAccessPowerShellPromise ??= (async () => {
|
|
25343
25016
|
try {
|
|
25344
|
-
const
|
|
25345
|
-
|
|
25346
|
-
|
|
25347
|
-
|
|
25348
|
-
|
|
25349
|
-
|
|
25350
|
-
|
|
25351
|
-
|
|
25352
|
-
};
|
|
25353
|
-
|
|
25354
|
-
const psPath = await powerShellPath2();
|
|
25355
|
-
const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
|
|
25356
|
-
const { stdout } = await executePowerShell(command, {
|
|
25357
|
-
powerShellPath: psPath,
|
|
25358
|
-
cwd: path4.dirname(psPath)
|
|
25359
|
-
});
|
|
25360
|
-
return stdout.trim();
|
|
25361
|
-
};
|
|
25362
|
-
var isUrl = (path5) => /^[a-z]+:\/\//i.test(path5);
|
|
25363
|
-
var convertWslPathToWindows = async (paths) => {
|
|
25364
|
-
const isBatch = Array.isArray(paths);
|
|
25365
|
-
const pathArray = isBatch ? paths : [paths];
|
|
25366
|
-
const indicesToConvert = [];
|
|
25367
|
-
const pathsToConvert = [];
|
|
25368
|
-
for (const [index, path5] of pathArray.entries()) {
|
|
25369
|
-
if (!isUrl(path5)) {
|
|
25370
|
-
indicesToConvert.push(index);
|
|
25371
|
-
pathsToConvert.push(path5);
|
|
25017
|
+
const response = await this.client.requestPermission({
|
|
25018
|
+
title: params.networkApprovalContext ? NETWORK_TITLE : COMMAND_TITLE,
|
|
25019
|
+
description: nonBlank(params.reason),
|
|
25020
|
+
subject: { type: "tool_call", toolCall: commandSubject(approvalParams) },
|
|
25021
|
+
options: decisions.map(({ option }) => option)
|
|
25022
|
+
}, this.signal());
|
|
25023
|
+
return { decision: selectedDecision(response, decisions) ?? "cancel" };
|
|
25024
|
+
} catch (error40) {
|
|
25025
|
+
logger.error("command approval failed", error40, { itemId: params.itemId });
|
|
25026
|
+
return { decision: "cancel" };
|
|
25372
25027
|
}
|
|
25373
25028
|
}
|
|
25374
|
-
|
|
25375
|
-
|
|
25029
|
+
async handleFileChange(params) {
|
|
25030
|
+
const decisions = fileChangeDecisionOptions();
|
|
25376
25031
|
try {
|
|
25377
|
-
const
|
|
25378
|
-
|
|
25379
|
-
|
|
25380
|
-
|
|
25381
|
-
|
|
25382
|
-
|
|
25383
|
-
|
|
25384
|
-
|
|
25385
|
-
};
|
|
25386
|
-
|
|
25387
|
-
// node_modules/powershell-utils/index.js
|
|
25388
|
-
import process4 from "process";
|
|
25389
|
-
import { Buffer as Buffer3 } from "buffer";
|
|
25390
|
-
import { promisify as promisify3 } from "util";
|
|
25391
|
-
import childProcess3 from "child_process";
|
|
25392
|
-
var execFile3 = promisify3(childProcess3.execFile);
|
|
25393
|
-
var powerShellPath3 = () => `${process4.env.SYSTEMROOT || process4.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
25394
|
-
var argumentsPrefix = [
|
|
25395
|
-
"-NoProfile",
|
|
25396
|
-
"-NonInteractive",
|
|
25397
|
-
"-ExecutionPolicy",
|
|
25398
|
-
"Bypass",
|
|
25399
|
-
"-EncodedCommand"
|
|
25400
|
-
];
|
|
25401
|
-
var encodeCommand = (command) => Buffer3.from(command, "utf16le").toString("base64");
|
|
25402
|
-
var escapeArgument = (value) => `'${String(value).replaceAll(/['\u2018\u2019\u201A\u201B]/g, (match) => match + match)}'`;
|
|
25403
|
-
var createArguments = (command) => [...argumentsPrefix, encodeCommand(command)];
|
|
25404
|
-
var createExecFileOptions = (options) => ({
|
|
25405
|
-
encoding: "utf8",
|
|
25406
|
-
...options,
|
|
25407
|
-
shell: false
|
|
25408
|
-
});
|
|
25409
|
-
var executePowerShell2 = async (command, options = {}) => {
|
|
25410
|
-
const {
|
|
25411
|
-
powerShellPath: psPath,
|
|
25412
|
-
...execFileOptions
|
|
25413
|
-
} = options;
|
|
25414
|
-
return execFile3(psPath ?? powerShellPath3(), createArguments(command), createExecFileOptions(execFileOptions));
|
|
25415
|
-
};
|
|
25416
|
-
executePowerShell2.argumentsPrefix = argumentsPrefix;
|
|
25417
|
-
executePowerShell2.encodeCommand = encodeCommand;
|
|
25418
|
-
executePowerShell2.escapeArgument = escapeArgument;
|
|
25419
|
-
executePowerShell2.createArguments = createArguments;
|
|
25420
|
-
|
|
25421
|
-
// node_modules/define-lazy-prop/index.js
|
|
25422
|
-
function defineLazyProperty(object2, propertyName, valueGetter) {
|
|
25423
|
-
const define = (value) => Object.defineProperty(object2, propertyName, { value, enumerable: true, writable: true });
|
|
25424
|
-
Object.defineProperty(object2, propertyName, {
|
|
25425
|
-
configurable: true,
|
|
25426
|
-
enumerable: true,
|
|
25427
|
-
get() {
|
|
25428
|
-
const result = valueGetter();
|
|
25429
|
-
define(result);
|
|
25430
|
-
return result;
|
|
25431
|
-
},
|
|
25432
|
-
set(value) {
|
|
25433
|
-
define(value);
|
|
25032
|
+
const response = await this.client.requestPermission({
|
|
25033
|
+
title: FILE_CHANGE_TITLE,
|
|
25034
|
+
description: nonBlank(params.reason),
|
|
25035
|
+
subject: { type: "tool_call", toolCall: fileChangeSubject(params, this.turn.fileChange(params.itemId)) },
|
|
25036
|
+
options: decisions.map(({ option }) => option)
|
|
25037
|
+
}, this.signal());
|
|
25038
|
+
return { decision: selectedDecision(response, decisions) ?? "cancel" };
|
|
25039
|
+
} catch (error40) {
|
|
25040
|
+
logger.error("file change approval failed", error40, { itemId: params.itemId });
|
|
25041
|
+
return { decision: "cancel" };
|
|
25434
25042
|
}
|
|
25435
|
-
});
|
|
25436
|
-
return object2;
|
|
25437
|
-
}
|
|
25438
|
-
|
|
25439
|
-
// node_modules/default-browser/index.js
|
|
25440
|
-
import { promisify as promisify7 } from "util";
|
|
25441
|
-
import process8 from "process";
|
|
25442
|
-
import { execFile as execFile7 } from "child_process";
|
|
25443
|
-
|
|
25444
|
-
// node_modules/default-browser-id/index.js
|
|
25445
|
-
import { promisify as promisify4 } from "util";
|
|
25446
|
-
import process5 from "process";
|
|
25447
|
-
import { execFile as execFile4 } from "child_process";
|
|
25448
|
-
var execFileAsync = promisify4(execFile4);
|
|
25449
|
-
async function defaultBrowserId() {
|
|
25450
|
-
if (process5.platform !== "darwin") {
|
|
25451
|
-
throw new Error("macOS only");
|
|
25452
25043
|
}
|
|
25453
|
-
|
|
25454
|
-
|
|
25455
|
-
|
|
25456
|
-
|
|
25457
|
-
|
|
25044
|
+
async handlePermissionsRequest(params) {
|
|
25045
|
+
try {
|
|
25046
|
+
const response = await this.client.requestPermission({
|
|
25047
|
+
title: PERMISSIONS_TITLE,
|
|
25048
|
+
description: nonBlank(params.reason),
|
|
25049
|
+
subject: { type: "tool_call", toolCall: permissionsSubject(params.itemId, params.cwd, params.environmentId, params.permissions) },
|
|
25050
|
+
options: permissionProfileOptions()
|
|
25051
|
+
}, this.signal());
|
|
25052
|
+
return permissionsResponse(params.permissions, response);
|
|
25053
|
+
} catch (error40) {
|
|
25054
|
+
logger.error("permissions approval failed", error40, { itemId: params.itemId });
|
|
25055
|
+
return rejected();
|
|
25056
|
+
}
|
|
25458
25057
|
}
|
|
25459
|
-
return browserId;
|
|
25460
25058
|
}
|
|
25461
|
-
|
|
25462
|
-
|
|
25463
|
-
|
|
25464
|
-
|
|
25465
|
-
|
|
25466
|
-
|
|
25467
|
-
|
|
25468
|
-
|
|
25469
|
-
|
|
25470
|
-
|
|
25471
|
-
|
|
25472
|
-
|
|
25473
|
-
if (signal) {
|
|
25474
|
-
execOptions.signal = signal;
|
|
25059
|
+
function permissionsResponse(permissions, response) {
|
|
25060
|
+
if (response.outcome.outcome !== "selected")
|
|
25061
|
+
return rejected();
|
|
25062
|
+
switch (response.outcome.optionId) {
|
|
25063
|
+
case OptionId.AllowPermissionsForTurn:
|
|
25064
|
+
return { permissions: granted(permissions), scope: "turn", strictAutoReview: false };
|
|
25065
|
+
case OptionId.AllowPermissionsForTurnStrict:
|
|
25066
|
+
return { permissions: granted(permissions), scope: "turn", strictAutoReview: true };
|
|
25067
|
+
case OptionId.AllowPermissionsForSession:
|
|
25068
|
+
return { permissions: granted(permissions), scope: "session", strictAutoReview: false };
|
|
25069
|
+
default:
|
|
25070
|
+
return rejected();
|
|
25475
25071
|
}
|
|
25476
|
-
const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
|
|
25477
|
-
return stdout.trim();
|
|
25478
25072
|
}
|
|
25479
|
-
|
|
25480
|
-
|
|
25481
|
-
|
|
25482
|
-
|
|
25483
|
-
|
|
25073
|
+
function granted(permissions) {
|
|
25074
|
+
return {
|
|
25075
|
+
...permissions.network ? { network: permissions.network } : {},
|
|
25076
|
+
...permissions.fileSystem ? { fileSystem: permissions.fileSystem } : {}
|
|
25077
|
+
};
|
|
25078
|
+
}
|
|
25079
|
+
function rejected() {
|
|
25080
|
+
return { permissions: {}, scope: "turn", strictAutoReview: false };
|
|
25081
|
+
}
|
|
25082
|
+
function nonBlank(value) {
|
|
25083
|
+
const trimmed = value?.trim();
|
|
25084
|
+
return trimmed ? trimmed : null;
|
|
25484
25085
|
}
|
|
25485
25086
|
|
|
25486
|
-
//
|
|
25487
|
-
|
|
25488
|
-
|
|
25489
|
-
|
|
25490
|
-
|
|
25491
|
-
|
|
25492
|
-
|
|
25493
|
-
|
|
25494
|
-
MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
|
|
25495
|
-
AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
|
|
25496
|
-
ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
|
|
25497
|
-
ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
|
|
25498
|
-
ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
|
|
25499
|
-
ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
|
|
25500
|
-
BraveHTML: { name: "Brave", id: "com.brave.Browser" },
|
|
25501
|
-
BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
|
|
25502
|
-
BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
|
|
25503
|
-
BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
|
|
25504
|
-
FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
|
|
25505
|
-
OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
|
|
25506
|
-
VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
|
|
25507
|
-
"IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
|
|
25087
|
+
// src/permissions/mcp.ts
|
|
25088
|
+
var McpOptionId = {
|
|
25089
|
+
AllowOnce: "allow_once",
|
|
25090
|
+
Accept: "accept",
|
|
25091
|
+
AllowSession: "allow_session",
|
|
25092
|
+
AllowAlways: "allow_always",
|
|
25093
|
+
Decline: "decline",
|
|
25094
|
+
Cancel: "cancel"
|
|
25508
25095
|
};
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25096
|
+
function parsePersistOptions(meta) {
|
|
25097
|
+
const result = new Set;
|
|
25098
|
+
if (!isRecord2(meta))
|
|
25099
|
+
return result;
|
|
25100
|
+
const persist = meta["persist"];
|
|
25101
|
+
const values = Array.isArray(persist) ? persist : [persist];
|
|
25102
|
+
if (values.includes("session"))
|
|
25103
|
+
result.add("session");
|
|
25104
|
+
if (values.includes("always"))
|
|
25105
|
+
result.add("always");
|
|
25106
|
+
return result;
|
|
25512
25107
|
}
|
|
25513
|
-
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
|
|
25517
|
-
|
|
25518
|
-
|
|
25519
|
-
"
|
|
25520
|
-
]);
|
|
25521
|
-
const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
|
|
25522
|
-
if (!match) {
|
|
25523
|
-
throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
|
|
25108
|
+
function isMcpToolCallApproval(meta) {
|
|
25109
|
+
return isRecord2(meta) && meta["codex_approval_kind"] === "mcp_tool_call";
|
|
25110
|
+
}
|
|
25111
|
+
function mcpPermissionOptions(isToolApproval, persist) {
|
|
25112
|
+
const options = [permissionOption(isToolApproval ? McpOptionId.AllowOnce : McpOptionId.Accept, "Allow", "allow_once", isToolApproval ? "Run the tool and continue." : "Allow this request and continue.")];
|
|
25113
|
+
if (persist.has("session")) {
|
|
25114
|
+
options.push(permissionOption(McpOptionId.AllowSession, "Allow for this session", "allow_always", isToolApproval ? "Run the tool and remember this choice for this session." : "Allow this request and remember this choice for this session."));
|
|
25524
25115
|
}
|
|
25525
|
-
|
|
25526
|
-
|
|
25527
|
-
|
|
25528
|
-
|
|
25529
|
-
|
|
25530
|
-
|
|
25116
|
+
if (persist.has("always")) {
|
|
25117
|
+
options.push(permissionOption(McpOptionId.AllowAlways, "Always allow", "allow_always", isToolApproval ? "Run the tool and remember this choice for future tool calls." : "Allow this request and remember this choice for future requests."));
|
|
25118
|
+
}
|
|
25119
|
+
if (!isToolApproval) {
|
|
25120
|
+
options.push(permissionOption(McpOptionId.Decline, "Deny", "reject_once", "Decline this request and continue."));
|
|
25121
|
+
}
|
|
25122
|
+
options.push(permissionOption(McpOptionId.Cancel, "Cancel", "reject_once", isToolApproval ? "Cancel this tool call." : "Cancel this request."));
|
|
25123
|
+
return options;
|
|
25531
25124
|
}
|
|
25532
|
-
|
|
25533
|
-
|
|
25534
|
-
|
|
25535
|
-
|
|
25536
|
-
|
|
25537
|
-
|
|
25538
|
-
|
|
25539
|
-
|
|
25540
|
-
|
|
25125
|
+
function mcpPermissionRequest(params, context, nextStandaloneToolCallId) {
|
|
25126
|
+
const message = { type: "content", content: { type: "text", text: params.message } };
|
|
25127
|
+
const options = mcpPermissionOptions(context.isToolApproval, context.persistOptions);
|
|
25128
|
+
const title = context.isToolApproval ? `Allow MCP tool call on ${params.serverName}?` : `${params.serverName} requests approval`;
|
|
25129
|
+
if (params.mode === "url") {
|
|
25130
|
+
return {
|
|
25131
|
+
title,
|
|
25132
|
+
description: params.message,
|
|
25133
|
+
subject: {
|
|
25134
|
+
type: "tool_call",
|
|
25135
|
+
toolCall: {
|
|
25136
|
+
toolCallId: `elicitation:${params.elicitationId}`,
|
|
25137
|
+
name: ToolName.Mcp,
|
|
25138
|
+
kind: "fetch",
|
|
25139
|
+
status: "pending",
|
|
25140
|
+
title: `Open ${params.url}`,
|
|
25141
|
+
content: [message],
|
|
25142
|
+
rawInput: { serverName: params.serverName, url: params.url }
|
|
25143
|
+
}
|
|
25144
|
+
},
|
|
25145
|
+
options
|
|
25146
|
+
};
|
|
25541
25147
|
}
|
|
25542
|
-
if (
|
|
25543
|
-
|
|
25544
|
-
|
|
25545
|
-
|
|
25546
|
-
|
|
25148
|
+
if (context.correlatedCallId !== undefined) {
|
|
25149
|
+
return {
|
|
25150
|
+
title,
|
|
25151
|
+
description: params.message,
|
|
25152
|
+
subject: { type: "tool_call", toolCall: { toolCallId: context.correlatedCallId, status: "pending" } },
|
|
25153
|
+
options
|
|
25154
|
+
};
|
|
25547
25155
|
}
|
|
25548
|
-
|
|
25549
|
-
|
|
25156
|
+
return {
|
|
25157
|
+
title,
|
|
25158
|
+
description: params.message,
|
|
25159
|
+
subject: {
|
|
25160
|
+
type: "tool_call",
|
|
25161
|
+
toolCall: {
|
|
25162
|
+
toolCallId: nextStandaloneToolCallId(),
|
|
25163
|
+
name: ToolName.Mcp,
|
|
25164
|
+
kind: context.isToolApproval ? "execute" : "other",
|
|
25165
|
+
status: "pending",
|
|
25166
|
+
title,
|
|
25167
|
+
content: [message],
|
|
25168
|
+
rawInput: { serverName: params.serverName, schema: params.requestedSchema }
|
|
25169
|
+
}
|
|
25170
|
+
},
|
|
25171
|
+
options
|
|
25172
|
+
};
|
|
25173
|
+
}
|
|
25174
|
+
function mcpPermissionResponse(response, isToolApproval, persist) {
|
|
25175
|
+
if (response.outcome.outcome !== "selected")
|
|
25176
|
+
return cancelled();
|
|
25177
|
+
switch (response.outcome.optionId) {
|
|
25178
|
+
case McpOptionId.AllowSession:
|
|
25179
|
+
return persist.has("session") ? accepted({ persist: "session" }) : cancelled();
|
|
25180
|
+
case McpOptionId.AllowAlways:
|
|
25181
|
+
return persist.has("always") ? accepted({ persist: "always" }) : cancelled();
|
|
25182
|
+
case McpOptionId.AllowOnce:
|
|
25183
|
+
return isToolApproval ? accepted(null) : cancelled();
|
|
25184
|
+
case McpOptionId.Accept:
|
|
25185
|
+
return isToolApproval ? cancelled() : accepted(null);
|
|
25186
|
+
case McpOptionId.Decline:
|
|
25187
|
+
return isToolApproval ? cancelled() : { action: "decline", content: null, _meta: null };
|
|
25188
|
+
default:
|
|
25189
|
+
return cancelled();
|
|
25550
25190
|
}
|
|
25551
|
-
|
|
25191
|
+
}
|
|
25192
|
+
function accepted(meta) {
|
|
25193
|
+
return { action: "accept", content: null, _meta: meta };
|
|
25194
|
+
}
|
|
25195
|
+
function cancelled() {
|
|
25196
|
+
return { action: "cancel", content: null, _meta: null };
|
|
25552
25197
|
}
|
|
25553
25198
|
|
|
25554
|
-
//
|
|
25555
|
-
|
|
25556
|
-
var isInSsh = Boolean(process9.env.SSH_CONNECTION || process9.env.SSH_CLIENT || process9.env.SSH_TTY);
|
|
25557
|
-
var is_in_ssh_default = isInSsh;
|
|
25199
|
+
// src/permissions/ElicitationHandler.ts
|
|
25200
|
+
var OTHER_SUFFIX = "__other";
|
|
25558
25201
|
|
|
25559
|
-
|
|
25560
|
-
|
|
25561
|
-
|
|
25562
|
-
|
|
25563
|
-
|
|
25564
|
-
|
|
25565
|
-
if (apps.length === 0) {
|
|
25202
|
+
class CodexElicitationHandler {
|
|
25203
|
+
client;
|
|
25204
|
+
turn;
|
|
25205
|
+
signal;
|
|
25206
|
+
pendingUrlElicitations = new Set;
|
|
25207
|
+
constructor(client, turn, signal = () => {
|
|
25566
25208
|
return;
|
|
25209
|
+
}) {
|
|
25210
|
+
this.client = client;
|
|
25211
|
+
this.turn = turn;
|
|
25212
|
+
this.signal = signal;
|
|
25567
25213
|
}
|
|
25568
|
-
|
|
25569
|
-
|
|
25570
|
-
|
|
25571
|
-
|
|
25572
|
-
|
|
25573
|
-
|
|
25214
|
+
async observe(notification) {
|
|
25215
|
+
if (notification.method !== "serverRequest/resolved")
|
|
25216
|
+
return;
|
|
25217
|
+
const ids = [...this.pendingUrlElicitations];
|
|
25218
|
+
this.pendingUrlElicitations.clear();
|
|
25219
|
+
for (const elicitationId of ids) {
|
|
25220
|
+
await this.client.completeElicitation(elicitationId);
|
|
25574
25221
|
}
|
|
25575
25222
|
}
|
|
25576
|
-
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
|
|
25581
|
-
background: false,
|
|
25582
|
-
newInstance: false,
|
|
25583
|
-
allowNonzeroExitCode: false,
|
|
25584
|
-
...options
|
|
25585
|
-
};
|
|
25586
|
-
const isFallbackAttempt = options[fallbackAttemptSymbol] === true;
|
|
25587
|
-
delete options[fallbackAttemptSymbol];
|
|
25588
|
-
if (Array.isArray(options.app)) {
|
|
25589
|
-
return tryEachApp(options.app, (singleApp) => baseOpen({
|
|
25590
|
-
...options,
|
|
25591
|
-
app: singleApp,
|
|
25592
|
-
[fallbackAttemptSymbol]: true
|
|
25593
|
-
}));
|
|
25594
|
-
}
|
|
25595
|
-
let { name: app, arguments: appArguments = [] } = options.app ?? {};
|
|
25596
|
-
appArguments = [...appArguments];
|
|
25597
|
-
if (Array.isArray(app)) {
|
|
25598
|
-
return tryEachApp(app, (appName) => baseOpen({
|
|
25599
|
-
...options,
|
|
25600
|
-
app: {
|
|
25601
|
-
name: appName,
|
|
25602
|
-
arguments: appArguments
|
|
25603
|
-
},
|
|
25604
|
-
[fallbackAttemptSymbol]: true
|
|
25605
|
-
}));
|
|
25606
|
-
}
|
|
25607
|
-
if (app === "browser" || app === "browserPrivate") {
|
|
25608
|
-
const ids = {
|
|
25609
|
-
"com.google.chrome": "chrome",
|
|
25610
|
-
"google-chrome.desktop": "chrome",
|
|
25611
|
-
"com.brave.browser": "brave",
|
|
25612
|
-
"org.mozilla.firefox": "firefox",
|
|
25613
|
-
"firefox.desktop": "firefox",
|
|
25614
|
-
"com.microsoft.msedge": "edge",
|
|
25615
|
-
"com.microsoft.edge": "edge",
|
|
25616
|
-
"com.microsoft.edgemac": "edge",
|
|
25617
|
-
"microsoft-edge.desktop": "edge",
|
|
25618
|
-
"com.apple.safari": "safari"
|
|
25619
|
-
};
|
|
25620
|
-
const flags = {
|
|
25621
|
-
chrome: "--incognito",
|
|
25622
|
-
brave: "--incognito",
|
|
25623
|
-
firefox: "--private-window",
|
|
25624
|
-
edge: "--inPrivate"
|
|
25625
|
-
};
|
|
25626
|
-
let browser;
|
|
25627
|
-
if (is_wsl_default) {
|
|
25628
|
-
const progId = await wslDefaultBrowser();
|
|
25629
|
-
const browserInfo = _windowsBrowserProgIdMap.get(progId);
|
|
25630
|
-
browser = browserInfo ?? {};
|
|
25631
|
-
} else {
|
|
25632
|
-
browser = await defaultBrowser2();
|
|
25633
|
-
}
|
|
25634
|
-
if (Object.hasOwn(ids, browser.id)) {
|
|
25635
|
-
const browserName = ids[browser.id.toLowerCase()];
|
|
25636
|
-
if (browserName === undefined) {
|
|
25637
|
-
throw new Error(`${browser.name} is not supported as a default browser`);
|
|
25223
|
+
async handleElicitation(params) {
|
|
25224
|
+
try {
|
|
25225
|
+
if (params.mode === "openai/userVerification") {
|
|
25226
|
+
logger.log("MCP user verification is not supported over ACP", { server: params.serverName });
|
|
25227
|
+
return cancelled();
|
|
25638
25228
|
}
|
|
25639
|
-
|
|
25640
|
-
|
|
25641
|
-
|
|
25642
|
-
|
|
25643
|
-
|
|
25229
|
+
const context = this.mcpContext(params);
|
|
25230
|
+
const messageOnly = isMessageOnlyForm(params);
|
|
25231
|
+
if (!messageOnly && this.useAcpElicitation(params)) {
|
|
25232
|
+
const response2 = await this.client.createElicitation(this.elicitationRequest(params, context), this.signal());
|
|
25233
|
+
const result2 = convertMcpElicitationResponse(response2, context);
|
|
25234
|
+
if (params.mode === "url" && result2.action === "accept")
|
|
25235
|
+
this.pendingUrlElicitations.add(params.elicitationId);
|
|
25236
|
+
if (result2.action === "accept")
|
|
25237
|
+
await this.markCorrelatedCallRunning(context);
|
|
25238
|
+
return result2;
|
|
25644
25239
|
}
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
app: {
|
|
25648
|
-
name: apps[browserName],
|
|
25649
|
-
arguments: appArguments
|
|
25650
|
-
}
|
|
25651
|
-
});
|
|
25652
|
-
}
|
|
25653
|
-
throw new Error(`${browser.name} is not supported as a default browser`);
|
|
25654
|
-
}
|
|
25655
|
-
let command;
|
|
25656
|
-
const cliArguments = [];
|
|
25657
|
-
const childProcessOptions = {};
|
|
25658
|
-
let shouldUseWindowsInWsl = false;
|
|
25659
|
-
if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
|
|
25660
|
-
shouldUseWindowsInWsl = await canAccessPowerShell();
|
|
25661
|
-
}
|
|
25662
|
-
if (platform === "darwin") {
|
|
25663
|
-
command = "open";
|
|
25664
|
-
if (options.wait) {
|
|
25665
|
-
cliArguments.push("--wait-apps");
|
|
25666
|
-
}
|
|
25667
|
-
if (options.background) {
|
|
25668
|
-
cliArguments.push("--background");
|
|
25669
|
-
}
|
|
25670
|
-
if (options.newInstance) {
|
|
25671
|
-
cliArguments.push("--new");
|
|
25672
|
-
}
|
|
25673
|
-
if (app) {
|
|
25674
|
-
cliArguments.push("-a", app);
|
|
25675
|
-
}
|
|
25676
|
-
} else if (platform === "win32" || shouldUseWindowsInWsl) {
|
|
25677
|
-
command = await powerShellPath2();
|
|
25678
|
-
cliArguments.push(...executePowerShell2.argumentsPrefix);
|
|
25679
|
-
if (!is_wsl_default) {
|
|
25680
|
-
childProcessOptions.windowsVerbatimArguments = true;
|
|
25681
|
-
}
|
|
25682
|
-
if (is_wsl_default && options.target) {
|
|
25683
|
-
options.target = await convertWslPathToWindows(options.target);
|
|
25684
|
-
}
|
|
25685
|
-
const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
|
|
25686
|
-
if (options.wait) {
|
|
25687
|
-
encodedArguments.push("-Wait");
|
|
25688
|
-
}
|
|
25689
|
-
if (app) {
|
|
25690
|
-
encodedArguments.push(executePowerShell2.escapeArgument(app));
|
|
25691
|
-
if (options.target) {
|
|
25692
|
-
appArguments.push(options.target);
|
|
25240
|
+
if (params.mode !== "url" && !messageOnly) {
|
|
25241
|
+
return cancelled();
|
|
25693
25242
|
}
|
|
25694
|
-
|
|
25695
|
-
|
|
25696
|
-
|
|
25697
|
-
|
|
25698
|
-
|
|
25699
|
-
|
|
25700
|
-
}
|
|
25701
|
-
|
|
25702
|
-
|
|
25703
|
-
childProcessOptions.stdio = "ignore";
|
|
25704
|
-
}
|
|
25705
|
-
if (is_wsl_default) {
|
|
25706
|
-
childProcessOptions.cwd = path5.dirname(command);
|
|
25707
|
-
}
|
|
25708
|
-
} else {
|
|
25709
|
-
if (app) {
|
|
25710
|
-
command = app;
|
|
25711
|
-
} else {
|
|
25712
|
-
const isBundled = !__dirname2 || __dirname2 === "/";
|
|
25713
|
-
let exeLocalXdgOpen = false;
|
|
25714
|
-
try {
|
|
25715
|
-
await fs6.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
25716
|
-
exeLocalXdgOpen = true;
|
|
25717
|
-
} catch {}
|
|
25718
|
-
const useSystemXdgOpen = process10.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
|
|
25719
|
-
command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
|
|
25720
|
-
}
|
|
25721
|
-
if (appArguments.length > 0) {
|
|
25722
|
-
cliArguments.push(...appArguments);
|
|
25723
|
-
}
|
|
25724
|
-
if (!options.wait) {
|
|
25725
|
-
childProcessOptions.stdio = "ignore";
|
|
25726
|
-
childProcessOptions.detached = true;
|
|
25243
|
+
const request = mcpPermissionRequest(params, context, () => this.turn.nextStandaloneToolCallId(params.serverName));
|
|
25244
|
+
const response = await this.client.requestPermission(request, this.signal());
|
|
25245
|
+
const result = mcpPermissionResponse(response, context.isToolApproval, context.persistOptions);
|
|
25246
|
+
if (result.action === "accept")
|
|
25247
|
+
await this.markCorrelatedCallRunning(context);
|
|
25248
|
+
return result;
|
|
25249
|
+
} catch (error40) {
|
|
25250
|
+
logger.error("MCP elicitation failed", error40, { server: params.serverName });
|
|
25251
|
+
return cancelled();
|
|
25727
25252
|
}
|
|
25728
25253
|
}
|
|
25729
|
-
|
|
25730
|
-
|
|
25731
|
-
|
|
25732
|
-
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
|
|
25739
|
-
subprocess.once("close", (exitCode) => {
|
|
25740
|
-
if (!options.allowNonzeroExitCode && exitCode !== 0) {
|
|
25741
|
-
reject(new Error(`Exited with code ${exitCode}`));
|
|
25742
|
-
return;
|
|
25743
|
-
}
|
|
25744
|
-
resolve(subprocess);
|
|
25745
|
-
});
|
|
25746
|
-
});
|
|
25747
|
-
}
|
|
25748
|
-
if (isFallbackAttempt) {
|
|
25749
|
-
return new Promise((resolve, reject) => {
|
|
25750
|
-
subprocess.once("error", reject);
|
|
25751
|
-
subprocess.once("spawn", () => {
|
|
25752
|
-
subprocess.once("close", (exitCode) => {
|
|
25753
|
-
subprocess.off("error", reject);
|
|
25754
|
-
if (exitCode !== 0) {
|
|
25755
|
-
reject(new Error(`Exited with code ${exitCode}`));
|
|
25756
|
-
return;
|
|
25757
|
-
}
|
|
25758
|
-
subprocess.unref();
|
|
25759
|
-
resolve(subprocess);
|
|
25760
|
-
});
|
|
25761
|
-
});
|
|
25762
|
-
});
|
|
25763
|
-
}
|
|
25764
|
-
subprocess.unref();
|
|
25765
|
-
return new Promise((resolve, reject) => {
|
|
25766
|
-
subprocess.once("error", reject);
|
|
25767
|
-
subprocess.once("spawn", () => {
|
|
25768
|
-
subprocess.off("error", reject);
|
|
25769
|
-
resolve(subprocess);
|
|
25770
|
-
});
|
|
25771
|
-
});
|
|
25772
|
-
};
|
|
25773
|
-
var open = (target, options) => {
|
|
25774
|
-
if (typeof target !== "string") {
|
|
25775
|
-
throw new TypeError("Expected a `target`");
|
|
25776
|
-
}
|
|
25777
|
-
return baseOpen({
|
|
25778
|
-
...options,
|
|
25779
|
-
target
|
|
25780
|
-
});
|
|
25781
|
-
};
|
|
25782
|
-
function detectArchBinary(binary) {
|
|
25783
|
-
if (typeof binary === "string" || Array.isArray(binary)) {
|
|
25784
|
-
return binary;
|
|
25254
|
+
async handleUserInput(params) {
|
|
25255
|
+
if (!this.client.capabilities.formElicitation)
|
|
25256
|
+
return { answers: {} };
|
|
25257
|
+
try {
|
|
25258
|
+
const response = await this.client.createElicitation(userInputRequest(this.client.sessionId, params), this.signal());
|
|
25259
|
+
return convertUserInputResponse(response, params);
|
|
25260
|
+
} catch (error40) {
|
|
25261
|
+
logger.error("user input request failed", error40, { itemId: params.itemId });
|
|
25262
|
+
return { answers: {} };
|
|
25263
|
+
}
|
|
25785
25264
|
}
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
|
|
25265
|
+
mcpContext(params) {
|
|
25266
|
+
const isToolApproval = isMcpToolCallApproval(params._meta) && isMessageOnlyForm(params);
|
|
25267
|
+
return {
|
|
25268
|
+
isToolApproval,
|
|
25269
|
+
persistOptions: parsePersistOptions(params._meta),
|
|
25270
|
+
correlatedCallId: isToolApproval ? this.turn.popPendingMcpCall(params.serverName) : undefined
|
|
25271
|
+
};
|
|
25789
25272
|
}
|
|
25790
|
-
|
|
25791
|
-
|
|
25792
|
-
|
|
25793
|
-
|
|
25794
|
-
|
|
25273
|
+
useAcpElicitation(params) {
|
|
25274
|
+
if (params.mode === "form")
|
|
25275
|
+
return this.client.capabilities.formElicitation;
|
|
25276
|
+
if (params.mode === "url")
|
|
25277
|
+
return this.client.capabilities.urlElicitation;
|
|
25278
|
+
return false;
|
|
25795
25279
|
}
|
|
25796
|
-
|
|
25797
|
-
|
|
25280
|
+
elicitationRequest(params, context) {
|
|
25281
|
+
const base = {
|
|
25282
|
+
sessionId: this.client.sessionId,
|
|
25283
|
+
...context.correlatedCallId ? { toolCallId: context.correlatedCallId } : {},
|
|
25284
|
+
message: params.message,
|
|
25285
|
+
_meta: recordOrNull(params._meta)
|
|
25286
|
+
};
|
|
25287
|
+
if (params.mode === "form") {
|
|
25288
|
+
return { ...base, mode: "form", requestedSchema: normalizeSchema(params.requestedSchema) };
|
|
25289
|
+
}
|
|
25290
|
+
return { ...base, mode: "url", url: params.url, elicitationId: params.elicitationId };
|
|
25291
|
+
}
|
|
25292
|
+
async markCorrelatedCallRunning(context) {
|
|
25293
|
+
if (context.correlatedCallId === undefined)
|
|
25294
|
+
return;
|
|
25295
|
+
await this.client.update({ sessionUpdate: "tool_call_update", toolCallId: context.correlatedCallId, status: "in_progress" });
|
|
25798
25296
|
}
|
|
25799
|
-
return detectArchBinary(platformBinary);
|
|
25800
25297
|
}
|
|
25801
|
-
|
|
25802
|
-
|
|
25803
|
-
|
|
25804
|
-
|
|
25805
|
-
|
|
25806
|
-
|
|
25807
|
-
|
|
25808
|
-
|
|
25809
|
-
|
|
25810
|
-
|
|
25811
|
-
|
|
25812
|
-
|
|
25298
|
+
function isMessageOnlyForm(params) {
|
|
25299
|
+
if (params.mode !== "form" && params.mode !== "openai/form" && params.mode !== "openaiForm")
|
|
25300
|
+
return false;
|
|
25301
|
+
const schema = params.requestedSchema;
|
|
25302
|
+
if (schema === null)
|
|
25303
|
+
return true;
|
|
25304
|
+
if (!isRecord2(schema))
|
|
25305
|
+
return false;
|
|
25306
|
+
return schema["type"] === "object" && isRecord2(schema["properties"]) && Object.keys(schema["properties"]).length === 0;
|
|
25307
|
+
}
|
|
25308
|
+
function normalizeSchema(value) {
|
|
25309
|
+
const normalized = normalizeSchemaValue(value);
|
|
25310
|
+
if (!isRecord2(normalized))
|
|
25311
|
+
return { type: "object", properties: {} };
|
|
25312
|
+
return { ...normalized, type: "object" };
|
|
25313
|
+
}
|
|
25314
|
+
function normalizeSchemaValue(value) {
|
|
25315
|
+
if (typeof value === "bigint")
|
|
25316
|
+
return Number(value);
|
|
25317
|
+
if (Array.isArray(value))
|
|
25318
|
+
return value.map(normalizeSchemaValue);
|
|
25319
|
+
if (!isRecord2(value))
|
|
25320
|
+
return value;
|
|
25321
|
+
const result = Object.fromEntries(Object.entries(value).filter(([, nested]) => nested !== undefined).map(([key, nested]) => [key, normalizeSchemaValue(nested)]));
|
|
25322
|
+
if (result["type"] === "string" && Array.isArray(result["enum"]) && Array.isArray(result["enumNames"]) && !Array.isArray(result["oneOf"])) {
|
|
25323
|
+
const values = result["enum"];
|
|
25324
|
+
const names = result["enumNames"];
|
|
25325
|
+
result["oneOf"] = values.map((entry, index) => ({ const: String(entry), title: String(names[index] ?? entry) }));
|
|
25326
|
+
delete result["enum"];
|
|
25327
|
+
delete result["enumNames"];
|
|
25813
25328
|
}
|
|
25814
|
-
|
|
25815
|
-
|
|
25816
|
-
|
|
25817
|
-
|
|
25818
|
-
|
|
25819
|
-
|
|
25820
|
-
|
|
25821
|
-
|
|
25822
|
-
|
|
25329
|
+
return result;
|
|
25330
|
+
}
|
|
25331
|
+
function convertMcpElicitationResponse(response, context) {
|
|
25332
|
+
if (CreateElicitationResponse.isAccept(response)) {
|
|
25333
|
+
const content = isRecord2(response.content) ? { ...response.content } : {};
|
|
25334
|
+
const persist = context.isToolApproval ? content["persist"] : undefined;
|
|
25335
|
+
if (context.isToolApproval && !persistAllowed(persist, context.persistOptions))
|
|
25336
|
+
return cancelled();
|
|
25337
|
+
if (persist === "session" || persist === "always" || persist === "once")
|
|
25338
|
+
delete content["persist"];
|
|
25339
|
+
return { action: "accept", content: contentOrNull(content), _meta: responseMeta(response, context, persist) };
|
|
25823
25340
|
}
|
|
25824
|
-
|
|
25825
|
-
|
|
25826
|
-
darwin: "firefox",
|
|
25827
|
-
win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
|
|
25828
|
-
linux: "firefox"
|
|
25829
|
-
}, {
|
|
25830
|
-
wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
|
|
25831
|
-
}));
|
|
25832
|
-
defineLazyProperty(apps, "edge", () => detectPlatformBinary({
|
|
25833
|
-
darwin: "microsoft edge",
|
|
25834
|
-
win32: "msedge",
|
|
25835
|
-
linux: ["microsoft-edge", "microsoft-edge-dev"]
|
|
25836
|
-
}, {
|
|
25837
|
-
wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
|
|
25838
|
-
}));
|
|
25839
|
-
defineLazyProperty(apps, "safari", () => detectPlatformBinary({
|
|
25840
|
-
darwin: "Safari"
|
|
25841
|
-
}));
|
|
25842
|
-
var open_default = open;
|
|
25843
|
-
|
|
25844
|
-
// src/agent/auth.ts
|
|
25845
|
-
var AuthMethodId = {
|
|
25846
|
-
ApiKey: "api-key",
|
|
25847
|
-
ChatGpt: "chat-gpt",
|
|
25848
|
-
ChatGptDeviceCode: "chat-gpt-device-code"
|
|
25849
|
-
};
|
|
25850
|
-
var CODEX_API_KEY_ENV = "CODEX_API_KEY";
|
|
25851
|
-
var OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
|
|
25852
|
-
function authMethods(capabilities, env = process.env) {
|
|
25853
|
-
const methods2 = [{
|
|
25854
|
-
type: "agent",
|
|
25855
|
-
methodId: AuthMethodId.ApiKey,
|
|
25856
|
-
name: "API key",
|
|
25857
|
-
description: `Use the API key from ${CODEX_API_KEY_ENV} or ${OPENAI_API_KEY_ENV}, or pass it in _meta["api-key"].apiKey`
|
|
25858
|
-
}];
|
|
25859
|
-
if (!env["NO_BROWSER"]) {
|
|
25860
|
-
methods2.push({
|
|
25861
|
-
type: "agent",
|
|
25862
|
-
methodId: AuthMethodId.ChatGpt,
|
|
25863
|
-
name: "ChatGPT",
|
|
25864
|
-
description: "Sign in to ChatGPT in your browser"
|
|
25865
|
-
});
|
|
25341
|
+
if (CreateElicitationResponse.isDecline(response)) {
|
|
25342
|
+
return context.isToolApproval ? { action: "cancel", content: null, _meta: responseMeta(response, context) } : { action: "decline", content: null, _meta: responseMeta(response, context) };
|
|
25866
25343
|
}
|
|
25867
|
-
if (
|
|
25868
|
-
|
|
25869
|
-
type: "agent",
|
|
25870
|
-
methodId: AuthMethodId.ChatGptDeviceCode,
|
|
25871
|
-
name: "ChatGPT (device code)",
|
|
25872
|
-
description: "Sign in to ChatGPT by opening a verification page and entering a one-time code"
|
|
25873
|
-
});
|
|
25344
|
+
if (CreateElicitationResponse.isCancel(response)) {
|
|
25345
|
+
return { action: "cancel", content: null, _meta: responseMeta(response, context) };
|
|
25874
25346
|
}
|
|
25875
|
-
return
|
|
25347
|
+
return cancelled();
|
|
25876
25348
|
}
|
|
25877
|
-
|
|
25878
|
-
|
|
25879
|
-
case AuthMethodId.ApiKey:
|
|
25880
|
-
return await loginWithApiKey(codex, request, env);
|
|
25881
|
-
case AuthMethodId.ChatGpt:
|
|
25882
|
-
return await loginWithChatGpt(codex);
|
|
25883
|
-
case AuthMethodId.ChatGptDeviceCode:
|
|
25884
|
-
return await loginWithDeviceCode(codex, link, requestId);
|
|
25885
|
-
default:
|
|
25886
|
-
throw RequestError.invalidParams({ methodId: request.methodId }, `Unknown auth method "${request.methodId}"; expected one of ${Object.values(AuthMethodId).join(", ")}`);
|
|
25887
|
-
}
|
|
25349
|
+
function persistAllowed(persist, options) {
|
|
25350
|
+
return persist === undefined || persist === "once" || persist === "session" && options.has("session") || persist === "always" && options.has("always");
|
|
25888
25351
|
}
|
|
25889
|
-
|
|
25890
|
-
const
|
|
25891
|
-
|
|
25892
|
-
|
|
25893
|
-
|
|
25894
|
-
|
|
25352
|
+
function contentOrNull(content) {
|
|
25353
|
+
const entries = Object.entries(content);
|
|
25354
|
+
if (entries.length === 0)
|
|
25355
|
+
return null;
|
|
25356
|
+
return Object.fromEntries(entries.map(([key, value]) => [key, normalizeJsonValue(value)]));
|
|
25357
|
+
}
|
|
25358
|
+
function responseMeta(response, context, persist) {
|
|
25359
|
+
const source = recordOrNull(response._meta);
|
|
25360
|
+
const meta = source ? normalizeJsonObject(source) : {};
|
|
25361
|
+
if (context.isToolApproval)
|
|
25362
|
+
delete meta["persist"];
|
|
25363
|
+
if (persist === "session" || persist === "always")
|
|
25364
|
+
meta["persist"] = persist;
|
|
25365
|
+
return Object.keys(meta).length === 0 ? null : meta;
|
|
25366
|
+
}
|
|
25367
|
+
function userInputRequest(sessionId, params) {
|
|
25368
|
+
const properties = {};
|
|
25369
|
+
const required2 = [];
|
|
25370
|
+
const ids = new Set(params.questions.map((question) => question.id));
|
|
25371
|
+
for (const question of params.questions) {
|
|
25372
|
+
const options = question.options ?? [];
|
|
25373
|
+
const hasOther = question.isOther && options.length > 0;
|
|
25374
|
+
const base = {
|
|
25375
|
+
title: question.header || question.id,
|
|
25376
|
+
description: question.question,
|
|
25377
|
+
_meta: { codex: { isOther: question.isOther, isSecret: question.isSecret } }
|
|
25378
|
+
};
|
|
25379
|
+
if (!hasOther)
|
|
25380
|
+
required2.push(question.id);
|
|
25381
|
+
properties[question.id] = options.length > 0 ? { ...base, type: "string", oneOf: options.map((option) => ({ const: option.label, title: option.label, description: option.description })) } : { ...base, type: "string" };
|
|
25382
|
+
if (hasOther) {
|
|
25383
|
+
properties[otherFieldId(question.id, ids)] = {
|
|
25384
|
+
type: "string",
|
|
25385
|
+
title: "Other",
|
|
25386
|
+
description: "Type your own answer instead of choosing an option above.",
|
|
25387
|
+
_meta: { codex: { questionId: question.id, isOtherAnswer: true, isSecret: question.isSecret } }
|
|
25388
|
+
};
|
|
25389
|
+
}
|
|
25895
25390
|
}
|
|
25896
|
-
|
|
25897
|
-
|
|
25898
|
-
|
|
25899
|
-
|
|
25900
|
-
|
|
25901
|
-
|
|
25391
|
+
const first = params.questions[0];
|
|
25392
|
+
return {
|
|
25393
|
+
sessionId,
|
|
25394
|
+
toolCallId: params.itemId,
|
|
25395
|
+
mode: "form",
|
|
25396
|
+
message: params.questions.length === 1 && first ? first.question : "Input requested",
|
|
25397
|
+
requestedSchema: { type: "object", properties, required: required2 },
|
|
25398
|
+
_meta: { codex: { isBlocking: params.isBlocking } }
|
|
25399
|
+
};
|
|
25902
25400
|
}
|
|
25903
|
-
|
|
25904
|
-
|
|
25905
|
-
|
|
25906
|
-
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
}
|
|
25401
|
+
function convertUserInputResponse(response, params) {
|
|
25402
|
+
if (!CreateElicitationResponse.isAccept(response))
|
|
25403
|
+
return { answers: {} };
|
|
25404
|
+
const content = isRecord2(response.content) ? response.content : {};
|
|
25405
|
+
const ids = new Set(params.questions.map((question) => question.id));
|
|
25406
|
+
const answers = {};
|
|
25407
|
+
for (const question of params.questions) {
|
|
25408
|
+
const hasOther = question.isOther && (question.options?.length ?? 0) > 0;
|
|
25409
|
+
const value = (hasOther ? answerValue(content, otherFieldId(question.id, ids)) : undefined) ?? answerValue(content, question.id);
|
|
25410
|
+
if (value === undefined)
|
|
25411
|
+
continue;
|
|
25412
|
+
answers[question.id] = { answers: Array.isArray(value) ? value.map(String) : [String(value)] };
|
|
25413
|
+
}
|
|
25414
|
+
return { answers };
|
|
25917
25415
|
}
|
|
25918
|
-
|
|
25919
|
-
const
|
|
25920
|
-
if (
|
|
25416
|
+
function answerValue(content, fieldId) {
|
|
25417
|
+
const value = content[fieldId];
|
|
25418
|
+
if (typeof value === "string" && value.trim() === "")
|
|
25921
25419
|
return;
|
|
25922
|
-
|
|
25923
|
-
|
|
25924
|
-
|
|
25925
|
-
throw RequestError.internalError({ type: started.type }, "Unexpected login response");
|
|
25926
|
-
const elicitation = link.request(methods.client.elicitation.create, {
|
|
25927
|
-
mode: "url",
|
|
25928
|
-
requestId,
|
|
25929
|
-
elicitationId: started.loginId,
|
|
25930
|
-
url: started.verificationUrl,
|
|
25931
|
-
message: `Sign in to ChatGPT and enter this code: ${started.userCode}`
|
|
25932
|
-
});
|
|
25933
|
-
elicitation.catch(() => {});
|
|
25934
|
-
const first = await Promise.race([
|
|
25935
|
-
completed.then((result2) => ({ kind: "completed", result: result2 })),
|
|
25936
|
-
elicitation.then((response) => ({ kind: "elicitation", response }))
|
|
25937
|
-
]);
|
|
25938
|
-
if (first.kind === "elicitation" && !CreateElicitationResponse.isAccept(first.response)) {
|
|
25939
|
-
await codex.accountLoginCancel({ loginId: started.loginId });
|
|
25940
|
-
throw RequestError.authRequired(undefined, "ChatGPT device code login was cancelled");
|
|
25941
|
-
}
|
|
25942
|
-
const result = first.kind === "completed" ? first.result : await completed;
|
|
25943
|
-
await link.notify(methods.client.elicitation.complete, { elicitationId: started.loginId });
|
|
25944
|
-
if (!result.success)
|
|
25945
|
-
throw RequestError.authRequired({ error: result.error }, result.error ?? "ChatGPT login failed");
|
|
25946
|
-
});
|
|
25420
|
+
if (Array.isArray(value) && value.length === 0)
|
|
25421
|
+
return;
|
|
25422
|
+
return value;
|
|
25947
25423
|
}
|
|
25948
|
-
|
|
25949
|
-
|
|
25950
|
-
|
|
25951
|
-
|
|
25952
|
-
|
|
25424
|
+
function otherFieldId(questionId, ids) {
|
|
25425
|
+
const base = `${questionId}${OTHER_SUFFIX}`;
|
|
25426
|
+
if (!ids.has(base))
|
|
25427
|
+
return base;
|
|
25428
|
+
let index = 1;
|
|
25429
|
+
while (ids.has(`${base}${index}`))
|
|
25430
|
+
index += 1;
|
|
25431
|
+
return `${base}${index}`;
|
|
25953
25432
|
}
|
|
25954
25433
|
|
|
25955
|
-
// src/
|
|
25956
|
-
class
|
|
25434
|
+
// src/permissions/turnContext.ts
|
|
25435
|
+
class TurnContext {
|
|
25957
25436
|
sessionId;
|
|
25958
|
-
|
|
25959
|
-
|
|
25960
|
-
|
|
25961
|
-
|
|
25962
|
-
turnGeneration = 0;
|
|
25963
|
-
disposed = false;
|
|
25964
|
-
output = new AbortController;
|
|
25965
|
-
terminalAttempted = false;
|
|
25966
|
-
messageId;
|
|
25967
|
-
get signal() {
|
|
25968
|
-
return this.output.signal;
|
|
25969
|
-
}
|
|
25970
|
-
constructor(sessionId, link, capabilities) {
|
|
25437
|
+
fileChanges = new Map;
|
|
25438
|
+
pendingMcpCalls = new Map;
|
|
25439
|
+
sequence = 0;
|
|
25440
|
+
constructor(sessionId) {
|
|
25971
25441
|
this.sessionId = sessionId;
|
|
25972
|
-
this.link = link;
|
|
25973
|
-
this.capabilities = capabilities;
|
|
25974
25442
|
}
|
|
25975
|
-
|
|
25976
|
-
|
|
25443
|
+
observe(notification) {
|
|
25444
|
+
switch (notification.method) {
|
|
25445
|
+
case "item/started":
|
|
25446
|
+
this.itemStarted(notification.params.item);
|
|
25447
|
+
return;
|
|
25448
|
+
case "item/completed":
|
|
25449
|
+
this.itemCompleted(notification.params.item);
|
|
25450
|
+
return;
|
|
25451
|
+
case "turn/completed":
|
|
25452
|
+
this.fileChanges.clear();
|
|
25453
|
+
this.pendingMcpCalls.clear();
|
|
25454
|
+
return;
|
|
25455
|
+
case "serverRequest/resolved":
|
|
25456
|
+
this.pendingMcpCalls.clear();
|
|
25457
|
+
return;
|
|
25458
|
+
default:
|
|
25459
|
+
return;
|
|
25460
|
+
}
|
|
25461
|
+
}
|
|
25462
|
+
fileChange(itemId) {
|
|
25463
|
+
return this.fileChanges.get(itemId);
|
|
25464
|
+
}
|
|
25465
|
+
popPendingMcpCall(serverName) {
|
|
25466
|
+
const pending = this.pendingMcpCalls.get(serverName);
|
|
25467
|
+
if (pending?.length !== 1)
|
|
25977
25468
|
return;
|
|
25978
|
-
|
|
25469
|
+
this.pendingMcpCalls.delete(serverName);
|
|
25470
|
+
return pending[0];
|
|
25979
25471
|
}
|
|
25980
|
-
|
|
25981
|
-
|
|
25982
|
-
|
|
25472
|
+
nextStandaloneToolCallId(serverName) {
|
|
25473
|
+
this.sequence += 1;
|
|
25474
|
+
return `elicitation:${this.sessionId}:${serverName}:${this.sequence}`;
|
|
25983
25475
|
}
|
|
25984
|
-
|
|
25985
|
-
|
|
25986
|
-
|
|
25987
|
-
|
|
25988
|
-
|
|
25989
|
-
|
|
25990
|
-
|
|
25991
|
-
}
|
|
25476
|
+
itemStarted(item) {
|
|
25477
|
+
if (item.type === "fileChange") {
|
|
25478
|
+
this.fileChanges.set(item.id, item);
|
|
25479
|
+
} else if (item.type === "mcpToolCall") {
|
|
25480
|
+
const pending = this.pendingMcpCalls.get(item.server) ?? [];
|
|
25481
|
+
pending.push(item.id);
|
|
25482
|
+
this.pendingMcpCalls.set(item.server, pending);
|
|
25483
|
+
}
|
|
25992
25484
|
}
|
|
25993
|
-
|
|
25994
|
-
|
|
25485
|
+
itemCompleted(item) {
|
|
25486
|
+
if (item.type === "fileChange") {
|
|
25487
|
+
this.fileChanges.delete(item.id);
|
|
25488
|
+
} else if (item.type === "mcpToolCall") {
|
|
25489
|
+
const pending = this.pendingMcpCalls.get(item.server);
|
|
25490
|
+
if (!pending)
|
|
25491
|
+
return;
|
|
25492
|
+
const index = pending.indexOf(item.id);
|
|
25493
|
+
if (index >= 0)
|
|
25494
|
+
pending.splice(index, 1);
|
|
25495
|
+
if (pending.length === 0)
|
|
25496
|
+
this.pendingMcpCalls.delete(item.server);
|
|
25497
|
+
}
|
|
25995
25498
|
}
|
|
25996
|
-
|
|
25997
|
-
|
|
25998
|
-
|
|
25999
|
-
|
|
26000
|
-
|
|
26001
|
-
|
|
26002
|
-
|
|
26003
|
-
|
|
25499
|
+
}
|
|
25500
|
+
|
|
25501
|
+
// node_modules/open/index.js
|
|
25502
|
+
import process10 from "process";
|
|
25503
|
+
import path6 from "path";
|
|
25504
|
+
import { fileURLToPath } from "url";
|
|
25505
|
+
import childProcess4 from "child_process";
|
|
25506
|
+
import fs6, { constants as fsConstants2 } from "fs/promises";
|
|
25507
|
+
|
|
25508
|
+
// node_modules/wsl-utils/index.js
|
|
25509
|
+
import path5 from "path";
|
|
25510
|
+
import { promisify as promisify2 } from "util";
|
|
25511
|
+
import childProcess2 from "child_process";
|
|
25512
|
+
import fs5, { constants as fsConstants } from "fs/promises";
|
|
25513
|
+
|
|
25514
|
+
// node_modules/is-wsl/index.js
|
|
25515
|
+
import process2 from "process";
|
|
25516
|
+
import os2 from "os";
|
|
25517
|
+
import fs4 from "fs";
|
|
25518
|
+
|
|
25519
|
+
// node_modules/is-inside-container/index.js
|
|
25520
|
+
import fs3 from "fs";
|
|
25521
|
+
|
|
25522
|
+
// node_modules/is-docker/index.js
|
|
25523
|
+
import fs2 from "fs";
|
|
25524
|
+
var isDockerCached;
|
|
25525
|
+
function hasDockerEnv() {
|
|
25526
|
+
try {
|
|
25527
|
+
fs2.statSync("/.dockerenv");
|
|
25528
|
+
return true;
|
|
25529
|
+
} catch {
|
|
25530
|
+
return false;
|
|
26004
25531
|
}
|
|
26005
|
-
|
|
26006
|
-
|
|
26007
|
-
|
|
26008
|
-
|
|
25532
|
+
}
|
|
25533
|
+
function hasDockerCGroup() {
|
|
25534
|
+
try {
|
|
25535
|
+
return fs2.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
25536
|
+
} catch {
|
|
25537
|
+
return false;
|
|
26009
25538
|
}
|
|
26010
|
-
|
|
26011
|
-
|
|
26012
|
-
|
|
26013
|
-
|
|
26014
|
-
this.turnActive = true;
|
|
26015
|
-
this.waiting = 0;
|
|
26016
|
-
this.state({ sessionUpdate: "state_update", state: "running" });
|
|
25539
|
+
}
|
|
25540
|
+
function isDocker() {
|
|
25541
|
+
if (isDockerCached === undefined) {
|
|
25542
|
+
isDockerCached = hasDockerEnv() || hasDockerCGroup();
|
|
26017
25543
|
}
|
|
26018
|
-
|
|
26019
|
-
|
|
26020
|
-
|
|
26021
|
-
|
|
26022
|
-
|
|
26023
|
-
|
|
26024
|
-
|
|
26025
|
-
|
|
26026
|
-
|
|
26027
|
-
|
|
26028
|
-
|
|
26029
|
-
|
|
26030
|
-
|
|
26031
|
-
|
|
26032
|
-
|
|
25544
|
+
return isDockerCached;
|
|
25545
|
+
}
|
|
25546
|
+
|
|
25547
|
+
// node_modules/is-inside-container/index.js
|
|
25548
|
+
var cachedResult;
|
|
25549
|
+
var hasContainerEnv = () => {
|
|
25550
|
+
try {
|
|
25551
|
+
fs3.statSync("/run/.containerenv");
|
|
25552
|
+
return true;
|
|
25553
|
+
} catch {
|
|
25554
|
+
return false;
|
|
25555
|
+
}
|
|
25556
|
+
};
|
|
25557
|
+
function isInsideContainer() {
|
|
25558
|
+
if (cachedResult === undefined) {
|
|
25559
|
+
cachedResult = hasContainerEnv() || isDocker();
|
|
25560
|
+
}
|
|
25561
|
+
return cachedResult;
|
|
25562
|
+
}
|
|
25563
|
+
|
|
25564
|
+
// node_modules/is-wsl/index.js
|
|
25565
|
+
var isWsl = () => {
|
|
25566
|
+
if (process2.platform !== "linux") {
|
|
25567
|
+
return false;
|
|
25568
|
+
}
|
|
25569
|
+
if (os2.release().toLowerCase().includes("microsoft")) {
|
|
25570
|
+
if (isInsideContainer()) {
|
|
25571
|
+
return false;
|
|
25572
|
+
}
|
|
25573
|
+
return true;
|
|
25574
|
+
}
|
|
25575
|
+
try {
|
|
25576
|
+
if (fs4.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
|
|
25577
|
+
return !isInsideContainer();
|
|
25578
|
+
}
|
|
25579
|
+
} catch {}
|
|
25580
|
+
if (fs4.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs4.existsSync("/run/WSL")) {
|
|
25581
|
+
return !isInsideContainer();
|
|
26033
25582
|
}
|
|
26034
|
-
|
|
26035
|
-
|
|
26036
|
-
|
|
26037
|
-
|
|
26038
|
-
|
|
26039
|
-
|
|
25583
|
+
return false;
|
|
25584
|
+
};
|
|
25585
|
+
var is_wsl_default = process2.env.__IS_WSL_TEST__ ? isWsl : isWsl();
|
|
25586
|
+
|
|
25587
|
+
// node_modules/wsl-utils/node_modules/powershell-utils/index.js
|
|
25588
|
+
import process3 from "process";
|
|
25589
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
25590
|
+
import { promisify } from "util";
|
|
25591
|
+
import childProcess from "child_process";
|
|
25592
|
+
var execFile = promisify(childProcess.execFile);
|
|
25593
|
+
var powerShellPath = () => `${process3.env.SYSTEMROOT || process3.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
25594
|
+
var executePowerShell = async (command, options = {}) => {
|
|
25595
|
+
const {
|
|
25596
|
+
powerShellPath: psPath,
|
|
25597
|
+
...execFileOptions
|
|
25598
|
+
} = options;
|
|
25599
|
+
const encodedCommand = executePowerShell.encodeCommand(command);
|
|
25600
|
+
return execFile(psPath ?? powerShellPath(), [
|
|
25601
|
+
...executePowerShell.argumentsPrefix,
|
|
25602
|
+
encodedCommand
|
|
25603
|
+
], {
|
|
25604
|
+
encoding: "utf8",
|
|
25605
|
+
...execFileOptions
|
|
25606
|
+
});
|
|
25607
|
+
};
|
|
25608
|
+
executePowerShell.argumentsPrefix = [
|
|
25609
|
+
"-NoProfile",
|
|
25610
|
+
"-NonInteractive",
|
|
25611
|
+
"-ExecutionPolicy",
|
|
25612
|
+
"Bypass",
|
|
25613
|
+
"-EncodedCommand"
|
|
25614
|
+
];
|
|
25615
|
+
executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
|
|
25616
|
+
executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
|
|
25617
|
+
|
|
25618
|
+
// node_modules/wsl-utils/utilities.js
|
|
25619
|
+
function parseMountPointFromConfig(content) {
|
|
25620
|
+
for (const line of content.split(`
|
|
25621
|
+
`)) {
|
|
25622
|
+
if (/^\s*#/.test(line)) {
|
|
25623
|
+
continue;
|
|
25624
|
+
}
|
|
25625
|
+
const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
|
|
25626
|
+
if (!match) {
|
|
25627
|
+
continue;
|
|
25628
|
+
}
|
|
25629
|
+
return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
|
|
26040
25630
|
}
|
|
26041
|
-
|
|
26042
|
-
|
|
26043
|
-
|
|
26044
|
-
|
|
25631
|
+
}
|
|
25632
|
+
|
|
25633
|
+
// node_modules/wsl-utils/index.js
|
|
25634
|
+
var execFile2 = promisify2(childProcess2.execFile);
|
|
25635
|
+
var wslDrivesMountPoint = (() => {
|
|
25636
|
+
const defaultMountPoint = "/mnt/";
|
|
25637
|
+
let mountPoint;
|
|
25638
|
+
return async function() {
|
|
25639
|
+
if (mountPoint) {
|
|
25640
|
+
return mountPoint;
|
|
25641
|
+
}
|
|
25642
|
+
const configFilePath = "/etc/wsl.conf";
|
|
25643
|
+
let isConfigFileExists = false;
|
|
26045
25644
|
try {
|
|
26046
|
-
|
|
26047
|
-
|
|
26048
|
-
|
|
26049
|
-
|
|
26050
|
-
return
|
|
26051
|
-
} finally {
|
|
26052
|
-
if (this.turnActive && generation === this.turnGeneration && --this.waiting === 0) {
|
|
26053
|
-
const pending = this.state({ sessionUpdate: "state_update", state: "running" });
|
|
26054
|
-
await (signal ? abortable(pending, signal).catch(() => {}) : pending);
|
|
26055
|
-
}
|
|
25645
|
+
await fs5.access(configFilePath, fsConstants.F_OK);
|
|
25646
|
+
isConfigFileExists = true;
|
|
25647
|
+
} catch {}
|
|
25648
|
+
if (!isConfigFileExists) {
|
|
25649
|
+
return defaultMountPoint;
|
|
26056
25650
|
}
|
|
26057
|
-
|
|
26058
|
-
|
|
25651
|
+
const configContent = await fs5.readFile(configFilePath, { encoding: "utf8" });
|
|
25652
|
+
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
25653
|
+
if (parsedMountPoint === undefined) {
|
|
25654
|
+
return defaultMountPoint;
|
|
25655
|
+
}
|
|
25656
|
+
mountPoint = parsedMountPoint;
|
|
25657
|
+
mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
|
|
25658
|
+
return mountPoint;
|
|
25659
|
+
};
|
|
25660
|
+
})();
|
|
25661
|
+
var powerShellPathFromWsl = async () => {
|
|
25662
|
+
const mountPoint = await wslDrivesMountPoint();
|
|
25663
|
+
return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
|
|
25664
|
+
};
|
|
25665
|
+
var powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
|
|
25666
|
+
var canAccessPowerShellPromise;
|
|
25667
|
+
var canAccessPowerShell = async () => {
|
|
25668
|
+
canAccessPowerShellPromise ??= (async () => {
|
|
26059
25669
|
try {
|
|
26060
|
-
await
|
|
26061
|
-
|
|
26062
|
-
|
|
25670
|
+
const psPath = await powerShellPath2();
|
|
25671
|
+
await fs5.access(psPath, fsConstants.X_OK);
|
|
25672
|
+
return true;
|
|
25673
|
+
} catch {
|
|
25674
|
+
return false;
|
|
25675
|
+
}
|
|
25676
|
+
})();
|
|
25677
|
+
return canAccessPowerShellPromise;
|
|
25678
|
+
};
|
|
25679
|
+
var wslDefaultBrowser = async () => {
|
|
25680
|
+
const psPath = await powerShellPath2();
|
|
25681
|
+
const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
|
|
25682
|
+
const { stdout } = await executePowerShell(command, {
|
|
25683
|
+
powerShellPath: psPath,
|
|
25684
|
+
cwd: path5.dirname(psPath)
|
|
25685
|
+
});
|
|
25686
|
+
return stdout.trim();
|
|
25687
|
+
};
|
|
25688
|
+
var isUrl = (path6) => /^[a-z]+:\/\//i.test(path6);
|
|
25689
|
+
var convertWslPathToWindows = async (paths) => {
|
|
25690
|
+
const isBatch = Array.isArray(paths);
|
|
25691
|
+
const pathArray = isBatch ? paths : [paths];
|
|
25692
|
+
const indicesToConvert = [];
|
|
25693
|
+
const pathsToConvert = [];
|
|
25694
|
+
for (const [index, path6] of pathArray.entries()) {
|
|
25695
|
+
if (!isUrl(path6)) {
|
|
25696
|
+
indicesToConvert.push(index);
|
|
25697
|
+
pathsToConvert.push(path6);
|
|
26063
25698
|
}
|
|
26064
25699
|
}
|
|
26065
|
-
|
|
26066
|
-
|
|
26067
|
-
|
|
26068
|
-
|
|
26069
|
-
|
|
25700
|
+
const results = [...pathArray];
|
|
25701
|
+
if (pathsToConvert.length > 0) {
|
|
25702
|
+
try {
|
|
25703
|
+
const { stdout } = await execFile2("wslpath", ["-aw", ...pathsToConvert], { encoding: "utf8" });
|
|
25704
|
+
const convertedPaths = stdout.split(/\r?\n/).filter(Boolean);
|
|
25705
|
+
for (const [index, originalIndex] of indicesToConvert.entries()) {
|
|
25706
|
+
results[originalIndex] = convertedPaths[index] ?? pathArray[originalIndex];
|
|
25707
|
+
}
|
|
25708
|
+
} catch {}
|
|
26070
25709
|
}
|
|
26071
|
-
|
|
25710
|
+
return isBatch ? results : results[0];
|
|
25711
|
+
};
|
|
26072
25712
|
|
|
26073
|
-
//
|
|
26074
|
-
|
|
26075
|
-
|
|
26076
|
-
|
|
26077
|
-
|
|
26078
|
-
|
|
26079
|
-
|
|
26080
|
-
|
|
26081
|
-
|
|
26082
|
-
|
|
26083
|
-
|
|
25713
|
+
// node_modules/powershell-utils/index.js
|
|
25714
|
+
import process4 from "process";
|
|
25715
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
25716
|
+
import { promisify as promisify3 } from "util";
|
|
25717
|
+
import childProcess3 from "child_process";
|
|
25718
|
+
var execFile3 = promisify3(childProcess3.execFile);
|
|
25719
|
+
var powerShellPath3 = () => `${process4.env.SYSTEMROOT || process4.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
25720
|
+
var argumentsPrefix = [
|
|
25721
|
+
"-NoProfile",
|
|
25722
|
+
"-NonInteractive",
|
|
25723
|
+
"-ExecutionPolicy",
|
|
25724
|
+
"Bypass",
|
|
25725
|
+
"-EncodedCommand"
|
|
26084
25726
|
];
|
|
26085
|
-
|
|
26086
|
-
|
|
26087
|
-
|
|
26088
|
-
|
|
26089
|
-
|
|
26090
|
-
|
|
26091
|
-
|
|
26092
|
-
|
|
26093
|
-
|
|
26094
|
-
|
|
26095
|
-
|
|
26096
|
-
|
|
26097
|
-
}
|
|
26098
|
-
|
|
26099
|
-
|
|
26100
|
-
|
|
26101
|
-
|
|
26102
|
-
|
|
26103
|
-
|
|
26104
|
-
|
|
26105
|
-
|
|
26106
|
-
|
|
26107
|
-
|
|
26108
|
-
|
|
26109
|
-
|
|
26110
|
-
|
|
26111
|
-
|
|
26112
|
-
|
|
26113
|
-
|
|
26114
|
-
|
|
26115
|
-
|
|
26116
|
-
|
|
26117
|
-
|
|
26118
|
-
case "skills":
|
|
26119
|
-
return { kind: "message", text: "" };
|
|
26120
|
-
default:
|
|
26121
|
-
return { kind: "prompt" };
|
|
26122
|
-
}
|
|
26123
|
-
}
|
|
26124
|
-
function usage(name, hint) {
|
|
26125
|
-
return `Usage: /${name} ${hint}`;
|
|
26126
|
-
}
|
|
26127
|
-
function availableCommands(skills) {
|
|
26128
|
-
const commands = new Map;
|
|
26129
|
-
for (const builtin of BUILTIN_COMMANDS)
|
|
26130
|
-
commands.set(builtin.name, builtin);
|
|
26131
|
-
for (const entry of skills) {
|
|
26132
|
-
for (const skill of entry.skills) {
|
|
26133
|
-
const name = `$${skill.name}`;
|
|
26134
|
-
if (commands.has(name))
|
|
26135
|
-
continue;
|
|
26136
|
-
commands.set(name, { name, description: skill.shortDescription || skill.description || skill.name });
|
|
25727
|
+
var encodeCommand = (command) => Buffer3.from(command, "utf16le").toString("base64");
|
|
25728
|
+
var escapeArgument = (value) => `'${String(value).replaceAll(/['\u2018\u2019\u201A\u201B]/g, (match) => match + match)}'`;
|
|
25729
|
+
var createArguments = (command) => [...argumentsPrefix, encodeCommand(command)];
|
|
25730
|
+
var createExecFileOptions = (options) => ({
|
|
25731
|
+
encoding: "utf8",
|
|
25732
|
+
...options,
|
|
25733
|
+
shell: false
|
|
25734
|
+
});
|
|
25735
|
+
var executePowerShell2 = async (command, options = {}) => {
|
|
25736
|
+
const {
|
|
25737
|
+
powerShellPath: psPath,
|
|
25738
|
+
...execFileOptions
|
|
25739
|
+
} = options;
|
|
25740
|
+
return execFile3(psPath ?? powerShellPath3(), createArguments(command), createExecFileOptions(execFileOptions));
|
|
25741
|
+
};
|
|
25742
|
+
executePowerShell2.argumentsPrefix = argumentsPrefix;
|
|
25743
|
+
executePowerShell2.encodeCommand = encodeCommand;
|
|
25744
|
+
executePowerShell2.escapeArgument = escapeArgument;
|
|
25745
|
+
executePowerShell2.createArguments = createArguments;
|
|
25746
|
+
|
|
25747
|
+
// node_modules/define-lazy-prop/index.js
|
|
25748
|
+
function defineLazyProperty(object2, propertyName, valueGetter) {
|
|
25749
|
+
const define = (value) => Object.defineProperty(object2, propertyName, { value, enumerable: true, writable: true });
|
|
25750
|
+
Object.defineProperty(object2, propertyName, {
|
|
25751
|
+
configurable: true,
|
|
25752
|
+
enumerable: true,
|
|
25753
|
+
get() {
|
|
25754
|
+
const result = valueGetter();
|
|
25755
|
+
define(result);
|
|
25756
|
+
return result;
|
|
25757
|
+
},
|
|
25758
|
+
set(value) {
|
|
25759
|
+
define(value);
|
|
26137
25760
|
}
|
|
25761
|
+
});
|
|
25762
|
+
return object2;
|
|
25763
|
+
}
|
|
25764
|
+
|
|
25765
|
+
// node_modules/default-browser/index.js
|
|
25766
|
+
import { promisify as promisify7 } from "util";
|
|
25767
|
+
import process8 from "process";
|
|
25768
|
+
import { execFile as execFile7 } from "child_process";
|
|
25769
|
+
|
|
25770
|
+
// node_modules/default-browser-id/index.js
|
|
25771
|
+
import { promisify as promisify4 } from "util";
|
|
25772
|
+
import process5 from "process";
|
|
25773
|
+
import { execFile as execFile4 } from "child_process";
|
|
25774
|
+
var execFileAsync = promisify4(execFile4);
|
|
25775
|
+
async function defaultBrowserId() {
|
|
25776
|
+
if (process5.platform !== "darwin") {
|
|
25777
|
+
throw new Error("macOS only");
|
|
26138
25778
|
}
|
|
26139
|
-
|
|
25779
|
+
const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
|
|
25780
|
+
const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
|
|
25781
|
+
const browserId = match?.groups.id ?? "com.apple.Safari";
|
|
25782
|
+
if (browserId === "com.apple.safari") {
|
|
25783
|
+
return "com.apple.Safari";
|
|
25784
|
+
}
|
|
25785
|
+
return browserId;
|
|
26140
25786
|
}
|
|
26141
|
-
|
|
26142
|
-
|
|
26143
|
-
|
|
26144
|
-
|
|
26145
|
-
|
|
26146
|
-
|
|
26147
|
-
|
|
25787
|
+
|
|
25788
|
+
// node_modules/run-applescript/index.js
|
|
25789
|
+
import process6 from "process";
|
|
25790
|
+
import { promisify as promisify5 } from "util";
|
|
25791
|
+
import { execFile as execFile5, execFileSync } from "child_process";
|
|
25792
|
+
var execFileAsync2 = promisify5(execFile5);
|
|
25793
|
+
async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
|
|
25794
|
+
if (process6.platform !== "darwin") {
|
|
25795
|
+
throw new Error("macOS only");
|
|
25796
|
+
}
|
|
25797
|
+
const outputArguments = humanReadableOutput ? [] : ["-ss"];
|
|
25798
|
+
const execOptions = {};
|
|
25799
|
+
if (signal) {
|
|
25800
|
+
execOptions.signal = signal;
|
|
25801
|
+
}
|
|
25802
|
+
const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
|
|
25803
|
+
return stdout.trim();
|
|
26148
25804
|
}
|
|
26149
|
-
|
|
26150
|
-
|
|
26151
|
-
|
|
26152
|
-
|
|
26153
|
-
|
|
26154
|
-
return `- ${server.name}: ${tools} tools, ${resources} resources, auth=${server.authStatus}`;
|
|
26155
|
-
});
|
|
26156
|
-
const sessionLines = session.mcpServerNames.map((name) => `- ${name}`);
|
|
26157
|
-
return [
|
|
26158
|
-
lines.length > 0 ? "Configured MCP servers:" : "No MCP servers configured.",
|
|
26159
|
-
...lines,
|
|
26160
|
-
...sessionLines.length > 0 ? ["", "Session MCP servers:", ...sessionLines] : []
|
|
26161
|
-
].join(`
|
|
26162
|
-
`);
|
|
25805
|
+
|
|
25806
|
+
// node_modules/bundle-name/index.js
|
|
25807
|
+
async function bundleName(bundleId) {
|
|
25808
|
+
return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
|
|
25809
|
+
tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
|
|
26163
25810
|
}
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26175
|
-
|
|
26176
|
-
|
|
26177
|
-
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
25811
|
+
|
|
25812
|
+
// node_modules/default-browser/windows.js
|
|
25813
|
+
import process7 from "process";
|
|
25814
|
+
import { promisify as promisify6 } from "util";
|
|
25815
|
+
import { execFile as execFile6 } from "child_process";
|
|
25816
|
+
var execFileAsync3 = promisify6(execFile6);
|
|
25817
|
+
var windowsBrowserProgIds = {
|
|
25818
|
+
MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
|
|
25819
|
+
MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
|
|
25820
|
+
MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
|
|
25821
|
+
AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
|
|
25822
|
+
ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
|
|
25823
|
+
ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
|
|
25824
|
+
ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
|
|
25825
|
+
ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
|
|
25826
|
+
BraveHTML: { name: "Brave", id: "com.brave.Browser" },
|
|
25827
|
+
BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
|
|
25828
|
+
BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
|
|
25829
|
+
BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
|
|
25830
|
+
FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
|
|
25831
|
+
OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
|
|
25832
|
+
VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
|
|
25833
|
+
"IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
|
|
25834
|
+
};
|
|
25835
|
+
var _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
|
|
25836
|
+
|
|
25837
|
+
class UnknownBrowserError extends Error {
|
|
26183
25838
|
}
|
|
26184
|
-
function
|
|
26185
|
-
const
|
|
26186
|
-
|
|
26187
|
-
|
|
26188
|
-
|
|
26189
|
-
|
|
26190
|
-
|
|
26191
|
-
|
|
26192
|
-
|
|
26193
|
-
|
|
26194
|
-
|
|
25839
|
+
async function defaultBrowser(_execFileAsync = execFileAsync3) {
|
|
25840
|
+
const regPath = `${process7.env.SYSTEMROOT ?? process7.env.windir ?? "C:\\Windows"}\\System32\\reg.exe`;
|
|
25841
|
+
const { stdout } = await _execFileAsync(regPath, [
|
|
25842
|
+
"QUERY",
|
|
25843
|
+
" HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
|
|
25844
|
+
"/v",
|
|
25845
|
+
"ProgId"
|
|
25846
|
+
]);
|
|
25847
|
+
const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
|
|
25848
|
+
if (!match) {
|
|
25849
|
+
throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
|
|
26195
25850
|
}
|
|
25851
|
+
const { id } = match.groups;
|
|
25852
|
+
const dotIndex = id.lastIndexOf(".");
|
|
25853
|
+
const hyphenIndex = id.lastIndexOf("-");
|
|
25854
|
+
const baseIdByDot = dotIndex === -1 ? undefined : id.slice(0, dotIndex);
|
|
25855
|
+
const baseIdByHyphen = hyphenIndex === -1 ? undefined : id.slice(0, hyphenIndex);
|
|
25856
|
+
return windowsBrowserProgIds[id] ?? windowsBrowserProgIds[baseIdByDot] ?? windowsBrowserProgIds[baseIdByHyphen] ?? { name: id, id };
|
|
26196
25857
|
}
|
|
26197
|
-
|
|
26198
|
-
|
|
26199
|
-
|
|
26200
|
-
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
|
|
26204
|
-
|
|
26205
|
-
|
|
26206
|
-
lines.push(`**${prefix}${label}:** ${Math.round(100 - window.usedPercent)}% left${reset}`);
|
|
26207
|
-
}
|
|
26208
|
-
if (snapshot.credits?.unlimited)
|
|
26209
|
-
lines.push(`**${prefix}Credits:** unlimited`);
|
|
26210
|
-
else if (snapshot.credits?.balance)
|
|
26211
|
-
lines.push(`**${prefix}Credits:** ${snapshot.credits.balance}`);
|
|
25858
|
+
|
|
25859
|
+
// node_modules/default-browser/index.js
|
|
25860
|
+
var execFileAsync4 = promisify7(execFile7);
|
|
25861
|
+
var titleize = (string4) => string4.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
|
|
25862
|
+
async function defaultBrowser2() {
|
|
25863
|
+
if (process8.platform === "darwin") {
|
|
25864
|
+
const id = await defaultBrowserId();
|
|
25865
|
+
const name = await bundleName(id);
|
|
25866
|
+
return { name, id };
|
|
26212
25867
|
}
|
|
26213
|
-
|
|
26214
|
-
}
|
|
26215
|
-
|
|
26216
|
-
|
|
26217
|
-
return
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
|
|
26221
|
-
|
|
26222
|
-
|
|
26223
|
-
return `${Math.round(minutes / 1440)}d limit`;
|
|
26224
|
-
return "Weekly limit";
|
|
25868
|
+
if (process8.platform === "linux") {
|
|
25869
|
+
const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
|
|
25870
|
+
const id = stdout.trim();
|
|
25871
|
+
const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
|
|
25872
|
+
return { name, id };
|
|
25873
|
+
}
|
|
25874
|
+
if (process8.platform === "win32") {
|
|
25875
|
+
return defaultBrowser();
|
|
25876
|
+
}
|
|
25877
|
+
throw new Error("Only macOS, Linux, and Windows are supported");
|
|
26225
25878
|
}
|
|
26226
25879
|
|
|
26227
|
-
//
|
|
26228
|
-
|
|
26229
|
-
|
|
26230
|
-
|
|
26231
|
-
|
|
26232
|
-
|
|
26233
|
-
|
|
26234
|
-
|
|
26235
|
-
|
|
26236
|
-
|
|
26237
|
-
|
|
26238
|
-
|
|
26239
|
-
|
|
26240
|
-
|
|
26241
|
-
|
|
26242
|
-
|
|
26243
|
-
|
|
26244
|
-
|
|
26245
|
-
|
|
26246
|
-
|
|
26247
|
-
return;
|
|
26248
|
-
}
|
|
26249
|
-
case MODEL_CONFIG_ID: {
|
|
26250
|
-
const modelId = stringValue(request);
|
|
26251
|
-
const model = findModel(session.catalog, modelId);
|
|
26252
|
-
session.model = { model: modelId, effort: coerceEffort(model, session.model.effort) };
|
|
26253
|
-
if (!modelSupportsFast(model))
|
|
26254
|
-
session.fastMode = false;
|
|
26255
|
-
return;
|
|
25880
|
+
// node_modules/is-in-ssh/index.js
|
|
25881
|
+
import process9 from "process";
|
|
25882
|
+
var isInSsh = Boolean(process9.env.SSH_CONNECTION || process9.env.SSH_CLIENT || process9.env.SSH_TTY);
|
|
25883
|
+
var is_in_ssh_default = isInSsh;
|
|
25884
|
+
|
|
25885
|
+
// node_modules/open/index.js
|
|
25886
|
+
var fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
25887
|
+
var __dirname2 = import.meta.url ? path6.dirname(fileURLToPath(import.meta.url)) : "";
|
|
25888
|
+
var localXdgOpenPath = path6.join(__dirname2, "xdg-open");
|
|
25889
|
+
var { platform, arch } = process10;
|
|
25890
|
+
var tryEachApp = async (apps, opener) => {
|
|
25891
|
+
if (apps.length === 0) {
|
|
25892
|
+
return;
|
|
25893
|
+
}
|
|
25894
|
+
const errors3 = [];
|
|
25895
|
+
for (const app of apps) {
|
|
25896
|
+
try {
|
|
25897
|
+
return await opener(app);
|
|
25898
|
+
} catch (error40) {
|
|
25899
|
+
errors3.push(error40);
|
|
26256
25900
|
}
|
|
26257
|
-
|
|
26258
|
-
|
|
26259
|
-
|
|
26260
|
-
|
|
26261
|
-
|
|
26262
|
-
|
|
26263
|
-
|
|
26264
|
-
|
|
26265
|
-
|
|
25901
|
+
}
|
|
25902
|
+
throw new AggregateError(errors3, "Failed to open in all supported apps");
|
|
25903
|
+
};
|
|
25904
|
+
var baseOpen = async (options) => {
|
|
25905
|
+
options = {
|
|
25906
|
+
wait: false,
|
|
25907
|
+
background: false,
|
|
25908
|
+
newInstance: false,
|
|
25909
|
+
allowNonzeroExitCode: false,
|
|
25910
|
+
...options
|
|
25911
|
+
};
|
|
25912
|
+
const isFallbackAttempt = options[fallbackAttemptSymbol] === true;
|
|
25913
|
+
delete options[fallbackAttemptSymbol];
|
|
25914
|
+
if (Array.isArray(options.app)) {
|
|
25915
|
+
return tryEachApp(options.app, (singleApp) => baseOpen({
|
|
25916
|
+
...options,
|
|
25917
|
+
app: singleApp,
|
|
25918
|
+
[fallbackAttemptSymbol]: true
|
|
25919
|
+
}));
|
|
25920
|
+
}
|
|
25921
|
+
let { name: app, arguments: appArguments = [] } = options.app ?? {};
|
|
25922
|
+
appArguments = [...appArguments];
|
|
25923
|
+
if (Array.isArray(app)) {
|
|
25924
|
+
return tryEachApp(app, (appName) => baseOpen({
|
|
25925
|
+
...options,
|
|
25926
|
+
app: {
|
|
25927
|
+
name: appName,
|
|
25928
|
+
arguments: appArguments
|
|
25929
|
+
},
|
|
25930
|
+
[fallbackAttemptSymbol]: true
|
|
25931
|
+
}));
|
|
25932
|
+
}
|
|
25933
|
+
if (app === "browser" || app === "browserPrivate") {
|
|
25934
|
+
const ids = {
|
|
25935
|
+
"com.google.chrome": "chrome",
|
|
25936
|
+
"google-chrome.desktop": "chrome",
|
|
25937
|
+
"com.brave.browser": "brave",
|
|
25938
|
+
"org.mozilla.firefox": "firefox",
|
|
25939
|
+
"firefox.desktop": "firefox",
|
|
25940
|
+
"com.microsoft.msedge": "edge",
|
|
25941
|
+
"com.microsoft.edge": "edge",
|
|
25942
|
+
"com.microsoft.edgemac": "edge",
|
|
25943
|
+
"microsoft-edge.desktop": "edge",
|
|
25944
|
+
"com.apple.safari": "safari"
|
|
25945
|
+
};
|
|
25946
|
+
const flags = {
|
|
25947
|
+
chrome: "--incognito",
|
|
25948
|
+
brave: "--incognito",
|
|
25949
|
+
firefox: "--private-window",
|
|
25950
|
+
edge: "--inPrivate"
|
|
25951
|
+
};
|
|
25952
|
+
let browser;
|
|
25953
|
+
if (is_wsl_default) {
|
|
25954
|
+
const progId = await wslDefaultBrowser();
|
|
25955
|
+
const browserInfo = _windowsBrowserProgIdMap.get(progId);
|
|
25956
|
+
browser = browserInfo ?? {};
|
|
25957
|
+
} else {
|
|
25958
|
+
browser = await defaultBrowser2();
|
|
26266
25959
|
}
|
|
26267
|
-
|
|
26268
|
-
|
|
26269
|
-
|
|
25960
|
+
if (Object.hasOwn(ids, browser.id)) {
|
|
25961
|
+
const browserName = ids[browser.id.toLowerCase()];
|
|
25962
|
+
if (browserName === undefined) {
|
|
25963
|
+
throw new Error(`${browser.name} is not supported as a default browser`);
|
|
26270
25964
|
}
|
|
26271
|
-
if (
|
|
26272
|
-
|
|
25965
|
+
if (app === "browserPrivate") {
|
|
25966
|
+
if (browserName === "safari") {
|
|
25967
|
+
throw new Error("Safari doesn't support opening in private mode via command line");
|
|
25968
|
+
}
|
|
25969
|
+
appArguments.push(flags[browserName]);
|
|
26273
25970
|
}
|
|
26274
|
-
|
|
26275
|
-
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
|
|
26279
|
-
if (!mode)
|
|
26280
|
-
throw RequestError.invalidParams({ configId: request.configId }, `Unknown collaboration mode "${String(request.value)}"`);
|
|
26281
|
-
await codex.threadSettingsUpdate({
|
|
26282
|
-
threadId: session.id,
|
|
26283
|
-
collaborationMode: {
|
|
26284
|
-
mode,
|
|
26285
|
-
settings: { model: findModel(session.catalog, session.model.model)?.model ?? session.model.model, reasoning_effort: session.model.effort, developer_instructions: null }
|
|
25971
|
+
return baseOpen({
|
|
25972
|
+
...options,
|
|
25973
|
+
app: {
|
|
25974
|
+
name: apps[browserName],
|
|
25975
|
+
arguments: appArguments
|
|
26286
25976
|
}
|
|
26287
25977
|
});
|
|
26288
|
-
session.collaborationMode = mode;
|
|
26289
|
-
return;
|
|
26290
25978
|
}
|
|
26291
|
-
default
|
|
26292
|
-
throw RequestError.invalidParams({ configId: request.configId }, `Unknown config option "${request.configId}"`);
|
|
25979
|
+
throw new Error(`${browser.name} is not supported as a default browser`);
|
|
26293
25980
|
}
|
|
26294
|
-
|
|
26295
|
-
|
|
26296
|
-
|
|
26297
|
-
|
|
25981
|
+
let command;
|
|
25982
|
+
const cliArguments = [];
|
|
25983
|
+
const childProcessOptions = {};
|
|
25984
|
+
let shouldUseWindowsInWsl = false;
|
|
25985
|
+
if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
|
|
25986
|
+
shouldUseWindowsInWsl = await canAccessPowerShell();
|
|
26298
25987
|
}
|
|
26299
|
-
|
|
26300
|
-
|
|
26301
|
-
|
|
26302
|
-
|
|
26303
|
-
function historyUpdates(turns) {
|
|
26304
|
-
const updates = [];
|
|
26305
|
-
for (const turn of turns) {
|
|
26306
|
-
for (const item of turn.items) {
|
|
26307
|
-
updates.push(...itemSnapshot(item));
|
|
25988
|
+
if (platform === "darwin") {
|
|
25989
|
+
command = "open";
|
|
25990
|
+
if (options.wait) {
|
|
25991
|
+
cliArguments.push("--wait-apps");
|
|
26308
25992
|
}
|
|
26309
|
-
|
|
26310
|
-
|
|
26311
|
-
}
|
|
26312
|
-
|
|
26313
|
-
|
|
26314
|
-
|
|
26315
|
-
|
|
26316
|
-
|
|
26317
|
-
|
|
26318
|
-
|
|
26319
|
-
|
|
26320
|
-
|
|
26321
|
-
|
|
26322
|
-
|
|
25993
|
+
if (options.background) {
|
|
25994
|
+
cliArguments.push("--background");
|
|
25995
|
+
}
|
|
25996
|
+
if (options.newInstance) {
|
|
25997
|
+
cliArguments.push("--new");
|
|
25998
|
+
}
|
|
25999
|
+
if (app) {
|
|
26000
|
+
cliArguments.push("-a", app);
|
|
26001
|
+
}
|
|
26002
|
+
} else if (platform === "win32" || shouldUseWindowsInWsl) {
|
|
26003
|
+
command = await powerShellPath2();
|
|
26004
|
+
cliArguments.push(...executePowerShell2.argumentsPrefix);
|
|
26005
|
+
if (!is_wsl_default) {
|
|
26006
|
+
childProcessOptions.windowsVerbatimArguments = true;
|
|
26007
|
+
}
|
|
26008
|
+
if (is_wsl_default && options.target) {
|
|
26009
|
+
options.target = await convertWslPathToWindows(options.target);
|
|
26010
|
+
}
|
|
26011
|
+
const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
|
|
26012
|
+
if (options.wait) {
|
|
26013
|
+
encodedArguments.push("-Wait");
|
|
26014
|
+
}
|
|
26015
|
+
if (app) {
|
|
26016
|
+
encodedArguments.push(executePowerShell2.escapeArgument(app));
|
|
26017
|
+
if (options.target) {
|
|
26018
|
+
appArguments.push(options.target);
|
|
26019
|
+
}
|
|
26020
|
+
} else if (options.target) {
|
|
26021
|
+
encodedArguments.push(executePowerShell2.escapeArgument(options.target));
|
|
26022
|
+
}
|
|
26023
|
+
if (appArguments.length > 0) {
|
|
26024
|
+
appArguments = appArguments.map((argument) => executePowerShell2.escapeArgument(argument));
|
|
26025
|
+
encodedArguments.push("-ArgumentList", appArguments.join(","));
|
|
26026
|
+
}
|
|
26027
|
+
options.target = executePowerShell2.encodeCommand(encodedArguments.join(" "));
|
|
26028
|
+
if (!options.wait) {
|
|
26029
|
+
childProcessOptions.stdio = "ignore";
|
|
26030
|
+
}
|
|
26031
|
+
if (is_wsl_default) {
|
|
26032
|
+
childProcessOptions.cwd = path6.dirname(command);
|
|
26033
|
+
}
|
|
26034
|
+
} else {
|
|
26035
|
+
if (app) {
|
|
26036
|
+
command = app;
|
|
26037
|
+
} else {
|
|
26038
|
+
const isBundled = !__dirname2 || __dirname2 === "/";
|
|
26039
|
+
let exeLocalXdgOpen = false;
|
|
26040
|
+
try {
|
|
26041
|
+
await fs6.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
26042
|
+
exeLocalXdgOpen = true;
|
|
26043
|
+
} catch {}
|
|
26044
|
+
const useSystemXdgOpen = process10.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
|
|
26045
|
+
command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
|
|
26046
|
+
}
|
|
26047
|
+
if (appArguments.length > 0) {
|
|
26048
|
+
cliArguments.push(...appArguments);
|
|
26049
|
+
}
|
|
26050
|
+
if (!options.wait) {
|
|
26051
|
+
childProcessOptions.stdio = "ignore";
|
|
26052
|
+
childProcessOptions.detached = true;
|
|
26323
26053
|
}
|
|
26324
26054
|
}
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
}
|
|
26328
|
-
function firstLine(text2) {
|
|
26329
|
-
const line = text2.split(/\r?\n/).map((part) => part.trim()).find((part) => part.length > 0) ?? text2;
|
|
26330
|
-
return line.length > 120 ? `${line.slice(0, 117)}...` : line;
|
|
26331
|
-
}
|
|
26332
|
-
|
|
26333
|
-
// src/agent/providers.ts
|
|
26334
|
-
import { readFileSync } from "fs";
|
|
26335
|
-
import os2 from "os";
|
|
26336
|
-
import path6 from "path";
|
|
26337
|
-
var OPENAI_PROVIDER_ID = "openai";
|
|
26338
|
-
var GATEWAY_MODEL_PROVIDER = "custom-gateway";
|
|
26339
|
-
var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
26340
|
-
var GATEWAY_ID_PATTERN = /^[a-z0-9-]+$/;
|
|
26341
|
-
function gatewayModelId(connectionId, modelId) {
|
|
26342
|
-
return `gateway:${encodeURIComponent(connectionId)}:${encodeURIComponent(modelId)}`;
|
|
26343
|
-
}
|
|
26344
|
-
|
|
26345
|
-
class ProviderRouting {
|
|
26346
|
-
baseConfig;
|
|
26347
|
-
configuredProvider;
|
|
26348
|
-
gateways = new Map;
|
|
26349
|
-
constructor(baseConfig, configuredProvider) {
|
|
26350
|
-
this.baseConfig = baseConfig;
|
|
26351
|
-
this.configuredProvider = configuredProvider;
|
|
26055
|
+
if (platform === "darwin" && appArguments.length > 0) {
|
|
26056
|
+
cliArguments.push("--args", ...appArguments);
|
|
26352
26057
|
}
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
copy.gateways = new Map(this.gateways);
|
|
26356
|
-
return copy;
|
|
26058
|
+
if (options.target) {
|
|
26059
|
+
cliArguments.push(options.target);
|
|
26357
26060
|
}
|
|
26358
|
-
|
|
26359
|
-
|
|
26061
|
+
const subprocess = childProcess4.spawn(command, cliArguments, childProcessOptions);
|
|
26062
|
+
if (options.wait) {
|
|
26063
|
+
return new Promise((resolve, reject) => {
|
|
26064
|
+
subprocess.once("error", reject);
|
|
26065
|
+
subprocess.once("close", (exitCode) => {
|
|
26066
|
+
if (!options.allowNonzeroExitCode && exitCode !== 0) {
|
|
26067
|
+
reject(new Error(`Exited with code ${exitCode}`));
|
|
26068
|
+
return;
|
|
26069
|
+
}
|
|
26070
|
+
resolve(subprocess);
|
|
26071
|
+
});
|
|
26072
|
+
});
|
|
26073
|
+
}
|
|
26074
|
+
if (isFallbackAttempt) {
|
|
26075
|
+
return new Promise((resolve, reject) => {
|
|
26076
|
+
subprocess.once("error", reject);
|
|
26077
|
+
subprocess.once("spawn", () => {
|
|
26078
|
+
subprocess.once("close", (exitCode) => {
|
|
26079
|
+
subprocess.off("error", reject);
|
|
26080
|
+
if (exitCode !== 0) {
|
|
26081
|
+
reject(new Error(`Exited with code ${exitCode}`));
|
|
26082
|
+
return;
|
|
26083
|
+
}
|
|
26084
|
+
subprocess.unref();
|
|
26085
|
+
resolve(subprocess);
|
|
26086
|
+
});
|
|
26087
|
+
});
|
|
26088
|
+
});
|
|
26089
|
+
}
|
|
26090
|
+
subprocess.unref();
|
|
26091
|
+
return new Promise((resolve, reject) => {
|
|
26092
|
+
subprocess.once("error", reject);
|
|
26093
|
+
subprocess.once("spawn", () => {
|
|
26094
|
+
subprocess.off("error", reject);
|
|
26095
|
+
resolve(subprocess);
|
|
26096
|
+
});
|
|
26097
|
+
});
|
|
26098
|
+
};
|
|
26099
|
+
var open = (target, options) => {
|
|
26100
|
+
if (typeof target !== "string") {
|
|
26101
|
+
throw new TypeError("Expected a `target`");
|
|
26360
26102
|
}
|
|
26361
|
-
|
|
26362
|
-
|
|
26103
|
+
return baseOpen({
|
|
26104
|
+
...options,
|
|
26105
|
+
target
|
|
26106
|
+
});
|
|
26107
|
+
};
|
|
26108
|
+
function detectArchBinary(binary) {
|
|
26109
|
+
if (typeof binary === "string" || Array.isArray(binary)) {
|
|
26110
|
+
return binary;
|
|
26363
26111
|
}
|
|
26364
|
-
|
|
26365
|
-
|
|
26112
|
+
const { [arch]: archBinary } = binary;
|
|
26113
|
+
if (!archBinary) {
|
|
26114
|
+
throw new Error(`${arch} is not supported`);
|
|
26366
26115
|
}
|
|
26367
|
-
|
|
26368
|
-
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
return
|
|
26372
|
-
providers: [{
|
|
26373
|
-
providerId: OPENAI_PROVIDER_ID,
|
|
26374
|
-
supported: ["openai"],
|
|
26375
|
-
required: false,
|
|
26376
|
-
current: routed && first ? { apiType: "openai", baseUrl: first.baseUrl } : { apiType: "openai", baseUrl: this.nativeBaseUrl() },
|
|
26377
|
-
...first && !routed ? { _meta: { codex: { mode: "catalog", gateway: summary(first), gateways: [...this.gateways.values()].map(summary) } } } : {}
|
|
26378
|
-
}]
|
|
26379
|
-
};
|
|
26116
|
+
return archBinary;
|
|
26117
|
+
}
|
|
26118
|
+
function detectPlatformBinary({ [platform]: platformBinary }, { wsl } = {}) {
|
|
26119
|
+
if (wsl && is_wsl_default) {
|
|
26120
|
+
return detectArchBinary(wsl);
|
|
26380
26121
|
}
|
|
26381
|
-
|
|
26382
|
-
|
|
26383
|
-
throw RequestError.invalidParams({ providerId: request.providerId }, `Unknown providerId "${request.providerId}"; only "${OPENAI_PROVIDER_ID}" is configurable`);
|
|
26384
|
-
}
|
|
26385
|
-
if (request.apiType !== "openai") {
|
|
26386
|
-
throw RequestError.invalidParams({ apiType: request.apiType }, `Codex only speaks the OpenAI protocol; got apiType "${request.apiType}"`);
|
|
26387
|
-
}
|
|
26388
|
-
const baseUrl = typeof request.baseUrl === "string" ? request.baseUrl.trim() : "";
|
|
26389
|
-
if (!/^https?:\/\//.test(baseUrl)) {
|
|
26390
|
-
throw RequestError.invalidParams({ baseUrl: request.baseUrl }, "baseUrl must be an http(s) URL");
|
|
26391
|
-
}
|
|
26392
|
-
const hints = readHints(request._meta);
|
|
26393
|
-
if (hints.id === OPENAI_PROVIDER_ID || hints.id === this.configuredProvider) {
|
|
26394
|
-
throw RequestError.invalidParams({ id: hints.id }, `_meta.codex.id "${hints.id}" is Codex's own provider; pick another gateway id`);
|
|
26395
|
-
}
|
|
26396
|
-
if (hints.mode === "route" || this.mode !== hints.mode)
|
|
26397
|
-
this.gateways.clear();
|
|
26398
|
-
this.gateways.set(hints.id, {
|
|
26399
|
-
id: hints.id,
|
|
26400
|
-
baseUrl,
|
|
26401
|
-
headers: { ...request.headers ?? {} },
|
|
26402
|
-
name: hints.name ?? "Client-configured gateway",
|
|
26403
|
-
models: hints.models,
|
|
26404
|
-
model: hints.model,
|
|
26405
|
-
bearerToken: hints.bearerToken,
|
|
26406
|
-
config: hints.config,
|
|
26407
|
-
mode: hints.mode,
|
|
26408
|
-
catalogEntries: hints.catalogEntries
|
|
26409
|
-
});
|
|
26122
|
+
if (!platformBinary) {
|
|
26123
|
+
throw new Error(`${platform} is not supported`);
|
|
26410
26124
|
}
|
|
26411
|
-
|
|
26412
|
-
|
|
26125
|
+
return detectArchBinary(platformBinary);
|
|
26126
|
+
}
|
|
26127
|
+
var apps = {
|
|
26128
|
+
browser: "browser",
|
|
26129
|
+
browserPrivate: "browserPrivate"
|
|
26130
|
+
};
|
|
26131
|
+
defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
|
|
26132
|
+
darwin: "google chrome",
|
|
26133
|
+
win32: "chrome",
|
|
26134
|
+
linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
|
|
26135
|
+
}, {
|
|
26136
|
+
wsl: {
|
|
26137
|
+
ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
|
|
26138
|
+
x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
|
|
26413
26139
|
}
|
|
26414
|
-
|
|
26415
|
-
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
26419
|
-
|
|
26140
|
+
}));
|
|
26141
|
+
defineLazyProperty(apps, "brave", () => detectPlatformBinary({
|
|
26142
|
+
darwin: "brave browser",
|
|
26143
|
+
win32: "brave",
|
|
26144
|
+
linux: ["brave-browser", "brave"]
|
|
26145
|
+
}, {
|
|
26146
|
+
wsl: {
|
|
26147
|
+
ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
|
|
26148
|
+
x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
|
|
26420
26149
|
}
|
|
26421
|
-
|
|
26422
|
-
|
|
26150
|
+
}));
|
|
26151
|
+
defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
|
|
26152
|
+
darwin: "firefox",
|
|
26153
|
+
win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
|
|
26154
|
+
linux: "firefox"
|
|
26155
|
+
}, {
|
|
26156
|
+
wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
|
|
26157
|
+
}));
|
|
26158
|
+
defineLazyProperty(apps, "edge", () => detectPlatformBinary({
|
|
26159
|
+
darwin: "microsoft edge",
|
|
26160
|
+
win32: "msedge",
|
|
26161
|
+
linux: ["microsoft-edge", "microsoft-edge-dev"]
|
|
26162
|
+
}, {
|
|
26163
|
+
wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
|
|
26164
|
+
}));
|
|
26165
|
+
defineLazyProperty(apps, "safari", () => detectPlatformBinary({
|
|
26166
|
+
darwin: "Safari"
|
|
26167
|
+
}));
|
|
26168
|
+
var open_default = open;
|
|
26169
|
+
|
|
26170
|
+
// src/agent/auth.ts
|
|
26171
|
+
var AuthMethodId = {
|
|
26172
|
+
ApiKey: "api-key",
|
|
26173
|
+
ChatGpt: "chat-gpt",
|
|
26174
|
+
ChatGptDeviceCode: "chat-gpt-device-code"
|
|
26175
|
+
};
|
|
26176
|
+
var CODEX_API_KEY_ENV = "CODEX_API_KEY";
|
|
26177
|
+
var OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
|
|
26178
|
+
function authMethods(capabilities, env = process.env) {
|
|
26179
|
+
const methods2 = [{
|
|
26180
|
+
type: "agent",
|
|
26181
|
+
methodId: AuthMethodId.ApiKey,
|
|
26182
|
+
name: "API key",
|
|
26183
|
+
description: `Use the API key from ${CODEX_API_KEY_ENV} or ${OPENAI_API_KEY_ENV}, or pass it in _meta["api-key"].apiKey`
|
|
26184
|
+
}];
|
|
26185
|
+
if (!env["NO_BROWSER"]) {
|
|
26186
|
+
methods2.push({
|
|
26187
|
+
type: "agent",
|
|
26188
|
+
methodId: AuthMethodId.ChatGpt,
|
|
26189
|
+
name: "ChatGPT",
|
|
26190
|
+
description: "Sign in to ChatGPT in your browser"
|
|
26191
|
+
});
|
|
26423
26192
|
}
|
|
26424
|
-
|
|
26425
|
-
|
|
26193
|
+
if (capabilities.urlElicitation) {
|
|
26194
|
+
methods2.push({
|
|
26195
|
+
type: "agent",
|
|
26196
|
+
methodId: AuthMethodId.ChatGptDeviceCode,
|
|
26197
|
+
name: "ChatGPT (device code)",
|
|
26198
|
+
description: "Sign in to ChatGPT by opening a verification page and entering a one-time code"
|
|
26199
|
+
});
|
|
26426
26200
|
}
|
|
26427
|
-
|
|
26428
|
-
|
|
26429
|
-
|
|
26430
|
-
|
|
26201
|
+
return methods2;
|
|
26202
|
+
}
|
|
26203
|
+
async function login(codex, link, request, requestId, env = process.env) {
|
|
26204
|
+
switch (request.methodId) {
|
|
26205
|
+
case AuthMethodId.ApiKey:
|
|
26206
|
+
return await loginWithApiKey(codex, request, env);
|
|
26207
|
+
case AuthMethodId.ChatGpt:
|
|
26208
|
+
return await loginWithChatGpt(codex);
|
|
26209
|
+
case AuthMethodId.ChatGptDeviceCode:
|
|
26210
|
+
return await loginWithDeviceCode(codex, link, requestId);
|
|
26211
|
+
default:
|
|
26212
|
+
throw RequestError.invalidParams({ methodId: request.methodId }, `Unknown auth method "${request.methodId}"; expected one of ${Object.values(AuthMethodId).join(", ")}`);
|
|
26431
26213
|
}
|
|
26432
|
-
|
|
26433
|
-
|
|
26214
|
+
}
|
|
26215
|
+
async function loginWithApiKey(codex, request, env) {
|
|
26216
|
+
const meta = request._meta?.["api-key"];
|
|
26217
|
+
const fromMeta = typeof meta === "object" && meta !== null ? meta.apiKey : undefined;
|
|
26218
|
+
const apiKey = typeof fromMeta === "string" && fromMeta.trim().length > 0 ? fromMeta.trim() : env[CODEX_API_KEY_ENV]?.trim() || env[OPENAI_API_KEY_ENV]?.trim();
|
|
26219
|
+
if (!apiKey) {
|
|
26220
|
+
throw RequestError.invalidParams({ envVars: [CODEX_API_KEY_ENV, OPENAI_API_KEY_ENV] }, `No API key: set ${CODEX_API_KEY_ENV} or ${OPENAI_API_KEY_ENV}, or pass _meta["api-key"].apiKey`);
|
|
26434
26221
|
}
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
|
|
26222
|
+
await codex.withNotification("account/login/completed", async (completed) => {
|
|
26223
|
+
await codex.accountLogin({ type: "apiKey", apiKey });
|
|
26224
|
+
const result = await completed;
|
|
26225
|
+
if (!result.success)
|
|
26226
|
+
throw RequestError.authRequired({ error: result.error }, result.error ?? "API key login failed");
|
|
26227
|
+
});
|
|
26228
|
+
}
|
|
26229
|
+
async function loginWithChatGpt(codex) {
|
|
26230
|
+
const account = await codex.accountRead({ refreshToken: true });
|
|
26231
|
+
if (account.account?.type === "chatgpt")
|
|
26232
|
+
return;
|
|
26233
|
+
await codex.withNotification("account/login/completed", async (completed) => {
|
|
26234
|
+
const started = await codex.accountLogin({ type: "chatgpt" });
|
|
26235
|
+
if (started.type !== "chatgpt")
|
|
26236
|
+
throw RequestError.internalError({ type: started.type }, "Unexpected login response");
|
|
26237
|
+
logger.log("opening browser for ChatGPT login", { loginId: started.loginId });
|
|
26238
|
+
await open_default(started.authUrl);
|
|
26239
|
+
const result = await completed;
|
|
26240
|
+
if (!result.success)
|
|
26241
|
+
throw RequestError.authRequired({ error: result.error }, result.error ?? "ChatGPT login failed");
|
|
26242
|
+
});
|
|
26243
|
+
}
|
|
26244
|
+
async function loginWithDeviceCode(codex, link, requestId) {
|
|
26245
|
+
const account = await codex.accountRead({ refreshToken: true });
|
|
26246
|
+
if (account.account?.type === "chatgpt")
|
|
26247
|
+
return;
|
|
26248
|
+
await codex.withNotification("account/login/completed", async (completed) => {
|
|
26249
|
+
const started = await codex.accountLogin({ type: "chatgptDeviceCode" });
|
|
26250
|
+
if (started.type !== "chatgptDeviceCode")
|
|
26251
|
+
throw RequestError.internalError({ type: started.type }, "Unexpected login response");
|
|
26252
|
+
const elicitation = link.request(methods.client.elicitation.create, {
|
|
26253
|
+
mode: "url",
|
|
26254
|
+
requestId,
|
|
26255
|
+
elicitationId: started.loginId,
|
|
26256
|
+
url: started.verificationUrl,
|
|
26257
|
+
message: `Sign in to ChatGPT and enter this code: ${started.userCode}`
|
|
26258
|
+
});
|
|
26259
|
+
elicitation.catch(() => {});
|
|
26260
|
+
const first = await Promise.race([
|
|
26261
|
+
completed.then((result2) => ({ kind: "completed", result: result2 })),
|
|
26262
|
+
elicitation.then((response) => ({ kind: "elicitation", response }))
|
|
26263
|
+
]);
|
|
26264
|
+
if (first.kind === "elicitation" && !CreateElicitationResponse.isAccept(first.response)) {
|
|
26265
|
+
await codex.accountLoginCancel({ loginId: started.loginId });
|
|
26266
|
+
throw RequestError.authRequired(undefined, "ChatGPT device code login was cancelled");
|
|
26267
|
+
}
|
|
26268
|
+
const result = first.kind === "completed" ? first.result : await completed;
|
|
26269
|
+
await link.notify(methods.client.elicitation.complete, { elicitationId: started.loginId });
|
|
26270
|
+
if (!result.success)
|
|
26271
|
+
throw RequestError.authRequired({ error: result.error }, result.error ?? "ChatGPT login failed");
|
|
26272
|
+
});
|
|
26273
|
+
}
|
|
26274
|
+
async function logout(codex) {
|
|
26275
|
+
await codex.withNotification("account/updated", async (updated) => {
|
|
26276
|
+
await codex.accountLogout();
|
|
26277
|
+
await updated;
|
|
26278
|
+
});
|
|
26279
|
+
}
|
|
26280
|
+
|
|
26281
|
+
// src/agent/clientSession.ts
|
|
26282
|
+
class ClientSession {
|
|
26283
|
+
sessionId;
|
|
26284
|
+
link;
|
|
26285
|
+
capabilities;
|
|
26286
|
+
waiting = 0;
|
|
26287
|
+
turnActive = false;
|
|
26288
|
+
turnGeneration = 0;
|
|
26289
|
+
disposed = false;
|
|
26290
|
+
output = new AbortController;
|
|
26291
|
+
terminalAttempted = false;
|
|
26292
|
+
messageId;
|
|
26293
|
+
get signal() {
|
|
26294
|
+
return this.output.signal;
|
|
26443
26295
|
}
|
|
26444
|
-
|
|
26445
|
-
|
|
26296
|
+
constructor(sessionId, link, capabilities) {
|
|
26297
|
+
this.sessionId = sessionId;
|
|
26298
|
+
this.link = link;
|
|
26299
|
+
this.capabilities = capabilities;
|
|
26300
|
+
}
|
|
26301
|
+
async update(update) {
|
|
26302
|
+
if (this.disposed)
|
|
26446
26303
|
return;
|
|
26447
|
-
|
|
26448
|
-
if (id === null)
|
|
26449
|
-
this.gateways.clear();
|
|
26450
|
-
else
|
|
26451
|
-
this.gateways.delete(id);
|
|
26304
|
+
await abortable(this.link.notify(methods.client.session.update, { sessionId: this.sessionId, update }), this.output.signal);
|
|
26452
26305
|
}
|
|
26453
|
-
|
|
26454
|
-
|
|
26306
|
+
async updateAll(updates) {
|
|
26307
|
+
for (const update of updates)
|
|
26308
|
+
await this.update(update);
|
|
26455
26309
|
}
|
|
26456
|
-
|
|
26457
|
-
|
|
26310
|
+
async requestPermission(request, signal) {
|
|
26311
|
+
signal = signal ? AbortSignal.any([signal, this.output.signal]) : this.output.signal;
|
|
26312
|
+
signal.throwIfAborted();
|
|
26313
|
+
return await this.waitingOnClient(() => {
|
|
26314
|
+
signal?.throwIfAborted();
|
|
26315
|
+
const pending = this.link.request(methods.client.session.requestPermission, { sessionId: this.sessionId, ...request }, signal ? { cancellationSignal: signal } : undefined);
|
|
26316
|
+
return signal ? abortable(pending, signal) : pending;
|
|
26317
|
+
}, signal);
|
|
26458
26318
|
}
|
|
26459
|
-
|
|
26460
|
-
|
|
26461
|
-
return first !== null && first.mode === "route" ? first.id : null;
|
|
26319
|
+
async requestTool(params, signal) {
|
|
26320
|
+
return await this.link.request("_alwith/tool/call", params, { cancellationSignal: signal });
|
|
26462
26321
|
}
|
|
26463
|
-
|
|
26464
|
-
|
|
26465
|
-
|
|
26466
|
-
|
|
26467
|
-
|
|
26322
|
+
async createElicitation(request, signal) {
|
|
26323
|
+
signal = signal ? AbortSignal.any([signal, this.output.signal]) : this.output.signal;
|
|
26324
|
+
signal.throwIfAborted();
|
|
26325
|
+
return await this.waitingOnClient(() => {
|
|
26326
|
+
signal?.throwIfAborted();
|
|
26327
|
+
const pending = this.link.request(methods.client.elicitation.create, request, signal ? { cancellationSignal: signal } : undefined);
|
|
26328
|
+
return signal ? abortable(pending, signal) : pending;
|
|
26329
|
+
}, signal);
|
|
26468
26330
|
}
|
|
26469
|
-
|
|
26470
|
-
|
|
26331
|
+
async completeElicitation(elicitationId) {
|
|
26332
|
+
if (this.disposed)
|
|
26333
|
+
return;
|
|
26334
|
+
await abortable(this.link.notify(methods.client.elicitation.complete, { elicitationId }), this.output.signal);
|
|
26471
26335
|
}
|
|
26472
|
-
|
|
26473
|
-
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26336
|
+
reportRunning(messageId) {
|
|
26337
|
+
this.messageId = messageId;
|
|
26338
|
+
this.terminalAttempted = false;
|
|
26339
|
+
this.turnGeneration += 1;
|
|
26340
|
+
this.turnActive = true;
|
|
26341
|
+
this.waiting = 0;
|
|
26342
|
+
this.state({ sessionUpdate: "state_update", state: "running" });
|
|
26478
26343
|
}
|
|
26479
|
-
|
|
26480
|
-
|
|
26481
|
-
|
|
26482
|
-
|
|
26483
|
-
|
|
26484
|
-
|
|
26485
|
-
|
|
26486
|
-
|
|
26487
|
-
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26344
|
+
async reportIdle(stopReason, extra, messageId = this.messageId) {
|
|
26345
|
+
if (messageId !== this.messageId)
|
|
26346
|
+
return;
|
|
26347
|
+
if (this.terminalAttempted)
|
|
26348
|
+
return;
|
|
26349
|
+
this.terminalAttempted = true;
|
|
26350
|
+
this.turnActive = false;
|
|
26351
|
+
this.waiting = 0;
|
|
26352
|
+
await this.update(this.correlated({
|
|
26353
|
+
sessionUpdate: "state_update",
|
|
26354
|
+
state: "idle",
|
|
26355
|
+
stopReason,
|
|
26356
|
+
...extra?.usage === undefined ? {} : { usage: extra.usage },
|
|
26357
|
+
...extra?._meta === undefined ? {} : { _meta: extra._meta }
|
|
26358
|
+
}));
|
|
26492
26359
|
}
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26496
|
-
|
|
26497
|
-
|
|
26498
|
-
|
|
26499
|
-
http_headers: { "X-Client-Feature-ID": "codex", ...gateway.headers }
|
|
26500
|
-
};
|
|
26501
|
-
if (gateway.bearerToken !== null)
|
|
26502
|
-
entry["experimental_bearer_token"] = gateway.bearerToken;
|
|
26503
|
-
return {
|
|
26504
|
-
...config2,
|
|
26505
|
-
...gateway.config,
|
|
26506
|
-
model_providers: { ...existing, [gateway.id]: entry }
|
|
26507
|
-
};
|
|
26360
|
+
correlated(update) {
|
|
26361
|
+
if (this.messageId === undefined)
|
|
26362
|
+
return update;
|
|
26363
|
+
const meta = update._meta;
|
|
26364
|
+
const alwith = meta?.["alwith"];
|
|
26365
|
+
return { ...update, _meta: { ...meta, alwith: { ...alwith, messageId: this.messageId } } };
|
|
26508
26366
|
}
|
|
26509
|
-
|
|
26510
|
-
|
|
26511
|
-
|
|
26512
|
-
|
|
26513
|
-
|
|
26514
|
-
if (
|
|
26515
|
-
|
|
26516
|
-
|
|
26367
|
+
async waitingOnClient(operation, signal) {
|
|
26368
|
+
if (this.disposed)
|
|
26369
|
+
throw new Error("Session is closed");
|
|
26370
|
+
const generation = this.turnActive ? this.turnGeneration : null;
|
|
26371
|
+
try {
|
|
26372
|
+
if (generation !== null && this.waiting++ === 0) {
|
|
26373
|
+
const pending = this.state({ sessionUpdate: "state_update", state: "requires_action" });
|
|
26374
|
+
await (signal ? abortable(pending, signal) : pending);
|
|
26375
|
+
}
|
|
26376
|
+
return await operation();
|
|
26377
|
+
} finally {
|
|
26378
|
+
if (this.turnActive && generation === this.turnGeneration && --this.waiting === 0) {
|
|
26379
|
+
const pending = this.state({ sessionUpdate: "state_update", state: "running" });
|
|
26380
|
+
await (signal ? abortable(pending, signal).catch(() => {}) : pending);
|
|
26381
|
+
}
|
|
26517
26382
|
}
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
|
|
26383
|
+
}
|
|
26384
|
+
async state(update) {
|
|
26385
|
+
try {
|
|
26386
|
+
await this.update(this.correlated(update));
|
|
26387
|
+
} catch (error40) {
|
|
26388
|
+
logger.error("Failed to publish session state", error40, { sessionId: this.sessionId });
|
|
26522
26389
|
}
|
|
26523
|
-
return [...codexCatalog, ...own];
|
|
26524
26390
|
}
|
|
26525
|
-
|
|
26526
|
-
|
|
26527
|
-
|
|
26528
|
-
|
|
26529
|
-
|
|
26530
|
-
|
|
26531
|
-
|
|
26391
|
+
dispose() {
|
|
26392
|
+
this.disposed = true;
|
|
26393
|
+
this.output.abort();
|
|
26394
|
+
this.turnActive = false;
|
|
26395
|
+
this.waiting = 0;
|
|
26396
|
+
}
|
|
26397
|
+
}
|
|
26398
|
+
|
|
26399
|
+
// src/agent/commands.ts
|
|
26400
|
+
var BUILTIN_COMMANDS = [
|
|
26401
|
+
{ name: "plan", description: "Toggle plan mode: Codex proposes a plan before making changes" },
|
|
26402
|
+
{ name: "review", description: "Review uncommitted changes, or review with custom instructions", input: { type: "text", hint: "optional review instructions" } },
|
|
26403
|
+
{ name: "review-branch", description: "Review changes relative to a base branch", input: { type: "text", hint: "branch name" } },
|
|
26404
|
+
{ name: "review-commit", description: "Review a specific commit", input: { type: "text", hint: "commit sha" } },
|
|
26405
|
+
{ name: "compact", description: "Summarize the conversation to free up context" },
|
|
26406
|
+
{ name: "status", description: "Show session configuration, token usage, and rate limits" },
|
|
26407
|
+
{ name: "mcp", description: "List configured Model Context Protocol (MCP) servers" },
|
|
26408
|
+
{ name: "skills", description: "List available skills" },
|
|
26409
|
+
{ name: "logout", description: "Sign out of the Codex account" }
|
|
26410
|
+
];
|
|
26411
|
+
function parseCommand(prompt) {
|
|
26412
|
+
const first = prompt[0];
|
|
26413
|
+
if (!first || first.type !== "text")
|
|
26414
|
+
return null;
|
|
26415
|
+
const text2 = first.text.trim();
|
|
26416
|
+
if (!text2.startsWith("/"))
|
|
26417
|
+
return null;
|
|
26418
|
+
const body = text2.slice(1).trim();
|
|
26419
|
+
const [name] = body.split(/\s+/);
|
|
26420
|
+
if (!name || name.startsWith("$"))
|
|
26421
|
+
return null;
|
|
26422
|
+
return { name: name.toLowerCase(), rest: body.slice(name.length).trim() };
|
|
26423
|
+
}
|
|
26424
|
+
function resolveCommand(command, session) {
|
|
26425
|
+
switch (command.name) {
|
|
26426
|
+
case "plan":
|
|
26427
|
+
return {
|
|
26428
|
+
kind: "config",
|
|
26429
|
+
configId: COLLABORATION_MODE_CONFIG_ID,
|
|
26430
|
+
value: session.collaborationMode === PLAN_COLLABORATION_MODE ? DEFAULT_COLLABORATION_MODE : PLAN_COLLABORATION_MODE
|
|
26431
|
+
};
|
|
26432
|
+
case "compact":
|
|
26433
|
+
return { kind: "compact" };
|
|
26434
|
+
case "review":
|
|
26435
|
+
return { kind: "review", target: command.rest.length > 0 ? { type: "custom", instructions: command.rest } : { type: "uncommittedChanges" } };
|
|
26436
|
+
case "review-branch":
|
|
26437
|
+
return command.rest.length > 0 ? { kind: "review", target: { type: "baseBranch", branch: command.rest } } : { kind: "message", text: usage("review-branch", "<branch name>") };
|
|
26438
|
+
case "review-commit":
|
|
26439
|
+
return command.rest.length > 0 ? { kind: "review", target: { type: "commit", sha: command.rest, title: null } } : { kind: "message", text: usage("review-commit", "<commit sha>") };
|
|
26440
|
+
case "logout":
|
|
26441
|
+
return { kind: "logout" };
|
|
26442
|
+
case "status":
|
|
26443
|
+
case "mcp":
|
|
26444
|
+
case "skills":
|
|
26445
|
+
return { kind: "message", text: "" };
|
|
26446
|
+
default:
|
|
26447
|
+
return { kind: "prompt" };
|
|
26448
|
+
}
|
|
26449
|
+
}
|
|
26450
|
+
function usage(name, hint) {
|
|
26451
|
+
return `Usage: /${name} ${hint}`;
|
|
26452
|
+
}
|
|
26453
|
+
function availableCommands(skills) {
|
|
26454
|
+
const commands = new Map;
|
|
26455
|
+
for (const builtin of BUILTIN_COMMANDS)
|
|
26456
|
+
commands.set(builtin.name, builtin);
|
|
26457
|
+
for (const entry of skills) {
|
|
26458
|
+
for (const skill of entry.skills) {
|
|
26459
|
+
const name = `$${skill.name}`;
|
|
26460
|
+
if (commands.has(name))
|
|
26461
|
+
continue;
|
|
26462
|
+
commands.set(name, { name, description: skill.shortDescription || skill.description || skill.name });
|
|
26532
26463
|
}
|
|
26533
|
-
|
|
26464
|
+
}
|
|
26465
|
+
return [...commands.values()];
|
|
26466
|
+
}
|
|
26467
|
+
function skillsMessage(skills) {
|
|
26468
|
+
const lines = skills.flatMap((entry) => entry.skills).map((skill) => {
|
|
26469
|
+
const description = skill.shortDescription || skill.description;
|
|
26470
|
+
return description ? `- ${skill.name}: ${description}` : `- ${skill.name}`;
|
|
26471
|
+
});
|
|
26472
|
+
return lines.length > 0 ? ["Available skills:", ...lines].join(`
|
|
26473
|
+
`) : "No skills configured.";
|
|
26474
|
+
}
|
|
26475
|
+
async function mcpMessage(codex, session) {
|
|
26476
|
+
const servers = await codex.mcpServerStatusList({});
|
|
26477
|
+
const lines = servers.data.map((server) => {
|
|
26478
|
+
const tools = Object.keys(server.tools ?? {}).length;
|
|
26479
|
+
const resources = (server.resources ?? []).length;
|
|
26480
|
+
return `- ${server.name}: ${tools} tools, ${resources} resources, auth=${server.authStatus}`;
|
|
26481
|
+
});
|
|
26482
|
+
const sessionLines = session.mcpServerNames.map((name) => `- ${name}`);
|
|
26483
|
+
return [
|
|
26484
|
+
lines.length > 0 ? "Configured MCP servers:" : "No MCP servers configured.",
|
|
26485
|
+
...lines,
|
|
26486
|
+
...sessionLines.length > 0 ? ["", "Session MCP servers:", ...sessionLines] : []
|
|
26487
|
+
].join(`
|
|
26488
|
+
`);
|
|
26489
|
+
}
|
|
26490
|
+
function statusMessage(session, rateLimits) {
|
|
26491
|
+
const usage2 = session.lastUsage;
|
|
26492
|
+
const lines = [
|
|
26493
|
+
"**Session**",
|
|
26494
|
+
`**Session ID:** ${session.id}`,
|
|
26495
|
+
`**Working directory:** ${session.cwd}`,
|
|
26496
|
+
`**Model:** ${session.model.model} (${session.model.effort})`,
|
|
26497
|
+
`**Mode:** ${session.mode.name}`,
|
|
26498
|
+
`**Collaboration:** ${session.collaborationMode}`,
|
|
26499
|
+
`**Fast mode:** ${session.fastMode ? "on" : "off"}`,
|
|
26500
|
+
`**Account:** ${accountInfo(session)}`,
|
|
26501
|
+
"",
|
|
26502
|
+
"**Usage**",
|
|
26503
|
+
`**Last turn:** ${usage2 ? `${formatTokenCount(usage2.totalTokens)} total (${formatTokenCount(usage2.inputTokens)} input + ${formatTokenCount(usage2.cachedInputTokens)} cached, ${formatTokenCount(usage2.outputTokens)} output)` : "not available yet"}`,
|
|
26504
|
+
`**Context window:** ${usage2 && session.contextWindow ? `${Math.round((session.contextWindow - usage2.totalTokens) / session.contextWindow * 100)}% left (${formatTokenCount(usage2.totalTokens)} / ${formatTokenCount(session.contextWindow)})` : "not available yet"}`,
|
|
26505
|
+
...rateLimitLines(rateLimits)
|
|
26506
|
+
];
|
|
26507
|
+
return lines.join(`
|
|
26508
|
+
`);
|
|
26509
|
+
}
|
|
26510
|
+
function accountInfo(session) {
|
|
26511
|
+
const account = session.account;
|
|
26512
|
+
if (!account)
|
|
26513
|
+
return "not logged in";
|
|
26514
|
+
switch (account.type) {
|
|
26515
|
+
case "apiKey":
|
|
26516
|
+
return "API key";
|
|
26517
|
+
case "chatgpt":
|
|
26518
|
+
return `ChatGPT ${account.planType}${account.email ? ` (${account.email})` : ""}`;
|
|
26519
|
+
case "amazonBedrock":
|
|
26520
|
+
return "Amazon Bedrock";
|
|
26534
26521
|
}
|
|
26535
26522
|
}
|
|
26536
|
-
function
|
|
26537
|
-
const
|
|
26538
|
-
const
|
|
26539
|
-
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
if (Array.isArray(alwith["models"])) {
|
|
26543
|
-
for (const entry of alwith["models"]) {
|
|
26544
|
-
if (!isRecord2(entry) || typeof entry["id"] !== "string" || entry["id"].length === 0)
|
|
26523
|
+
function rateLimitLines(rateLimits) {
|
|
26524
|
+
const lines = [];
|
|
26525
|
+
for (const snapshot of rateLimits.values()) {
|
|
26526
|
+
const prefix = snapshot.limitName ? `${snapshot.limitName} ` : "";
|
|
26527
|
+
for (const window of [snapshot.primary, snapshot.secondary]) {
|
|
26528
|
+
if (!window)
|
|
26545
26529
|
continue;
|
|
26546
|
-
|
|
26547
|
-
|
|
26548
|
-
|
|
26549
|
-
description: typeof entry["description"] === "string" ? entry["description"] : null
|
|
26550
|
-
});
|
|
26530
|
+
const label = windowLabel(window.windowDurationMins);
|
|
26531
|
+
const reset = window.resetsAt === null ? "" : ` (resets ${new Date(window.resetsAt * 1000).toLocaleString()})`;
|
|
26532
|
+
lines.push(`**${prefix}${label}:** ${Math.round(100 - window.usedPercent)}% left${reset}`);
|
|
26551
26533
|
}
|
|
26534
|
+
if (snapshot.credits?.unlimited)
|
|
26535
|
+
lines.push(`**${prefix}Credits:** unlimited`);
|
|
26536
|
+
else if (snapshot.credits?.balance)
|
|
26537
|
+
lines.push(`**${prefix}Credits:** ${snapshot.credits.balance}`);
|
|
26552
26538
|
}
|
|
26553
|
-
|
|
26554
|
-
|
|
26555
|
-
|
|
26556
|
-
|
|
26557
|
-
|
|
26558
|
-
|
|
26559
|
-
|
|
26560
|
-
|
|
26561
|
-
|
|
26562
|
-
|
|
26563
|
-
|
|
26564
|
-
|
|
26565
|
-
|
|
26539
|
+
return lines.length > 0 ? lines : ["**Limits:** not available yet"];
|
|
26540
|
+
}
|
|
26541
|
+
function windowLabel(minutes) {
|
|
26542
|
+
if (minutes === null)
|
|
26543
|
+
return "Limit";
|
|
26544
|
+
if (minutes < 60)
|
|
26545
|
+
return `${minutes}m limit`;
|
|
26546
|
+
if (minutes < 1440)
|
|
26547
|
+
return `${Math.round(minutes / 60)}h limit`;
|
|
26548
|
+
if (minutes < 10080)
|
|
26549
|
+
return `${Math.round(minutes / 1440)}d limit`;
|
|
26550
|
+
return "Weekly limit";
|
|
26551
|
+
}
|
|
26552
|
+
|
|
26553
|
+
// src/agent/configOptions.ts
|
|
26554
|
+
function sessionConfigOptions(session) {
|
|
26555
|
+
const model = findModel(session.catalog, session.model.model);
|
|
26556
|
+
const options = [
|
|
26557
|
+
modeConfigOption(session.mode),
|
|
26558
|
+
modelConfigOption(session.catalog, session.model.model, session.gatewayGroups),
|
|
26559
|
+
effortConfigOption(supportedEfforts(model), session.model.effort),
|
|
26560
|
+
collaborationModeConfigOption(session.collaborationMode)
|
|
26561
|
+
];
|
|
26562
|
+
if (modelSupportsFast(model))
|
|
26563
|
+
options.push(fastModeConfigOption(session.fastMode));
|
|
26564
|
+
return options;
|
|
26565
|
+
}
|
|
26566
|
+
async function applyConfigOption(session, codex, request) {
|
|
26567
|
+
switch (request.configId) {
|
|
26568
|
+
case MODE_CONFIG_ID: {
|
|
26569
|
+
const mode = findAgentMode(stringValue(request));
|
|
26570
|
+
if (!mode)
|
|
26571
|
+
throw RequestError.invalidParams({ configId: request.configId }, `Unknown mode "${String(request.value)}"`);
|
|
26572
|
+
session.mode = mode;
|
|
26573
|
+
return;
|
|
26566
26574
|
}
|
|
26575
|
+
case MODEL_CONFIG_ID: {
|
|
26576
|
+
const modelId = stringValue(request);
|
|
26577
|
+
const model = findModel(session.catalog, modelId);
|
|
26578
|
+
session.model = { model: modelId, effort: coerceEffort(model, session.model.effort) };
|
|
26579
|
+
if (!modelSupportsFast(model))
|
|
26580
|
+
session.fastMode = false;
|
|
26581
|
+
return;
|
|
26582
|
+
}
|
|
26583
|
+
case EFFORT_CONFIG_ID: {
|
|
26584
|
+
const effort = stringValue(request);
|
|
26585
|
+
const model = findModel(session.catalog, session.model.model);
|
|
26586
|
+
const efforts = supportedEfforts(model);
|
|
26587
|
+
if (efforts.length > 0 && !efforts.some((option) => option.reasoningEffort === effort)) {
|
|
26588
|
+
throw RequestError.invalidParams({ configId: request.configId }, `Model ${session.model.model} does not support effort "${effort}"`);
|
|
26589
|
+
}
|
|
26590
|
+
session.model = { ...session.model, effort };
|
|
26591
|
+
return;
|
|
26592
|
+
}
|
|
26593
|
+
case FAST_MODE_CONFIG_ID: {
|
|
26594
|
+
if (request.type !== "boolean" || typeof request.value !== "boolean") {
|
|
26595
|
+
throw RequestError.invalidParams({ configId: request.configId }, "fast_mode expects a boolean value");
|
|
26596
|
+
}
|
|
26597
|
+
if (request.value && !modelSupportsFast(findModel(session.catalog, session.model.model))) {
|
|
26598
|
+
throw RequestError.invalidParams({ configId: request.configId }, `Model ${session.model.model} does not support fast mode`);
|
|
26599
|
+
}
|
|
26600
|
+
session.fastMode = request.value;
|
|
26601
|
+
return;
|
|
26602
|
+
}
|
|
26603
|
+
case COLLABORATION_MODE_CONFIG_ID: {
|
|
26604
|
+
const mode = parseCollaborationMode(stringValue(request));
|
|
26605
|
+
if (!mode)
|
|
26606
|
+
throw RequestError.invalidParams({ configId: request.configId }, `Unknown collaboration mode "${String(request.value)}"`);
|
|
26607
|
+
await codex.threadSettingsUpdate({
|
|
26608
|
+
threadId: session.id,
|
|
26609
|
+
collaborationMode: {
|
|
26610
|
+
mode,
|
|
26611
|
+
settings: { model: findModel(session.catalog, session.model.model)?.model ?? session.model.model, reasoning_effort: session.model.effort, developer_instructions: null }
|
|
26612
|
+
}
|
|
26613
|
+
});
|
|
26614
|
+
session.collaborationMode = mode;
|
|
26615
|
+
return;
|
|
26616
|
+
}
|
|
26617
|
+
default:
|
|
26618
|
+
throw RequestError.invalidParams({ configId: request.configId }, `Unknown config option "${request.configId}"`);
|
|
26567
26619
|
}
|
|
26568
|
-
const rawMode = codex["mode"];
|
|
26569
|
-
if (rawMode !== undefined && rawMode !== "route" && rawMode !== "catalog") {
|
|
26570
|
-
throw RequestError.invalidParams({ mode: rawMode }, '_meta.codex.mode must be "route" or "catalog"');
|
|
26571
|
-
}
|
|
26572
|
-
const mode = rawMode === "catalog" ? "catalog" : "route";
|
|
26573
|
-
if (mode === "catalog" && models.length === 0) {
|
|
26574
|
-
throw RequestError.invalidParams(undefined, `_meta.codex.mode "catalog" needs _meta.alwith.models: the models offered next to Codex's`);
|
|
26575
|
-
}
|
|
26576
|
-
const catalogPath = config2["model_catalog_json"];
|
|
26577
|
-
const catalogEntries = typeof catalogPath === "string" ? readCatalogFile(catalogPath) : new Map;
|
|
26578
|
-
return { id, models, model, name, bearerToken, config: config2, mode, catalogEntries };
|
|
26579
26620
|
}
|
|
26580
|
-
function
|
|
26581
|
-
|
|
26582
|
-
|
|
26583
|
-
const raw = codex["id"];
|
|
26584
|
-
if (raw === undefined)
|
|
26585
|
-
return null;
|
|
26586
|
-
if (typeof raw !== "string" || !GATEWAY_ID_PATTERN.test(raw)) {
|
|
26587
|
-
throw RequestError.invalidParams({ id: raw }, "_meta.codex.id must match [a-z0-9-]+");
|
|
26621
|
+
function stringValue(request) {
|
|
26622
|
+
if (typeof request.value !== "string" || request.value.length === 0) {
|
|
26623
|
+
throw RequestError.invalidParams({ configId: request.configId }, `Config option "${request.configId}" expects a string value`);
|
|
26588
26624
|
}
|
|
26589
|
-
return
|
|
26625
|
+
return request.value;
|
|
26590
26626
|
}
|
|
26591
|
-
|
|
26592
|
-
|
|
26593
|
-
|
|
26594
|
-
|
|
26595
|
-
|
|
26596
|
-
|
|
26597
|
-
|
|
26627
|
+
|
|
26628
|
+
// src/agent/history.ts
|
|
26629
|
+
function historyUpdates(turns) {
|
|
26630
|
+
const updates = [];
|
|
26631
|
+
for (const turn of turns) {
|
|
26632
|
+
for (const item of turn.items) {
|
|
26633
|
+
updates.push(...itemSnapshot(item));
|
|
26634
|
+
}
|
|
26598
26635
|
}
|
|
26599
|
-
|
|
26600
|
-
|
|
26601
|
-
|
|
26602
|
-
const
|
|
26603
|
-
|
|
26604
|
-
|
|
26605
|
-
|
|
26606
|
-
const
|
|
26607
|
-
|
|
26608
|
-
for (const level of levels) {
|
|
26609
|
-
if (!isRecord2(level) || typeof level["effort"] !== "string")
|
|
26636
|
+
return updates;
|
|
26637
|
+
}
|
|
26638
|
+
function historyTitle(thread, turns) {
|
|
26639
|
+
const explicit = thread.name?.trim();
|
|
26640
|
+
if (explicit)
|
|
26641
|
+
return explicit;
|
|
26642
|
+
for (const turn of turns) {
|
|
26643
|
+
for (const item of turn.items) {
|
|
26644
|
+
if (item.type !== "userMessage")
|
|
26610
26645
|
continue;
|
|
26611
|
-
|
|
26646
|
+
const text2 = item.content.filter((input) => input.type === "text").map((input) => input.text.trim()).find((part) => part.length > 0);
|
|
26647
|
+
if (text2)
|
|
26648
|
+
return firstLine(text2);
|
|
26612
26649
|
}
|
|
26613
|
-
const modalities = Array.isArray(entry["input_modalities"]) ? entry["input_modalities"].filter((value) => value === "text" || value === "image") : null;
|
|
26614
|
-
entries.set(entry["slug"], {
|
|
26615
|
-
displayName: typeof entry["display_name"] === "string" ? entry["display_name"] : null,
|
|
26616
|
-
description: typeof entry["description"] === "string" ? entry["description"] : null,
|
|
26617
|
-
efforts,
|
|
26618
|
-
defaultEffort: typeof entry["default_reasoning_level"] === "string" ? entry["default_reasoning_level"] : null,
|
|
26619
|
-
inputModalities: modalities
|
|
26620
|
-
});
|
|
26621
26650
|
}
|
|
26622
|
-
|
|
26651
|
+
const preview = thread.preview.trim();
|
|
26652
|
+
return preview.length > 0 ? firstLine(preview) : null;
|
|
26623
26653
|
}
|
|
26624
|
-
function
|
|
26625
|
-
const
|
|
26626
|
-
|
|
26627
|
-
return {
|
|
26628
|
-
id: model.id,
|
|
26629
|
-
model: model.id,
|
|
26630
|
-
upgrade: null,
|
|
26631
|
-
upgradeInfo: null,
|
|
26632
|
-
availabilityNux: null,
|
|
26633
|
-
displayName: model.name !== model.id ? model.name : entry?.displayName ?? model.name,
|
|
26634
|
-
description: model.description ?? entry?.description ?? "",
|
|
26635
|
-
modelSpecialty: null,
|
|
26636
|
-
hidden: false,
|
|
26637
|
-
supportedReasoningEfforts: efforts,
|
|
26638
|
-
defaultReasoningEffort: defaultEffort,
|
|
26639
|
-
inputModalities: entry?.inputModalities ?? ["text", "image"],
|
|
26640
|
-
supportsPersonality: false,
|
|
26641
|
-
multiAgentVersion: null,
|
|
26642
|
-
additionalSpeedTiers: [],
|
|
26643
|
-
serviceTiers: [],
|
|
26644
|
-
defaultServiceTier: null,
|
|
26645
|
-
availableAccessPrograms: null,
|
|
26646
|
-
isDefault
|
|
26647
|
-
};
|
|
26654
|
+
function firstLine(text2) {
|
|
26655
|
+
const line = text2.split(/\r?\n/).map((part) => part.trim()).find((part) => part.length > 0) ?? text2;
|
|
26656
|
+
return line.length > 120 ? `${line.slice(0, 117)}...` : line;
|
|
26648
26657
|
}
|
|
26649
26658
|
|
|
26650
26659
|
// src/agent/session.ts
|
|
@@ -28597,7 +28606,8 @@ function threadIdOf(notification) {
|
|
|
28597
28606
|
}
|
|
28598
28607
|
|
|
28599
28608
|
// src/codex/process.ts
|
|
28600
|
-
import { existsSync } from "fs";
|
|
28609
|
+
import { existsSync, mkdtempSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
28610
|
+
import os3 from "os";
|
|
28601
28611
|
import path8 from "path";
|
|
28602
28612
|
import { spawn, spawnSync } from "child_process";
|
|
28603
28613
|
import { createRequire } from "module";
|
|
@@ -28909,7 +28919,9 @@ function startCodexProcess(codexPath, env = process.env) {
|
|
|
28909
28919
|
if (probe.error !== undefined)
|
|
28910
28920
|
throw new Error(`Could not run Codex (${launcher.command}): ${probe.error.message}`);
|
|
28911
28921
|
assertCodexVersion(probe.stdout.length > 0 ? probe.stdout : probe.stderr);
|
|
28912
|
-
const
|
|
28922
|
+
const catalog = mergedModelCatalog(launcher, env);
|
|
28923
|
+
const catalogOverride = catalog === null ? null : `model_catalog_json='${catalog}'`;
|
|
28924
|
+
const child = launcher.shell ? spawn(`${launcher.command}${catalogOverride === null ? "" : ` -c "${catalogOverride}"`} app-server`, { shell: true, env }) : spawn(launcher.command, [...launcher.prefixArgs, ...catalogOverride === null ? [] : ["-c", catalogOverride], "app-server"], { env });
|
|
28913
28925
|
let stderr = "";
|
|
28914
28926
|
child.stderr.on("data", (data) => {
|
|
28915
28927
|
stderr = (stderr + data.toString()).slice(-STDERR_RETAIN_BYTES);
|
|
@@ -28934,6 +28946,44 @@ function startCodexProcess(codexPath, env = process.env) {
|
|
|
28934
28946
|
exitCode: () => child.exitCode
|
|
28935
28947
|
};
|
|
28936
28948
|
}
|
|
28949
|
+
var EXTRA_MODEL_CATALOGS_ENV = "CODEX_ACP_MODEL_CATALOGS";
|
|
28950
|
+
function mergedModelCatalog(launcher, env) {
|
|
28951
|
+
const raw = env[EXTRA_MODEL_CATALOGS_ENV];
|
|
28952
|
+
if (raw === undefined || raw.trim() === "")
|
|
28953
|
+
return null;
|
|
28954
|
+
const files = raw.split(path8.delimiter).filter((file2) => file2.length > 0);
|
|
28955
|
+
const dump = spawnSync(launcher.command, [...launcher.prefixArgs, "debug", "models"], {
|
|
28956
|
+
shell: launcher.shell,
|
|
28957
|
+
env,
|
|
28958
|
+
encoding: "utf8",
|
|
28959
|
+
maxBuffer: 64 * 1024 * 1024
|
|
28960
|
+
});
|
|
28961
|
+
if (dump.error !== undefined || dump.status !== 0) {
|
|
28962
|
+
throw new Error(`codex debug models failed: ${dump.error?.message ?? dump.stderr.trim()}`);
|
|
28963
|
+
}
|
|
28964
|
+
const base = JSON.parse(dump.stdout);
|
|
28965
|
+
if (!Array.isArray(base.models))
|
|
28966
|
+
throw new Error("codex debug models did not return {models: [...]}");
|
|
28967
|
+
const models = base.models;
|
|
28968
|
+
const slugs = new Set(models.map((model) => model["slug"]));
|
|
28969
|
+
for (const file2 of files) {
|
|
28970
|
+
const extra = JSON.parse(readFileSync2(file2, "utf8"));
|
|
28971
|
+
if (!Array.isArray(extra.models))
|
|
28972
|
+
throw new Error(`${EXTRA_MODEL_CATALOGS_ENV}: ${file2} is not {models: [...]}`);
|
|
28973
|
+
for (const entry of extra.models) {
|
|
28974
|
+
if (typeof entry["slug"] !== "string")
|
|
28975
|
+
throw new Error(`${EXTRA_MODEL_CATALOGS_ENV}: ${file2} has an entry without a slug`);
|
|
28976
|
+
if (slugs.has(entry["slug"]))
|
|
28977
|
+
continue;
|
|
28978
|
+
slugs.add(entry["slug"]);
|
|
28979
|
+
models.push({ ...entry, visibility: "hide" });
|
|
28980
|
+
}
|
|
28981
|
+
}
|
|
28982
|
+
const directory = mkdtempSync(path8.join(os3.tmpdir(), "codex-acp-v2-catalog-"));
|
|
28983
|
+
const target = path8.join(directory, "models.json");
|
|
28984
|
+
writeFileSync(target, JSON.stringify({ ...base, models }));
|
|
28985
|
+
return target;
|
|
28986
|
+
}
|
|
28937
28987
|
|
|
28938
28988
|
// src/index.ts
|
|
28939
28989
|
var CODEX_EXIT_GRACE_MS = 2000;
|