@rynfar/meridian 1.59.0 → 1.61.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.
Files changed (35) hide show
  1. package/README.md +2 -1
  2. package/dist/{cli-wqvrd0yy.js → cli-tbhba0cv.js} +687 -231
  3. package/dist/cli.js +1 -1
  4. package/dist/{profilePage-naychnb8.js → profilePage-gtazq15d.js} +9 -0
  5. package/dist/proxy/adapters/detect.d.ts.map +1 -1
  6. package/dist/proxy/adapters/jcode.d.ts +13 -0
  7. package/dist/proxy/adapters/jcode.d.ts.map +1 -0
  8. package/dist/proxy/errors.d.ts.map +1 -1
  9. package/dist/proxy/messages.d.ts +29 -0
  10. package/dist/proxy/messages.d.ts.map +1 -1
  11. package/dist/proxy/oauthUsage.d.ts +34 -1
  12. package/dist/proxy/oauthUsage.d.ts.map +1 -1
  13. package/dist/proxy/openai.d.ts +5 -1
  14. package/dist/proxy/openai.d.ts.map +1 -1
  15. package/dist/proxy/passthroughEarlyStop.d.ts +48 -0
  16. package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
  17. package/dist/proxy/query.d.ts +5 -2
  18. package/dist/proxy/query.d.ts.map +1 -1
  19. package/dist/proxy/sdkFeatures.d.ts +8 -0
  20. package/dist/proxy/sdkFeatures.d.ts.map +1 -1
  21. package/dist/proxy/server.d.ts.map +1 -1
  22. package/dist/proxy/session/cache.d.ts +1 -1
  23. package/dist/proxy/session/cache.d.ts.map +1 -1
  24. package/dist/proxy/session/lineage.d.ts +12 -0
  25. package/dist/proxy/session/lineage.d.ts.map +1 -1
  26. package/dist/proxy/sessionStore.d.ts +5 -1
  27. package/dist/proxy/sessionStore.d.ts.map +1 -1
  28. package/dist/proxy/transforms/registry.d.ts.map +1 -1
  29. package/dist/proxy/turnOutcome.d.ts +179 -0
  30. package/dist/proxy/turnOutcome.d.ts.map +1 -0
  31. package/dist/server.js +1 -1
  32. package/dist/telemetry/profilePage.d.ts.map +1 -1
  33. package/dist/telemetry/settingsPage.d.ts +1 -1
  34. package/dist/telemetry/settingsPage.d.ts.map +1 -1
  35. 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;
@@ -1949,7 +2083,15 @@ function describeToolCall(info) {
1949
2083
  const head = info.target ? `your ${info.name} ${info.target}` : `your ${info.name}`;
1950
2084
  return info.contentSummary ? `[${head} → "${info.contentSummary}"]` : `[${head}]`;
1951
2085
  }
1952
- var HASH_IGNORED_BLOCK_TYPES, HASH_HANDLED_BLOCK_TYPES, HASH_SERIALIZED_BLOCK_TYPES, MULTIMODAL_TYPES, TOOL_TARGET_KEYS, CONTENT_SUMMARY_MAX = 120;
2086
+ function extractSystemText(system) {
2087
+ if (typeof system === "string")
2088
+ return system;
2089
+ if (!Array.isArray(system))
2090
+ return "";
2091
+ 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(`
2092
+ `);
2093
+ }
2094
+ var HASH_IGNORED_BLOCK_TYPES, HASH_HANDLED_BLOCK_TYPES, HASH_SERIALIZED_BLOCK_TYPES, PASSTHROUGH_CONTINUATION_LEAD_IN, MULTIMODAL_TYPES, TOOL_TARGET_KEYS, CONTENT_SUMMARY_MAX = 120, TRANSPORT_HEADER_BLOCK;
1953
2095
  var init_messages = __esm(() => {
1954
2096
  HASH_IGNORED_BLOCK_TYPES = new Set(["thinking", "redacted_thinking"]);
1955
2097
  HASH_HANDLED_BLOCK_TYPES = new Set(["text", "tool_use", "tool_result"]);
@@ -1966,137 +2108,12 @@ var init_messages = __esm(() => {
1966
2108
  "tool_search_tool_result",
1967
2109
  "container_upload"
1968
2110
  ]);
2111
+ 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.";
1969
2112
  MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
1970
2113
  TOOL_TARGET_KEYS = ["filePath", "file_path", "path", "command", "pattern", "query", "url"];
2114
+ TRANSPORT_HEADER_BLOCK = /^\s*x-anthropic-[a-z0-9-]*header\s*:/i;
1971
2115
  });
1972
2116
 
