@ycodium-ai/agent-codex 0.2.3082 → 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 +795 -787
- 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 对单图的明文
|
|
@@ -1381,662 +1381,515 @@ function buildCodexDeveloperInstructions(interactionMode, runtime, browserToolsA
|
|
|
1381
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>`;
|
|
1382
1382
|
}
|
|
1383
1383
|
|
|
1384
|
-
// src/events.ts
|
|
1384
|
+
// src/events/base.ts
|
|
1385
1385
|
import * as NodeCrypto from "node:crypto";
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
const savedPath = item["savedPath"];
|
|
1393
|
-
if (typeof savedPath !== "string" || savedPath.trim().length === 0) return [];
|
|
1394
|
-
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}`;
|
|
1395
1392
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
function normalizeItemType(value) {
|
|
1399
|
-
if (!value) return "item";
|
|
1400
|
-
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();
|
|
1401
1395
|
}
|
|
1402
|
-
function
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
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
|
+
};
|
|
1422
1419
|
}
|
|
1423
1420
|
|
|
1424
|
-
// src/
|
|
1425
|
-
function
|
|
1426
|
-
if (!isRecord3(params)) return
|
|
1421
|
+
// src/events/fields.ts
|
|
1422
|
+
function itemIdOf(params) {
|
|
1423
|
+
if (!isRecord3(params)) return void 0;
|
|
1427
1424
|
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1428
|
-
|
|
1429
|
-
if (!isRecord3(changes) && !Array.isArray(changes)) return [];
|
|
1430
|
-
const entries = Array.isArray(changes) ? changes : Object.entries(changes).map(([path, change]) => ({
|
|
1431
|
-
path,
|
|
1432
|
-
...isRecord3(change) ? change : {}
|
|
1433
|
-
}));
|
|
1434
|
-
const out = [];
|
|
1435
|
-
for (const entry of entries) {
|
|
1436
|
-
if (!isRecord3(entry)) continue;
|
|
1437
|
-
const path = asString(entry["path"]) ?? asString(entry["file"]);
|
|
1438
|
-
if (!path) continue;
|
|
1439
|
-
out.push({
|
|
1440
|
-
path,
|
|
1441
|
-
kind: (asString(entry["type"]) ?? asString(entry["kind"]) ?? "update").toLowerCase(),
|
|
1442
|
-
movePath: asString(entry["move_path"]) ?? asString(entry["movePath"]),
|
|
1443
|
-
diff: asString(entry["unified_diff"]) ?? asString(entry["unifiedDiff"]),
|
|
1444
|
-
content: asString(entry["content"])
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
return out;
|
|
1425
|
+
return asString(item["id"]) ?? asString(params["itemId"]) ?? void 0;
|
|
1448
1426
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
);
|
|
1454
|
-
if (entries.length === 0) return void 0;
|
|
1455
|
-
const lines = entries.slice(0, MAX_DESCRIBED_FILE_CHANGES).map(
|
|
1456
|
-
(entry) => entry.movePath ? `${entry.kind} ${entry.path} -> ${entry.movePath}` : `${entry.kind} ${entry.path}`
|
|
1457
|
-
);
|
|
1458
|
-
const remaining = entries.length - lines.length;
|
|
1459
|
-
return remaining > 0 ? `${lines.join("\n")}
|
|
1460
|
-
+${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;
|
|
1461
1431
|
}
|
|
1462
|
-
function
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
if (kind === "add") {
|
|
1466
|
-
out.push({
|
|
1467
|
-
path,
|
|
1468
|
-
changeKind: "add",
|
|
1469
|
-
...content !== void 0 ? { content: { kind: "replace", newText: content } } : {}
|
|
1470
|
-
});
|
|
1471
|
-
} else if (kind === "delete") {
|
|
1472
|
-
out.push({
|
|
1473
|
-
path,
|
|
1474
|
-
changeKind: "delete",
|
|
1475
|
-
...content !== void 0 ? { content: { kind: "replace", oldText: content, newText: "" } } : {}
|
|
1476
|
-
});
|
|
1477
|
-
} else if (movePath) {
|
|
1478
|
-
out.push({
|
|
1479
|
-
path,
|
|
1480
|
-
changeKind: "rename",
|
|
1481
|
-
...diff ? { content: { kind: "unified", diff } } : {}
|
|
1482
|
-
});
|
|
1483
|
-
} else {
|
|
1484
|
-
out.push({
|
|
1485
|
-
path,
|
|
1486
|
-
changeKind: "modify",
|
|
1487
|
-
...diff ? { content: { kind: "unified", diff } } : {}
|
|
1488
|
-
});
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
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;
|
|
1492
1435
|
}
|
|
1493
|
-
function
|
|
1494
|
-
|
|
1495
|
-
const
|
|
1496
|
-
|
|
1497
|
-
const cwd = asString(record["cwd"]);
|
|
1498
|
-
const reason = trimText(record["reason"]);
|
|
1499
|
-
const callId = asString(record["callId"]);
|
|
1500
|
-
return {
|
|
1501
|
-
command,
|
|
1502
|
-
...cwd ? { cwd } : {},
|
|
1503
|
-
...reason ? { reason } : {},
|
|
1504
|
-
...callId ? { callId } : {}
|
|
1505
|
-
};
|
|
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;
|
|
1506
1440
|
}
|
|
1507
|
-
function
|
|
1508
|
-
|
|
1509
|
-
const
|
|
1510
|
-
const
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
...grantRoot ? { grantRoot } : {},
|
|
1517
|
-
...callId ? { callId } : {},
|
|
1518
|
-
fileChanges: toApplyPatchFileChanges(record)
|
|
1519
|
-
};
|
|
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);
|
|
1520
1450
|
}
|
|
1521
|
-
function
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
const target = entry["path"];
|
|
1533
|
-
const path = target["type"] === "path" ? asString(target["path"]) : target["type"] === "glob_pattern" ? asString(target["pattern"]) : void 0;
|
|
1534
|
-
if (!path) continue;
|
|
1535
|
-
if (entry["access"] === "read") read.push(path);
|
|
1536
|
-
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";
|
|
1537
1462
|
}
|
|
1538
|
-
const reason = trimText(record["reason"]);
|
|
1539
|
-
return {
|
|
1540
|
-
...reason ? { reason } : {},
|
|
1541
|
-
read: [...new Set(read)],
|
|
1542
|
-
write: [...new Set(write)],
|
|
1543
|
-
network: network["enabled"] === true
|
|
1544
|
-
};
|
|
1545
1463
|
}
|
|
1546
|
-
function
|
|
1547
|
-
|
|
1548
|
-
|
|
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";
|
|
1469
|
+
}
|
|
1470
|
+
function codexCompactionId(threadId, turnId) {
|
|
1471
|
+
return `codex:${threadId}:${turnId ?? "session"}`;
|
|
1472
|
+
}
|
|
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"]);
|
|
1549
1486
|
return {
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
},
|
|
1554
|
-
|
|
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
|
|
1555
1496
|
};
|
|
1556
1497
|
}
|
|
1557
|
-
function
|
|
1558
|
-
const
|
|
1559
|
-
const
|
|
1560
|
-
const
|
|
1561
|
-
|
|
1562
|
-
const url = asString(record["url"]) ?? asString(record["authorizationUrl"]);
|
|
1563
|
-
const identity = appName ?? url ?? "unknown";
|
|
1564
|
-
return {
|
|
1565
|
-
...appName ? { appName } : {},
|
|
1566
|
-
...serverName ? { serverName } : {},
|
|
1567
|
-
...message ? { message } : {},
|
|
1568
|
-
...url ? { url } : {},
|
|
1569
|
-
identity,
|
|
1570
|
-
mode: url ? "url" : "form",
|
|
1571
|
-
...record["requestedSchema"] !== void 0 ? { schema: record["requestedSchema"] } : {}
|
|
1572
|
-
};
|
|
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 };
|
|
1573
1503
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1504
|
+
|
|
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;
|
|
1582
1515
|
}
|
|
1583
|
-
function
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
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"
|
|
1544
|
+
};
|
|
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
|
+
];
|
|
1589
1557
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
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
|
+
];
|
|
1605
1575
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
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
|
+
];
|
|
1610
1587
|
}
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
var clampPercent = (value) => Math.min(100, Math.max(0, value));
|
|
1624
|
-
var epochSecondsToIso = (value) => {
|
|
1625
|
-
const seconds = asFiniteNumber(value);
|
|
1626
|
-
if (seconds === null) return null;
|
|
1627
|
-
const date = new Date(seconds * 1e3);
|
|
1628
|
-
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
1629
|
-
};
|
|
1630
|
-
var SESSION_WINDOW_MINUTES = 5 * 60;
|
|
1631
|
-
var WEEKLY_WINDOW_MINUTES = 7 * 24 * 60;
|
|
1632
|
-
var formatWindowDuration = (minutes) => {
|
|
1633
|
-
if (minutes % (24 * 60) === 0) return `${String(minutes / (24 * 60))}d`;
|
|
1634
|
-
if (minutes % 60 === 0) return `${String(minutes / 60)}h`;
|
|
1635
|
-
return `${String(minutes)}m`;
|
|
1636
|
-
};
|
|
1637
|
-
var classifyWindow = (minutes, slot) => {
|
|
1638
|
-
if (minutes === SESSION_WINDOW_MINUTES) {
|
|
1639
|
-
return { id: "session", label: { key: "usage.window.session" } };
|
|
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
|
+
];
|
|
1640
1600
|
}
|
|
1641
|
-
if (
|
|
1642
|
-
return
|
|
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
|
+
];
|
|
1643
1611
|
}
|
|
1644
|
-
if (
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
const { id, label } = describe(minutes, slot);
|
|
1657
|
-
return {
|
|
1658
|
-
id,
|
|
1659
|
-
label,
|
|
1660
|
-
usedPercent: clampPercent(usedPercent),
|
|
1661
|
-
resetsAt: epochSecondsToIso(window["resetsAt"]),
|
|
1662
|
-
...minutes === null ? {} : { windowMs: minutes * 6e4 }
|
|
1663
|
-
};
|
|
1664
|
-
};
|
|
1665
|
-
var PLAN_LABELS = {
|
|
1666
|
-
free: "Free",
|
|
1667
|
-
go: "Go",
|
|
1668
|
-
plus: "Plus",
|
|
1669
|
-
pro: "Pro",
|
|
1670
|
-
prolite: "Pro Lite",
|
|
1671
|
-
team: "Team",
|
|
1672
|
-
business: "Business",
|
|
1673
|
-
enterprise: "Enterprise",
|
|
1674
|
-
edu: "Edu"
|
|
1675
|
-
};
|
|
1676
|
-
var planLabel = (planType) => {
|
|
1677
|
-
const id = asNonEmptyString(planType);
|
|
1678
|
-
if (id === null || id === "unknown") return void 0;
|
|
1679
|
-
return PLAN_LABELS[id] ?? id.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1680
|
-
};
|
|
1681
|
-
var creditBalance = (value) => {
|
|
1682
|
-
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
1683
|
-
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
1684
|
-
const numeric = Number(value.trim());
|
|
1685
|
-
return Number.isFinite(numeric) ? numeric : null;
|
|
1686
|
-
};
|
|
1687
|
-
var MAIN_LIMIT_ID = "codex";
|
|
1688
|
-
var CREDITS_BALANCE_ID = "credits";
|
|
1689
|
-
var isMainCodexLimit = (snapshot) => {
|
|
1690
|
-
const limitId = asNonEmptyString(asRecord(snapshot)?.["limitId"]);
|
|
1691
|
-
return limitId === null || limitId === MAIN_LIMIT_ID;
|
|
1692
|
-
};
|
|
1693
|
-
var mapCodexSnapshotWindows = (snapshot) => {
|
|
1694
|
-
const bucket = asRecord(snapshot);
|
|
1695
|
-
if (bucket === null || !isMainCodexLimit(bucket)) return [];
|
|
1696
|
-
const windows = [];
|
|
1697
|
-
const takenIds = /* @__PURE__ */ new Set();
|
|
1698
|
-
for (const slot of ["primary", "secondary"]) {
|
|
1699
|
-
const window = mapWindow(bucket[slot], slot, classifyWindow);
|
|
1700
|
-
if (window === null) continue;
|
|
1701
|
-
const id = takenIds.has(window.id) ? `${window.id}:${slot}` : window.id;
|
|
1702
|
-
takenIds.add(id);
|
|
1703
|
-
windows.push({ ...window, id, group: MAIN_LIMIT_ID });
|
|
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
|
+
];
|
|
1704
1624
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
label: minutes === null ? { text: name } : {
|
|
1737
|
-
key: "usage.window.named",
|
|
1738
|
-
values: { name, duration: formatWindowDuration(minutes) }
|
|
1739
|
-
}
|
|
1740
|
-
}));
|
|
1741
|
-
push(window === null ? null : { ...window, group: limitId }, slot);
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
const balances = [];
|
|
1745
|
-
const credits = asRecord(main?.["credits"]);
|
|
1746
|
-
const balance = creditBalance(credits?.["balance"]);
|
|
1747
|
-
if (credits !== null && credits["unlimited"] !== true && balance !== null) {
|
|
1748
|
-
if (credits["hasCredits"] === true || balance > 0) {
|
|
1749
|
-
balances.push({
|
|
1750
|
-
id: CREDITS_BALANCE_ID,
|
|
1751
|
-
label: { key: "usage.balance.credits" },
|
|
1752
|
-
remaining: balance,
|
|
1753
|
-
used: null,
|
|
1754
|
-
limit: null,
|
|
1755
|
-
unit: { kind: "credits" },
|
|
1756
|
-
resetsAt: null
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
const plan = planLabel((main ?? legacy)?.["planType"]);
|
|
1761
|
-
const resetSummary = asRecord(root["rateLimitResetCredits"]);
|
|
1762
|
-
const availableCount = asFiniteNumber(resetSummary?.["availableCount"]);
|
|
1763
|
-
return {
|
|
1764
|
-
status: "available",
|
|
1765
|
-
...plan === void 0 ? {} : { plan },
|
|
1766
|
-
windows,
|
|
1767
|
-
balances,
|
|
1768
|
-
accountKey,
|
|
1769
|
-
// 回包没带摘要(老版本 CLI)就是不知道,不填;填 0 会让界面说「没有可用的重置额度」。
|
|
1770
|
-
...availableCount === null || availableCount < 0 ? {} : { resetCredits: { availableCount: Math.trunc(availableCount) } }
|
|
1771
|
-
};
|
|
1772
|
-
};
|
|
1773
|
-
var codexQuotaFromRateLimits = (snapshot) => {
|
|
1774
|
-
const bucket = asRecord(snapshot);
|
|
1775
|
-
if (bucket === null) return null;
|
|
1776
|
-
const reading = mapCodexRateLimits({ rateLimits: snapshot }, "");
|
|
1777
|
-
if (reading === null || reading.status !== "available") return null;
|
|
1778
|
-
const main = isMainCodexLimit(bucket);
|
|
1779
|
-
const group = main ? MAIN_LIMIT_ID : asNonEmptyString(bucket["limitId"]);
|
|
1780
|
-
const replaces = {
|
|
1781
|
-
windowGroups: group === null ? [] : [group],
|
|
1782
|
-
balanceIds: main ? [CREDITS_BALANCE_ID] : []
|
|
1783
|
-
};
|
|
1784
|
-
return {
|
|
1785
|
-
windows: reading.windows,
|
|
1786
|
-
balances: reading.balances,
|
|
1787
|
-
...reading.plan === void 0 ? {} : { plan: reading.plan },
|
|
1788
|
-
replaces
|
|
1789
|
-
};
|
|
1790
|
-
};
|
|
1791
|
-
var classifyRateLimitsError = (message) => {
|
|
1792
|
-
const text = message.toLowerCase();
|
|
1793
|
-
if (text.includes("chatgpt authentication required")) return "unsupportedLogin";
|
|
1794
|
-
if (text.includes("authentication required")) return "missingCredentials";
|
|
1795
|
-
if (/\b(401|403)\b|unauthorized|forbidden/.test(text)) return "authRejected";
|
|
1796
|
-
if (/\b429\b|too many requests/.test(text)) return "rateLimited";
|
|
1797
|
-
return "unreachable";
|
|
1798
|
-
};
|
|
1799
|
-
var CODEX_OFFICIAL_HOSTS = /* @__PURE__ */ new Set(["chatgpt.com"]);
|
|
1800
|
-
var CODEX_DEFAULT_MODEL_PROVIDER = "openai";
|
|
1801
|
-
var isOfficialUrl = (value) => {
|
|
1802
|
-
const parsed = URL.parse(value.trim());
|
|
1803
|
-
return parsed === null ? false : CODEX_OFFICIAL_HOSTS.has(parsed.hostname.toLowerCase());
|
|
1804
|
-
};
|
|
1805
|
-
var readTomlScalar = (raw) => {
|
|
1806
|
-
const quoted = /^(["'])(.*?)\1/.exec(raw.trim());
|
|
1807
|
-
if (quoted?.[2] !== void 0) return quoted[2];
|
|
1808
|
-
const [beforeComment = ""] = raw.split("#");
|
|
1809
|
-
return beforeComment.trim();
|
|
1810
|
-
};
|
|
1811
|
-
var TABLE_HEADER = /^\[\s*([^\][]+?)\s*\]\s*(?:#.*)?$/;
|
|
1812
|
-
var KEY_VALUE = /^([A-Za-z0-9_.\-"']+)\s*=\s*(.*)$/;
|
|
1813
|
-
var BASE_URL_KEYS = /* @__PURE__ */ new Set([
|
|
1814
|
-
"chatgpt_base_url",
|
|
1815
|
-
"openai_base_url",
|
|
1816
|
-
"base_url"
|
|
1817
|
-
]);
|
|
1818
|
-
var codexConfigSignal = (contents, options) => {
|
|
1819
|
-
let table = null;
|
|
1820
|
-
let selectedProvider = CODEX_DEFAULT_MODEL_PROVIDER;
|
|
1821
|
-
const providerBaseUrls = /* @__PURE__ */ new Map();
|
|
1822
|
-
for (const rawLine of contents.split("\n")) {
|
|
1823
|
-
const line = rawLine.trim();
|
|
1824
|
-
if (line.length === 0 || line.startsWith("#")) continue;
|
|
1825
|
-
const header = TABLE_HEADER.exec(line);
|
|
1826
|
-
if (header !== null) {
|
|
1827
|
-
table = header[1] ?? "";
|
|
1828
|
-
if (table === "model_providers") return "config.toml:model_providers";
|
|
1829
|
-
continue;
|
|
1830
|
-
}
|
|
1831
|
-
const assignment = KEY_VALUE.exec(line);
|
|
1832
|
-
if (assignment === null) continue;
|
|
1833
|
-
const key = assignment[1] ?? "";
|
|
1834
|
-
const value = readTomlScalar(assignment[2] ?? "");
|
|
1835
|
-
if (table === null) {
|
|
1836
|
-
if (key === "model_provider") {
|
|
1837
|
-
selectedProvider = value;
|
|
1838
|
-
} else if (key === "profile" || key.startsWith("model_providers")) {
|
|
1839
|
-
return `config.toml:${key.split(".")[0] ?? key}`;
|
|
1840
|
-
} else if (BASE_URL_KEYS.has(key) && !isOfficialUrl(value)) {
|
|
1841
|
-
return `config.toml:${key}`;
|
|
1842
|
-
}
|
|
1843
|
-
continue;
|
|
1844
|
-
}
|
|
1845
|
-
if (table.startsWith("model_providers.") && key === "base_url") {
|
|
1846
|
-
providerBaseUrls.set(
|
|
1847
|
-
table.slice("model_providers.".length).replace(/^["']|["']$/g, ""),
|
|
1848
|
-
value
|
|
1849
|
-
);
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
const selected = options.providerOverridden ? CODEX_DEFAULT_MODEL_PROVIDER : selectedProvider;
|
|
1853
|
-
if (selected === CODEX_DEFAULT_MODEL_PROVIDER) return null;
|
|
1854
|
-
const baseUrl = providerBaseUrls.get(selected);
|
|
1855
|
-
if (baseUrl === void 0 || !isOfficialUrl(baseUrl)) return "config.toml:model_provider";
|
|
1856
|
-
return null;
|
|
1857
|
-
};
|
|
1858
|
-
var LAUNCH_ARG_MARKERS = [
|
|
1859
|
-
"model_provider",
|
|
1860
|
-
"chatgpt_base_url",
|
|
1861
|
-
"openai_base_url",
|
|
1862
|
-
"base_url",
|
|
1863
|
-
"--profile"
|
|
1864
|
-
];
|
|
1865
|
-
var codexLaunchArgsSignal = (launchArgs) => {
|
|
1866
|
-
for (const marker of LAUNCH_ARG_MARKERS) {
|
|
1867
|
-
if (launchArgs.includes(marker)) return `launchArgs:${marker}`;
|
|
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
|
+
];
|
|
1868
1656
|
}
|
|
1869
|
-
return
|
|
1870
|
-
}
|
|
1657
|
+
return [];
|
|
1658
|
+
}
|
|
1871
1659
|
|
|
1872
|
-
// src/
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
const
|
|
1876
|
-
|
|
1877
|
-
if (
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
if (window.id === "session") return "5-hour";
|
|
1895
|
-
if (window.id === "weekly") return "weekly";
|
|
1896
|
-
return window.windowMs === void 0 ? "usage" : formatWindowDuration(window.windowMs / 6e4);
|
|
1897
|
-
};
|
|
1898
|
-
var nextStep = (rateLimitReachedType) => {
|
|
1899
|
-
switch (rateLimitReachedType) {
|
|
1900
|
-
case "workspace_owner_credits_depleted":
|
|
1901
|
-
case "workspace_member_credits_depleted":
|
|
1902
|
-
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.";
|
|
1903
|
-
case "workspace_owner_usage_limit_reached":
|
|
1904
|
-
case "workspace_member_usage_limit_reached":
|
|
1905
|
-
return " The workspace spend limit is reached: ask the workspace owner to raise it, or send the message again once the limit resets.";
|
|
1906
|
-
default:
|
|
1907
|
-
return void 0;
|
|
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
|
+
});
|
|
1908
1682
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
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
|
+
});
|
|
1923
1725
|
}
|
|
1924
1726
|
}
|
|
1925
|
-
|
|
1926
|
-
if (name === void 0) {
|
|
1927
|
-
return `${providerMessage ?? "Codex usage limit reached."}${step ?? ""}`;
|
|
1928
|
-
}
|
|
1929
|
-
return `Codex usage limit reached. The ${name} limit resets in ${formatUsageLimitWait(waitMs)}.${step ?? " Send the message again once the limit resets."}`;
|
|
1930
|
-
};
|
|
1931
|
-
var isUsageLimitExceeded = (error) => asRecord2(error)?.["codexErrorInfo"] === "usageLimitExceeded";
|
|
1932
|
-
|
|
1933
|
-
// src/events.ts
|
|
1934
|
-
var CODEX_PROVIDER = "codex";
|
|
1935
|
-
var EVENT_ID_PREFIX = `codex-evt-${NodeCrypto.randomUUID()}`;
|
|
1936
|
-
var eventSeq = 0;
|
|
1937
|
-
function nextEventId() {
|
|
1938
|
-
eventSeq += 1;
|
|
1939
|
-
return `${EVENT_ID_PREFIX}-${eventSeq}`;
|
|
1727
|
+
return out;
|
|
1940
1728
|
}
|
|
1941
|
-
function
|
|
1942
|
-
|
|
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
|
+
};
|
|
1943
1742
|
}
|
|
1944
|
-
function
|
|
1945
|
-
|
|
1946
|
-
const
|
|
1947
|
-
|
|
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
|
+
};
|
|
1948
1756
|
}
|
|
1949
|
-
function
|
|
1950
|
-
|
|
1951
|
-
const
|
|
1952
|
-
|
|
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
|
+
};
|
|
1953
1781
|
}
|
|
1954
|
-
function
|
|
1955
|
-
if (
|
|
1956
|
-
|
|
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
|
+
};
|
|
1957
1792
|
}
|
|
1958
|
-
function
|
|
1959
|
-
|
|
1960
|
-
const
|
|
1961
|
-
|
|
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
|
+
};
|
|
1962
1809
|
}
|
|
1963
|
-
function
|
|
1964
|
-
if (!isRecord3(
|
|
1965
|
-
const
|
|
1966
|
-
|
|
1967
|
-
if (
|
|
1968
|
-
|
|
1969
|
-
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";
|
|
1970
1816
|
}
|
|
1971
|
-
return
|
|
1817
|
+
return void 0;
|
|
1972
1818
|
}
|
|
1973
|
-
function
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
return "cancelled";
|
|
1980
|
-
case "interrupted":
|
|
1981
|
-
return "interrupted";
|
|
1982
|
-
default:
|
|
1983
|
-
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
|
+
};
|
|
1984
1825
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
"
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
]);
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
const
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
|
-
return `codex:${threadId}:${turnId ?? "session"}`;
|
|
2008
|
-
}
|
|
2009
|
-
function normalizeCodexTokenUsage(tokenUsage) {
|
|
2010
|
-
if (!isRecord3(tokenUsage)) return void 0;
|
|
2011
|
-
const last = isRecord3(tokenUsage["last"]) ? tokenUsage["last"] : void 0;
|
|
2012
|
-
const total = isRecord3(tokenUsage["total"]) ? tokenUsage["total"] : void 0;
|
|
2013
|
-
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
2014
|
-
const usedTokens = count(last?.["totalTokens"]);
|
|
2015
|
-
if (usedTokens === void 0 || usedTokens <= 0) return void 0;
|
|
2016
|
-
const totalProcessedTokens = count(total?.["totalTokens"]);
|
|
2017
|
-
const maxTokens = count(tokenUsage["modelContextWindow"]);
|
|
2018
|
-
const inputTokens = count(last?.["inputTokens"]);
|
|
2019
|
-
const cachedInputTokens = count(last?.["cachedInputTokens"]);
|
|
2020
|
-
const outputTokens = count(last?.["outputTokens"]);
|
|
2021
|
-
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);
|
|
2022
1848
|
return {
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
...
|
|
2026
|
-
...maxTokens !== void 0 && maxTokens > 0 ? { maxTokens } : {},
|
|
2027
|
-
...inputTokens !== void 0 ? { inputTokens, lastInputTokens: inputTokens } : {},
|
|
2028
|
-
...cachedInputTokens !== void 0 ? { cachedInputTokens, lastCachedInputTokens: cachedInputTokens } : {},
|
|
2029
|
-
...outputTokens !== void 0 ? { outputTokens, lastOutputTokens: outputTokens } : {},
|
|
2030
|
-
...reasoningOutputTokens !== void 0 ? { reasoningOutputTokens, lastReasoningOutputTokens: reasoningOutputTokens } : {},
|
|
2031
|
-
compactsAutomatically: true
|
|
1849
|
+
action: "accept",
|
|
1850
|
+
content,
|
|
1851
|
+
...persist ? { _meta: { persist } } : {}
|
|
2032
1852
|
};
|
|
2033
1853
|
}
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
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";
|
|
2039
1890
|
}
|
|
1891
|
+
|
|
1892
|
+
// src/events/userInput.ts
|
|
2040
1893
|
function toUserInputQuestions(params) {
|
|
2041
1894
|
if (!isRecord3(params)) return void 0;
|
|
2042
1895
|
const questions = Array.isArray(params["questions"]) ? params["questions"] : [];
|
|
@@ -2103,185 +1956,338 @@ function toCodexUserInputAnswers(answers) {
|
|
|
2103
1956
|
out[questionId] = { answers: [...value] };
|
|
2104
1957
|
continue;
|
|
2105
1958
|
}
|
|
2106
|
-
if (isRecord3(value) && Array.isArray(value["answers"]) && value["answers"].every((entry) => typeof entry === "string")) {
|
|
2107
|
-
out[questionId] = { answers: [...value["answers"]] };
|
|
2108
|
-
continue;
|
|
1959
|
+
if (isRecord3(value) && Array.isArray(value["answers"]) && value["answers"].every((entry) => typeof entry === "string")) {
|
|
1960
|
+
out[questionId] = { answers: [...value["answers"]] };
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
return void 0;
|
|
1964
|
+
}
|
|
1965
|
+
return out;
|
|
1966
|
+
}
|
|
1967
|
+
function toCanonicalUserInputAnswers(answers) {
|
|
1968
|
+
return Object.fromEntries(
|
|
1969
|
+
Object.entries(answers).map(([questionId, value]) => [
|
|
1970
|
+
questionId,
|
|
1971
|
+
value.answers.length === 1 ? value.answers[0] : [...value.answers]
|
|
1972
|
+
])
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
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" } };
|
|
2002
|
+
return {
|
|
2003
|
+
id: `window-${String(minutes)}m`,
|
|
2004
|
+
label: { key: "usage.window.duration", values: { duration: formatWindowDuration(minutes) } }
|
|
2005
|
+
};
|
|
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 }
|
|
2020
|
+
};
|
|
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 });
|
|
2061
|
+
}
|
|
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
|
+
);
|
|
2109
2207
|
}
|
|
2110
|
-
return void 0;
|
|
2111
|
-
}
|
|
2112
|
-
return out;
|
|
2113
|
-
}
|
|
2114
|
-
function toCanonicalUserInputAnswers(answers) {
|
|
2115
|
-
return Object.fromEntries(
|
|
2116
|
-
Object.entries(answers).map(([questionId, value]) => [
|
|
2117
|
-
questionId,
|
|
2118
|
-
value.answers.length === 1 ? value.answers[0] : [...value.answers]
|
|
2119
|
-
])
|
|
2120
|
-
);
|
|
2121
|
-
}
|
|
2122
|
-
function pathLeaf(value) {
|
|
2123
|
-
if (!value) return void 0;
|
|
2124
|
-
const parts = value.split("/").filter((part) => part.length > 0);
|
|
2125
|
-
return parts.at(-1);
|
|
2126
|
-
}
|
|
2127
|
-
function eventRaw(source, method, payload) {
|
|
2128
|
-
return { source, method, payload };
|
|
2129
|
-
}
|
|
2130
|
-
function baseEvent(input) {
|
|
2131
|
-
return {
|
|
2132
|
-
eventId: nextEventId(),
|
|
2133
|
-
provider: CODEX_PROVIDER,
|
|
2134
|
-
threadId: input.threadId,
|
|
2135
|
-
createdAt: nowIso2(),
|
|
2136
|
-
type: input.type,
|
|
2137
|
-
payload: input.payload,
|
|
2138
|
-
...input.providerInstanceId ? { providerInstanceId: input.providerInstanceId } : {},
|
|
2139
|
-
...input.turnId ? { turnId: input.turnId } : {},
|
|
2140
|
-
...input.itemId ? { itemId: input.itemId } : {},
|
|
2141
|
-
...input.requestId ? { requestId: input.requestId } : {},
|
|
2142
|
-
...input.method ? {
|
|
2143
|
-
raw: eventRaw(
|
|
2144
|
-
input.source ?? "codex.app-server.notification",
|
|
2145
|
-
input.method,
|
|
2146
|
-
input.payload
|
|
2147
|
-
)
|
|
2148
|
-
} : {}
|
|
2149
|
-
};
|
|
2150
|
-
}
|
|
2151
|
-
function mapCollabAgentEvent(input) {
|
|
2152
|
-
if (!isRecord3(input.params)) return [];
|
|
2153
|
-
const agentThreadId = asString(input.params["agentThreadId"]) ?? asString(input.params["threadId"]);
|
|
2154
|
-
if (!agentThreadId) return [];
|
|
2155
|
-
const agentPath = asString(input.params["agentPath"]) ?? asString(input.params["path"]) ?? void 0;
|
|
2156
|
-
const nickname = asString(input.params["agentNickname"]) ?? asString(input.params["nickname"]);
|
|
2157
|
-
const role = asString(input.params["agentRole"]) ?? asString(input.params["role"]) ?? pathLeaf(agentPath) ?? "general-purpose";
|
|
2158
|
-
const knownName = nickname ?? pathLeaf(agentPath);
|
|
2159
|
-
const title = knownName ?? agentThreadId;
|
|
2160
|
-
const model = trimText(input.params["model"]);
|
|
2161
|
-
const effort = trimText(input.params["effort"]);
|
|
2162
|
-
const linkage = {
|
|
2163
|
-
role,
|
|
2164
|
-
...knownName ? { title: knownName } : {},
|
|
2165
|
-
...model ? { model } : {},
|
|
2166
|
-
...effort ? { effort } : {},
|
|
2167
|
-
...agentPath ? { agentPath } : {},
|
|
2168
|
-
timelineBypass: true,
|
|
2169
|
-
rosterKind: "subagent"
|
|
2170
|
-
};
|
|
2171
|
-
const threadId = input.rootThreadId;
|
|
2172
|
-
const method = input.method;
|
|
2173
|
-
if (method === "collabAgent/started" || method === "task.started") {
|
|
2174
|
-
return [
|
|
2175
|
-
baseEvent({
|
|
2176
|
-
type: "task.started",
|
|
2177
|
-
threadId,
|
|
2178
|
-
payload: { taskId: agentThreadId, ...linkage },
|
|
2179
|
-
method,
|
|
2180
|
-
providerInstanceId: input.providerInstanceId
|
|
2181
|
-
})
|
|
2182
|
-
];
|
|
2183
|
-
}
|
|
2184
|
-
if (method === "collabAgent/progress" || method === "task.progress") {
|
|
2185
|
-
const summary = trimText(input.params["detail"]) ?? trimText(input.params["text"]);
|
|
2186
|
-
return [
|
|
2187
|
-
baseEvent({
|
|
2188
|
-
type: "task.progress",
|
|
2189
|
-
threadId,
|
|
2190
|
-
// 契约 TaskProgressPayload 的 description 是必填;子代理行靠它显示名字。
|
|
2191
|
-
payload: {
|
|
2192
|
-
taskId: agentThreadId,
|
|
2193
|
-
description: title,
|
|
2194
|
-
...summary ? { summary } : {},
|
|
2195
|
-
...linkage
|
|
2196
|
-
},
|
|
2197
|
-
method,
|
|
2198
|
-
providerInstanceId: input.providerInstanceId
|
|
2199
|
-
})
|
|
2200
|
-
];
|
|
2201
2208
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
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}`;
|
|
2213
2225
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
];
|
|
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];
|
|
2226
2238
|
}
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
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;
|
|
2237
2265
|
}
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
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
|
+
}
|
|
2250
2281
|
}
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
2255
|
-
const totalTokens = total ? count(total["totalTokens"]) : void 0;
|
|
2256
|
-
if (total === void 0 || totalTokens === void 0) return [];
|
|
2257
|
-
const inputTokens = count(total["inputTokens"]);
|
|
2258
|
-
const cachedInputTokens = count(total["cachedInputTokens"]);
|
|
2259
|
-
const outputTokens = count(total["outputTokens"]);
|
|
2260
|
-
const reasoningOutputTokens = count(total["reasoningOutputTokens"]);
|
|
2261
|
-
const typedUsage = {
|
|
2262
|
-
totalTokens,
|
|
2263
|
-
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
2264
|
-
...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
|
|
2265
|
-
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
2266
|
-
...reasoningOutputTokens !== void 0 ? { reasoningOutputTokens } : {}
|
|
2267
|
-
};
|
|
2268
|
-
return [
|
|
2269
|
-
baseEvent({
|
|
2270
|
-
type: "task.progress",
|
|
2271
|
-
threadId,
|
|
2272
|
-
payload: {
|
|
2273
|
-
taskId: agentThreadId,
|
|
2274
|
-
description: title,
|
|
2275
|
-
typedUsage,
|
|
2276
|
-
...linkage
|
|
2277
|
-
},
|
|
2278
|
-
method,
|
|
2279
|
-
providerInstanceId: input.providerInstanceId
|
|
2280
|
-
})
|
|
2281
|
-
];
|
|
2282
|
+
const step = nextStep(snapshot?.["rateLimitReachedType"]);
|
|
2283
|
+
if (name === void 0) {
|
|
2284
|
+
return `${providerMessage ?? "Codex usage limit reached."}${step ?? ""}`;
|
|
2282
2285
|
}
|
|
2283
|
-
return
|
|
2284
|
-
}
|
|
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
|
|
2285
2291
|
function mapNotificationToEvents(input) {
|
|
2286
2292
|
const { method, params, threadId, providerInstanceId, rateLimits } = input;
|
|
2287
2293
|
if (method.startsWith("collabAgent/")) {
|
|
@@ -2851,6 +2857,8 @@ function mapNotificationToEvents(input) {
|
|
|
2851
2857
|
return [];
|
|
2852
2858
|
}
|
|
2853
2859
|
}
|
|
2860
|
+
|
|
2861
|
+
// src/events/serverRequests.ts
|
|
2854
2862
|
function toRequestTypeFromKind(kind) {
|
|
2855
2863
|
switch (kind) {
|
|
2856
2864
|
case "command":
|