@rynfar/meridian 1.60.0 → 1.62.0
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/README.md +3 -1
- package/dist/{cli-k1djafvr.js → cli-k36dddkm.js} +998 -282
- package/dist/cli.js +1 -1
- package/dist/{profilePage-naychnb8.js → profilePage-gtazq15d.js} +9 -0
- package/dist/proxy/adapters/detect.d.ts.map +1 -1
- package/dist/proxy/adapters/jcode.d.ts +13 -0
- package/dist/proxy/adapters/jcode.d.ts.map +1 -0
- package/dist/proxy/adapters/prime.d.ts +99 -0
- package/dist/proxy/adapters/prime.d.ts.map +1 -0
- package/dist/proxy/errors.d.ts +14 -0
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/messages.d.ts +28 -0
- package/dist/proxy/messages.d.ts.map +1 -1
- package/dist/proxy/oauthUsage.d.ts +34 -1
- package/dist/proxy/oauthUsage.d.ts.map +1 -1
- package/dist/proxy/openai.d.ts +5 -1
- package/dist/proxy/openai.d.ts.map +1 -1
- package/dist/proxy/passthroughEarlyStop.d.ts +48 -0
- package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
- package/dist/proxy/query.d.ts +3 -2
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/sdkFeatures.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/session/cache.d.ts +1 -1
- package/dist/proxy/session/cache.d.ts.map +1 -1
- package/dist/proxy/session/lineage.d.ts +70 -0
- package/dist/proxy/session/lineage.d.ts.map +1 -1
- package/dist/proxy/sessionStore.d.ts +5 -1
- package/dist/proxy/sessionStore.d.ts.map +1 -1
- package/dist/proxy/transform.d.ts +33 -0
- package/dist/proxy/transform.d.ts.map +1 -1
- package/dist/proxy/transforms/prime.d.ts +3 -0
- package/dist/proxy/transforms/prime.d.ts.map +1 -0
- package/dist/proxy/transforms/registry.d.ts.map +1 -1
- package/dist/proxy/turnOutcome.d.ts +179 -0
- package/dist/proxy/turnOutcome.d.ts.map +1 -0
- package/dist/server.js +1 -1
- package/dist/telemetry/landing.d.ts.map +1 -1
- package/dist/telemetry/profilePage.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1752,6 +1752,133 @@ var init_sqlite = __esm(() => {
|
|
|
1752
1752
|
];
|
|
1753
1753
|
});
|
|
1754
1754
|
|
|
1755
|
+
// src/proxy/fileChanges.ts
|
|
1756
|
+
function extractFileChange(toolName, toolInput, mcpPrefix) {
|
|
1757
|
+
if (!toolName.startsWith(mcpPrefix))
|
|
1758
|
+
return;
|
|
1759
|
+
const shortName = toolName.slice(mcpPrefix.length);
|
|
1760
|
+
const input = toolInput;
|
|
1761
|
+
if (shortName === "write" && input?.path) {
|
|
1762
|
+
return { operation: "wrote", path: String(input.path) };
|
|
1763
|
+
}
|
|
1764
|
+
if (shortName === "edit" && input?.path) {
|
|
1765
|
+
return { operation: "edited", path: String(input.path) };
|
|
1766
|
+
}
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
function createFileChangeHook(changes, mcpPrefix) {
|
|
1770
|
+
return {
|
|
1771
|
+
matcher: "",
|
|
1772
|
+
hooks: [async (input) => {
|
|
1773
|
+
const change = extractFileChange(input.tool_name, input.tool_input, mcpPrefix);
|
|
1774
|
+
if (change) {
|
|
1775
|
+
changes.push(change);
|
|
1776
|
+
return {};
|
|
1777
|
+
}
|
|
1778
|
+
if (input.tool_name === `${mcpPrefix}bash`) {
|
|
1779
|
+
const toolInput = input.tool_input;
|
|
1780
|
+
if (toolInput?.command) {
|
|
1781
|
+
const bashChanges = extractFileChangesFromBash(String(toolInput.command));
|
|
1782
|
+
changes.push(...bashChanges);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
return {};
|
|
1786
|
+
}]
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
function isLikelyFilePath(s) {
|
|
1790
|
+
if (s.length > 4096)
|
|
1791
|
+
return false;
|
|
1792
|
+
if (/[<>$`\\()[\]{}=]/.test(s))
|
|
1793
|
+
return false;
|
|
1794
|
+
if (/^-?\d+$/.test(s))
|
|
1795
|
+
return false;
|
|
1796
|
+
if (s.includes("/"))
|
|
1797
|
+
return true;
|
|
1798
|
+
if (/\.[A-Za-z0-9]{1,16}$/.test(s))
|
|
1799
|
+
return true;
|
|
1800
|
+
return false;
|
|
1801
|
+
}
|
|
1802
|
+
function extractFileChangesFromBash(command) {
|
|
1803
|
+
const changes = [];
|
|
1804
|
+
const seen = new Set;
|
|
1805
|
+
const addChange = (operation, path) => {
|
|
1806
|
+
if (path === "/dev/null" || path === "/dev/stderr" || path === "/dev/stdout")
|
|
1807
|
+
return;
|
|
1808
|
+
if (!path.trim())
|
|
1809
|
+
return;
|
|
1810
|
+
const key = `${operation}:${path}`;
|
|
1811
|
+
if (!seen.has(key)) {
|
|
1812
|
+
seen.add(key);
|
|
1813
|
+
changes.push({ operation, path });
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
const redirectRegex = /(?<![0-9=])>{1,2}\s*['"]?([^\s'";&|)]+)['"]?/g;
|
|
1817
|
+
let match2;
|
|
1818
|
+
while ((match2 = redirectRegex.exec(command)) !== null) {
|
|
1819
|
+
if (isLikelyFilePath(match2[1])) {
|
|
1820
|
+
addChange("wrote", match2[1]);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
const teeRegex = /\btee\s+(?:-[a-zA-Z]\s+)*['"]?([^\s'";&|)]+)['"]?/g;
|
|
1824
|
+
while ((match2 = teeRegex.exec(command)) !== null) {
|
|
1825
|
+
addChange("wrote", match2[1]);
|
|
1826
|
+
}
|
|
1827
|
+
const sedRegex = /\bsed\s+(?:-[a-zA-Z]*i[a-zA-Z]*|-i)\b.*?['"]?([^\s'";&|)]+)['"]?\s*$/gm;
|
|
1828
|
+
while ((match2 = sedRegex.exec(command)) !== null) {
|
|
1829
|
+
addChange("edited", match2[1]);
|
|
1830
|
+
}
|
|
1831
|
+
return changes;
|
|
1832
|
+
}
|
|
1833
|
+
function extractFileChangesFromMessages(messages, extractFn) {
|
|
1834
|
+
const changes = [];
|
|
1835
|
+
const executedToolIds = new Set;
|
|
1836
|
+
for (const msg of messages) {
|
|
1837
|
+
if (msg.role !== "user")
|
|
1838
|
+
continue;
|
|
1839
|
+
const content = Array.isArray(msg.content) ? msg.content : [];
|
|
1840
|
+
for (const block of content) {
|
|
1841
|
+
if (block?.type === "tool_result" && block.tool_use_id) {
|
|
1842
|
+
executedToolIds.add(block.tool_use_id);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
for (const msg of messages) {
|
|
1847
|
+
if (msg.role !== "assistant")
|
|
1848
|
+
continue;
|
|
1849
|
+
const content = Array.isArray(msg.content) ? msg.content : [];
|
|
1850
|
+
for (const block of content) {
|
|
1851
|
+
if (block?.type !== "tool_use")
|
|
1852
|
+
continue;
|
|
1853
|
+
if (!executedToolIds.has(block.id))
|
|
1854
|
+
continue;
|
|
1855
|
+
const blockChanges = extractFn(block.name, block.input);
|
|
1856
|
+
changes.push(...blockChanges);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
return changes;
|
|
1860
|
+
}
|
|
1861
|
+
function formatFileChangeSummary(changes) {
|
|
1862
|
+
if (changes.length === 0)
|
|
1863
|
+
return;
|
|
1864
|
+
const seen = new Set;
|
|
1865
|
+
const unique = [];
|
|
1866
|
+
for (const c of changes) {
|
|
1867
|
+
const key = `${c.operation}:${c.path}`;
|
|
1868
|
+
if (!seen.has(key)) {
|
|
1869
|
+
seen.add(key);
|
|
1870
|
+
unique.push(c);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
const lines = unique.map((c) => `- ${c.operation} ${c.path}`);
|
|
1874
|
+
return `
|
|
1875
|
+
|
|
1876
|
+
Files changed:
|
|
1877
|
+
${lines.join(`
|
|
1878
|
+
`)}`;
|
|
1879
|
+
}
|
|
1880
|
+
var init_fileChanges = () => {};
|
|
1881
|
+
|
|
1755
1882
|
// src/proxy/messages.ts
|
|
1756
1883
|
function stripCacheControlForHashing(obj) {
|
|
1757
1884
|
if (!obj || typeof obj !== "object")
|
|
@@ -1831,6 +1958,13 @@ ${history}
|
|
|
1831
1958
|
|
|
1832
1959
|
` + last.text;
|
|
1833
1960
|
}
|
|
1961
|
+
function framePassthroughContinuation(delta) {
|
|
1962
|
+
if (!delta)
|
|
1963
|
+
return delta;
|
|
1964
|
+
return `${PASSTHROUGH_CONTINUATION_LEAD_IN}
|
|
1965
|
+
|
|
1966
|
+
${delta}`;
|
|
1967
|
+
}
|
|
1834
1968
|
function stripNonStandardStreamFields(event) {
|
|
1835
1969
|
if (event && typeof event === "object") {
|
|
1836
1970
|
const e = event;
|
|
@@ -1886,6 +2020,12 @@ function consolidateMultimodalOntoLastUser(structured) {
|
|
|
1886
2020
|
};
|
|
1887
2021
|
return result;
|
|
1888
2022
|
}
|
|
2023
|
+
function normalizeTarget(value) {
|
|
2024
|
+
const collapsed = value.replace(/\s+/g, " ").trim();
|
|
2025
|
+
if (!collapsed)
|
|
2026
|
+
return;
|
|
2027
|
+
return collapsed.length > TOOL_TARGET_MAX ? collapsed.slice(0, TOOL_TARGET_MAX - 3) + "..." : collapsed;
|
|
2028
|
+
}
|
|
1889
2029
|
function summarizeContent(value) {
|
|
1890
2030
|
if (typeof value !== "string" || value.length === 0)
|
|
1891
2031
|
return;
|
|
@@ -1894,13 +2034,18 @@ function summarizeContent(value) {
|
|
|
1894
2034
|
return;
|
|
1895
2035
|
return collapsed.length > CONTENT_SUMMARY_MAX ? collapsed.slice(0, CONTENT_SUMMARY_MAX) + "..." : collapsed;
|
|
1896
2036
|
}
|
|
2037
|
+
function editReplacementText(rec) {
|
|
2038
|
+
if (!rec)
|
|
2039
|
+
return;
|
|
2040
|
+
return rec.new_string ?? rec.newString ?? rec.new_text ?? rec.newText;
|
|
2041
|
+
}
|
|
1897
2042
|
function extractContentSummary(name, input) {
|
|
1898
2043
|
if (!input || typeof input !== "object")
|
|
1899
2044
|
return;
|
|
1900
2045
|
const rec = input;
|
|
1901
2046
|
switch (name.toLowerCase()) {
|
|
1902
2047
|
case "edit":
|
|
1903
|
-
return summarizeContent(rec
|
|
2048
|
+
return summarizeContent(editReplacementText(rec));
|
|
1904
2049
|
case "write":
|
|
1905
2050
|
return summarizeContent(rec.content);
|
|
1906
2051
|
case "multiedit": {
|
|
@@ -1908,7 +2053,7 @@ function extractContentSummary(name, input) {
|
|
|
1908
2053
|
if (!Array.isArray(edits) || edits.length === 0)
|
|
1909
2054
|
return;
|
|
1910
2055
|
const first = edits[0];
|
|
1911
|
-
const firstSummary = summarizeContent(first
|
|
2056
|
+
const firstSummary = summarizeContent(editReplacementText(first));
|
|
1912
2057
|
if (!firstSummary)
|
|
1913
2058
|
return;
|
|
1914
2059
|
return edits.length > 1 ? `${edits.length} edits; first: ${firstSummary}` : firstSummary;
|
|
@@ -1931,8 +2076,9 @@ function buildToolUseIndex(messages) {
|
|
|
1931
2076
|
for (const key of TOOL_TARGET_KEYS) {
|
|
1932
2077
|
const v = input[key];
|
|
1933
2078
|
if (typeof v === "string" && v) {
|
|
1934
|
-
target = v
|
|
1935
|
-
|
|
2079
|
+
target = normalizeTarget(v);
|
|
2080
|
+
if (target)
|
|
2081
|
+
break;
|
|
1936
2082
|
}
|
|
1937
2083
|
}
|
|
1938
2084
|
}
|
|
@@ -1957,7 +2103,7 @@ function extractSystemText(system) {
|
|
|
1957
2103
|
return system.filter((b) => b?.type === "text" && typeof b.text === "string" && b.text).map((b) => b.text).filter((text) => !TRANSPORT_HEADER_BLOCK.test(text)).join(`
|
|
1958
2104
|
`);
|
|
1959
2105
|
}
|
|
1960
|
-
var HASH_IGNORED_BLOCK_TYPES, HASH_HANDLED_BLOCK_TYPES, HASH_SERIALIZED_BLOCK_TYPES, MULTIMODAL_TYPES, TOOL_TARGET_KEYS, CONTENT_SUMMARY_MAX = 120, TRANSPORT_HEADER_BLOCK;
|
|
2106
|
+
var HASH_IGNORED_BLOCK_TYPES, HASH_HANDLED_BLOCK_TYPES, HASH_SERIALIZED_BLOCK_TYPES, PASSTHROUGH_CONTINUATION_LEAD_IN, MULTIMODAL_TYPES, TOOL_TARGET_KEYS, TOOL_TARGET_MAX = 80, CONTENT_SUMMARY_MAX = 120, TRANSPORT_HEADER_BLOCK;
|
|
1961
2107
|
var init_messages = __esm(() => {
|
|
1962
2108
|
HASH_IGNORED_BLOCK_TYPES = new Set(["thinking", "redacted_thinking"]);
|
|
1963
2109
|
HASH_HANDLED_BLOCK_TYPES = new Set(["text", "tool_use", "tool_result"]);
|
|
@@ -1974,138 +2120,12 @@ var init_messages = __esm(() => {
|
|
|
1974
2120
|
"tool_search_tool_result",
|
|
1975
2121
|
"container_upload"
|
|
1976
2122
|
]);
|
|
2123
|
+
PASSTHROUGH_CONTINUATION_LEAD_IN = "The tool calls from your previous turn were forwarded to the client, which has now executed them — " + "their results follow. The instruction to end that turn without further text applied to it alone and " + "is now discharged: continue the work and respond.";
|
|
1977
2124
|
MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
|
|
1978
|
-
TOOL_TARGET_KEYS = ["filePath", "file_path", "path", "command", "pattern", "query", "url"];
|
|
2125
|
+
TOOL_TARGET_KEYS = ["filePath", "file_path", "path", "command", "code", "pattern", "query", "url"];
|
|
1979
2126
|
TRANSPORT_HEADER_BLOCK = /^\s*x-anthropic-[a-z0-9-]*header\s*:/i;
|
|
1980
2127
|
});
|
|
1981
2128
|
|
|
1982
|
-
// src/proxy/fileChanges.ts
|
|
1983
|
-
function extractFileChange(toolName, toolInput, mcpPrefix) {
|
|
1984
|
-
if (!toolName.startsWith(mcpPrefix))
|
|
1985
|
-
return;
|
|
1986
|
-
const shortName = toolName.slice(mcpPrefix.length);
|
|
1987
|
-
const input = toolInput;
|
|
1988
|
-
if (shortName === "write" && input?.path) {
|
|
1989
|
-
return { operation: "wrote", path: String(input.path) };
|
|
1990
|
-
}
|
|
1991
|
-
if (shortName === "edit" && input?.path) {
|
|
1992
|
-
return { operation: "edited", path: String(input.path) };
|
|
1993
|
-
}
|
|
1994
|
-
return;
|
|
1995
|
-
}
|
|
1996
|
-
function createFileChangeHook(changes, mcpPrefix) {
|
|
1997
|
-
return {
|
|
1998
|
-
matcher: "",
|
|
1999
|
-
hooks: [async (input) => {
|
|
2000
|
-
const change = extractFileChange(input.tool_name, input.tool_input, mcpPrefix);
|
|
2001
|
-
if (change) {
|
|
2002
|
-
changes.push(change);
|
|
2003
|
-
return {};
|
|
2004
|
-
}
|
|
2005
|
-
if (input.tool_name === `${mcpPrefix}bash`) {
|
|
2006
|
-
const toolInput = input.tool_input;
|
|
2007
|
-
if (toolInput?.command) {
|
|
2008
|
-
const bashChanges = extractFileChangesFromBash(String(toolInput.command));
|
|
2009
|
-
changes.push(...bashChanges);
|
|
2010
|
-
}
|
|
2011
|
-
}
|
|
2012
|
-
return {};
|
|
2013
|
-
}]
|
|
2014
|
-
};
|
|
2015
|
-
}
|
|
2016
|
-
function isLikelyFilePath(s) {
|
|
2017
|
-
if (s.length > 4096)
|
|
2018
|
-
return false;
|
|
2019
|
-
if (/[<>$`\\()[\]{}=]/.test(s))
|
|
2020
|
-
return false;
|
|
2021
|
-
if (/^-?\d+$/.test(s))
|
|
2022
|
-
return false;
|
|
2023
|
-
if (s.includes("/"))
|
|
2024
|
-
return true;
|
|
2025
|
-
if (/\.[A-Za-z0-9]{1,16}$/.test(s))
|
|
2026
|
-
return true;
|
|
2027
|
-
return false;
|
|
2028
|
-
}
|
|
2029
|
-
function extractFileChangesFromBash(command) {
|
|
2030
|
-
const changes = [];
|
|
2031
|
-
const seen = new Set;
|
|
2032
|
-
const addChange = (operation, path) => {
|
|
2033
|
-
if (path === "/dev/null" || path === "/dev/stderr" || path === "/dev/stdout")
|
|
2034
|
-
return;
|
|
2035
|
-
if (!path.trim())
|
|
2036
|
-
return;
|
|
2037
|
-
const key = `${operation}:${path}`;
|
|
2038
|
-
if (!seen.has(key)) {
|
|
2039
|
-
seen.add(key);
|
|
2040
|
-
changes.push({ operation, path });
|
|
2041
|
-
}
|
|
2042
|
-
};
|
|
2043
|
-
const redirectRegex = /(?<![0-9=])>{1,2}\s*['"]?([^\s'";&|)]+)['"]?/g;
|
|
2044
|
-
let match2;
|
|
2045
|
-
while ((match2 = redirectRegex.exec(command)) !== null) {
|
|
2046
|
-
if (isLikelyFilePath(match2[1])) {
|
|
2047
|
-
addChange("wrote", match2[1]);
|
|
2048
|
-
}
|
|
2049
|
-
}
|
|
2050
|
-
const teeRegex = /\btee\s+(?:-[a-zA-Z]\s+)*['"]?([^\s'";&|)]+)['"]?/g;
|
|
2051
|
-
while ((match2 = teeRegex.exec(command)) !== null) {
|
|
2052
|
-
addChange("wrote", match2[1]);
|
|
2053
|
-
}
|
|
2054
|
-
const sedRegex = /\bsed\s+(?:-[a-zA-Z]*i[a-zA-Z]*|-i)\b.*?['"]?([^\s'";&|)]+)['"]?\s*$/gm;
|
|
2055
|
-
while ((match2 = sedRegex.exec(command)) !== null) {
|
|
2056
|
-
addChange("edited", match2[1]);
|
|
2057
|
-
}
|
|
2058
|
-
return changes;
|
|
2059
|
-
}
|
|
2060
|
-
function extractFileChangesFromMessages(messages, extractFn) {
|
|
2061
|
-
const changes = [];
|
|
2062
|
-
const executedToolIds = new Set;
|
|
2063
|
-
for (const msg of messages) {
|
|
2064
|
-
if (msg.role !== "user")
|
|
2065
|
-
continue;
|
|
2066
|
-
const content = Array.isArray(msg.content) ? msg.content : [];
|
|
2067
|
-
for (const block of content) {
|
|
2068
|
-
if (block?.type === "tool_result" && block.tool_use_id) {
|
|
2069
|
-
executedToolIds.add(block.tool_use_id);
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
}
|
|
2073
|
-
for (const msg of messages) {
|
|
2074
|
-
if (msg.role !== "assistant")
|
|
2075
|
-
continue;
|
|
2076
|
-
const content = Array.isArray(msg.content) ? msg.content : [];
|
|
2077
|
-
for (const block of content) {
|
|
2078
|
-
if (block?.type !== "tool_use")
|
|
2079
|
-
continue;
|
|
2080
|
-
if (!executedToolIds.has(block.id))
|
|
2081
|
-
continue;
|
|
2082
|
-
const blockChanges = extractFn(block.name, block.input);
|
|
2083
|
-
changes.push(...blockChanges);
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
return changes;
|
|
2087
|
-
}
|
|
2088
|
-
function formatFileChangeSummary(changes) {
|
|
2089
|
-
if (changes.length === 0)
|
|
2090
|
-
return;
|
|
2091
|
-
const seen = new Set;
|
|
2092
|
-
const unique = [];
|
|
2093
|
-
for (const c of changes) {
|
|
2094
|
-
const key = `${c.operation}:${c.path}`;
|
|
2095
|
-
if (!seen.has(key)) {
|
|
2096
|
-
seen.add(key);
|
|
2097
|
-
unique.push(c);
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
const lines = unique.map((c) => `- ${c.operation} ${c.path}`);
|
|
2101
|
-
return `
|
|
2102
|
-
|
|
2103
|
-
Files changed:
|
|
2104
|
-
${lines.join(`
|
|
2105
|
-
`)}`;
|
|
2106
|
-
}
|
|
2107
|
-
var init_fileChanges = () => {};
|
|
2108
|
-
|
|
2109
2129
|
// src/proxy/session/fingerprint.ts
|
|
2110
2130
|
import { createHash } from "crypto";
|
|
2111
2131
|
function extractClientCwd(body) {
|
|
@@ -2151,7 +2171,7 @@ var init_opencode = __esm(() => {
|
|
|
2151
2171
|
openCodeTransforms = [
|
|
2152
2172
|
{
|
|
2153
2173
|
name: "opencode-core",
|
|
2154
|
-
adapters: ["opencode", "openai", "codex"],
|
|
2174
|
+
adapters: ["opencode", "openai", "jcode", "codex"],
|
|
2155
2175
|
onRequest(ctx) {
|
|
2156
2176
|
const body = ctx.body;
|
|
2157
2177
|
const blockedTools = BLOCKED_BUILTIN_TOOLS;
|
|
@@ -2325,6 +2345,51 @@ IMPORTANT: When using the task/Task tool, the subagent_type parameter must be on
|
|
|
2325
2345
|
};
|
|
2326
2346
|
});
|
|
2327
2347
|
|
|
2348
|
+
// src/proxy/adapters/openai.ts
|
|
2349
|
+
var openAiAdapter;
|
|
2350
|
+
var init_openai = __esm(() => {
|
|
2351
|
+
init_opencode2();
|
|
2352
|
+
openAiAdapter = {
|
|
2353
|
+
...openCodeAdapter,
|
|
2354
|
+
name: "openai"
|
|
2355
|
+
};
|
|
2356
|
+
});
|
|
2357
|
+
|
|
2358
|
+
// src/proxy/adapters/jcode.ts
|
|
2359
|
+
function normalizeJcodeSessionId(value) {
|
|
2360
|
+
const trimmed = value?.trim();
|
|
2361
|
+
return trimmed && JCODE_SESSION_ID.test(trimmed) ? trimmed : undefined;
|
|
2362
|
+
}
|
|
2363
|
+
function isTextSystemBlock(value) {
|
|
2364
|
+
if (value === null || typeof value !== "object")
|
|
2365
|
+
return false;
|
|
2366
|
+
const part = value;
|
|
2367
|
+
return part.type === "text" && typeof part.text === "string";
|
|
2368
|
+
}
|
|
2369
|
+
function extractJcodeWorkingDirectory(body) {
|
|
2370
|
+
if (body === null || typeof body !== "object")
|
|
2371
|
+
return;
|
|
2372
|
+
const system = body.system;
|
|
2373
|
+
const text = typeof system === "string" ? system : Array.isArray(system) ? system.filter(isTextSystemBlock).map((part) => part.text).join(`
|
|
2374
|
+
`) : "";
|
|
2375
|
+
return text.match(/(?:^|\n)Working directory:[^\S\n]*([^\n]+)/)?.[1]?.trim() || undefined;
|
|
2376
|
+
}
|
|
2377
|
+
var JCODE_SESSION_ID, jcodeAdapter;
|
|
2378
|
+
var init_jcode = __esm(() => {
|
|
2379
|
+
init_openai();
|
|
2380
|
+
JCODE_SESSION_ID = /^[A-Za-z0-9._:-]{1,256}$/;
|
|
2381
|
+
jcodeAdapter = {
|
|
2382
|
+
...openAiAdapter,
|
|
2383
|
+
name: "jcode",
|
|
2384
|
+
getSessionId(c) {
|
|
2385
|
+
return normalizeJcodeSessionId(c.req.header("x-jcode-session"));
|
|
2386
|
+
},
|
|
2387
|
+
extractWorkingDirectory(body) {
|
|
2388
|
+
return extractJcodeWorkingDirectory(body);
|
|
2389
|
+
}
|
|
2390
|
+
};
|
|
2391
|
+
});
|
|
2392
|
+
|
|
2328
2393
|
// src/proxy/transforms/droid.ts
|
|
2329
2394
|
function resolveDroidPassthrough() {
|
|
2330
2395
|
return resolvePassthrough(false);
|
|
@@ -2815,32 +2880,161 @@ function extractPiCwd(body) {
|
|
|
2815
2880
|
const match2 = systemText.match(/Current working directory:\s*([^\n]+)/i);
|
|
2816
2881
|
return match2?.[1]?.trim() || undefined;
|
|
2817
2882
|
}
|
|
2818
|
-
var PI_MCP_SERVER_NAME2 = "pi", PI_ALLOWED_MCP_TOOLS2, piAdapter;
|
|
2819
|
-
var init_pi2 = __esm(() => {
|
|
2883
|
+
var PI_MCP_SERVER_NAME2 = "pi", PI_ALLOWED_MCP_TOOLS2, piAdapter;
|
|
2884
|
+
var init_pi2 = __esm(() => {
|
|
2885
|
+
init_fileChanges();
|
|
2886
|
+
init_messages();
|
|
2887
|
+
init_tools();
|
|
2888
|
+
init_env();
|
|
2889
|
+
init_claudecode();
|
|
2890
|
+
init_pi();
|
|
2891
|
+
PI_ALLOWED_MCP_TOOLS2 = [
|
|
2892
|
+
`mcp__${PI_MCP_SERVER_NAME2}__read`,
|
|
2893
|
+
`mcp__${PI_MCP_SERVER_NAME2}__write`,
|
|
2894
|
+
`mcp__${PI_MCP_SERVER_NAME2}__edit`,
|
|
2895
|
+
`mcp__${PI_MCP_SERVER_NAME2}__bash`,
|
|
2896
|
+
`mcp__${PI_MCP_SERVER_NAME2}__glob`,
|
|
2897
|
+
`mcp__${PI_MCP_SERVER_NAME2}__grep`
|
|
2898
|
+
];
|
|
2899
|
+
piAdapter = {
|
|
2900
|
+
name: "pi",
|
|
2901
|
+
getSessionId(c, body) {
|
|
2902
|
+
return c.req.header("x-session-affinity") ?? extractClaudeCodeSessionId(body);
|
|
2903
|
+
},
|
|
2904
|
+
extractWorkingDirectory(body) {
|
|
2905
|
+
return extractPiCwd(body);
|
|
2906
|
+
},
|
|
2907
|
+
extractClientWorkingDirectory(body) {
|
|
2908
|
+
return extractPiCwd(body);
|
|
2909
|
+
},
|
|
2910
|
+
normalizeContent(content) {
|
|
2911
|
+
return normalizeContent(content);
|
|
2912
|
+
},
|
|
2913
|
+
getBlockedBuiltinTools() {
|
|
2914
|
+
return BLOCKED_BUILTIN_TOOLS;
|
|
2915
|
+
},
|
|
2916
|
+
getAgentIncompatibleTools() {
|
|
2917
|
+
return CLAUDE_CODE_ONLY_TOOLS;
|
|
2918
|
+
},
|
|
2919
|
+
getMcpServerName() {
|
|
2920
|
+
return PI_MCP_SERVER_NAME2;
|
|
2921
|
+
},
|
|
2922
|
+
getAllowedMcpTools() {
|
|
2923
|
+
return PI_ALLOWED_MCP_TOOLS2;
|
|
2924
|
+
},
|
|
2925
|
+
buildSdkAgents(_body, _mcpToolNames) {
|
|
2926
|
+
return {};
|
|
2927
|
+
},
|
|
2928
|
+
supportsThinking() {
|
|
2929
|
+
return true;
|
|
2930
|
+
},
|
|
2931
|
+
buildSdkHooks(_body, _sdkAgents) {
|
|
2932
|
+
return;
|
|
2933
|
+
},
|
|
2934
|
+
buildSystemContextAddendum(_body, _sdkAgents) {
|
|
2935
|
+
return "";
|
|
2936
|
+
},
|
|
2937
|
+
usesPassthrough() {
|
|
2938
|
+
return resolvePassthrough(true);
|
|
2939
|
+
},
|
|
2940
|
+
extractFileChangesFromToolUse(toolName, toolInput) {
|
|
2941
|
+
const input = toolInput;
|
|
2942
|
+
const filePath = input?.filePath ?? input?.file_path ?? input?.path;
|
|
2943
|
+
if (toolName === "write" && filePath) {
|
|
2944
|
+
return [{ operation: "wrote", path: String(filePath) }];
|
|
2945
|
+
}
|
|
2946
|
+
if (toolName === "edit" && filePath) {
|
|
2947
|
+
return [{ operation: "edited", path: String(filePath) }];
|
|
2948
|
+
}
|
|
2949
|
+
if (toolName === "bash" && input?.command) {
|
|
2950
|
+
return extractFileChangesFromBash(String(input.command));
|
|
2951
|
+
}
|
|
2952
|
+
return [];
|
|
2953
|
+
}
|
|
2954
|
+
};
|
|
2955
|
+
});
|
|
2956
|
+
|
|
2957
|
+
// src/proxy/adapters/prime.ts
|
|
2958
|
+
function extractPrimeCwd(body) {
|
|
2959
|
+
let systemText = "";
|
|
2960
|
+
if (typeof body?.system === "string") {
|
|
2961
|
+
systemText = body.system;
|
|
2962
|
+
} else if (Array.isArray(body?.system)) {
|
|
2963
|
+
systemText = body.system.filter((b) => b?.type === "text" && b.text).map((b) => b.text).join(`
|
|
2964
|
+
`);
|
|
2965
|
+
}
|
|
2966
|
+
if (!systemText)
|
|
2967
|
+
return;
|
|
2968
|
+
const match2 = systemText.match(RLM_CWD_LINE) ?? systemText.match(CUSTOM_PROMPT_CWD_LINE);
|
|
2969
|
+
return match2?.[1]?.trim() || undefined;
|
|
2970
|
+
}
|
|
2971
|
+
function extractFileChangesFromIpythonCell(code) {
|
|
2972
|
+
const lines = code.split(`
|
|
2973
|
+
`);
|
|
2974
|
+
const firstMeaningful = lines.find((l) => l.trim().length > 0) ?? "";
|
|
2975
|
+
if (/^[ \t]*%%bash\b/.test(firstMeaningful)) {
|
|
2976
|
+
const bodyStart = lines.indexOf(firstMeaningful) + 1;
|
|
2977
|
+
return extractFileChangesFromBash(lines.slice(bodyStart).join(`
|
|
2978
|
+
`));
|
|
2979
|
+
}
|
|
2980
|
+
const changes = [];
|
|
2981
|
+
for (const line of lines) {
|
|
2982
|
+
const escaped = line.match(SHELL_ESCAPE_LINE);
|
|
2983
|
+
if (escaped?.[1])
|
|
2984
|
+
changes.push(...extractFileChangesFromBash(escaped[1]));
|
|
2985
|
+
}
|
|
2986
|
+
for (const m of code.matchAll(EDIT_SKILL_CALL)) {
|
|
2987
|
+
if (m[2])
|
|
2988
|
+
changes.push({ operation: "edited", path: m[2] });
|
|
2989
|
+
}
|
|
2990
|
+
return changes;
|
|
2991
|
+
}
|
|
2992
|
+
function extractPrimeFileChanges(toolName, toolInput) {
|
|
2993
|
+
const input = toolInput;
|
|
2994
|
+
if (toolName === "ipython" && typeof input?.code === "string") {
|
|
2995
|
+
return extractFileChangesFromIpythonCell(input.code);
|
|
2996
|
+
}
|
|
2997
|
+
const filePath = input?.path ?? input?.file_path ?? input?.filePath;
|
|
2998
|
+
if (toolName === "edit" && filePath) {
|
|
2999
|
+
return [{ operation: "edited", path: String(filePath) }];
|
|
3000
|
+
}
|
|
3001
|
+
if (toolName === "write" && filePath) {
|
|
3002
|
+
return [{ operation: "wrote", path: String(filePath) }];
|
|
3003
|
+
}
|
|
3004
|
+
if (toolName === "bash" && input?.command) {
|
|
3005
|
+
return extractFileChangesFromBash(String(input.command));
|
|
3006
|
+
}
|
|
3007
|
+
return [];
|
|
3008
|
+
}
|
|
3009
|
+
var PRIME_MCP_SERVER_NAME = "prime", PRIME_ALLOWED_MCP_TOOLS, RLM_CWD_LINE, CUSTOM_PROMPT_CWD_LINE, EDIT_SKILL_CALL, SHELL_ESCAPE_LINE, primeAdapter;
|
|
3010
|
+
var init_prime = __esm(() => {
|
|
2820
3011
|
init_fileChanges();
|
|
2821
3012
|
init_messages();
|
|
2822
3013
|
init_tools();
|
|
2823
3014
|
init_env();
|
|
2824
3015
|
init_claudecode();
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
`mcp__${
|
|
2828
|
-
`mcp__${
|
|
2829
|
-
`mcp__${
|
|
2830
|
-
`mcp__${
|
|
2831
|
-
`mcp__${
|
|
2832
|
-
`mcp__${PI_MCP_SERVER_NAME2}__grep`
|
|
3016
|
+
PRIME_ALLOWED_MCP_TOOLS = [
|
|
3017
|
+
`mcp__${PRIME_MCP_SERVER_NAME}__read`,
|
|
3018
|
+
`mcp__${PRIME_MCP_SERVER_NAME}__write`,
|
|
3019
|
+
`mcp__${PRIME_MCP_SERVER_NAME}__edit`,
|
|
3020
|
+
`mcp__${PRIME_MCP_SERVER_NAME}__bash`,
|
|
3021
|
+
`mcp__${PRIME_MCP_SERVER_NAME}__glob`,
|
|
3022
|
+
`mcp__${PRIME_MCP_SERVER_NAME}__grep`
|
|
2833
3023
|
];
|
|
2834
|
-
|
|
2835
|
-
|
|
3024
|
+
RLM_CWD_LINE = /^Working directory:[ \t]*(.+)$/m;
|
|
3025
|
+
CUSTOM_PROMPT_CWD_LINE = /^Current working directory:[ \t]*(.+)$/m;
|
|
3026
|
+
EDIT_SKILL_CALL = /\bedit\s*\(\s*path\s*=\s*(['"])(.+?)\1/g;
|
|
3027
|
+
SHELL_ESCAPE_LINE = /^[ \t]*!(.+)$/;
|
|
3028
|
+
primeAdapter = {
|
|
3029
|
+
name: "prime",
|
|
2836
3030
|
getSessionId(c, body) {
|
|
2837
3031
|
return c.req.header("x-session-affinity") ?? extractClaudeCodeSessionId(body);
|
|
2838
3032
|
},
|
|
2839
3033
|
extractWorkingDirectory(body) {
|
|
2840
|
-
return
|
|
3034
|
+
return extractPrimeCwd(body);
|
|
2841
3035
|
},
|
|
2842
3036
|
extractClientWorkingDirectory(body) {
|
|
2843
|
-
return
|
|
3037
|
+
return extractPrimeCwd(body);
|
|
2844
3038
|
},
|
|
2845
3039
|
normalizeContent(content) {
|
|
2846
3040
|
return normalizeContent(content);
|
|
@@ -2852,10 +3046,10 @@ var init_pi2 = __esm(() => {
|
|
|
2852
3046
|
return CLAUDE_CODE_ONLY_TOOLS;
|
|
2853
3047
|
},
|
|
2854
3048
|
getMcpServerName() {
|
|
2855
|
-
return
|
|
3049
|
+
return PRIME_MCP_SERVER_NAME;
|
|
2856
3050
|
},
|
|
2857
3051
|
getAllowedMcpTools() {
|
|
2858
|
-
return
|
|
3052
|
+
return PRIME_ALLOWED_MCP_TOOLS;
|
|
2859
3053
|
},
|
|
2860
3054
|
buildSdkAgents(_body, _mcpToolNames) {
|
|
2861
3055
|
return {};
|
|
@@ -2873,18 +3067,7 @@ var init_pi2 = __esm(() => {
|
|
|
2873
3067
|
return resolvePassthrough(true);
|
|
2874
3068
|
},
|
|
2875
3069
|
extractFileChangesFromToolUse(toolName, toolInput) {
|
|
2876
|
-
|
|
2877
|
-
const filePath = input?.filePath ?? input?.file_path ?? input?.path;
|
|
2878
|
-
if (toolName === "write" && filePath) {
|
|
2879
|
-
return [{ operation: "wrote", path: String(filePath) }];
|
|
2880
|
-
}
|
|
2881
|
-
if (toolName === "edit" && filePath) {
|
|
2882
|
-
return [{ operation: "edited", path: String(filePath) }];
|
|
2883
|
-
}
|
|
2884
|
-
if (toolName === "bash" && input?.command) {
|
|
2885
|
-
return extractFileChangesFromBash(String(input.command));
|
|
2886
|
-
}
|
|
2887
|
-
return [];
|
|
3070
|
+
return extractPrimeFileChanges(toolName, toolInput);
|
|
2888
3071
|
}
|
|
2889
3072
|
};
|
|
2890
3073
|
});
|
|
@@ -3008,16 +3191,6 @@ var init_forgecode2 = __esm(() => {
|
|
|
3008
3191
|
};
|
|
3009
3192
|
});
|
|
3010
3193
|
|
|
3011
|
-
// src/proxy/adapters/openai.ts
|
|
3012
|
-
var openAiAdapter;
|
|
3013
|
-
var init_openai = __esm(() => {
|
|
3014
|
-
init_opencode2();
|
|
3015
|
-
openAiAdapter = {
|
|
3016
|
-
...openCodeAdapter,
|
|
3017
|
-
name: "openai"
|
|
3018
|
-
};
|
|
3019
|
-
});
|
|
3020
|
-
|
|
3021
3194
|
// src/proxy/adapters/codex.ts
|
|
3022
3195
|
var codexAdapter;
|
|
3023
3196
|
var init_codex = __esm(() => {
|
|
@@ -3202,6 +3375,9 @@ function detectAdapter(c) {
|
|
|
3202
3375
|
return openCodeAdapter;
|
|
3203
3376
|
}
|
|
3204
3377
|
const userAgent = c.req.header("user-agent") || "";
|
|
3378
|
+
if (userAgent.startsWith("jcode/") && normalizeJcodeSessionId(c.req.header("x-jcode-session"))) {
|
|
3379
|
+
return jcodeAdapter;
|
|
3380
|
+
}
|
|
3205
3381
|
if (userAgent.startsWith("opencode/")) {
|
|
3206
3382
|
return openCodeAdapter;
|
|
3207
3383
|
}
|
|
@@ -3233,9 +3409,11 @@ var init_detect = __esm(() => {
|
|
|
3233
3409
|
init_crush2();
|
|
3234
3410
|
init_passthrough2();
|
|
3235
3411
|
init_pi2();
|
|
3412
|
+
init_prime();
|
|
3236
3413
|
init_forgecode2();
|
|
3237
3414
|
init_claudecode();
|
|
3238
3415
|
init_openai();
|
|
3416
|
+
init_jcode();
|
|
3239
3417
|
init_codex();
|
|
3240
3418
|
init_cherry();
|
|
3241
3419
|
init_adapterInstances();
|
|
@@ -3245,12 +3423,15 @@ var init_detect = __esm(() => {
|
|
|
3245
3423
|
crush: crushAdapter,
|
|
3246
3424
|
passthrough: passthroughAdapter,
|
|
3247
3425
|
pi: piAdapter,
|
|
3426
|
+
prime: primeAdapter,
|
|
3427
|
+
"prime-agent": primeAdapter,
|
|
3248
3428
|
forgecode: forgeCodeAdapter,
|
|
3249
3429
|
"claude-code": claudeCodeAdapter,
|
|
3250
3430
|
claudecode: claudeCodeAdapter,
|
|
3251
3431
|
cherry: cherryAdapter,
|
|
3252
3432
|
cherrystudio: cherryAdapter,
|
|
3253
3433
|
openai: openAiAdapter,
|
|
3434
|
+
jcode: jcodeAdapter,
|
|
3254
3435
|
codex: codexAdapter
|
|
3255
3436
|
};
|
|
3256
3437
|
envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
|
|
@@ -3401,9 +3582,15 @@ var init_sdkFeatures = __esm(() => {
|
|
|
3401
3582
|
openai: {
|
|
3402
3583
|
codeSystemPrompt: false
|
|
3403
3584
|
},
|
|
3585
|
+
jcode: {
|
|
3586
|
+
codeSystemPrompt: false
|
|
3587
|
+
},
|
|
3404
3588
|
cherry: {
|
|
3405
3589
|
codeSystemPrompt: false
|
|
3406
3590
|
},
|
|
3591
|
+
prime: {
|
|
3592
|
+
codeSystemPrompt: false
|
|
3593
|
+
},
|
|
3407
3594
|
codex: {
|
|
3408
3595
|
codeSystemPrompt: false
|
|
3409
3596
|
}
|
|
@@ -6294,8 +6481,10 @@ var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
|
6294
6481
|
var OAUTH_BETA_HEADER = "oauth-2025-04-20";
|
|
6295
6482
|
var CACHE_TTL_MS_DEFAULT = 30000;
|
|
6296
6483
|
var STALE_MAX_MS_DEFAULT = 15 * 60000;
|
|
6484
|
+
var RATE_LIMIT_BACKOFF_MS_DEFAULT = 60000;
|
|
6297
6485
|
var cacheByProfile = new Map;
|
|
6298
6486
|
var inflightByProfile = new Map;
|
|
6487
|
+
var rateLimitedUntilByProfile = new Map;
|
|
6299
6488
|
var DEFAULT_KEY = "__default__";
|
|
6300
6489
|
var WINDOW_TYPES = [
|
|
6301
6490
|
"five_hour",
|
|
@@ -6317,6 +6506,15 @@ function normalizeUtilization(raw2) {
|
|
|
6317
6506
|
return null;
|
|
6318
6507
|
return Math.max(0, raw2 / 100);
|
|
6319
6508
|
}
|
|
6509
|
+
function parseRetryAfterMs(raw2) {
|
|
6510
|
+
if (!raw2)
|
|
6511
|
+
return null;
|
|
6512
|
+
const seconds = Number(raw2);
|
|
6513
|
+
if (Number.isFinite(seconds))
|
|
6514
|
+
return Math.max(0, seconds * 1000);
|
|
6515
|
+
const retryAt = Date.parse(raw2);
|
|
6516
|
+
return Number.isFinite(retryAt) ? Math.max(0, retryAt - Date.now()) : null;
|
|
6517
|
+
}
|
|
6320
6518
|
function modelScopedWindowType(limit) {
|
|
6321
6519
|
if (limit.kind !== "weekly_scoped")
|
|
6322
6520
|
return null;
|
|
@@ -6374,14 +6572,27 @@ async function callAnthropic(token, fetchImpl, signal) {
|
|
|
6374
6572
|
},
|
|
6375
6573
|
signal: signal ?? AbortSignal.timeout(1e4)
|
|
6376
6574
|
});
|
|
6377
|
-
if (!res.ok)
|
|
6378
|
-
return {
|
|
6575
|
+
if (!res.ok) {
|
|
6576
|
+
return {
|
|
6577
|
+
__status: res.status,
|
|
6578
|
+
retryAfterMs: parseRetryAfterMs(res.headers.get("retry-after"))
|
|
6579
|
+
};
|
|
6580
|
+
}
|
|
6379
6581
|
return await res.json();
|
|
6380
6582
|
}
|
|
6381
6583
|
var _testOverride = null;
|
|
6382
6584
|
async function fetchOAuthUsage(opts) {
|
|
6585
|
+
return (await fetchOAuthUsageResult(opts)).snapshot;
|
|
6586
|
+
}
|
|
6587
|
+
function missingReason(cacheKey2) {
|
|
6588
|
+
const until = rateLimitedUntilByProfile.get(cacheKey2);
|
|
6589
|
+
return until !== undefined && Date.now() < until ? "rate_limited" : "no_token";
|
|
6590
|
+
}
|
|
6591
|
+
async function fetchOAuthUsageResult(opts) {
|
|
6383
6592
|
if (_testOverride && !opts?.fetchImpl && !opts?.store) {
|
|
6384
|
-
|
|
6593
|
+
const snapshot = await _testOverride(opts);
|
|
6594
|
+
const error = snapshot ? null : missingReason(opts?.profileId ?? DEFAULT_KEY);
|
|
6595
|
+
return { snapshot, error };
|
|
6385
6596
|
}
|
|
6386
6597
|
return fetchOAuthUsageImpl(opts);
|
|
6387
6598
|
}
|
|
@@ -6389,30 +6600,37 @@ async function fetchOAuthUsageImpl(opts) {
|
|
|
6389
6600
|
const ttl = opts?.ttlMs ?? CACHE_TTL_MS_DEFAULT;
|
|
6390
6601
|
const cacheKey2 = opts?.profileId ?? DEFAULT_KEY;
|
|
6391
6602
|
const fetchImpl = opts?.fetchImpl ?? globalThis.fetch;
|
|
6603
|
+
const staleMaxMs = opts?.staleMaxMs ?? STALE_MAX_MS_DEFAULT;
|
|
6604
|
+
const staleOr = (reason, error) => {
|
|
6605
|
+
const last = cacheByProfile.get(cacheKey2);
|
|
6606
|
+
if (last && Date.now() - last.fetchedAt < staleMaxMs) {
|
|
6607
|
+
claudeLog("oauth_usage.serving_stale", { profile: cacheKey2, reason, ageMs: Date.now() - last.fetchedAt });
|
|
6608
|
+
return { snapshot: { ...last, stale: true }, error: null };
|
|
6609
|
+
}
|
|
6610
|
+
return { snapshot: null, error };
|
|
6611
|
+
};
|
|
6392
6612
|
if (!opts?.force) {
|
|
6393
6613
|
const cached = cacheByProfile.get(cacheKey2);
|
|
6394
6614
|
if (cached && Date.now() - cached.fetchedAt < ttl)
|
|
6395
|
-
return cached;
|
|
6615
|
+
return { snapshot: cached, error: null };
|
|
6396
6616
|
}
|
|
6397
6617
|
const existing = inflightByProfile.get(cacheKey2);
|
|
6398
6618
|
if (existing)
|
|
6399
6619
|
return existing;
|
|
6620
|
+
const rateLimitedUntil = rateLimitedUntilByProfile.get(cacheKey2);
|
|
6621
|
+
if (rateLimitedUntil !== undefined) {
|
|
6622
|
+
if (Date.now() < rateLimitedUntil)
|
|
6623
|
+
return staleOr("rate_limited", "rate_limited");
|
|
6624
|
+
rateLimitedUntilByProfile.delete(cacheKey2);
|
|
6625
|
+
}
|
|
6400
6626
|
const store = opts?.store ?? createPlatformCredentialStore({ claudeConfigDir: opts?.claudeConfigDir });
|
|
6401
|
-
const
|
|
6402
|
-
const staleOr = (reason) => {
|
|
6403
|
-
const last = cacheByProfile.get(cacheKey2);
|
|
6404
|
-
if (last && Date.now() - last.fetchedAt < staleMaxMs) {
|
|
6405
|
-
claudeLog("oauth_usage.serving_stale", { profile: cacheKey2, reason, ageMs: Date.now() - last.fetchedAt });
|
|
6406
|
-
return { ...last, stale: true };
|
|
6407
|
-
}
|
|
6408
|
-
return null;
|
|
6409
|
-
};
|
|
6627
|
+
const rateLimitBackoffMs = opts?.rateLimitBackoffMs ?? RATE_LIMIT_BACKOFF_MS_DEFAULT;
|
|
6410
6628
|
const promise = (async () => {
|
|
6411
6629
|
try {
|
|
6412
6630
|
const token = await readAccessToken(store);
|
|
6413
6631
|
if (!token) {
|
|
6414
6632
|
claudeLog("oauth_usage.no_token", { profile: cacheKey2 });
|
|
6415
|
-
return staleOr("no_token");
|
|
6633
|
+
return staleOr("no_token", "no_token");
|
|
6416
6634
|
}
|
|
6417
6635
|
let result = await callAnthropic(token, fetchImpl);
|
|
6418
6636
|
if ("__status" in result && result.__status === 401) {
|
|
@@ -6420,23 +6638,28 @@ async function fetchOAuthUsageImpl(opts) {
|
|
|
6420
6638
|
const refreshed = await refreshOAuthToken(store);
|
|
6421
6639
|
if (!refreshed) {
|
|
6422
6640
|
claudeLog("oauth_usage.refresh_failed", { profile: cacheKey2 });
|
|
6423
|
-
return staleOr("refresh_failed");
|
|
6641
|
+
return staleOr("refresh_failed", "upstream_error");
|
|
6424
6642
|
}
|
|
6425
6643
|
const newToken = await readAccessToken(store);
|
|
6426
6644
|
if (!newToken)
|
|
6427
|
-
return staleOr("no_token_after_refresh");
|
|
6645
|
+
return staleOr("no_token_after_refresh", "no_token");
|
|
6428
6646
|
result = await callAnthropic(newToken, fetchImpl);
|
|
6429
6647
|
}
|
|
6430
6648
|
if ("__status" in result) {
|
|
6649
|
+
if (result.__status === 429) {
|
|
6650
|
+
const retryAfterMs = Math.min(Math.max(rateLimitBackoffMs, result.retryAfterMs ?? 0), Math.max(staleMaxMs, rateLimitBackoffMs));
|
|
6651
|
+
rateLimitedUntilByProfile.set(cacheKey2, Date.now() + retryAfterMs);
|
|
6652
|
+
}
|
|
6431
6653
|
claudeLog("oauth_usage.upstream_error", { profile: cacheKey2, status: result.__status });
|
|
6432
|
-
return staleOr(`upstream_${result.__status}
|
|
6654
|
+
return staleOr(`upstream_${result.__status}`, result.__status === 429 ? "rate_limited" : "upstream_error");
|
|
6433
6655
|
}
|
|
6656
|
+
rateLimitedUntilByProfile.delete(cacheKey2);
|
|
6434
6657
|
const snapshot = buildSnapshot(result);
|
|
6435
6658
|
cacheByProfile.set(cacheKey2, snapshot);
|
|
6436
|
-
return snapshot;
|
|
6659
|
+
return { snapshot, error: null };
|
|
6437
6660
|
} catch (err) {
|
|
6438
6661
|
claudeLog("oauth_usage.fetch_failed", { profile: cacheKey2, error: err instanceof Error ? err.message : String(err) });
|
|
6439
|
-
return staleOr("exception");
|
|
6662
|
+
return staleOr("exception", "upstream_error");
|
|
6440
6663
|
} finally {
|
|
6441
6664
|
inflightByProfile.delete(cacheKey2);
|
|
6442
6665
|
}
|
|
@@ -10715,6 +10938,31 @@ function shouldEarlyStop(tracker) {
|
|
|
10715
10938
|
tracker.fired = true;
|
|
10716
10939
|
return true;
|
|
10717
10940
|
}
|
|
10941
|
+
function clientAbortDisposition(input) {
|
|
10942
|
+
if (input.isIndependentSession || !input.profileSessionId)
|
|
10943
|
+
return { action: "none" };
|
|
10944
|
+
if (!input.passthrough)
|
|
10945
|
+
return { action: "evict" };
|
|
10946
|
+
if (input.currentSessionId && !input.sawDuplicateToolUse && input.resumeBoundaryUuid) {
|
|
10947
|
+
return { action: "store", resumeUuid: input.resumeBoundaryUuid };
|
|
10948
|
+
}
|
|
10949
|
+
return { action: "evict" };
|
|
10950
|
+
}
|
|
10951
|
+
function resumeBoundaryUuid(message) {
|
|
10952
|
+
const m = message;
|
|
10953
|
+
if (m?.type !== "user")
|
|
10954
|
+
return;
|
|
10955
|
+
if (typeof m.uuid !== "string" || m.uuid.length === 0)
|
|
10956
|
+
return;
|
|
10957
|
+
const content = m.message?.content;
|
|
10958
|
+
if (!Array.isArray(content))
|
|
10959
|
+
return;
|
|
10960
|
+
const hasResult = content.some((block) => {
|
|
10961
|
+
const b = block;
|
|
10962
|
+
return b?.type === "tool_result";
|
|
10963
|
+
});
|
|
10964
|
+
return hasResult ? m.uuid : undefined;
|
|
10965
|
+
}
|
|
10718
10966
|
|
|
10719
10967
|
// src/proxy/envelopeIntegrity.ts
|
|
10720
10968
|
function checkEmptyToolInputs(contentBlocks, tools) {
|
|
@@ -10755,6 +11003,68 @@ function checkUndeliveredToolUses(captured, deliveredIds) {
|
|
|
10755
11003
|
return violations;
|
|
10756
11004
|
}
|
|
10757
11005
|
|
|
11006
|
+
// src/proxy/turnOutcome.ts
|
|
11007
|
+
function classifyTurnOutcome(input) {
|
|
11008
|
+
if (input.toolUses > 0)
|
|
11009
|
+
return { kind: "productive" };
|
|
11010
|
+
if (input.textEvents > 0)
|
|
11011
|
+
return { kind: "productive" };
|
|
11012
|
+
return {
|
|
11013
|
+
kind: "silent",
|
|
11014
|
+
reason: input.blocksForwarded > 0 ? "no_actionable_content" : "no_blocks"
|
|
11015
|
+
};
|
|
11016
|
+
}
|
|
11017
|
+
var SILENT_TURN_NUDGE = "Your previous turn produced no visible output — no text and no tool call — so the client received " + "nothing to act on. Any earlier instruction to end your turn without further text applied only to " + "that turn and is now discharged. Answer now, in text, addressing the most recent request and any " + "tool results above it. If a tool call is still required, make it.";
|
|
11018
|
+
function shouldInjectSilentTurn(input) {
|
|
11019
|
+
if (!input.raw)
|
|
11020
|
+
return false;
|
|
11021
|
+
if (input.raw === "1")
|
|
11022
|
+
return true;
|
|
11023
|
+
return Boolean(input.sessionId && input.raw === input.sessionId);
|
|
11024
|
+
}
|
|
11025
|
+
function createRecoveryLifter(allocateBlockIndex) {
|
|
11026
|
+
let blockIndex;
|
|
11027
|
+
return {
|
|
11028
|
+
lift(innerEvent) {
|
|
11029
|
+
const inner = innerEvent;
|
|
11030
|
+
if (!inner)
|
|
11031
|
+
return;
|
|
11032
|
+
if (inner.type === "content_block_start" && inner.content_block?.type === "text") {
|
|
11033
|
+
blockIndex = allocateBlockIndex();
|
|
11034
|
+
return {
|
|
11035
|
+
kind: "block_start",
|
|
11036
|
+
frame: { type: "content_block_start", index: blockIndex, content_block: { type: "text", text: "" } }
|
|
11037
|
+
};
|
|
11038
|
+
}
|
|
11039
|
+
if (inner.type === "content_block_delta" && inner.delta?.type === "text_delta" && blockIndex !== undefined) {
|
|
11040
|
+
const text = inner.delta.text;
|
|
11041
|
+
return {
|
|
11042
|
+
kind: "text_delta",
|
|
11043
|
+
frame: { type: "content_block_delta", index: blockIndex, delta: { type: "text_delta", text } },
|
|
11044
|
+
textChars: typeof text === "string" ? text.length : 0
|
|
11045
|
+
};
|
|
11046
|
+
}
|
|
11047
|
+
if (inner.type === "content_block_stop" && blockIndex !== undefined) {
|
|
11048
|
+
const index = blockIndex;
|
|
11049
|
+
blockIndex = undefined;
|
|
11050
|
+
return { kind: "block_stop", frame: { type: "content_block_stop", index } };
|
|
11051
|
+
}
|
|
11052
|
+
return;
|
|
11053
|
+
}
|
|
11054
|
+
};
|
|
11055
|
+
}
|
|
11056
|
+
function shouldAttemptRecovery(input) {
|
|
11057
|
+
if (!input.enabled)
|
|
11058
|
+
return false;
|
|
11059
|
+
if (input.outcome.kind === "productive")
|
|
11060
|
+
return false;
|
|
11061
|
+
if (input.alreadyAttempted)
|
|
11062
|
+
return false;
|
|
11063
|
+
if (input.clientGone)
|
|
11064
|
+
return false;
|
|
11065
|
+
return Boolean(input.sessionId);
|
|
11066
|
+
}
|
|
11067
|
+
|
|
10758
11068
|
// src/proxy/server.ts
|
|
10759
11069
|
init_agentMatch();
|
|
10760
11070
|
|
|
@@ -11594,7 +11904,14 @@ function profileSection(q,s,pl,h){
|
|
|
11594
11904
|
var orderIdx=(pl.profileOrder||[]).indexOf(p.id);
|
|
11595
11905
|
if(orderIdx>=0)badge+='<span class="pool-chip">#'+(orderIdx+1)+' in pool</span>';
|
|
11596
11906
|
var exh=(pl.exhausted||[]).filter(function(e){return e.id===p.id})[0];
|
|
11597
|
-
if(exh)
|
|
11907
|
+
if(exh){
|
|
11908
|
+
// A billing refusal has no reset to wait for — the pool re-probes on the
|
|
11909
|
+
// same timer, but nothing changes until a human fixes the account.
|
|
11910
|
+
// Showing it as 'resets in 9m' promises a recovery that never comes.
|
|
11911
|
+
badge+=exh.reason==='billing_error'
|
|
11912
|
+
?' <span class="pool-chip exhausted" title="Subscription or payment refused — this does not clear on its own">subscription refused</span>'
|
|
11913
|
+
:' <span class="pool-chip exhausted">exhausted · resets '+resetIn(exh.until)+'</span>';
|
|
11914
|
+
}
|
|
11598
11915
|
}
|
|
11599
11916
|
cards+='<div class="profile-card'+(p.isActive?' active':'')+(switchable?' switchable':'')+'"'+(switchable?' data-profile="'+esc(p.id)+'" role="button" tabindex="0"':'')+'>'
|
|
11600
11917
|
+'<div class="profile-head"><span class="profile-name"><span class="prof-dot"></span>'+esc(p.label||p.id)+' '+badge+'</span>'
|
|
@@ -11780,6 +12097,17 @@ function extendedContextHint(model) {
|
|
|
11780
12097
|
return advise("MERIDIAN_SONNET_MODEL=sonnet");
|
|
11781
12098
|
return advise("MERIDIAN_1M_CONTEXT_SUPPORT=0");
|
|
11782
12099
|
}
|
|
12100
|
+
var BILLING_SIGNALS = [
|
|
12101
|
+
/\b402\b(?!:\d)/,
|
|
12102
|
+
/billing[_ ](?:error|issue|problem|failure)/,
|
|
12103
|
+
/subscription (?:is |has )?(?:inactive|expired|lapsed|cancell?ed|ended|invalid|not active)/,
|
|
12104
|
+
/(?:expired|inactive|lapsed|invalid|no active|cancell?ed) subscription/,
|
|
12105
|
+
/payment (?:method|required|failed|declined|details|info)/,
|
|
12106
|
+
/update your payment/,
|
|
12107
|
+
/(?:out of|draw from|draws from) extra usage/,
|
|
12108
|
+
/insufficient (?:credit|funds|balance)/
|
|
12109
|
+
];
|
|
12110
|
+
var HIT_YOUR_LIMIT = /hit your (?:[\w-]+ )?limit/;
|
|
11783
12111
|
function classifyError(errMsg, model) {
|
|
11784
12112
|
const lower = errMsg.toLowerCase();
|
|
11785
12113
|
if (lower.includes("oauth token has expired") || lower.includes("not logged in")) {
|
|
@@ -11796,7 +12124,7 @@ function classifyError(errMsg, model) {
|
|
|
11796
12124
|
message: "Claude authentication expired or invalid. Run 'claude login' in your terminal to re-authenticate, then restart the proxy."
|
|
11797
12125
|
};
|
|
11798
12126
|
}
|
|
11799
|
-
if (lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests") ||
|
|
12127
|
+
if (lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests") || HIT_YOUR_LIMIT.test(lower) || lower.includes("usage limit reached")) {
|
|
11800
12128
|
const hint = lower.includes("1m") || lower.includes("context") ? extendedContextHint(model) : "";
|
|
11801
12129
|
return {
|
|
11802
12130
|
status: 429,
|
|
@@ -11804,7 +12132,7 @@ function classifyError(errMsg, model) {
|
|
|
11804
12132
|
message: `Claude Max rate limit reached. Wait a moment and try again.${hint}`
|
|
11805
12133
|
};
|
|
11806
12134
|
}
|
|
11807
|
-
if (
|
|
12135
|
+
if (BILLING_SIGNALS.some((rx) => rx.test(lower))) {
|
|
11808
12136
|
return {
|
|
11809
12137
|
status: 402,
|
|
11810
12138
|
type: "billing_error",
|
|
@@ -11889,6 +12217,16 @@ function isRateLimitError(errMsg) {
|
|
|
11889
12217
|
const lower = errMsg.toLowerCase();
|
|
11890
12218
|
return lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests");
|
|
11891
12219
|
}
|
|
12220
|
+
var ACCOUNT_FAILOVER_ERROR_TYPES = new Set([
|
|
12221
|
+
"rate_limit_error",
|
|
12222
|
+
"billing_error"
|
|
12223
|
+
]);
|
|
12224
|
+
function isAccountFailoverError(errorType) {
|
|
12225
|
+
return typeof errorType === "string" && ACCOUNT_FAILOVER_ERROR_TYPES.has(errorType);
|
|
12226
|
+
}
|
|
12227
|
+
function isQuotaRefusal(errorType) {
|
|
12228
|
+
return errorType === "rate_limit_error";
|
|
12229
|
+
}
|
|
11892
12230
|
function isExtraUsageRequiredError(errMsg) {
|
|
11893
12231
|
const lower = errMsg.toLowerCase();
|
|
11894
12232
|
return lower.includes("extra usage") && lower.includes("1m") || lower.includes("out of extra usage");
|
|
@@ -12279,7 +12617,7 @@ ${c.text}
|
|
|
12279
12617
|
return "";
|
|
12280
12618
|
}).filter(Boolean).join("");
|
|
12281
12619
|
}
|
|
12282
|
-
function translateOpenAiToAnthropic(body) {
|
|
12620
|
+
function translateOpenAiToAnthropic(body, options = {}) {
|
|
12283
12621
|
const messages = body.messages ?? [];
|
|
12284
12622
|
if (messages.length === 0)
|
|
12285
12623
|
return null;
|
|
@@ -12371,7 +12709,7 @@ function translateOpenAiToAnthropic(body) {
|
|
|
12371
12709
|
let systemPrompt = systemParts.join(`
|
|
12372
12710
|
`);
|
|
12373
12711
|
let messagesToSend = turns;
|
|
12374
|
-
if (turns.length > 1) {
|
|
12712
|
+
if (turns.length > 1 && !options.preserveConversationHistory) {
|
|
12375
12713
|
const history = turns.slice(0, -1).map((m) => `${m.role}: ${summarizeAnthropicContent(m.content)}`).join(`
|
|
12376
12714
|
`);
|
|
12377
12715
|
const historyBlock = `<conversation_history>
|
|
@@ -12666,6 +13004,9 @@ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000))
|
|
|
12666
13004
|
];
|
|
12667
13005
|
}
|
|
12668
13006
|
|
|
13007
|
+
// src/proxy/server.ts
|
|
13008
|
+
init_jcode();
|
|
13009
|
+
|
|
12669
13010
|
// src/proxy/openaiResponses.ts
|
|
12670
13011
|
function itemDiscriminator(item) {
|
|
12671
13012
|
if (typeof item !== "object" || item === null)
|
|
@@ -18858,7 +19199,7 @@ function buildQueryOptions(ctx, abortController) {
|
|
|
18858
19199
|
hasDeferredTools,
|
|
18859
19200
|
resumeSessionId,
|
|
18860
19201
|
isUndo,
|
|
18861
|
-
|
|
19202
|
+
resumeSessionAtUuid,
|
|
18862
19203
|
forkSession,
|
|
18863
19204
|
sdkHooks,
|
|
18864
19205
|
blockedTools,
|
|
@@ -18929,7 +19270,7 @@ function buildQueryOptions(ctx, abortController) {
|
|
|
18929
19270
|
...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
|
|
18930
19271
|
...resumeSessionId ? { resume: resumeSessionId } : {},
|
|
18931
19272
|
...isUndo || forkSession ? { forkSession: true } : {},
|
|
18932
|
-
...
|
|
19273
|
+
...resumeSessionAtUuid ? { resumeSessionAt: resumeSessionAtUuid } : {},
|
|
18933
19274
|
...sdkHooks ? { hooks: sdkHooks } : {},
|
|
18934
19275
|
...effort ? { effort } : {},
|
|
18935
19276
|
...thinking ? { thinking } : {},
|
|
@@ -18990,6 +19331,34 @@ init_opencode();
|
|
|
18990
19331
|
init_crush();
|
|
18991
19332
|
init_droid();
|
|
18992
19333
|
init_pi();
|
|
19334
|
+
|
|
19335
|
+
// src/proxy/transforms/prime.ts
|
|
19336
|
+
init_tools();
|
|
19337
|
+
init_env();
|
|
19338
|
+
init_prime();
|
|
19339
|
+
function resolvePrimePassthrough() {
|
|
19340
|
+
return resolvePassthrough(true);
|
|
19341
|
+
}
|
|
19342
|
+
var primeTransforms = [
|
|
19343
|
+
{
|
|
19344
|
+
name: "prime-core",
|
|
19345
|
+
adapters: ["prime"],
|
|
19346
|
+
onRequest(ctx) {
|
|
19347
|
+
return {
|
|
19348
|
+
...ctx,
|
|
19349
|
+
blockedTools: BLOCKED_BUILTIN_TOOLS,
|
|
19350
|
+
incompatibleTools: CLAUDE_CODE_ONLY_TOOLS,
|
|
19351
|
+
allowedMcpTools: PRIME_ALLOWED_MCP_TOOLS,
|
|
19352
|
+
sdkAgents: {},
|
|
19353
|
+
passthrough: resolvePrimePassthrough(),
|
|
19354
|
+
supportsThinking: true,
|
|
19355
|
+
extractFileChangesFromToolUse: extractPrimeFileChanges
|
|
19356
|
+
};
|
|
19357
|
+
}
|
|
19358
|
+
}
|
|
19359
|
+
];
|
|
19360
|
+
|
|
19361
|
+
// src/proxy/transforms/registry.ts
|
|
18993
19362
|
init_forgecode();
|
|
18994
19363
|
init_passthrough();
|
|
18995
19364
|
|
|
@@ -19071,11 +19440,13 @@ var ADAPTER_TRANSFORMS = {
|
|
|
19071
19440
|
crush: crushTransforms,
|
|
19072
19441
|
droid: droidTransforms,
|
|
19073
19442
|
pi: piTransforms,
|
|
19443
|
+
prime: primeTransforms,
|
|
19074
19444
|
forgecode: forgeCodeTransforms,
|
|
19075
19445
|
passthrough: passthroughTransforms,
|
|
19076
19446
|
cherry: cherryTransforms,
|
|
19077
19447
|
"claude-code": claudeCodeTransforms,
|
|
19078
19448
|
openai: openCodeTransforms,
|
|
19449
|
+
jcode: openCodeTransforms,
|
|
19079
19450
|
codex: [...openCodeTransforms, ...codexTransforms]
|
|
19080
19451
|
};
|
|
19081
19452
|
function getAdapterTransforms(adapterName) {
|
|
@@ -19088,7 +19459,7 @@ import { join as join5, isAbsolute as isAbsolute2, extname } from "path";
|
|
|
19088
19459
|
import { pathToFileURL } from "url";
|
|
19089
19460
|
|
|
19090
19461
|
// src/proxy/plugins/validation.ts
|
|
19091
|
-
var KNOWN_ADAPTERS = ["opencode", "openai", "crush", "droid", "pi", "forgecode", "passthrough"];
|
|
19462
|
+
var KNOWN_ADAPTERS = ["opencode", "openai", "jcode", "crush", "droid", "pi", "forgecode", "passthrough"];
|
|
19092
19463
|
var KNOWN_HOOKS = ["onRequest", "onResponse", "onTelemetry", "onSession", "onToolUse", "onToolResult", "onError"];
|
|
19093
19464
|
function validateTransform(exported) {
|
|
19094
19465
|
if (exported == null || typeof exported !== "object") {
|
|
@@ -19443,11 +19814,66 @@ function computeLineageHash(messages) {
|
|
|
19443
19814
|
function hashMessage(message) {
|
|
19444
19815
|
return createHash2("sha256").update(`${message.role}:${normalizeContent(message.content)}`).digest("hex").slice(0, 32);
|
|
19445
19816
|
}
|
|
19817
|
+
function describeShape(message) {
|
|
19818
|
+
const normalized = normalizeContent(message.content);
|
|
19819
|
+
return {
|
|
19820
|
+
role: message.role,
|
|
19821
|
+
blocks: Array.isArray(message.content) ? message.content.map((b) => String(b?.type ?? "unknown")).join(",") : typeof message.content === "string" ? "string" : "unknown",
|
|
19822
|
+
bytes: Buffer.byteLength(normalized, "utf8")
|
|
19823
|
+
};
|
|
19824
|
+
}
|
|
19825
|
+
function describeLineageMismatch(cached, messages, precomputedIncomingHashes) {
|
|
19826
|
+
const storedHashes = cached.messageHashes ?? [];
|
|
19827
|
+
const incomingHashes = precomputedIncomingHashes ?? computeMessageHashes(messages);
|
|
19828
|
+
const limit = Math.min(storedHashes.length, incomingHashes.length);
|
|
19829
|
+
let index = -1;
|
|
19830
|
+
for (let i = 0;i < limit; i++) {
|
|
19831
|
+
if (storedHashes[i] !== incomingHashes[i]) {
|
|
19832
|
+
index = i;
|
|
19833
|
+
break;
|
|
19834
|
+
}
|
|
19835
|
+
}
|
|
19836
|
+
const base = {
|
|
19837
|
+
index,
|
|
19838
|
+
storedCount: cached.messageCount,
|
|
19839
|
+
incomingCount: messages.length
|
|
19840
|
+
};
|
|
19841
|
+
if (index < 0)
|
|
19842
|
+
return base;
|
|
19843
|
+
return {
|
|
19844
|
+
...base,
|
|
19845
|
+
storedDigest: storedHashes[index],
|
|
19846
|
+
incomingDigest: incomingHashes[index],
|
|
19847
|
+
incomingShape: messages[index] ? describeShape(messages[index]) : undefined,
|
|
19848
|
+
previousDigest: index > 0 ? storedHashes[index - 1] : undefined
|
|
19849
|
+
};
|
|
19850
|
+
}
|
|
19851
|
+
function formatLineageMismatch(mismatch) {
|
|
19852
|
+
if (mismatch.index < 0)
|
|
19853
|
+
return;
|
|
19854
|
+
const short = (digest) => digest ? digest.slice(0, 12) : "—";
|
|
19855
|
+
const trailing = mismatch.index === mismatch.storedCount - 1 ? " (trailing message only — the rest of the history matched)" : "";
|
|
19856
|
+
const shape = mismatch.incomingShape ? `${mismatch.incomingShape.role}[${mismatch.incomingShape.blocks}] ${mismatch.incomingShape.bytes}B` : "unknown";
|
|
19857
|
+
return `first mismatch at index ${mismatch.index}${trailing}: ` + `stored=${short(mismatch.storedDigest)} incoming=${short(mismatch.incomingDigest)}, ` + `incoming now ${shape}`;
|
|
19858
|
+
}
|
|
19446
19859
|
function computeMessageHashes(messages) {
|
|
19447
19860
|
if (!messages || messages.length === 0)
|
|
19448
19861
|
return [];
|
|
19449
19862
|
return messages.map(hashMessage);
|
|
19450
19863
|
}
|
|
19864
|
+
function hashNormalizedContent(content) {
|
|
19865
|
+
return createHash2("sha256").update(normalizeContent(content)).digest("hex").slice(0, 32);
|
|
19866
|
+
}
|
|
19867
|
+
function hashableContentBlocks(content) {
|
|
19868
|
+
if (!Array.isArray(content))
|
|
19869
|
+
return [content];
|
|
19870
|
+
return content.filter((block) => !HASH_IGNORED_BLOCK_TYPES.has(block?.type));
|
|
19871
|
+
}
|
|
19872
|
+
function computeMessageBlockHashes(messages) {
|
|
19873
|
+
if (!messages || messages.length === 0)
|
|
19874
|
+
return [];
|
|
19875
|
+
return messages.map((message) => hashableContentBlocks(message.content).map((block) => hashNormalizedContent(Array.isArray(message.content) ? [block] : block)));
|
|
19876
|
+
}
|
|
19451
19877
|
function measurePrefixOverlap(storedHashes, incomingHashes) {
|
|
19452
19878
|
let overlap = 0;
|
|
19453
19879
|
const minLen = Math.min(storedHashes.length, incomingHashes.length);
|
|
@@ -19530,6 +19956,34 @@ function verifyLineage(cached, messages) {
|
|
|
19530
19956
|
suffixOverlap
|
|
19531
19957
|
};
|
|
19532
19958
|
}
|
|
19959
|
+
const boundary = cached.messageCount - 1;
|
|
19960
|
+
if (boundary >= 0 && prefixOverlap === boundary && messages.length >= cached.messageCount && cached.messageBlockHashes?.length === cached.messageCount) {
|
|
19961
|
+
const incomingBoundary = messages[boundary];
|
|
19962
|
+
const storedBlocks = cached.messageBlockHashes[boundary];
|
|
19963
|
+
if (incomingBoundary?.role === "user" && storedBlocks && Array.isArray(incomingBoundary.content)) {
|
|
19964
|
+
const incomingBlocks = hashableContentBlocks(incomingBoundary.content);
|
|
19965
|
+
const incomingBlockHashes = incomingBlocks.map((block) => hashNormalizedContent([block]));
|
|
19966
|
+
const preservesStoredBlocks = incomingBlocks.length === incomingBoundary.content.length && incomingBlockHashes.length > storedBlocks.length && storedBlocks.every((hash, index) => incomingBlockHashes[index] === hash);
|
|
19967
|
+
const appendedBlocks = incomingBlocks.slice(storedBlocks.length);
|
|
19968
|
+
const seenToolResultIds = new Set(incomingBlocks.slice(0, storedBlocks.length).filter((block) => block?.type === "tool_result" && typeof block.tool_use_id === "string").map((block) => block.tool_use_id));
|
|
19969
|
+
const hasOnlyNewToolResults = appendedBlocks.every((block) => {
|
|
19970
|
+
if (block?.type !== "tool_result" || typeof block.tool_use_id !== "string")
|
|
19971
|
+
return false;
|
|
19972
|
+
if (seenToolResultIds.has(block.tool_use_id))
|
|
19973
|
+
return false;
|
|
19974
|
+
seenToolResultIds.add(block.tool_use_id);
|
|
19975
|
+
return true;
|
|
19976
|
+
});
|
|
19977
|
+
if (preservesStoredBlocks && hasOnlyNewToolResults) {
|
|
19978
|
+
return {
|
|
19979
|
+
type: "continuation",
|
|
19980
|
+
session: cached,
|
|
19981
|
+
resumeFrom: boundary,
|
|
19982
|
+
resumeContentFrom: storedBlocks.length
|
|
19983
|
+
};
|
|
19984
|
+
}
|
|
19985
|
+
}
|
|
19986
|
+
}
|
|
19533
19987
|
if (prefixOverlap > 0 && suffixOverlap === 0 && messages.length <= cached.messageCount) {
|
|
19534
19988
|
let rollbackUuid;
|
|
19535
19989
|
if (cached.sdkMessageUuids) {
|
|
@@ -19543,7 +19997,12 @@ function verifyLineage(cached, messages) {
|
|
|
19543
19997
|
return { type: "undo", session: cached, prefixOverlap, rollbackUuid };
|
|
19544
19998
|
}
|
|
19545
19999
|
if (prefixOverlap > 0 && messages.length > cached.messageCount) {
|
|
19546
|
-
return {
|
|
20000
|
+
return {
|
|
20001
|
+
type: "diverged",
|
|
20002
|
+
reason: "modified-history",
|
|
20003
|
+
prefixOverlap,
|
|
20004
|
+
mismatch: describeLineageMismatch(cached, messages, incomingHashes)
|
|
20005
|
+
};
|
|
19547
20006
|
}
|
|
19548
20007
|
return { type: "diverged", reason: "unrelated-history", prefixOverlap };
|
|
19549
20008
|
}
|
|
@@ -19676,7 +20135,7 @@ function lookupSharedSessionByClaudeId(claudeSessionId) {
|
|
|
19676
20135
|
}
|
|
19677
20136
|
return newest;
|
|
19678
20137
|
}
|
|
19679
|
-
function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage) {
|
|
20138
|
+
function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughResumeUuid) {
|
|
19680
20139
|
const path3 = getStorePath();
|
|
19681
20140
|
const lockPath = `${path3}.lock`;
|
|
19682
20141
|
const hasLock = skipLocking ? false : acquireLock(lockPath);
|
|
@@ -19694,7 +20153,9 @@ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, mes
|
|
|
19694
20153
|
messageCount: messageCount ?? existing?.messageCount ?? 0,
|
|
19695
20154
|
lineageHash: lineageHash ?? existing?.lineageHash,
|
|
19696
20155
|
messageHashes: messageHashes ?? existing?.messageHashes,
|
|
20156
|
+
messageBlockHashes: messageBlockHashes ?? existing?.messageBlockHashes,
|
|
19697
20157
|
sdkMessageUuids: sdkMessageUuids ?? existing?.sdkMessageUuids,
|
|
20158
|
+
passthroughResumeUuid: passthroughResumeUuid === undefined ? existing?.passthroughResumeUuid : passthroughResumeUuid ?? undefined,
|
|
19698
20159
|
contextUsage: contextUsage ?? existing?.contextUsage,
|
|
19699
20160
|
...previousClaudeSessionId ? { previousClaudeSessionId } : {}
|
|
19700
20161
|
};
|
|
@@ -19852,7 +20313,11 @@ function touchSession(state) {
|
|
|
19852
20313
|
}
|
|
19853
20314
|
function classifyLineage(state, messages, cacheKey2) {
|
|
19854
20315
|
const result = verifyLineage(state, messages);
|
|
19855
|
-
if (result.type === "
|
|
20316
|
+
if (result.type === "continuation" && result.resumeContentFrom !== undefined) {
|
|
20317
|
+
const msg = `Parallel tool-result continuation (key=${cacheKey2.slice(0, 8)}…): resume from message ${result.resumeFrom}, content block ${result.resumeContentFrom}.`;
|
|
20318
|
+
console.error(`[PROXY] ${msg}`);
|
|
20319
|
+
diagnosticLog2.lineage(msg);
|
|
20320
|
+
} else if (result.type === "compaction") {
|
|
19856
20321
|
const msg = `Compaction detected (key=${cacheKey2.slice(0, 8)}…): suffix overlap ${result.suffixOverlap}/${state.messageCount}, resume from incoming message ${result.resumeFrom}.`;
|
|
19857
20322
|
console.error(`[PROXY] ${msg}`);
|
|
19858
20323
|
diagnosticLog2.lineage(msg);
|
|
@@ -19861,7 +20326,9 @@ function classifyLineage(state, messages, cacheKey2) {
|
|
|
19861
20326
|
console.error(`[PROXY] ${msg}`);
|
|
19862
20327
|
diagnosticLog2.lineage(msg);
|
|
19863
20328
|
} else if (result.type === "diverged" && result.reason === "modified-history") {
|
|
19864
|
-
const
|
|
20329
|
+
const detail = result.mismatch ? formatLineageMismatch(result.mismatch) : undefined;
|
|
20330
|
+
const msg = `Stale session detected (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${result.prefixOverlap || 0}/${state.messageCount}, incoming ${messages.length} msgs. Starting fresh replay.` + (detail ? `
|
|
20331
|
+
${detail}` : "");
|
|
19865
20332
|
console.error(`[PROXY] ${msg}`);
|
|
19866
20333
|
diagnosticLog2.lineage(msg);
|
|
19867
20334
|
}
|
|
@@ -19884,7 +20351,9 @@ function lookupSession(sessionId, messages, workingDirectory) {
|
|
|
19884
20351
|
messageCount: shared.messageCount || 0,
|
|
19885
20352
|
lineageHash: shared.lineageHash || "",
|
|
19886
20353
|
messageHashes: shared.messageHashes,
|
|
20354
|
+
messageBlockHashes: shared.messageBlockHashes,
|
|
19887
20355
|
sdkMessageUuids: shared.sdkMessageUuids,
|
|
20356
|
+
passthroughResumeUuid: shared.passthroughResumeUuid,
|
|
19888
20357
|
contextUsage: shared.contextUsage
|
|
19889
20358
|
};
|
|
19890
20359
|
const result = classifyLineage(state, messages, sessionId);
|
|
@@ -19912,7 +20381,9 @@ function lookupSession(sessionId, messages, workingDirectory) {
|
|
|
19912
20381
|
messageCount: shared.messageCount || 0,
|
|
19913
20382
|
lineageHash: shared.lineageHash || "",
|
|
19914
20383
|
messageHashes: shared.messageHashes,
|
|
20384
|
+
messageBlockHashes: shared.messageBlockHashes,
|
|
19915
20385
|
sdkMessageUuids: shared.sdkMessageUuids,
|
|
20386
|
+
passthroughResumeUuid: shared.passthroughResumeUuid,
|
|
19916
20387
|
contextUsage: shared.contextUsage
|
|
19917
20388
|
};
|
|
19918
20389
|
const result = classifyLineage(state, messages, fp);
|
|
@@ -19945,24 +20416,29 @@ function getSessionByClaudeId(claudeSessionId) {
|
|
|
19945
20416
|
messageCount: shared.messageCount || 0,
|
|
19946
20417
|
lineageHash: shared.lineageHash || "",
|
|
19947
20418
|
messageHashes: shared.messageHashes,
|
|
20419
|
+
messageBlockHashes: shared.messageBlockHashes,
|
|
19948
20420
|
sdkMessageUuids: shared.sdkMessageUuids,
|
|
20421
|
+
passthroughResumeUuid: shared.passthroughResumeUuid,
|
|
19949
20422
|
contextUsage: shared.contextUsage
|
|
19950
20423
|
});
|
|
19951
20424
|
}
|
|
19952
20425
|
return newest;
|
|
19953
20426
|
}
|
|
19954
|
-
function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage) {
|
|
20427
|
+
function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage, passthroughResumeUuid) {
|
|
19955
20428
|
if (!claudeSessionId)
|
|
19956
20429
|
return;
|
|
19957
20430
|
const lineageHash = computeLineageHash(messages);
|
|
19958
20431
|
const messageHashes = computeMessageHashes(messages);
|
|
20432
|
+
const messageBlockHashes = computeMessageBlockHashes(messages);
|
|
19959
20433
|
const state = {
|
|
19960
20434
|
claudeSessionId,
|
|
19961
20435
|
lastAccess: Date.now(),
|
|
19962
20436
|
messageCount: messages?.length || 0,
|
|
19963
20437
|
lineageHash,
|
|
19964
20438
|
messageHashes,
|
|
20439
|
+
messageBlockHashes,
|
|
19965
20440
|
sdkMessageUuids,
|
|
20441
|
+
...passthroughResumeUuid ? { passthroughResumeUuid } : {},
|
|
19966
20442
|
...contextUsage ? { contextUsage } : {}
|
|
19967
20443
|
};
|
|
19968
20444
|
if (sessionId)
|
|
@@ -19972,14 +20448,14 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
|
|
|
19972
20448
|
fingerprintCache.set(fp, state);
|
|
19973
20449
|
const key = sessionId || fp;
|
|
19974
20450
|
if (key) {
|
|
19975
|
-
storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage);
|
|
20451
|
+
storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughResumeUuid ?? null);
|
|
19976
20452
|
}
|
|
19977
20453
|
}
|
|
19978
20454
|
|
|
19979
20455
|
// src/proxy/server.ts
|
|
19980
20456
|
var exec2 = promisify3(execCallback);
|
|
19981
20457
|
var claudeExecutable = "";
|
|
19982
|
-
var UPSTREAM_IDLE_MS = 90000;
|
|
20458
|
+
var UPSTREAM_IDLE_MS = envInt("UPSTREAM_IDLE_MS", 90000);
|
|
19983
20459
|
function credentialStoreForProfile(profile) {
|
|
19984
20460
|
if (profile.type !== "claude-max")
|
|
19985
20461
|
return;
|
|
@@ -20266,23 +20742,25 @@ function createProxyServer(config = {}) {
|
|
|
20266
20742
|
});
|
|
20267
20743
|
});
|
|
20268
20744
|
}
|
|
20269
|
-
async function
|
|
20745
|
+
async function sniffAccountFailure(res) {
|
|
20270
20746
|
const contentType = res.headers.get("content-type") ?? "";
|
|
20271
20747
|
if (!contentType.includes("text/event-stream")) {
|
|
20272
|
-
if (res.
|
|
20748
|
+
if (!res.ok) {
|
|
20273
20749
|
const body = await res.clone().json().catch(() => null);
|
|
20274
|
-
|
|
20275
|
-
|
|
20750
|
+
const errorType = body?.error?.type;
|
|
20751
|
+
if (isAccountFailoverError(errorType)) {
|
|
20752
|
+
return { failed: true, errorPayload: body, errorType, response: res };
|
|
20753
|
+
}
|
|
20276
20754
|
}
|
|
20277
|
-
return { failed: false, errorPayload: null, response: res };
|
|
20755
|
+
return { failed: false, errorPayload: null, errorType: null, response: res };
|
|
20278
20756
|
}
|
|
20279
20757
|
const reader = res.body?.getReader();
|
|
20280
20758
|
if (!reader)
|
|
20281
|
-
return { failed: false, errorPayload: null, response: res };
|
|
20759
|
+
return { failed: false, errorPayload: null, errorType: null, response: res };
|
|
20282
20760
|
const decoder = new TextDecoder;
|
|
20283
20761
|
const consumed = [];
|
|
20284
20762
|
let text = "";
|
|
20285
|
-
let
|
|
20763
|
+
let failure = null;
|
|
20286
20764
|
while (true) {
|
|
20287
20765
|
const { done, value } = await reader.read();
|
|
20288
20766
|
if (done)
|
|
@@ -20300,16 +20778,17 @@ function createProxyServer(config = {}) {
|
|
|
20300
20778
|
`).find((l) => l.startsWith("data: "));
|
|
20301
20779
|
try {
|
|
20302
20780
|
const parsed = dataLine ? JSON.parse(dataLine.slice(6)) : null;
|
|
20303
|
-
|
|
20304
|
-
|
|
20781
|
+
const parsedType = parsed?.error?.type;
|
|
20782
|
+
if (isAccountFailoverError(parsedType)) {
|
|
20783
|
+
failure = { payload: parsed, type: parsedType };
|
|
20305
20784
|
}
|
|
20306
20785
|
} catch {}
|
|
20307
20786
|
}
|
|
20308
20787
|
break;
|
|
20309
20788
|
}
|
|
20310
|
-
if (
|
|
20789
|
+
if (failure) {
|
|
20311
20790
|
await reader.cancel().catch(() => {});
|
|
20312
|
-
return { failed: true, errorPayload:
|
|
20791
|
+
return { failed: true, errorPayload: failure.payload, errorType: failure.type, response: res };
|
|
20313
20792
|
}
|
|
20314
20793
|
const rest = new ReadableStream({
|
|
20315
20794
|
start(ctrl) {
|
|
@@ -20327,33 +20806,40 @@ function createProxyServer(config = {}) {
|
|
|
20327
20806
|
reader.cancel(reason).catch(() => {});
|
|
20328
20807
|
}
|
|
20329
20808
|
});
|
|
20330
|
-
return { failed: false, errorPayload: null, response: new Response(rest, { status: res.status, headers: res.headers }) };
|
|
20809
|
+
return { failed: false, errorPayload: null, errorType: null, response: new Response(rest, { status: res.status, headers: res.headers }) };
|
|
20331
20810
|
}
|
|
20332
20811
|
async function dispatchPriority(c, orderedCandidateIds, sessionKey, wantsStream) {
|
|
20333
20812
|
const bodyBuf = await c.req.arrayBuffer();
|
|
20334
20813
|
let lastError = null;
|
|
20814
|
+
let lastStatus = 429;
|
|
20335
20815
|
let previous = null;
|
|
20816
|
+
let previousReason = "rate_limit_error";
|
|
20336
20817
|
for (const candidate of orderedCandidateIds) {
|
|
20337
20818
|
const headers = new Headers(c.req.raw.headers);
|
|
20338
20819
|
headers.set("x-meridian-profile", candidate);
|
|
20339
20820
|
headers.set("x-meridian-priority-dispatch", "1");
|
|
20340
20821
|
const inner = await app.fetch(new Request(c.req.url, { method: "POST", headers, body: bodyBuf }));
|
|
20341
|
-
const
|
|
20342
|
-
if (!failed) {
|
|
20822
|
+
const sniffed = await sniffAccountFailure(inner);
|
|
20823
|
+
if (!sniffed.failed) {
|
|
20343
20824
|
if (sessionKey)
|
|
20344
20825
|
priorityAssignments.set(sessionKey, candidate);
|
|
20345
20826
|
if (previous) {
|
|
20346
|
-
claudeLog("profile.failover", { from: previous, to: candidate, reason:
|
|
20347
|
-
plog(`[PROXY] PRIORITY failover ${previous} -> ${candidate}`);
|
|
20348
|
-
}
|
|
20349
|
-
return response;
|
|
20350
|
-
}
|
|
20351
|
-
const
|
|
20352
|
-
|
|
20353
|
-
|
|
20354
|
-
|
|
20355
|
-
|
|
20827
|
+
claudeLog("profile.failover", { from: previous, to: candidate, reason: previousReason, sessionKey });
|
|
20828
|
+
plog(`[PROXY] PRIORITY failover ${previous} -> ${candidate} (${previousReason})`);
|
|
20829
|
+
}
|
|
20830
|
+
return sniffed.response;
|
|
20831
|
+
}
|
|
20832
|
+
const reason = sniffed.errorType;
|
|
20833
|
+
const quotaRefusal = isQuotaRefusal(reason);
|
|
20834
|
+
const cooldownUntil = quotaRefusal ? priorityCooldownUntil(candidate, Date.now()) : Date.now() + PRIORITY_DEFAULT_COOLDOWN_MS;
|
|
20835
|
+
priorityExhaustion.mark(candidate, cooldownUntil, reason);
|
|
20836
|
+
claudeLog("priority.exhausted", { profile: candidate, until: cooldownUntil, reason });
|
|
20837
|
+
if (quotaRefusal)
|
|
20838
|
+
refinePriorityCooldown(candidate);
|
|
20839
|
+
lastError = sniffed.errorPayload;
|
|
20840
|
+
lastStatus = inner.status;
|
|
20356
20841
|
previous = candidate;
|
|
20842
|
+
previousReason = reason;
|
|
20357
20843
|
}
|
|
20358
20844
|
if (wantsStream) {
|
|
20359
20845
|
return new Response(`event: error
|
|
@@ -20364,7 +20850,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
20364
20850
|
headers: { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache" }
|
|
20365
20851
|
});
|
|
20366
20852
|
}
|
|
20367
|
-
return new Response(JSON.stringify(lastError), { status:
|
|
20853
|
+
return new Response(JSON.stringify(lastError), { status: lastStatus, headers: { "content-type": "application/json" } });
|
|
20368
20854
|
}
|
|
20369
20855
|
app.use("/auth/*", requireAuth);
|
|
20370
20856
|
app.get("/", (c) => {
|
|
@@ -20562,12 +21048,34 @@ data: ${JSON.stringify(lastError)}
|
|
|
20562
21048
|
if (lineageResult.type === "undo" && adapterBase === "opencode" && !agentSessionId) {
|
|
20563
21049
|
lineageResult = { type: "diverged", reason: "missing-session-header" };
|
|
20564
21050
|
}
|
|
21051
|
+
if (pipeline.some((t) => t.onSession)) {
|
|
21052
|
+
const mismatch = lineageResult.type === "diverged" ? lineageResult.mismatch : undefined;
|
|
21053
|
+
runTransformHook(pipeline, "onSession", {
|
|
21054
|
+
adapter: adapterBase,
|
|
21055
|
+
lineage: lineageResult.type,
|
|
21056
|
+
reason: lineageResult.type === "diverged" ? lineageResult.reason : undefined,
|
|
21057
|
+
sessionKey: profileSessionId,
|
|
21058
|
+
storedCount: mismatch?.storedCount,
|
|
21059
|
+
incomingCount: (body.messages || []).length,
|
|
21060
|
+
prefixOverlap: lineageResult.type === "diverged" ? lineageResult.prefixOverlap : undefined,
|
|
21061
|
+
mismatch: mismatch && mismatch.index >= 0 ? {
|
|
21062
|
+
index: mismatch.index,
|
|
21063
|
+
storedDigest: mismatch.storedDigest,
|
|
21064
|
+
incomingDigest: mismatch.incomingDigest,
|
|
21065
|
+
previousDigest: mismatch.previousDigest,
|
|
21066
|
+
incomingShape: mismatch.incomingShape
|
|
21067
|
+
} : undefined
|
|
21068
|
+
}, adapterBase);
|
|
21069
|
+
}
|
|
20565
21070
|
const isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
|
|
20566
21071
|
const isUndo = lineageResult.type === "undo";
|
|
20567
21072
|
const cachedSession = lineageResult.type !== "diverged" ? lineageResult.session : undefined;
|
|
20568
21073
|
const resumeSessionId = cachedSession?.claudeSessionId;
|
|
21074
|
+
const passthrough = adapter.instancePassthrough !== undefined ? adapter.instancePassthrough : pipelineCtx.passthrough !== undefined ? pipelineCtx.passthrough : envBool("PASSTHROUGH");
|
|
20569
21075
|
const resumeFrom = lineageResult.type === "continuation" || lineageResult.type === "compaction" ? lineageResult.resumeFrom : undefined;
|
|
21076
|
+
const resumeContentFrom = lineageResult.type === "continuation" ? lineageResult.resumeContentFrom : undefined;
|
|
20570
21077
|
const undoRollbackUuid = isUndo && lineageResult.type === "undo" ? lineageResult.rollbackUuid : undefined;
|
|
21078
|
+
const passthroughResumeUuid = passthrough && isResume ? cachedSession?.passthroughResumeUuid : undefined;
|
|
20571
21079
|
const msgSummary = body.messages?.map((m) => {
|
|
20572
21080
|
const contentTypes = Array.isArray(m.content) ? m.content.map((b) => b.type).join(",") : "string";
|
|
20573
21081
|
return `${m.role}[${contentTypes}]`;
|
|
@@ -20610,7 +21118,16 @@ data: ${JSON.stringify(lastError)}
|
|
|
20610
21118
|
if (isUndo && undoRollbackUuid) {
|
|
20611
21119
|
messagesToConvert = getLastUserMessage(allMessages);
|
|
20612
21120
|
} else if (isResume) {
|
|
20613
|
-
if (resumeFrom !== undefined && resumeFrom < allMessages.length) {
|
|
21121
|
+
if (resumeFrom !== undefined && resumeContentFrom !== undefined && resumeFrom < allMessages.length && Array.isArray(allMessages[resumeFrom]?.content)) {
|
|
21122
|
+
const boundaryMessage = allMessages[resumeFrom];
|
|
21123
|
+
messagesToConvert = [
|
|
21124
|
+
{
|
|
21125
|
+
...boundaryMessage,
|
|
21126
|
+
content: boundaryMessage.content.slice(resumeContentFrom)
|
|
21127
|
+
},
|
|
21128
|
+
...allMessages.slice(resumeFrom + 1)
|
|
21129
|
+
];
|
|
21130
|
+
} else if (resumeFrom !== undefined && resumeFrom < allMessages.length) {
|
|
20614
21131
|
messagesToConvert = allMessages.slice(resumeFrom);
|
|
20615
21132
|
} else {
|
|
20616
21133
|
messagesToConvert = getLastUserMessage(allMessages);
|
|
@@ -20659,6 +21176,13 @@ data: ${JSON.stringify(lastError)}
|
|
|
20659
21176
|
if (structuredMessages.length > 1) {
|
|
20660
21177
|
structuredMessages = consolidateMultimodalOntoLastUser(structuredMessages);
|
|
20661
21178
|
}
|
|
21179
|
+
if (passthroughResumeUuid && structuredMessages.length > 0) {
|
|
21180
|
+
structuredMessages.unshift({
|
|
21181
|
+
type: "user",
|
|
21182
|
+
message: { role: "user", content: PASSTHROUGH_CONTINUATION_LEAD_IN },
|
|
21183
|
+
parent_tool_use_id: null
|
|
21184
|
+
});
|
|
21185
|
+
}
|
|
20662
21186
|
} else {
|
|
20663
21187
|
const toolIndex = buildToolUseIndex(allMessages ?? messagesToConvert ?? []);
|
|
20664
21188
|
const promptTurns = (messagesToConvert ?? []).map((m) => {
|
|
@@ -20670,11 +21194,11 @@ data: ${JSON.stringify(lastError)}
|
|
|
20670
21194
|
}
|
|
20671
21195
|
return { role: "user", text: flattenUserContent(m.content, sanitizeOpts, toolIndex) };
|
|
20672
21196
|
});
|
|
20673
|
-
|
|
21197
|
+
const resumeDelta = promptTurns.map((t) => t.text).filter(Boolean).join(`
|
|
20674
21198
|
|
|
20675
|
-
`) || ""
|
|
21199
|
+
`) || "";
|
|
21200
|
+
textPrompt = isResume ? passthroughResumeUuid ? framePassthroughContinuation(resumeDelta) : resumeDelta : frameReplayTurns(promptTurns);
|
|
20676
21201
|
}
|
|
20677
|
-
const passthrough = adapter.instancePassthrough !== undefined ? adapter.instancePassthrough : pipelineCtx.passthrough !== undefined ? pipelineCtx.passthrough : envBool("PASSTHROUGH");
|
|
20678
21202
|
const settingSources = envBool("LOAD_CONTEXT") || sdkFeatures.claudeMd === "full" ? ["user", "project"] : sdkFeatures.claudeMd === "project" ? ["project"] : pipelineCtx.settingSources ?? [];
|
|
20679
21203
|
const capturedToolUses = [];
|
|
20680
21204
|
const capturedSignatures = new Set;
|
|
@@ -20842,6 +21366,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
20842
21366
|
claudeLog("upstream.start", { mode: "non_stream", model });
|
|
20843
21367
|
let lastUsage;
|
|
20844
21368
|
let lastStopReason;
|
|
21369
|
+
let nextPassthroughResumeUuid;
|
|
20845
21370
|
try {
|
|
20846
21371
|
if (!claudeExecutable) {
|
|
20847
21372
|
claudeExecutable = await resolveClaudeExecutableAsync();
|
|
@@ -20878,8 +21403,8 @@ data: ${JSON.stringify(lastError)}
|
|
|
20878
21403
|
hasDeferredTools,
|
|
20879
21404
|
resumeSessionId,
|
|
20880
21405
|
isUndo,
|
|
20881
|
-
undoRollbackUuid,
|
|
20882
|
-
forkSession: busySessionFork || undefined,
|
|
21406
|
+
resumeSessionAtUuid: undoRollbackUuid ?? passthroughResumeUuid,
|
|
21407
|
+
forkSession: busySessionFork || Boolean(passthroughResumeUuid) || undefined,
|
|
20883
21408
|
sdkHooks,
|
|
20884
21409
|
blockedTools: pipelineCtx.blockedTools,
|
|
20885
21410
|
incompatibleTools: pipelineCtx.incompatibleTools,
|
|
@@ -20962,7 +21487,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
20962
21487
|
hasDeferredTools,
|
|
20963
21488
|
resumeSessionId: undefined,
|
|
20964
21489
|
isUndo: false,
|
|
20965
|
-
|
|
21490
|
+
resumeSessionAtUuid: undefined,
|
|
20966
21491
|
sdkHooks,
|
|
20967
21492
|
blockedTools: pipelineCtx.blockedTools,
|
|
20968
21493
|
incompatibleTools: pipelineCtx.incompatibleTools,
|
|
@@ -21031,7 +21556,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
21031
21556
|
hasDeferredTools,
|
|
21032
21557
|
resumeSessionId: undefined,
|
|
21033
21558
|
isUndo: false,
|
|
21034
|
-
|
|
21559
|
+
resumeSessionAtUuid: undefined,
|
|
21035
21560
|
sdkHooks,
|
|
21036
21561
|
blockedTools: pipelineCtx.blockedTools,
|
|
21037
21562
|
incompatibleTools: pipelineCtx.incompatibleTools,
|
|
@@ -21112,6 +21637,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
21112
21637
|
if (message.type === "assistant") {
|
|
21113
21638
|
noteAssistantContent(earlyStop, message.message?.content);
|
|
21114
21639
|
} else if (message.type === "user") {
|
|
21640
|
+
nextPassthroughResumeUuid = resumeBoundaryUuid(message) ?? nextPassthroughResumeUuid;
|
|
21115
21641
|
noteUserContent(earlyStop, message.message?.content);
|
|
21116
21642
|
if (shouldEarlyStop(earlyStop)) {
|
|
21117
21643
|
earlyStopFired = true;
|
|
@@ -21351,7 +21877,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
21351
21877
|
]);
|
|
21352
21878
|
}
|
|
21353
21879
|
if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
|
|
21354
|
-
storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
|
|
21880
|
+
storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughResumeUuid : null);
|
|
21355
21881
|
}
|
|
21356
21882
|
const responseSessionId = currentSessionId || resumeSessionId || `session_${Date.now()}`;
|
|
21357
21883
|
return new Response(JSON.stringify({
|
|
@@ -21383,6 +21909,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
21383
21909
|
let streamEventsSeen = 0;
|
|
21384
21910
|
let eventsForwarded = 0;
|
|
21385
21911
|
let textEventsForwarded = 0;
|
|
21912
|
+
let textCharsForwarded = 0;
|
|
21386
21913
|
let bytesSent = 0;
|
|
21387
21914
|
let streamClosed = false;
|
|
21388
21915
|
let awaitingEarlyStopDrain = false;
|
|
@@ -21414,7 +21941,30 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
21414
21941
|
let lastUsage;
|
|
21415
21942
|
let hasStructuredOutput = false;
|
|
21416
21943
|
let structuredOutput;
|
|
21944
|
+
let nextPassthroughResumeUuid;
|
|
21945
|
+
const silentTurnRecoveryEnabled = env("SILENT_TURN_RECOVERY") !== "0";
|
|
21946
|
+
let silentTurnRecoveryAttempted = false;
|
|
21947
|
+
let silentTurnRecovered = false;
|
|
21417
21948
|
const streamedToolUseIds = new Set;
|
|
21949
|
+
let pendingTerminalDelta = null;
|
|
21950
|
+
let terminalDeltaSent = false;
|
|
21951
|
+
const sendTerminalDelta = (stopReasonOverride) => {
|
|
21952
|
+
if (terminalDeltaSent)
|
|
21953
|
+
return;
|
|
21954
|
+
const payload = stopReasonOverride ? encoder.encode(`event: message_delta
|
|
21955
|
+
data: ${JSON.stringify({
|
|
21956
|
+
type: "message_delta",
|
|
21957
|
+
delta: { stop_reason: stopReasonOverride, stop_sequence: null },
|
|
21958
|
+
usage: { output_tokens: lastUsage?.output_tokens ?? 0 }
|
|
21959
|
+
})}
|
|
21960
|
+
|
|
21961
|
+
`) : pendingTerminalDelta;
|
|
21962
|
+
if (!payload)
|
|
21963
|
+
return;
|
|
21964
|
+
terminalDeltaSent = true;
|
|
21965
|
+
if (safeEnqueue(payload, "terminal_message_delta"))
|
|
21966
|
+
eventsForwarded += 1;
|
|
21967
|
+
};
|
|
21418
21968
|
const openClientBlocks = new Set;
|
|
21419
21969
|
const resolvePendingStore = passthrough && earlyStopEnabled && !isIndependentSession && profileSessionId ? registerPendingStore(profileSessionId) : () => {};
|
|
21420
21970
|
let pendingEarlyStop = false;
|
|
@@ -21430,10 +21980,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
21430
21980
|
});
|
|
21431
21981
|
pendingEarlyStop = false;
|
|
21432
21982
|
flushOpenClientBlocks("early_stop");
|
|
21433
|
-
|
|
21434
|
-
data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "tool_use", stop_sequence: null }, usage: { output_tokens: lastUsage?.output_tokens ?? 0 } })}
|
|
21435
|
-
|
|
21436
|
-
`), "early_stop");
|
|
21983
|
+
sendTerminalDelta("tool_use");
|
|
21437
21984
|
safeEnqueue(encoder.encode(`event: message_stop
|
|
21438
21985
|
data: ${JSON.stringify({ type: "message_stop" })}
|
|
21439
21986
|
|
|
@@ -21463,8 +22010,8 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
21463
22010
|
}
|
|
21464
22011
|
openClientBlocks.clear();
|
|
21465
22012
|
};
|
|
22013
|
+
let currentSessionId;
|
|
21466
22014
|
try {
|
|
21467
|
-
let currentSessionId;
|
|
21468
22015
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
21469
22016
|
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
21470
22017
|
const response = async function* () {
|
|
@@ -21496,8 +22043,8 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
21496
22043
|
hasDeferredTools,
|
|
21497
22044
|
resumeSessionId,
|
|
21498
22045
|
isUndo,
|
|
21499
|
-
undoRollbackUuid,
|
|
21500
|
-
forkSession: busySessionFork || undefined,
|
|
22046
|
+
resumeSessionAtUuid: undoRollbackUuid ?? passthroughResumeUuid,
|
|
22047
|
+
forkSession: busySessionFork || Boolean(passthroughResumeUuid) || undefined,
|
|
21501
22048
|
sdkHooks,
|
|
21502
22049
|
blockedTools: pipelineCtx.blockedTools,
|
|
21503
22050
|
incompatibleTools: pipelineCtx.incompatibleTools,
|
|
@@ -21579,7 +22126,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
21579
22126
|
hasDeferredTools,
|
|
21580
22127
|
resumeSessionId: undefined,
|
|
21581
22128
|
isUndo: false,
|
|
21582
|
-
|
|
22129
|
+
resumeSessionAtUuid: undefined,
|
|
21583
22130
|
sdkHooks,
|
|
21584
22131
|
blockedTools: pipelineCtx.blockedTools,
|
|
21585
22132
|
incompatibleTools: pipelineCtx.incompatibleTools,
|
|
@@ -21648,7 +22195,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
21648
22195
|
hasDeferredTools,
|
|
21649
22196
|
resumeSessionId: undefined,
|
|
21650
22197
|
isUndo: false,
|
|
21651
|
-
|
|
22198
|
+
resumeSessionAtUuid: undefined,
|
|
21652
22199
|
sdkHooks,
|
|
21653
22200
|
blockedTools: pipelineCtx.blockedTools,
|
|
21654
22201
|
incompatibleTools: pipelineCtx.incompatibleTools,
|
|
@@ -21761,6 +22308,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
21761
22308
|
if (message.type === "assistant" && message.uuid) {
|
|
21762
22309
|
sdkUuidMap.push(message.uuid);
|
|
21763
22310
|
}
|
|
22311
|
+
nextPassthroughResumeUuid = resumeBoundaryUuid(message) ?? nextPassthroughResumeUuid;
|
|
21764
22312
|
if (earlyStopEnabled) {
|
|
21765
22313
|
if (message.type === "assistant") {
|
|
21766
22314
|
noteAssistantContent(earlyStop, message.message?.content);
|
|
@@ -21832,10 +22380,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
21832
22380
|
if (messageStartEmitted) {
|
|
21833
22381
|
if (passthrough && streamedToolUseIds.size > 0) {
|
|
21834
22382
|
flushOpenClientBlocks("turn2_suppression");
|
|
21835
|
-
|
|
21836
|
-
data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "tool_use", stop_sequence: null }, usage: { output_tokens: lastUsage?.output_tokens ?? 0 } })}
|
|
21837
|
-
|
|
21838
|
-
`), "passthrough_turn2_stop");
|
|
22383
|
+
sendTerminalDelta("tool_use");
|
|
21839
22384
|
safeEnqueue(encoder.encode(`event: message_stop
|
|
21840
22385
|
data: ${JSON.stringify({ type: "message_stop" })}
|
|
21841
22386
|
|
|
@@ -21939,15 +22484,26 @@ data: ${JSON.stringify({
|
|
|
21939
22484
|
}
|
|
21940
22485
|
}
|
|
21941
22486
|
}
|
|
22487
|
+
if (eventType === "content_block_delta" && event.delta?.type === "text_delta" && shouldInjectSilentTurn({
|
|
22488
|
+
raw: env("DEBUG_FORCE_SILENT_TURN"),
|
|
22489
|
+
sessionId: agentSessionId
|
|
22490
|
+
})) {
|
|
22491
|
+
claudeLog("debug.silent_turn_injected", { sessionId: agentSessionId });
|
|
22492
|
+
continue;
|
|
22493
|
+
}
|
|
21942
22494
|
stripNonStandardStreamFields(event);
|
|
21943
22495
|
const payload = encoder.encode(`event: ${eventType}
|
|
21944
22496
|
data: ${JSON.stringify(event)}
|
|
21945
22497
|
|
|
21946
22498
|
`);
|
|
21947
|
-
if (
|
|
21948
|
-
|
|
22499
|
+
if (eventType === "message_delta") {
|
|
22500
|
+
pendingTerminalDelta = payload;
|
|
22501
|
+
} else {
|
|
22502
|
+
if (!safeEnqueue(payload, `stream_event:${eventType}`)) {
|
|
22503
|
+
break;
|
|
22504
|
+
}
|
|
22505
|
+
eventsForwarded += 1;
|
|
21949
22506
|
}
|
|
21950
|
-
eventsForwarded += 1;
|
|
21951
22507
|
if (eventType === "content_block_start") {
|
|
21952
22508
|
const idx = event.index;
|
|
21953
22509
|
if (typeof idx === "number")
|
|
@@ -21963,6 +22519,7 @@ data: ${JSON.stringify(event)}
|
|
|
21963
22519
|
}
|
|
21964
22520
|
if (passthrough && eventType === "message_delta" && event.delta?.stop_reason === "tool_use" && streamedToolUseIds.size > 0) {
|
|
21965
22521
|
flushOpenClientBlocks("drain_close");
|
|
22522
|
+
sendTerminalDelta();
|
|
21966
22523
|
safeEnqueue(encoder.encode(`event: message_stop
|
|
21967
22524
|
data: ${JSON.stringify({ type: "message_stop" })}
|
|
21968
22525
|
|
|
@@ -21979,6 +22536,8 @@ data: ${JSON.stringify({ type: "message_stop" })}
|
|
|
21979
22536
|
const delta = event.delta;
|
|
21980
22537
|
if (delta?.type === "text_delta") {
|
|
21981
22538
|
textEventsForwarded += 1;
|
|
22539
|
+
if (typeof delta.text === "string")
|
|
22540
|
+
textCharsForwarded += delta.text.length;
|
|
21982
22541
|
}
|
|
21983
22542
|
}
|
|
21984
22543
|
}
|
|
@@ -22040,6 +22599,7 @@ data: ${JSON.stringify({
|
|
|
22040
22599
|
messageStartEmitted = true;
|
|
22041
22600
|
eventsForwarded += 5;
|
|
22042
22601
|
textEventsForwarded += 1;
|
|
22602
|
+
textCharsForwarded += text.length;
|
|
22043
22603
|
}
|
|
22044
22604
|
if (passthrough) {
|
|
22045
22605
|
recordEnvelopeViolations(checkUndeliveredToolUses(capturedToolUses, streamedToolUseIds));
|
|
@@ -22065,9 +22625,125 @@ data: ${JSON.stringify({
|
|
|
22065
22625
|
plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
|
|
22066
22626
|
}
|
|
22067
22627
|
if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
|
|
22068
|
-
storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
|
|
22628
|
+
storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughResumeUuid : null);
|
|
22069
22629
|
}
|
|
22070
22630
|
resolvePendingStore();
|
|
22631
|
+
const classifyNow = () => classifyTurnOutcome({
|
|
22632
|
+
textEvents: textEventsForwarded,
|
|
22633
|
+
toolUses: streamedToolUseIds.size,
|
|
22634
|
+
blocksForwarded: eventsForwarded
|
|
22635
|
+
});
|
|
22636
|
+
const preRecoveryOutcome = classifyNow();
|
|
22637
|
+
if (!streamClosed && messageStartEmitted && shouldAttemptRecovery({
|
|
22638
|
+
outcome: preRecoveryOutcome,
|
|
22639
|
+
alreadyAttempted: silentTurnRecoveryAttempted,
|
|
22640
|
+
clientGone: streamClosed,
|
|
22641
|
+
sessionId: currentSessionId || resumeSessionId,
|
|
22642
|
+
enabled: silentTurnRecoveryEnabled
|
|
22643
|
+
})) {
|
|
22644
|
+
silentTurnRecoveryAttempted = true;
|
|
22645
|
+
const capturedBeforeRecovery = capturedToolUses.length;
|
|
22646
|
+
claudeLog("response.silent_turn_recovery", {
|
|
22647
|
+
mode: "stream",
|
|
22648
|
+
kind: preRecoveryOutcome.kind,
|
|
22649
|
+
reason: preRecoveryOutcome.kind === "silent" ? preRecoveryOutcome.reason : undefined,
|
|
22650
|
+
sdkSessionId: currentSessionId || resumeSessionId
|
|
22651
|
+
});
|
|
22652
|
+
const recoveryLifter = createRecoveryLifter(() => nextClientBlockIndex++);
|
|
22653
|
+
let recoverySessionId;
|
|
22654
|
+
let recoveryBoundaryUuid;
|
|
22655
|
+
try {
|
|
22656
|
+
for await (const event of guardUpstreamIdle(query(buildQueryOptions({
|
|
22657
|
+
prompt: SILENT_TURN_NUDGE,
|
|
22658
|
+
model,
|
|
22659
|
+
workingDirectory,
|
|
22660
|
+
clientWorkingDirectory,
|
|
22661
|
+
systemContext,
|
|
22662
|
+
claudeExecutable,
|
|
22663
|
+
passthrough,
|
|
22664
|
+
stream: true,
|
|
22665
|
+
sdkAgents,
|
|
22666
|
+
passthroughMcp,
|
|
22667
|
+
cleanEnv: profileEnv,
|
|
22668
|
+
envOverrides,
|
|
22669
|
+
hasDeferredTools,
|
|
22670
|
+
resumeSessionId: currentSessionId || resumeSessionId,
|
|
22671
|
+
isUndo: false,
|
|
22672
|
+
resumeSessionAtUuid: nextPassthroughResumeUuid,
|
|
22673
|
+
forkSession: true,
|
|
22674
|
+
sdkHooks,
|
|
22675
|
+
blockedTools: pipelineCtx.blockedTools,
|
|
22676
|
+
incompatibleTools: pipelineCtx.incompatibleTools,
|
|
22677
|
+
mcpServerName: adapter.getMcpServerName(),
|
|
22678
|
+
allowedMcpTools: pipelineCtx.allowedMcpTools,
|
|
22679
|
+
onStderr,
|
|
22680
|
+
effort,
|
|
22681
|
+
thinking,
|
|
22682
|
+
taskBudget,
|
|
22683
|
+
outputFormat,
|
|
22684
|
+
betas,
|
|
22685
|
+
settingSources,
|
|
22686
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt,
|
|
22687
|
+
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
22688
|
+
memory: sdkFeatures.memory,
|
|
22689
|
+
dreaming: sdkFeatures.dreaming,
|
|
22690
|
+
sharedMemory: sdkFeatures.sharedMemory,
|
|
22691
|
+
webFetchPreflight: sdkFeatures.webFetchPreflight,
|
|
22692
|
+
claudeAiConnectors: sdkFeatures.claudeAiConnectors,
|
|
22693
|
+
maxBudgetUsd: sdkFeatures.maxBudgetUsd,
|
|
22694
|
+
fallbackModel: sdkFeatures.fallbackModel,
|
|
22695
|
+
sdkDebug: sdkFeatures.sdkDebug,
|
|
22696
|
+
additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
|
|
22697
|
+
advisorModel
|
|
22698
|
+
}, requestAbort.controller)), UPSTREAM_IDLE_MS, (sinceLastMs) => claudeLog("upstream.stalled", { mode: "silent_recovery", model, sinceLastMs }))) {
|
|
22699
|
+
const recoveryMessage = event;
|
|
22700
|
+
if (recoveryMessage.session_id)
|
|
22701
|
+
recoverySessionId = recoveryMessage.session_id;
|
|
22702
|
+
recoveryBoundaryUuid = resumeBoundaryUuid(recoveryMessage) ?? recoveryBoundaryUuid;
|
|
22703
|
+
if (recoveryMessage.type !== "stream_event")
|
|
22704
|
+
continue;
|
|
22705
|
+
const lifted = recoveryLifter.lift(event.event);
|
|
22706
|
+
if (!lifted)
|
|
22707
|
+
continue;
|
|
22708
|
+
safeEnqueue(encoder.encode(`event: ${lifted.frame.type}
|
|
22709
|
+
data: ${JSON.stringify(lifted.frame)}
|
|
22710
|
+
|
|
22711
|
+
`), `silent_recovery_${lifted.kind}`);
|
|
22712
|
+
if (lifted.kind === "block_start") {
|
|
22713
|
+
eventsForwarded += 1;
|
|
22714
|
+
} else if (lifted.kind === "text_delta") {
|
|
22715
|
+
textEventsForwarded += 1;
|
|
22716
|
+
textCharsForwarded += lifted.textChars;
|
|
22717
|
+
silentTurnRecovered = true;
|
|
22718
|
+
}
|
|
22719
|
+
}
|
|
22720
|
+
} catch (recoveryError) {
|
|
22721
|
+
claudeLog("response.silent_turn_recovery_failed", {
|
|
22722
|
+
mode: "stream",
|
|
22723
|
+
error: recoveryError instanceof Error ? recoveryError.message : String(recoveryError)
|
|
22724
|
+
});
|
|
22725
|
+
}
|
|
22726
|
+
if (capturedToolUses.length > capturedBeforeRecovery) {
|
|
22727
|
+
silentTurnRecovered = true;
|
|
22728
|
+
}
|
|
22729
|
+
if (silentTurnRecovered && recoverySessionId && !isIndependentSession && !sawDuplicateToolUse) {
|
|
22730
|
+
currentSessionId = recoverySessionId;
|
|
22731
|
+
nextPassthroughResumeUuid = recoveryBoundaryUuid;
|
|
22732
|
+
sdkUuidMap.length = 0;
|
|
22733
|
+
for (let i = 0;i < allMessages.length; i++)
|
|
22734
|
+
sdkUuidMap.push(null);
|
|
22735
|
+
storeSession(profileSessionId, body.messages || [], recoverySessionId, profileScopedCwd, sdkUuidMap, lastUsage, recoveryBoundaryUuid ?? null);
|
|
22736
|
+
}
|
|
22737
|
+
claudeLog("response.silent_turn_recovery_result", {
|
|
22738
|
+
mode: "stream",
|
|
22739
|
+
recovered: silentTurnRecovered,
|
|
22740
|
+
textEvents: textEventsForwarded,
|
|
22741
|
+
forkedSession: recoverySessionId ?? null
|
|
22742
|
+
});
|
|
22743
|
+
if (silentTurnRecovered && preRecoveryOutcome.kind === "silent") {
|
|
22744
|
+
diagnosticLog2.session(`${requestMeta.requestId} silent_turn reason=${preRecoveryOutcome.reason} ` + `blocks=${eventsForwarded} out=${lastUsage?.output_tokens ?? 0} ` + `recovery=succeeded`, requestMeta.requestId);
|
|
22745
|
+
}
|
|
22746
|
+
}
|
|
22071
22747
|
if (!streamClosed) {
|
|
22072
22748
|
const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
|
|
22073
22749
|
if (passthrough && unseenToolUses.length > 0 && messageStartEmitted) {
|
|
@@ -22099,14 +22775,7 @@ data: ${JSON.stringify({
|
|
|
22099
22775
|
|
|
22100
22776
|
`), "passthrough_tool_block_stop");
|
|
22101
22777
|
}
|
|
22102
|
-
|
|
22103
|
-
data: ${JSON.stringify({
|
|
22104
|
-
type: "message_delta",
|
|
22105
|
-
delta: { stop_reason: "tool_use", stop_sequence: null },
|
|
22106
|
-
usage: { output_tokens: 0 }
|
|
22107
|
-
})}
|
|
22108
|
-
|
|
22109
|
-
`), "passthrough_message_delta");
|
|
22778
|
+
sendTerminalDelta("tool_use");
|
|
22110
22779
|
}
|
|
22111
22780
|
if (trackFileChanges && passthrough && pipelineCtx.extractFileChangesFromToolUse) {
|
|
22112
22781
|
const passthroughChanges = extractFileChangesFromMessages(body.messages || [], pipelineCtx.extractFileChangesFromToolUse);
|
|
@@ -22143,6 +22812,7 @@ data: ${JSON.stringify({
|
|
|
22143
22812
|
}
|
|
22144
22813
|
}
|
|
22145
22814
|
if (messageStartEmitted) {
|
|
22815
|
+
sendTerminalDelta();
|
|
22146
22816
|
safeEnqueue(encoder.encode(`event: message_stop
|
|
22147
22817
|
data: {"type":"message_stop"}
|
|
22148
22818
|
|
|
@@ -22208,13 +22878,18 @@ data: {"type":"message_stop"}
|
|
|
22208
22878
|
cacheHitRate: computeCacheHitRate(lastUsage),
|
|
22209
22879
|
...envelopeViolations.length > 0 ? { envelopeViolations: [...envelopeViolations] } : {}
|
|
22210
22880
|
});
|
|
22211
|
-
|
|
22212
|
-
|
|
22881
|
+
const turnOutcome = classifyNow();
|
|
22882
|
+
if (turnOutcome.kind === "silent") {
|
|
22883
|
+
claudeLog("response.silent_turn", {
|
|
22213
22884
|
model,
|
|
22885
|
+
reason: turnOutcome.reason,
|
|
22214
22886
|
streamEventsSeen,
|
|
22215
22887
|
eventsForwarded,
|
|
22216
|
-
|
|
22888
|
+
outputTokens: lastUsage?.output_tokens,
|
|
22889
|
+
recovered: silentTurnRecovered,
|
|
22890
|
+
recoveryAttempted: silentTurnRecoveryAttempted
|
|
22217
22891
|
});
|
|
22892
|
+
diagnosticLog2.session(`${requestMeta.requestId} silent_turn reason=${turnOutcome.reason} ` + `blocks=${eventsForwarded} out=${lastUsage?.output_tokens ?? 0} ` + `recovery=${silentTurnRecoveryAttempted ? silentTurnRecovered ? "succeeded" : "failed" : "off"}`, requestMeta.requestId);
|
|
22218
22893
|
}
|
|
22219
22894
|
}
|
|
22220
22895
|
} catch (error) {
|
|
@@ -22227,6 +22902,21 @@ data: {"type":"message_stop"}
|
|
|
22227
22902
|
textEventsForwarded,
|
|
22228
22903
|
durationMs: Date.now() - requestStartAt
|
|
22229
22904
|
});
|
|
22905
|
+
const disposition = clientAbortDisposition({
|
|
22906
|
+
isIndependentSession,
|
|
22907
|
+
profileSessionId,
|
|
22908
|
+
currentSessionId,
|
|
22909
|
+
sawDuplicateToolUse,
|
|
22910
|
+
resumeBoundaryUuid: nextPassthroughResumeUuid,
|
|
22911
|
+
passthrough
|
|
22912
|
+
});
|
|
22913
|
+
if (disposition.action === "store" && currentSessionId) {
|
|
22914
|
+
storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, disposition.resumeUuid);
|
|
22915
|
+
} else if (disposition.action === "evict") {
|
|
22916
|
+
evictSession(profileSessionId, profileScopedCwd, body.messages || []);
|
|
22917
|
+
}
|
|
22918
|
+
claudeLog("passthrough.client_abort_settled", { action: disposition.action });
|
|
22919
|
+
resolvePendingStore();
|
|
22230
22920
|
return;
|
|
22231
22921
|
}
|
|
22232
22922
|
resolvePendingStore();
|
|
@@ -22380,26 +23070,41 @@ data: {"type":"message_stop"}
|
|
|
22380
23070
|
error: streamErr.type
|
|
22381
23071
|
});
|
|
22382
23072
|
if (messageStartEmitted) {
|
|
23073
|
+
const errorStopReason = textEventsForwarded > 0 ? "end_turn" : "max_tokens";
|
|
23074
|
+
claudeLog("response.error_envelope", {
|
|
23075
|
+
mode: "stream",
|
|
23076
|
+
stopReason: errorStopReason,
|
|
23077
|
+
textEvents: textEventsForwarded,
|
|
23078
|
+
classified: streamErr.type
|
|
23079
|
+
});
|
|
22383
23080
|
safeEnqueue(encoder.encode(`event: message_delta
|
|
22384
23081
|
data: ${JSON.stringify({
|
|
22385
23082
|
type: "message_delta",
|
|
22386
|
-
delta: { stop_reason:
|
|
23083
|
+
delta: { stop_reason: errorStopReason, stop_sequence: null },
|
|
22387
23084
|
usage: { output_tokens: 0 }
|
|
22388
23085
|
})}
|
|
22389
23086
|
|
|
22390
23087
|
`), "error_message_delta");
|
|
23088
|
+
safeEnqueue(encoder.encode(`event: error
|
|
23089
|
+
data: ${JSON.stringify({
|
|
23090
|
+
type: "error",
|
|
23091
|
+
error: { type: streamErr.type, message: streamErr.message }
|
|
23092
|
+
})}
|
|
23093
|
+
|
|
23094
|
+
`), "error_event_before_stop");
|
|
22391
23095
|
safeEnqueue(encoder.encode(`event: message_stop
|
|
22392
23096
|
data: {"type":"message_stop"}
|
|
22393
23097
|
|
|
22394
23098
|
`), "error_message_stop");
|
|
22395
|
-
}
|
|
22396
|
-
|
|
23099
|
+
} else {
|
|
23100
|
+
safeEnqueue(encoder.encode(`event: error
|
|
22397
23101
|
data: ${JSON.stringify({
|
|
22398
|
-
|
|
22399
|
-
|
|
22400
|
-
|
|
23102
|
+
type: "error",
|
|
23103
|
+
error: { type: streamErr.type, message: streamErr.message }
|
|
23104
|
+
})}
|
|
22401
23105
|
|
|
22402
23106
|
`), "error_event");
|
|
23107
|
+
}
|
|
22403
23108
|
if (!streamClosed) {
|
|
22404
23109
|
try {
|
|
22405
23110
|
controller.close();
|
|
@@ -22657,7 +23362,7 @@ data: ${JSON.stringify({
|
|
|
22657
23362
|
});
|
|
22658
23363
|
});
|
|
22659
23364
|
app.get("/profiles", async (c) => {
|
|
22660
|
-
const { profilePageHtml } = await import("./profilePage-
|
|
23365
|
+
const { profilePageHtml } = await import("./profilePage-gtazq15d.js");
|
|
22661
23366
|
return c.html(profilePageHtml);
|
|
22662
23367
|
});
|
|
22663
23368
|
app.post("/profiles/active", async (c) => {
|
|
@@ -22740,14 +23445,25 @@ data: ${JSON.stringify({
|
|
|
22740
23445
|
});
|
|
22741
23446
|
app.post("/v1/chat/completions", async (c) => {
|
|
22742
23447
|
const rawBody = await c.req.json();
|
|
22743
|
-
const
|
|
23448
|
+
const userAgent = c.req.header("user-agent") ?? "";
|
|
23449
|
+
const jcodeSessionId = userAgent.startsWith("jcode/") ? normalizeJcodeSessionId(c.req.header("x-jcode-session")) : undefined;
|
|
23450
|
+
const isJcode = jcodeSessionId !== undefined;
|
|
23451
|
+
const adapterName = isJcode ? "jcode" : "openai";
|
|
23452
|
+
const anthropicBody = translateOpenAiToAnthropic(rawBody, {
|
|
23453
|
+
preserveConversationHistory: isJcode
|
|
23454
|
+
});
|
|
22744
23455
|
if (!anthropicBody) {
|
|
22745
23456
|
return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
|
|
22746
23457
|
}
|
|
22747
23458
|
const internalHeaders = {
|
|
22748
23459
|
"Content-Type": "application/json",
|
|
22749
|
-
"x-meridian-agent":
|
|
23460
|
+
"x-meridian-agent": adapterName
|
|
22750
23461
|
};
|
|
23462
|
+
if (jcodeSessionId)
|
|
23463
|
+
internalHeaders["x-jcode-session"] = jcodeSessionId;
|
|
23464
|
+
const requestedProfile = c.req.header("x-meridian-profile");
|
|
23465
|
+
if (requestedProfile)
|
|
23466
|
+
internalHeaders["x-meridian-profile"] = requestedProfile;
|
|
22751
23467
|
const xApiKey = c.req.header("x-api-key");
|
|
22752
23468
|
if (xApiKey)
|
|
22753
23469
|
internalHeaders["x-api-key"] = xApiKey;
|
|
@@ -22768,7 +23484,7 @@ data: ${JSON.stringify({
|
|
|
22768
23484
|
const created = Math.floor(Date.now() / 1000);
|
|
22769
23485
|
const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : CANONICAL_SONNET_MODEL;
|
|
22770
23486
|
const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
|
|
22771
|
-
const sdkFeatures = getFeaturesForAdapter2(
|
|
23487
|
+
const sdkFeatures = getFeaturesForAdapter2(adapterName);
|
|
22772
23488
|
if (!anthropicBody.stream) {
|
|
22773
23489
|
const anthropicRes = await internalRes.json();
|
|
22774
23490
|
return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created, {
|
|
@@ -23014,7 +23730,7 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
|
|
|
23014
23730
|
const profilesList = getEffectiveProfiles(finalConfig.profiles);
|
|
23015
23731
|
const activeId = getActiveProfileId() || finalConfig.defaultProfile || profilesList[0]?.id || null;
|
|
23016
23732
|
if (profilesList.length === 0) {
|
|
23017
|
-
const oauth = await
|
|
23733
|
+
const { snapshot: oauth, error } = await fetchOAuthUsageResult({});
|
|
23018
23734
|
return c.json({
|
|
23019
23735
|
profiles: [{
|
|
23020
23736
|
id: "default",
|
|
@@ -23022,7 +23738,7 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
|
|
|
23022
23738
|
windows: oauth?.windows ?? [],
|
|
23023
23739
|
extraUsage: oauth?.extraUsage ?? null,
|
|
23024
23740
|
fetchedAt: oauth?.fetchedAt ?? null,
|
|
23025
|
-
error
|
|
23741
|
+
error
|
|
23026
23742
|
}],
|
|
23027
23743
|
activeProfile: "default",
|
|
23028
23744
|
asOf: Date.now()
|
|
@@ -23041,7 +23757,7 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
|
|
|
23041
23757
|
error: "not_oauth"
|
|
23042
23758
|
};
|
|
23043
23759
|
}
|
|
23044
|
-
const oauth = await
|
|
23760
|
+
const { snapshot: oauth, error } = await fetchOAuthUsageResult({
|
|
23045
23761
|
profileId: p.id,
|
|
23046
23762
|
claudeConfigDir: p.claudeConfigDir
|
|
23047
23763
|
});
|
|
@@ -23052,7 +23768,7 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
|
|
|
23052
23768
|
windows: oauth?.windows ?? [],
|
|
23053
23769
|
extraUsage: oauth?.extraUsage ?? null,
|
|
23054
23770
|
fetchedAt: oauth?.fetchedAt ?? null,
|
|
23055
|
-
error
|
|
23771
|
+
error
|
|
23056
23772
|
};
|
|
23057
23773
|
}));
|
|
23058
23774
|
return c.json({
|