1973
- // src/proxy/fileChanges.ts
1974
- function extractFileChange(toolName, toolInput, mcpPrefix) {
1975
- if (!toolName.startsWith(mcpPrefix))
1976
- return;
1977
- const shortName = toolName.slice(mcpPrefix.length);
1978
- const input = toolInput;
1979
- if (shortName === "write" && input?.path) {
1980
- return { operation: "wrote", path: String(input.path) };
1981
- }
1982
- if (shortName === "edit" && input?.path) {
1983
- return { operation: "edited", path: String(input.path) };
1984
- }
1985
- return;
1986
- }
1987
- function createFileChangeHook(changes, mcpPrefix) {
1988
- return {
1989
- matcher: "",
1990
- hooks: [async (input) => {
1991
- const change = extractFileChange(input.tool_name, input.tool_input, mcpPrefix);
1992
- if (change) {
1993
- changes.push(change);
1994
- return {};
1995
- }
1996
- if (input.tool_name === `${mcpPrefix}bash`) {
1997
- const toolInput = input.tool_input;
1998
- if (toolInput?.command) {
1999
- const bashChanges = extractFileChangesFromBash(String(toolInput.command));
2000
- changes.push(...bashChanges);
2001
- }
2002
- }
2003
- return {};
2004
- }]
2005
- };
2006
- }
2007
- function isLikelyFilePath(s) {
2008
- if (s.length > 4096)
2009
- return false;
2010
- if (/[<>$`\\()[\]{}=]/.test(s))
2011
- return false;
2012
- if (/^-?\d+$/.test(s))
2013
- return false;
2014
- if (s.includes("/"))
2015
- return true;
2016
- if (/\.[A-Za-z0-9]{1,16}$/.test(s))
2017
- return true;
2018
- return false;
2019
- }
2020
- function extractFileChangesFromBash(command) {
2021
- const changes = [];
2022
- const seen = new Set;
2023
- const addChange = (operation, path) => {
2024
- if (path === "/dev/null" || path === "/dev/stderr" || path === "/dev/stdout")
2025
- return;
2026
- if (!path.trim())
2027
- return;
2028
- const key = `${operation}:${path}`;
2029
- if (!seen.has(key)) {
2030
- seen.add(key);
2031
- changes.push({ operation, path });
2032
- }
2033
- };
2034
- const redirectRegex = /(?<![0-9=])>{1,2}\s*['"]?([^\s'";&|)]+)['"]?/g;
2035
- let match2;
2036
- while ((match2 = redirectRegex.exec(command)) !== null) {
2037
- if (isLikelyFilePath(match2[1])) {
2038
- addChange("wrote", match2[1]);
2039
- }
2040
- }
2041
- const teeRegex = /\btee\s+(?:-[a-zA-Z]\s+)*['"]?([^\s'";&|)]+)['"]?/g;
2042
- while ((match2 = teeRegex.exec(command)) !== null) {
2043
- addChange("wrote", match2[1]);
2044
- }
2045
- const sedRegex = /\bsed\s+(?:-[a-zA-Z]*i[a-zA-Z]*|-i)\b.*?['"]?([^\s'";&|)]+)['"]?\s*$/gm;
2046
- while ((match2 = sedRegex.exec(command)) !== null) {
2047
- addChange("edited", match2[1]);
2048
- }
2049
- return changes;
2050
- }
2051
- function extractFileChangesFromMessages(messages, extractFn) {
2052
- const changes = [];
2053
- const executedToolIds = new Set;
2054
- for (const msg of messages) {
2055
- if (msg.role !== "user")
2056
- continue;
2057
- const content = Array.isArray(msg.content) ? msg.content : [];
2058
- for (const block of content) {
2059
- if (block?.type === "tool_result" && block.tool_use_id) {
2060
- executedToolIds.add(block.tool_use_id);
2061
- }
2062
- }
2063
- }
2064
- for (const msg of messages) {
2065
- if (msg.role !== "assistant")
2066
- continue;
2067
- const content = Array.isArray(msg.content) ? msg.content : [];
2068
- for (const block of content) {
2069
- if (block?.type !== "tool_use")
2070
- continue;
2071
- if (!executedToolIds.has(block.id))
2072
- continue;
2073
- const blockChanges = extractFn(block.name, block.input);
2074
- changes.push(...blockChanges);
2075
- }
2076
- }
2077
- return changes;
2078
- }
2079
- function formatFileChangeSummary(changes) {
2080
- if (changes.length === 0)
2081
- return;
2082
- const seen = new Set;
2083
- const unique = [];
2084
- for (const c of changes) {
2085
- const key = `${c.operation}:${c.path}`;
2086
- if (!seen.has(key)) {
2087
- seen.add(key);
2088
- unique.push(c);
2089
- }
2090
- }
2091
- const lines = unique.map((c) => `- ${c.operation} ${c.path}`);
2092
- return `
2093
-
2094
- Files changed:
2095
- ${lines.join(`
2096
- `)}`;
2097
- }
2098
- var init_fileChanges = () => {};
2099
-
2100
2117
  // src/proxy/session/fingerprint.ts
2101
2118
  import { createHash } from "crypto";
2102
2119
  function extractClientCwd(body) {
@@ -2142,7 +2159,7 @@ var init_opencode = __esm(() => {
2142
2159
  openCodeTransforms = [
2143
2160
  {
2144
2161
  name: "opencode-core",
2145
- adapters: ["opencode", "openai", "codex"],
2162
+ adapters: ["opencode", "openai", "jcode", "codex"],
2146
2163
  onRequest(ctx) {
2147
2164
  const body = ctx.body;
2148
2165
  const blockedTools = BLOCKED_BUILTIN_TOOLS;
@@ -2316,6 +2333,51 @@ IMPORTANT: When using the task/Task tool, the subagent_type parameter must be on
2316
2333
  };
2317
2334
  });
2318
2335
 
2336
+ // src/proxy/adapters/openai.ts
2337
+ var openAiAdapter;
2338
+ var init_openai = __esm(() => {
2339
+ init_opencode2();
2340
+ openAiAdapter = {
2341
+ ...openCodeAdapter,
2342
+ name: "openai"
2343
+ };
2344
+ });
2345
+
2346
+ // src/proxy/adapters/jcode.ts
2347
+ function normalizeJcodeSessionId(value) {
2348
+ const trimmed = value?.trim();
2349
+ return trimmed && JCODE_SESSION_ID.test(trimmed) ? trimmed : undefined;
2350
+ }
2351
+ function isTextSystemBlock(value) {
2352
+ if (value === null || typeof value !== "object")
2353
+ return false;
2354
+ const part = value;
2355
+ return part.type === "text" && typeof part.text === "string";
2356
+ }
2357
+ function extractJcodeWorkingDirectory(body) {
2358
+ if (body === null || typeof body !== "object")
2359
+ return;
2360
+ const system = body.system;
2361
+ const text = typeof system === "string" ? system : Array.isArray(system) ? system.filter(isTextSystemBlock).map((part) => part.text).join(`
2362
+ `) : "";
2363
+ return text.match(/(?:^|\n)Working directory:[^\S\n]*([^\n]+)/)?.[1]?.trim() || undefined;
2364
+ }
2365
+ var JCODE_SESSION_ID, jcodeAdapter;
2366
+ var init_jcode = __esm(() => {
2367
+ init_openai();
2368
+ JCODE_SESSION_ID = /^[A-Za-z0-9._:-]{1,256}$/;
2369
+ jcodeAdapter = {
2370
+ ...openAiAdapter,
2371
+ name: "jcode",
2372
+ getSessionId(c) {
2373
+ return normalizeJcodeSessionId(c.req.header("x-jcode-session"));
2374
+ },
2375
+ extractWorkingDirectory(body) {
2376
+ return extractJcodeWorkingDirectory(body);
2377
+ }
2378
+ };
2379
+ });
2380
+
2319
2381
  // src/proxy/transforms/droid.ts
2320
2382
  function resolveDroidPassthrough() {
2321
2383
  return resolvePassthrough(false);
@@ -2999,16 +3061,6 @@ var init_forgecode2 = __esm(() => {
2999
3061
  };
3000
3062
  });
3001
3063
 
3002
- // src/proxy/adapters/openai.ts
3003
- var openAiAdapter;
3004
- var init_openai = __esm(() => {
3005
- init_opencode2();
3006
- openAiAdapter = {
3007
- ...openCodeAdapter,
3008
- name: "openai"
3009
- };
3010
- });
3011
-
3012
3064
  // src/proxy/adapters/codex.ts
3013
3065
  var codexAdapter;
3014
3066
  var init_codex = __esm(() => {
@@ -3193,6 +3245,9 @@ function detectAdapter(c) {
3193
3245
  return openCodeAdapter;
3194
3246
  }
3195
3247
  const userAgent = c.req.header("user-agent") || "";
3248
+ if (userAgent.startsWith("jcode/") && normalizeJcodeSessionId(c.req.header("x-jcode-session"))) {
3249
+ return jcodeAdapter;
3250
+ }
3196
3251
  if (userAgent.startsWith("opencode/")) {
3197
3252
  return openCodeAdapter;
3198
3253
  }
@@ -3227,6 +3282,7 @@ var init_detect = __esm(() => {
3227
3282
  init_forgecode2();
3228
3283
  init_claudecode();
3229
3284
  init_openai();
3285
+ init_jcode();
3230
3286
  init_codex();
3231
3287
  init_cherry();
3232
3288
  init_adapterInstances();
@@ -3242,6 +3298,7 @@ var init_detect = __esm(() => {
3242
3298
  cherry: cherryAdapter,
3243
3299
  cherrystudio: cherryAdapter,
3244
3300
  openai: openAiAdapter,
3301
+ jcode: jcodeAdapter,
3245
3302
  codex: codexAdapter
3246
3303
  };
3247
3304
  envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
@@ -3379,6 +3436,7 @@ var init_sdkFeatures = __esm(() => {
3379
3436
  thinkingPassthrough: false,
3380
3437
  sharedMemory: false,
3381
3438
  webFetchPreflight: true,
3439
+ claudeAiConnectors: false,
3382
3440
  maxBudgetUsd: 0,
3383
3441
  fallbackModel: "",
3384
3442
  sdkDebug: false,
@@ -3391,6 +3449,9 @@ var init_sdkFeatures = __esm(() => {
3391
3449
  openai: {
3392
3450
  codeSystemPrompt: false
3393
3451
  },
3452
+ jcode: {
3453
+ codeSystemPrompt: false
3454
+ },
3394
3455
  cherry: {
3395
3456
  codeSystemPrompt: false
3396
3457
  },
@@ -3587,6 +3648,7 @@ const FEATURES = [
3587
3648
  { key: 'thinkingPassthrough', label: 'Thinking Passthrough', desc: 'Forward thinking blocks to the client', type: 'toggle' },
3588
3649
  { key: 'sharedMemory', label: 'Shared Memory', desc: 'Share memory with Claude Code (~/.claude) instead of isolated storage', type: 'toggle' },
3589
3650
  { key: 'webFetchPreflight', label: 'WebFetch Preflight', desc: 'Check each WebFetch hostname against the Anthropic blocklist before fetching — off keeps hostnames local but fetches any URL unchecked. Only affects Cherry Studio; other adapters never run the SDK\\'s built-in WebFetch', type: 'toggle' },
3651
+ { key: 'claudeAiConnectors', label: 'claude.ai Connectors', desc: 'Load the MCP connectors attached to your claude.ai account (Drive, Gmail, Calendar). Off by default; always off in passthrough mode', type: 'toggle' },
3590
3652
  { key: 'maxBudgetUsd', label: 'Max Budget (USD)', desc: 'Per-request cost cap — query aborts if exceeded (0 = disabled)', type: 'number' },
3591
3653
  { key: 'fallbackModel', label: 'Fallback Model', desc: 'Auto-fallback model if primary fails', type: 'select', options: ['', 'sonnet', 'opus', 'haiku', 'sonnet[1m]', 'opus[1m]'] },
3592
3654
  { key: 'sdkDebug', label: 'SDK Debug Logging', desc: 'Enable verbose SDK debug output to proxy stderr', type: 'toggle' },
@@ -3645,7 +3707,8 @@ function showSaved() {
3645
3707
  function hasAnyEnabled(features) {
3646
3708
  return features.codeSystemPrompt || !features.clientSystemPrompt || features.claudeMd !== 'off' || features.memory || features.dreaming ||
3647
3709
  features.thinking !== 'disabled' || features.thinkingPassthrough ||
3648
- features.sharedMemory || features.webFetchPreflight === false || features.maxBudgetUsd > 0 ||
3710
+ features.sharedMemory || features.webFetchPreflight === false ||
3711
+ features.claudeAiConnectors || features.maxBudgetUsd > 0 ||
3649
3712
  features.fallbackModel || features.sdkDebug ||
3650
3713
  features.additionalDirectories;
3651
3714
  }
@@ -6282,8 +6345,10 @@ var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
6282
6345
  var OAUTH_BETA_HEADER = "oauth-2025-04-20";
6283
6346
  var CACHE_TTL_MS_DEFAULT = 30000;
6284
6347
  var STALE_MAX_MS_DEFAULT = 15 * 60000;
6348
+ var RATE_LIMIT_BACKOFF_MS_DEFAULT = 60000;
6285
6349
  var cacheByProfile = new Map;
6286
6350
  var inflightByProfile = new Map;
6351
+ var rateLimitedUntilByProfile = new Map;
6287
6352
  var DEFAULT_KEY = "__default__";
6288
6353
  var WINDOW_TYPES = [
6289
6354
  "five_hour",
@@ -6305,6 +6370,15 @@ function normalizeUtilization(raw2) {
6305
6370
  return null;
6306
6371
  return Math.max(0, raw2 / 100);
6307
6372
  }
6373
+ function parseRetryAfterMs(raw2) {
6374
+ if (!raw2)
6375
+ return null;
6376
+ const seconds = Number(raw2);
6377
+ if (Number.isFinite(seconds))
6378
+ return Math.max(0, seconds * 1000);
6379
+ const retryAt = Date.parse(raw2);
6380
+ return Number.isFinite(retryAt) ? Math.max(0, retryAt - Date.now()) : null;
6381
+ }
6308
6382
  function modelScopedWindowType(limit) {
6309
6383
  if (limit.kind !== "weekly_scoped")
6310
6384
  return null;
@@ -6362,14 +6436,27 @@ async function callAnthropic(token, fetchImpl, signal) {
6362
6436
  },
6363
6437
  signal: signal ?? AbortSignal.timeout(1e4)
6364
6438
  });
6365
- if (!res.ok)
6366
- return { __status: res.status };
6439
+ if (!res.ok) {
6440
+ return {
6441
+ __status: res.status,
6442
+ retryAfterMs: parseRetryAfterMs(res.headers.get("retry-after"))
6443
+ };
6444
+ }
6367
6445
  return await res.json();
6368
6446
  }
6369
6447
  var _testOverride = null;
6370
6448
  async function fetchOAuthUsage(opts) {
6449
+ return (await fetchOAuthUsageResult(opts)).snapshot;
6450
+ }
6451
+ function missingReason(cacheKey2) {
6452
+ const until = rateLimitedUntilByProfile.get(cacheKey2);
6453
+ return until !== undefined && Date.now() < until ? "rate_limited" : "no_token";
6454
+ }
6455
+ async function fetchOAuthUsageResult(opts) {
6371
6456
  if (_testOverride && !opts?.fetchImpl && !opts?.store) {
6372
- return _testOverride(opts);
6457
+ const snapshot = await _testOverride(opts);
6458
+ const error = snapshot ? null : missingReason(opts?.profileId ?? DEFAULT_KEY);
6459
+ return { snapshot, error };
6373
6460
  }
6374
6461
  return fetchOAuthUsageImpl(opts);
6375
6462
  }
@@ -6377,30 +6464,37 @@ async function fetchOAuthUsageImpl(opts) {
6377
6464
  const ttl = opts?.ttlMs ?? CACHE_TTL_MS_DEFAULT;
6378
6465
  const cacheKey2 = opts?.profileId ?? DEFAULT_KEY;
6379
6466
  const fetchImpl = opts?.fetchImpl ?? globalThis.fetch;
6467
+ const staleMaxMs = opts?.staleMaxMs ?? STALE_MAX_MS_DEFAULT;
6468
+ const staleOr = (reason, error) => {
6469
+ const last = cacheByProfile.get(cacheKey2);
6470
+ if (last && Date.now() - last.fetchedAt < staleMaxMs) {
6471
+ claudeLog("oauth_usage.serving_stale", { profile: cacheKey2, reason, ageMs: Date.now() - last.fetchedAt });
6472
+ return { snapshot: { ...last, stale: true }, error: null };
6473
+ }
6474
+ return { snapshot: null, error };
6475
+ };
6380
6476
  if (!opts?.force) {
6381
6477
  const cached = cacheByProfile.get(cacheKey2);
6382
6478
  if (cached && Date.now() - cached.fetchedAt < ttl)
6383
- return cached;
6479
+ return { snapshot: cached, error: null };
6384
6480
  }
6385
6481
  const existing = inflightByProfile.get(cacheKey2);
6386
6482
  if (existing)
6387
6483
  return existing;
6484
+ const rateLimitedUntil = rateLimitedUntilByProfile.get(cacheKey2);
6485
+ if (rateLimitedUntil !== undefined) {
6486
+ if (Date.now() < rateLimitedUntil)
6487
+ return staleOr("rate_limited", "rate_limited");
6488
+ rateLimitedUntilByProfile.delete(cacheKey2);
6489
+ }
6388
6490
  const store = opts?.store ?? createPlatformCredentialStore({ claudeConfigDir: opts?.claudeConfigDir });
6389
- const staleMaxMs = opts?.staleMaxMs ?? STALE_MAX_MS_DEFAULT;
6390
- const staleOr = (reason) => {
6391
- const last = cacheByProfile.get(cacheKey2);
6392
- if (last && Date.now() - last.fetchedAt < staleMaxMs) {
6393
- claudeLog("oauth_usage.serving_stale", { profile: cacheKey2, reason, ageMs: Date.now() - last.fetchedAt });
6394
- return { ...last, stale: true };
6395
- }
6396
- return null;
6397
- };
6491
+ const rateLimitBackoffMs = opts?.rateLimitBackoffMs ?? RATE_LIMIT_BACKOFF_MS_DEFAULT;
6398
6492
  const promise = (async () => {
6399
6493
  try {
6400
6494
  const token = await readAccessToken(store);
6401
6495
  if (!token) {
6402
6496
  claudeLog("oauth_usage.no_token", { profile: cacheKey2 });
6403
- return staleOr("no_token");
6497
+ return staleOr("no_token", "no_token");
6404
6498
  }
6405
6499
  let result = await callAnthropic(token, fetchImpl);
6406
6500
  if ("__status" in result && result.__status === 401) {
@@ -6408,23 +6502,28 @@ async function fetchOAuthUsageImpl(opts) {
6408
6502
  const refreshed = await refreshOAuthToken(store);
6409
6503
  if (!refreshed) {
6410
6504
  claudeLog("oauth_usage.refresh_failed", { profile: cacheKey2 });
6411
- return staleOr("refresh_failed");
6505
+ return staleOr("refresh_failed", "upstream_error");
6412
6506
  }
6413
6507
  const newToken = await readAccessToken(store);
6414
6508
  if (!newToken)
6415
- return staleOr("no_token_after_refresh");
6509
+ return staleOr("no_token_after_refresh", "no_token");
6416
6510
  result = await callAnthropic(newToken, fetchImpl);
6417
6511
  }
6418
6512
  if ("__status" in result) {
6513
+ if (result.__status === 429) {
6514
+ const retryAfterMs = Math.min(Math.max(rateLimitBackoffMs, result.retryAfterMs ?? 0), Math.max(staleMaxMs, rateLimitBackoffMs));
6515
+ rateLimitedUntilByProfile.set(cacheKey2, Date.now() + retryAfterMs);
6516
+ }
6419
6517
  claudeLog("oauth_usage.upstream_error", { profile: cacheKey2, status: result.__status });
6420
- return staleOr(`upstream_${result.__status}`);
6518
+ return staleOr(`upstream_${result.__status}`, result.__status === 429 ? "rate_limited" : "upstream_error");
6421
6519
  }
6520
+ rateLimitedUntilByProfile.delete(cacheKey2);
6422
6521
  const snapshot = buildSnapshot(result);
6423
6522
  cacheByProfile.set(cacheKey2, snapshot);
6424
- return snapshot;
6523
+ return { snapshot, error: null };
6425
6524
  } catch (err) {
6426
6525
  claudeLog("oauth_usage.fetch_failed", { profile: cacheKey2, error: err instanceof Error ? err.message : String(err) });
6427
- return staleOr("exception");
6526
+ return staleOr("exception", "upstream_error");
6428
6527
  } finally {
6429
6528
  inflightByProfile.delete(cacheKey2);
6430
6529
  }
@@ -10703,6 +10802,31 @@ function shouldEarlyStop(tracker) {
10703
10802
  tracker.fired = true;
10704
10803
  return true;
10705
10804
  }
10805
+ function clientAbortDisposition(input) {
10806
+ if (input.isIndependentSession || !input.profileSessionId)
10807
+ return { action: "none" };
10808
+ if (!input.passthrough)
10809
+ return { action: "evict" };
10810
+ if (input.currentSessionId && !input.sawDuplicateToolUse && input.resumeBoundaryUuid) {
10811
+ return { action: "store", resumeUuid: input.resumeBoundaryUuid };
10812
+ }
10813
+ return { action: "evict" };
10814
+ }
10815
+ function resumeBoundaryUuid(message) {
10816
+ const m = message;
10817
+ if (m?.type !== "user")
10818
+ return;
10819
+ if (typeof m.uuid !== "string" || m.uuid.length === 0)
10820
+ return;
10821
+ const content = m.message?.content;
10822
+ if (!Array.isArray(content))
10823
+ return;
10824
+ const hasResult = content.some((block) => {
10825
+ const b = block;
10826
+ return b?.type === "tool_result";
10827
+ });
10828
+ return hasResult ? m.uuid : undefined;
10829
+ }
10706
10830
 
10707
10831
  // src/proxy/envelopeIntegrity.ts
10708
10832
  function checkEmptyToolInputs(contentBlocks, tools) {
@@ -10743,6 +10867,68 @@ function checkUndeliveredToolUses(captured, deliveredIds) {
10743
10867
  return violations;
10744
10868
  }
10745
10869
 
10870
+ // src/proxy/turnOutcome.ts
10871
+ function classifyTurnOutcome(input) {
10872
+ if (input.toolUses > 0)
10873
+ return { kind: "productive" };
10874
+ if (input.textEvents > 0)
10875
+ return { kind: "productive" };
10876
+ return {
10877
+ kind: "silent",
10878
+ reason: input.blocksForwarded > 0 ? "no_actionable_content" : "no_blocks"
10879
+ };
10880
+ }
10881
+ 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.";
10882
+ function shouldInjectSilentTurn(input) {
10883
+ if (!input.raw)
10884
+ return false;
10885
+ if (input.raw === "1")
10886
+ return true;
10887
+ return Boolean(input.sessionId && input.raw === input.sessionId);
10888
+ }
10889
+ function createRecoveryLifter(allocateBlockIndex) {
10890
+ let blockIndex;
10891
+ return {
10892
+ lift(innerEvent) {
10893
+ const inner = innerEvent;
10894
+ if (!inner)
10895
+ return;
10896
+ if (inner.type === "content_block_start" && inner.content_block?.type === "text") {
10897
+ blockIndex = allocateBlockIndex();
10898
+ return {
10899
+ kind: "block_start",
10900
+ frame: { type: "content_block_start", index: blockIndex, content_block: { type: "text", text: "" } }
10901
+ };
10902
+ }
10903
+ if (inner.type === "content_block_delta" && inner.delta?.type === "text_delta" && blockIndex !== undefined) {
10904
+ const text = inner.delta.text;
10905
+ return {
10906
+ kind: "text_delta",
10907
+ frame: { type: "content_block_delta", index: blockIndex, delta: { type: "text_delta", text } },
10908
+ textChars: typeof text === "string" ? text.length : 0
10909
+ };
10910
+ }
10911
+ if (inner.type === "content_block_stop" && blockIndex !== undefined) {
10912
+ const index = blockIndex;
10913
+ blockIndex = undefined;
10914
+ return { kind: "block_stop", frame: { type: "content_block_stop", index } };
10915
+ }
10916
+ return;
10917
+ }
10918
+ };
10919
+ }
10920
+ function shouldAttemptRecovery(input) {
10921
+ if (!input.enabled)
10922
+ return false;
10923
+ if (input.outcome.kind === "productive")
10924
+ return false;
10925
+ if (input.alreadyAttempted)
10926
+ return false;
10927
+ if (input.clientGone)
10928
+ return false;
10929
+ return Boolean(input.sessionId);
10930
+ }
10931
+
10746
10932
  // src/proxy/server.ts
10747
10933
  init_agentMatch();
10748
10934
 
@@ -11768,6 +11954,7 @@ function extendedContextHint(model) {
11768
11954
  return advise("MERIDIAN_SONNET_MODEL=sonnet");
11769
11955
  return advise("MERIDIAN_1M_CONTEXT_SUPPORT=0");
11770
11956
  }
11957
+ var HIT_YOUR_LIMIT = /hit your (?:[\w-]+ )?limit/;
11771
11958
  function classifyError(errMsg, model) {
11772
11959
  const lower = errMsg.toLowerCase();
11773
11960
  if (lower.includes("oauth token has expired") || lower.includes("not logged in")) {
@@ -11784,7 +11971,7 @@ function classifyError(errMsg, model) {
11784
11971
  message: "Claude authentication expired or invalid. Run 'claude login' in your terminal to re-authenticate, then restart the proxy."
11785
11972
  };
11786
11973
  }
11787
- if (lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests") || lower.includes("hit your session limit") || lower.includes("usage limit reached")) {
11974
+ if (lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests") || HIT_YOUR_LIMIT.test(lower) || lower.includes("usage limit reached")) {
11788
11975
  const hint = lower.includes("1m") || lower.includes("context") ? extendedContextHint(model) : "";
11789
11976
  return {
11790
11977
  status: 429,
@@ -12267,7 +12454,7 @@ ${c.text}
12267
12454
  return "";
12268
12455
  }).filter(Boolean).join("");
12269
12456
  }
12270
- function translateOpenAiToAnthropic(body) {
12457
+ function translateOpenAiToAnthropic(body, options = {}) {
12271
12458
  const messages = body.messages ?? [];
12272
12459
  if (messages.length === 0)
12273
12460
  return null;
@@ -12359,7 +12546,7 @@ function translateOpenAiToAnthropic(body) {
12359
12546
  let systemPrompt = systemParts.join(`
12360
12547
  `);
12361
12548
  let messagesToSend = turns;
12362
- if (turns.length > 1) {
12549
+ if (turns.length > 1 && !options.preserveConversationHistory) {
12363
12550
  const history = turns.slice(0, -1).map((m) => `${m.role}: ${summarizeAnthropicContent(m.content)}`).join(`
12364
12551
  `);
12365
12552
  const historyBlock = `<conversation_history>
@@ -12654,6 +12841,9 @@ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000))
12654
12841
  ];
