@ycodium-ai/agent-codex 0.2.3080 → 0.2.3086
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +820 -832
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ var CODEX_CARD = {
|
|
|
48
48
|
// 三字段组合在 openThread 时定,切档要重开会话(与 runtimeModeSwitch 同为 next-turn)。
|
|
49
49
|
nativeModeSwitch: true,
|
|
50
50
|
contextCompaction: "native",
|
|
51
|
-
// 子代理经 collab 通知报成 task.*(events.ts 的 mapCollabAgentEvent)。
|
|
51
|
+
// 子代理经 collab 通知报成 task.*(events/collabAgents.ts 的 mapCollabAgentEvent)。
|
|
52
52
|
reportsSpawnedAgents: true,
|
|
53
53
|
// 出处不是 provider 文档:这是产品此前对所有 provider 统一施加的单图上限(10 MiB),
|
|
54
54
|
// codex 在这个上限下一直把图片以 data URL 内联。没有查到 Codex / OpenAI 对单图的明文
|
|
@@ -573,39 +573,15 @@ function codexExecLaunchArgs(launchArgs) {
|
|
|
573
573
|
}
|
|
574
574
|
return execArgs;
|
|
575
575
|
}
|
|
576
|
-
function
|
|
577
|
-
return
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
const environment = {};
|
|
586
|
-
const toml = (value) => JSON.stringify(value);
|
|
587
|
-
for (const server of servers) {
|
|
588
|
-
const key = `mcp_servers.${server.name}`;
|
|
589
|
-
if (server.transport.type === "stdio") {
|
|
590
|
-
args.push("-c", `${key}.command=${toml(server.transport.command)}`);
|
|
591
|
-
const commandArgs = server.transport.args ?? [];
|
|
592
|
-
if (commandArgs.length > 0) {
|
|
593
|
-
args.push("-c", `${key}.args=[${commandArgs.map(toml).join(",")}]`);
|
|
594
|
-
}
|
|
595
|
-
for (const [name, value] of Object.entries(server.transport.env ?? {})) {
|
|
596
|
-
args.push("-c", `${key}.env.${name}=${toml(value)}`);
|
|
597
|
-
}
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
args.push("-c", `${key}.url=${server.transport.url}`);
|
|
601
|
-
const bearer = server.transport.headers?.["Authorization"]?.replace(/^Bearer\s+/, "");
|
|
602
|
-
if (bearer) {
|
|
603
|
-
const envVar = codexBearerTokenEnvVar(server.name);
|
|
604
|
-
environment[envVar] = bearer;
|
|
605
|
-
args.push("-c", `${key}.bearer_token_env_var=${toml(envVar)}`);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
return { args, environment };
|
|
576
|
+
function codexMcpThreadConfig(servers) {
|
|
577
|
+
return {
|
|
578
|
+
mcp_servers: Object.fromEntries(
|
|
579
|
+
servers.map(({ name, transport }) => [
|
|
580
|
+
name,
|
|
581
|
+
transport.type === "stdio" ? { command: transport.command, args: transport.args ?? [], env: transport.env ?? {} } : { url: transport.url, http_headers: transport.headers ?? {} }
|
|
582
|
+
])
|
|
583
|
+
)
|
|
584
|
+
};
|
|
609
585
|
}
|
|
610
586
|
function modelIdFromSelection(selection) {
|
|
611
587
|
if (typeof selection !== "object" || selection === null || Array.isArray(selection)) {
|
|
@@ -669,8 +645,7 @@ function buildCodexSpawnEnv(input) {
|
|
|
669
645
|
return mergeLaunchEnv({
|
|
670
646
|
accountHome: input.layout.effectiveHomePath !== void 0 ? { CODEX_HOME: input.layout.effectiveHomePath } : {},
|
|
671
647
|
instanceTable,
|
|
672
|
-
handOverrides: input.crushApiKeys ? CRUSHED_API_KEYS : {}
|
|
673
|
-
launch: input.launchEnv
|
|
648
|
+
handOverrides: input.crushApiKeys ? CRUSHED_API_KEYS : {}
|
|
674
649
|
});
|
|
675
650
|
}
|
|
676
651
|
function nativeLoginSpawnEnv(processEnv, isolatedHome) {
|
|
@@ -689,13 +664,9 @@ async function spawnCodexAppServer(input) {
|
|
|
689
664
|
const env = input.env ?? buildCodexSpawnEnv({
|
|
690
665
|
recipe: input.recipe,
|
|
691
666
|
layout: input.layout,
|
|
692
|
-
...input.launchEnv === void 0 ? {} : { launchEnv: input.launchEnv },
|
|
693
667
|
crushApiKeys: shouldCrushApiKeys(input.recipe.authMode)
|
|
694
668
|
});
|
|
695
|
-
const args = input.args ??
|
|
696
|
-
...codexSpawnArgs(input.recipe, input.layout),
|
|
697
|
-
...input.extraArgs ?? []
|
|
698
|
-
];
|
|
669
|
+
const args = input.args ?? codexSpawnArgs(input.recipe, input.layout);
|
|
699
670
|
const handle = await input.host.process.spawn({
|
|
700
671
|
program: input.recipe.binaryPath,
|
|
701
672
|
args,
|
|
@@ -1410,662 +1381,515 @@ function buildCodexDeveloperInstructions(interactionMode, runtime, browserToolsA
|
|
|
1410
1381
|
<runtime_info>In case you're asked: you are running in Ycodium through the Codex harness, as ${toSingleLine(runtime.model)} with ${toSingleLine(runtime.reasoningEffort)} reasoning effort. No need to mention this otherwise.</runtime_info>`;
|
|
1411
1382
|
}
|
|
1412
1383
|
|
|
1413
|
-
// src/events.ts
|
|
1384
|
+
// src/events/base.ts
|
|
1414
1385
|
import * as NodeCrypto from "node:crypto";
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
const savedPath = item["savedPath"];
|
|
1422
|
-
if (typeof savedPath !== "string" || savedPath.trim().length === 0) return [];
|
|
1423
|
-
return [{ path: savedPath, ...input.itemId ? { toolCallId: input.itemId } : {} }];
|
|
1386
|
+
var CODEX_PROVIDER = "codex";
|
|
1387
|
+
var EVENT_ID_PREFIX = `codex-evt-${NodeCrypto.randomUUID()}`;
|
|
1388
|
+
var eventSeq = 0;
|
|
1389
|
+
function nextEventId() {
|
|
1390
|
+
eventSeq += 1;
|
|
1391
|
+
return `${EVENT_ID_PREFIX}-${eventSeq}`;
|
|
1424
1392
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
function normalizeItemType(value) {
|
|
1428
|
-
if (!value) return "item";
|
|
1429
|
-
return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[._/-]/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1393
|
+
function nowIso2() {
|
|
1394
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1430
1395
|
}
|
|
1431
|
-
function
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1396
|
+
function eventRaw(source, method, payload) {
|
|
1397
|
+
return { source, method, payload };
|
|
1398
|
+
}
|
|
1399
|
+
function baseEvent(input) {
|
|
1400
|
+
return {
|
|
1401
|
+
eventId: nextEventId(),
|
|
1402
|
+
provider: CODEX_PROVIDER,
|
|
1403
|
+
threadId: input.threadId,
|
|
1404
|
+
createdAt: nowIso2(),
|
|
1405
|
+
type: input.type,
|
|
1406
|
+
payload: input.payload,
|
|
1407
|
+
...input.providerInstanceId ? { providerInstanceId: input.providerInstanceId } : {},
|
|
1408
|
+
...input.turnId ? { turnId: input.turnId } : {},
|
|
1409
|
+
...input.itemId ? { itemId: input.itemId } : {},
|
|
1410
|
+
...input.requestId ? { requestId: input.requestId } : {},
|
|
1411
|
+
...input.method ? {
|
|
1412
|
+
raw: eventRaw(
|
|
1413
|
+
input.source ?? "codex.app-server.notification",
|
|
1414
|
+
input.method,
|
|
1415
|
+
input.payload
|
|
1416
|
+
)
|
|
1417
|
+
} : {}
|
|
1418
|
+
};
|
|
1451
1419
|
}
|
|
1452
1420
|
|
|
1453
|
-
// src/
|
|
1454
|
-
function
|
|
1455
|
-
if (!isRecord3(params)) return
|
|
1421
|
+
// src/events/fields.ts
|
|
1422
|
+
function itemIdOf(params) {
|
|
1423
|
+
if (!isRecord3(params)) return void 0;
|
|
1456
1424
|
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1457
|
-
|
|
1458
|
-
if (!isRecord3(changes) && !Array.isArray(changes)) return [];
|
|
1459
|
-
const entries = Array.isArray(changes) ? changes : Object.entries(changes).map(([path, change]) => ({
|
|
1460
|
-
path,
|
|
1461
|
-
...isRecord3(change) ? change : {}
|
|
1462
|
-
}));
|
|
1463
|
-
const out = [];
|
|
1464
|
-
for (const entry of entries) {
|
|
1465
|
-
if (!isRecord3(entry)) continue;
|
|
1466
|
-
const path = asString(entry["path"]) ?? asString(entry["file"]);
|
|
1467
|
-
if (!path) continue;
|
|
1468
|
-
out.push({
|
|
1469
|
-
path,
|
|
1470
|
-
kind: (asString(entry["type"]) ?? asString(entry["kind"]) ?? "update").toLowerCase(),
|
|
1471
|
-
movePath: asString(entry["move_path"]) ?? asString(entry["movePath"]),
|
|
1472
|
-
diff: asString(entry["unified_diff"]) ?? asString(entry["unifiedDiff"]),
|
|
1473
|
-
content: asString(entry["content"])
|
|
1474
|
-
});
|
|
1475
|
-
}
|
|
1476
|
-
return out;
|
|
1425
|
+
return asString(item["id"]) ?? asString(params["itemId"]) ?? void 0;
|
|
1477
1426
|
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
);
|
|
1483
|
-
if (entries.length === 0) return void 0;
|
|
1484
|
-
const lines = entries.slice(0, MAX_DESCRIBED_FILE_CHANGES).map(
|
|
1485
|
-
(entry) => entry.movePath ? `${entry.kind} ${entry.path} -> ${entry.movePath}` : `${entry.kind} ${entry.path}`
|
|
1486
|
-
);
|
|
1487
|
-
const remaining = entries.length - lines.length;
|
|
1488
|
-
return remaining > 0 ? `${lines.join("\n")}
|
|
1489
|
-
+${remaining} more` : lines.join("\n");
|
|
1427
|
+
function itemStatusOf(params) {
|
|
1428
|
+
if (!isRecord3(params)) return void 0;
|
|
1429
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1430
|
+
return asString(item["status"]) ?? void 0;
|
|
1490
1431
|
}
|
|
1491
|
-
function
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
if (kind === "add") {
|
|
1495
|
-
out.push({
|
|
1496
|
-
path,
|
|
1497
|
-
changeKind: "add",
|
|
1498
|
-
...content !== void 0 ? { content: { kind: "replace", newText: content } } : {}
|
|
1499
|
-
});
|
|
1500
|
-
} else if (kind === "delete") {
|
|
1501
|
-
out.push({
|
|
1502
|
-
path,
|
|
1503
|
-
changeKind: "delete",
|
|
1504
|
-
...content !== void 0 ? { content: { kind: "replace", oldText: content, newText: "" } } : {}
|
|
1505
|
-
});
|
|
1506
|
-
} else if (movePath) {
|
|
1507
|
-
out.push({
|
|
1508
|
-
path,
|
|
1509
|
-
changeKind: "rename",
|
|
1510
|
-
...diff ? { content: { kind: "unified", diff } } : {}
|
|
1511
|
-
});
|
|
1512
|
-
} else {
|
|
1513
|
-
out.push({
|
|
1514
|
-
path,
|
|
1515
|
-
changeKind: "modify",
|
|
1516
|
-
...diff ? { content: { kind: "unified", diff } } : {}
|
|
1517
|
-
});
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
return out;
|
|
1432
|
+
function turnIdOf(params) {
|
|
1433
|
+
if (!isRecord3(params)) return void 0;
|
|
1434
|
+
return asString(params["turnId"]) ?? (isRecord3(params["turn"]) ? asString(params["turn"]["id"]) : null) ?? void 0;
|
|
1521
1435
|
}
|
|
1522
|
-
function
|
|
1523
|
-
|
|
1524
|
-
const
|
|
1525
|
-
|
|
1526
|
-
const cwd = asString(record["cwd"]);
|
|
1527
|
-
const reason = trimText(record["reason"]);
|
|
1528
|
-
const callId = asString(record["callId"]);
|
|
1529
|
-
return {
|
|
1530
|
-
command,
|
|
1531
|
-
...cwd ? { cwd } : {},
|
|
1532
|
-
...reason ? { reason } : {},
|
|
1533
|
-
...callId ? { callId } : {}
|
|
1534
|
-
};
|
|
1436
|
+
function itemTitle(params) {
|
|
1437
|
+
if (!isRecord3(params)) return void 0;
|
|
1438
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1439
|
+
return trimText(item["title"]) ?? trimText(item["name"]) ?? trimText(item["command"]) ?? trimText(item["summary"]) ?? void 0;
|
|
1535
1440
|
}
|
|
1536
|
-
function
|
|
1537
|
-
|
|
1538
|
-
const
|
|
1539
|
-
const
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
...grantRoot ? { grantRoot } : {},
|
|
1546
|
-
...callId ? { callId } : {},
|
|
1547
|
-
fileChanges: toApplyPatchFileChanges(record)
|
|
1548
|
-
};
|
|
1441
|
+
function itemDetail(params) {
|
|
1442
|
+
if (!isRecord3(params)) return void 0;
|
|
1443
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1444
|
+
const query = isRecord3(item["action"]) ? item["action"]["queries"] : item["query"];
|
|
1445
|
+
if (Array.isArray(query)) {
|
|
1446
|
+
const joined = query.filter((entry) => typeof entry === "string").join(", ");
|
|
1447
|
+
if (joined.length > 0) return joined;
|
|
1448
|
+
}
|
|
1449
|
+
return trimText(item["text"]) ?? trimText(item["summary"]) ?? trimText(item["path"]) ?? trimText(item["savedPath"]) ?? trimText(item["prompt"]) ?? (typeof query === "string" ? query : void 0);
|
|
1549
1450
|
}
|
|
1550
|
-
function
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
const target = entry["path"];
|
|
1562
|
-
const path = target["type"] === "path" ? asString(target["path"]) : target["type"] === "glob_pattern" ? asString(target["pattern"]) : void 0;
|
|
1563
|
-
if (!path) continue;
|
|
1564
|
-
if (entry["access"] === "read") read.push(path);
|
|
1565
|
-
if (entry["access"] === "write") write.push(path);
|
|
1451
|
+
function toTurnStatus(status) {
|
|
1452
|
+
switch (status) {
|
|
1453
|
+
case "failed":
|
|
1454
|
+
return "failed";
|
|
1455
|
+
case "cancelled":
|
|
1456
|
+
case "canceled":
|
|
1457
|
+
return "cancelled";
|
|
1458
|
+
case "interrupted":
|
|
1459
|
+
return "interrupted";
|
|
1460
|
+
default:
|
|
1461
|
+
return "completed";
|
|
1566
1462
|
}
|
|
1567
|
-
const reason = trimText(record["reason"]);
|
|
1568
|
-
return {
|
|
1569
|
-
...reason ? { reason } : {},
|
|
1570
|
-
read: [...new Set(read)],
|
|
1571
|
-
write: [...new Set(write)],
|
|
1572
|
-
network: network["enabled"] === true
|
|
1573
|
-
};
|
|
1574
1463
|
}
|
|
1575
|
-
function
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
return
|
|
1579
|
-
|
|
1580
|
-
...requested["fileSystem"] !== void 0 ? { fileSystem: requested["fileSystem"] } : {},
|
|
1581
|
-
...requested["network"] !== void 0 ? { network: requested["network"] } : {}
|
|
1582
|
-
},
|
|
1583
|
-
scope: persistent ? "session" : "turn"
|
|
1584
|
-
};
|
|
1464
|
+
function threadStateFromStatus(status) {
|
|
1465
|
+
const type = isRecord3(status) ? asString(status["type"]) : asString(status);
|
|
1466
|
+
if (type === "idle") return "idle";
|
|
1467
|
+
if (type === "systemError" || type === "error") return "error";
|
|
1468
|
+
return "active";
|
|
1585
1469
|
}
|
|
1586
|
-
function
|
|
1587
|
-
|
|
1588
|
-
const appName = asString(record["appName"]) ?? asString(record["serverName"]) ?? void 0;
|
|
1589
|
-
const serverName = asString(record["serverName"]);
|
|
1590
|
-
const message = trimText(record["message"]);
|
|
1591
|
-
const url = asString(record["url"]) ?? asString(record["authorizationUrl"]);
|
|
1592
|
-
const identity = appName ?? url ?? "unknown";
|
|
1593
|
-
return {
|
|
1594
|
-
...appName ? { appName } : {},
|
|
1595
|
-
...serverName ? { serverName } : {},
|
|
1596
|
-
...message ? { message } : {},
|
|
1597
|
-
...url ? { url } : {},
|
|
1598
|
-
identity,
|
|
1599
|
-
mode: url ? "url" : "form",
|
|
1600
|
-
...record["requestedSchema"] !== void 0 ? { schema: record["requestedSchema"] } : {}
|
|
1601
|
-
};
|
|
1602
|
-
}
|
|
1603
|
-
function persistFromMeta(meta) {
|
|
1604
|
-
if (!isRecord3(meta)) return void 0;
|
|
1605
|
-
const persist = asString(meta["persist"]) ?? asString(meta["persistence"]);
|
|
1606
|
-
if (persist === "session" || persist === "acceptForSession") return "session";
|
|
1607
|
-
if (persist === "always" || persist === "permanent" || persist === "forever" || persist === "persistent" || persist === "acceptAlways") {
|
|
1608
|
-
return "always";
|
|
1609
|
-
}
|
|
1610
|
-
return void 0;
|
|
1470
|
+
function codexCompactionId(threadId, turnId) {
|
|
1471
|
+
return `codex:${threadId}:${turnId ?? "session"}`;
|
|
1611
1472
|
}
|
|
1612
|
-
function
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
const
|
|
1620
|
-
const
|
|
1621
|
-
const
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
if (Array.isArray(spec["oneOf"]) || Array.isArray(spec["enum"])) {
|
|
1626
|
-
const options = Array.isArray(spec["oneOf"]) ? spec["oneOf"] : Array.isArray(spec["enum"]) ? spec["enum"] : [];
|
|
1627
|
-
const first = options[0];
|
|
1628
|
-
content[key] = isRecord3(first) ? first["const"] ?? first["value"] ?? first : first;
|
|
1629
|
-
} else if (spec["type"] === "boolean") {
|
|
1630
|
-
content[key] = true;
|
|
1631
|
-
} else if (typeof spec["default"] !== "undefined") {
|
|
1632
|
-
content[key] = spec["default"];
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
for (const key of required) {
|
|
1636
|
-
if (typeof key === "string" && !(key in content)) {
|
|
1637
|
-
return { action: "decline" };
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
const persist = persistFromMeta(isRecord3(params) ? params["_meta"] : void 0);
|
|
1473
|
+
function normalizeCodexTokenUsage(tokenUsage) {
|
|
1474
|
+
if (!isRecord3(tokenUsage)) return void 0;
|
|
1475
|
+
const last = isRecord3(tokenUsage["last"]) ? tokenUsage["last"] : void 0;
|
|
1476
|
+
const total = isRecord3(tokenUsage["total"]) ? tokenUsage["total"] : void 0;
|
|
1477
|
+
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1478
|
+
const usedTokens = count(last?.["totalTokens"]);
|
|
1479
|
+
if (usedTokens === void 0 || usedTokens <= 0) return void 0;
|
|
1480
|
+
const totalProcessedTokens = count(total?.["totalTokens"]);
|
|
1481
|
+
const maxTokens = count(tokenUsage["modelContextWindow"]);
|
|
1482
|
+
const inputTokens = count(last?.["inputTokens"]);
|
|
1483
|
+
const cachedInputTokens = count(last?.["cachedInputTokens"]);
|
|
1484
|
+
const outputTokens = count(last?.["outputTokens"]);
|
|
1485
|
+
const reasoningOutputTokens = count(last?.["reasoningOutputTokens"]);
|
|
1641
1486
|
return {
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
...
|
|
1487
|
+
usedTokens,
|
|
1488
|
+
lastUsedTokens: usedTokens,
|
|
1489
|
+
...totalProcessedTokens !== void 0 && totalProcessedTokens > usedTokens ? { totalProcessedTokens } : {},
|
|
1490
|
+
...maxTokens !== void 0 && maxTokens > 0 ? { maxTokens } : {},
|
|
1491
|
+
...inputTokens !== void 0 ? { inputTokens, lastInputTokens: inputTokens } : {},
|
|
1492
|
+
...cachedInputTokens !== void 0 ? { cachedInputTokens, lastCachedInputTokens: cachedInputTokens } : {},
|
|
1493
|
+
...outputTokens !== void 0 ? { outputTokens, lastOutputTokens: outputTokens } : {},
|
|
1494
|
+
...reasoningOutputTokens !== void 0 ? { reasoningOutputTokens, lastReasoningOutputTokens: reasoningOutputTokens } : {},
|
|
1495
|
+
compactsAutomatically: true
|
|
1645
1496
|
};
|
|
1646
1497
|
}
|
|
1498
|
+
function turnCompletedControlFlow(params) {
|
|
1499
|
+
const turn = isRecord3(params) ? isRecord3(params["turn"]) ? params["turn"] : params : {};
|
|
1500
|
+
const state = toTurnStatus(asString(turn["status"]) ?? void 0);
|
|
1501
|
+
const error = isRecord3(turn["error"]) ? trimText(turn["error"]["message"]) : void 0;
|
|
1502
|
+
return error ? { state, errorMessage: error } : { state };
|
|
1503
|
+
}
|
|
1647
1504
|
|
|
1648
|
-
// src/
|
|
1649
|
-
var
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
return
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
id,
|
|
1688
|
-
label,
|
|
1689
|
-
usedPercent: clampPercent(usedPercent),
|
|
1690
|
-
resetsAt: epochSecondsToIso(window["resetsAt"]),
|
|
1691
|
-
...minutes === null ? {} : { windowMs: minutes * 6e4 }
|
|
1692
|
-
};
|
|
1693
|
-
};
|
|
1694
|
-
var PLAN_LABELS = {
|
|
1695
|
-
free: "Free",
|
|
1696
|
-
go: "Go",
|
|
1697
|
-
plus: "Plus",
|
|
1698
|
-
pro: "Pro",
|
|
1699
|
-
prolite: "Pro Lite",
|
|
1700
|
-
team: "Team",
|
|
1701
|
-
business: "Business",
|
|
1702
|
-
enterprise: "Enterprise",
|
|
1703
|
-
edu: "Edu"
|
|
1704
|
-
};
|
|
1705
|
-
var planLabel = (planType) => {
|
|
1706
|
-
const id = asNonEmptyString(planType);
|
|
1707
|
-
if (id === null || id === "unknown") return void 0;
|
|
1708
|
-
return PLAN_LABELS[id] ?? id.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1709
|
-
};
|
|
1710
|
-
var creditBalance = (value) => {
|
|
1711
|
-
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
1712
|
-
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
1713
|
-
const numeric = Number(value.trim());
|
|
1714
|
-
return Number.isFinite(numeric) ? numeric : null;
|
|
1715
|
-
};
|
|
1716
|
-
var MAIN_LIMIT_ID = "codex";
|
|
1717
|
-
var CREDITS_BALANCE_ID = "credits";
|
|
1718
|
-
var isMainCodexLimit = (snapshot) => {
|
|
1719
|
-
const limitId = asNonEmptyString(asRecord(snapshot)?.["limitId"]);
|
|
1720
|
-
return limitId === null || limitId === MAIN_LIMIT_ID;
|
|
1721
|
-
};
|
|
1722
|
-
var mapCodexSnapshotWindows = (snapshot) => {
|
|
1723
|
-
const bucket = asRecord(snapshot);
|
|
1724
|
-
if (bucket === null || !isMainCodexLimit(bucket)) return [];
|
|
1725
|
-
const windows = [];
|
|
1726
|
-
const takenIds = /* @__PURE__ */ new Set();
|
|
1727
|
-
for (const slot of ["primary", "secondary"]) {
|
|
1728
|
-
const window = mapWindow(bucket[slot], slot, classifyWindow);
|
|
1729
|
-
if (window === null) continue;
|
|
1730
|
-
const id = takenIds.has(window.id) ? `${window.id}:${slot}` : window.id;
|
|
1731
|
-
takenIds.add(id);
|
|
1732
|
-
windows.push({ ...window, id, group: MAIN_LIMIT_ID });
|
|
1733
|
-
}
|
|
1734
|
-
return windows;
|
|
1735
|
-
};
|
|
1736
|
-
var mapCodexRateLimits = (response, accountKey) => {
|
|
1737
|
-
const root = asRecord(response);
|
|
1738
|
-
const legacy = asRecord(root?.["rateLimits"]);
|
|
1739
|
-
const byLimitId = asRecord(root?.["rateLimitsByLimitId"]);
|
|
1740
|
-
const codexBucket = asRecord(byLimitId?.[MAIN_LIMIT_ID]);
|
|
1741
|
-
const main = codexBucket ?? (legacy !== null && isMainCodexLimit(legacy) ? legacy : null);
|
|
1742
|
-
if (root === null || legacy === null && codexBucket === null) return null;
|
|
1743
|
-
const windows = mapCodexSnapshotWindows(main);
|
|
1744
|
-
const takenIds = new Set(windows.map((window) => window.id));
|
|
1745
|
-
const push = (window, slot) => {
|
|
1746
|
-
if (window === null) return;
|
|
1747
|
-
const id = takenIds.has(window.id) ? `${window.id}:${slot}` : window.id;
|
|
1748
|
-
takenIds.add(id);
|
|
1749
|
-
windows.push({ ...window, id });
|
|
1505
|
+
// src/events/collabAgents.ts
|
|
1506
|
+
var COLLAB_CHILD_STATUSES = /* @__PURE__ */ new Set([
|
|
1507
|
+
"running",
|
|
1508
|
+
"idle",
|
|
1509
|
+
"failed",
|
|
1510
|
+
"interrupted",
|
|
1511
|
+
"cancelled"
|
|
1512
|
+
]);
|
|
1513
|
+
function asCollabChildStatus(value) {
|
|
1514
|
+
return typeof value === "string" && COLLAB_CHILD_STATUSES.has(value) ? value : void 0;
|
|
1515
|
+
}
|
|
1516
|
+
function collabTurnEndStatus(turnStatus) {
|
|
1517
|
+
const state = toTurnStatus(turnStatus);
|
|
1518
|
+
return state === "completed" ? "idle" : state;
|
|
1519
|
+
}
|
|
1520
|
+
function pathLeaf(value) {
|
|
1521
|
+
if (!value) return void 0;
|
|
1522
|
+
const parts = value.split("/").filter((part) => part.length > 0);
|
|
1523
|
+
return parts.at(-1);
|
|
1524
|
+
}
|
|
1525
|
+
function mapCollabAgentEvent(input) {
|
|
1526
|
+
if (!isRecord3(input.params)) return [];
|
|
1527
|
+
const agentThreadId = asString(input.params["agentThreadId"]) ?? asString(input.params["threadId"]);
|
|
1528
|
+
if (!agentThreadId) return [];
|
|
1529
|
+
const agentPath = asString(input.params["agentPath"]) ?? asString(input.params["path"]) ?? void 0;
|
|
1530
|
+
const nickname = asString(input.params["agentNickname"]) ?? asString(input.params["nickname"]);
|
|
1531
|
+
const role = asString(input.params["agentRole"]) ?? asString(input.params["role"]) ?? pathLeaf(agentPath) ?? "general-purpose";
|
|
1532
|
+
const knownName = nickname ?? pathLeaf(agentPath);
|
|
1533
|
+
const title = knownName ?? agentThreadId;
|
|
1534
|
+
const model = trimText(input.params["model"]);
|
|
1535
|
+
const effort = trimText(input.params["effort"]);
|
|
1536
|
+
const linkage = {
|
|
1537
|
+
role,
|
|
1538
|
+
...knownName ? { title: knownName } : {},
|
|
1539
|
+
...model ? { model } : {},
|
|
1540
|
+
...effort ? { effort } : {},
|
|
1541
|
+
...agentPath ? { agentPath } : {},
|
|
1542
|
+
timelineBypass: true,
|
|
1543
|
+
rosterKind: "subagent"
|
|
1750
1544
|
};
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1545
|
+
const threadId = input.rootThreadId;
|
|
1546
|
+
const method = input.method;
|
|
1547
|
+
if (method === "collabAgent/started" || method === "task.started") {
|
|
1548
|
+
return [
|
|
1549
|
+
baseEvent({
|
|
1550
|
+
type: "task.started",
|
|
1551
|
+
threadId,
|
|
1552
|
+
payload: { taskId: agentThreadId, ...linkage },
|
|
1553
|
+
method,
|
|
1554
|
+
providerInstanceId: input.providerInstanceId
|
|
1555
|
+
})
|
|
1556
|
+
];
|
|
1755
1557
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1558
|
+
if (method === "collabAgent/progress" || method === "task.progress") {
|
|
1559
|
+
const summary = trimText(input.params["detail"]) ?? trimText(input.params["text"]);
|
|
1560
|
+
return [
|
|
1561
|
+
baseEvent({
|
|
1562
|
+
type: "task.progress",
|
|
1563
|
+
threadId,
|
|
1564
|
+
// 契约 TaskProgressPayload 的 description 是必填;子代理行靠它显示名字。
|
|
1565
|
+
payload: {
|
|
1566
|
+
taskId: agentThreadId,
|
|
1567
|
+
description: title,
|
|
1568
|
+
...summary ? { summary } : {},
|
|
1569
|
+
...linkage
|
|
1570
|
+
},
|
|
1571
|
+
method,
|
|
1572
|
+
providerInstanceId: input.providerInstanceId
|
|
1573
|
+
})
|
|
1574
|
+
];
|
|
1759
1575
|
}
|
|
1760
|
-
|
|
1761
|
-
const
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
})
|
|
1770
|
-
|
|
1771
|
-
}
|
|
1576
|
+
if (method === "collabAgent/completed" || method === "task.completed") {
|
|
1577
|
+
const status = collabTurnEndStatus(asString(input.params["turnStatus"]) ?? void 0);
|
|
1578
|
+
return [
|
|
1579
|
+
baseEvent({
|
|
1580
|
+
type: "task.updated",
|
|
1581
|
+
threadId,
|
|
1582
|
+
payload: { taskId: agentThreadId, status, ...linkage },
|
|
1583
|
+
method,
|
|
1584
|
+
providerInstanceId: input.providerInstanceId
|
|
1585
|
+
})
|
|
1586
|
+
];
|
|
1772
1587
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
resetsAt: null
|
|
1786
|
-
});
|
|
1787
|
-
}
|
|
1588
|
+
if (method === "collabAgent/statusChanged") {
|
|
1589
|
+
const raw = asString(input.params["status"]);
|
|
1590
|
+
const status = raw === "systemError" || raw === "error" ? "failed" : raw === "idle" ? "idle" : "running";
|
|
1591
|
+
return [
|
|
1592
|
+
baseEvent({
|
|
1593
|
+
type: "task.updated",
|
|
1594
|
+
threadId,
|
|
1595
|
+
payload: { taskId: agentThreadId, status, ...linkage },
|
|
1596
|
+
method,
|
|
1597
|
+
providerInstanceId: input.providerInstanceId
|
|
1598
|
+
})
|
|
1599
|
+
];
|
|
1788
1600
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
...availableCount === null || availableCount < 0 ? {} : { resetCredits: { availableCount: Math.trunc(availableCount) } }
|
|
1800
|
-
};
|
|
1801
|
-
};
|
|
1802
|
-
var codexQuotaFromRateLimits = (snapshot) => {
|
|
1803
|
-
const bucket = asRecord(snapshot);
|
|
1804
|
-
if (bucket === null) return null;
|
|
1805
|
-
const reading = mapCodexRateLimits({ rateLimits: snapshot }, "");
|
|
1806
|
-
if (reading === null || reading.status !== "available") return null;
|
|
1807
|
-
const main = isMainCodexLimit(bucket);
|
|
1808
|
-
const group = main ? MAIN_LIMIT_ID : asNonEmptyString(bucket["limitId"]);
|
|
1809
|
-
const replaces = {
|
|
1810
|
-
windowGroups: group === null ? [] : [group],
|
|
1811
|
-
balanceIds: main ? [CREDITS_BALANCE_ID] : []
|
|
1812
|
-
};
|
|
1813
|
-
return {
|
|
1814
|
-
windows: reading.windows,
|
|
1815
|
-
balances: reading.balances,
|
|
1816
|
-
...reading.plan === void 0 ? {} : { plan: reading.plan },
|
|
1817
|
-
replaces
|
|
1818
|
-
};
|
|
1819
|
-
};
|
|
1820
|
-
var classifyRateLimitsError = (message) => {
|
|
1821
|
-
const text = message.toLowerCase();
|
|
1822
|
-
if (text.includes("chatgpt authentication required")) return "unsupportedLogin";
|
|
1823
|
-
if (text.includes("authentication required")) return "missingCredentials";
|
|
1824
|
-
if (/\b(401|403)\b|unauthorized|forbidden/.test(text)) return "authRejected";
|
|
1825
|
-
if (/\b429\b|too many requests/.test(text)) return "rateLimited";
|
|
1826
|
-
return "unreachable";
|
|
1827
|
-
};
|
|
1828
|
-
var CODEX_OFFICIAL_HOSTS = /* @__PURE__ */ new Set(["chatgpt.com"]);
|
|
1829
|
-
var CODEX_DEFAULT_MODEL_PROVIDER = "openai";
|
|
1830
|
-
var isOfficialUrl = (value) => {
|
|
1831
|
-
const parsed = URL.parse(value.trim());
|
|
1832
|
-
return parsed === null ? false : CODEX_OFFICIAL_HOSTS.has(parsed.hostname.toLowerCase());
|
|
1833
|
-
};
|
|
1834
|
-
var readTomlScalar = (raw) => {
|
|
1835
|
-
const quoted = /^(["'])(.*?)\1/.exec(raw.trim());
|
|
1836
|
-
if (quoted?.[2] !== void 0) return quoted[2];
|
|
1837
|
-
const [beforeComment = ""] = raw.split("#");
|
|
1838
|
-
return beforeComment.trim();
|
|
1839
|
-
};
|
|
1840
|
-
var TABLE_HEADER = /^\[\s*([^\][]+?)\s*\]\s*(?:#.*)?$/;
|
|
1841
|
-
var KEY_VALUE = /^([A-Za-z0-9_.\-"']+)\s*=\s*(.*)$/;
|
|
1842
|
-
var BASE_URL_KEYS = /* @__PURE__ */ new Set([
|
|
1843
|
-
"chatgpt_base_url",
|
|
1844
|
-
"openai_base_url",
|
|
1845
|
-
"base_url"
|
|
1846
|
-
]);
|
|
1847
|
-
var codexConfigSignal = (contents, options) => {
|
|
1848
|
-
let table = null;
|
|
1849
|
-
let selectedProvider = CODEX_DEFAULT_MODEL_PROVIDER;
|
|
1850
|
-
const providerBaseUrls = /* @__PURE__ */ new Map();
|
|
1851
|
-
for (const rawLine of contents.split("\n")) {
|
|
1852
|
-
const line = rawLine.trim();
|
|
1853
|
-
if (line.length === 0 || line.startsWith("#")) continue;
|
|
1854
|
-
const header = TABLE_HEADER.exec(line);
|
|
1855
|
-
if (header !== null) {
|
|
1856
|
-
table = header[1] ?? "";
|
|
1857
|
-
if (table === "model_providers") return "config.toml:model_providers";
|
|
1858
|
-
continue;
|
|
1859
|
-
}
|
|
1860
|
-
const assignment = KEY_VALUE.exec(line);
|
|
1861
|
-
if (assignment === null) continue;
|
|
1862
|
-
const key = assignment[1] ?? "";
|
|
1863
|
-
const value = readTomlScalar(assignment[2] ?? "");
|
|
1864
|
-
if (table === null) {
|
|
1865
|
-
if (key === "model_provider") {
|
|
1866
|
-
selectedProvider = value;
|
|
1867
|
-
} else if (key === "profile" || key.startsWith("model_providers")) {
|
|
1868
|
-
return `config.toml:${key.split(".")[0] ?? key}`;
|
|
1869
|
-
} else if (BASE_URL_KEYS.has(key) && !isOfficialUrl(value)) {
|
|
1870
|
-
return `config.toml:${key}`;
|
|
1871
|
-
}
|
|
1872
|
-
continue;
|
|
1873
|
-
}
|
|
1874
|
-
if (table.startsWith("model_providers.") && key === "base_url") {
|
|
1875
|
-
providerBaseUrls.set(
|
|
1876
|
-
table.slice("model_providers.".length).replace(/^["']|["']$/g, ""),
|
|
1877
|
-
value
|
|
1878
|
-
);
|
|
1879
|
-
}
|
|
1601
|
+
if (method === "collabAgent/metadataUpdated") {
|
|
1602
|
+
return [
|
|
1603
|
+
baseEvent({
|
|
1604
|
+
type: "task.updated",
|
|
1605
|
+
threadId,
|
|
1606
|
+
payload: { taskId: agentThreadId, ...linkage },
|
|
1607
|
+
method,
|
|
1608
|
+
providerInstanceId: input.providerInstanceId
|
|
1609
|
+
})
|
|
1610
|
+
];
|
|
1880
1611
|
}
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
];
|
|
1894
|
-
var codexLaunchArgsSignal = (launchArgs) => {
|
|
1895
|
-
for (const marker of LAUNCH_ARG_MARKERS) {
|
|
1896
|
-
if (launchArgs.includes(marker)) return `launchArgs:${marker}`;
|
|
1612
|
+
if (method === "collabAgent/metadata") {
|
|
1613
|
+
const status = asCollabChildStatus(input.params["status"]);
|
|
1614
|
+
if (status === void 0) return [];
|
|
1615
|
+
return [
|
|
1616
|
+
baseEvent({
|
|
1617
|
+
type: "task.updated",
|
|
1618
|
+
threadId,
|
|
1619
|
+
payload: { taskId: agentThreadId, status, ...linkage },
|
|
1620
|
+
method,
|
|
1621
|
+
providerInstanceId: input.providerInstanceId
|
|
1622
|
+
})
|
|
1623
|
+
];
|
|
1897
1624
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1625
|
+
if (method === "collabAgent/tokenUsage") {
|
|
1626
|
+
const tokenUsage = isRecord3(input.params["tokenUsage"]) ? input.params["tokenUsage"] : void 0;
|
|
1627
|
+
const total = tokenUsage && isRecord3(tokenUsage["total"]) ? tokenUsage["total"] : void 0;
|
|
1628
|
+
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1629
|
+
const totalTokens = total ? count(total["totalTokens"]) : void 0;
|
|
1630
|
+
if (total === void 0 || totalTokens === void 0) return [];
|
|
1631
|
+
const inputTokens = count(total["inputTokens"]);
|
|
1632
|
+
const cachedInputTokens = count(total["cachedInputTokens"]);
|
|
1633
|
+
const outputTokens = count(total["outputTokens"]);
|
|
1634
|
+
const reasoningOutputTokens = count(total["reasoningOutputTokens"]);
|
|
1635
|
+
const typedUsage = {
|
|
1636
|
+
totalTokens,
|
|
1637
|
+
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
1638
|
+
...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
|
|
1639
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
1640
|
+
...reasoningOutputTokens !== void 0 ? { reasoningOutputTokens } : {}
|
|
1641
|
+
};
|
|
1642
|
+
return [
|
|
1643
|
+
baseEvent({
|
|
1644
|
+
type: "task.progress",
|
|
1645
|
+
threadId,
|
|
1646
|
+
payload: {
|
|
1647
|
+
taskId: agentThreadId,
|
|
1648
|
+
description: title,
|
|
1649
|
+
typedUsage,
|
|
1650
|
+
...linkage
|
|
1651
|
+
},
|
|
1652
|
+
method,
|
|
1653
|
+
providerInstanceId: input.providerInstanceId
|
|
1654
|
+
})
|
|
1655
|
+
];
|
|
1910
1656
|
}
|
|
1911
|
-
return
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
const
|
|
1918
|
-
|
|
1919
|
-
if (
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
return void 0;
|
|
1657
|
+
return [];
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
// src/approvalMapping.ts
|
|
1661
|
+
function fileChangeEntries(params) {
|
|
1662
|
+
if (!isRecord3(params)) return [];
|
|
1663
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1664
|
+
const changes = item["changes"] ?? item["fileChanges"] ?? params["fileChanges"];
|
|
1665
|
+
if (!isRecord3(changes) && !Array.isArray(changes)) return [];
|
|
1666
|
+
const entries = Array.isArray(changes) ? changes : Object.entries(changes).map(([path, change]) => ({
|
|
1667
|
+
path,
|
|
1668
|
+
...isRecord3(change) ? change : {}
|
|
1669
|
+
}));
|
|
1670
|
+
const out = [];
|
|
1671
|
+
for (const entry of entries) {
|
|
1672
|
+
if (!isRecord3(entry)) continue;
|
|
1673
|
+
const path = asString(entry["path"]) ?? asString(entry["file"]);
|
|
1674
|
+
if (!path) continue;
|
|
1675
|
+
out.push({
|
|
1676
|
+
path,
|
|
1677
|
+
kind: (asString(entry["type"]) ?? asString(entry["kind"]) ?? "update").toLowerCase(),
|
|
1678
|
+
movePath: asString(entry["move_path"]) ?? asString(entry["movePath"]),
|
|
1679
|
+
diff: asString(entry["unified_diff"]) ?? asString(entry["unifiedDiff"]),
|
|
1680
|
+
content: asString(entry["content"])
|
|
1681
|
+
});
|
|
1937
1682
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1683
|
+
return out;
|
|
1684
|
+
}
|
|
1685
|
+
var MAX_DESCRIBED_FILE_CHANGES = 20;
|
|
1686
|
+
function describeFileChanges(params) {
|
|
1687
|
+
const entries = fileChangeEntries(params).toSorted(
|
|
1688
|
+
(left, right) => left.path.localeCompare(right.path)
|
|
1689
|
+
);
|
|
1690
|
+
if (entries.length === 0) return void 0;
|
|
1691
|
+
const lines = entries.slice(0, MAX_DESCRIBED_FILE_CHANGES).map(
|
|
1692
|
+
(entry) => entry.movePath ? `${entry.kind} ${entry.path} -> ${entry.movePath}` : `${entry.kind} ${entry.path}`
|
|
1693
|
+
);
|
|
1694
|
+
const remaining = entries.length - lines.length;
|
|
1695
|
+
return remaining > 0 ? `${lines.join("\n")}
|
|
1696
|
+
+${remaining} more` : lines.join("\n");
|
|
1697
|
+
}
|
|
1698
|
+
function toApplyPatchFileChanges(params) {
|
|
1699
|
+
const out = [];
|
|
1700
|
+
for (const { path, kind, movePath, diff, content } of fileChangeEntries(params)) {
|
|
1701
|
+
if (kind === "add") {
|
|
1702
|
+
out.push({
|
|
1703
|
+
path,
|
|
1704
|
+
changeKind: "add",
|
|
1705
|
+
...content !== void 0 ? { content: { kind: "replace", newText: content } } : {}
|
|
1706
|
+
});
|
|
1707
|
+
} else if (kind === "delete") {
|
|
1708
|
+
out.push({
|
|
1709
|
+
path,
|
|
1710
|
+
changeKind: "delete",
|
|
1711
|
+
...content !== void 0 ? { content: { kind: "replace", oldText: content, newText: "" } } : {}
|
|
1712
|
+
});
|
|
1713
|
+
} else if (movePath) {
|
|
1714
|
+
out.push({
|
|
1715
|
+
path,
|
|
1716
|
+
changeKind: "rename",
|
|
1717
|
+
...diff ? { content: { kind: "unified", diff } } : {}
|
|
1718
|
+
});
|
|
1719
|
+
} else {
|
|
1720
|
+
out.push({
|
|
1721
|
+
path,
|
|
1722
|
+
changeKind: "modify",
|
|
1723
|
+
...diff ? { content: { kind: "unified", diff } } : {}
|
|
1724
|
+
});
|
|
1952
1725
|
}
|
|
1953
1726
|
}
|
|
1954
|
-
|
|
1955
|
-
if (name === void 0) {
|
|
1956
|
-
return `${providerMessage ?? "Codex usage limit reached."}${step ?? ""}`;
|
|
1957
|
-
}
|
|
1958
|
-
return `Codex usage limit reached. The ${name} limit resets in ${formatUsageLimitWait(waitMs)}.${step ?? " Send the message again once the limit resets."}`;
|
|
1959
|
-
};
|
|
1960
|
-
var isUsageLimitExceeded = (error) => asRecord2(error)?.["codexErrorInfo"] === "usageLimitExceeded";
|
|
1961
|
-
|
|
1962
|
-
// src/events.ts
|
|
1963
|
-
var CODEX_PROVIDER = "codex";
|
|
1964
|
-
var EVENT_ID_PREFIX = `codex-evt-${NodeCrypto.randomUUID()}`;
|
|
1965
|
-
var eventSeq = 0;
|
|
1966
|
-
function nextEventId() {
|
|
1967
|
-
eventSeq += 1;
|
|
1968
|
-
return `${EVENT_ID_PREFIX}-${eventSeq}`;
|
|
1727
|
+
return out;
|
|
1969
1728
|
}
|
|
1970
|
-
function
|
|
1971
|
-
|
|
1729
|
+
function describeCommandApproval(params) {
|
|
1730
|
+
const record = isRecord3(params) ? params : {};
|
|
1731
|
+
const commandValue = record["command"];
|
|
1732
|
+
const command = Array.isArray(commandValue) ? commandValue.filter((entry) => typeof entry === "string").join(" ") : asString(commandValue) ?? "";
|
|
1733
|
+
const cwd = asString(record["cwd"]);
|
|
1734
|
+
const reason = trimText(record["reason"]);
|
|
1735
|
+
const callId = asString(record["callId"]);
|
|
1736
|
+
return {
|
|
1737
|
+
command,
|
|
1738
|
+
...cwd ? { cwd } : {},
|
|
1739
|
+
...reason ? { reason } : {},
|
|
1740
|
+
...callId ? { callId } : {}
|
|
1741
|
+
};
|
|
1972
1742
|
}
|
|
1973
|
-
function
|
|
1974
|
-
|
|
1975
|
-
const
|
|
1976
|
-
|
|
1743
|
+
function describeFileChangeApproval(params) {
|
|
1744
|
+
const record = isRecord3(params) ? params : {};
|
|
1745
|
+
const reason = trimText(record["reason"]);
|
|
1746
|
+
const grantRoot = asString(record["grantRoot"]);
|
|
1747
|
+
const callId = asString(record["callId"]);
|
|
1748
|
+
const detail = reason ?? describeFileChanges(record) ?? trimText(grantRoot);
|
|
1749
|
+
return {
|
|
1750
|
+
...reason ? { reason } : {},
|
|
1751
|
+
...detail ? { detail } : {},
|
|
1752
|
+
...grantRoot ? { grantRoot } : {},
|
|
1753
|
+
...callId ? { callId } : {},
|
|
1754
|
+
fileChanges: toApplyPatchFileChanges(record)
|
|
1755
|
+
};
|
|
1977
1756
|
}
|
|
1978
|
-
function
|
|
1979
|
-
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1757
|
+
function describePermissionsApproval(params) {
|
|
1758
|
+
const record = isRecord3(params) ? params : {};
|
|
1759
|
+
const permissions = isRecord3(record["permissions"]) ? record["permissions"] : {};
|
|
1760
|
+
const fileSystem = isRecord3(permissions["fileSystem"]) ? permissions["fileSystem"] : {};
|
|
1761
|
+
const network = isRecord3(permissions["network"]) ? permissions["network"] : {};
|
|
1762
|
+
const pathsOf = (value) => Array.isArray(value) ? value.flatMap((entry) => typeof entry === "string" && entry.length > 0 ? [entry] : []) : [];
|
|
1763
|
+
const read = pathsOf(fileSystem["read"]);
|
|
1764
|
+
const write = pathsOf(fileSystem["write"]);
|
|
1765
|
+
const entries = Array.isArray(fileSystem["entries"]) ? fileSystem["entries"] : [];
|
|
1766
|
+
for (const entry of entries) {
|
|
1767
|
+
if (!isRecord3(entry) || !isRecord3(entry["path"])) continue;
|
|
1768
|
+
const target = entry["path"];
|
|
1769
|
+
const path = target["type"] === "path" ? asString(target["path"]) : target["type"] === "glob_pattern" ? asString(target["pattern"]) : void 0;
|
|
1770
|
+
if (!path) continue;
|
|
1771
|
+
if (entry["access"] === "read") read.push(path);
|
|
1772
|
+
if (entry["access"] === "write") write.push(path);
|
|
1773
|
+
}
|
|
1774
|
+
const reason = trimText(record["reason"]);
|
|
1775
|
+
return {
|
|
1776
|
+
...reason ? { reason } : {},
|
|
1777
|
+
read: [...new Set(read)],
|
|
1778
|
+
write: [...new Set(write)],
|
|
1779
|
+
network: network["enabled"] === true
|
|
1780
|
+
};
|
|
1982
1781
|
}
|
|
1983
|
-
function
|
|
1984
|
-
if (
|
|
1985
|
-
|
|
1782
|
+
function toPermissionsResponse(decision, params, persistent) {
|
|
1783
|
+
if (decision !== "allow") return { permissions: {} };
|
|
1784
|
+
const requested = isRecord3(params) && isRecord3(params["permissions"]) ? params["permissions"] : {};
|
|
1785
|
+
return {
|
|
1786
|
+
permissions: {
|
|
1787
|
+
...requested["fileSystem"] !== void 0 ? { fileSystem: requested["fileSystem"] } : {},
|
|
1788
|
+
...requested["network"] !== void 0 ? { network: requested["network"] } : {}
|
|
1789
|
+
},
|
|
1790
|
+
scope: persistent ? "session" : "turn"
|
|
1791
|
+
};
|
|
1986
1792
|
}
|
|
1987
|
-
function
|
|
1988
|
-
|
|
1989
|
-
const
|
|
1990
|
-
|
|
1793
|
+
function describeMcpElicitation(params) {
|
|
1794
|
+
const record = isRecord3(params) ? params : {};
|
|
1795
|
+
const appName = asString(record["appName"]) ?? asString(record["serverName"]) ?? void 0;
|
|
1796
|
+
const serverName = asString(record["serverName"]);
|
|
1797
|
+
const message = trimText(record["message"]);
|
|
1798
|
+
const url = asString(record["url"]) ?? asString(record["authorizationUrl"]);
|
|
1799
|
+
const identity = appName ?? url ?? "unknown";
|
|
1800
|
+
return {
|
|
1801
|
+
...appName ? { appName } : {},
|
|
1802
|
+
...serverName ? { serverName } : {},
|
|
1803
|
+
...message ? { message } : {},
|
|
1804
|
+
...url ? { url } : {},
|
|
1805
|
+
identity,
|
|
1806
|
+
mode: url ? "url" : "form",
|
|
1807
|
+
...record["requestedSchema"] !== void 0 ? { schema: record["requestedSchema"] } : {}
|
|
1808
|
+
};
|
|
1991
1809
|
}
|
|
1992
|
-
function
|
|
1993
|
-
if (!isRecord3(
|
|
1994
|
-
const
|
|
1995
|
-
|
|
1996
|
-
if (
|
|
1997
|
-
|
|
1998
|
-
if (joined.length > 0) return joined;
|
|
1810
|
+
function persistFromMeta(meta) {
|
|
1811
|
+
if (!isRecord3(meta)) return void 0;
|
|
1812
|
+
const persist = asString(meta["persist"]) ?? asString(meta["persistence"]);
|
|
1813
|
+
if (persist === "session" || persist === "acceptForSession") return "session";
|
|
1814
|
+
if (persist === "always" || persist === "permanent" || persist === "forever" || persist === "persistent" || persist === "acceptAlways") {
|
|
1815
|
+
return "always";
|
|
1999
1816
|
}
|
|
2000
|
-
return
|
|
1817
|
+
return void 0;
|
|
2001
1818
|
}
|
|
2002
|
-
function
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
return "cancelled";
|
|
2009
|
-
case "interrupted":
|
|
2010
|
-
return "interrupted";
|
|
2011
|
-
default:
|
|
2012
|
-
return "completed";
|
|
1819
|
+
function toMcpElicitationResponse(decision, params) {
|
|
1820
|
+
const described = describeMcpElicitation(params);
|
|
1821
|
+
if (decision !== "allow" || described.mode === "url") {
|
|
1822
|
+
return {
|
|
1823
|
+
action: decision === "allow" ? "decline" : decision === "cancel" ? "cancel" : "decline"
|
|
1824
|
+
};
|
|
2013
1825
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
"
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
]);
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
const
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
return `codex:${threadId}:${turnId ?? "session"}`;
|
|
2037
|
-
}
|
|
2038
|
-
function normalizeCodexTokenUsage(tokenUsage) {
|
|
2039
|
-
if (!isRecord3(tokenUsage)) return void 0;
|
|
2040
|
-
const last = isRecord3(tokenUsage["last"]) ? tokenUsage["last"] : void 0;
|
|
2041
|
-
const total = isRecord3(tokenUsage["total"]) ? tokenUsage["total"] : void 0;
|
|
2042
|
-
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
2043
|
-
const usedTokens = count(last?.["totalTokens"]);
|
|
2044
|
-
if (usedTokens === void 0 || usedTokens <= 0) return void 0;
|
|
2045
|
-
const totalProcessedTokens = count(total?.["totalTokens"]);
|
|
2046
|
-
const maxTokens = count(tokenUsage["modelContextWindow"]);
|
|
2047
|
-
const inputTokens = count(last?.["inputTokens"]);
|
|
2048
|
-
const cachedInputTokens = count(last?.["cachedInputTokens"]);
|
|
2049
|
-
const outputTokens = count(last?.["outputTokens"]);
|
|
2050
|
-
const reasoningOutputTokens = count(last?.["reasoningOutputTokens"]);
|
|
1826
|
+
const schema = isRecord3(described.schema) ? described.schema : void 0;
|
|
1827
|
+
const properties = schema && isRecord3(schema["properties"]) ? schema["properties"] : {};
|
|
1828
|
+
const required = schema && Array.isArray(schema["required"]) ? schema["required"] : [];
|
|
1829
|
+
const content = {};
|
|
1830
|
+
for (const [key, spec] of Object.entries(properties)) {
|
|
1831
|
+
if (!isRecord3(spec)) continue;
|
|
1832
|
+
if (Array.isArray(spec["oneOf"]) || Array.isArray(spec["enum"])) {
|
|
1833
|
+
const options = Array.isArray(spec["oneOf"]) ? spec["oneOf"] : Array.isArray(spec["enum"]) ? spec["enum"] : [];
|
|
1834
|
+
const first = options[0];
|
|
1835
|
+
content[key] = isRecord3(first) ? first["const"] ?? first["value"] ?? first : first;
|
|
1836
|
+
} else if (spec["type"] === "boolean") {
|
|
1837
|
+
content[key] = true;
|
|
1838
|
+
} else if (typeof spec["default"] !== "undefined") {
|
|
1839
|
+
content[key] = spec["default"];
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
for (const key of required) {
|
|
1843
|
+
if (typeof key === "string" && !(key in content)) {
|
|
1844
|
+
return { action: "decline" };
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
const persist = persistFromMeta(isRecord3(params) ? params["_meta"] : void 0);
|
|
2051
1848
|
return {
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
...
|
|
2055
|
-
...maxTokens !== void 0 && maxTokens > 0 ? { maxTokens } : {},
|
|
2056
|
-
...inputTokens !== void 0 ? { inputTokens, lastInputTokens: inputTokens } : {},
|
|
2057
|
-
...cachedInputTokens !== void 0 ? { cachedInputTokens, lastCachedInputTokens: cachedInputTokens } : {},
|
|
2058
|
-
...outputTokens !== void 0 ? { outputTokens, lastOutputTokens: outputTokens } : {},
|
|
2059
|
-
...reasoningOutputTokens !== void 0 ? { reasoningOutputTokens, lastReasoningOutputTokens: reasoningOutputTokens } : {},
|
|
2060
|
-
compactsAutomatically: true
|
|
1849
|
+
action: "accept",
|
|
1850
|
+
content,
|
|
1851
|
+
...persist ? { _meta: { persist } } : {}
|
|
2061
1852
|
};
|
|
2062
1853
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
1854
|
+
|
|
1855
|
+
// src/codexArtifacts.ts
|
|
1856
|
+
function imageGenerationArtifacts(input) {
|
|
1857
|
+
if (input.itemType !== "imageGeneration" || input.status !== "completed") return [];
|
|
1858
|
+
if (!isRecord3(input.params)) return [];
|
|
1859
|
+
const item = isRecord3(input.params["item"]) ? input.params["item"] : input.params;
|
|
1860
|
+
const savedPath = item["savedPath"];
|
|
1861
|
+
if (typeof savedPath !== "string" || savedPath.trim().length === 0) return [];
|
|
1862
|
+
return [{ path: savedPath, ...input.itemId ? { toolCallId: input.itemId } : {} }];
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
// src/codexItemTypes.ts
|
|
1866
|
+
function normalizeItemType(value) {
|
|
1867
|
+
if (!value) return "item";
|
|
1868
|
+
return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[._/-]/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1869
|
+
}
|
|
1870
|
+
function toCanonicalItemType(raw) {
|
|
1871
|
+
const type = normalizeItemType(raw);
|
|
1872
|
+
if (type.includes("user")) return "user_message";
|
|
1873
|
+
if (type.includes("agent message") || type.includes("assistant")) return "assistant_message";
|
|
1874
|
+
if (type.includes("reasoning") || type.includes("thought")) return "reasoning";
|
|
1875
|
+
if (type.includes("plan") || type.includes("todo")) return "plan";
|
|
1876
|
+
if (type.includes("command")) return "command_execution";
|
|
1877
|
+
if (type.includes("file change") || type.includes("patch") || type.includes("edit")) {
|
|
1878
|
+
return "file_change";
|
|
1879
|
+
}
|
|
1880
|
+
if (type.includes("mcp")) return "mcp_tool_call";
|
|
1881
|
+
if (type.includes("dynamic tool")) return "dynamic_tool_call";
|
|
1882
|
+
if (type.includes("collab")) return "collab_agent_tool_call";
|
|
1883
|
+
if (type.includes("web search")) return "web_search";
|
|
1884
|
+
if (type.includes("image")) return "image_view";
|
|
1885
|
+
if (type.includes("review entered")) return "review_entered";
|
|
1886
|
+
if (type.includes("review exited")) return "review_exited";
|
|
1887
|
+
if (type.includes("compact")) return "context_compaction";
|
|
1888
|
+
if (type.includes("error")) return "error";
|
|
1889
|
+
return "unknown";
|
|
2068
1890
|
}
|
|
1891
|
+
|
|
1892
|
+
// src/events/userInput.ts
|
|
2069
1893
|
function toUserInputQuestions(params) {
|
|
2070
1894
|
if (!isRecord3(params)) return void 0;
|
|
2071
1895
|
const questions = Array.isArray(params["questions"]) ? params["questions"] : [];
|
|
@@ -2148,169 +1972,322 @@ function toCanonicalUserInputAnswers(answers) {
|
|
|
2148
1972
|
])
|
|
2149
1973
|
);
|
|
2150
1974
|
}
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
1975
|
+
|
|
1976
|
+
// src/usage/codexQuota.ts
|
|
1977
|
+
var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1978
|
+
var asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1979
|
+
var asNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
1980
|
+
var clampPercent = (value) => Math.min(100, Math.max(0, value));
|
|
1981
|
+
var epochSecondsToIso = (value) => {
|
|
1982
|
+
const seconds = asFiniteNumber(value);
|
|
1983
|
+
if (seconds === null) return null;
|
|
1984
|
+
const date = new Date(seconds * 1e3);
|
|
1985
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
1986
|
+
};
|
|
1987
|
+
var SESSION_WINDOW_MINUTES = 5 * 60;
|
|
1988
|
+
var WEEKLY_WINDOW_MINUTES = 7 * 24 * 60;
|
|
1989
|
+
var formatWindowDuration = (minutes) => {
|
|
1990
|
+
if (minutes % (24 * 60) === 0) return `${String(minutes / (24 * 60))}d`;
|
|
1991
|
+
if (minutes % 60 === 0) return `${String(minutes / 60)}h`;
|
|
1992
|
+
return `${String(minutes)}m`;
|
|
1993
|
+
};
|
|
1994
|
+
var classifyWindow = (minutes, slot) => {
|
|
1995
|
+
if (minutes === SESSION_WINDOW_MINUTES) {
|
|
1996
|
+
return { id: "session", label: { key: "usage.window.session" } };
|
|
1997
|
+
}
|
|
1998
|
+
if (minutes === WEEKLY_WINDOW_MINUTES) {
|
|
1999
|
+
return { id: "weekly", label: { key: "usage.window.weekly" } };
|
|
2000
|
+
}
|
|
2001
|
+
if (minutes === null) return { id: slot, label: { key: "usage.window.limit" } };
|
|
2160
2002
|
return {
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
threadId: input.threadId,
|
|
2164
|
-
createdAt: nowIso2(),
|
|
2165
|
-
type: input.type,
|
|
2166
|
-
payload: input.payload,
|
|
2167
|
-
...input.providerInstanceId ? { providerInstanceId: input.providerInstanceId } : {},
|
|
2168
|
-
...input.turnId ? { turnId: input.turnId } : {},
|
|
2169
|
-
...input.itemId ? { itemId: input.itemId } : {},
|
|
2170
|
-
...input.requestId ? { requestId: input.requestId } : {},
|
|
2171
|
-
...input.method ? {
|
|
2172
|
-
raw: eventRaw(
|
|
2173
|
-
input.source ?? "codex.app-server.notification",
|
|
2174
|
-
input.method,
|
|
2175
|
-
input.payload
|
|
2176
|
-
)
|
|
2177
|
-
} : {}
|
|
2003
|
+
id: `window-${String(minutes)}m`,
|
|
2004
|
+
label: { key: "usage.window.duration", values: { duration: formatWindowDuration(minutes) } }
|
|
2178
2005
|
};
|
|
2179
|
-
}
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
const
|
|
2183
|
-
if (
|
|
2184
|
-
const
|
|
2185
|
-
const
|
|
2186
|
-
const
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
...knownName ? { title: knownName } : {},
|
|
2194
|
-
...model ? { model } : {},
|
|
2195
|
-
...effort ? { effort } : {},
|
|
2196
|
-
...agentPath ? { agentPath } : {},
|
|
2197
|
-
timelineBypass: true,
|
|
2198
|
-
rosterKind: "subagent"
|
|
2006
|
+
};
|
|
2007
|
+
var mapWindow = (raw, slot, describe) => {
|
|
2008
|
+
const window = asRecord(raw);
|
|
2009
|
+
const usedPercent = asFiniteNumber(window?.["usedPercent"]);
|
|
2010
|
+
if (window === null || usedPercent === null) return null;
|
|
2011
|
+
const rawMinutes = asFiniteNumber(window["windowDurationMins"]);
|
|
2012
|
+
const minutes = rawMinutes !== null && rawMinutes > 0 ? Math.trunc(rawMinutes) : null;
|
|
2013
|
+
const { id, label } = describe(minutes, slot);
|
|
2014
|
+
return {
|
|
2015
|
+
id,
|
|
2016
|
+
label,
|
|
2017
|
+
usedPercent: clampPercent(usedPercent),
|
|
2018
|
+
resetsAt: epochSecondsToIso(window["resetsAt"]),
|
|
2019
|
+
...minutes === null ? {} : { windowMs: minutes * 6e4 }
|
|
2199
2020
|
};
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2021
|
+
};
|
|
2022
|
+
var PLAN_LABELS = {
|
|
2023
|
+
free: "Free",
|
|
2024
|
+
go: "Go",
|
|
2025
|
+
plus: "Plus",
|
|
2026
|
+
pro: "Pro",
|
|
2027
|
+
prolite: "Pro Lite",
|
|
2028
|
+
team: "Team",
|
|
2029
|
+
business: "Business",
|
|
2030
|
+
enterprise: "Enterprise",
|
|
2031
|
+
edu: "Edu"
|
|
2032
|
+
};
|
|
2033
|
+
var planLabel = (planType) => {
|
|
2034
|
+
const id = asNonEmptyString(planType);
|
|
2035
|
+
if (id === null || id === "unknown") return void 0;
|
|
2036
|
+
return PLAN_LABELS[id] ?? id.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
2037
|
+
};
|
|
2038
|
+
var creditBalance = (value) => {
|
|
2039
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
2040
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
2041
|
+
const numeric = Number(value.trim());
|
|
2042
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
2043
|
+
};
|
|
2044
|
+
var MAIN_LIMIT_ID = "codex";
|
|
2045
|
+
var CREDITS_BALANCE_ID = "credits";
|
|
2046
|
+
var isMainCodexLimit = (snapshot) => {
|
|
2047
|
+
const limitId = asNonEmptyString(asRecord(snapshot)?.["limitId"]);
|
|
2048
|
+
return limitId === null || limitId === MAIN_LIMIT_ID;
|
|
2049
|
+
};
|
|
2050
|
+
var mapCodexSnapshotWindows = (snapshot) => {
|
|
2051
|
+
const bucket = asRecord(snapshot);
|
|
2052
|
+
if (bucket === null || !isMainCodexLimit(bucket)) return [];
|
|
2053
|
+
const windows = [];
|
|
2054
|
+
const takenIds = /* @__PURE__ */ new Set();
|
|
2055
|
+
for (const slot of ["primary", "secondary"]) {
|
|
2056
|
+
const window = mapWindow(bucket[slot], slot, classifyWindow);
|
|
2057
|
+
if (window === null) continue;
|
|
2058
|
+
const id = takenIds.has(window.id) ? `${window.id}:${slot}` : window.id;
|
|
2059
|
+
takenIds.add(id);
|
|
2060
|
+
windows.push({ ...window, id, group: MAIN_LIMIT_ID });
|
|
2212
2061
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2062
|
+
return windows;
|
|
2063
|
+
};
|
|
2064
|
+
var mapCodexRateLimits = (response, accountKey) => {
|
|
2065
|
+
const root = asRecord(response);
|
|
2066
|
+
const legacy = asRecord(root?.["rateLimits"]);
|
|
2067
|
+
const byLimitId = asRecord(root?.["rateLimitsByLimitId"]);
|
|
2068
|
+
const codexBucket = asRecord(byLimitId?.[MAIN_LIMIT_ID]);
|
|
2069
|
+
const main = codexBucket ?? (legacy !== null && isMainCodexLimit(legacy) ? legacy : null);
|
|
2070
|
+
if (root === null || legacy === null && codexBucket === null) return null;
|
|
2071
|
+
const windows = mapCodexSnapshotWindows(main);
|
|
2072
|
+
const takenIds = new Set(windows.map((window) => window.id));
|
|
2073
|
+
const push = (window, slot) => {
|
|
2074
|
+
if (window === null) return;
|
|
2075
|
+
const id = takenIds.has(window.id) ? `${window.id}:${slot}` : window.id;
|
|
2076
|
+
takenIds.add(id);
|
|
2077
|
+
windows.push({ ...window, id });
|
|
2078
|
+
};
|
|
2079
|
+
const others = /* @__PURE__ */ new Map();
|
|
2080
|
+
for (const [limitId, rawBucket] of Object.entries(byLimitId ?? {})) {
|
|
2081
|
+
const bucket = asRecord(rawBucket);
|
|
2082
|
+
if (limitId !== MAIN_LIMIT_ID && bucket !== null) others.set(limitId, bucket);
|
|
2083
|
+
}
|
|
2084
|
+
const legacyLimitId = asNonEmptyString(legacy?.["limitId"]);
|
|
2085
|
+
if (legacy !== null && legacyLimitId !== null && !isMainCodexLimit(legacy)) {
|
|
2086
|
+
if (!others.has(legacyLimitId)) others.set(legacyLimitId, legacy);
|
|
2087
|
+
}
|
|
2088
|
+
for (const [limitId, bucket] of others) {
|
|
2089
|
+
const name = asNonEmptyString(bucket["limitName"]) ?? limitId;
|
|
2090
|
+
for (const slot of ["primary", "secondary"]) {
|
|
2091
|
+
const window = mapWindow(bucket[slot], slot, (minutes) => ({
|
|
2092
|
+
id: `${limitId}:${slot}`,
|
|
2093
|
+
label: minutes === null ? { text: name } : {
|
|
2094
|
+
key: "usage.window.named",
|
|
2095
|
+
values: { name, duration: formatWindowDuration(minutes) }
|
|
2096
|
+
}
|
|
2097
|
+
}));
|
|
2098
|
+
push(window === null ? null : { ...window, group: limitId }, slot);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
const balances = [];
|
|
2102
|
+
const credits = asRecord(main?.["credits"]);
|
|
2103
|
+
const balance = creditBalance(credits?.["balance"]);
|
|
2104
|
+
if (credits !== null && credits["unlimited"] !== true && balance !== null) {
|
|
2105
|
+
if (credits["hasCredits"] === true || balance > 0) {
|
|
2106
|
+
balances.push({
|
|
2107
|
+
id: CREDITS_BALANCE_ID,
|
|
2108
|
+
label: { key: "usage.balance.credits" },
|
|
2109
|
+
remaining: balance,
|
|
2110
|
+
used: null,
|
|
2111
|
+
limit: null,
|
|
2112
|
+
unit: { kind: "credits" },
|
|
2113
|
+
resetsAt: null
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
const plan = planLabel((main ?? legacy)?.["planType"]);
|
|
2118
|
+
const resetSummary = asRecord(root["rateLimitResetCredits"]);
|
|
2119
|
+
const availableCount = asFiniteNumber(resetSummary?.["availableCount"]);
|
|
2120
|
+
return {
|
|
2121
|
+
status: "available",
|
|
2122
|
+
...plan === void 0 ? {} : { plan },
|
|
2123
|
+
windows,
|
|
2124
|
+
balances,
|
|
2125
|
+
accountKey,
|
|
2126
|
+
// 回包没带摘要(老版本 CLI)就是不知道,不填;填 0 会让界面说「没有可用的重置额度」。
|
|
2127
|
+
...availableCount === null || availableCount < 0 ? {} : { resetCredits: { availableCount: Math.trunc(availableCount) } }
|
|
2128
|
+
};
|
|
2129
|
+
};
|
|
2130
|
+
var codexQuotaFromRateLimits = (snapshot) => {
|
|
2131
|
+
const bucket = asRecord(snapshot);
|
|
2132
|
+
if (bucket === null) return null;
|
|
2133
|
+
const reading = mapCodexRateLimits({ rateLimits: snapshot }, "");
|
|
2134
|
+
if (reading === null || reading.status !== "available") return null;
|
|
2135
|
+
const main = isMainCodexLimit(bucket);
|
|
2136
|
+
const group = main ? MAIN_LIMIT_ID : asNonEmptyString(bucket["limitId"]);
|
|
2137
|
+
const replaces = {
|
|
2138
|
+
windowGroups: group === null ? [] : [group],
|
|
2139
|
+
balanceIds: main ? [CREDITS_BALANCE_ID] : []
|
|
2140
|
+
};
|
|
2141
|
+
return {
|
|
2142
|
+
windows: reading.windows,
|
|
2143
|
+
balances: reading.balances,
|
|
2144
|
+
...reading.plan === void 0 ? {} : { plan: reading.plan },
|
|
2145
|
+
replaces
|
|
2146
|
+
};
|
|
2147
|
+
};
|
|
2148
|
+
var classifyRateLimitsError = (message) => {
|
|
2149
|
+
const text = message.toLowerCase();
|
|
2150
|
+
if (text.includes("chatgpt authentication required")) return "unsupportedLogin";
|
|
2151
|
+
if (text.includes("authentication required")) return "missingCredentials";
|
|
2152
|
+
if (/\b(401|403)\b|unauthorized|forbidden/.test(text)) return "authRejected";
|
|
2153
|
+
if (/\b429\b|too many requests/.test(text)) return "rateLimited";
|
|
2154
|
+
return "unreachable";
|
|
2155
|
+
};
|
|
2156
|
+
var CODEX_OFFICIAL_HOSTS = /* @__PURE__ */ new Set(["chatgpt.com"]);
|
|
2157
|
+
var CODEX_DEFAULT_MODEL_PROVIDER = "openai";
|
|
2158
|
+
var isOfficialUrl = (value) => {
|
|
2159
|
+
const parsed = URL.parse(value.trim());
|
|
2160
|
+
return parsed === null ? false : CODEX_OFFICIAL_HOSTS.has(parsed.hostname.toLowerCase());
|
|
2161
|
+
};
|
|
2162
|
+
var readTomlScalar = (raw) => {
|
|
2163
|
+
const quoted = /^(["'])(.*?)\1/.exec(raw.trim());
|
|
2164
|
+
if (quoted?.[2] !== void 0) return quoted[2];
|
|
2165
|
+
const [beforeComment = ""] = raw.split("#");
|
|
2166
|
+
return beforeComment.trim();
|
|
2167
|
+
};
|
|
2168
|
+
var TABLE_HEADER = /^\[\s*([^\][]+?)\s*\]\s*(?:#.*)?$/;
|
|
2169
|
+
var KEY_VALUE = /^([A-Za-z0-9_.\-"']+)\s*=\s*(.*)$/;
|
|
2170
|
+
var BASE_URL_KEYS = /* @__PURE__ */ new Set([
|
|
2171
|
+
"chatgpt_base_url",
|
|
2172
|
+
"openai_base_url",
|
|
2173
|
+
"base_url"
|
|
2174
|
+
]);
|
|
2175
|
+
var codexConfigSignal = (contents, options) => {
|
|
2176
|
+
let table = null;
|
|
2177
|
+
let selectedProvider = CODEX_DEFAULT_MODEL_PROVIDER;
|
|
2178
|
+
const providerBaseUrls = /* @__PURE__ */ new Map();
|
|
2179
|
+
for (const rawLine of contents.split("\n")) {
|
|
2180
|
+
const line = rawLine.trim();
|
|
2181
|
+
if (line.length === 0 || line.startsWith("#")) continue;
|
|
2182
|
+
const header = TABLE_HEADER.exec(line);
|
|
2183
|
+
if (header !== null) {
|
|
2184
|
+
table = header[1] ?? "";
|
|
2185
|
+
if (table === "model_providers") return "config.toml:model_providers";
|
|
2186
|
+
continue;
|
|
2187
|
+
}
|
|
2188
|
+
const assignment = KEY_VALUE.exec(line);
|
|
2189
|
+
if (assignment === null) continue;
|
|
2190
|
+
const key = assignment[1] ?? "";
|
|
2191
|
+
const value = readTomlScalar(assignment[2] ?? "");
|
|
2192
|
+
if (table === null) {
|
|
2193
|
+
if (key === "model_provider") {
|
|
2194
|
+
selectedProvider = value;
|
|
2195
|
+
} else if (key === "profile" || key.startsWith("model_providers")) {
|
|
2196
|
+
return `config.toml:${key.split(".")[0] ?? key}`;
|
|
2197
|
+
} else if (BASE_URL_KEYS.has(key) && !isOfficialUrl(value)) {
|
|
2198
|
+
return `config.toml:${key}`;
|
|
2199
|
+
}
|
|
2200
|
+
continue;
|
|
2201
|
+
}
|
|
2202
|
+
if (table.startsWith("model_providers.") && key === "base_url") {
|
|
2203
|
+
providerBaseUrls.set(
|
|
2204
|
+
table.slice("model_providers.".length).replace(/^["']|["']$/g, ""),
|
|
2205
|
+
value
|
|
2206
|
+
);
|
|
2207
|
+
}
|
|
2230
2208
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2209
|
+
const selected = options.providerOverridden ? CODEX_DEFAULT_MODEL_PROVIDER : selectedProvider;
|
|
2210
|
+
if (selected === CODEX_DEFAULT_MODEL_PROVIDER) return null;
|
|
2211
|
+
const baseUrl = providerBaseUrls.get(selected);
|
|
2212
|
+
if (baseUrl === void 0 || !isOfficialUrl(baseUrl)) return "config.toml:model_provider";
|
|
2213
|
+
return null;
|
|
2214
|
+
};
|
|
2215
|
+
var LAUNCH_ARG_MARKERS = [
|
|
2216
|
+
"model_provider",
|
|
2217
|
+
"chatgpt_base_url",
|
|
2218
|
+
"openai_base_url",
|
|
2219
|
+
"base_url",
|
|
2220
|
+
"--profile"
|
|
2221
|
+
];
|
|
2222
|
+
var codexLaunchArgsSignal = (launchArgs) => {
|
|
2223
|
+
for (const marker of LAUNCH_ARG_MARKERS) {
|
|
2224
|
+
if (launchArgs.includes(marker)) return `launchArgs:${marker}`;
|
|
2242
2225
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
];
|
|
2226
|
+
return null;
|
|
2227
|
+
};
|
|
2228
|
+
|
|
2229
|
+
// src/usageLimit.ts
|
|
2230
|
+
var asRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
2231
|
+
var mergeCodexRateLimits = (previous, update) => {
|
|
2232
|
+
const next = asRecord2(update);
|
|
2233
|
+
if (next === void 0 || !isMainCodexLimit(next)) return previous;
|
|
2234
|
+
if (previous === void 0) return next;
|
|
2235
|
+
const merged = { ...previous };
|
|
2236
|
+
for (const key of ["limitId", "planType", "rateLimitReachedType", "primary", "secondary"]) {
|
|
2237
|
+
if (next[key] !== void 0) merged[key] = next[key];
|
|
2255
2238
|
}
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2239
|
+
return merged;
|
|
2240
|
+
};
|
|
2241
|
+
var formatUsageLimitWait = (waitMs) => {
|
|
2242
|
+
const totalMinutes = Math.max(1, Math.ceil(waitMs / 6e4));
|
|
2243
|
+
const days = Math.floor(totalMinutes / (24 * 60));
|
|
2244
|
+
const hours = Math.floor(totalMinutes % (24 * 60) / 60);
|
|
2245
|
+
const minutes = totalMinutes % 60;
|
|
2246
|
+
if (days > 0) return hours === 0 ? `${String(days)}d` : `${String(days)}d ${String(hours)}h`;
|
|
2247
|
+
if (hours === 0) return `${String(totalMinutes)}m`;
|
|
2248
|
+
return minutes === 0 ? `${String(hours)}h` : `${String(hours)}h ${String(minutes)}m`;
|
|
2249
|
+
};
|
|
2250
|
+
var windowName = (window) => {
|
|
2251
|
+
if (window.id === "session") return "5-hour";
|
|
2252
|
+
if (window.id === "weekly") return "weekly";
|
|
2253
|
+
return window.windowMs === void 0 ? "usage" : formatWindowDuration(window.windowMs / 6e4);
|
|
2254
|
+
};
|
|
2255
|
+
var nextStep = (rateLimitReachedType) => {
|
|
2256
|
+
switch (rateLimitReachedType) {
|
|
2257
|
+
case "workspace_owner_credits_depleted":
|
|
2258
|
+
case "workspace_member_credits_depleted":
|
|
2259
|
+
return " The workspace has no credits left to continue sooner: ask the workspace owner to add credits, or send the message again once the limit resets.";
|
|
2260
|
+
case "workspace_owner_usage_limit_reached":
|
|
2261
|
+
case "workspace_member_usage_limit_reached":
|
|
2262
|
+
return " The workspace spend limit is reached: ask the workspace owner to raise it, or send the message again once the limit resets.";
|
|
2263
|
+
default:
|
|
2264
|
+
return void 0;
|
|
2266
2265
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2266
|
+
};
|
|
2267
|
+
var codexUsageLimitMessage = (snapshot, atIso, providerMessage) => {
|
|
2268
|
+
const atMs = Date.parse(atIso);
|
|
2269
|
+
let name;
|
|
2270
|
+
let waitMs = 0;
|
|
2271
|
+
let latestResetMs = Number.NEGATIVE_INFINITY;
|
|
2272
|
+
if (snapshot !== void 0 && Number.isFinite(atMs)) {
|
|
2273
|
+
for (const window of mapCodexSnapshotWindows(snapshot)) {
|
|
2274
|
+
if (window.usedPercent < 100 || window.resetsAt === null) continue;
|
|
2275
|
+
const resetMs = Date.parse(window.resetsAt);
|
|
2276
|
+
if (!Number.isFinite(resetMs) || resetMs <= atMs || resetMs <= latestResetMs) continue;
|
|
2277
|
+
latestResetMs = resetMs;
|
|
2278
|
+
name = windowName(window);
|
|
2279
|
+
waitMs = resetMs - atMs;
|
|
2280
|
+
}
|
|
2279
2281
|
}
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
2284
|
-
const totalTokens = total ? count(total["totalTokens"]) : void 0;
|
|
2285
|
-
if (total === void 0 || totalTokens === void 0) return [];
|
|
2286
|
-
const inputTokens = count(total["inputTokens"]);
|
|
2287
|
-
const cachedInputTokens = count(total["cachedInputTokens"]);
|
|
2288
|
-
const outputTokens = count(total["outputTokens"]);
|
|
2289
|
-
const reasoningOutputTokens = count(total["reasoningOutputTokens"]);
|
|
2290
|
-
const typedUsage = {
|
|
2291
|
-
totalTokens,
|
|
2292
|
-
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
2293
|
-
...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
|
|
2294
|
-
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
2295
|
-
...reasoningOutputTokens !== void 0 ? { reasoningOutputTokens } : {}
|
|
2296
|
-
};
|
|
2297
|
-
return [
|
|
2298
|
-
baseEvent({
|
|
2299
|
-
type: "task.progress",
|
|
2300
|
-
threadId,
|
|
2301
|
-
payload: {
|
|
2302
|
-
taskId: agentThreadId,
|
|
2303
|
-
description: title,
|
|
2304
|
-
typedUsage,
|
|
2305
|
-
...linkage
|
|
2306
|
-
},
|
|
2307
|
-
method,
|
|
2308
|
-
providerInstanceId: input.providerInstanceId
|
|
2309
|
-
})
|
|
2310
|
-
];
|
|
2282
|
+
const step = nextStep(snapshot?.["rateLimitReachedType"]);
|
|
2283
|
+
if (name === void 0) {
|
|
2284
|
+
return `${providerMessage ?? "Codex usage limit reached."}${step ?? ""}`;
|
|
2311
2285
|
}
|
|
2312
|
-
return
|
|
2313
|
-
}
|
|
2286
|
+
return `Codex usage limit reached. The ${name} limit resets in ${formatUsageLimitWait(waitMs)}.${step ?? " Send the message again once the limit resets."}`;
|
|
2287
|
+
};
|
|
2288
|
+
var isUsageLimitExceeded = (error) => asRecord2(error)?.["codexErrorInfo"] === "usageLimitExceeded";
|
|
2289
|
+
|
|
2290
|
+
// src/events.ts
|
|
2314
2291
|
function mapNotificationToEvents(input) {
|
|
2315
2292
|
const { method, params, threadId, providerInstanceId, rateLimits } = input;
|
|
2316
2293
|
if (method.startsWith("collabAgent/")) {
|
|
@@ -2880,6 +2857,8 @@ function mapNotificationToEvents(input) {
|
|
|
2880
2857
|
return [];
|
|
2881
2858
|
}
|
|
2882
2859
|
}
|
|
2860
|
+
|
|
2861
|
+
// src/events/serverRequests.ts
|
|
2883
2862
|
function toRequestTypeFromKind(kind) {
|
|
2884
2863
|
switch (kind) {
|
|
2885
2864
|
case "command":
|
|
@@ -3150,6 +3129,7 @@ var CodexSessionRuntime = class {
|
|
|
3150
3129
|
/** 这个会话见过的主额度快照(按字段合并),额度用尽停轮时点名窗口用。 */
|
|
3151
3130
|
rateLimits;
|
|
3152
3131
|
appServerArgs;
|
|
3132
|
+
hasConfiguredMcpServer;
|
|
3153
3133
|
host;
|
|
3154
3134
|
attachments;
|
|
3155
3135
|
rpc;
|
|
@@ -3192,6 +3172,7 @@ var CodexSessionRuntime = class {
|
|
|
3192
3172
|
this.runtimeMode = input.runtimeMode;
|
|
3193
3173
|
this.nativeModeId = input.nativeModeId;
|
|
3194
3174
|
this.appServerArgs = appServerArgs;
|
|
3175
|
+
this.hasConfiguredMcpServer = (input.mcpServers?.length ?? 0) > 0;
|
|
3195
3176
|
this.grants = grants ?? createApprovalGrantStore();
|
|
3196
3177
|
this.declineRelay = declineRelay ?? createDeclineReasonRelay();
|
|
3197
3178
|
const createdAt = nowIso();
|
|
@@ -3711,21 +3692,14 @@ var CodexSessionRuntime = class {
|
|
|
3711
3692
|
}
|
|
3712
3693
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
3713
3694
|
}
|
|
3714
|
-
threadConfigParams() {
|
|
3715
|
-
const config = this.#threadConfig();
|
|
3716
|
-
return {
|
|
3717
|
-
approvalPolicy: config.approvalPolicy,
|
|
3718
|
-
sandbox: config.sandbox,
|
|
3719
|
-
approvalsReviewer: config.approvalsReviewer,
|
|
3720
|
-
cwd: this.cwd
|
|
3721
|
-
};
|
|
3722
|
-
}
|
|
3723
3695
|
async openThread(input) {
|
|
3724
3696
|
const resume = input.discardResumeCursor === true ? void 0 : parseCodexResumeCursor(input.resumeCursor);
|
|
3725
3697
|
const requestedModel = modelIdFromSelection(input.modelSelection);
|
|
3726
3698
|
const startParams = {
|
|
3727
|
-
...this
|
|
3728
|
-
|
|
3699
|
+
...this.#threadConfig(),
|
|
3700
|
+
cwd: this.cwd,
|
|
3701
|
+
...requestedModel ? { model: requestedModel } : {},
|
|
3702
|
+
...this.hasConfiguredMcpServer ? { config: codexMcpThreadConfig(input.mcpServers ?? []) } : {}
|
|
3729
3703
|
};
|
|
3730
3704
|
const result = resume ? await this.withActiveWriterRecovery(
|
|
3731
3705
|
() => this.rpc.request("thread/resume", { threadId: resume.threadId, ...startParams }, 0),
|
|
@@ -3762,7 +3736,7 @@ var CodexSessionRuntime = class {
|
|
|
3762
3736
|
return mode;
|
|
3763
3737
|
}
|
|
3764
3738
|
developerInstructions(input, mode) {
|
|
3765
|
-
const tools =
|
|
3739
|
+
const tools = this.hasConfiguredMcpServer;
|
|
3766
3740
|
const model = this.model ?? DEFAULT_MODEL;
|
|
3767
3741
|
const effort = modelSelectionStringOption(input.modelSelection, "reasoningEffort") ?? DEFAULT_TEXT_GENERATION_REASONING_EFFORT;
|
|
3768
3742
|
return buildCodexDeveloperInstructions(mode, { model, reasoningEffort: effort }, tools, tools);
|
|
@@ -3799,7 +3773,7 @@ var CodexSessionRuntime = class {
|
|
|
3799
3773
|
}
|
|
3800
3774
|
async sendTurn(input) {
|
|
3801
3775
|
if (!this.nativeThreadId) throw new Error("codex session has no native thread");
|
|
3802
|
-
if (
|
|
3776
|
+
if (this.hasConfiguredMcpServer) {
|
|
3803
3777
|
await this.rpc.request("config/mcpServer/reload", void 0, 0).catch((error) => {
|
|
3804
3778
|
this.host.log.warn("refreshing the codex MCP tool list before a turn failed", {
|
|
3805
3779
|
threadId: this.threadId,
|
|
@@ -4179,17 +4153,35 @@ async function readRolloutModel(input, rolloutPath, sameDirectory) {
|
|
|
4179
4153
|
}
|
|
4180
4154
|
|
|
4181
4155
|
// src/mcpOAuth.ts
|
|
4156
|
+
function codexMcpOAuthLaunchConfig(name, transport) {
|
|
4157
|
+
const key = `mcp_servers.${name}`;
|
|
4158
|
+
const args = ["-c", `${key}.url=${transport.url}`];
|
|
4159
|
+
const environment = {};
|
|
4160
|
+
const bearer = transport.headers?.["Authorization"]?.replace(/^Bearer\s+/, "");
|
|
4161
|
+
if (bearer) {
|
|
4162
|
+
const envVar = name === "ycodium" ? "YCODIUM_MCP_BEARER_TOKEN" : `YCODIUM_MCP_TOKEN_${name.toUpperCase().replaceAll(/[^A-Z0-9]/g, "_")}`;
|
|
4163
|
+
environment[envVar] = bearer;
|
|
4164
|
+
args.push("-c", `${key}.bearer_token_env_var=${JSON.stringify(envVar)}`);
|
|
4165
|
+
}
|
|
4166
|
+
return { args, environment };
|
|
4167
|
+
}
|
|
4182
4168
|
async function startCodexMcpOAuth(input) {
|
|
4183
4169
|
if (input.recipe.binaryPath.length === 0) {
|
|
4184
4170
|
throw new Error("Codex program path is not configured");
|
|
4185
4171
|
}
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4172
|
+
if (input.server.transport.type === "stdio") {
|
|
4173
|
+
throw new Error(`MCP OAuth needs an http server: ${input.server.name}`);
|
|
4174
|
+
}
|
|
4175
|
+
const mcp = codexMcpOAuthLaunchConfig(input.server.name, input.server.transport);
|
|
4176
|
+
const env = {
|
|
4177
|
+
...buildCodexSpawnEnv({
|
|
4178
|
+
recipe: input.recipe,
|
|
4179
|
+
layout: input.layout,
|
|
4180
|
+
crushApiKeys: shouldCrushApiKeys(input.recipe.authMode)
|
|
4181
|
+
}),
|
|
4182
|
+
...input.processEnv,
|
|
4183
|
+
...mcp.environment
|
|
4184
|
+
};
|
|
4193
4185
|
const { handle } = await spawnCodexAppServer({
|
|
4194
4186
|
host: input.host,
|
|
4195
4187
|
recipe: input.recipe,
|
|
@@ -5906,15 +5898,11 @@ var createCodexInstance = (config, host, attachments, cache) => {
|
|
|
5906
5898
|
threads.delete(input.threadId);
|
|
5907
5899
|
}
|
|
5908
5900
|
const layout = layoutFor(recipe);
|
|
5909
|
-
const mcp = codexMcpLaunchConfig(input.mcpServers ?? []);
|
|
5910
5901
|
const { handle, args } = await spawnCodexAppServer({
|
|
5911
5902
|
host,
|
|
5912
5903
|
recipe,
|
|
5913
5904
|
layout,
|
|
5914
|
-
cwd
|
|
5915
|
-
extraArgs: mcp.args,
|
|
5916
|
-
// MCP 那几个变量是宿主挑好的 server 带来的,铺在最终层:实例表盖不得。
|
|
5917
|
-
launchEnv: mcp.environment
|
|
5905
|
+
cwd
|
|
5918
5906
|
});
|
|
5919
5907
|
trackHandle(handle, [recipe.binaryPath, ...args].join(" "), input.threadId);
|
|
5920
5908
|
let runtime;
|