@ycodium-ai/agent-codex 0.2.3082 → 0.2.3088
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 +1395 -1351
- 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,9 +1381,6 @@ 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
|
|
1385
|
-
import * as NodeCrypto from "node:crypto";
|
|
1386
|
-
|
|
1387
1384
|
// src/codexArtifacts.ts
|
|
1388
1385
|
function imageGenerationArtifacts(input) {
|
|
1389
1386
|
if (input.itemType !== "imageGeneration" || input.status !== "completed") return [];
|
|
@@ -1421,407 +1418,572 @@ function toCanonicalItemType(raw) {
|
|
|
1421
1418
|
return "unknown";
|
|
1422
1419
|
}
|
|
1423
1420
|
|
|
1424
|
-
// src/
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
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;
|
|
1421
|
+
// src/events/base.ts
|
|
1422
|
+
import * as NodeCrypto from "node:crypto";
|
|
1423
|
+
var CODEX_PROVIDER = "codex";
|
|
1424
|
+
var EVENT_ID_PREFIX = `codex-evt-${NodeCrypto.randomUUID()}`;
|
|
1425
|
+
var eventSeq = 0;
|
|
1426
|
+
function nextEventId() {
|
|
1427
|
+
eventSeq += 1;
|
|
1428
|
+
return `${EVENT_ID_PREFIX}-${eventSeq}`;
|
|
1448
1429
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
const entries = fileChangeEntries(params).toSorted(
|
|
1452
|
-
(left, right) => left.path.localeCompare(right.path)
|
|
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");
|
|
1430
|
+
function nowIso2() {
|
|
1431
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1461
1432
|
}
|
|
1462
|
-
function
|
|
1463
|
-
|
|
1464
|
-
for (const { path, kind, movePath, diff, content } of fileChangeEntries(params)) {
|
|
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;
|
|
1433
|
+
function eventRaw(source, method, payload) {
|
|
1434
|
+
return { source, method, payload };
|
|
1492
1435
|
}
|
|
1493
|
-
function
|
|
1494
|
-
const record = isRecord3(params) ? params : {};
|
|
1495
|
-
const commandValue = record["command"];
|
|
1496
|
-
const command = Array.isArray(commandValue) ? commandValue.filter((entry) => typeof entry === "string").join(" ") : asString(commandValue) ?? "";
|
|
1497
|
-
const cwd = asString(record["cwd"]);
|
|
1498
|
-
const reason = trimText(record["reason"]);
|
|
1499
|
-
const callId = asString(record["callId"]);
|
|
1436
|
+
function baseEvent(input) {
|
|
1500
1437
|
return {
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1438
|
+
eventId: nextEventId(),
|
|
1439
|
+
provider: CODEX_PROVIDER,
|
|
1440
|
+
threadId: input.threadId,
|
|
1441
|
+
createdAt: nowIso2(),
|
|
1442
|
+
type: input.type,
|
|
1443
|
+
payload: input.payload,
|
|
1444
|
+
...input.providerInstanceId ? { providerInstanceId: input.providerInstanceId } : {},
|
|
1445
|
+
...input.turnId ? { turnId: input.turnId } : {},
|
|
1446
|
+
...input.itemId ? { itemId: input.itemId } : {},
|
|
1447
|
+
...input.requestId ? { requestId: input.requestId } : {},
|
|
1448
|
+
...input.method ? {
|
|
1449
|
+
raw: eventRaw(
|
|
1450
|
+
input.source ?? "codex.app-server.notification",
|
|
1451
|
+
input.method,
|
|
1452
|
+
input.payload
|
|
1453
|
+
)
|
|
1454
|
+
} : {}
|
|
1505
1455
|
};
|
|
1506
1456
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
const
|
|
1512
|
-
|
|
1513
|
-
return {
|
|
1514
|
-
...reason ? { reason } : {},
|
|
1515
|
-
...detail ? { detail } : {},
|
|
1516
|
-
...grantRoot ? { grantRoot } : {},
|
|
1517
|
-
...callId ? { callId } : {},
|
|
1518
|
-
fileChanges: toApplyPatchFileChanges(record)
|
|
1519
|
-
};
|
|
1457
|
+
|
|
1458
|
+
// src/events/fields.ts
|
|
1459
|
+
function itemIdOf(params) {
|
|
1460
|
+
if (!isRecord3(params)) return void 0;
|
|
1461
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1462
|
+
return asString(item["id"]) ?? asString(params["itemId"]) ?? void 0;
|
|
1520
1463
|
}
|
|
1521
|
-
function
|
|
1522
|
-
|
|
1523
|
-
const
|
|
1524
|
-
|
|
1525
|
-
const network = isRecord3(permissions["network"]) ? permissions["network"] : {};
|
|
1526
|
-
const pathsOf = (value) => Array.isArray(value) ? value.flatMap((entry) => typeof entry === "string" && entry.length > 0 ? [entry] : []) : [];
|
|
1527
|
-
const read = pathsOf(fileSystem["read"]);
|
|
1528
|
-
const write = pathsOf(fileSystem["write"]);
|
|
1529
|
-
const entries = Array.isArray(fileSystem["entries"]) ? fileSystem["entries"] : [];
|
|
1530
|
-
for (const entry of entries) {
|
|
1531
|
-
if (!isRecord3(entry) || !isRecord3(entry["path"])) continue;
|
|
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);
|
|
1537
|
-
}
|
|
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
|
-
};
|
|
1464
|
+
function itemStatusOf(params) {
|
|
1465
|
+
if (!isRecord3(params)) return void 0;
|
|
1466
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1467
|
+
return asString(item["status"]) ?? void 0;
|
|
1545
1468
|
}
|
|
1546
|
-
function
|
|
1547
|
-
if (
|
|
1548
|
-
|
|
1549
|
-
return {
|
|
1550
|
-
permissions: {
|
|
1551
|
-
...requested["fileSystem"] !== void 0 ? { fileSystem: requested["fileSystem"] } : {},
|
|
1552
|
-
...requested["network"] !== void 0 ? { network: requested["network"] } : {}
|
|
1553
|
-
},
|
|
1554
|
-
scope: persistent ? "session" : "turn"
|
|
1555
|
-
};
|
|
1469
|
+
function turnIdOf(params) {
|
|
1470
|
+
if (!isRecord3(params)) return void 0;
|
|
1471
|
+
return asString(params["turnId"]) ?? (isRecord3(params["turn"]) ? asString(params["turn"]["id"]) : null) ?? void 0;
|
|
1556
1472
|
}
|
|
1557
|
-
function
|
|
1558
|
-
|
|
1559
|
-
const
|
|
1560
|
-
|
|
1561
|
-
const message = trimText(record["message"]);
|
|
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
|
-
};
|
|
1473
|
+
function itemTitle(params) {
|
|
1474
|
+
if (!isRecord3(params)) return void 0;
|
|
1475
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1476
|
+
return trimText(item["title"]) ?? trimText(item["name"]) ?? trimText(item["command"]) ?? trimText(item["summary"]) ?? void 0;
|
|
1573
1477
|
}
|
|
1574
|
-
function
|
|
1575
|
-
if (!isRecord3(
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
if (
|
|
1579
|
-
|
|
1478
|
+
function itemDetail(params) {
|
|
1479
|
+
if (!isRecord3(params)) return void 0;
|
|
1480
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1481
|
+
const query = isRecord3(item["action"]) ? item["action"]["queries"] : item["query"];
|
|
1482
|
+
if (Array.isArray(query)) {
|
|
1483
|
+
const joined = query.filter((entry) => typeof entry === "string").join(", ");
|
|
1484
|
+
if (joined.length > 0) return joined;
|
|
1580
1485
|
}
|
|
1581
|
-
return void 0;
|
|
1486
|
+
return trimText(item["text"]) ?? trimText(item["summary"]) ?? trimText(item["path"]) ?? trimText(item["savedPath"]) ?? trimText(item["prompt"]) ?? (typeof query === "string" ? query : void 0);
|
|
1582
1487
|
}
|
|
1583
|
-
function
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
for (const [key, spec] of Object.entries(properties)) {
|
|
1595
|
-
if (!isRecord3(spec)) continue;
|
|
1596
|
-
if (Array.isArray(spec["oneOf"]) || Array.isArray(spec["enum"])) {
|
|
1597
|
-
const options = Array.isArray(spec["oneOf"]) ? spec["oneOf"] : Array.isArray(spec["enum"]) ? spec["enum"] : [];
|
|
1598
|
-
const first = options[0];
|
|
1599
|
-
content[key] = isRecord3(first) ? first["const"] ?? first["value"] ?? first : first;
|
|
1600
|
-
} else if (spec["type"] === "boolean") {
|
|
1601
|
-
content[key] = true;
|
|
1602
|
-
} else if (typeof spec["default"] !== "undefined") {
|
|
1603
|
-
content[key] = spec["default"];
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
for (const key of required) {
|
|
1607
|
-
if (typeof key === "string" && !(key in content)) {
|
|
1608
|
-
return { action: "decline" };
|
|
1609
|
-
}
|
|
1488
|
+
function toTurnStatus(status) {
|
|
1489
|
+
switch (status) {
|
|
1490
|
+
case "failed":
|
|
1491
|
+
return "failed";
|
|
1492
|
+
case "cancelled":
|
|
1493
|
+
case "canceled":
|
|
1494
|
+
return "cancelled";
|
|
1495
|
+
case "interrupted":
|
|
1496
|
+
return "interrupted";
|
|
1497
|
+
default:
|
|
1498
|
+
return "completed";
|
|
1610
1499
|
}
|
|
1611
|
-
const persist = persistFromMeta(isRecord3(params) ? params["_meta"] : void 0);
|
|
1612
|
-
return {
|
|
1613
|
-
action: "accept",
|
|
1614
|
-
content,
|
|
1615
|
-
...persist ? { _meta: { persist } } : {}
|
|
1616
|
-
};
|
|
1617
1500
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
if (
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
}
|
|
1641
|
-
if (minutes === WEEKLY_WINDOW_MINUTES) {
|
|
1642
|
-
return { id: "weekly", label: { key: "usage.window.weekly" } };
|
|
1643
|
-
}
|
|
1644
|
-
if (minutes === null) return { id: slot, label: { key: "usage.window.limit" } };
|
|
1645
|
-
return {
|
|
1646
|
-
id: `window-${String(minutes)}m`,
|
|
1647
|
-
label: { key: "usage.window.duration", values: { duration: formatWindowDuration(minutes) } }
|
|
1648
|
-
};
|
|
1649
|
-
};
|
|
1650
|
-
var mapWindow = (raw, slot, describe) => {
|
|
1651
|
-
const window = asRecord(raw);
|
|
1652
|
-
const usedPercent = asFiniteNumber(window?.["usedPercent"]);
|
|
1653
|
-
if (window === null || usedPercent === null) return null;
|
|
1654
|
-
const rawMinutes = asFiniteNumber(window["windowDurationMins"]);
|
|
1655
|
-
const minutes = rawMinutes !== null && rawMinutes > 0 ? Math.trunc(rawMinutes) : null;
|
|
1656
|
-
const { id, label } = describe(minutes, slot);
|
|
1501
|
+
function threadStateFromStatus(status) {
|
|
1502
|
+
const type = isRecord3(status) ? asString(status["type"]) : asString(status);
|
|
1503
|
+
if (type === "idle") return "idle";
|
|
1504
|
+
if (type === "systemError" || type === "error") return "error";
|
|
1505
|
+
return "active";
|
|
1506
|
+
}
|
|
1507
|
+
function codexCompactionId(threadId, turnId) {
|
|
1508
|
+
return `codex:${threadId}:${turnId ?? "session"}`;
|
|
1509
|
+
}
|
|
1510
|
+
function normalizeCodexTokenUsage(tokenUsage) {
|
|
1511
|
+
if (!isRecord3(tokenUsage)) return void 0;
|
|
1512
|
+
const last = isRecord3(tokenUsage["last"]) ? tokenUsage["last"] : void 0;
|
|
1513
|
+
const total = isRecord3(tokenUsage["total"]) ? tokenUsage["total"] : void 0;
|
|
1514
|
+
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1515
|
+
const usedTokens = count(last?.["totalTokens"]);
|
|
1516
|
+
if (usedTokens === void 0 || usedTokens <= 0) return void 0;
|
|
1517
|
+
const totalProcessedTokens = count(total?.["totalTokens"]);
|
|
1518
|
+
const maxTokens = count(tokenUsage["modelContextWindow"]);
|
|
1519
|
+
const inputTokens = count(last?.["inputTokens"]);
|
|
1520
|
+
const cachedInputTokens = count(last?.["cachedInputTokens"]);
|
|
1521
|
+
const outputTokens = count(last?.["outputTokens"]);
|
|
1522
|
+
const reasoningOutputTokens = count(last?.["reasoningOutputTokens"]);
|
|
1657
1523
|
return {
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
...
|
|
1524
|
+
usedTokens,
|
|
1525
|
+
lastUsedTokens: usedTokens,
|
|
1526
|
+
...totalProcessedTokens !== void 0 && totalProcessedTokens > usedTokens ? { totalProcessedTokens } : {},
|
|
1527
|
+
...maxTokens !== void 0 && maxTokens > 0 ? { maxTokens } : {},
|
|
1528
|
+
...inputTokens !== void 0 ? { inputTokens, lastInputTokens: inputTokens } : {},
|
|
1529
|
+
...cachedInputTokens !== void 0 ? { cachedInputTokens, lastCachedInputTokens: cachedInputTokens } : {},
|
|
1530
|
+
...outputTokens !== void 0 ? { outputTokens, lastOutputTokens: outputTokens } : {},
|
|
1531
|
+
...reasoningOutputTokens !== void 0 ? { reasoningOutputTokens, lastReasoningOutputTokens: reasoningOutputTokens } : {},
|
|
1532
|
+
compactsAutomatically: true
|
|
1663
1533
|
};
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
const
|
|
1685
|
-
return
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
const
|
|
1695
|
-
if (
|
|
1696
|
-
const
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
const codexBucket = asRecord(byLimitId?.[MAIN_LIMIT_ID]);
|
|
1712
|
-
const main = codexBucket ?? (legacy !== null && isMainCodexLimit(legacy) ? legacy : null);
|
|
1713
|
-
if (root === null || legacy === null && codexBucket === null) return null;
|
|
1714
|
-
const windows = mapCodexSnapshotWindows(main);
|
|
1715
|
-
const takenIds = new Set(windows.map((window) => window.id));
|
|
1716
|
-
const push = (window, slot) => {
|
|
1717
|
-
if (window === null) return;
|
|
1718
|
-
const id = takenIds.has(window.id) ? `${window.id}:${slot}` : window.id;
|
|
1719
|
-
takenIds.add(id);
|
|
1720
|
-
windows.push({ ...window, id });
|
|
1534
|
+
}
|
|
1535
|
+
function turnCompletedControlFlow(params) {
|
|
1536
|
+
const turn = isRecord3(params) ? isRecord3(params["turn"]) ? params["turn"] : params : {};
|
|
1537
|
+
const state = toTurnStatus(asString(turn["status"]) ?? void 0);
|
|
1538
|
+
const error = isRecord3(turn["error"]) ? trimText(turn["error"]["message"]) : void 0;
|
|
1539
|
+
return error ? { state, errorMessage: error } : { state };
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
// src/events/collabAgents.ts
|
|
1543
|
+
var COLLAB_CHILD_STATUSES = /* @__PURE__ */ new Set([
|
|
1544
|
+
"running",
|
|
1545
|
+
"idle",
|
|
1546
|
+
"failed",
|
|
1547
|
+
"interrupted",
|
|
1548
|
+
"cancelled"
|
|
1549
|
+
]);
|
|
1550
|
+
function asCollabChildStatus(value) {
|
|
1551
|
+
return typeof value === "string" && COLLAB_CHILD_STATUSES.has(value) ? value : void 0;
|
|
1552
|
+
}
|
|
1553
|
+
function collabTurnEndStatus(turnStatus) {
|
|
1554
|
+
const state = toTurnStatus(turnStatus);
|
|
1555
|
+
return state === "completed" ? "idle" : state;
|
|
1556
|
+
}
|
|
1557
|
+
function pathLeaf(value) {
|
|
1558
|
+
if (!value) return void 0;
|
|
1559
|
+
const parts = value.split("/").filter((part) => part.length > 0);
|
|
1560
|
+
return parts.at(-1);
|
|
1561
|
+
}
|
|
1562
|
+
function mapCollabAgentEvent(input) {
|
|
1563
|
+
if (!isRecord3(input.params)) return [];
|
|
1564
|
+
const agentThreadId = asString(input.params["agentThreadId"]) ?? asString(input.params["threadId"]);
|
|
1565
|
+
if (!agentThreadId) return [];
|
|
1566
|
+
const agentPath = asString(input.params["agentPath"]) ?? asString(input.params["path"]) ?? void 0;
|
|
1567
|
+
const nickname = asString(input.params["agentNickname"]) ?? asString(input.params["nickname"]);
|
|
1568
|
+
const role = asString(input.params["agentRole"]) ?? asString(input.params["role"]) ?? pathLeaf(agentPath) ?? "general-purpose";
|
|
1569
|
+
const knownName = nickname ?? pathLeaf(agentPath);
|
|
1570
|
+
const title = knownName ?? agentThreadId;
|
|
1571
|
+
const model = trimText(input.params["model"]);
|
|
1572
|
+
const effort = trimText(input.params["effort"]);
|
|
1573
|
+
const linkage = {
|
|
1574
|
+
role,
|
|
1575
|
+
...knownName ? { title: knownName } : {},
|
|
1576
|
+
...model ? { model } : {},
|
|
1577
|
+
...effort ? { effort } : {},
|
|
1578
|
+
...agentPath ? { agentPath } : {},
|
|
1579
|
+
timelineBypass: true,
|
|
1580
|
+
rosterKind: "subagent"
|
|
1721
1581
|
};
|
|
1722
|
-
const
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1582
|
+
const threadId = input.rootThreadId;
|
|
1583
|
+
const method = input.method;
|
|
1584
|
+
if (method === "collabAgent/started" || method === "task.started") {
|
|
1585
|
+
return [
|
|
1586
|
+
baseEvent({
|
|
1587
|
+
type: "task.started",
|
|
1588
|
+
threadId,
|
|
1589
|
+
payload: { taskId: agentThreadId, ...linkage },
|
|
1590
|
+
method,
|
|
1591
|
+
providerInstanceId: input.providerInstanceId
|
|
1592
|
+
})
|
|
1593
|
+
];
|
|
1726
1594
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1595
|
+
if (method === "collabAgent/progress" || method === "task.progress") {
|
|
1596
|
+
const summary = trimText(input.params["detail"]) ?? trimText(input.params["text"]);
|
|
1597
|
+
return [
|
|
1598
|
+
baseEvent({
|
|
1599
|
+
type: "task.progress",
|
|
1600
|
+
threadId,
|
|
1601
|
+
// 契约 TaskProgressPayload 的 description 是必填;子代理行靠它显示名字。
|
|
1602
|
+
payload: {
|
|
1603
|
+
taskId: agentThreadId,
|
|
1604
|
+
description: title,
|
|
1605
|
+
...summary ? { summary } : {},
|
|
1606
|
+
...linkage
|
|
1607
|
+
},
|
|
1608
|
+
method,
|
|
1609
|
+
providerInstanceId: input.providerInstanceId
|
|
1610
|
+
})
|
|
1611
|
+
];
|
|
1730
1612
|
}
|
|
1731
|
-
|
|
1732
|
-
const
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
})
|
|
1741
|
-
|
|
1742
|
-
}
|
|
1613
|
+
if (method === "collabAgent/completed" || method === "task.completed") {
|
|
1614
|
+
const status = collabTurnEndStatus(asString(input.params["turnStatus"]) ?? void 0);
|
|
1615
|
+
return [
|
|
1616
|
+
baseEvent({
|
|
1617
|
+
type: "task.updated",
|
|
1618
|
+
threadId,
|
|
1619
|
+
payload: { taskId: agentThreadId, status, ...linkage },
|
|
1620
|
+
method,
|
|
1621
|
+
providerInstanceId: input.providerInstanceId
|
|
1622
|
+
})
|
|
1623
|
+
];
|
|
1743
1624
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
resetsAt: null
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1625
|
+
if (method === "collabAgent/statusChanged") {
|
|
1626
|
+
const raw = asString(input.params["status"]);
|
|
1627
|
+
const status = raw === "systemError" || raw === "error" ? "failed" : raw === "idle" ? "idle" : "running";
|
|
1628
|
+
return [
|
|
1629
|
+
baseEvent({
|
|
1630
|
+
type: "task.updated",
|
|
1631
|
+
threadId,
|
|
1632
|
+
payload: { taskId: agentThreadId, status, ...linkage },
|
|
1633
|
+
method,
|
|
1634
|
+
providerInstanceId: input.providerInstanceId
|
|
1635
|
+
})
|
|
1636
|
+
];
|
|
1759
1637
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1638
|
+
if (method === "collabAgent/metadataUpdated") {
|
|
1639
|
+
return [
|
|
1640
|
+
baseEvent({
|
|
1641
|
+
type: "task.updated",
|
|
1642
|
+
threadId,
|
|
1643
|
+
payload: { taskId: agentThreadId, ...linkage },
|
|
1644
|
+
method,
|
|
1645
|
+
providerInstanceId: input.providerInstanceId
|
|
1646
|
+
})
|
|
1647
|
+
];
|
|
1648
|
+
}
|
|
1649
|
+
if (method === "collabAgent/metadata") {
|
|
1650
|
+
const status = asCollabChildStatus(input.params["status"]);
|
|
1651
|
+
if (status === void 0) return [];
|
|
1652
|
+
return [
|
|
1653
|
+
baseEvent({
|
|
1654
|
+
type: "task.updated",
|
|
1655
|
+
threadId,
|
|
1656
|
+
payload: { taskId: agentThreadId, status, ...linkage },
|
|
1657
|
+
method,
|
|
1658
|
+
providerInstanceId: input.providerInstanceId
|
|
1659
|
+
})
|
|
1660
|
+
];
|
|
1661
|
+
}
|
|
1662
|
+
if (method === "collabAgent/tokenUsage") {
|
|
1663
|
+
const tokenUsage = isRecord3(input.params["tokenUsage"]) ? input.params["tokenUsage"] : void 0;
|
|
1664
|
+
const total = tokenUsage && isRecord3(tokenUsage["total"]) ? tokenUsage["total"] : void 0;
|
|
1665
|
+
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1666
|
+
const totalTokens = total ? count(total["totalTokens"]) : void 0;
|
|
1667
|
+
if (total === void 0 || totalTokens === void 0) return [];
|
|
1668
|
+
const inputTokens = count(total["inputTokens"]);
|
|
1669
|
+
const cachedInputTokens = count(total["cachedInputTokens"]);
|
|
1670
|
+
const outputTokens = count(total["outputTokens"]);
|
|
1671
|
+
const reasoningOutputTokens = count(total["reasoningOutputTokens"]);
|
|
1672
|
+
const typedUsage = {
|
|
1673
|
+
totalTokens,
|
|
1674
|
+
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
1675
|
+
...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
|
|
1676
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
1677
|
+
...reasoningOutputTokens !== void 0 ? { reasoningOutputTokens } : {}
|
|
1678
|
+
};
|
|
1679
|
+
return [
|
|
1680
|
+
baseEvent({
|
|
1681
|
+
type: "task.progress",
|
|
1682
|
+
threadId,
|
|
1683
|
+
payload: {
|
|
1684
|
+
taskId: agentThreadId,
|
|
1685
|
+
description: title,
|
|
1686
|
+
typedUsage,
|
|
1687
|
+
...linkage
|
|
1688
|
+
},
|
|
1689
|
+
method,
|
|
1690
|
+
providerInstanceId: input.providerInstanceId
|
|
1691
|
+
})
|
|
1692
|
+
];
|
|
1693
|
+
}
|
|
1694
|
+
return [];
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// src/events/userInput.ts
|
|
1698
|
+
function toUserInputQuestions(params) {
|
|
1699
|
+
if (!isRecord3(params)) return void 0;
|
|
1700
|
+
const questions = Array.isArray(params["questions"]) ? params["questions"] : [];
|
|
1701
|
+
const mapped = [];
|
|
1702
|
+
for (const question of questions) {
|
|
1703
|
+
if (!isRecord3(question)) return void 0;
|
|
1704
|
+
const id = asString(question["id"]);
|
|
1705
|
+
const header = asString(question["header"]);
|
|
1706
|
+
const text = asString(question["question"]);
|
|
1707
|
+
const rawOptions = Array.isArray(question["options"]) ? question["options"] : [];
|
|
1708
|
+
const options = [];
|
|
1709
|
+
for (const option of rawOptions) {
|
|
1710
|
+
if (!isRecord3(option)) continue;
|
|
1711
|
+
const label = trimText(option["label"]);
|
|
1712
|
+
if (!label) continue;
|
|
1713
|
+
options.push({
|
|
1714
|
+
...asString(option["id"]) ? { id: asString(option["id"]) } : {},
|
|
1715
|
+
label,
|
|
1716
|
+
description: trimText(option["description"]) ?? ""
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
if (!id || !header || !text || options.length === 0) return void 0;
|
|
1720
|
+
mapped.push({
|
|
1721
|
+
id,
|
|
1722
|
+
header,
|
|
1723
|
+
question: text,
|
|
1724
|
+
options,
|
|
1725
|
+
multiSelect: false
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
return mapped.length > 0 ? mapped : void 0;
|
|
1729
|
+
}
|
|
1730
|
+
function toAsyncQuestions(params) {
|
|
1731
|
+
if (!isRecord3(params)) return void 0;
|
|
1732
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1733
|
+
if (item["delivery"] !== "async" || !Array.isArray(item["questions"])) return void 0;
|
|
1734
|
+
const mapped = [];
|
|
1735
|
+
item["questions"].forEach((question, index) => {
|
|
1736
|
+
if (!isRecord3(question)) return;
|
|
1737
|
+
const title = trimText(question["title"]);
|
|
1738
|
+
if (!title) return;
|
|
1739
|
+
const labels = Array.isArray(question["options"]) ? question["options"] : [];
|
|
1740
|
+
const options = labels.map((label) => trimText(label)).filter((label) => label !== void 0).map((label) => ({ label, description: "" }));
|
|
1741
|
+
mapped.push({
|
|
1742
|
+
id: `q${index}`,
|
|
1743
|
+
header: title,
|
|
1744
|
+
question: title,
|
|
1745
|
+
options,
|
|
1746
|
+
allowFreeform: true,
|
|
1747
|
+
multiSelect: false
|
|
1748
|
+
});
|
|
1749
|
+
});
|
|
1750
|
+
return mapped.length > 0 ? mapped : void 0;
|
|
1751
|
+
}
|
|
1752
|
+
function toCodexUserInputAnswers(answers) {
|
|
1753
|
+
if (!isRecord3(answers)) return void 0;
|
|
1754
|
+
const out = {};
|
|
1755
|
+
for (const [questionId, value] of Object.entries(answers)) {
|
|
1756
|
+
if (typeof value === "string") {
|
|
1757
|
+
out[questionId] = { answers: [value] };
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
1761
|
+
out[questionId] = { answers: [...value] };
|
|
1762
|
+
continue;
|
|
1763
|
+
}
|
|
1764
|
+
if (isRecord3(value) && Array.isArray(value["answers"]) && value["answers"].every((entry) => typeof entry === "string")) {
|
|
1765
|
+
out[questionId] = { answers: [...value["answers"]] };
|
|
1766
|
+
continue;
|
|
1767
|
+
}
|
|
1768
|
+
return void 0;
|
|
1769
|
+
}
|
|
1770
|
+
return out;
|
|
1771
|
+
}
|
|
1772
|
+
function toCanonicalUserInputAnswers(answers) {
|
|
1773
|
+
return Object.fromEntries(
|
|
1774
|
+
Object.entries(answers).map(([questionId, value]) => [
|
|
1775
|
+
questionId,
|
|
1776
|
+
value.answers.length === 1 ? value.answers[0] : [...value.answers]
|
|
1777
|
+
])
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
// src/usage/codexQuota.ts
|
|
1782
|
+
var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1783
|
+
var asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1784
|
+
var asNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
1785
|
+
var clampPercent = (value) => Math.min(100, Math.max(0, value));
|
|
1786
|
+
var epochSecondsToIso = (value) => {
|
|
1787
|
+
const seconds = asFiniteNumber(value);
|
|
1788
|
+
if (seconds === null) return null;
|
|
1789
|
+
const date = new Date(seconds * 1e3);
|
|
1790
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
1791
|
+
};
|
|
1792
|
+
var SESSION_WINDOW_MINUTES = 5 * 60;
|
|
1793
|
+
var WEEKLY_WINDOW_MINUTES = 7 * 24 * 60;
|
|
1794
|
+
var formatWindowDuration = (minutes) => {
|
|
1795
|
+
if (minutes % (24 * 60) === 0) return `${String(minutes / (24 * 60))}d`;
|
|
1796
|
+
if (minutes % 60 === 0) return `${String(minutes / 60)}h`;
|
|
1797
|
+
return `${String(minutes)}m`;
|
|
1798
|
+
};
|
|
1799
|
+
var classifyWindow = (minutes, slot) => {
|
|
1800
|
+
if (minutes === SESSION_WINDOW_MINUTES) {
|
|
1801
|
+
return { id: "session", label: { key: "usage.window.session" } };
|
|
1802
|
+
}
|
|
1803
|
+
if (minutes === WEEKLY_WINDOW_MINUTES) {
|
|
1804
|
+
return { id: "weekly", label: { key: "usage.window.weekly" } };
|
|
1805
|
+
}
|
|
1806
|
+
if (minutes === null) return { id: slot, label: { key: "usage.window.limit" } };
|
|
1763
1807
|
return {
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
windows,
|
|
1767
|
-
balances,
|
|
1768
|
-
accountKey,
|
|
1769
|
-
// 回包没带摘要(老版本 CLI)就是不知道,不填;填 0 会让界面说「没有可用的重置额度」。
|
|
1770
|
-
...availableCount === null || availableCount < 0 ? {} : { resetCredits: { availableCount: Math.trunc(availableCount) } }
|
|
1808
|
+
id: `window-${String(minutes)}m`,
|
|
1809
|
+
label: { key: "usage.window.duration", values: { duration: formatWindowDuration(minutes) } }
|
|
1771
1810
|
};
|
|
1772
1811
|
};
|
|
1773
|
-
var
|
|
1774
|
-
const
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
const
|
|
1779
|
-
const
|
|
1780
|
-
const replaces = {
|
|
1781
|
-
windowGroups: group === null ? [] : [group],
|
|
1782
|
-
balanceIds: main ? [CREDITS_BALANCE_ID] : []
|
|
1783
|
-
};
|
|
1812
|
+
var mapWindow = (raw, slot, describe) => {
|
|
1813
|
+
const window = asRecord(raw);
|
|
1814
|
+
const usedPercent = asFiniteNumber(window?.["usedPercent"]);
|
|
1815
|
+
if (window === null || usedPercent === null) return null;
|
|
1816
|
+
const rawMinutes = asFiniteNumber(window["windowDurationMins"]);
|
|
1817
|
+
const minutes = rawMinutes !== null && rawMinutes > 0 ? Math.trunc(rawMinutes) : null;
|
|
1818
|
+
const { id, label } = describe(minutes, slot);
|
|
1784
1819
|
return {
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1820
|
+
id,
|
|
1821
|
+
label,
|
|
1822
|
+
usedPercent: clampPercent(usedPercent),
|
|
1823
|
+
resetsAt: epochSecondsToIso(window["resetsAt"]),
|
|
1824
|
+
...minutes === null ? {} : { windowMs: minutes * 6e4 }
|
|
1789
1825
|
};
|
|
1790
1826
|
};
|
|
1791
|
-
var
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1827
|
+
var PLAN_LABELS = {
|
|
1828
|
+
free: "Free",
|
|
1829
|
+
go: "Go",
|
|
1830
|
+
plus: "Plus",
|
|
1831
|
+
pro: "Pro",
|
|
1832
|
+
prolite: "Pro Lite",
|
|
1833
|
+
team: "Team",
|
|
1834
|
+
business: "Business",
|
|
1835
|
+
enterprise: "Enterprise",
|
|
1836
|
+
edu: "Edu"
|
|
1798
1837
|
};
|
|
1799
|
-
var
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
return parsed === null ? false : CODEX_OFFICIAL_HOSTS.has(parsed.hostname.toLowerCase());
|
|
1838
|
+
var planLabel = (planType) => {
|
|
1839
|
+
const id = asNonEmptyString(planType);
|
|
1840
|
+
if (id === null || id === "unknown") return void 0;
|
|
1841
|
+
return PLAN_LABELS[id] ?? id.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1804
1842
|
};
|
|
1805
|
-
var
|
|
1806
|
-
|
|
1807
|
-
if (
|
|
1808
|
-
const
|
|
1809
|
-
return
|
|
1843
|
+
var creditBalance = (value) => {
|
|
1844
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
1845
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
1846
|
+
const numeric = Number(value.trim());
|
|
1847
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
1810
1848
|
};
|
|
1811
|
-
var
|
|
1812
|
-
var
|
|
1813
|
-
var
|
|
1814
|
-
"
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
const
|
|
1822
|
-
for (const
|
|
1823
|
-
const
|
|
1824
|
-
if (
|
|
1849
|
+
var MAIN_LIMIT_ID = "codex";
|
|
1850
|
+
var CREDITS_BALANCE_ID = "credits";
|
|
1851
|
+
var isMainCodexLimit = (snapshot) => {
|
|
1852
|
+
const limitId = asNonEmptyString(asRecord(snapshot)?.["limitId"]);
|
|
1853
|
+
return limitId === null || limitId === MAIN_LIMIT_ID;
|
|
1854
|
+
};
|
|
1855
|
+
var mapCodexSnapshotWindows = (snapshot) => {
|
|
1856
|
+
const bucket = asRecord(snapshot);
|
|
1857
|
+
if (bucket === null || !isMainCodexLimit(bucket)) return [];
|
|
1858
|
+
const windows = [];
|
|
1859
|
+
const takenIds = /* @__PURE__ */ new Set();
|
|
1860
|
+
for (const slot of ["primary", "secondary"]) {
|
|
1861
|
+
const window = mapWindow(bucket[slot], slot, classifyWindow);
|
|
1862
|
+
if (window === null) continue;
|
|
1863
|
+
const id = takenIds.has(window.id) ? `${window.id}:${slot}` : window.id;
|
|
1864
|
+
takenIds.add(id);
|
|
1865
|
+
windows.push({ ...window, id, group: MAIN_LIMIT_ID });
|
|
1866
|
+
}
|
|
1867
|
+
return windows;
|
|
1868
|
+
};
|
|
1869
|
+
var mapCodexRateLimits = (response, accountKey) => {
|
|
1870
|
+
const root = asRecord(response);
|
|
1871
|
+
const legacy = asRecord(root?.["rateLimits"]);
|
|
1872
|
+
const byLimitId = asRecord(root?.["rateLimitsByLimitId"]);
|
|
1873
|
+
const codexBucket = asRecord(byLimitId?.[MAIN_LIMIT_ID]);
|
|
1874
|
+
const main = codexBucket ?? (legacy !== null && isMainCodexLimit(legacy) ? legacy : null);
|
|
1875
|
+
if (root === null || legacy === null && codexBucket === null) return null;
|
|
1876
|
+
const windows = mapCodexSnapshotWindows(main);
|
|
1877
|
+
const takenIds = new Set(windows.map((window) => window.id));
|
|
1878
|
+
const push = (window, slot) => {
|
|
1879
|
+
if (window === null) return;
|
|
1880
|
+
const id = takenIds.has(window.id) ? `${window.id}:${slot}` : window.id;
|
|
1881
|
+
takenIds.add(id);
|
|
1882
|
+
windows.push({ ...window, id });
|
|
1883
|
+
};
|
|
1884
|
+
const others = /* @__PURE__ */ new Map();
|
|
1885
|
+
for (const [limitId, rawBucket] of Object.entries(byLimitId ?? {})) {
|
|
1886
|
+
const bucket = asRecord(rawBucket);
|
|
1887
|
+
if (limitId !== MAIN_LIMIT_ID && bucket !== null) others.set(limitId, bucket);
|
|
1888
|
+
}
|
|
1889
|
+
const legacyLimitId = asNonEmptyString(legacy?.["limitId"]);
|
|
1890
|
+
if (legacy !== null && legacyLimitId !== null && !isMainCodexLimit(legacy)) {
|
|
1891
|
+
if (!others.has(legacyLimitId)) others.set(legacyLimitId, legacy);
|
|
1892
|
+
}
|
|
1893
|
+
for (const [limitId, bucket] of others) {
|
|
1894
|
+
const name = asNonEmptyString(bucket["limitName"]) ?? limitId;
|
|
1895
|
+
for (const slot of ["primary", "secondary"]) {
|
|
1896
|
+
const window = mapWindow(bucket[slot], slot, (minutes) => ({
|
|
1897
|
+
id: `${limitId}:${slot}`,
|
|
1898
|
+
label: minutes === null ? { text: name } : {
|
|
1899
|
+
key: "usage.window.named",
|
|
1900
|
+
values: { name, duration: formatWindowDuration(minutes) }
|
|
1901
|
+
}
|
|
1902
|
+
}));
|
|
1903
|
+
push(window === null ? null : { ...window, group: limitId }, slot);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
const balances = [];
|
|
1907
|
+
const credits = asRecord(main?.["credits"]);
|
|
1908
|
+
const balance = creditBalance(credits?.["balance"]);
|
|
1909
|
+
if (credits !== null && credits["unlimited"] !== true && balance !== null) {
|
|
1910
|
+
if (credits["hasCredits"] === true || balance > 0) {
|
|
1911
|
+
balances.push({
|
|
1912
|
+
id: CREDITS_BALANCE_ID,
|
|
1913
|
+
label: { key: "usage.balance.credits" },
|
|
1914
|
+
remaining: balance,
|
|
1915
|
+
used: null,
|
|
1916
|
+
limit: null,
|
|
1917
|
+
unit: { kind: "credits" },
|
|
1918
|
+
resetsAt: null
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
const plan = planLabel((main ?? legacy)?.["planType"]);
|
|
1923
|
+
const resetSummary = asRecord(root["rateLimitResetCredits"]);
|
|
1924
|
+
const availableCount = asFiniteNumber(resetSummary?.["availableCount"]);
|
|
1925
|
+
return {
|
|
1926
|
+
status: "available",
|
|
1927
|
+
...plan === void 0 ? {} : { plan },
|
|
1928
|
+
windows,
|
|
1929
|
+
balances,
|
|
1930
|
+
accountKey,
|
|
1931
|
+
// 回包没带摘要(老版本 CLI)就是不知道,不填;填 0 会让界面说「没有可用的重置额度」。
|
|
1932
|
+
...availableCount === null || availableCount < 0 ? {} : { resetCredits: { availableCount: Math.trunc(availableCount) } }
|
|
1933
|
+
};
|
|
1934
|
+
};
|
|
1935
|
+
var codexQuotaFromRateLimits = (snapshot) => {
|
|
1936
|
+
const bucket = asRecord(snapshot);
|
|
1937
|
+
if (bucket === null) return null;
|
|
1938
|
+
const reading = mapCodexRateLimits({ rateLimits: snapshot }, "");
|
|
1939
|
+
if (reading === null || reading.status !== "available") return null;
|
|
1940
|
+
const main = isMainCodexLimit(bucket);
|
|
1941
|
+
const group = main ? MAIN_LIMIT_ID : asNonEmptyString(bucket["limitId"]);
|
|
1942
|
+
const replaces = {
|
|
1943
|
+
windowGroups: group === null ? [] : [group],
|
|
1944
|
+
balanceIds: main ? [CREDITS_BALANCE_ID] : []
|
|
1945
|
+
};
|
|
1946
|
+
return {
|
|
1947
|
+
windows: reading.windows,
|
|
1948
|
+
balances: reading.balances,
|
|
1949
|
+
...reading.plan === void 0 ? {} : { plan: reading.plan },
|
|
1950
|
+
replaces
|
|
1951
|
+
};
|
|
1952
|
+
};
|
|
1953
|
+
var classifyRateLimitsError = (message) => {
|
|
1954
|
+
const text = message.toLowerCase();
|
|
1955
|
+
if (text.includes("chatgpt authentication required")) return "unsupportedLogin";
|
|
1956
|
+
if (text.includes("authentication required")) return "missingCredentials";
|
|
1957
|
+
if (/\b(401|403)\b|unauthorized|forbidden/.test(text)) return "authRejected";
|
|
1958
|
+
if (/\b429\b|too many requests/.test(text)) return "rateLimited";
|
|
1959
|
+
return "unreachable";
|
|
1960
|
+
};
|
|
1961
|
+
var CODEX_OFFICIAL_HOSTS = /* @__PURE__ */ new Set(["chatgpt.com"]);
|
|
1962
|
+
var CODEX_DEFAULT_MODEL_PROVIDER = "openai";
|
|
1963
|
+
var isOfficialUrl = (value) => {
|
|
1964
|
+
const parsed = URL.parse(value.trim());
|
|
1965
|
+
return parsed === null ? false : CODEX_OFFICIAL_HOSTS.has(parsed.hostname.toLowerCase());
|
|
1966
|
+
};
|
|
1967
|
+
var readTomlScalar = (raw) => {
|
|
1968
|
+
const quoted = /^(["'])(.*?)\1/.exec(raw.trim());
|
|
1969
|
+
if (quoted?.[2] !== void 0) return quoted[2];
|
|
1970
|
+
const [beforeComment = ""] = raw.split("#");
|
|
1971
|
+
return beforeComment.trim();
|
|
1972
|
+
};
|
|
1973
|
+
var TABLE_HEADER = /^\[\s*([^\][]+?)\s*\]\s*(?:#.*)?$/;
|
|
1974
|
+
var KEY_VALUE = /^([A-Za-z0-9_.\-"']+)\s*=\s*(.*)$/;
|
|
1975
|
+
var BASE_URL_KEYS = /* @__PURE__ */ new Set([
|
|
1976
|
+
"chatgpt_base_url",
|
|
1977
|
+
"openai_base_url",
|
|
1978
|
+
"base_url"
|
|
1979
|
+
]);
|
|
1980
|
+
var codexConfigSignal = (contents, options) => {
|
|
1981
|
+
let table = null;
|
|
1982
|
+
let selectedProvider = CODEX_DEFAULT_MODEL_PROVIDER;
|
|
1983
|
+
const providerBaseUrls = /* @__PURE__ */ new Map();
|
|
1984
|
+
for (const rawLine of contents.split("\n")) {
|
|
1985
|
+
const line = rawLine.trim();
|
|
1986
|
+
if (line.length === 0 || line.startsWith("#")) continue;
|
|
1825
1987
|
const header = TABLE_HEADER.exec(line);
|
|
1826
1988
|
if (header !== null) {
|
|
1827
1989
|
table = header[1] ?? "";
|
|
@@ -1931,402 +2093,51 @@ var codexUsageLimitMessage = (snapshot, atIso, providerMessage) => {
|
|
|
1931
2093
|
var isUsageLimitExceeded = (error) => asRecord2(error)?.["codexErrorInfo"] === "usageLimitExceeded";
|
|
1932
2094
|
|
|
1933
2095
|
// src/events.ts
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
eventSeq += 1;
|
|
1939
|
-
return `${EVENT_ID_PREFIX}-${eventSeq}`;
|
|
1940
|
-
}
|
|
1941
|
-
function nowIso2() {
|
|
1942
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1943
|
-
}
|
|
1944
|
-
function itemIdOf(params) {
|
|
1945
|
-
if (!isRecord3(params)) return void 0;
|
|
1946
|
-
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1947
|
-
return asString(item["id"]) ?? asString(params["itemId"]) ?? void 0;
|
|
1948
|
-
}
|
|
1949
|
-
function itemStatusOf(params) {
|
|
1950
|
-
if (!isRecord3(params)) return void 0;
|
|
1951
|
-
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1952
|
-
return asString(item["status"]) ?? void 0;
|
|
1953
|
-
}
|
|
1954
|
-
function turnIdOf(params) {
|
|
1955
|
-
if (!isRecord3(params)) return void 0;
|
|
1956
|
-
return asString(params["turnId"]) ?? (isRecord3(params["turn"]) ? asString(params["turn"]["id"]) : null) ?? void 0;
|
|
1957
|
-
}
|
|
1958
|
-
function itemTitle(params) {
|
|
1959
|
-
if (!isRecord3(params)) return void 0;
|
|
1960
|
-
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1961
|
-
return trimText(item["title"]) ?? trimText(item["name"]) ?? trimText(item["command"]) ?? trimText(item["summary"]) ?? void 0;
|
|
1962
|
-
}
|
|
1963
|
-
function itemDetail(params) {
|
|
1964
|
-
if (!isRecord3(params)) return void 0;
|
|
1965
|
-
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1966
|
-
const query = isRecord3(item["action"]) ? item["action"]["queries"] : item["query"];
|
|
1967
|
-
if (Array.isArray(query)) {
|
|
1968
|
-
const joined = query.filter((entry) => typeof entry === "string").join(", ");
|
|
1969
|
-
if (joined.length > 0) return joined;
|
|
2096
|
+
function mapNotificationToEvents(input) {
|
|
2097
|
+
const { method, params, threadId, providerInstanceId, rateLimits } = input;
|
|
2098
|
+
if (method.startsWith("collabAgent/")) {
|
|
2099
|
+
return mapCollabAgentEvent({ method, params, rootThreadId: threadId, providerInstanceId });
|
|
1970
2100
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2101
|
+
if (method === "error") {
|
|
2102
|
+
if (isUsageLimitExceeded(isRecord3(params) ? params["error"] : void 0)) return [];
|
|
2103
|
+
const willRetry = isRecord3(params) && params["willRetry"] === true;
|
|
2104
|
+
const nested = isRecord3(params) && isRecord3(params["error"]) ? params["error"] : void 0;
|
|
2105
|
+
const message = (nested ? trimText(nested["message"]) : void 0) ?? (isRecord3(params) ? trimText(params["message"]) : void 0) ?? "Codex error";
|
|
2106
|
+
const payload = { message };
|
|
2107
|
+
if (!willRetry) payload["class"] = "provider_error";
|
|
2108
|
+
if (params !== void 0) payload["detail"] = params;
|
|
2109
|
+
return [
|
|
2110
|
+
baseEvent({
|
|
2111
|
+
type: willRetry ? "runtime.warning" : "runtime.error",
|
|
2112
|
+
threadId,
|
|
2113
|
+
payload,
|
|
2114
|
+
turnId: turnIdOf(params),
|
|
2115
|
+
method,
|
|
2116
|
+
providerInstanceId
|
|
2117
|
+
})
|
|
2118
|
+
];
|
|
1984
2119
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
return state === "completed" ? "idle" : state;
|
|
1999
|
-
}
|
|
2000
|
-
function threadStateFromStatus(status) {
|
|
2001
|
-
const type = isRecord3(status) ? asString(status["type"]) : asString(status);
|
|
2002
|
-
if (type === "idle") return "idle";
|
|
2003
|
-
if (type === "systemError" || type === "error") return "error";
|
|
2004
|
-
return "active";
|
|
2005
|
-
}
|
|
2006
|
-
function codexCompactionId(threadId, turnId) {
|
|
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"]);
|
|
2022
|
-
return {
|
|
2023
|
-
usedTokens,
|
|
2024
|
-
lastUsedTokens: usedTokens,
|
|
2025
|
-
...totalProcessedTokens !== void 0 && totalProcessedTokens > usedTokens ? { totalProcessedTokens } : {},
|
|
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
|
|
2032
|
-
};
|
|
2033
|
-
}
|
|
2034
|
-
function turnCompletedControlFlow(params) {
|
|
2035
|
-
const turn = isRecord3(params) ? isRecord3(params["turn"]) ? params["turn"] : params : {};
|
|
2036
|
-
const state = toTurnStatus(asString(turn["status"]) ?? void 0);
|
|
2037
|
-
const error = isRecord3(turn["error"]) ? trimText(turn["error"]["message"]) : void 0;
|
|
2038
|
-
return error ? { state, errorMessage: error } : { state };
|
|
2039
|
-
}
|
|
2040
|
-
function toUserInputQuestions(params) {
|
|
2041
|
-
if (!isRecord3(params)) return void 0;
|
|
2042
|
-
const questions = Array.isArray(params["questions"]) ? params["questions"] : [];
|
|
2043
|
-
const mapped = [];
|
|
2044
|
-
for (const question of questions) {
|
|
2045
|
-
if (!isRecord3(question)) return void 0;
|
|
2046
|
-
const id = asString(question["id"]);
|
|
2047
|
-
const header = asString(question["header"]);
|
|
2048
|
-
const text = asString(question["question"]);
|
|
2049
|
-
const rawOptions = Array.isArray(question["options"]) ? question["options"] : [];
|
|
2050
|
-
const options = [];
|
|
2051
|
-
for (const option of rawOptions) {
|
|
2052
|
-
if (!isRecord3(option)) continue;
|
|
2053
|
-
const label = trimText(option["label"]);
|
|
2054
|
-
if (!label) continue;
|
|
2055
|
-
options.push({
|
|
2056
|
-
...asString(option["id"]) ? { id: asString(option["id"]) } : {},
|
|
2057
|
-
label,
|
|
2058
|
-
description: trimText(option["description"]) ?? ""
|
|
2059
|
-
});
|
|
2060
|
-
}
|
|
2061
|
-
if (!id || !header || !text || options.length === 0) return void 0;
|
|
2062
|
-
mapped.push({
|
|
2063
|
-
id,
|
|
2064
|
-
header,
|
|
2065
|
-
question: text,
|
|
2066
|
-
options,
|
|
2067
|
-
multiSelect: false
|
|
2068
|
-
});
|
|
2069
|
-
}
|
|
2070
|
-
return mapped.length > 0 ? mapped : void 0;
|
|
2071
|
-
}
|
|
2072
|
-
function toAsyncQuestions(params) {
|
|
2073
|
-
if (!isRecord3(params)) return void 0;
|
|
2074
|
-
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
2075
|
-
if (item["delivery"] !== "async" || !Array.isArray(item["questions"])) return void 0;
|
|
2076
|
-
const mapped = [];
|
|
2077
|
-
item["questions"].forEach((question, index) => {
|
|
2078
|
-
if (!isRecord3(question)) return;
|
|
2079
|
-
const title = trimText(question["title"]);
|
|
2080
|
-
if (!title) return;
|
|
2081
|
-
const labels = Array.isArray(question["options"]) ? question["options"] : [];
|
|
2082
|
-
const options = labels.map((label) => trimText(label)).filter((label) => label !== void 0).map((label) => ({ label, description: "" }));
|
|
2083
|
-
mapped.push({
|
|
2084
|
-
id: `q${index}`,
|
|
2085
|
-
header: title,
|
|
2086
|
-
question: title,
|
|
2087
|
-
options,
|
|
2088
|
-
allowFreeform: true,
|
|
2089
|
-
multiSelect: false
|
|
2090
|
-
});
|
|
2091
|
-
});
|
|
2092
|
-
return mapped.length > 0 ? mapped : void 0;
|
|
2093
|
-
}
|
|
2094
|
-
function toCodexUserInputAnswers(answers) {
|
|
2095
|
-
if (!isRecord3(answers)) return void 0;
|
|
2096
|
-
const out = {};
|
|
2097
|
-
for (const [questionId, value] of Object.entries(answers)) {
|
|
2098
|
-
if (typeof value === "string") {
|
|
2099
|
-
out[questionId] = { answers: [value] };
|
|
2100
|
-
continue;
|
|
2101
|
-
}
|
|
2102
|
-
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
2103
|
-
out[questionId] = { answers: [...value] };
|
|
2104
|
-
continue;
|
|
2105
|
-
}
|
|
2106
|
-
if (isRecord3(value) && Array.isArray(value["answers"]) && value["answers"].every((entry) => typeof entry === "string")) {
|
|
2107
|
-
out[questionId] = { answers: [...value["answers"]] };
|
|
2108
|
-
continue;
|
|
2120
|
+
if (method === "process/stderr") {
|
|
2121
|
+
const text = isRecord3(params) ? trimText(params["text"]) ?? "" : "";
|
|
2122
|
+
if (!text) return [];
|
|
2123
|
+
if (isFatalStderr(text)) {
|
|
2124
|
+
return [
|
|
2125
|
+
baseEvent({
|
|
2126
|
+
type: "runtime.error",
|
|
2127
|
+
threadId,
|
|
2128
|
+
payload: { message: text },
|
|
2129
|
+
method,
|
|
2130
|
+
providerInstanceId
|
|
2131
|
+
})
|
|
2132
|
+
];
|
|
2109
2133
|
}
|
|
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
|
-
}
|
|
2202
|
-
if (method === "collabAgent/completed" || method === "task.completed") {
|
|
2203
|
-
const status = collabTurnEndStatus(asString(input.params["turnStatus"]) ?? void 0);
|
|
2204
|
-
return [
|
|
2205
|
-
baseEvent({
|
|
2206
|
-
type: "task.updated",
|
|
2207
|
-
threadId,
|
|
2208
|
-
payload: { taskId: agentThreadId, status, ...linkage },
|
|
2209
|
-
method,
|
|
2210
|
-
providerInstanceId: input.providerInstanceId
|
|
2211
|
-
})
|
|
2212
|
-
];
|
|
2213
|
-
}
|
|
2214
|
-
if (method === "collabAgent/statusChanged") {
|
|
2215
|
-
const raw = asString(input.params["status"]);
|
|
2216
|
-
const status = raw === "systemError" || raw === "error" ? "failed" : raw === "idle" ? "idle" : "running";
|
|
2217
|
-
return [
|
|
2218
|
-
baseEvent({
|
|
2219
|
-
type: "task.updated",
|
|
2220
|
-
threadId,
|
|
2221
|
-
payload: { taskId: agentThreadId, status, ...linkage },
|
|
2222
|
-
method,
|
|
2223
|
-
providerInstanceId: input.providerInstanceId
|
|
2224
|
-
})
|
|
2225
|
-
];
|
|
2226
|
-
}
|
|
2227
|
-
if (method === "collabAgent/metadataUpdated") {
|
|
2228
2134
|
return [
|
|
2229
2135
|
baseEvent({
|
|
2230
|
-
type: "
|
|
2231
|
-
threadId,
|
|
2232
|
-
payload: { taskId: agentThreadId, ...linkage },
|
|
2233
|
-
method,
|
|
2234
|
-
providerInstanceId: input.providerInstanceId
|
|
2235
|
-
})
|
|
2236
|
-
];
|
|
2237
|
-
}
|
|
2238
|
-
if (method === "collabAgent/metadata") {
|
|
2239
|
-
const status = asCollabChildStatus(input.params["status"]);
|
|
2240
|
-
if (status === void 0) return [];
|
|
2241
|
-
return [
|
|
2242
|
-
baseEvent({
|
|
2243
|
-
type: "task.updated",
|
|
2136
|
+
type: "runtime.warning",
|
|
2244
2137
|
threadId,
|
|
2245
|
-
payload: {
|
|
2138
|
+
payload: { message: text },
|
|
2246
2139
|
method,
|
|
2247
|
-
providerInstanceId
|
|
2248
|
-
})
|
|
2249
|
-
];
|
|
2250
|
-
}
|
|
2251
|
-
if (method === "collabAgent/tokenUsage") {
|
|
2252
|
-
const tokenUsage = isRecord3(input.params["tokenUsage"]) ? input.params["tokenUsage"] : void 0;
|
|
2253
|
-
const total = tokenUsage && isRecord3(tokenUsage["total"]) ? tokenUsage["total"] : void 0;
|
|
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
|
-
}
|
|
2283
|
-
return [];
|
|
2284
|
-
}
|
|
2285
|
-
function mapNotificationToEvents(input) {
|
|
2286
|
-
const { method, params, threadId, providerInstanceId, rateLimits } = input;
|
|
2287
|
-
if (method.startsWith("collabAgent/")) {
|
|
2288
|
-
return mapCollabAgentEvent({ method, params, rootThreadId: threadId, providerInstanceId });
|
|
2289
|
-
}
|
|
2290
|
-
if (method === "error") {
|
|
2291
|
-
if (isUsageLimitExceeded(isRecord3(params) ? params["error"] : void 0)) return [];
|
|
2292
|
-
const willRetry = isRecord3(params) && params["willRetry"] === true;
|
|
2293
|
-
const nested = isRecord3(params) && isRecord3(params["error"]) ? params["error"] : void 0;
|
|
2294
|
-
const message = (nested ? trimText(nested["message"]) : void 0) ?? (isRecord3(params) ? trimText(params["message"]) : void 0) ?? "Codex error";
|
|
2295
|
-
const payload = { message };
|
|
2296
|
-
if (!willRetry) payload["class"] = "provider_error";
|
|
2297
|
-
if (params !== void 0) payload["detail"] = params;
|
|
2298
|
-
return [
|
|
2299
|
-
baseEvent({
|
|
2300
|
-
type: willRetry ? "runtime.warning" : "runtime.error",
|
|
2301
|
-
threadId,
|
|
2302
|
-
payload,
|
|
2303
|
-
turnId: turnIdOf(params),
|
|
2304
|
-
method,
|
|
2305
|
-
providerInstanceId
|
|
2306
|
-
})
|
|
2307
|
-
];
|
|
2308
|
-
}
|
|
2309
|
-
if (method === "process/stderr") {
|
|
2310
|
-
const text = isRecord3(params) ? trimText(params["text"]) ?? "" : "";
|
|
2311
|
-
if (!text) return [];
|
|
2312
|
-
if (isFatalStderr(text)) {
|
|
2313
|
-
return [
|
|
2314
|
-
baseEvent({
|
|
2315
|
-
type: "runtime.error",
|
|
2316
|
-
threadId,
|
|
2317
|
-
payload: { message: text },
|
|
2318
|
-
method,
|
|
2319
|
-
providerInstanceId
|
|
2320
|
-
})
|
|
2321
|
-
];
|
|
2322
|
-
}
|
|
2323
|
-
return [
|
|
2324
|
-
baseEvent({
|
|
2325
|
-
type: "runtime.warning",
|
|
2326
|
-
threadId,
|
|
2327
|
-
payload: { message: text },
|
|
2328
|
-
method,
|
|
2329
|
-
providerInstanceId
|
|
2140
|
+
providerInstanceId
|
|
2330
2141
|
})
|
|
2331
2142
|
];
|
|
2332
2143
|
}
|
|
@@ -2851,6 +2662,203 @@ function mapNotificationToEvents(input) {
|
|
|
2851
2662
|
return [];
|
|
2852
2663
|
}
|
|
2853
2664
|
}
|
|
2665
|
+
|
|
2666
|
+
// src/approvalMapping.ts
|
|
2667
|
+
function fileChangeEntries(params) {
|
|
2668
|
+
if (!isRecord3(params)) return [];
|
|
2669
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
2670
|
+
const changes = item["changes"] ?? item["fileChanges"] ?? params["fileChanges"];
|
|
2671
|
+
if (!isRecord3(changes) && !Array.isArray(changes)) return [];
|
|
2672
|
+
const entries = Array.isArray(changes) ? changes : Object.entries(changes).map(([path, change]) => ({
|
|
2673
|
+
path,
|
|
2674
|
+
...isRecord3(change) ? change : {}
|
|
2675
|
+
}));
|
|
2676
|
+
const out = [];
|
|
2677
|
+
for (const entry of entries) {
|
|
2678
|
+
if (!isRecord3(entry)) continue;
|
|
2679
|
+
const path = asString(entry["path"]) ?? asString(entry["file"]);
|
|
2680
|
+
if (!path) continue;
|
|
2681
|
+
out.push({
|
|
2682
|
+
path,
|
|
2683
|
+
kind: (asString(entry["type"]) ?? asString(entry["kind"]) ?? "update").toLowerCase(),
|
|
2684
|
+
movePath: asString(entry["move_path"]) ?? asString(entry["movePath"]),
|
|
2685
|
+
diff: asString(entry["unified_diff"]) ?? asString(entry["unifiedDiff"]),
|
|
2686
|
+
content: asString(entry["content"])
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
return out;
|
|
2690
|
+
}
|
|
2691
|
+
var MAX_DESCRIBED_FILE_CHANGES = 20;
|
|
2692
|
+
function describeFileChanges(params) {
|
|
2693
|
+
const entries = fileChangeEntries(params).toSorted(
|
|
2694
|
+
(left, right) => left.path.localeCompare(right.path)
|
|
2695
|
+
);
|
|
2696
|
+
if (entries.length === 0) return void 0;
|
|
2697
|
+
const lines = entries.slice(0, MAX_DESCRIBED_FILE_CHANGES).map(
|
|
2698
|
+
(entry) => entry.movePath ? `${entry.kind} ${entry.path} -> ${entry.movePath}` : `${entry.kind} ${entry.path}`
|
|
2699
|
+
);
|
|
2700
|
+
const remaining = entries.length - lines.length;
|
|
2701
|
+
return remaining > 0 ? `${lines.join("\n")}
|
|
2702
|
+
+${remaining} more` : lines.join("\n");
|
|
2703
|
+
}
|
|
2704
|
+
function toApplyPatchFileChanges(params) {
|
|
2705
|
+
const out = [];
|
|
2706
|
+
for (const { path, kind, movePath, diff, content } of fileChangeEntries(params)) {
|
|
2707
|
+
if (kind === "add") {
|
|
2708
|
+
out.push({
|
|
2709
|
+
path,
|
|
2710
|
+
changeKind: "add",
|
|
2711
|
+
...content !== void 0 ? { content: { kind: "replace", newText: content } } : {}
|
|
2712
|
+
});
|
|
2713
|
+
} else if (kind === "delete") {
|
|
2714
|
+
out.push({
|
|
2715
|
+
path,
|
|
2716
|
+
changeKind: "delete",
|
|
2717
|
+
...content !== void 0 ? { content: { kind: "replace", oldText: content, newText: "" } } : {}
|
|
2718
|
+
});
|
|
2719
|
+
} else if (movePath) {
|
|
2720
|
+
out.push({
|
|
2721
|
+
path,
|
|
2722
|
+
changeKind: "rename",
|
|
2723
|
+
...diff ? { content: { kind: "unified", diff } } : {}
|
|
2724
|
+
});
|
|
2725
|
+
} else {
|
|
2726
|
+
out.push({
|
|
2727
|
+
path,
|
|
2728
|
+
changeKind: "modify",
|
|
2729
|
+
...diff ? { content: { kind: "unified", diff } } : {}
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
return out;
|
|
2734
|
+
}
|
|
2735
|
+
function describeCommandApproval(params) {
|
|
2736
|
+
const record = isRecord3(params) ? params : {};
|
|
2737
|
+
const commandValue = record["command"];
|
|
2738
|
+
const command = Array.isArray(commandValue) ? commandValue.filter((entry) => typeof entry === "string").join(" ") : asString(commandValue) ?? "";
|
|
2739
|
+
const cwd = asString(record["cwd"]);
|
|
2740
|
+
const reason = trimText(record["reason"]);
|
|
2741
|
+
const callId = asString(record["callId"]);
|
|
2742
|
+
return {
|
|
2743
|
+
command,
|
|
2744
|
+
...cwd ? { cwd } : {},
|
|
2745
|
+
...reason ? { reason } : {},
|
|
2746
|
+
...callId ? { callId } : {}
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
function describeFileChangeApproval(params) {
|
|
2750
|
+
const record = isRecord3(params) ? params : {};
|
|
2751
|
+
const reason = trimText(record["reason"]);
|
|
2752
|
+
const grantRoot = asString(record["grantRoot"]);
|
|
2753
|
+
const callId = asString(record["callId"]);
|
|
2754
|
+
const detail = reason ?? describeFileChanges(record) ?? trimText(grantRoot);
|
|
2755
|
+
return {
|
|
2756
|
+
...reason ? { reason } : {},
|
|
2757
|
+
...detail ? { detail } : {},
|
|
2758
|
+
...grantRoot ? { grantRoot } : {},
|
|
2759
|
+
...callId ? { callId } : {},
|
|
2760
|
+
fileChanges: toApplyPatchFileChanges(record)
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
function describePermissionsApproval(params) {
|
|
2764
|
+
const record = isRecord3(params) ? params : {};
|
|
2765
|
+
const permissions = isRecord3(record["permissions"]) ? record["permissions"] : {};
|
|
2766
|
+
const fileSystem = isRecord3(permissions["fileSystem"]) ? permissions["fileSystem"] : {};
|
|
2767
|
+
const network = isRecord3(permissions["network"]) ? permissions["network"] : {};
|
|
2768
|
+
const pathsOf = (value) => Array.isArray(value) ? value.flatMap((entry) => typeof entry === "string" && entry.length > 0 ? [entry] : []) : [];
|
|
2769
|
+
const read = pathsOf(fileSystem["read"]);
|
|
2770
|
+
const write = pathsOf(fileSystem["write"]);
|
|
2771
|
+
const entries = Array.isArray(fileSystem["entries"]) ? fileSystem["entries"] : [];
|
|
2772
|
+
for (const entry of entries) {
|
|
2773
|
+
if (!isRecord3(entry) || !isRecord3(entry["path"])) continue;
|
|
2774
|
+
const target = entry["path"];
|
|
2775
|
+
const path = target["type"] === "path" ? asString(target["path"]) : target["type"] === "glob_pattern" ? asString(target["pattern"]) : void 0;
|
|
2776
|
+
if (!path) continue;
|
|
2777
|
+
if (entry["access"] === "read") read.push(path);
|
|
2778
|
+
if (entry["access"] === "write") write.push(path);
|
|
2779
|
+
}
|
|
2780
|
+
const reason = trimText(record["reason"]);
|
|
2781
|
+
return {
|
|
2782
|
+
...reason ? { reason } : {},
|
|
2783
|
+
read: [...new Set(read)],
|
|
2784
|
+
write: [...new Set(write)],
|
|
2785
|
+
network: network["enabled"] === true
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2788
|
+
function toPermissionsResponse(decision, params, persistent) {
|
|
2789
|
+
if (decision !== "allow") return { permissions: {} };
|
|
2790
|
+
const requested = isRecord3(params) && isRecord3(params["permissions"]) ? params["permissions"] : {};
|
|
2791
|
+
return {
|
|
2792
|
+
permissions: {
|
|
2793
|
+
...requested["fileSystem"] !== void 0 ? { fileSystem: requested["fileSystem"] } : {},
|
|
2794
|
+
...requested["network"] !== void 0 ? { network: requested["network"] } : {}
|
|
2795
|
+
},
|
|
2796
|
+
scope: persistent ? "session" : "turn"
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
function describeMcpElicitation(params) {
|
|
2800
|
+
const record = isRecord3(params) ? params : {};
|
|
2801
|
+
const appName = asString(record["appName"]) ?? asString(record["serverName"]) ?? void 0;
|
|
2802
|
+
const serverName = asString(record["serverName"]);
|
|
2803
|
+
const message = trimText(record["message"]);
|
|
2804
|
+
const url = asString(record["url"]) ?? asString(record["authorizationUrl"]);
|
|
2805
|
+
const identity = appName ?? url ?? "unknown";
|
|
2806
|
+
return {
|
|
2807
|
+
...appName ? { appName } : {},
|
|
2808
|
+
...serverName ? { serverName } : {},
|
|
2809
|
+
...message ? { message } : {},
|
|
2810
|
+
...url ? { url } : {},
|
|
2811
|
+
identity,
|
|
2812
|
+
mode: url ? "url" : "form",
|
|
2813
|
+
...record["requestedSchema"] !== void 0 ? { schema: record["requestedSchema"] } : {}
|
|
2814
|
+
};
|
|
2815
|
+
}
|
|
2816
|
+
function persistFromMeta(meta) {
|
|
2817
|
+
if (!isRecord3(meta)) return void 0;
|
|
2818
|
+
const persist = asString(meta["persist"]) ?? asString(meta["persistence"]);
|
|
2819
|
+
if (persist === "session" || persist === "acceptForSession") return "session";
|
|
2820
|
+
if (persist === "always" || persist === "permanent" || persist === "forever" || persist === "persistent" || persist === "acceptAlways") {
|
|
2821
|
+
return "always";
|
|
2822
|
+
}
|
|
2823
|
+
return void 0;
|
|
2824
|
+
}
|
|
2825
|
+
function toMcpElicitationResponse(decision, params) {
|
|
2826
|
+
const described = describeMcpElicitation(params);
|
|
2827
|
+
if (decision !== "allow" || described.mode === "url") {
|
|
2828
|
+
return {
|
|
2829
|
+
action: decision === "allow" ? "decline" : decision === "cancel" ? "cancel" : "decline"
|
|
2830
|
+
};
|
|
2831
|
+
}
|
|
2832
|
+
const schema = isRecord3(described.schema) ? described.schema : void 0;
|
|
2833
|
+
const properties = schema && isRecord3(schema["properties"]) ? schema["properties"] : {};
|
|
2834
|
+
const required = schema && Array.isArray(schema["required"]) ? schema["required"] : [];
|
|
2835
|
+
const content = {};
|
|
2836
|
+
for (const [key, spec] of Object.entries(properties)) {
|
|
2837
|
+
if (!isRecord3(spec)) continue;
|
|
2838
|
+
if (Array.isArray(spec["oneOf"]) || Array.isArray(spec["enum"])) {
|
|
2839
|
+
const options = Array.isArray(spec["oneOf"]) ? spec["oneOf"] : Array.isArray(spec["enum"]) ? spec["enum"] : [];
|
|
2840
|
+
const first = options[0];
|
|
2841
|
+
content[key] = isRecord3(first) ? first["const"] ?? first["value"] ?? first : first;
|
|
2842
|
+
} else if (spec["type"] === "boolean") {
|
|
2843
|
+
content[key] = true;
|
|
2844
|
+
} else if (typeof spec["default"] !== "undefined") {
|
|
2845
|
+
content[key] = spec["default"];
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
for (const key of required) {
|
|
2849
|
+
if (typeof key === "string" && !(key in content)) {
|
|
2850
|
+
return { action: "decline" };
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
const persist = persistFromMeta(isRecord3(params) ? params["_meta"] : void 0);
|
|
2854
|
+
return {
|
|
2855
|
+
action: "accept",
|
|
2856
|
+
content,
|
|
2857
|
+
...persist ? { _meta: { persist } } : {}
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
// src/events/serverRequests.ts
|
|
2854
2862
|
function toRequestTypeFromKind(kind) {
|
|
2855
2863
|
switch (kind) {
|
|
2856
2864
|
case "command":
|
|
@@ -2995,311 +3003,90 @@ var windowsPath = (path) => path.replaceAll("/", "\\").toLowerCase();
|
|
|
2995
3003
|
function basename(path) {
|
|
2996
3004
|
const parts = path.split(/[\\/]/);
|
|
2997
3005
|
return (parts[parts.length - 1] ?? "").toLowerCase();
|
|
2998
|
-
}
|
|
2999
|
-
function codexCommandMatcher(display) {
|
|
3000
|
-
const expected = splitPosixCommand(display);
|
|
3001
|
-
if (expected === null || expected.length === 0) return void 0;
|
|
3002
|
-
return (argv, platform2) => {
|
|
3003
|
-
if (platform2 !== "win32") return posixCommandMatches(expected, argv);
|
|
3004
|
-
if (argv.length !== expected.length) return false;
|
|
3005
|
-
const [actualExe, ...actualRest] = argv;
|
|
3006
|
-
const [expectedExe, ...expectedRest] = expected;
|
|
3007
|
-
if (actualExe === void 0 || expectedExe === void 0) return false;
|
|
3008
|
-
if (windowsPath(actualExe) !== windowsPath(expectedExe)) return false;
|
|
3009
|
-
const lastIndex = expectedRest.length - 1;
|
|
3010
|
-
return expectedRest.every((arg, index) => {
|
|
3011
|
-
const actual = actualRest[index];
|
|
3012
|
-
if (actual === arg) return true;
|
|
3013
|
-
return index === lastIndex && index >= 1 && expectedRest[index - 1]?.toLowerCase() === "-command" && isPowerShellExecutableName(basename(expectedExe)) && actual === WINDOWS_POWERSHELL_PREFIX + arg;
|
|
3014
|
-
});
|
|
3015
|
-
};
|
|
3016
|
-
}
|
|
3017
|
-
function trackOpenCommand(open, method, params) {
|
|
3018
|
-
if (!isRecord3(params)) return;
|
|
3019
|
-
const turnId = turnIdOf(params);
|
|
3020
|
-
if (method === "turn/completed") {
|
|
3021
|
-
for (const [id2, entry] of open) {
|
|
3022
|
-
if (turnId === void 0 || entry.turnId === turnId) open.delete(id2);
|
|
3023
|
-
}
|
|
3024
|
-
return;
|
|
3025
|
-
}
|
|
3026
|
-
const item = params["item"];
|
|
3027
|
-
if (!isRecord3(item) || item["type"] !== "commandExecution") return;
|
|
3028
|
-
const id = item["id"];
|
|
3029
|
-
if (typeof id !== "string") return;
|
|
3030
|
-
if (method === "item/completed") {
|
|
3031
|
-
open.delete(id);
|
|
3032
|
-
return;
|
|
3033
|
-
}
|
|
3034
|
-
if (method !== "item/started") return;
|
|
3035
|
-
const startedAtMs = params["startedAtMs"];
|
|
3036
|
-
const command = item["command"];
|
|
3037
|
-
if (typeof startedAtMs !== "number" || typeof command !== "string") return;
|
|
3038
|
-
open.set(id, { turnId, startedAtMs, command });
|
|
3039
|
-
}
|
|
3040
|
-
function codexSessionProcessMatcher(appServerArgs) {
|
|
3041
|
-
return (argv) => argv.length === appServerArgs.length + 1 && sameArgv(argv.slice(1), appServerArgs);
|
|
3042
|
-
}
|
|
3043
|
-
function openCommandsOf(booked) {
|
|
3044
|
-
if (booked === void 0) return void 0;
|
|
3045
|
-
const activeTurn = booked.runtime.activeTurnId;
|
|
3046
|
-
const commands = [...booked.runtime.openCommands].flatMap(([toolCallId, entry]) => {
|
|
3047
|
-
if (activeTurn !== void 0 && entry.turnId !== activeTurn) return [];
|
|
3048
|
-
const matches = codexCommandMatcher(entry.command);
|
|
3049
|
-
return matches === void 0 ? [] : [{ toolCallId, startedAtMs: entry.startedAtMs, matches }];
|
|
3050
|
-
});
|
|
3051
|
-
if (commands.length === 0) return void 0;
|
|
3052
|
-
return {
|
|
3053
|
-
rootPid: booked.handle.pid,
|
|
3054
|
-
sessionProcess: codexSessionProcessMatcher(booked.runtime.appServerArgs),
|
|
3055
|
-
commands
|
|
3056
|
-
};
|
|
3057
|
-
}
|
|
3058
|
-
|
|
3059
|
-
// src/sessionRuntime.ts
|
|
3060
|
-
var COLLAB_META_LOOKUP_TIMEOUT_MS = 5e3;
|
|
3061
|
-
function metaValue(value) {
|
|
3062
|
-
const text = asString(value)?.trim();
|
|
3063
|
-
return text === void 0 || text.length === 0 ? void 0 : text;
|
|
3064
|
-
}
|
|
3065
|
-
function cancelWireFor(pending) {
|
|
3066
|
-
if (pending.kind === "permission") return toPermissionsResponse("cancel", pending.params, false);
|
|
3067
|
-
return pending.kind === "mcp-elicitation" ? toMcpElicitationResponse("cancel", pending.params) : { decision: "cancel" };
|
|
3068
|
-
}
|
|
3069
|
-
var sleep = (ms) => new Promise((resolve2) => {
|
|
3070
|
-
setTimeout(resolve2, ms);
|
|
3071
|
-
});
|
|
3072
|
-
function isRootChild(agentPath, agentThreadId, rootThreadId) {
|
|
3073
|
-
if (agentThreadId === rootThreadId) return true;
|
|
3074
|
-
return agentPath === "/root" || agentPath === "/";
|
|
3075
|
-
}
|
|
3076
|
-
function readSubAgentActivity(method, params) {
|
|
3077
|
-
if (method !== "item/started" && method !== "item/completed") return void 0;
|
|
3078
|
-
if (itemTypeOf(params) !== "subAgentActivity") return void 0;
|
|
3079
|
-
const item = isRecord3(params) ? isRecord3(params["item"]) ? params["item"] : params : {};
|
|
3080
|
-
return {
|
|
3081
|
-
agentThreadId: asString(item["agentThreadId"]) ?? void 0,
|
|
3082
|
-
agentPath: asString(item["agentPath"]) ?? void 0
|
|
3083
|
-
};
|
|
3084
|
-
}
|
|
3085
|
-
function pathLeaf2(value) {
|
|
3086
|
-
if (!value) return void 0;
|
|
3087
|
-
const parts = value.split("/").filter((part) => part.length > 0);
|
|
3088
|
-
return parts.at(-1);
|
|
3089
|
-
}
|
|
3090
|
-
function unconfirmedAnswerError(requestId, delivery) {
|
|
3091
|
-
return new Error(
|
|
3092
|
-
delivery === "closed" ? `could not confirm that codex received the answer to request ${requestId}: the session closed before the write was confirmed` : `could not confirm that codex received the answer to request ${requestId}: writing it to the CLI failed`
|
|
3093
|
-
);
|
|
3094
|
-
}
|
|
3095
|
-
var CodexSessionRuntime = class {
|
|
3096
|
-
threadId;
|
|
3097
|
-
cwd;
|
|
3098
|
-
session;
|
|
3099
|
-
nativeThreadId;
|
|
3100
|
-
activeTurnId;
|
|
3101
|
-
model;
|
|
3102
|
-
runtimeMode;
|
|
3103
|
-
/** 宿主解好的这一轮的档(座位画的就是它)。缺席 = 宿主没给,按 `runtimeMode` 查表。 */
|
|
3104
|
-
nativeModeId;
|
|
3105
|
-
interactionMode;
|
|
3106
|
-
closed = false;
|
|
3107
|
-
closeCompleted = false;
|
|
3108
|
-
turns = [];
|
|
3109
|
-
pendingApprovals = /* @__PURE__ */ new Map();
|
|
3110
|
-
pendingUserInputs = /* @__PURE__ */ new Map();
|
|
3111
|
-
requestKinds = /* @__PURE__ */ new Map();
|
|
3112
|
-
writeAcks = /* @__PURE__ */ new Map();
|
|
3113
|
-
grants;
|
|
3114
|
-
declineRelay;
|
|
3115
|
-
collabChildren = /* @__PURE__ */ new Map();
|
|
3116
|
-
collabChildMeta = /* @__PURE__ */ new Map();
|
|
3117
|
-
liveForeignTurns = /* @__PURE__ */ new Map();
|
|
3118
|
-
openCommands = /* @__PURE__ */ new Map();
|
|
3119
|
-
collabReceiverTurns = /* @__PURE__ */ new Map();
|
|
3120
|
-
memoryThreadIds = /* @__PURE__ */ new Set();
|
|
3121
|
-
/** 这个会话见过的主额度快照(按字段合并),额度用尽停轮时点名窗口用。 */
|
|
3122
|
-
rateLimits;
|
|
3123
|
-
appServerArgs;
|
|
3124
|
-
hasConfiguredMcpServer;
|
|
3125
|
-
host;
|
|
3126
|
-
attachments;
|
|
3127
|
-
rpc;
|
|
3128
|
-
emit;
|
|
3129
|
-
/**
|
|
3130
|
-
* 对外 requestId 的前缀,每个会话(即每个 app-server 进程)一份。JSON-RPC id 每个进程
|
|
3131
|
-
* 都从 0 数起,宿主按 requestId 记「已接受」,不加前缀的话重启后的新请求会撞上旧请求。
|
|
3132
|
-
*/
|
|
3133
|
-
#requestIdPrefix = `codex-${NodeCrypto2.randomUUID()}:`;
|
|
3134
|
-
/** JSON-RPC id → 对外 requestId。`serverRequest/resolved` 里的 id 可能是数字也可能是字符串。 */
|
|
3135
|
-
requestIdFor(jsonRpcId) {
|
|
3136
|
-
return `${this.#requestIdPrefix}${String(jsonRpcId)}`;
|
|
3137
|
-
}
|
|
3138
|
-
/** 这一轮该用哪套三字段组合:只由这一轮的档决定(见 `codexNativeModeFor`)。 */
|
|
3139
|
-
#threadConfig() {
|
|
3140
|
-
return codexThreadConfigForNativeMode(codexNativeModeFor(this.runtimeMode, this.nativeModeId));
|
|
3141
|
-
}
|
|
3142
|
-
/** 同上,沙箱策略那一位。 */
|
|
3143
|
-
#turnSandboxPolicy() {
|
|
3144
|
-
return codexTurnSandboxPolicyForNativeMode(
|
|
3145
|
-
codexNativeModeFor(this.runtimeMode, this.nativeModeId)
|
|
3146
|
-
);
|
|
3147
|
-
}
|
|
3148
|
-
/** 给 CLI 的应答写入失败:带上线程与方法名记一行 warn。 */
|
|
3149
|
-
#warnAnswerWriteFailed(method, jsonRpcId, error) {
|
|
3150
|
-
this.host.log.warn("writing an answer to codex failed", {
|
|
3151
|
-
threadId: this.threadId,
|
|
3152
|
-
method,
|
|
3153
|
-
jsonRpcId,
|
|
3154
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3155
|
-
});
|
|
3156
|
-
}
|
|
3157
|
-
constructor(host, rpc, emit, input, cwd, appServerArgs, grants, declineRelay, attachments) {
|
|
3158
|
-
this.attachments = attachments;
|
|
3159
|
-
this.host = host;
|
|
3160
|
-
this.rpc = rpc;
|
|
3161
|
-
this.emit = emit;
|
|
3162
|
-
this.threadId = input.threadId;
|
|
3163
|
-
this.cwd = cwd;
|
|
3164
|
-
this.runtimeMode = input.runtimeMode;
|
|
3165
|
-
this.nativeModeId = input.nativeModeId;
|
|
3166
|
-
this.appServerArgs = appServerArgs;
|
|
3167
|
-
this.hasConfiguredMcpServer = (input.mcpServers?.length ?? 0) > 0;
|
|
3168
|
-
this.grants = grants ?? createApprovalGrantStore();
|
|
3169
|
-
this.declineRelay = declineRelay ?? createDeclineReasonRelay();
|
|
3170
|
-
const createdAt = nowIso();
|
|
3171
|
-
this.session = {
|
|
3172
|
-
provider: CODEX_DRIVER_KIND,
|
|
3173
|
-
providerInstanceId: host.instanceId,
|
|
3174
|
-
status: "idle",
|
|
3175
|
-
runtimeMode: input.runtimeMode,
|
|
3176
|
-
nativeModeId: codexNativeModeFor(input.runtimeMode, input.nativeModeId),
|
|
3177
|
-
cwd,
|
|
3178
|
-
threadId: input.threadId,
|
|
3179
|
-
createdAt,
|
|
3180
|
-
updatedAt: createdAt,
|
|
3181
|
-
...input.bindingGeneration === void 0 ? {} : { bindingGeneration: input.bindingGeneration }
|
|
3182
|
-
};
|
|
3183
|
-
}
|
|
3184
|
-
registerWriteAck(jsonRpcId) {
|
|
3185
|
-
return new Promise((resolve2) => {
|
|
3186
|
-
this.writeAcks.set(jsonRpcId, { resolve: resolve2 });
|
|
3187
|
-
});
|
|
3188
|
-
}
|
|
3189
|
-
settleNativeWrite(requestId, status) {
|
|
3190
|
-
const ack = this.writeAcks.get(requestId);
|
|
3191
|
-
if (!ack) return;
|
|
3192
|
-
this.writeAcks.delete(requestId);
|
|
3193
|
-
ack.resolve(status);
|
|
3194
|
-
}
|
|
3195
|
-
failPendingNativeWrites(reason) {
|
|
3196
|
-
for (const [, ack] of this.writeAcks) ack.resolve("closed");
|
|
3197
|
-
this.writeAcks.clear();
|
|
3198
|
-
void reason;
|
|
3199
|
-
}
|
|
3200
|
-
async confirmAfterNativeWrite(jsonRpcId) {
|
|
3201
|
-
const existing = this.writeAcks.get(jsonRpcId);
|
|
3202
|
-
if (existing) {
|
|
3203
|
-
return await new Promise((resolve2) => {
|
|
3204
|
-
const previous = existing.resolve;
|
|
3205
|
-
existing.resolve = (status) => {
|
|
3206
|
-
previous(status);
|
|
3207
|
-
resolve2(status);
|
|
3208
|
-
};
|
|
3209
|
-
});
|
|
3210
|
-
}
|
|
3211
|
-
return await this.registerWriteAck(jsonRpcId);
|
|
3212
|
-
}
|
|
3213
|
-
handleStderrLine(line) {
|
|
3214
|
-
const kind = classifyStderrLine(line);
|
|
3215
|
-
if (kind === "drop") return;
|
|
3216
|
-
if (isFatalStderr(line)) {
|
|
3217
|
-
this.emit({
|
|
3218
|
-
eventId: nextEventId(),
|
|
3219
|
-
provider: CODEX_DRIVER_KIND,
|
|
3220
|
-
threadId: this.threadId,
|
|
3221
|
-
createdAt: nowIso(),
|
|
3222
|
-
type: "runtime.error",
|
|
3223
|
-
payload: { message: line }
|
|
3224
|
-
});
|
|
3225
|
-
return;
|
|
3226
|
-
}
|
|
3227
|
-
if (kind === "error") {
|
|
3228
|
-
this.host.log.error(line, { threadId: this.threadId });
|
|
3229
|
-
return;
|
|
3230
|
-
}
|
|
3231
|
-
this.host.log.warn(line, { threadId: this.threadId });
|
|
3232
|
-
}
|
|
3233
|
-
handleNotification(method, params) {
|
|
3234
|
-
if (this.closed) return;
|
|
3235
|
-
const conversationId = conversationIdFromNotification(params);
|
|
3236
|
-
if (conversationId && conversationId !== this.nativeThreadId && conversationId !== this.threadId) {
|
|
3237
|
-
this.interceptCollab(method, params, conversationId);
|
|
3238
|
-
return;
|
|
3239
|
-
}
|
|
3240
|
-
if (conversationId && this.memoryThreadIds.has(conversationId)) {
|
|
3241
|
-
if (method === "serverRequest" || method === "serverRequest/resolved") {
|
|
3242
|
-
this.dispatchMapped(method, params);
|
|
3243
|
-
}
|
|
3244
|
-
return;
|
|
3245
|
-
}
|
|
3246
|
-
if (method === "thread/started") {
|
|
3247
|
-
const name = isRecord3(params) ? asString(params["name"]) ?? (isRecord3(params["thread"]) ? asString(params["thread"]["name"]) : null) : null;
|
|
3248
|
-
if (isMemoryConsolidationThread(name ?? void 0) && conversationId) {
|
|
3249
|
-
this.memoryThreadIds.add(conversationId);
|
|
3250
|
-
return;
|
|
3251
|
-
}
|
|
3252
|
-
}
|
|
3253
|
-
const activity = readSubAgentActivity(method, params);
|
|
3254
|
-
if (activity?.agentThreadId !== void 0 && !isRootChild(activity.agentPath, activity.agentThreadId, this.nativeThreadId ?? this.threadId)) {
|
|
3255
|
-
this.upsertCollabChild(activity.agentThreadId, { agentPath: activity.agentPath });
|
|
3256
|
-
}
|
|
3257
|
-
this.dispatchMapped(method, params);
|
|
3258
|
-
}
|
|
3259
|
-
dispatchMapped(method, params) {
|
|
3260
|
-
if (method === "serverRequest/resolved") {
|
|
3261
|
-
const rawId = isRecord3(params) ? params["requestId"] : void 0;
|
|
3262
|
-
if (typeof rawId !== "number" && typeof rawId !== "string") return;
|
|
3263
|
-
const resolvedId = this.requestIdFor(rawId);
|
|
3264
|
-
const pending = this.pendingApprovals.get(resolvedId);
|
|
3265
|
-
if (pending === void 0) return;
|
|
3266
|
-
pending.release();
|
|
3267
|
-
this.emit({
|
|
3268
|
-
eventId: nextEventId(),
|
|
3269
|
-
provider: CODEX_DRIVER_KIND,
|
|
3270
|
-
threadId: this.threadId,
|
|
3271
|
-
createdAt: nowIso(),
|
|
3272
|
-
type: "request.resolved",
|
|
3273
|
-
requestId: resolvedId,
|
|
3274
|
-
payload: {
|
|
3275
|
-
requestType: toRequestTypeFromKind(this.requestKinds.get(resolvedId)),
|
|
3276
|
-
resolution: params
|
|
3277
|
-
}
|
|
3278
|
-
});
|
|
3279
|
-
return;
|
|
3280
|
-
}
|
|
3281
|
-
trackOpenCommand(this.openCommands, method, params);
|
|
3282
|
-
if (method === "account/rateLimits/updated" && isRecord3(params)) {
|
|
3283
|
-
this.rateLimits = mergeCodexRateLimits(this.rateLimits, params["rateLimits"]);
|
|
3284
|
-
}
|
|
3285
|
-
for (const event of mapNotificationToEvents({
|
|
3286
|
-
method,
|
|
3287
|
-
params,
|
|
3288
|
-
threadId: this.threadId,
|
|
3289
|
-
providerInstanceId: this.host.instanceId,
|
|
3290
|
-
rateLimits: this.rateLimits
|
|
3291
|
-
})) {
|
|
3292
|
-
this.emit(event);
|
|
3293
|
-
}
|
|
3294
|
-
if (method === "turn/started") {
|
|
3295
|
-
const turnId = turnIdOf(params);
|
|
3296
|
-
if (turnId) this.activeTurnId = turnId;
|
|
3297
|
-
}
|
|
3298
|
-
if (method === "turn/completed") {
|
|
3299
|
-
this.activeTurnId = void 0;
|
|
3300
|
-
const { activeTurnId: _completedTurn, ...idleSession } = this.session;
|
|
3301
|
-
this.session = { ...idleSession, status: "idle", updatedAt: nowIso() };
|
|
3006
|
+
}
|
|
3007
|
+
function codexCommandMatcher(display) {
|
|
3008
|
+
const expected = splitPosixCommand(display);
|
|
3009
|
+
if (expected === null || expected.length === 0) return void 0;
|
|
3010
|
+
return (argv, platform2) => {
|
|
3011
|
+
if (platform2 !== "win32") return posixCommandMatches(expected, argv);
|
|
3012
|
+
if (argv.length !== expected.length) return false;
|
|
3013
|
+
const [actualExe, ...actualRest] = argv;
|
|
3014
|
+
const [expectedExe, ...expectedRest] = expected;
|
|
3015
|
+
if (actualExe === void 0 || expectedExe === void 0) return false;
|
|
3016
|
+
if (windowsPath(actualExe) !== windowsPath(expectedExe)) return false;
|
|
3017
|
+
const lastIndex = expectedRest.length - 1;
|
|
3018
|
+
return expectedRest.every((arg, index) => {
|
|
3019
|
+
const actual = actualRest[index];
|
|
3020
|
+
if (actual === arg) return true;
|
|
3021
|
+
return index === lastIndex && index >= 1 && expectedRest[index - 1]?.toLowerCase() === "-command" && isPowerShellExecutableName(basename(expectedExe)) && actual === WINDOWS_POWERSHELL_PREFIX + arg;
|
|
3022
|
+
});
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
function trackOpenCommand(open, method, params) {
|
|
3026
|
+
if (!isRecord3(params)) return;
|
|
3027
|
+
const turnId = turnIdOf(params);
|
|
3028
|
+
if (method === "turn/completed") {
|
|
3029
|
+
for (const [id2, entry] of open) {
|
|
3030
|
+
if (turnId === void 0 || entry.turnId === turnId) open.delete(id2);
|
|
3302
3031
|
}
|
|
3032
|
+
return;
|
|
3033
|
+
}
|
|
3034
|
+
const item = params["item"];
|
|
3035
|
+
if (!isRecord3(item) || item["type"] !== "commandExecution") return;
|
|
3036
|
+
const id = item["id"];
|
|
3037
|
+
if (typeof id !== "string") return;
|
|
3038
|
+
if (method === "item/completed") {
|
|
3039
|
+
open.delete(id);
|
|
3040
|
+
return;
|
|
3041
|
+
}
|
|
3042
|
+
if (method !== "item/started") return;
|
|
3043
|
+
const startedAtMs = params["startedAtMs"];
|
|
3044
|
+
const command = item["command"];
|
|
3045
|
+
if (typeof startedAtMs !== "number" || typeof command !== "string") return;
|
|
3046
|
+
open.set(id, { turnId, startedAtMs, command });
|
|
3047
|
+
}
|
|
3048
|
+
function codexSessionProcessMatcher(appServerArgs) {
|
|
3049
|
+
return (argv) => argv.length === appServerArgs.length + 1 && sameArgv(argv.slice(1), appServerArgs);
|
|
3050
|
+
}
|
|
3051
|
+
function openCommandsOf(booked) {
|
|
3052
|
+
if (booked === void 0) return void 0;
|
|
3053
|
+
const activeTurn = booked.runtime.activeTurnId;
|
|
3054
|
+
const commands = [...booked.runtime.openCommands].flatMap(([toolCallId, entry]) => {
|
|
3055
|
+
if (activeTurn !== void 0 && entry.turnId !== activeTurn) return [];
|
|
3056
|
+
const matches = codexCommandMatcher(entry.command);
|
|
3057
|
+
return matches === void 0 ? [] : [{ toolCallId, startedAtMs: entry.startedAtMs, matches }];
|
|
3058
|
+
});
|
|
3059
|
+
if (commands.length === 0) return void 0;
|
|
3060
|
+
return {
|
|
3061
|
+
rootPid: booked.handle.pid,
|
|
3062
|
+
sessionProcess: codexSessionProcessMatcher(booked.runtime.appServerArgs),
|
|
3063
|
+
commands
|
|
3064
|
+
};
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
// src/sessionCollab.ts
|
|
3068
|
+
var COLLAB_META_LOOKUP_TIMEOUT_MS = 5e3;
|
|
3069
|
+
function metaValue(value) {
|
|
3070
|
+
const text = asString(value)?.trim();
|
|
3071
|
+
return text === void 0 || text.length === 0 ? void 0 : text;
|
|
3072
|
+
}
|
|
3073
|
+
function isRootChild(agentPath, agentThreadId, rootThreadId) {
|
|
3074
|
+
if (agentThreadId === rootThreadId) return true;
|
|
3075
|
+
return agentPath === "/root" || agentPath === "/";
|
|
3076
|
+
}
|
|
3077
|
+
function readSubAgentActivity(method, params) {
|
|
3078
|
+
if (method !== "item/started" && method !== "item/completed") return void 0;
|
|
3079
|
+
if (itemTypeOf(params) !== "subAgentActivity") return void 0;
|
|
3080
|
+
const item = isRecord3(params) ? isRecord3(params["item"]) ? params["item"] : params : {};
|
|
3081
|
+
return {
|
|
3082
|
+
agentThreadId: asString(item["agentThreadId"]) ?? void 0,
|
|
3083
|
+
agentPath: asString(item["agentPath"]) ?? void 0
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
var CodexSessionCollab = class {
|
|
3087
|
+
runtime;
|
|
3088
|
+
constructor(runtime) {
|
|
3089
|
+
this.runtime = runtime;
|
|
3303
3090
|
}
|
|
3304
3091
|
interceptCollab(method, params, conversationId) {
|
|
3305
3092
|
const thread = isRecord3(params) && isRecord3(params["thread"]) ? params["thread"] : void 0;
|
|
@@ -3308,8 +3095,9 @@ var CodexSessionRuntime = class {
|
|
|
3308
3095
|
const spawn = isRecord3(subAgent) ? subAgent["thread_spawn"] : void 0;
|
|
3309
3096
|
if (method === "thread/started" && thread !== void 0 && isRecord3(spawn)) {
|
|
3310
3097
|
const agentPath = asString(spawn["agent_path"]) ?? void 0;
|
|
3311
|
-
const knownPath = agentPath ?? this.collabChildren.get(conversationId)?.agentPath;
|
|
3312
|
-
if (isRootChild(knownPath, conversationId, this.nativeThreadId ?? this.threadId))
|
|
3098
|
+
const knownPath = agentPath ?? this.runtime.collabChildren.get(conversationId)?.agentPath;
|
|
3099
|
+
if (isRootChild(knownPath, conversationId, this.runtime.nativeThreadId ?? this.runtime.threadId))
|
|
3100
|
+
return;
|
|
3313
3101
|
this.upsertCollabChild(conversationId, {
|
|
3314
3102
|
agentPath,
|
|
3315
3103
|
role: asString(spawn["agent_role"]) ?? asString(thread["agentRole"]) ?? void 0,
|
|
@@ -3321,13 +3109,18 @@ var CodexSessionRuntime = class {
|
|
|
3321
3109
|
if ((method === "item/started" || method === "item/completed") && itemType === "collabAgentToolCall") {
|
|
3322
3110
|
const item = isRecord3(params) ? isRecord3(params["item"]) ? params["item"] : params : {};
|
|
3323
3111
|
const receiver = asString(item["receiverThreadId"]) ?? asString(item["agentThreadId"]);
|
|
3324
|
-
if (receiver && this.
|
|
3112
|
+
if (receiver && this.runtime.activeTurnId)
|
|
3113
|
+
this.runtime.collabReceiverTurns.set(receiver, this.runtime.activeTurnId);
|
|
3325
3114
|
return;
|
|
3326
3115
|
}
|
|
3327
3116
|
const activity = readSubAgentActivity(method, params);
|
|
3328
3117
|
if (activity !== void 0) {
|
|
3329
3118
|
const agentThreadId = activity.agentThreadId ?? conversationId;
|
|
3330
|
-
if (isRootChild(
|
|
3119
|
+
if (isRootChild(
|
|
3120
|
+
activity.agentPath,
|
|
3121
|
+
agentThreadId,
|
|
3122
|
+
this.runtime.nativeThreadId ?? this.runtime.threadId
|
|
3123
|
+
)) {
|
|
3331
3124
|
return;
|
|
3332
3125
|
}
|
|
3333
3126
|
this.upsertCollabChild(agentThreadId, { agentPath: activity.agentPath });
|
|
@@ -3342,16 +3135,16 @@ var CodexSessionRuntime = class {
|
|
|
3342
3135
|
this.updateCollabChildMeta(conversationId, { model, effort }, true);
|
|
3343
3136
|
return;
|
|
3344
3137
|
}
|
|
3345
|
-
const child = this.collabChildren.get(conversationId);
|
|
3138
|
+
const child = this.runtime.collabChildren.get(conversationId);
|
|
3346
3139
|
if (method === "turn/started") {
|
|
3347
3140
|
this.reopenCollabChildMeta(conversationId);
|
|
3348
3141
|
const turnId = turnIdOf(params);
|
|
3349
|
-
if (turnId) this.liveForeignTurns.set(conversationId, turnId);
|
|
3142
|
+
if (turnId) this.runtime.liveForeignTurns.set(conversationId, turnId);
|
|
3350
3143
|
}
|
|
3351
3144
|
if (method === "turn/completed" || method === "thread/closed") {
|
|
3352
|
-
this.liveForeignTurns.delete(conversationId);
|
|
3145
|
+
this.runtime.liveForeignTurns.delete(conversationId);
|
|
3353
3146
|
}
|
|
3354
|
-
if (method === "thread/closed" && this.collabChildren.has(conversationId)) {
|
|
3147
|
+
if (method === "thread/closed" && this.runtime.collabChildren.has(conversationId)) {
|
|
3355
3148
|
this.metaOf(conversationId).closed = true;
|
|
3356
3149
|
}
|
|
3357
3150
|
if (!child) {
|
|
@@ -3374,285 +3167,568 @@ var CodexSessionRuntime = class {
|
|
|
3374
3167
|
);
|
|
3375
3168
|
return;
|
|
3376
3169
|
}
|
|
3377
|
-
if (method === "error") {
|
|
3378
|
-
const willRetry = isRecord3(params) ? params["willRetry"] === true : false;
|
|
3379
|
-
if (willRetry) return;
|
|
3380
|
-
child.status = "failed";
|
|
3381
|
-
this.emitCollab("collabAgent/statusChanged", child, { status: "systemError" });
|
|
3170
|
+
if (method === "error") {
|
|
3171
|
+
const willRetry = isRecord3(params) ? params["willRetry"] === true : false;
|
|
3172
|
+
if (willRetry) return;
|
|
3173
|
+
child.status = "failed";
|
|
3174
|
+
this.emitCollab("collabAgent/statusChanged", child, { status: "systemError" });
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
if (method === "thread/tokenUsage/updated") {
|
|
3178
|
+
this.emitCollab("collabAgent/tokenUsage", child, params);
|
|
3179
|
+
return;
|
|
3180
|
+
}
|
|
3181
|
+
if (CHILD_CHATTER_METHODS.has(method)) return;
|
|
3182
|
+
if (CHILD_AGENT_EVENT_METHODS.has(method)) {
|
|
3183
|
+
this.emitCollab("collabAgent/progress", child, params);
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
/**
|
|
3187
|
+
* 两条登记路(根线程的 subAgentActivity、子线程的 thread/started)都走这里。
|
|
3188
|
+
* 第一次见到就登记并宣布;已登记的只合并身份:已有的字段保留,缺的补上,补到了就发
|
|
3189
|
+
* 一条带当前状态的元数据更新。不能再宣布一次:一轮结束之后才到的宣布,会让宿主把
|
|
3190
|
+
* idle 当成新一轮开始。
|
|
3191
|
+
*/
|
|
3192
|
+
upsertCollabChild(agentThreadId, identity) {
|
|
3193
|
+
const fill = (known, next) => known ?? (next !== void 0 && next.length > 0 ? next : void 0);
|
|
3194
|
+
const existing = this.runtime.collabChildren.get(agentThreadId);
|
|
3195
|
+
const agentPath = fill(existing?.agentPath, identity.agentPath);
|
|
3196
|
+
const role = fill(existing?.role, identity.role);
|
|
3197
|
+
const nickname = fill(existing?.nickname, identity.nickname);
|
|
3198
|
+
if (existing === void 0) {
|
|
3199
|
+
const spawnTurnId = this.runtime.activeTurnId;
|
|
3200
|
+
const child = {
|
|
3201
|
+
agentThreadId,
|
|
3202
|
+
...agentPath ? { agentPath } : {},
|
|
3203
|
+
...role ? { role } : {},
|
|
3204
|
+
...nickname ? { nickname } : {},
|
|
3205
|
+
...spawnTurnId ? { spawnTurnId } : {},
|
|
3206
|
+
status: "running"
|
|
3207
|
+
};
|
|
3208
|
+
this.runtime.collabChildren.set(agentThreadId, child);
|
|
3209
|
+
this.emitCollab("collabAgent/started", child);
|
|
3210
|
+
this.startCollabChildMetaLookup(agentThreadId);
|
|
3211
|
+
return;
|
|
3212
|
+
}
|
|
3213
|
+
if (agentPath === existing.agentPath && role === existing.role && nickname === existing.nickname) {
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
const merged = {
|
|
3217
|
+
...existing,
|
|
3218
|
+
...agentPath ? { agentPath } : {},
|
|
3219
|
+
...role ? { role } : {},
|
|
3220
|
+
...nickname ? { nickname } : {}
|
|
3221
|
+
};
|
|
3222
|
+
this.runtime.collabChildren.set(agentThreadId, merged);
|
|
3223
|
+
this.emitCollab("collabAgent/metadata", merged, { status: merged.status });
|
|
3224
|
+
}
|
|
3225
|
+
metaOf(agentThreadId) {
|
|
3226
|
+
let meta = this.runtime.collabChildMeta.get(agentThreadId);
|
|
3227
|
+
if (meta === void 0) {
|
|
3228
|
+
meta = { lookupStarted: false, closed: false };
|
|
3229
|
+
this.runtime.collabChildMeta.set(agentThreadId, meta);
|
|
3230
|
+
}
|
|
3231
|
+
return meta;
|
|
3232
|
+
}
|
|
3233
|
+
reopenCollabChildMeta(agentThreadId) {
|
|
3234
|
+
const meta = this.runtime.collabChildMeta.get(agentThreadId);
|
|
3235
|
+
if (meta !== void 0) meta.closed = false;
|
|
3236
|
+
}
|
|
3237
|
+
/**
|
|
3238
|
+
* 合并子线程的型号与推理档;变了且子代理已登记就补发一条不带状态的更新。
|
|
3239
|
+
* 通知报的是当前值(overwrite),回读只补缺:回读的回包可能比通知晚到,不能盖掉更新的值。
|
|
3240
|
+
*/
|
|
3241
|
+
updateCollabChildMeta(agentThreadId, update, overwrite) {
|
|
3242
|
+
const meta = this.metaOf(agentThreadId);
|
|
3243
|
+
const model = update.model !== void 0 && (overwrite || !meta.model) ? update.model : meta.model;
|
|
3244
|
+
const effort = update.effort !== void 0 && (overwrite || !meta.effort) ? update.effort : meta.effort;
|
|
3245
|
+
if (model === meta.model && effort === meta.effort) return;
|
|
3246
|
+
if (model !== void 0) meta.model = model;
|
|
3247
|
+
if (effort !== void 0) meta.effort = effort;
|
|
3248
|
+
const child = this.runtime.collabChildren.get(agentThreadId);
|
|
3249
|
+
if (child !== void 0 && !meta.closed) this.emitCollab("collabAgent/metadataUpdated", child);
|
|
3250
|
+
}
|
|
3251
|
+
/**
|
|
3252
|
+
* 子线程已经在 CLI 里加载着:thread/resume 只是重新接上它,不开新一轮;
|
|
3253
|
+
* excludeTurns 避免把它的历史再读一遍。回包带当前的型号与推理档。
|
|
3254
|
+
*/
|
|
3255
|
+
startCollabChildMetaLookup(agentThreadId) {
|
|
3256
|
+
const meta = this.metaOf(agentThreadId);
|
|
3257
|
+
if (meta.lookupStarted || meta.closed) return;
|
|
3258
|
+
meta.lookupStarted = true;
|
|
3259
|
+
void this.runtime.rpc.request(
|
|
3260
|
+
"thread/resume",
|
|
3261
|
+
{ threadId: agentThreadId, excludeTurns: true },
|
|
3262
|
+
COLLAB_META_LOOKUP_TIMEOUT_MS
|
|
3263
|
+
).then((response) => {
|
|
3264
|
+
if (this.runtime.closed || meta.closed || !isRecord3(response)) return;
|
|
3265
|
+
const thread = response["thread"];
|
|
3266
|
+
if (!isRecord3(thread) || thread["id"] !== agentThreadId) return;
|
|
3267
|
+
this.updateCollabChildMeta(
|
|
3268
|
+
agentThreadId,
|
|
3269
|
+
{ model: metaValue(response["model"]), effort: metaValue(response["reasoningEffort"]) },
|
|
3270
|
+
false
|
|
3271
|
+
);
|
|
3272
|
+
}).catch(() => void 0);
|
|
3273
|
+
}
|
|
3274
|
+
emitCollab(method, child, extra) {
|
|
3275
|
+
const meta = this.runtime.collabChildMeta.get(child.agentThreadId);
|
|
3276
|
+
const params = {
|
|
3277
|
+
agentThreadId: child.agentThreadId,
|
|
3278
|
+
agentPath: child.agentPath,
|
|
3279
|
+
agentRole: child.role ?? pathLeaf(child.agentPath) ?? "general-purpose",
|
|
3280
|
+
agentNickname: child.nickname,
|
|
3281
|
+
...meta?.model ? { model: meta.model } : {},
|
|
3282
|
+
...meta?.effort ? { effort: meta.effort } : {},
|
|
3283
|
+
...isRecord3(extra) ? extra : {}
|
|
3284
|
+
};
|
|
3285
|
+
for (const event of mapNotificationToEvents({
|
|
3286
|
+
method,
|
|
3287
|
+
params,
|
|
3288
|
+
threadId: this.runtime.threadId,
|
|
3289
|
+
providerInstanceId: this.runtime.host.instanceId
|
|
3290
|
+
})) {
|
|
3291
|
+
this.runtime.emit(event);
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
};
|
|
3295
|
+
|
|
3296
|
+
// src/sessionRequests.ts
|
|
3297
|
+
function cancelWireFor(pending) {
|
|
3298
|
+
if (pending.kind === "permission") return toPermissionsResponse("cancel", pending.params, false);
|
|
3299
|
+
return pending.kind === "mcp-elicitation" ? toMcpElicitationResponse("cancel", pending.params) : { decision: "cancel" };
|
|
3300
|
+
}
|
|
3301
|
+
function unconfirmedAnswerError(requestId, delivery) {
|
|
3302
|
+
return new Error(
|
|
3303
|
+
delivery === "closed" ? `could not confirm that codex received the answer to request ${requestId}: the session closed before the write was confirmed` : `could not confirm that codex received the answer to request ${requestId}: writing it to the CLI failed`
|
|
3304
|
+
);
|
|
3305
|
+
}
|
|
3306
|
+
var CodexSessionRequests = class {
|
|
3307
|
+
runtime;
|
|
3308
|
+
constructor(runtime) {
|
|
3309
|
+
this.runtime = runtime;
|
|
3310
|
+
}
|
|
3311
|
+
/** 给 CLI 的应答写入失败:带上线程与方法名记一行 warn。 */
|
|
3312
|
+
#warnAnswerWriteFailed(method, jsonRpcId, error) {
|
|
3313
|
+
this.runtime.host.log.warn("writing an answer to codex failed", {
|
|
3314
|
+
threadId: this.runtime.threadId,
|
|
3315
|
+
method,
|
|
3316
|
+
jsonRpcId,
|
|
3317
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
async handleServerRequest(id, method, params) {
|
|
3321
|
+
if (method === "item/commandExecution/requestApproval") {
|
|
3322
|
+
await this.handleApproval(
|
|
3323
|
+
id,
|
|
3324
|
+
method,
|
|
3325
|
+
params,
|
|
3326
|
+
"command",
|
|
3327
|
+
describeCommandApproval(params).command
|
|
3328
|
+
);
|
|
3329
|
+
return;
|
|
3330
|
+
}
|
|
3331
|
+
if (method === "item/fileChange/requestApproval") {
|
|
3332
|
+
const described = describeFileChangeApproval(params);
|
|
3333
|
+
await this.handleApproval(
|
|
3334
|
+
id,
|
|
3335
|
+
method,
|
|
3336
|
+
params,
|
|
3337
|
+
"file-change",
|
|
3338
|
+
described.callId ?? "file-change"
|
|
3339
|
+
);
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
if (method === "mcpServer/elicitation/request") {
|
|
3343
|
+
const described = describeMcpElicitation(params);
|
|
3344
|
+
await this.handleApproval(id, method, params, "mcp-elicitation", described.identity);
|
|
3345
|
+
return;
|
|
3346
|
+
}
|
|
3347
|
+
if (method === "item/permissions/requestApproval") {
|
|
3348
|
+
await this.handleApproval(
|
|
3349
|
+
id,
|
|
3350
|
+
method,
|
|
3351
|
+
params,
|
|
3352
|
+
"permission",
|
|
3353
|
+
isRecord3(params) ? asString(params["itemId"]) ?? "permission" : "permission"
|
|
3354
|
+
);
|
|
3355
|
+
return;
|
|
3356
|
+
}
|
|
3357
|
+
if (method === "item/tool/requestUserInput") {
|
|
3358
|
+
const requestId = this.runtime.requestIdFor(id);
|
|
3359
|
+
const events = mapServerRequestToEvents({
|
|
3360
|
+
requestId,
|
|
3361
|
+
method,
|
|
3362
|
+
params,
|
|
3363
|
+
threadId: this.runtime.threadId,
|
|
3364
|
+
providerInstanceId: this.runtime.host.instanceId
|
|
3365
|
+
});
|
|
3366
|
+
if (events.length === 0) {
|
|
3367
|
+
await this.runtime.rpc.respond(id, {});
|
|
3368
|
+
return;
|
|
3369
|
+
}
|
|
3370
|
+
await new Promise((resolve2) => {
|
|
3371
|
+
this.runtime.pendingUserInputs.set(requestId, {
|
|
3372
|
+
jsonRpcId: id,
|
|
3373
|
+
settle: (result) => {
|
|
3374
|
+
this.runtime.pendingUserInputs.delete(requestId);
|
|
3375
|
+
void this.runtime.rpc.respond(id, result).catch((error) => this.#warnAnswerWriteFailed(method, id, error)).finally(resolve2);
|
|
3376
|
+
}
|
|
3377
|
+
});
|
|
3378
|
+
for (const event of events) this.runtime.emit(event);
|
|
3379
|
+
});
|
|
3380
|
+
return;
|
|
3381
|
+
}
|
|
3382
|
+
await this.runtime.rpc.respondError(id, -32601, `Method not found: ${method}`);
|
|
3383
|
+
}
|
|
3384
|
+
async handleApproval(id, method, params, kind, identity) {
|
|
3385
|
+
const grant = kind === "permission" ? void 0 : this.runtime.grants.lookup(codexApprovalGrantKey(kind, identity));
|
|
3386
|
+
if (grant) {
|
|
3387
|
+
const decision = wireDecisionFromPersist(kind, grant);
|
|
3388
|
+
await this.runtime.rpc.respond(id, { decision });
|
|
3382
3389
|
return;
|
|
3383
3390
|
}
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3391
|
+
const requestId = this.runtime.requestIdFor(id);
|
|
3392
|
+
const events = mapServerRequestToEvents({
|
|
3393
|
+
requestId,
|
|
3394
|
+
method,
|
|
3395
|
+
params,
|
|
3396
|
+
threadId: this.runtime.threadId,
|
|
3397
|
+
providerInstanceId: this.runtime.host.instanceId
|
|
3398
|
+
});
|
|
3399
|
+
this.runtime.requestKinds.set(requestId, kind);
|
|
3400
|
+
await new Promise((resolve2) => {
|
|
3401
|
+
this.runtime.pendingApprovals.set(requestId, {
|
|
3402
|
+
jsonRpcId: id,
|
|
3403
|
+
method,
|
|
3404
|
+
params,
|
|
3405
|
+
kind,
|
|
3406
|
+
identity,
|
|
3407
|
+
settle: (decision) => {
|
|
3408
|
+
this.runtime.pendingApprovals.delete(requestId);
|
|
3409
|
+
void this.runtime.rpc.respond(id, decision).catch((error) => this.#warnAnswerWriteFailed(method, id, error)).finally(resolve2);
|
|
3410
|
+
},
|
|
3411
|
+
release: () => {
|
|
3412
|
+
this.runtime.pendingApprovals.delete(requestId);
|
|
3413
|
+
resolve2();
|
|
3414
|
+
}
|
|
3415
|
+
});
|
|
3416
|
+
for (const event of events) this.runtime.emit(event);
|
|
3417
|
+
});
|
|
3418
|
+
}
|
|
3419
|
+
async respondToRequest(requestId, decision, reason, persist) {
|
|
3420
|
+
const pending = this.runtime.pendingApprovals.get(requestId);
|
|
3421
|
+
if (!pending) throw new Error(`Unknown pending Codex approval request: ${requestId}`);
|
|
3422
|
+
if (decision === "deny" && reason)
|
|
3423
|
+
this.runtime.declineRelay.queue(this.runtime.threadId, reason);
|
|
3424
|
+
let wire = { decision: decision === "allow" ? "accept" : "decline" };
|
|
3425
|
+
if (decision === "allow" && pending.kind === "mcp-elicitation") {
|
|
3426
|
+
wire = toMcpElicitationResponse("allow", pending.params);
|
|
3387
3427
|
}
|
|
3388
|
-
if (
|
|
3389
|
-
|
|
3390
|
-
|
|
3428
|
+
if (pending.kind === "permission") {
|
|
3429
|
+
wire = toPermissionsResponse(decision, pending.params, persist !== void 0);
|
|
3430
|
+
} else if (decision === "allow" && persist) {
|
|
3431
|
+
this.runtime.grants.remember(codexApprovalGrantKey(pending.kind, pending.identity), persist);
|
|
3432
|
+
if (pending.kind !== "mcp-elicitation") {
|
|
3433
|
+
wire = { decision: wireDecisionFromPersist(pending.kind, persist) };
|
|
3434
|
+
}
|
|
3391
3435
|
}
|
|
3436
|
+
const ack = this.runtime.registerWriteAck(pending.jsonRpcId);
|
|
3437
|
+
pending.settle(wire);
|
|
3438
|
+
const delivery = await ack.catch(() => "closed");
|
|
3439
|
+
this.runtime.emit({
|
|
3440
|
+
eventId: nextEventId(),
|
|
3441
|
+
provider: CODEX_DRIVER_KIND,
|
|
3442
|
+
threadId: this.runtime.threadId,
|
|
3443
|
+
createdAt: nowIso(),
|
|
3444
|
+
type: "request.resolved",
|
|
3445
|
+
requestId,
|
|
3446
|
+
// requestType 是契约必填;缺了它宿主整条解不动,卡片停在「等待决议」。
|
|
3447
|
+
payload: {
|
|
3448
|
+
requestType: toRequestTypeFromKind(pending.kind),
|
|
3449
|
+
decision,
|
|
3450
|
+
...reason ? { reason } : {}
|
|
3451
|
+
}
|
|
3452
|
+
});
|
|
3453
|
+
if (delivery !== "written") throw unconfirmedAnswerError(requestId, delivery);
|
|
3392
3454
|
}
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
upsertCollabChild(agentThreadId, identity) {
|
|
3400
|
-
const fill = (known, next) => known ?? (next !== void 0 && next.length > 0 ? next : void 0);
|
|
3401
|
-
const existing = this.collabChildren.get(agentThreadId);
|
|
3402
|
-
const agentPath = fill(existing?.agentPath, identity.agentPath);
|
|
3403
|
-
const role = fill(existing?.role, identity.role);
|
|
3404
|
-
const nickname = fill(existing?.nickname, identity.nickname);
|
|
3405
|
-
if (existing === void 0) {
|
|
3406
|
-
const spawnTurnId = this.activeTurnId;
|
|
3407
|
-
const child = {
|
|
3408
|
-
agentThreadId,
|
|
3409
|
-
...agentPath ? { agentPath } : {},
|
|
3410
|
-
...role ? { role } : {},
|
|
3411
|
-
...nickname ? { nickname } : {},
|
|
3412
|
-
...spawnTurnId ? { spawnTurnId } : {},
|
|
3413
|
-
status: "running"
|
|
3414
|
-
};
|
|
3415
|
-
this.collabChildren.set(agentThreadId, child);
|
|
3416
|
-
this.emitCollab("collabAgent/started", child);
|
|
3417
|
-
this.startCollabChildMetaLookup(agentThreadId);
|
|
3418
|
-
return;
|
|
3455
|
+
async respondToUserInput(requestId, answers) {
|
|
3456
|
+
const pending = this.runtime.pendingUserInputs.get(requestId);
|
|
3457
|
+
if (!pending) throw new Error(`Unknown pending Codex user input request: ${requestId}`);
|
|
3458
|
+
const mapped = toCodexUserInputAnswers(answers);
|
|
3459
|
+
if (mapped === void 0) {
|
|
3460
|
+
throw new Error(`invalid user input answers for request ${requestId}`);
|
|
3419
3461
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3462
|
+
const ack = this.runtime.registerWriteAck(pending.jsonRpcId);
|
|
3463
|
+
pending.settle({ answers: mapped });
|
|
3464
|
+
const delivery = await ack.catch(() => "closed");
|
|
3465
|
+
this.runtime.emit({
|
|
3466
|
+
eventId: nextEventId(),
|
|
3467
|
+
provider: CODEX_DRIVER_KIND,
|
|
3468
|
+
threadId: this.runtime.threadId,
|
|
3469
|
+
createdAt: nowIso(),
|
|
3470
|
+
type: "user-input.resolved",
|
|
3471
|
+
requestId,
|
|
3472
|
+
// 契约的 answers 是 questionId → 答案 的 record,不是数组。
|
|
3473
|
+
payload: { answers: toCanonicalUserInputAnswers(mapped) }
|
|
3474
|
+
});
|
|
3475
|
+
if (delivery !== "written") throw unconfirmedAnswerError(requestId, delivery);
|
|
3476
|
+
}
|
|
3477
|
+
/** 把还挂着的审批答成「取消」,逐条发 request.resolved。 */
|
|
3478
|
+
cancelPendingApprovals() {
|
|
3479
|
+
for (const [requestId, pending] of this.runtime.pendingApprovals) {
|
|
3480
|
+
pending.settle(cancelWireFor(pending));
|
|
3481
|
+
this.runtime.emit({
|
|
3482
|
+
eventId: nextEventId(),
|
|
3483
|
+
provider: CODEX_DRIVER_KIND,
|
|
3484
|
+
threadId: this.runtime.threadId,
|
|
3485
|
+
createdAt: nowIso(),
|
|
3486
|
+
type: "request.resolved",
|
|
3487
|
+
requestId,
|
|
3488
|
+
payload: { requestType: toRequestTypeFromKind(pending.kind), decision: "cancel" }
|
|
3489
|
+
});
|
|
3422
3490
|
}
|
|
3423
|
-
const merged = {
|
|
3424
|
-
...existing,
|
|
3425
|
-
...agentPath ? { agentPath } : {},
|
|
3426
|
-
...role ? { role } : {},
|
|
3427
|
-
...nickname ? { nickname } : {}
|
|
3428
|
-
};
|
|
3429
|
-
this.collabChildren.set(agentThreadId, merged);
|
|
3430
|
-
this.emitCollab("collabAgent/metadata", merged, { status: merged.status });
|
|
3431
3491
|
}
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
this.
|
|
3492
|
+
/** 会话收口时把还挂着的审批答成「取消」:应答形状按审批种类,权限审批不认 decision。 */
|
|
3493
|
+
settlePendingApprovals() {
|
|
3494
|
+
for (const [requestId, pending] of this.runtime.pendingApprovals) {
|
|
3495
|
+
pending.settle(cancelWireFor(pending));
|
|
3496
|
+
this.runtime.pendingApprovals.delete(requestId);
|
|
3437
3497
|
}
|
|
3438
|
-
return meta;
|
|
3439
3498
|
}
|
|
3440
|
-
|
|
3441
|
-
const
|
|
3442
|
-
|
|
3499
|
+
settlePendingUserInputs(result) {
|
|
3500
|
+
for (const [requestId, pending] of this.runtime.pendingUserInputs) {
|
|
3501
|
+
pending.settle(result);
|
|
3502
|
+
this.runtime.pendingUserInputs.delete(requestId);
|
|
3503
|
+
}
|
|
3443
3504
|
}
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
const
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
if (effort !== void 0) meta.effort = effort;
|
|
3455
|
-
const child = this.collabChildren.get(agentThreadId);
|
|
3456
|
-
if (child !== void 0 && !meta.closed) this.emitCollab("collabAgent/metadataUpdated", child);
|
|
3505
|
+
};
|
|
3506
|
+
async function markProviderRejection(run) {
|
|
3507
|
+
try {
|
|
3508
|
+
return await run();
|
|
3509
|
+
} catch (error) {
|
|
3510
|
+
if (!(error instanceof CodexRpcResponseError)) throw error;
|
|
3511
|
+
const rejected = Object.assign(new Error(error.message), {
|
|
3512
|
+
code: "provider-rejected"
|
|
3513
|
+
});
|
|
3514
|
+
throw rejected;
|
|
3457
3515
|
}
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
// src/sessionRuntime.ts
|
|
3519
|
+
var sleep = (ms) => new Promise((resolve2) => {
|
|
3520
|
+
setTimeout(resolve2, ms);
|
|
3521
|
+
});
|
|
3522
|
+
var CodexSessionRuntime = class {
|
|
3523
|
+
threadId;
|
|
3524
|
+
cwd;
|
|
3525
|
+
session;
|
|
3526
|
+
nativeThreadId;
|
|
3527
|
+
activeTurnId;
|
|
3528
|
+
model;
|
|
3529
|
+
runtimeMode;
|
|
3530
|
+
/** 宿主解好的这一轮的档(座位画的就是它)。缺席 = 宿主没给,按 `runtimeMode` 查表。 */
|
|
3531
|
+
nativeModeId;
|
|
3532
|
+
interactionMode;
|
|
3533
|
+
closed = false;
|
|
3534
|
+
closeCompleted = false;
|
|
3535
|
+
turns = [];
|
|
3536
|
+
pendingApprovals = /* @__PURE__ */ new Map();
|
|
3537
|
+
pendingUserInputs = /* @__PURE__ */ new Map();
|
|
3538
|
+
requestKinds = /* @__PURE__ */ new Map();
|
|
3539
|
+
writeAcks = /* @__PURE__ */ new Map();
|
|
3540
|
+
grants;
|
|
3541
|
+
declineRelay;
|
|
3542
|
+
collabChildren = /* @__PURE__ */ new Map();
|
|
3543
|
+
collabChildMeta = /* @__PURE__ */ new Map();
|
|
3544
|
+
liveForeignTurns = /* @__PURE__ */ new Map();
|
|
3545
|
+
openCommands = /* @__PURE__ */ new Map();
|
|
3546
|
+
collabReceiverTurns = /* @__PURE__ */ new Map();
|
|
3547
|
+
memoryThreadIds = /* @__PURE__ */ new Set();
|
|
3548
|
+
/** 这个会话见过的主额度快照(按字段合并),额度用尽停轮时点名窗口用。 */
|
|
3549
|
+
rateLimits;
|
|
3550
|
+
appServerArgs;
|
|
3551
|
+
hasConfiguredMcpServer;
|
|
3552
|
+
host;
|
|
3553
|
+
attachments;
|
|
3554
|
+
rpc;
|
|
3555
|
+
emit;
|
|
3556
|
+
#collab = new CodexSessionCollab(this);
|
|
3557
|
+
#requests = new CodexSessionRequests(this);
|
|
3458
3558
|
/**
|
|
3459
|
-
*
|
|
3460
|
-
*
|
|
3559
|
+
* 对外 requestId 的前缀,每个会话(即每个 app-server 进程)一份。JSON-RPC id 每个进程
|
|
3560
|
+
* 都从 0 数起,宿主按 requestId 记「已接受」,不加前缀的话重启后的新请求会撞上旧请求。
|
|
3461
3561
|
*/
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
void this.rpc.request(
|
|
3467
|
-
"thread/resume",
|
|
3468
|
-
{ threadId: agentThreadId, excludeTurns: true },
|
|
3469
|
-
COLLAB_META_LOOKUP_TIMEOUT_MS
|
|
3470
|
-
).then((response) => {
|
|
3471
|
-
if (this.closed || meta.closed || !isRecord3(response)) return;
|
|
3472
|
-
const thread = response["thread"];
|
|
3473
|
-
if (!isRecord3(thread) || thread["id"] !== agentThreadId) return;
|
|
3474
|
-
this.updateCollabChildMeta(
|
|
3475
|
-
agentThreadId,
|
|
3476
|
-
{ model: metaValue(response["model"]), effort: metaValue(response["reasoningEffort"]) },
|
|
3477
|
-
false
|
|
3478
|
-
);
|
|
3479
|
-
}).catch(() => void 0);
|
|
3562
|
+
#requestIdPrefix = `codex-${NodeCrypto2.randomUUID()}:`;
|
|
3563
|
+
/** JSON-RPC id → 对外 requestId。`serverRequest/resolved` 里的 id 可能是数字也可能是字符串。 */
|
|
3564
|
+
requestIdFor(jsonRpcId) {
|
|
3565
|
+
return `${this.#requestIdPrefix}${String(jsonRpcId)}`;
|
|
3480
3566
|
}
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3567
|
+
/** 这一轮该用哪套三字段组合:只由这一轮的档决定(见 `codexNativeModeFor`)。 */
|
|
3568
|
+
#threadConfig() {
|
|
3569
|
+
return codexThreadConfigForNativeMode(codexNativeModeFor(this.runtimeMode, this.nativeModeId));
|
|
3570
|
+
}
|
|
3571
|
+
/** 同上,沙箱策略那一位。 */
|
|
3572
|
+
#turnSandboxPolicy() {
|
|
3573
|
+
return codexTurnSandboxPolicyForNativeMode(
|
|
3574
|
+
codexNativeModeFor(this.runtimeMode, this.nativeModeId)
|
|
3575
|
+
);
|
|
3576
|
+
}
|
|
3577
|
+
constructor(host, rpc, emit, input, cwd, appServerArgs, grants, declineRelay, attachments) {
|
|
3578
|
+
this.attachments = attachments;
|
|
3579
|
+
this.host = host;
|
|
3580
|
+
this.rpc = rpc;
|
|
3581
|
+
this.emit = emit;
|
|
3582
|
+
this.threadId = input.threadId;
|
|
3583
|
+
this.cwd = cwd;
|
|
3584
|
+
this.runtimeMode = input.runtimeMode;
|
|
3585
|
+
this.nativeModeId = input.nativeModeId;
|
|
3586
|
+
this.appServerArgs = appServerArgs;
|
|
3587
|
+
this.hasConfiguredMcpServer = (input.mcpServers?.length ?? 0) > 0;
|
|
3588
|
+
this.grants = grants ?? createApprovalGrantStore();
|
|
3589
|
+
this.declineRelay = declineRelay ?? createDeclineReasonRelay();
|
|
3590
|
+
const createdAt = nowIso();
|
|
3591
|
+
this.session = {
|
|
3592
|
+
provider: CODEX_DRIVER_KIND,
|
|
3593
|
+
providerInstanceId: host.instanceId,
|
|
3594
|
+
status: "idle",
|
|
3595
|
+
runtimeMode: input.runtimeMode,
|
|
3596
|
+
nativeModeId: codexNativeModeFor(input.runtimeMode, input.nativeModeId),
|
|
3597
|
+
cwd,
|
|
3598
|
+
threadId: input.threadId,
|
|
3599
|
+
createdAt,
|
|
3600
|
+
updatedAt: createdAt,
|
|
3601
|
+
...input.bindingGeneration === void 0 ? {} : { bindingGeneration: input.bindingGeneration }
|
|
3491
3602
|
};
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3603
|
+
}
|
|
3604
|
+
registerWriteAck(jsonRpcId) {
|
|
3605
|
+
return new Promise((resolve2) => {
|
|
3606
|
+
this.writeAcks.set(jsonRpcId, { resolve: resolve2 });
|
|
3607
|
+
});
|
|
3608
|
+
}
|
|
3609
|
+
settleNativeWrite(requestId, status) {
|
|
3610
|
+
const ack = this.writeAcks.get(requestId);
|
|
3611
|
+
if (!ack) return;
|
|
3612
|
+
this.writeAcks.delete(requestId);
|
|
3613
|
+
ack.resolve(status);
|
|
3614
|
+
}
|
|
3615
|
+
failPendingNativeWrites(reason) {
|
|
3616
|
+
for (const [, ack] of this.writeAcks) ack.resolve("closed");
|
|
3617
|
+
this.writeAcks.clear();
|
|
3618
|
+
void reason;
|
|
3619
|
+
}
|
|
3620
|
+
async confirmAfterNativeWrite(jsonRpcId) {
|
|
3621
|
+
const existing = this.writeAcks.get(jsonRpcId);
|
|
3622
|
+
if (existing) {
|
|
3623
|
+
return await new Promise((resolve2) => {
|
|
3624
|
+
const previous = existing.resolve;
|
|
3625
|
+
existing.resolve = (status) => {
|
|
3626
|
+
previous(status);
|
|
3627
|
+
resolve2(status);
|
|
3628
|
+
};
|
|
3629
|
+
});
|
|
3499
3630
|
}
|
|
3631
|
+
return await this.registerWriteAck(jsonRpcId);
|
|
3500
3632
|
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3633
|
+
handleStderrLine(line) {
|
|
3634
|
+
const kind = classifyStderrLine(line);
|
|
3635
|
+
if (kind === "drop") return;
|
|
3636
|
+
if (isFatalStderr(line)) {
|
|
3637
|
+
this.emit({
|
|
3638
|
+
eventId: nextEventId(),
|
|
3639
|
+
provider: CODEX_DRIVER_KIND,
|
|
3640
|
+
threadId: this.threadId,
|
|
3641
|
+
createdAt: nowIso(),
|
|
3642
|
+
type: "runtime.error",
|
|
3643
|
+
payload: { message: line }
|
|
3644
|
+
});
|
|
3510
3645
|
return;
|
|
3511
3646
|
}
|
|
3512
|
-
if (
|
|
3513
|
-
|
|
3514
|
-
await this.handleApproval(
|
|
3515
|
-
id,
|
|
3516
|
-
method,
|
|
3517
|
-
params,
|
|
3518
|
-
"file-change",
|
|
3519
|
-
described.callId ?? "file-change"
|
|
3520
|
-
);
|
|
3647
|
+
if (kind === "error") {
|
|
3648
|
+
this.host.log.error(line, { threadId: this.threadId });
|
|
3521
3649
|
return;
|
|
3522
3650
|
}
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3651
|
+
this.host.log.warn(line, { threadId: this.threadId });
|
|
3652
|
+
}
|
|
3653
|
+
handleNotification(method, params) {
|
|
3654
|
+
if (this.closed) return;
|
|
3655
|
+
const conversationId = conversationIdFromNotification(params);
|
|
3656
|
+
if (conversationId && conversationId !== this.nativeThreadId && conversationId !== this.threadId) {
|
|
3657
|
+
this.#collab.interceptCollab(method, params, conversationId);
|
|
3526
3658
|
return;
|
|
3527
3659
|
}
|
|
3528
|
-
if (
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
params,
|
|
3533
|
-
"permission",
|
|
3534
|
-
isRecord3(params) ? asString(params["itemId"]) ?? "permission" : "permission"
|
|
3535
|
-
);
|
|
3660
|
+
if (conversationId && this.memoryThreadIds.has(conversationId)) {
|
|
3661
|
+
if (method === "serverRequest" || method === "serverRequest/resolved") {
|
|
3662
|
+
this.dispatchMapped(method, params);
|
|
3663
|
+
}
|
|
3536
3664
|
return;
|
|
3537
3665
|
}
|
|
3538
|
-
if (method === "
|
|
3539
|
-
const
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
method,
|
|
3543
|
-
params,
|
|
3544
|
-
threadId: this.threadId,
|
|
3545
|
-
providerInstanceId: this.host.instanceId
|
|
3546
|
-
});
|
|
3547
|
-
if (events.length === 0) {
|
|
3548
|
-
await this.rpc.respond(id, {});
|
|
3666
|
+
if (method === "thread/started") {
|
|
3667
|
+
const name = isRecord3(params) ? asString(params["name"]) ?? (isRecord3(params["thread"]) ? asString(params["thread"]["name"]) : null) : null;
|
|
3668
|
+
if (isMemoryConsolidationThread(name ?? void 0) && conversationId) {
|
|
3669
|
+
this.memoryThreadIds.add(conversationId);
|
|
3549
3670
|
return;
|
|
3550
3671
|
}
|
|
3551
|
-
await new Promise((resolve2) => {
|
|
3552
|
-
this.pendingUserInputs.set(requestId, {
|
|
3553
|
-
jsonRpcId: id,
|
|
3554
|
-
settle: (result) => {
|
|
3555
|
-
this.pendingUserInputs.delete(requestId);
|
|
3556
|
-
void this.rpc.respond(id, result).catch((error) => this.#warnAnswerWriteFailed(method, id, error)).finally(resolve2);
|
|
3557
|
-
}
|
|
3558
|
-
});
|
|
3559
|
-
for (const event of events) this.emit(event);
|
|
3560
|
-
});
|
|
3561
|
-
return;
|
|
3562
3672
|
}
|
|
3563
|
-
|
|
3673
|
+
const activity = readSubAgentActivity(method, params);
|
|
3674
|
+
if (activity?.agentThreadId !== void 0 && !isRootChild(activity.agentPath, activity.agentThreadId, this.nativeThreadId ?? this.threadId)) {
|
|
3675
|
+
this.#collab.upsertCollabChild(activity.agentThreadId, { agentPath: activity.agentPath });
|
|
3676
|
+
}
|
|
3677
|
+
this.dispatchMapped(method, params);
|
|
3564
3678
|
}
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3679
|
+
dispatchMapped(method, params) {
|
|
3680
|
+
if (method === "serverRequest/resolved") {
|
|
3681
|
+
const rawId = isRecord3(params) ? params["requestId"] : void 0;
|
|
3682
|
+
if (typeof rawId !== "number" && typeof rawId !== "string") return;
|
|
3683
|
+
const resolvedId = this.requestIdFor(rawId);
|
|
3684
|
+
const pending = this.pendingApprovals.get(resolvedId);
|
|
3685
|
+
if (pending === void 0) return;
|
|
3686
|
+
pending.release();
|
|
3687
|
+
this.emit({
|
|
3688
|
+
eventId: nextEventId(),
|
|
3689
|
+
provider: CODEX_DRIVER_KIND,
|
|
3690
|
+
threadId: this.threadId,
|
|
3691
|
+
createdAt: nowIso(),
|
|
3692
|
+
type: "request.resolved",
|
|
3693
|
+
requestId: resolvedId,
|
|
3694
|
+
payload: {
|
|
3695
|
+
requestType: toRequestTypeFromKind(this.requestKinds.get(resolvedId)),
|
|
3696
|
+
resolution: params
|
|
3697
|
+
}
|
|
3698
|
+
});
|
|
3570
3699
|
return;
|
|
3571
3700
|
}
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3701
|
+
trackOpenCommand(this.openCommands, method, params);
|
|
3702
|
+
if (method === "account/rateLimits/updated" && isRecord3(params)) {
|
|
3703
|
+
this.rateLimits = mergeCodexRateLimits(this.rateLimits, params["rateLimits"]);
|
|
3704
|
+
}
|
|
3705
|
+
for (const event of mapNotificationToEvents({
|
|
3575
3706
|
method,
|
|
3576
3707
|
params,
|
|
3577
3708
|
threadId: this.threadId,
|
|
3578
|
-
providerInstanceId: this.host.instanceId
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
this.pendingApprovals.set(requestId, {
|
|
3583
|
-
jsonRpcId: id,
|
|
3584
|
-
method,
|
|
3585
|
-
params,
|
|
3586
|
-
kind,
|
|
3587
|
-
identity,
|
|
3588
|
-
settle: (decision) => {
|
|
3589
|
-
this.pendingApprovals.delete(requestId);
|
|
3590
|
-
void this.rpc.respond(id, decision).catch((error) => this.#warnAnswerWriteFailed(method, id, error)).finally(resolve2);
|
|
3591
|
-
},
|
|
3592
|
-
release: () => {
|
|
3593
|
-
this.pendingApprovals.delete(requestId);
|
|
3594
|
-
resolve2();
|
|
3595
|
-
}
|
|
3596
|
-
});
|
|
3597
|
-
for (const event of events) this.emit(event);
|
|
3598
|
-
});
|
|
3599
|
-
}
|
|
3600
|
-
async respondToRequest(requestId, decision, reason, persist) {
|
|
3601
|
-
const pending = this.pendingApprovals.get(requestId);
|
|
3602
|
-
if (!pending) throw new Error(`Unknown pending Codex approval request: ${requestId}`);
|
|
3603
|
-
if (decision === "deny" && reason) this.declineRelay.queue(this.threadId, reason);
|
|
3604
|
-
let wire = { decision: decision === "allow" ? "accept" : "decline" };
|
|
3605
|
-
if (decision === "allow" && pending.kind === "mcp-elicitation") {
|
|
3606
|
-
wire = toMcpElicitationResponse("allow", pending.params);
|
|
3709
|
+
providerInstanceId: this.host.instanceId,
|
|
3710
|
+
rateLimits: this.rateLimits
|
|
3711
|
+
})) {
|
|
3712
|
+
this.emit(event);
|
|
3607
3713
|
}
|
|
3608
|
-
if (
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
this.grants.remember(codexApprovalGrantKey(pending.kind, pending.identity), persist);
|
|
3612
|
-
if (pending.kind !== "mcp-elicitation") {
|
|
3613
|
-
wire = { decision: wireDecisionFromPersist(pending.kind, persist) };
|
|
3614
|
-
}
|
|
3714
|
+
if (method === "turn/started") {
|
|
3715
|
+
const turnId = turnIdOf(params);
|
|
3716
|
+
if (turnId) this.activeTurnId = turnId;
|
|
3615
3717
|
}
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
payload: {
|
|
3628
|
-
requestType: toRequestTypeFromKind(pending.kind),
|
|
3629
|
-
decision,
|
|
3630
|
-
...reason ? { reason } : {}
|
|
3631
|
-
}
|
|
3632
|
-
});
|
|
3633
|
-
if (delivery !== "written") throw unconfirmedAnswerError(requestId, delivery);
|
|
3718
|
+
if (method === "turn/completed") {
|
|
3719
|
+
this.activeTurnId = void 0;
|
|
3720
|
+
const { activeTurnId: _completedTurn, ...idleSession } = this.session;
|
|
3721
|
+
this.session = { ...idleSession, status: "idle", updatedAt: nowIso() };
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
async handleServerRequest(id, method, params) {
|
|
3725
|
+
return this.#requests.handleServerRequest(id, method, params);
|
|
3726
|
+
}
|
|
3727
|
+
async respondToRequest(requestId, decision, reason, persist) {
|
|
3728
|
+
return this.#requests.respondToRequest(requestId, decision, reason, persist);
|
|
3634
3729
|
}
|
|
3635
3730
|
async respondToUserInput(requestId, answers) {
|
|
3636
|
-
|
|
3637
|
-
if (!pending) throw new Error(`Unknown pending Codex user input request: ${requestId}`);
|
|
3638
|
-
const mapped = toCodexUserInputAnswers(answers);
|
|
3639
|
-
if (mapped === void 0) {
|
|
3640
|
-
throw new Error(`invalid user input answers for request ${requestId}`);
|
|
3641
|
-
}
|
|
3642
|
-
const ack = this.registerWriteAck(pending.jsonRpcId);
|
|
3643
|
-
pending.settle({ answers: mapped });
|
|
3644
|
-
const delivery = await ack.catch(() => "closed");
|
|
3645
|
-
this.emit({
|
|
3646
|
-
eventId: nextEventId(),
|
|
3647
|
-
provider: CODEX_DRIVER_KIND,
|
|
3648
|
-
threadId: this.threadId,
|
|
3649
|
-
createdAt: nowIso(),
|
|
3650
|
-
type: "user-input.resolved",
|
|
3651
|
-
requestId,
|
|
3652
|
-
// 契约的 answers 是 questionId → 答案 的 record,不是数组。
|
|
3653
|
-
payload: { answers: toCanonicalUserInputAnswers(mapped) }
|
|
3654
|
-
});
|
|
3655
|
-
if (delivery !== "written") throw unconfirmedAnswerError(requestId, delivery);
|
|
3731
|
+
return this.#requests.respondToUserInput(requestId, answers);
|
|
3656
3732
|
}
|
|
3657
3733
|
async withActiveWriterRecovery(run, allowUnsubscribe) {
|
|
3658
3734
|
let lastError;
|
|
@@ -3846,22 +3922,7 @@ var CodexSessionRuntime = class {
|
|
|
3846
3922
|
if (target) {
|
|
3847
3923
|
await this.rpc.request("turn/interrupt", { threadId: this.nativeThreadId, turnId: target }, 0).catch(() => void 0);
|
|
3848
3924
|
}
|
|
3849
|
-
this.cancelPendingApprovals();
|
|
3850
|
-
}
|
|
3851
|
-
/** 把还挂着的审批答成「取消」,逐条发 request.resolved。 */
|
|
3852
|
-
cancelPendingApprovals() {
|
|
3853
|
-
for (const [requestId, pending] of this.pendingApprovals) {
|
|
3854
|
-
pending.settle(cancelWireFor(pending));
|
|
3855
|
-
this.emit({
|
|
3856
|
-
eventId: nextEventId(),
|
|
3857
|
-
provider: CODEX_DRIVER_KIND,
|
|
3858
|
-
threadId: this.threadId,
|
|
3859
|
-
createdAt: nowIso(),
|
|
3860
|
-
type: "request.resolved",
|
|
3861
|
-
requestId,
|
|
3862
|
-
payload: { requestType: toRequestTypeFromKind(pending.kind), decision: "cancel" }
|
|
3863
|
-
});
|
|
3864
|
-
}
|
|
3925
|
+
this.#requests.cancelPendingApprovals();
|
|
3865
3926
|
}
|
|
3866
3927
|
async compactContext() {
|
|
3867
3928
|
if (!this.nativeThreadId) return;
|
|
@@ -3895,16 +3956,10 @@ var CodexSessionRuntime = class {
|
|
|
3895
3956
|
}
|
|
3896
3957
|
/** 会话收口时把还挂着的审批答成「取消」:应答形状按审批种类,权限审批不认 decision。 */
|
|
3897
3958
|
settlePendingApprovals() {
|
|
3898
|
-
|
|
3899
|
-
pending.settle(cancelWireFor(pending));
|
|
3900
|
-
this.pendingApprovals.delete(requestId);
|
|
3901
|
-
}
|
|
3959
|
+
return this.#requests.settlePendingApprovals();
|
|
3902
3960
|
}
|
|
3903
3961
|
settlePendingUserInputs(result) {
|
|
3904
|
-
|
|
3905
|
-
pending.settle(result);
|
|
3906
|
-
this.pendingUserInputs.delete(requestId);
|
|
3907
|
-
}
|
|
3962
|
+
return this.#requests.settlePendingUserInputs(result);
|
|
3908
3963
|
}
|
|
3909
3964
|
async close() {
|
|
3910
3965
|
if (this.closeCompleted) return;
|
|
@@ -3930,17 +3985,6 @@ var CodexSessionRuntime = class {
|
|
|
3930
3985
|
});
|
|
3931
3986
|
}
|
|
3932
3987
|
};
|
|
3933
|
-
async function markProviderRejection(run) {
|
|
3934
|
-
try {
|
|
3935
|
-
return await run();
|
|
3936
|
-
} catch (error) {
|
|
3937
|
-
if (!(error instanceof CodexRpcResponseError)) throw error;
|
|
3938
|
-
const rejected = Object.assign(new Error(error.message), {
|
|
3939
|
-
code: "provider-rejected"
|
|
3940
|
-
});
|
|
3941
|
-
throw rejected;
|
|
3942
|
-
}
|
|
3943
|
-
}
|
|
3944
3988
|
|
|
3945
3989
|
// src/sessions.ts
|
|
3946
3990
|
import * as NodeFS3 from "node:fs";
|