12655
12842
  }
12656
12843
 
12844
+ // src/proxy/server.ts
12845
+ init_jcode();
12846
+
12657
12847
  // src/proxy/openaiResponses.ts
12658
12848
  function itemDiscriminator(item) {
12659
12849
  if (typeof item !== "object" || item === null)
@@ -18846,7 +19036,7 @@ function buildQueryOptions(ctx, abortController) {
18846
19036
  hasDeferredTools,
18847
19037
  resumeSessionId,
18848
19038
  isUndo,
18849
- undoRollbackUuid,
19039
+ resumeSessionAtUuid,
18850
19040
  forkSession,
18851
19041
  sdkHooks,
18852
19042
  blockedTools,
@@ -18909,7 +19099,7 @@ function buildQueryOptions(ctx, abortController) {
18909
19099
  ...QUIET_SUBPROCESS_ENV,
18910
19100
  ...sharedMemory ? stripConfigDir(cleanEnv) : cleanEnv,
18911
19101
  ENABLE_TOOL_SEARCH: hasDeferredTools ? "true" : "false",
18912
- ...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {},
19102
+ ENABLE_CLAUDEAI_MCP_SERVERS: !passthrough && ctx.claudeAiConnectors === true ? "true" : "false",
18913
19103
  ...passthrough && process.env.MERIDIAN_SUPPRESS_SCRATCHPAD !== "0" ? { CLAUDE_CODE_SESSION_KIND: "bg" } : {},
18914
19104
  ...process.getuid?.() === 0 ? { IS_SANDBOX: "1" } : {},
18915
19105
  ...ctx.envOverrides
@@ -18917,7 +19107,7 @@ function buildQueryOptions(ctx, abortController) {
18917
19107
  ...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
18918
19108
  ...resumeSessionId ? { resume: resumeSessionId } : {},
18919
19109
  ...isUndo || forkSession ? { forkSession: true } : {},
18920
- ...isUndo && undoRollbackUuid ? { resumeSessionAt: undoRollbackUuid } : {},
19110
+ ...resumeSessionAtUuid ? { resumeSessionAt: resumeSessionAtUuid } : {},
18921
19111
  ...sdkHooks ? { hooks: sdkHooks } : {},
18922
19112
  ...effort ? { effort } : {},
18923
19113
  ...thinking ? { thinking } : {},
@@ -19064,6 +19254,7 @@ var ADAPTER_TRANSFORMS = {
19064
19254
  cherry: cherryTransforms,
19065
19255
  "claude-code": claudeCodeTransforms,
19066
19256
  openai: openCodeTransforms,
19257
+ jcode: openCodeTransforms,
19067
19258
  codex: [...openCodeTransforms, ...codexTransforms]
19068
19259
  };
19069
19260
  function getAdapterTransforms(adapterName) {
@@ -19076,7 +19267,7 @@ import { join as join5, isAbsolute as isAbsolute2, extname } from "path";
19076
19267
  import { pathToFileURL } from "url";
19077
19268
 
19078
19269
  // src/proxy/plugins/validation.ts
19079
- var KNOWN_ADAPTERS = ["opencode", "openai", "crush", "droid", "pi", "forgecode", "passthrough"];
19270
+ var KNOWN_ADAPTERS = ["opencode", "openai", "jcode", "crush", "droid", "pi", "forgecode", "passthrough"];
19080
19271
  var KNOWN_HOOKS = ["onRequest", "onResponse", "onTelemetry", "onSession", "onToolUse", "onToolResult", "onError"];
19081
19272
  function validateTransform(exported) {
19082
19273
  if (exported == null || typeof exported !== "object") {
@@ -19436,6 +19627,19 @@ function computeMessageHashes(messages) {
19436
19627
  return [];
19437
19628
  return messages.map(hashMessage);
19438
19629
  }
19630
+ function hashNormalizedContent(content) {
19631
+ return createHash2("sha256").update(normalizeContent(content)).digest("hex").slice(0, 32);
19632
+ }
19633
+ function hashableContentBlocks(content) {
19634
+ if (!Array.isArray(content))
19635
+ return [content];
19636
+ return content.filter((block) => !HASH_IGNORED_BLOCK_TYPES.has(block?.type));
19637
+ }
19638
+ function computeMessageBlockHashes(messages) {
19639
+ if (!messages || messages.length === 0)
19640
+ return [];
19641
+ return messages.map((message) => hashableContentBlocks(message.content).map((block) => hashNormalizedContent(Array.isArray(message.content) ? [block] : block)));
19642
+ }
19439
19643
  function measurePrefixOverlap(storedHashes, incomingHashes) {
19440
19644
  let overlap = 0;
19441
19645
  const minLen = Math.min(storedHashes.length, incomingHashes.length);
@@ -19518,6 +19722,34 @@ function verifyLineage(cached, messages) {
19518
19722
  suffixOverlap
19519
19723
  };
19520
19724
  }
19725
+ const boundary = cached.messageCount - 1;
19726
+ if (boundary >= 0 && prefixOverlap === boundary && messages.length >= cached.messageCount && cached.messageBlockHashes?.length === cached.messageCount) {
19727
+ const incomingBoundary = messages[boundary];
19728
+ const storedBlocks = cached.messageBlockHashes[boundary];
19729
+ if (incomingBoundary?.role === "user" && storedBlocks && Array.isArray(incomingBoundary.content)) {
19730
+ const incomingBlocks = hashableContentBlocks(incomingBoundary.content);
19731
+ const incomingBlockHashes = incomingBlocks.map((block) => hashNormalizedContent([block]));
19732
+ const preservesStoredBlocks = incomingBlocks.length === incomingBoundary.content.length && incomingBlockHashes.length > storedBlocks.length && storedBlocks.every((hash, index) => incomingBlockHashes[index] === hash);
19733
+ const appendedBlocks = incomingBlocks.slice(storedBlocks.length);
19734
+ 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));
19735
+ const hasOnlyNewToolResults = appendedBlocks.every((block) => {
19736
+ if (block?.type !== "tool_result" || typeof block.tool_use_id !== "string")
19737
+ return false;
19738
+ if (seenToolResultIds.has(block.tool_use_id))
19739
+ return false;
19740
+ seenToolResultIds.add(block.tool_use_id);
19741
+ return true;
19742
+ });
19743
+ if (preservesStoredBlocks && hasOnlyNewToolResults) {
19744
+ return {
19745
+ type: "continuation",
19746
+ session: cached,
19747
+ resumeFrom: boundary,
19748
+ resumeContentFrom: storedBlocks.length
19749
+ };
19750
+ }
19751
+ }
19752
+ }
19521
19753
  if (prefixOverlap > 0 && suffixOverlap === 0 && messages.length <= cached.messageCount) {
19522
19754
  let rollbackUuid;
19523
19755
  if (cached.sdkMessageUuids) {
@@ -19664,7 +19896,7 @@ function lookupSharedSessionByClaudeId(claudeSessionId) {
19664
19896
  }
19665
19897
  return newest;
19666
19898
  }
19667
- function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage) {
19899
+ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughResumeUuid) {
19668
19900
  const path3 = getStorePath();
19669
19901
  const lockPath = `${path3}.lock`;
19670
19902
  const hasLock = skipLocking ? false : acquireLock(lockPath);
@@ -19682,7 +19914,9 @@ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, mes
19682
19914
  messageCount: messageCount ?? existing?.messageCount ?? 0,
19683
19915
  lineageHash: lineageHash ?? existing?.lineageHash,
19684
19916
  messageHashes: messageHashes ?? existing?.messageHashes,
19917
+ messageBlockHashes: messageBlockHashes ?? existing?.messageBlockHashes,
19685
19918
  sdkMessageUuids: sdkMessageUuids ?? existing?.sdkMessageUuids,
19919
+ passthroughResumeUuid: passthroughResumeUuid === undefined ? existing?.passthroughResumeUuid : passthroughResumeUuid ?? undefined,
19686
19920
  contextUsage: contextUsage ?? existing?.contextUsage,
19687
19921
  ...previousClaudeSessionId ? { previousClaudeSessionId } : {}
19688
19922
  };
@@ -19840,7 +20074,11 @@ function touchSession(state) {
19840
20074
  }
19841
20075
  function classifyLineage(state, messages, cacheKey2) {
19842
20076
  const result = verifyLineage(state, messages);
19843
- if (result.type === "compaction") {
20077
+ if (result.type === "continuation" && result.resumeContentFrom !== undefined) {
20078
+ const msg = `Parallel tool-result continuation (key=${cacheKey2.slice(0, 8)}…): resume from message ${result.resumeFrom}, content block ${result.resumeContentFrom}.`;
20079
+ console.error(`[PROXY] ${msg}`);
20080
+ diagnosticLog2.lineage(msg);
20081
+ } else if (result.type === "compaction") {
19844
20082
  const msg = `Compaction detected (key=${cacheKey2.slice(0, 8)}…): suffix overlap ${result.suffixOverlap}/${state.messageCount}, resume from incoming message ${result.resumeFrom}.`;
19845
20083
  console.error(`[PROXY] ${msg}`);
19846
20084
  diagnosticLog2.lineage(msg);
@@ -19872,7 +20110,9 @@ function lookupSession(sessionId, messages, workingDirectory) {
19872
20110
  messageCount: shared.messageCount || 0,
19873
20111
  lineageHash: shared.lineageHash || "",
19874
20112
  messageHashes: shared.messageHashes,
20113
+ messageBlockHashes: shared.messageBlockHashes,
19875
20114
  sdkMessageUuids: shared.sdkMessageUuids,
20115
+ passthroughResumeUuid: shared.passthroughResumeUuid,
19876
20116
  contextUsage: shared.contextUsage
19877
20117
  };
19878
20118
  const result = classifyLineage(state, messages, sessionId);
@@ -19900,7 +20140,9 @@ function lookupSession(sessionId, messages, workingDirectory) {
19900
20140
  messageCount: shared.messageCount || 0,
19901
20141
  lineageHash: shared.lineageHash || "",
19902
20142
  messageHashes: shared.messageHashes,
20143
+ messageBlockHashes: shared.messageBlockHashes,
19903
20144
  sdkMessageUuids: shared.sdkMessageUuids,
20145
+ passthroughResumeUuid: shared.passthroughResumeUuid,
19904
20146
  contextUsage: shared.contextUsage
19905
20147
  };
19906
20148
  const result = classifyLineage(state, messages, fp);
@@ -19933,24 +20175,29 @@ function getSessionByClaudeId(claudeSessionId) {
19933
20175
  messageCount: shared.messageCount || 0,
19934
20176
  lineageHash: shared.lineageHash || "",
19935
20177
  messageHashes: shared.messageHashes,
20178
+ messageBlockHashes: shared.messageBlockHashes,
19936
20179
  sdkMessageUuids: shared.sdkMessageUuids,
20180
+ passthroughResumeUuid: shared.passthroughResumeUuid,
19937
20181
  contextUsage: shared.contextUsage
19938
20182
  });
19939
20183
  }
19940
20184
  return newest;
19941
20185
  }
19942
- function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage) {
20186
+ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage, passthroughResumeUuid) {
19943
20187
  if (!claudeSessionId)
19944
20188
  return;
19945
20189
  const lineageHash = computeLineageHash(messages);
19946
20190
  const messageHashes = computeMessageHashes(messages);
20191
+ const messageBlockHashes = computeMessageBlockHashes(messages);
19947
20192
  const state = {
19948
20193
  claudeSessionId,
19949
20194
  lastAccess: Date.now(),
19950
20195
  messageCount: messages?.length || 0,
19951
20196
  lineageHash,
19952
20197
  messageHashes,
20198
+ messageBlockHashes,
19953
20199
  sdkMessageUuids,
20200
+ ...passthroughResumeUuid ? { passthroughResumeUuid } : {},
19954
20201
  ...contextUsage ? { contextUsage } : {}
19955
20202
  };
19956
20203
  if (sessionId)
@@ -19960,14 +20207,14 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
19960
20207
  fingerprintCache.set(fp, state);
19961
20208
  const key = sessionId || fp;
19962
20209
  if (key) {
19963
- storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage);
20210
+ storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage, messageBlockHashes, passthroughResumeUuid ?? null);
19964
20211
  }
19965
20212
  }
19966
20213
 
19967
20214
  // src/proxy/server.ts
19968
20215
  var exec2 = promisify3(execCallback);
19969
20216
  var claudeExecutable = "";
19970
- var UPSTREAM_IDLE_MS = 90000;
20217
+ var UPSTREAM_IDLE_MS = envInt("UPSTREAM_IDLE_MS", 90000);
19971
20218
  function credentialStoreForProfile(profile) {
19972
20219
  if (profile.type !== "claude-max")
19973
20220
  return;
@@ -20472,15 +20719,7 @@ data: ${JSON.stringify(lastError)}
20472
20719
  const sdkModelDefaults = resolveSdkModelDefaults();
20473
20720
  const profileEnv = { ...sdkModelDefaults, ...cleanEnv, ...profile.env };
20474
20721
  const profileCredentialStore = credentialStoreForProfile(profile);
20475
- let systemContext = "";
20476
- if (body.system) {
20477
- if (typeof body.system === "string") {
20478
- systemContext = body.system;
20479
- } else if (Array.isArray(body.system)) {
20480
- systemContext = body.system.filter((b) => b.type === "text" && b.text).map((b) => b.text).join(`
20481
- `);
20482
- }
20483
- }
20722
+ let systemContext = extractSystemText(body.system);
20484
20723
  const adapterBase = adapter.baseName ?? adapter.name;
20485
20724
  const adapterTransforms = getAdapterTransforms(adapterBase);
20486
20725
  const pipeline = buildPipeline(adapterTransforms, pluginTransforms);
@@ -20562,8 +20801,11 @@ data: ${JSON.stringify(lastError)}
20562
20801
  const isUndo = lineageResult.type === "undo";
20563
20802
  const cachedSession = lineageResult.type !== "diverged" ? lineageResult.session : undefined;
20564
20803
  const resumeSessionId = cachedSession?.claudeSessionId;
20804
+ const passthrough = adapter.instancePassthrough !== undefined ? adapter.instancePassthrough : pipelineCtx.passthrough !== undefined ? pipelineCtx.passthrough : envBool("PASSTHROUGH");
20565
20805
  const resumeFrom = lineageResult.type === "continuation" || lineageResult.type === "compaction" ? lineageResult.resumeFrom : undefined;
20806
+ const resumeContentFrom = lineageResult.type === "continuation" ? lineageResult.resumeContentFrom : undefined;
20566
20807
  const undoRollbackUuid = isUndo && lineageResult.type === "undo" ? lineageResult.rollbackUuid : undefined;
20808
+ const passthroughResumeUuid = passthrough && isResume ? cachedSession?.passthroughResumeUuid : undefined;
20567
20809
  const msgSummary = body.messages?.map((m) => {
20568
20810
  const contentTypes = Array.isArray(m.content) ? m.content.map((b) => b.type).join(",") : "string";
20569
20811
  return `${m.role}[${contentTypes}]`;
@@ -20606,7 +20848,16 @@ data: ${JSON.stringify(lastError)}
20606
20848
  if (isUndo && undoRollbackUuid) {
20607
20849
  messagesToConvert = getLastUserMessage(allMessages);
20608
20850
  } else if (isResume) {
20609
- if (resumeFrom !== undefined && resumeFrom < allMessages.length) {
20851
+ if (resumeFrom !== undefined && resumeContentFrom !== undefined && resumeFrom < allMessages.length && Array.isArray(allMessages[resumeFrom]?.content)) {
20852
+ const boundaryMessage = allMessages[resumeFrom];
20853
+ messagesToConvert = [
20854
+ {
20855
+ ...boundaryMessage,
20856
+ content: boundaryMessage.content.slice(resumeContentFrom)
20857
+ },
20858
+ ...allMessages.slice(resumeFrom + 1)
20859
+ ];
20860
+ } else if (resumeFrom !== undefined && resumeFrom < allMessages.length) {
20610
20861
  messagesToConvert = allMessages.slice(resumeFrom);
20611
20862
  } else {
20612
20863
  messagesToConvert = getLastUserMessage(allMessages);
@@ -20655,6 +20906,13 @@ data: ${JSON.stringify(lastError)}
20655
20906
  if (structuredMessages.length > 1) {
20656
20907
  structuredMessages = consolidateMultimodalOntoLastUser(structuredMessages);
20657
20908
  }
20909
+ if (passthroughResumeUuid && structuredMessages.length > 0) {
20910
+ structuredMessages.unshift({
20911
+ type: "user",
20912
+ message: { role: "user", content: PASSTHROUGH_CONTINUATION_LEAD_IN },
20913
+ parent_tool_use_id: null
20914
+ });
20915
+ }
20658
20916
  } else {
20659
20917
  const toolIndex = buildToolUseIndex(allMessages ?? messagesToConvert ?? []);
20660
20918
  const promptTurns = (messagesToConvert ?? []).map((m) => {
@@ -20666,11 +20924,11 @@ data: ${JSON.stringify(lastError)}
20666
20924
  }
20667
20925
  return { role: "user", text: flattenUserContent(m.content, sanitizeOpts, toolIndex) };
20668
20926
  });
20669
- textPrompt = isResume ? promptTurns.map((t) => t.text).filter(Boolean).join(`
20927
+ const resumeDelta = promptTurns.map((t) => t.text).filter(Boolean).join(`
20670
20928
 
20671
- `) || "" : frameReplayTurns(promptTurns);
20929
+ `) || "";
20930
+ textPrompt = isResume ? passthroughResumeUuid ? framePassthroughContinuation(resumeDelta) : resumeDelta : frameReplayTurns(promptTurns);
20672
20931
  }
20673
- const passthrough = adapter.instancePassthrough !== undefined ? adapter.instancePassthrough : pipelineCtx.passthrough !== undefined ? pipelineCtx.passthrough : envBool("PASSTHROUGH");
20674
20932
  const settingSources = envBool("LOAD_CONTEXT") || sdkFeatures.claudeMd === "full" ? ["user", "project"] : sdkFeatures.claudeMd === "project" ? ["project"] : pipelineCtx.settingSources ?? [];
20675
20933
  const capturedToolUses = [];
20676
20934
  const capturedSignatures = new Set;
@@ -20838,6 +21096,7 @@ data: ${JSON.stringify(lastError)}
20838
21096
  claudeLog("upstream.start", { mode: "non_stream", model });
20839
21097
  let lastUsage;
20840
21098
  let lastStopReason;
21099
+ let nextPassthroughResumeUuid;
20841
21100
  try {
20842
21101
  if (!claudeExecutable) {
20843
21102
  claudeExecutable = await resolveClaudeExecutableAsync();
@@ -20874,8 +21133,8 @@ data: ${JSON.stringify(lastError)}
20874
21133
  hasDeferredTools,
20875
21134
  resumeSessionId,
20876
21135
  isUndo,
20877
- undoRollbackUuid,
20878
- forkSession: busySessionFork || undefined,
21136
+ resumeSessionAtUuid: undoRollbackUuid ?? passthroughResumeUuid,
21137
+ forkSession: busySessionFork || Boolean(passthroughResumeUuid) || undefined,
20879
21138
  sdkHooks,
20880
21139
  blockedTools: pipelineCtx.blockedTools,
20881
21140
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -20894,6 +21153,7 @@ data: ${JSON.stringify(lastError)}
20894
21153
  dreaming: sdkFeatures.dreaming,
20895
21154
  sharedMemory: sdkFeatures.sharedMemory,
20896
21155
  webFetchPreflight: sdkFeatures.webFetchPreflight,
21156
+ claudeAiConnectors: sdkFeatures.claudeAiConnectors,
20897
21157
  maxBudgetUsd: sdkFeatures.maxBudgetUsd,
20898
21158
  fallbackModel: sdkFeatures.fallbackModel,
20899
21159
  sdkDebug: sdkFeatures.sdkDebug,
@@ -20957,7 +21217,7 @@ data: ${JSON.stringify(lastError)}
20957
21217
  hasDeferredTools,
20958
21218
  resumeSessionId: undefined,
20959
21219
  isUndo: false,
20960
- undoRollbackUuid: undefined,
21220
+ resumeSessionAtUuid: undefined,
20961
21221
  sdkHooks,
20962
21222
  blockedTools: pipelineCtx.blockedTools,
20963
21223
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -20976,6 +21236,7 @@ data: ${JSON.stringify(lastError)}
20976
21236
  dreaming: sdkFeatures.dreaming,
20977
21237
  sharedMemory: sdkFeatures.sharedMemory,
20978
21238
  webFetchPreflight: sdkFeatures.webFetchPreflight,
21239
+ claudeAiConnectors: sdkFeatures.claudeAiConnectors,
20979
21240
  maxBudgetUsd: sdkFeatures.maxBudgetUsd,
20980
21241
  fallbackModel: sdkFeatures.fallbackModel,
20981
21242
  sdkDebug: sdkFeatures.sdkDebug,
@@ -21025,7 +21286,7 @@ data: ${JSON.stringify(lastError)}
21025
21286
  hasDeferredTools,
21026
21287
  resumeSessionId: undefined,
21027
21288
  isUndo: false,
21028
- undoRollbackUuid: undefined,
21289
+ resumeSessionAtUuid: undefined,
21029
21290
  sdkHooks,
21030
21291
  blockedTools: pipelineCtx.blockedTools,
21031
21292
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -21044,6 +21305,7 @@ data: ${JSON.stringify(lastError)}
21044
21305
  dreaming: sdkFeatures.dreaming,
21045
21306
  sharedMemory: sdkFeatures.sharedMemory,
21046
21307
  webFetchPreflight: sdkFeatures.webFetchPreflight,
21308
+ claudeAiConnectors: sdkFeatures.claudeAiConnectors,
21047
21309
  maxBudgetUsd: sdkFeatures.maxBudgetUsd,
21048
21310
  fallbackModel: sdkFeatures.fallbackModel,
21049
21311
  sdkDebug: sdkFeatures.sdkDebug,
@@ -21105,6 +21367,7 @@ data: ${JSON.stringify(lastError)}
21105
21367
  if (message.type === "assistant") {
21106
21368
  noteAssistantContent(earlyStop, message.message?.content);
21107
21369
  } else if (message.type === "user") {
21370
+ nextPassthroughResumeUuid = resumeBoundaryUuid(message) ?? nextPassthroughResumeUuid;
21108
21371
  noteUserContent(earlyStop, message.message?.content);
21109
21372
  if (shouldEarlyStop(earlyStop)) {
21110
21373
  earlyStopFired = true;
@@ -21344,7 +21607,7 @@ Subprocess stderr: ${stderrOutput}`;
21344
21607
  ]);
21345
21608
  }
21346
21609
  if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
21347
- storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
21610
+ storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughResumeUuid : null);
21348
21611
  }
21349
21612
  const responseSessionId = currentSessionId || resumeSessionId || `session_${Date.now()}`;
21350
21613
  return new Response(JSON.stringify({
@@ -21376,6 +21639,7 @@ Subprocess stderr: ${stderrOutput}`;
21376
21639
  let streamEventsSeen = 0;
21377
21640
  let eventsForwarded = 0;
21378
21641
  let textEventsForwarded = 0;
21642
+ let textCharsForwarded = 0;
21379
21643
  let bytesSent = 0;
21380
21644
  let streamClosed = false;
21381
21645
  let awaitingEarlyStopDrain = false;
@@ -21407,7 +21671,30 @@ Subprocess stderr: ${stderrOutput}`;
21407
21671
  let lastUsage;
21408
21672
  let hasStructuredOutput = false;
21409
21673
  let structuredOutput;
21674
+ let nextPassthroughResumeUuid;
21675
+ const silentTurnRecoveryEnabled = env("SILENT_TURN_RECOVERY") !== "0";
21676
+ let silentTurnRecoveryAttempted = false;
21677
+ let silentTurnRecovered = false;
21410
21678
  const streamedToolUseIds = new Set;
21679
+ let pendingTerminalDelta = null;
21680
+ let terminalDeltaSent = false;
21681
+ const sendTerminalDelta = (stopReasonOverride) => {
21682
+ if (terminalDeltaSent)
21683
+ return;
21684
+ const payload = stopReasonOverride ? encoder.encode(`event: message_delta
21685
+ data: ${JSON.stringify({
21686
+ type: "message_delta",
21687
+ delta: { stop_reason: stopReasonOverride, stop_sequence: null },
21688
+ usage: { output_tokens: lastUsage?.output_tokens ?? 0 }
21689
+ })}
21690
+
21691
+ `) : pendingTerminalDelta;
21692
+ if (!payload)
21693
+ return;
21694
+ terminalDeltaSent = true;
21695
+ if (safeEnqueue(payload, "terminal_message_delta"))
21696
+ eventsForwarded += 1;
21697
+ };
21411
21698
  const openClientBlocks = new Set;
21412
21699
  const resolvePendingStore = passthrough && earlyStopEnabled && !isIndependentSession && profileSessionId ? registerPendingStore(profileSessionId) : () => {};
21413
21700
  let pendingEarlyStop = false;
@@ -21423,10 +21710,7 @@ Subprocess stderr: ${stderrOutput}`;
21423
21710
  });
21424
21711
  pendingEarlyStop = false;
21425
21712
  flushOpenClientBlocks("early_stop");
21426
- safeEnqueue(encoder.encode(`event: message_delta
21427
- data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "tool_use", stop_sequence: null }, usage: { output_tokens: lastUsage?.output_tokens ?? 0 } })}
21428
-
21429
- `), "early_stop");
21713
+ sendTerminalDelta("tool_use");
21430
21714
  safeEnqueue(encoder.encode(`event: message_stop
21431
21715
  data: ${JSON.stringify({ type: "message_stop" })}
21432
21716
 
@@ -21456,8 +21740,8 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21456
21740
  }
21457
21741
  openClientBlocks.clear();
21458
21742
  };
21743
+ let currentSessionId;
21459
21744
  try {
21460
- let currentSessionId;
21461
21745
  const MAX_RATE_LIMIT_RETRIES = 2;
21462
21746
  const RATE_LIMIT_BASE_DELAY_MS = 1000;
21463
21747
  const response = async function* () {
@@ -21489,8 +21773,8 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21489
21773
  hasDeferredTools,
21490
21774
  resumeSessionId,
21491
21775
  isUndo,
21492
- undoRollbackUuid,
21493
- forkSession: busySessionFork || undefined,
21776
+ resumeSessionAtUuid: undoRollbackUuid ?? passthroughResumeUuid,
21777
+ forkSession: busySessionFork || Boolean(passthroughResumeUuid) || undefined,
21494
21778
  sdkHooks,
21495
21779
  blockedTools: pipelineCtx.blockedTools,
21496
21780
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -21509,6 +21793,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21509
21793
  dreaming: sdkFeatures.dreaming,
21510
21794
  sharedMemory: sdkFeatures.sharedMemory,
21511
21795
  webFetchPreflight: sdkFeatures.webFetchPreflight,
21796
+ claudeAiConnectors: sdkFeatures.claudeAiConnectors,
21512
21797
  maxBudgetUsd: sdkFeatures.maxBudgetUsd,
21513
21798
  fallbackModel: sdkFeatures.fallbackModel,
21514
21799
  sdkDebug: sdkFeatures.sdkDebug,
@@ -21571,7 +21856,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21571
21856
  hasDeferredTools,
21572
21857
  resumeSessionId: undefined,
21573
21858
  isUndo: false,
21574
- undoRollbackUuid: undefined,
21859
+ resumeSessionAtUuid: undefined,
21575
21860
  sdkHooks,
21576
21861
  blockedTools: pipelineCtx.blockedTools,
21577
21862
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -21590,6 +21875,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21590
21875
  dreaming: sdkFeatures.dreaming,
21591
21876
  sharedMemory: sdkFeatures.sharedMemory,
21592
21877
  webFetchPreflight: sdkFeatures.webFetchPreflight,
21878
+ claudeAiConnectors: sdkFeatures.claudeAiConnectors,
21593
21879
  maxBudgetUsd: sdkFeatures.maxBudgetUsd,
21594
21880
  fallbackModel: sdkFeatures.fallbackModel,
21595
21881
  sdkDebug: sdkFeatures.sdkDebug,
@@ -21639,7 +21925,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21639
21925
  hasDeferredTools,
21640
21926
  resumeSessionId: undefined,
21641
21927
  isUndo: false,
21642
- undoRollbackUuid: undefined,
21928
+ resumeSessionAtUuid: undefined,
21643
21929
  sdkHooks,
21644
21930
  blockedTools: pipelineCtx.blockedTools,
21645
21931
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -21658,6 +21944,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21658
21944
  dreaming: sdkFeatures.dreaming,
21659
21945
  sharedMemory: sdkFeatures.sharedMemory,
21660
21946
  webFetchPreflight: sdkFeatures.webFetchPreflight,
21947
+ claudeAiConnectors: sdkFeatures.claudeAiConnectors,
21661
21948
  maxBudgetUsd: sdkFeatures.maxBudgetUsd,
21662
21949
  fallbackModel: sdkFeatures.fallbackModel,
21663
21950
  sdkDebug: sdkFeatures.sdkDebug,
@@ -21751,6 +22038,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21751
22038
  if (message.type === "assistant" && message.uuid) {
21752
22039
  sdkUuidMap.push(message.uuid);
21753
22040
  }
22041
+ nextPassthroughResumeUuid = resumeBoundaryUuid(message) ?? nextPassthroughResumeUuid;
21754
22042
  if (earlyStopEnabled) {
21755
22043
  if (message.type === "assistant") {
21756
22044
  noteAssistantContent(earlyStop, message.message?.content);
@@ -21822,10 +22110,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
21822
22110
  if (messageStartEmitted) {
21823
22111
  if (passthrough && streamedToolUseIds.size > 0) {
21824
22112
  flushOpenClientBlocks("turn2_suppression");
21825
- safeEnqueue(encoder.encode(`event: message_delta
21826
- data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "tool_use", stop_sequence: null }, usage: { output_tokens: lastUsage?.output_tokens ?? 0 } })}
21827
-
21828
- `), "passthrough_turn2_stop");
22113
+ sendTerminalDelta("tool_use");
21829
22114
  safeEnqueue(encoder.encode(`event: message_stop
21830
22115
  data: ${JSON.stringify({ type: "message_stop" })}
21831
22116
 
@@ -21929,15 +22214,26 @@ data: ${JSON.stringify({
21929
22214
  }
21930
22215
  }
21931
22216
  }
22217
+ if (eventType === "content_block_delta" && event.delta?.type === "text_delta" && shouldInjectSilentTurn({
22218
+ raw: env("DEBUG_FORCE_SILENT_TURN"),
22219
+ sessionId: agentSessionId
22220
+ })) {
22221
+ claudeLog("debug.silent_turn_injected", { sessionId: agentSessionId });
22222
+ continue;
22223
+ }
21932
22224
  stripNonStandardStreamFields(event);
21933
22225
  const payload = encoder.encode(`event: ${eventType}
21934
22226
  data: ${JSON.stringify(event)}
21935
22227
 
21936
22228
  `);
21937
- if (!safeEnqueue(payload, `stream_event:${eventType}`)) {
21938
- break;
22229
+ if (eventType === "message_delta") {
22230
+ pendingTerminalDelta = payload;
22231
+ } else {
22232
+ if (!safeEnqueue(payload, `stream_event:${eventType}`)) {
22233
+ break;
22234
+ }
22235
+ eventsForwarded += 1;
21939
22236
  }
21940
- eventsForwarded += 1;
21941
22237
  if (eventType === "content_block_start") {
21942
22238
  const idx = event.index;
21943
22239
  if (typeof idx === "number")
@@ -21953,6 +22249,7 @@ data: ${JSON.stringify(event)}
21953
22249
  }
21954
22250
  if (passthrough && eventType === "message_delta" && event.delta?.stop_reason === "tool_use" && streamedToolUseIds.size > 0) {
21955
22251
  flushOpenClientBlocks("drain_close");
22252
+ sendTerminalDelta();
21956
22253
  safeEnqueue(encoder.encode(`event: message_stop
21957
22254
  data: ${JSON.stringify({ type: "message_stop" })}
21958
22255
 
@@ -21969,6 +22266,8 @@ data: ${JSON.stringify({ type: "message_stop" })}
21969
22266
  const delta = event.delta;
21970
22267
  if (delta?.type === "text_delta") {
21971
22268
  textEventsForwarded += 1;
22269
+ if (typeof delta.text === "string")
22270
+ textCharsForwarded += delta.text.length;
21972
22271
  }
21973
22272
  }
21974
22273
  }
@@ -22030,6 +22329,7 @@ data: ${JSON.stringify({
22030
22329
  messageStartEmitted = true;
22031
22330
  eventsForwarded += 5;
22032
22331
  textEventsForwarded += 1;
22332
+ textCharsForwarded += text.length;
22033
22333
  }
22034
22334
  if (passthrough) {
22035
22335
  recordEnvelopeViolations(checkUndeliveredToolUses(capturedToolUses, streamedToolUseIds));
@@ -22055,9 +22355,125 @@ data: ${JSON.stringify({
22055
22355
  plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
22056
22356
  }
22057
22357
  if (currentSessionId && !isIndependentSession && !sawDuplicateToolUse) {
22058
- storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
22358
+ storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, earlyStopFired ? nextPassthroughResumeUuid : null);
22059
22359
  }
22060
22360
  resolvePendingStore();
22361
+ const classifyNow = () => classifyTurnOutcome({
22362
+ textEvents: textEventsForwarded,
22363
+ toolUses: streamedToolUseIds.size,
22364
+ blocksForwarded: eventsForwarded
22365
+ });
22366
+ const preRecoveryOutcome = classifyNow();
22367
+ if (!streamClosed && messageStartEmitted && shouldAttemptRecovery({
22368
+ outcome: preRecoveryOutcome,
22369
+ alreadyAttempted: silentTurnRecoveryAttempted,
22370
+ clientGone: streamClosed,
22371
+ sessionId: currentSessionId || resumeSessionId,
22372
+ enabled: silentTurnRecoveryEnabled
22373
+ })) {
22374
+ silentTurnRecoveryAttempted = true;
22375
+ const capturedBeforeRecovery = capturedToolUses.length;
22376
+ claudeLog("response.silent_turn_recovery", {
22377
+ mode: "stream",
22378
+ kind: preRecoveryOutcome.kind,
22379
+ reason: preRecoveryOutcome.kind === "silent" ? preRecoveryOutcome.reason : undefined,
22380
+ sdkSessionId: currentSessionId || resumeSessionId
22381
+ });
22382
+ const recoveryLifter = createRecoveryLifter(() => nextClientBlockIndex++);
22383
+ let recoverySessionId;
22384
+ let recoveryBoundaryUuid;
22385
+ try {
22386
+ for await (const event of guardUpstreamIdle(query(buildQueryOptions({
22387
+ prompt: SILENT_TURN_NUDGE,
22388
+ model,
22389
+ workingDirectory,
22390
+ clientWorkingDirectory,
22391
+ systemContext,
22392
+ claudeExecutable,
22393
+ passthrough,
22394
+ stream: true,
22395
+ sdkAgents,
22396
+ passthroughMcp,
22397
+ cleanEnv: profileEnv,
22398
+ envOverrides,
22399
+ hasDeferredTools,
22400
+ resumeSessionId: currentSessionId || resumeSessionId,
22401
+ isUndo: false,
22402
+ resumeSessionAtUuid: nextPassthroughResumeUuid,
22403
+ forkSession: true,
22404
+ sdkHooks,
22405
+ blockedTools: pipelineCtx.blockedTools,
22406
+ incompatibleTools: pipelineCtx.incompatibleTools,
22407
+ mcpServerName: adapter.getMcpServerName(),
22408
+ allowedMcpTools: pipelineCtx.allowedMcpTools,
22409
+ onStderr,
22410
+ effort,
22411
+ thinking,
22412
+ taskBudget,
22413
+ outputFormat,
22414
+ betas,
22415
+ settingSources,
22416
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt,
22417
+ clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
22418
+ memory: sdkFeatures.memory,
22419
+ dreaming: sdkFeatures.dreaming,
22420
+ sharedMemory: sdkFeatures.sharedMemory,
22421
+ webFetchPreflight: sdkFeatures.webFetchPreflight,
22422
+ claudeAiConnectors: sdkFeatures.claudeAiConnectors,
22423
+ maxBudgetUsd: sdkFeatures.maxBudgetUsd,
22424
+ fallbackModel: sdkFeatures.fallbackModel,
22425
+ sdkDebug: sdkFeatures.sdkDebug,
22426
+ additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
22427
+ advisorModel
22428
+ }, requestAbort.controller)), UPSTREAM_IDLE_MS, (sinceLastMs) => claudeLog("upstream.stalled", { mode: "silent_recovery", model, sinceLastMs }))) {
22429
+ const recoveryMessage = event;
22430
+ if (recoveryMessage.session_id)
22431
+ recoverySessionId = recoveryMessage.session_id;
22432
+ recoveryBoundaryUuid = resumeBoundaryUuid(recoveryMessage) ?? recoveryBoundaryUuid;
22433
+ if (recoveryMessage.type !== "stream_event")
22434
+ continue;
22435
+ const lifted = recoveryLifter.lift(event.event);
22436
+ if (!lifted)
22437
+ continue;
22438
+ safeEnqueue(encoder.encode(`event: ${lifted.frame.type}
22439
+ data: ${JSON.stringify(lifted.frame)}
22440
+
22441
+ `), `silent_recovery_${lifted.kind}`);
22442
+ if (lifted.kind === "block_start") {
22443
+ eventsForwarded += 1;
22444
+ } else if (lifted.kind === "text_delta") {
22445
+ textEventsForwarded += 1;
22446
+ textCharsForwarded += lifted.textChars;
22447
+ silentTurnRecovered = true;
22448
+ }
22449
+ }
22450
+ } catch (recoveryError) {
22451
+ claudeLog("response.silent_turn_recovery_failed", {
22452
+ mode: "stream",
22453
+ error: recoveryError instanceof Error ? recoveryError.message : String(recoveryError)
22454
+ });
22455
+ }
22456
+ if (capturedToolUses.length > capturedBeforeRecovery) {
22457
+ silentTurnRecovered = true;
22458
+ }
22459
+ if (silentTurnRecovered && recoverySessionId && !isIndependentSession && !sawDuplicateToolUse) {
22460
+ currentSessionId = recoverySessionId;
22461
+ nextPassthroughResumeUuid = recoveryBoundaryUuid;
22462
+ sdkUuidMap.length = 0;
22463
+ for (let i = 0;i < allMessages.length; i++)
22464
+ sdkUuidMap.push(null);
22465
+ storeSession(profileSessionId, body.messages || [], recoverySessionId, profileScopedCwd, sdkUuidMap, lastUsage, recoveryBoundaryUuid ?? null);
22466
+ }
22467
+ claudeLog("response.silent_turn_recovery_result", {
22468
+ mode: "stream",
22469
+ recovered: silentTurnRecovered,
22470
+ textEvents: textEventsForwarded,
22471
+ forkedSession: recoverySessionId ?? null
22472
+ });
22473
+ if (silentTurnRecovered && preRecoveryOutcome.kind === "silent") {
22474
+ diagnosticLog2.session(`${requestMeta.requestId} silent_turn reason=${preRecoveryOutcome.reason} ` + `blocks=${eventsForwarded} out=${lastUsage?.output_tokens ?? 0} ` + `recovery=succeeded`, requestMeta.requestId);
22475
+ }
22476
+ }
22061
22477
  if (!streamClosed) {
22062
22478
  const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
22063
22479
  if (passthrough && unseenToolUses.length > 0 && messageStartEmitted) {
@@ -22089,14 +22505,7 @@ data: ${JSON.stringify({
22089
22505
 
22090
22506
  `), "passthrough_tool_block_stop");
22091
22507
  }
22092
- safeEnqueue(encoder.encode(`event: message_delta
22093
- data: ${JSON.stringify({
22094
- type: "message_delta",
22095
- delta: { stop_reason: "tool_use", stop_sequence: null },
22096
- usage: { output_tokens: 0 }
22097
- })}
22098
-
22099
- `), "passthrough_message_delta");
22508
+ sendTerminalDelta("tool_use");
22100
22509
  }
22101
22510
  if (trackFileChanges && passthrough && pipelineCtx.extractFileChangesFromToolUse) {
22102
22511
  const passthroughChanges = extractFileChangesFromMessages(body.messages || [], pipelineCtx.extractFileChangesFromToolUse);
@@ -22133,6 +22542,7 @@ data: ${JSON.stringify({
22133
22542
  }
22134
22543
  }
22135
22544
  if (messageStartEmitted) {
22545
+ sendTerminalDelta();
22136
22546
  safeEnqueue(encoder.encode(`event: message_stop
22137
22547
  data: {"type":"message_stop"}
22138
22548
 
@@ -22198,13 +22608,18 @@ data: {"type":"message_stop"}
22198
22608
  cacheHitRate: computeCacheHitRate(lastUsage),
22199
22609
  ...envelopeViolations.length > 0 ? { envelopeViolations: [...envelopeViolations] } : {}
22200
22610
  });
22201
- if (textEventsForwarded === 0) {
22202
- claudeLog("response.empty_stream", {
22611
+ const turnOutcome = classifyNow();
22612
+ if (turnOutcome.kind === "silent") {
22613
+ claudeLog("response.silent_turn", {
22203
22614
  model,
22615
+ reason: turnOutcome.reason,
22204
22616
  streamEventsSeen,
22205
22617
  eventsForwarded,
22206
- reason: "no_text_deltas_forwarded"
22618
+ outputTokens: lastUsage?.output_tokens,
22619
+ recovered: silentTurnRecovered,
22620
+ recoveryAttempted: silentTurnRecoveryAttempted
22207
22621
  });
22622
+ diagnosticLog2.session(`${requestMeta.requestId} silent_turn reason=${turnOutcome.reason} ` + `blocks=${eventsForwarded} out=${lastUsage?.output_tokens ?? 0} ` + `recovery=${silentTurnRecoveryAttempted ? silentTurnRecovered ? "succeeded" : "failed" : "off"}`, requestMeta.requestId);
22208
22623
  }
22209
22624
  }
22210
22625
  } catch (error) {
@@ -22217,6 +22632,21 @@ data: {"type":"message_stop"}
22217
22632
  textEventsForwarded,
22218
22633
  durationMs: Date.now() - requestStartAt
22219
22634
  });
22635
+ const disposition = clientAbortDisposition({
22636
+ isIndependentSession,
22637
+ profileSessionId,
22638
+ currentSessionId,
22639
+ sawDuplicateToolUse,
22640
+ resumeBoundaryUuid: nextPassthroughResumeUuid,
22641
+ passthrough
22642
+ });
22643
+ if (disposition.action === "store" && currentSessionId) {
22644
+ storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage, disposition.resumeUuid);
22645
+ } else if (disposition.action === "evict") {
22646
+ evictSession(profileSessionId, profileScopedCwd, body.messages || []);
22647
+ }
22648
+ claudeLog("passthrough.client_abort_settled", { action: disposition.action });
22649
+ resolvePendingStore();
22220
22650
  return;
22221
22651
  }
22222
22652
  resolvePendingStore();
@@ -22370,26 +22800,41 @@ data: {"type":"message_stop"}
22370
22800
  error: streamErr.type
22371
22801
  });
22372
22802
  if (messageStartEmitted) {
22803
+ const errorStopReason = textEventsForwarded > 0 ? "end_turn" : "max_tokens";
22804
+ claudeLog("response.error_envelope", {
22805
+ mode: "stream",
22806
+ stopReason: errorStopReason,
22807
+ textEvents: textEventsForwarded,
22808
+ classified: streamErr.type
22809
+ });
22373
22810
  safeEnqueue(encoder.encode(`event: message_delta
22374
22811
  data: ${JSON.stringify({
22375
22812
  type: "message_delta",
22376
- delta: { stop_reason: "end_turn", stop_sequence: null },
22813
+ delta: { stop_reason: errorStopReason, stop_sequence: null },
22377
22814
  usage: { output_tokens: 0 }
22378
22815
  })}
22379
22816
 
22380
22817
  `), "error_message_delta");
22818
+ safeEnqueue(encoder.encode(`event: error
22819
+ data: ${JSON.stringify({
22820
+ type: "error",
22821
+ error: { type: streamErr.type, message: streamErr.message }
22822
+ })}
22823
+
22824
+ `), "error_event_before_stop");
22381
22825
  safeEnqueue(encoder.encode(`event: message_stop
22382
22826
  data: {"type":"message_stop"}
22383
22827
 
22384
22828
  `), "error_message_stop");
22385
- }
22386
- safeEnqueue(encoder.encode(`event: error
22829
+ } else {
22830
+ safeEnqueue(encoder.encode(`event: error
22387
22831
  data: ${JSON.stringify({
22388
- type: "error",
22389
- error: { type: streamErr.type, message: streamErr.message }
22390
- })}
22832
+ type: "error",
22833
+ error: { type: streamErr.type, message: streamErr.message }
22834
+ })}
22391
22835
 
22392
22836
  `), "error_event");
22837
+ }
22393
22838
  if (!streamClosed) {
22394
22839
  try {
22395
22840
  controller.close();
@@ -22647,7 +23092,7 @@ data: ${JSON.stringify({
22647
23092
  });
22648
23093
  });
22649
23094
  app.get("/profiles", async (c) => {
22650
- const { profilePageHtml } = await import("./profilePage-naychnb8.js");
23095
+ const { profilePageHtml } = await import("./profilePage-gtazq15d.js");
22651
23096
  return c.html(profilePageHtml);
22652
23097
  });
22653
23098
  app.post("/profiles/active", async (c) => {
@@ -22730,14 +23175,25 @@ data: ${JSON.stringify({
22730
23175
  });
22731
23176
  app.post("/v1/chat/completions", async (c) => {
22732
23177
  const rawBody = await c.req.json();
22733
- const anthropicBody = translateOpenAiToAnthropic(rawBody);
23178
+ const userAgent = c.req.header("user-agent") ?? "";
23179
+ const jcodeSessionId = userAgent.startsWith("jcode/") ? normalizeJcodeSessionId(c.req.header("x-jcode-session")) : undefined;
23180
+ const isJcode = jcodeSessionId !== undefined;
23181
+ const adapterName = isJcode ? "jcode" : "openai";
23182
+ const anthropicBody = translateOpenAiToAnthropic(rawBody, {
23183
+ preserveConversationHistory: isJcode
23184
+ });
22734
23185
  if (!anthropicBody) {
22735
23186
  return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
22736
23187
  }
22737
23188
  const internalHeaders = {
22738
23189
  "Content-Type": "application/json",
22739
- "x-meridian-agent": "openai"
23190
+ "x-meridian-agent": adapterName
22740
23191
  };
23192
+ if (jcodeSessionId)
23193
+ internalHeaders["x-jcode-session"] = jcodeSessionId;
23194
+ const requestedProfile = c.req.header("x-meridian-profile");
23195
+ if (requestedProfile)
23196
+ internalHeaders["x-meridian-profile"] = requestedProfile;
22741
23197
  const xApiKey = c.req.header("x-api-key");
22742
23198
  if (xApiKey)
22743
23199
  internalHeaders["x-api-key"] = xApiKey;
@@ -22758,7 +23214,7 @@ data: ${JSON.stringify({
22758
23214
  const created = Math.floor(Date.now() / 1000);
22759
23215
  const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : CANONICAL_SONNET_MODEL;
22760
23216
  const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
22761
- const sdkFeatures = getFeaturesForAdapter2("openai");
23217
+ const sdkFeatures = getFeaturesForAdapter2(adapterName);
22762
23218
  if (!anthropicBody.stream) {
22763
23219
  const anthropicRes = await internalRes.json();
22764
23220
  return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created, {
@@ -23004,7 +23460,7 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
23004
23460
  const profilesList = getEffectiveProfiles(finalConfig.profiles);
23005
23461
  const activeId = getActiveProfileId() || finalConfig.defaultProfile || profilesList[0]?.id || null;
23006
23462
  if (profilesList.length === 0) {
23007
- const oauth = await fetchOAuthUsage({});
23463
+ const { snapshot: oauth, error } = await fetchOAuthUsageResult({});
23008
23464
  return c.json({
23009
23465
  profiles: [{
23010
23466
  id: "default",
@@ -23012,7 +23468,7 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
23012
23468
  windows: oauth?.windows ?? [],
23013
23469
  extraUsage: oauth?.extraUsage ?? null,
23014
23470
  fetchedAt: oauth?.fetchedAt ?? null,
23015
- error: oauth ? null : "no_token"
23471
+ error
23016
23472
  }],
23017
23473
  activeProfile: "default",
23018
23474
  asOf: Date.now()
@@ -23031,7 +23487,7 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
23031
23487
  error: "not_oauth"
23032
23488
  };
23033
23489
  }
23034
- const oauth = await fetchOAuthUsage({
23490
+ const { snapshot: oauth, error } = await fetchOAuthUsageResult({
23035
23491
  profileId: p.id,
23036
23492
  claudeConfigDir: p.claudeConfigDir
23037
23493
  });
@@ -23042,7 +23498,7 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
23042
23498
  windows: oauth?.windows ?? [],
23043
23499
  extraUsage: oauth?.extraUsage ?? null,
23044
23500
  fetchedAt: oauth?.fetchedAt ?? null,
23045
- error: oauth ? null : "no_token"
23501
+ error
23046
23502
  };
23047
23503
  }));
23048
23504
  return c.json({