@ycodium-ai/agent-codex 0.2.3086 → 0.2.3094
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 +809 -773
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1381,6 +1381,43 @@ 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/codexArtifacts.ts
|
|
1385
|
+
function imageGenerationArtifacts(input) {
|
|
1386
|
+
if (input.itemType !== "imageGeneration" || input.status !== "completed") return [];
|
|
1387
|
+
if (!isRecord3(input.params)) return [];
|
|
1388
|
+
const item = isRecord3(input.params["item"]) ? input.params["item"] : input.params;
|
|
1389
|
+
const savedPath = item["savedPath"];
|
|
1390
|
+
if (typeof savedPath !== "string" || savedPath.trim().length === 0) return [];
|
|
1391
|
+
return [{ path: savedPath, ...input.itemId ? { toolCallId: input.itemId } : {} }];
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// src/codexItemTypes.ts
|
|
1395
|
+
function normalizeItemType(value) {
|
|
1396
|
+
if (!value) return "item";
|
|
1397
|
+
return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[._/-]/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1398
|
+
}
|
|
1399
|
+
function toCanonicalItemType(raw) {
|
|
1400
|
+
const type = normalizeItemType(raw);
|
|
1401
|
+
if (type.includes("user")) return "user_message";
|
|
1402
|
+
if (type.includes("agent message") || type.includes("assistant")) return "assistant_message";
|
|
1403
|
+
if (type.includes("reasoning") || type.includes("thought")) return "reasoning";
|
|
1404
|
+
if (type.includes("plan") || type.includes("todo")) return "plan";
|
|
1405
|
+
if (type.includes("command")) return "command_execution";
|
|
1406
|
+
if (type.includes("file change") || type.includes("patch") || type.includes("edit")) {
|
|
1407
|
+
return "file_change";
|
|
1408
|
+
}
|
|
1409
|
+
if (type.includes("mcp")) return "mcp_tool_call";
|
|
1410
|
+
if (type.includes("dynamic tool")) return "dynamic_tool_call";
|
|
1411
|
+
if (type.includes("collab")) return "collab_agent_tool_call";
|
|
1412
|
+
if (type.includes("web search")) return "web_search";
|
|
1413
|
+
if (type.includes("image")) return "image_view";
|
|
1414
|
+
if (type.includes("review entered")) return "review_entered";
|
|
1415
|
+
if (type.includes("review exited")) return "review_exited";
|
|
1416
|
+
if (type.includes("compact")) return "context_compaction";
|
|
1417
|
+
if (type.includes("error")) return "error";
|
|
1418
|
+
return "unknown";
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1384
1421
|
// src/events/base.ts
|
|
1385
1422
|
import * as NodeCrypto from "node:crypto";
|
|
1386
1423
|
var CODEX_PROVIDER = "codex";
|
|
@@ -1657,351 +1694,119 @@ function mapCollabAgentEvent(input) {
|
|
|
1657
1694
|
return [];
|
|
1658
1695
|
}
|
|
1659
1696
|
|
|
1660
|
-
// src/
|
|
1661
|
-
function
|
|
1662
|
-
if (!isRecord3(params)) return
|
|
1663
|
-
const
|
|
1664
|
-
const
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
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
|
|
1681
1726
|
});
|
|
1682
1727
|
}
|
|
1683
|
-
return
|
|
1728
|
+
return mapped.length > 0 ? mapped : void 0;
|
|
1684
1729
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
const
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
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;
|
|
1697
1751
|
}
|
|
1698
|
-
function
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
path,
|
|
1710
|
-
changeKind: "delete",
|
|
1711
|
-
...content !== void 0 ? { content: { kind: "replace", oldText: content, newText: "" } } : {}
|
|
1712
|
-
});
|
|
1713
|
-
} else if (movePath) {
|
|
1714
|
-
out.push({
|
|
1715
|
-
path,
|
|
1716
|
-
changeKind: "rename",
|
|
1717
|
-
...diff ? { content: { kind: "unified", diff } } : {}
|
|
1718
|
-
});
|
|
1719
|
-
} else {
|
|
1720
|
-
out.push({
|
|
1721
|
-
path,
|
|
1722
|
-
changeKind: "modify",
|
|
1723
|
-
...diff ? { content: { kind: "unified", diff } } : {}
|
|
1724
|
-
});
|
|
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;
|
|
1725
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;
|
|
1726
1769
|
}
|
|
1727
1770
|
return out;
|
|
1728
1771
|
}
|
|
1729
|
-
function
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
return {
|
|
1737
|
-
command,
|
|
1738
|
-
...cwd ? { cwd } : {},
|
|
1739
|
-
...reason ? { reason } : {},
|
|
1740
|
-
...callId ? { callId } : {}
|
|
1741
|
-
};
|
|
1742
|
-
}
|
|
1743
|
-
function describeFileChangeApproval(params) {
|
|
1744
|
-
const record = isRecord3(params) ? params : {};
|
|
1745
|
-
const reason = trimText(record["reason"]);
|
|
1746
|
-
const grantRoot = asString(record["grantRoot"]);
|
|
1747
|
-
const callId = asString(record["callId"]);
|
|
1748
|
-
const detail = reason ?? describeFileChanges(record) ?? trimText(grantRoot);
|
|
1749
|
-
return {
|
|
1750
|
-
...reason ? { reason } : {},
|
|
1751
|
-
...detail ? { detail } : {},
|
|
1752
|
-
...grantRoot ? { grantRoot } : {},
|
|
1753
|
-
...callId ? { callId } : {},
|
|
1754
|
-
fileChanges: toApplyPatchFileChanges(record)
|
|
1755
|
-
};
|
|
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
|
+
);
|
|
1756
1779
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
const
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
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" } };
|
|
1773
1802
|
}
|
|
1774
|
-
|
|
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" } };
|
|
1775
1807
|
return {
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
write: [...new Set(write)],
|
|
1779
|
-
network: network["enabled"] === true
|
|
1780
|
-
};
|
|
1781
|
-
}
|
|
1782
|
-
function toPermissionsResponse(decision, params, persistent) {
|
|
1783
|
-
if (decision !== "allow") return { permissions: {} };
|
|
1784
|
-
const requested = isRecord3(params) && isRecord3(params["permissions"]) ? params["permissions"] : {};
|
|
1785
|
-
return {
|
|
1786
|
-
permissions: {
|
|
1787
|
-
...requested["fileSystem"] !== void 0 ? { fileSystem: requested["fileSystem"] } : {},
|
|
1788
|
-
...requested["network"] !== void 0 ? { network: requested["network"] } : {}
|
|
1789
|
-
},
|
|
1790
|
-
scope: persistent ? "session" : "turn"
|
|
1791
|
-
};
|
|
1792
|
-
}
|
|
1793
|
-
function describeMcpElicitation(params) {
|
|
1794
|
-
const record = isRecord3(params) ? params : {};
|
|
1795
|
-
const appName = asString(record["appName"]) ?? asString(record["serverName"]) ?? void 0;
|
|
1796
|
-
const serverName = asString(record["serverName"]);
|
|
1797
|
-
const message = trimText(record["message"]);
|
|
1798
|
-
const url = asString(record["url"]) ?? asString(record["authorizationUrl"]);
|
|
1799
|
-
const identity = appName ?? url ?? "unknown";
|
|
1800
|
-
return {
|
|
1801
|
-
...appName ? { appName } : {},
|
|
1802
|
-
...serverName ? { serverName } : {},
|
|
1803
|
-
...message ? { message } : {},
|
|
1804
|
-
...url ? { url } : {},
|
|
1805
|
-
identity,
|
|
1806
|
-
mode: url ? "url" : "form",
|
|
1807
|
-
...record["requestedSchema"] !== void 0 ? { schema: record["requestedSchema"] } : {}
|
|
1808
|
-
};
|
|
1809
|
-
}
|
|
1810
|
-
function persistFromMeta(meta) {
|
|
1811
|
-
if (!isRecord3(meta)) return void 0;
|
|
1812
|
-
const persist = asString(meta["persist"]) ?? asString(meta["persistence"]);
|
|
1813
|
-
if (persist === "session" || persist === "acceptForSession") return "session";
|
|
1814
|
-
if (persist === "always" || persist === "permanent" || persist === "forever" || persist === "persistent" || persist === "acceptAlways") {
|
|
1815
|
-
return "always";
|
|
1816
|
-
}
|
|
1817
|
-
return void 0;
|
|
1818
|
-
}
|
|
1819
|
-
function toMcpElicitationResponse(decision, params) {
|
|
1820
|
-
const described = describeMcpElicitation(params);
|
|
1821
|
-
if (decision !== "allow" || described.mode === "url") {
|
|
1822
|
-
return {
|
|
1823
|
-
action: decision === "allow" ? "decline" : decision === "cancel" ? "cancel" : "decline"
|
|
1824
|
-
};
|
|
1825
|
-
}
|
|
1826
|
-
const schema = isRecord3(described.schema) ? described.schema : void 0;
|
|
1827
|
-
const properties = schema && isRecord3(schema["properties"]) ? schema["properties"] : {};
|
|
1828
|
-
const required = schema && Array.isArray(schema["required"]) ? schema["required"] : [];
|
|
1829
|
-
const content = {};
|
|
1830
|
-
for (const [key, spec] of Object.entries(properties)) {
|
|
1831
|
-
if (!isRecord3(spec)) continue;
|
|
1832
|
-
if (Array.isArray(spec["oneOf"]) || Array.isArray(spec["enum"])) {
|
|
1833
|
-
const options = Array.isArray(spec["oneOf"]) ? spec["oneOf"] : Array.isArray(spec["enum"]) ? spec["enum"] : [];
|
|
1834
|
-
const first = options[0];
|
|
1835
|
-
content[key] = isRecord3(first) ? first["const"] ?? first["value"] ?? first : first;
|
|
1836
|
-
} else if (spec["type"] === "boolean") {
|
|
1837
|
-
content[key] = true;
|
|
1838
|
-
} else if (typeof spec["default"] !== "undefined") {
|
|
1839
|
-
content[key] = spec["default"];
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
for (const key of required) {
|
|
1843
|
-
if (typeof key === "string" && !(key in content)) {
|
|
1844
|
-
return { action: "decline" };
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
const persist = persistFromMeta(isRecord3(params) ? params["_meta"] : void 0);
|
|
1848
|
-
return {
|
|
1849
|
-
action: "accept",
|
|
1850
|
-
content,
|
|
1851
|
-
...persist ? { _meta: { persist } } : {}
|
|
1852
|
-
};
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
// src/codexArtifacts.ts
|
|
1856
|
-
function imageGenerationArtifacts(input) {
|
|
1857
|
-
if (input.itemType !== "imageGeneration" || input.status !== "completed") return [];
|
|
1858
|
-
if (!isRecord3(input.params)) return [];
|
|
1859
|
-
const item = isRecord3(input.params["item"]) ? input.params["item"] : input.params;
|
|
1860
|
-
const savedPath = item["savedPath"];
|
|
1861
|
-
if (typeof savedPath !== "string" || savedPath.trim().length === 0) return [];
|
|
1862
|
-
return [{ path: savedPath, ...input.itemId ? { toolCallId: input.itemId } : {} }];
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
// src/codexItemTypes.ts
|
|
1866
|
-
function normalizeItemType(value) {
|
|
1867
|
-
if (!value) return "item";
|
|
1868
|
-
return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[._/-]/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1869
|
-
}
|
|
1870
|
-
function toCanonicalItemType(raw) {
|
|
1871
|
-
const type = normalizeItemType(raw);
|
|
1872
|
-
if (type.includes("user")) return "user_message";
|
|
1873
|
-
if (type.includes("agent message") || type.includes("assistant")) return "assistant_message";
|
|
1874
|
-
if (type.includes("reasoning") || type.includes("thought")) return "reasoning";
|
|
1875
|
-
if (type.includes("plan") || type.includes("todo")) return "plan";
|
|
1876
|
-
if (type.includes("command")) return "command_execution";
|
|
1877
|
-
if (type.includes("file change") || type.includes("patch") || type.includes("edit")) {
|
|
1878
|
-
return "file_change";
|
|
1879
|
-
}
|
|
1880
|
-
if (type.includes("mcp")) return "mcp_tool_call";
|
|
1881
|
-
if (type.includes("dynamic tool")) return "dynamic_tool_call";
|
|
1882
|
-
if (type.includes("collab")) return "collab_agent_tool_call";
|
|
1883
|
-
if (type.includes("web search")) return "web_search";
|
|
1884
|
-
if (type.includes("image")) return "image_view";
|
|
1885
|
-
if (type.includes("review entered")) return "review_entered";
|
|
1886
|
-
if (type.includes("review exited")) return "review_exited";
|
|
1887
|
-
if (type.includes("compact")) return "context_compaction";
|
|
1888
|
-
if (type.includes("error")) return "error";
|
|
1889
|
-
return "unknown";
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
// src/events/userInput.ts
|
|
1893
|
-
function toUserInputQuestions(params) {
|
|
1894
|
-
if (!isRecord3(params)) return void 0;
|
|
1895
|
-
const questions = Array.isArray(params["questions"]) ? params["questions"] : [];
|
|
1896
|
-
const mapped = [];
|
|
1897
|
-
for (const question of questions) {
|
|
1898
|
-
if (!isRecord3(question)) return void 0;
|
|
1899
|
-
const id = asString(question["id"]);
|
|
1900
|
-
const header = asString(question["header"]);
|
|
1901
|
-
const text = asString(question["question"]);
|
|
1902
|
-
const rawOptions = Array.isArray(question["options"]) ? question["options"] : [];
|
|
1903
|
-
const options = [];
|
|
1904
|
-
for (const option of rawOptions) {
|
|
1905
|
-
if (!isRecord3(option)) continue;
|
|
1906
|
-
const label = trimText(option["label"]);
|
|
1907
|
-
if (!label) continue;
|
|
1908
|
-
options.push({
|
|
1909
|
-
...asString(option["id"]) ? { id: asString(option["id"]) } : {},
|
|
1910
|
-
label,
|
|
1911
|
-
description: trimText(option["description"]) ?? ""
|
|
1912
|
-
});
|
|
1913
|
-
}
|
|
1914
|
-
if (!id || !header || !text || options.length === 0) return void 0;
|
|
1915
|
-
mapped.push({
|
|
1916
|
-
id,
|
|
1917
|
-
header,
|
|
1918
|
-
question: text,
|
|
1919
|
-
options,
|
|
1920
|
-
multiSelect: false
|
|
1921
|
-
});
|
|
1922
|
-
}
|
|
1923
|
-
return mapped.length > 0 ? mapped : void 0;
|
|
1924
|
-
}
|
|
1925
|
-
function toAsyncQuestions(params) {
|
|
1926
|
-
if (!isRecord3(params)) return void 0;
|
|
1927
|
-
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1928
|
-
if (item["delivery"] !== "async" || !Array.isArray(item["questions"])) return void 0;
|
|
1929
|
-
const mapped = [];
|
|
1930
|
-
item["questions"].forEach((question, index) => {
|
|
1931
|
-
if (!isRecord3(question)) return;
|
|
1932
|
-
const title = trimText(question["title"]);
|
|
1933
|
-
if (!title) return;
|
|
1934
|
-
const labels = Array.isArray(question["options"]) ? question["options"] : [];
|
|
1935
|
-
const options = labels.map((label) => trimText(label)).filter((label) => label !== void 0).map((label) => ({ label, description: "" }));
|
|
1936
|
-
mapped.push({
|
|
1937
|
-
id: `q${index}`,
|
|
1938
|
-
header: title,
|
|
1939
|
-
question: title,
|
|
1940
|
-
options,
|
|
1941
|
-
allowFreeform: true,
|
|
1942
|
-
multiSelect: false
|
|
1943
|
-
});
|
|
1944
|
-
});
|
|
1945
|
-
return mapped.length > 0 ? mapped : void 0;
|
|
1946
|
-
}
|
|
1947
|
-
function toCodexUserInputAnswers(answers) {
|
|
1948
|
-
if (!isRecord3(answers)) return void 0;
|
|
1949
|
-
const out = {};
|
|
1950
|
-
for (const [questionId, value] of Object.entries(answers)) {
|
|
1951
|
-
if (typeof value === "string") {
|
|
1952
|
-
out[questionId] = { answers: [value] };
|
|
1953
|
-
continue;
|
|
1954
|
-
}
|
|
1955
|
-
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
1956
|
-
out[questionId] = { answers: [...value] };
|
|
1957
|
-
continue;
|
|
1958
|
-
}
|
|
1959
|
-
if (isRecord3(value) && Array.isArray(value["answers"]) && value["answers"].every((entry) => typeof entry === "string")) {
|
|
1960
|
-
out[questionId] = { answers: [...value["answers"]] };
|
|
1961
|
-
continue;
|
|
1962
|
-
}
|
|
1963
|
-
return void 0;
|
|
1964
|
-
}
|
|
1965
|
-
return out;
|
|
1966
|
-
}
|
|
1967
|
-
function toCanonicalUserInputAnswers(answers) {
|
|
1968
|
-
return Object.fromEntries(
|
|
1969
|
-
Object.entries(answers).map(([questionId, value]) => [
|
|
1970
|
-
questionId,
|
|
1971
|
-
value.answers.length === 1 ? value.answers[0] : [...value.answers]
|
|
1972
|
-
])
|
|
1973
|
-
);
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
|
-
// src/usage/codexQuota.ts
|
|
1977
|
-
var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
1978
|
-
var asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1979
|
-
var asNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
1980
|
-
var clampPercent = (value) => Math.min(100, Math.max(0, value));
|
|
1981
|
-
var epochSecondsToIso = (value) => {
|
|
1982
|
-
const seconds = asFiniteNumber(value);
|
|
1983
|
-
if (seconds === null) return null;
|
|
1984
|
-
const date = new Date(seconds * 1e3);
|
|
1985
|
-
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
1986
|
-
};
|
|
1987
|
-
var SESSION_WINDOW_MINUTES = 5 * 60;
|
|
1988
|
-
var WEEKLY_WINDOW_MINUTES = 7 * 24 * 60;
|
|
1989
|
-
var formatWindowDuration = (minutes) => {
|
|
1990
|
-
if (minutes % (24 * 60) === 0) return `${String(minutes / (24 * 60))}d`;
|
|
1991
|
-
if (minutes % 60 === 0) return `${String(minutes / 60)}h`;
|
|
1992
|
-
return `${String(minutes)}m`;
|
|
1993
|
-
};
|
|
1994
|
-
var classifyWindow = (minutes, slot) => {
|
|
1995
|
-
if (minutes === SESSION_WINDOW_MINUTES) {
|
|
1996
|
-
return { id: "session", label: { key: "usage.window.session" } };
|
|
1997
|
-
}
|
|
1998
|
-
if (minutes === WEEKLY_WINDOW_MINUTES) {
|
|
1999
|
-
return { id: "weekly", label: { key: "usage.window.weekly" } };
|
|
2000
|
-
}
|
|
2001
|
-
if (minutes === null) return { id: slot, label: { key: "usage.window.limit" } };
|
|
2002
|
-
return {
|
|
2003
|
-
id: `window-${String(minutes)}m`,
|
|
2004
|
-
label: { key: "usage.window.duration", values: { duration: formatWindowDuration(minutes) } }
|
|
1808
|
+
id: `window-${String(minutes)}m`,
|
|
1809
|
+
label: { key: "usage.window.duration", values: { duration: formatWindowDuration(minutes) } }
|
|
2005
1810
|
};
|
|
2006
1811
|
};
|
|
2007
1812
|
var mapWindow = (raw, slot, describe) => {
|
|
@@ -2858,23 +2663,218 @@ function mapNotificationToEvents(input) {
|
|
|
2858
2663
|
}
|
|
2859
2664
|
}
|
|
2860
2665
|
|
|
2861
|
-
// src/
|
|
2862
|
-
function
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
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
|
|
2862
|
+
function toRequestTypeFromKind(kind) {
|
|
2863
|
+
switch (kind) {
|
|
2864
|
+
case "command":
|
|
2865
|
+
return "command_execution_approval";
|
|
2866
|
+
case "file-read":
|
|
2867
|
+
return "file_read_approval";
|
|
2868
|
+
case "file-change":
|
|
2869
|
+
return "file_change_approval";
|
|
2870
|
+
case "apply-patch":
|
|
2871
|
+
return "apply_patch_approval";
|
|
2872
|
+
case "mcp-elicitation":
|
|
2873
|
+
return "mcp_elicitation_approval";
|
|
2874
|
+
// 契约没有单独的权限审批类型;宿主把 unknown 当 tool 类审批卡片渲染。
|
|
2875
|
+
case "permission":
|
|
2876
|
+
default:
|
|
2877
|
+
return "unknown";
|
|
2878
2878
|
}
|
|
2879
2879
|
}
|
|
2880
2880
|
function toRequestTypeFromMethod(method) {
|
|
@@ -3064,19 +3064,12 @@ function openCommandsOf(booked) {
|
|
|
3064
3064
|
};
|
|
3065
3065
|
}
|
|
3066
3066
|
|
|
3067
|
-
// src/
|
|
3067
|
+
// src/sessionCollab.ts
|
|
3068
3068
|
var COLLAB_META_LOOKUP_TIMEOUT_MS = 5e3;
|
|
3069
3069
|
function metaValue(value) {
|
|
3070
3070
|
const text = asString(value)?.trim();
|
|
3071
3071
|
return text === void 0 || text.length === 0 ? void 0 : text;
|
|
3072
3072
|
}
|
|
3073
|
-
function cancelWireFor(pending) {
|
|
3074
|
-
if (pending.kind === "permission") return toPermissionsResponse("cancel", pending.params, false);
|
|
3075
|
-
return pending.kind === "mcp-elicitation" ? toMcpElicitationResponse("cancel", pending.params) : { decision: "cancel" };
|
|
3076
|
-
}
|
|
3077
|
-
var sleep = (ms) => new Promise((resolve2) => {
|
|
3078
|
-
setTimeout(resolve2, ms);
|
|
3079
|
-
});
|
|
3080
3073
|
function isRootChild(agentPath, agentThreadId, rootThreadId) {
|
|
3081
3074
|
if (agentThreadId === rootThreadId) return true;
|
|
3082
3075
|
return agentPath === "/root" || agentPath === "/";
|
|
@@ -3090,224 +3083,10 @@ function readSubAgentActivity(method, params) {
|
|
|
3090
3083
|
agentPath: asString(item["agentPath"]) ?? void 0
|
|
3091
3084
|
};
|
|
3092
3085
|
}
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
}
|
|
3098
|
-
function unconfirmedAnswerError(requestId, delivery) {
|
|
3099
|
-
return new Error(
|
|
3100
|
-
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`
|
|
3101
|
-
);
|
|
3102
|
-
}
|
|
3103
|
-
var CodexSessionRuntime = class {
|
|
3104
|
-
threadId;
|
|
3105
|
-
cwd;
|
|
3106
|
-
session;
|
|
3107
|
-
nativeThreadId;
|
|
3108
|
-
activeTurnId;
|
|
3109
|
-
model;
|
|
3110
|
-
runtimeMode;
|
|
3111
|
-
/** 宿主解好的这一轮的档(座位画的就是它)。缺席 = 宿主没给,按 `runtimeMode` 查表。 */
|
|
3112
|
-
nativeModeId;
|
|
3113
|
-
interactionMode;
|
|
3114
|
-
closed = false;
|
|
3115
|
-
closeCompleted = false;
|
|
3116
|
-
turns = [];
|
|
3117
|
-
pendingApprovals = /* @__PURE__ */ new Map();
|
|
3118
|
-
pendingUserInputs = /* @__PURE__ */ new Map();
|
|
3119
|
-
requestKinds = /* @__PURE__ */ new Map();
|
|
3120
|
-
writeAcks = /* @__PURE__ */ new Map();
|
|
3121
|
-
grants;
|
|
3122
|
-
declineRelay;
|
|
3123
|
-
collabChildren = /* @__PURE__ */ new Map();
|
|
3124
|
-
collabChildMeta = /* @__PURE__ */ new Map();
|
|
3125
|
-
liveForeignTurns = /* @__PURE__ */ new Map();
|
|
3126
|
-
openCommands = /* @__PURE__ */ new Map();
|
|
3127
|
-
collabReceiverTurns = /* @__PURE__ */ new Map();
|
|
3128
|
-
memoryThreadIds = /* @__PURE__ */ new Set();
|
|
3129
|
-
/** 这个会话见过的主额度快照(按字段合并),额度用尽停轮时点名窗口用。 */
|
|
3130
|
-
rateLimits;
|
|
3131
|
-
appServerArgs;
|
|
3132
|
-
hasConfiguredMcpServer;
|
|
3133
|
-
host;
|
|
3134
|
-
attachments;
|
|
3135
|
-
rpc;
|
|
3136
|
-
emit;
|
|
3137
|
-
/**
|
|
3138
|
-
* 对外 requestId 的前缀,每个会话(即每个 app-server 进程)一份。JSON-RPC id 每个进程
|
|
3139
|
-
* 都从 0 数起,宿主按 requestId 记「已接受」,不加前缀的话重启后的新请求会撞上旧请求。
|
|
3140
|
-
*/
|
|
3141
|
-
#requestIdPrefix = `codex-${NodeCrypto2.randomUUID()}:`;
|
|
3142
|
-
/** JSON-RPC id → 对外 requestId。`serverRequest/resolved` 里的 id 可能是数字也可能是字符串。 */
|
|
3143
|
-
requestIdFor(jsonRpcId) {
|
|
3144
|
-
return `${this.#requestIdPrefix}${String(jsonRpcId)}`;
|
|
3145
|
-
}
|
|
3146
|
-
/** 这一轮该用哪套三字段组合:只由这一轮的档决定(见 `codexNativeModeFor`)。 */
|
|
3147
|
-
#threadConfig() {
|
|
3148
|
-
return codexThreadConfigForNativeMode(codexNativeModeFor(this.runtimeMode, this.nativeModeId));
|
|
3149
|
-
}
|
|
3150
|
-
/** 同上,沙箱策略那一位。 */
|
|
3151
|
-
#turnSandboxPolicy() {
|
|
3152
|
-
return codexTurnSandboxPolicyForNativeMode(
|
|
3153
|
-
codexNativeModeFor(this.runtimeMode, this.nativeModeId)
|
|
3154
|
-
);
|
|
3155
|
-
}
|
|
3156
|
-
/** 给 CLI 的应答写入失败:带上线程与方法名记一行 warn。 */
|
|
3157
|
-
#warnAnswerWriteFailed(method, jsonRpcId, error) {
|
|
3158
|
-
this.host.log.warn("writing an answer to codex failed", {
|
|
3159
|
-
threadId: this.threadId,
|
|
3160
|
-
method,
|
|
3161
|
-
jsonRpcId,
|
|
3162
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3163
|
-
});
|
|
3164
|
-
}
|
|
3165
|
-
constructor(host, rpc, emit, input, cwd, appServerArgs, grants, declineRelay, attachments) {
|
|
3166
|
-
this.attachments = attachments;
|
|
3167
|
-
this.host = host;
|
|
3168
|
-
this.rpc = rpc;
|
|
3169
|
-
this.emit = emit;
|
|
3170
|
-
this.threadId = input.threadId;
|
|
3171
|
-
this.cwd = cwd;
|
|
3172
|
-
this.runtimeMode = input.runtimeMode;
|
|
3173
|
-
this.nativeModeId = input.nativeModeId;
|
|
3174
|
-
this.appServerArgs = appServerArgs;
|
|
3175
|
-
this.hasConfiguredMcpServer = (input.mcpServers?.length ?? 0) > 0;
|
|
3176
|
-
this.grants = grants ?? createApprovalGrantStore();
|
|
3177
|
-
this.declineRelay = declineRelay ?? createDeclineReasonRelay();
|
|
3178
|
-
const createdAt = nowIso();
|
|
3179
|
-
this.session = {
|
|
3180
|
-
provider: CODEX_DRIVER_KIND,
|
|
3181
|
-
providerInstanceId: host.instanceId,
|
|
3182
|
-
status: "idle",
|
|
3183
|
-
runtimeMode: input.runtimeMode,
|
|
3184
|
-
nativeModeId: codexNativeModeFor(input.runtimeMode, input.nativeModeId),
|
|
3185
|
-
cwd,
|
|
3186
|
-
threadId: input.threadId,
|
|
3187
|
-
createdAt,
|
|
3188
|
-
updatedAt: createdAt,
|
|
3189
|
-
...input.bindingGeneration === void 0 ? {} : { bindingGeneration: input.bindingGeneration }
|
|
3190
|
-
};
|
|
3191
|
-
}
|
|
3192
|
-
registerWriteAck(jsonRpcId) {
|
|
3193
|
-
return new Promise((resolve2) => {
|
|
3194
|
-
this.writeAcks.set(jsonRpcId, { resolve: resolve2 });
|
|
3195
|
-
});
|
|
3196
|
-
}
|
|
3197
|
-
settleNativeWrite(requestId, status) {
|
|
3198
|
-
const ack = this.writeAcks.get(requestId);
|
|
3199
|
-
if (!ack) return;
|
|
3200
|
-
this.writeAcks.delete(requestId);
|
|
3201
|
-
ack.resolve(status);
|
|
3202
|
-
}
|
|
3203
|
-
failPendingNativeWrites(reason) {
|
|
3204
|
-
for (const [, ack] of this.writeAcks) ack.resolve("closed");
|
|
3205
|
-
this.writeAcks.clear();
|
|
3206
|
-
void reason;
|
|
3207
|
-
}
|
|
3208
|
-
async confirmAfterNativeWrite(jsonRpcId) {
|
|
3209
|
-
const existing = this.writeAcks.get(jsonRpcId);
|
|
3210
|
-
if (existing) {
|
|
3211
|
-
return await new Promise((resolve2) => {
|
|
3212
|
-
const previous = existing.resolve;
|
|
3213
|
-
existing.resolve = (status) => {
|
|
3214
|
-
previous(status);
|
|
3215
|
-
resolve2(status);
|
|
3216
|
-
};
|
|
3217
|
-
});
|
|
3218
|
-
}
|
|
3219
|
-
return await this.registerWriteAck(jsonRpcId);
|
|
3220
|
-
}
|
|
3221
|
-
handleStderrLine(line) {
|
|
3222
|
-
const kind = classifyStderrLine(line);
|
|
3223
|
-
if (kind === "drop") return;
|
|
3224
|
-
if (isFatalStderr(line)) {
|
|
3225
|
-
this.emit({
|
|
3226
|
-
eventId: nextEventId(),
|
|
3227
|
-
provider: CODEX_DRIVER_KIND,
|
|
3228
|
-
threadId: this.threadId,
|
|
3229
|
-
createdAt: nowIso(),
|
|
3230
|
-
type: "runtime.error",
|
|
3231
|
-
payload: { message: line }
|
|
3232
|
-
});
|
|
3233
|
-
return;
|
|
3234
|
-
}
|
|
3235
|
-
if (kind === "error") {
|
|
3236
|
-
this.host.log.error(line, { threadId: this.threadId });
|
|
3237
|
-
return;
|
|
3238
|
-
}
|
|
3239
|
-
this.host.log.warn(line, { threadId: this.threadId });
|
|
3240
|
-
}
|
|
3241
|
-
handleNotification(method, params) {
|
|
3242
|
-
if (this.closed) return;
|
|
3243
|
-
const conversationId = conversationIdFromNotification(params);
|
|
3244
|
-
if (conversationId && conversationId !== this.nativeThreadId && conversationId !== this.threadId) {
|
|
3245
|
-
this.interceptCollab(method, params, conversationId);
|
|
3246
|
-
return;
|
|
3247
|
-
}
|
|
3248
|
-
if (conversationId && this.memoryThreadIds.has(conversationId)) {
|
|
3249
|
-
if (method === "serverRequest" || method === "serverRequest/resolved") {
|
|
3250
|
-
this.dispatchMapped(method, params);
|
|
3251
|
-
}
|
|
3252
|
-
return;
|
|
3253
|
-
}
|
|
3254
|
-
if (method === "thread/started") {
|
|
3255
|
-
const name = isRecord3(params) ? asString(params["name"]) ?? (isRecord3(params["thread"]) ? asString(params["thread"]["name"]) : null) : null;
|
|
3256
|
-
if (isMemoryConsolidationThread(name ?? void 0) && conversationId) {
|
|
3257
|
-
this.memoryThreadIds.add(conversationId);
|
|
3258
|
-
return;
|
|
3259
|
-
}
|
|
3260
|
-
}
|
|
3261
|
-
const activity = readSubAgentActivity(method, params);
|
|
3262
|
-
if (activity?.agentThreadId !== void 0 && !isRootChild(activity.agentPath, activity.agentThreadId, this.nativeThreadId ?? this.threadId)) {
|
|
3263
|
-
this.upsertCollabChild(activity.agentThreadId, { agentPath: activity.agentPath });
|
|
3264
|
-
}
|
|
3265
|
-
this.dispatchMapped(method, params);
|
|
3266
|
-
}
|
|
3267
|
-
dispatchMapped(method, params) {
|
|
3268
|
-
if (method === "serverRequest/resolved") {
|
|
3269
|
-
const rawId = isRecord3(params) ? params["requestId"] : void 0;
|
|
3270
|
-
if (typeof rawId !== "number" && typeof rawId !== "string") return;
|
|
3271
|
-
const resolvedId = this.requestIdFor(rawId);
|
|
3272
|
-
const pending = this.pendingApprovals.get(resolvedId);
|
|
3273
|
-
if (pending === void 0) return;
|
|
3274
|
-
pending.release();
|
|
3275
|
-
this.emit({
|
|
3276
|
-
eventId: nextEventId(),
|
|
3277
|
-
provider: CODEX_DRIVER_KIND,
|
|
3278
|
-
threadId: this.threadId,
|
|
3279
|
-
createdAt: nowIso(),
|
|
3280
|
-
type: "request.resolved",
|
|
3281
|
-
requestId: resolvedId,
|
|
3282
|
-
payload: {
|
|
3283
|
-
requestType: toRequestTypeFromKind(this.requestKinds.get(resolvedId)),
|
|
3284
|
-
resolution: params
|
|
3285
|
-
}
|
|
3286
|
-
});
|
|
3287
|
-
return;
|
|
3288
|
-
}
|
|
3289
|
-
trackOpenCommand(this.openCommands, method, params);
|
|
3290
|
-
if (method === "account/rateLimits/updated" && isRecord3(params)) {
|
|
3291
|
-
this.rateLimits = mergeCodexRateLimits(this.rateLimits, params["rateLimits"]);
|
|
3292
|
-
}
|
|
3293
|
-
for (const event of mapNotificationToEvents({
|
|
3294
|
-
method,
|
|
3295
|
-
params,
|
|
3296
|
-
threadId: this.threadId,
|
|
3297
|
-
providerInstanceId: this.host.instanceId,
|
|
3298
|
-
rateLimits: this.rateLimits
|
|
3299
|
-
})) {
|
|
3300
|
-
this.emit(event);
|
|
3301
|
-
}
|
|
3302
|
-
if (method === "turn/started") {
|
|
3303
|
-
const turnId = turnIdOf(params);
|
|
3304
|
-
if (turnId) this.activeTurnId = turnId;
|
|
3305
|
-
}
|
|
3306
|
-
if (method === "turn/completed") {
|
|
3307
|
-
this.activeTurnId = void 0;
|
|
3308
|
-
const { activeTurnId: _completedTurn, ...idleSession } = this.session;
|
|
3309
|
-
this.session = { ...idleSession, status: "idle", updatedAt: nowIso() };
|
|
3310
|
-
}
|
|
3086
|
+
var CodexSessionCollab = class {
|
|
3087
|
+
runtime;
|
|
3088
|
+
constructor(runtime) {
|
|
3089
|
+
this.runtime = runtime;
|
|
3311
3090
|
}
|
|
3312
3091
|
interceptCollab(method, params, conversationId) {
|
|
3313
3092
|
const thread = isRecord3(params) && isRecord3(params["thread"]) ? params["thread"] : void 0;
|
|
@@ -3316,8 +3095,9 @@ var CodexSessionRuntime = class {
|
|
|
3316
3095
|
const spawn = isRecord3(subAgent) ? subAgent["thread_spawn"] : void 0;
|
|
3317
3096
|
if (method === "thread/started" && thread !== void 0 && isRecord3(spawn)) {
|
|
3318
3097
|
const agentPath = asString(spawn["agent_path"]) ?? void 0;
|
|
3319
|
-
const knownPath = agentPath ?? this.collabChildren.get(conversationId)?.agentPath;
|
|
3320
|
-
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;
|
|
3321
3101
|
this.upsertCollabChild(conversationId, {
|
|
3322
3102
|
agentPath,
|
|
3323
3103
|
role: asString(spawn["agent_role"]) ?? asString(thread["agentRole"]) ?? void 0,
|
|
@@ -3329,13 +3109,18 @@ var CodexSessionRuntime = class {
|
|
|
3329
3109
|
if ((method === "item/started" || method === "item/completed") && itemType === "collabAgentToolCall") {
|
|
3330
3110
|
const item = isRecord3(params) ? isRecord3(params["item"]) ? params["item"] : params : {};
|
|
3331
3111
|
const receiver = asString(item["receiverThreadId"]) ?? asString(item["agentThreadId"]);
|
|
3332
|
-
if (receiver && this.
|
|
3112
|
+
if (receiver && this.runtime.activeTurnId)
|
|
3113
|
+
this.runtime.collabReceiverTurns.set(receiver, this.runtime.activeTurnId);
|
|
3333
3114
|
return;
|
|
3334
3115
|
}
|
|
3335
3116
|
const activity = readSubAgentActivity(method, params);
|
|
3336
3117
|
if (activity !== void 0) {
|
|
3337
3118
|
const agentThreadId = activity.agentThreadId ?? conversationId;
|
|
3338
|
-
if (isRootChild(
|
|
3119
|
+
if (isRootChild(
|
|
3120
|
+
activity.agentPath,
|
|
3121
|
+
agentThreadId,
|
|
3122
|
+
this.runtime.nativeThreadId ?? this.runtime.threadId
|
|
3123
|
+
)) {
|
|
3339
3124
|
return;
|
|
3340
3125
|
}
|
|
3341
3126
|
this.upsertCollabChild(agentThreadId, { agentPath: activity.agentPath });
|
|
@@ -3350,16 +3135,16 @@ var CodexSessionRuntime = class {
|
|
|
3350
3135
|
this.updateCollabChildMeta(conversationId, { model, effort }, true);
|
|
3351
3136
|
return;
|
|
3352
3137
|
}
|
|
3353
|
-
const child = this.collabChildren.get(conversationId);
|
|
3138
|
+
const child = this.runtime.collabChildren.get(conversationId);
|
|
3354
3139
|
if (method === "turn/started") {
|
|
3355
3140
|
this.reopenCollabChildMeta(conversationId);
|
|
3356
3141
|
const turnId = turnIdOf(params);
|
|
3357
|
-
if (turnId) this.liveForeignTurns.set(conversationId, turnId);
|
|
3142
|
+
if (turnId) this.runtime.liveForeignTurns.set(conversationId, turnId);
|
|
3358
3143
|
}
|
|
3359
3144
|
if (method === "turn/completed" || method === "thread/closed") {
|
|
3360
|
-
this.liveForeignTurns.delete(conversationId);
|
|
3145
|
+
this.runtime.liveForeignTurns.delete(conversationId);
|
|
3361
3146
|
}
|
|
3362
|
-
if (method === "thread/closed" && this.collabChildren.has(conversationId)) {
|
|
3147
|
+
if (method === "thread/closed" && this.runtime.collabChildren.has(conversationId)) {
|
|
3363
3148
|
this.metaOf(conversationId).closed = true;
|
|
3364
3149
|
}
|
|
3365
3150
|
if (!child) {
|
|
@@ -3406,12 +3191,12 @@ var CodexSessionRuntime = class {
|
|
|
3406
3191
|
*/
|
|
3407
3192
|
upsertCollabChild(agentThreadId, identity) {
|
|
3408
3193
|
const fill = (known, next) => known ?? (next !== void 0 && next.length > 0 ? next : void 0);
|
|
3409
|
-
const existing = this.collabChildren.get(agentThreadId);
|
|
3194
|
+
const existing = this.runtime.collabChildren.get(agentThreadId);
|
|
3410
3195
|
const agentPath = fill(existing?.agentPath, identity.agentPath);
|
|
3411
3196
|
const role = fill(existing?.role, identity.role);
|
|
3412
3197
|
const nickname = fill(existing?.nickname, identity.nickname);
|
|
3413
3198
|
if (existing === void 0) {
|
|
3414
|
-
const spawnTurnId = this.activeTurnId;
|
|
3199
|
+
const spawnTurnId = this.runtime.activeTurnId;
|
|
3415
3200
|
const child = {
|
|
3416
3201
|
agentThreadId,
|
|
3417
3202
|
...agentPath ? { agentPath } : {},
|
|
@@ -3420,7 +3205,7 @@ var CodexSessionRuntime = class {
|
|
|
3420
3205
|
...spawnTurnId ? { spawnTurnId } : {},
|
|
3421
3206
|
status: "running"
|
|
3422
3207
|
};
|
|
3423
|
-
this.collabChildren.set(agentThreadId, child);
|
|
3208
|
+
this.runtime.collabChildren.set(agentThreadId, child);
|
|
3424
3209
|
this.emitCollab("collabAgent/started", child);
|
|
3425
3210
|
this.startCollabChildMetaLookup(agentThreadId);
|
|
3426
3211
|
return;
|
|
@@ -3434,19 +3219,19 @@ var CodexSessionRuntime = class {
|
|
|
3434
3219
|
...role ? { role } : {},
|
|
3435
3220
|
...nickname ? { nickname } : {}
|
|
3436
3221
|
};
|
|
3437
|
-
this.collabChildren.set(agentThreadId, merged);
|
|
3222
|
+
this.runtime.collabChildren.set(agentThreadId, merged);
|
|
3438
3223
|
this.emitCollab("collabAgent/metadata", merged, { status: merged.status });
|
|
3439
3224
|
}
|
|
3440
3225
|
metaOf(agentThreadId) {
|
|
3441
|
-
let meta = this.collabChildMeta.get(agentThreadId);
|
|
3226
|
+
let meta = this.runtime.collabChildMeta.get(agentThreadId);
|
|
3442
3227
|
if (meta === void 0) {
|
|
3443
3228
|
meta = { lookupStarted: false, closed: false };
|
|
3444
|
-
this.collabChildMeta.set(agentThreadId, meta);
|
|
3229
|
+
this.runtime.collabChildMeta.set(agentThreadId, meta);
|
|
3445
3230
|
}
|
|
3446
3231
|
return meta;
|
|
3447
3232
|
}
|
|
3448
3233
|
reopenCollabChildMeta(agentThreadId) {
|
|
3449
|
-
const meta = this.collabChildMeta.get(agentThreadId);
|
|
3234
|
+
const meta = this.runtime.collabChildMeta.get(agentThreadId);
|
|
3450
3235
|
if (meta !== void 0) meta.closed = false;
|
|
3451
3236
|
}
|
|
3452
3237
|
/**
|
|
@@ -3460,7 +3245,7 @@ var CodexSessionRuntime = class {
|
|
|
3460
3245
|
if (model === meta.model && effort === meta.effort) return;
|
|
3461
3246
|
if (model !== void 0) meta.model = model;
|
|
3462
3247
|
if (effort !== void 0) meta.effort = effort;
|
|
3463
|
-
const child = this.collabChildren.get(agentThreadId);
|
|
3248
|
+
const child = this.runtime.collabChildren.get(agentThreadId);
|
|
3464
3249
|
if (child !== void 0 && !meta.closed) this.emitCollab("collabAgent/metadataUpdated", child);
|
|
3465
3250
|
}
|
|
3466
3251
|
/**
|
|
@@ -3471,12 +3256,12 @@ var CodexSessionRuntime = class {
|
|
|
3471
3256
|
const meta = this.metaOf(agentThreadId);
|
|
3472
3257
|
if (meta.lookupStarted || meta.closed) return;
|
|
3473
3258
|
meta.lookupStarted = true;
|
|
3474
|
-
void this.rpc.request(
|
|
3259
|
+
void this.runtime.rpc.request(
|
|
3475
3260
|
"thread/resume",
|
|
3476
3261
|
{ threadId: agentThreadId, excludeTurns: true },
|
|
3477
3262
|
COLLAB_META_LOOKUP_TIMEOUT_MS
|
|
3478
3263
|
).then((response) => {
|
|
3479
|
-
if (this.closed || meta.closed || !isRecord3(response)) return;
|
|
3264
|
+
if (this.runtime.closed || meta.closed || !isRecord3(response)) return;
|
|
3480
3265
|
const thread = response["thread"];
|
|
3481
3266
|
if (!isRecord3(thread) || thread["id"] !== agentThreadId) return;
|
|
3482
3267
|
this.updateCollabChildMeta(
|
|
@@ -3487,180 +3272,463 @@ var CodexSessionRuntime = class {
|
|
|
3487
3272
|
}).catch(() => void 0);
|
|
3488
3273
|
}
|
|
3489
3274
|
emitCollab(method, child, extra) {
|
|
3490
|
-
const meta = this.collabChildMeta.get(child.agentThreadId);
|
|
3275
|
+
const meta = this.runtime.collabChildMeta.get(child.agentThreadId);
|
|
3491
3276
|
const params = {
|
|
3492
3277
|
agentThreadId: child.agentThreadId,
|
|
3493
3278
|
agentPath: child.agentPath,
|
|
3494
|
-
agentRole: child.role ??
|
|
3279
|
+
agentRole: child.role ?? pathLeaf(child.agentPath) ?? "general-purpose",
|
|
3495
3280
|
agentNickname: child.nickname,
|
|
3496
3281
|
...meta?.model ? { model: meta.model } : {},
|
|
3497
3282
|
...meta?.effort ? { effort: meta.effort } : {},
|
|
3498
3283
|
...isRecord3(extra) ? extra : {}
|
|
3499
3284
|
};
|
|
3500
|
-
for (const event of mapNotificationToEvents({
|
|
3501
|
-
method,
|
|
3502
|
-
params,
|
|
3503
|
-
threadId: this.threadId,
|
|
3504
|
-
providerInstanceId: this.host.instanceId
|
|
3505
|
-
})) {
|
|
3506
|
-
this.emit(event);
|
|
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 });
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
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);
|
|
3427
|
+
}
|
|
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
|
+
}
|
|
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);
|
|
3454
|
+
}
|
|
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}`);
|
|
3461
|
+
}
|
|
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
|
+
});
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
/** 会话收口时把还挂着的审批答成「取消」:应答形状按审批种类,权限审批不认 decision。 */
|
|
3493
|
+
settlePendingApprovals() {
|
|
3494
|
+
for (const [requestId, pending] of this.runtime.pendingApprovals) {
|
|
3495
|
+
pending.settle(cancelWireFor(pending));
|
|
3496
|
+
this.runtime.pendingApprovals.delete(requestId);
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
settlePendingUserInputs(result) {
|
|
3500
|
+
for (const [requestId, pending] of this.runtime.pendingUserInputs) {
|
|
3501
|
+
pending.settle(result);
|
|
3502
|
+
this.runtime.pendingUserInputs.delete(requestId);
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
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;
|
|
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);
|
|
3558
|
+
/**
|
|
3559
|
+
* 对外 requestId 的前缀,每个会话(即每个 app-server 进程)一份。JSON-RPC id 每个进程
|
|
3560
|
+
* 都从 0 数起,宿主按 requestId 记「已接受」,不加前缀的话重启后的新请求会撞上旧请求。
|
|
3561
|
+
*/
|
|
3562
|
+
#requestIdPrefix = `codex-${NodeCrypto2.randomUUID()}:`;
|
|
3563
|
+
/** JSON-RPC id → 对外 requestId。`serverRequest/resolved` 里的 id 可能是数字也可能是字符串。 */
|
|
3564
|
+
requestIdFor(jsonRpcId) {
|
|
3565
|
+
return `${this.#requestIdPrefix}${String(jsonRpcId)}`;
|
|
3566
|
+
}
|
|
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 }
|
|
3602
|
+
};
|
|
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
|
+
});
|
|
3507
3630
|
}
|
|
3631
|
+
return await this.registerWriteAck(jsonRpcId);
|
|
3508
3632
|
}
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
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
|
+
});
|
|
3518
3645
|
return;
|
|
3519
3646
|
}
|
|
3520
|
-
if (
|
|
3521
|
-
|
|
3522
|
-
await this.handleApproval(
|
|
3523
|
-
id,
|
|
3524
|
-
method,
|
|
3525
|
-
params,
|
|
3526
|
-
"file-change",
|
|
3527
|
-
described.callId ?? "file-change"
|
|
3528
|
-
);
|
|
3647
|
+
if (kind === "error") {
|
|
3648
|
+
this.host.log.error(line, { threadId: this.threadId });
|
|
3529
3649
|
return;
|
|
3530
3650
|
}
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
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);
|
|
3534
3658
|
return;
|
|
3535
3659
|
}
|
|
3536
|
-
if (
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
params,
|
|
3541
|
-
"permission",
|
|
3542
|
-
isRecord3(params) ? asString(params["itemId"]) ?? "permission" : "permission"
|
|
3543
|
-
);
|
|
3660
|
+
if (conversationId && this.memoryThreadIds.has(conversationId)) {
|
|
3661
|
+
if (method === "serverRequest" || method === "serverRequest/resolved") {
|
|
3662
|
+
this.dispatchMapped(method, params);
|
|
3663
|
+
}
|
|
3544
3664
|
return;
|
|
3545
3665
|
}
|
|
3546
|
-
if (method === "
|
|
3547
|
-
const
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
method,
|
|
3551
|
-
params,
|
|
3552
|
-
threadId: this.threadId,
|
|
3553
|
-
providerInstanceId: this.host.instanceId
|
|
3554
|
-
});
|
|
3555
|
-
if (events.length === 0) {
|
|
3556
|
-
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);
|
|
3557
3670
|
return;
|
|
3558
3671
|
}
|
|
3559
|
-
await new Promise((resolve2) => {
|
|
3560
|
-
this.pendingUserInputs.set(requestId, {
|
|
3561
|
-
jsonRpcId: id,
|
|
3562
|
-
settle: (result) => {
|
|
3563
|
-
this.pendingUserInputs.delete(requestId);
|
|
3564
|
-
void this.rpc.respond(id, result).catch((error) => this.#warnAnswerWriteFailed(method, id, error)).finally(resolve2);
|
|
3565
|
-
}
|
|
3566
|
-
});
|
|
3567
|
-
for (const event of events) this.emit(event);
|
|
3568
|
-
});
|
|
3569
|
-
return;
|
|
3570
3672
|
}
|
|
3571
|
-
|
|
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);
|
|
3572
3678
|
}
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
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
|
+
});
|
|
3578
3699
|
return;
|
|
3579
3700
|
}
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
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({
|
|
3583
3706
|
method,
|
|
3584
3707
|
params,
|
|
3585
3708
|
threadId: this.threadId,
|
|
3586
|
-
providerInstanceId: this.host.instanceId
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
this.pendingApprovals.set(requestId, {
|
|
3591
|
-
jsonRpcId: id,
|
|
3592
|
-
method,
|
|
3593
|
-
params,
|
|
3594
|
-
kind,
|
|
3595
|
-
identity,
|
|
3596
|
-
settle: (decision) => {
|
|
3597
|
-
this.pendingApprovals.delete(requestId);
|
|
3598
|
-
void this.rpc.respond(id, decision).catch((error) => this.#warnAnswerWriteFailed(method, id, error)).finally(resolve2);
|
|
3599
|
-
},
|
|
3600
|
-
release: () => {
|
|
3601
|
-
this.pendingApprovals.delete(requestId);
|
|
3602
|
-
resolve2();
|
|
3603
|
-
}
|
|
3604
|
-
});
|
|
3605
|
-
for (const event of events) this.emit(event);
|
|
3606
|
-
});
|
|
3607
|
-
}
|
|
3608
|
-
async respondToRequest(requestId, decision, reason, persist) {
|
|
3609
|
-
const pending = this.pendingApprovals.get(requestId);
|
|
3610
|
-
if (!pending) throw new Error(`Unknown pending Codex approval request: ${requestId}`);
|
|
3611
|
-
if (decision === "deny" && reason) this.declineRelay.queue(this.threadId, reason);
|
|
3612
|
-
let wire = { decision: decision === "allow" ? "accept" : "decline" };
|
|
3613
|
-
if (decision === "allow" && pending.kind === "mcp-elicitation") {
|
|
3614
|
-
wire = toMcpElicitationResponse("allow", pending.params);
|
|
3709
|
+
providerInstanceId: this.host.instanceId,
|
|
3710
|
+
rateLimits: this.rateLimits
|
|
3711
|
+
})) {
|
|
3712
|
+
this.emit(event);
|
|
3615
3713
|
}
|
|
3616
|
-
if (
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
this.grants.remember(codexApprovalGrantKey(pending.kind, pending.identity), persist);
|
|
3620
|
-
if (pending.kind !== "mcp-elicitation") {
|
|
3621
|
-
wire = { decision: wireDecisionFromPersist(pending.kind, persist) };
|
|
3622
|
-
}
|
|
3714
|
+
if (method === "turn/started") {
|
|
3715
|
+
const turnId = turnIdOf(params);
|
|
3716
|
+
if (turnId) this.activeTurnId = turnId;
|
|
3623
3717
|
}
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
payload: {
|
|
3636
|
-
requestType: toRequestTypeFromKind(pending.kind),
|
|
3637
|
-
decision,
|
|
3638
|
-
...reason ? { reason } : {}
|
|
3639
|
-
}
|
|
3640
|
-
});
|
|
3641
|
-
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);
|
|
3642
3729
|
}
|
|
3643
3730
|
async respondToUserInput(requestId, answers) {
|
|
3644
|
-
|
|
3645
|
-
if (!pending) throw new Error(`Unknown pending Codex user input request: ${requestId}`);
|
|
3646
|
-
const mapped = toCodexUserInputAnswers(answers);
|
|
3647
|
-
if (mapped === void 0) {
|
|
3648
|
-
throw new Error(`invalid user input answers for request ${requestId}`);
|
|
3649
|
-
}
|
|
3650
|
-
const ack = this.registerWriteAck(pending.jsonRpcId);
|
|
3651
|
-
pending.settle({ answers: mapped });
|
|
3652
|
-
const delivery = await ack.catch(() => "closed");
|
|
3653
|
-
this.emit({
|
|
3654
|
-
eventId: nextEventId(),
|
|
3655
|
-
provider: CODEX_DRIVER_KIND,
|
|
3656
|
-
threadId: this.threadId,
|
|
3657
|
-
createdAt: nowIso(),
|
|
3658
|
-
type: "user-input.resolved",
|
|
3659
|
-
requestId,
|
|
3660
|
-
// 契约的 answers 是 questionId → 答案 的 record,不是数组。
|
|
3661
|
-
payload: { answers: toCanonicalUserInputAnswers(mapped) }
|
|
3662
|
-
});
|
|
3663
|
-
if (delivery !== "written") throw unconfirmedAnswerError(requestId, delivery);
|
|
3731
|
+
return this.#requests.respondToUserInput(requestId, answers);
|
|
3664
3732
|
}
|
|
3665
3733
|
async withActiveWriterRecovery(run, allowUnsubscribe) {
|
|
3666
3734
|
let lastError;
|
|
@@ -3854,22 +3922,7 @@ var CodexSessionRuntime = class {
|
|
|
3854
3922
|
if (target) {
|
|
3855
3923
|
await this.rpc.request("turn/interrupt", { threadId: this.nativeThreadId, turnId: target }, 0).catch(() => void 0);
|
|
3856
3924
|
}
|
|
3857
|
-
this.cancelPendingApprovals();
|
|
3858
|
-
}
|
|
3859
|
-
/** 把还挂着的审批答成「取消」,逐条发 request.resolved。 */
|
|
3860
|
-
cancelPendingApprovals() {
|
|
3861
|
-
for (const [requestId, pending] of this.pendingApprovals) {
|
|
3862
|
-
pending.settle(cancelWireFor(pending));
|
|
3863
|
-
this.emit({
|
|
3864
|
-
eventId: nextEventId(),
|
|
3865
|
-
provider: CODEX_DRIVER_KIND,
|
|
3866
|
-
threadId: this.threadId,
|
|
3867
|
-
createdAt: nowIso(),
|
|
3868
|
-
type: "request.resolved",
|
|
3869
|
-
requestId,
|
|
3870
|
-
payload: { requestType: toRequestTypeFromKind(pending.kind), decision: "cancel" }
|
|
3871
|
-
});
|
|
3872
|
-
}
|
|
3925
|
+
this.#requests.cancelPendingApprovals();
|
|
3873
3926
|
}
|
|
3874
3927
|
async compactContext() {
|
|
3875
3928
|
if (!this.nativeThreadId) return;
|
|
@@ -3903,16 +3956,10 @@ var CodexSessionRuntime = class {
|
|
|
3903
3956
|
}
|
|
3904
3957
|
/** 会话收口时把还挂着的审批答成「取消」:应答形状按审批种类,权限审批不认 decision。 */
|
|
3905
3958
|
settlePendingApprovals() {
|
|
3906
|
-
|
|
3907
|
-
pending.settle(cancelWireFor(pending));
|
|
3908
|
-
this.pendingApprovals.delete(requestId);
|
|
3909
|
-
}
|
|
3959
|
+
return this.#requests.settlePendingApprovals();
|
|
3910
3960
|
}
|
|
3911
3961
|
settlePendingUserInputs(result) {
|
|
3912
|
-
|
|
3913
|
-
pending.settle(result);
|
|
3914
|
-
this.pendingUserInputs.delete(requestId);
|
|
3915
|
-
}
|
|
3962
|
+
return this.#requests.settlePendingUserInputs(result);
|
|
3916
3963
|
}
|
|
3917
3964
|
async close() {
|
|
3918
3965
|
if (this.closeCompleted) return;
|
|
@@ -3938,17 +3985,6 @@ var CodexSessionRuntime = class {
|
|
|
3938
3985
|
});
|
|
3939
3986
|
}
|
|
3940
3987
|
};
|
|
3941
|
-
async function markProviderRejection(run) {
|
|
3942
|
-
try {
|
|
3943
|
-
return await run();
|
|
3944
|
-
} catch (error) {
|
|
3945
|
-
if (!(error instanceof CodexRpcResponseError)) throw error;
|
|
3946
|
-
const rejected = Object.assign(new Error(error.message), {
|
|
3947
|
-
code: "provider-rejected"
|
|
3948
|
-
});
|
|
3949
|
-
throw rejected;
|
|
3950
|
-
}
|
|
3951
|
-
}
|
|
3952
3988
|
|
|
3953
3989
|
// src/sessions.ts
|
|
3954
3990
|
import * as NodeFS3 from "node:fs";
|