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