cascade-ai 0.76.0 → 0.77.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/dist/cli.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var dns = require('dns');
3
+ var dnsCallback = require('dns');
4
4
  var http = require('http');
5
5
  var https = require('https');
6
6
  var zlib = require('zlib');
@@ -9,8 +9,8 @@ var Anthropic = require('@anthropic-ai/sdk');
9
9
  var OpenAI = require('openai');
10
10
  var genai = require('@google/genai');
11
11
  var Database2 = require('better-sqlite3');
12
- var path35 = require('path');
13
- var fs32 = require('fs');
12
+ var path36 = require('path');
13
+ var fs33 = require('fs');
14
14
  var os10 = require('os');
15
15
  var crypto = require('crypto');
16
16
  var ink = require('ink');
@@ -31,6 +31,7 @@ var simpleGit = require('simple-git');
31
31
  var PDFDocument = require('pdfkit');
32
32
  var dns2 = require('dns/promises');
33
33
  var net = require('net');
34
+ var undici = require('undici');
34
35
  var index_js = require('@modelcontextprotocol/sdk/client/index.js');
35
36
  var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
36
37
  var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
@@ -70,15 +71,15 @@ function _interopNamespace(e) {
70
71
  return Object.freeze(n);
71
72
  }
72
73
 
73
- var dns__default = /*#__PURE__*/_interopDefault(dns);
74
+ var dnsCallback__default = /*#__PURE__*/_interopDefault(dnsCallback);
74
75
  var http__default = /*#__PURE__*/_interopDefault(http);
75
76
  var https__default = /*#__PURE__*/_interopDefault(https);
76
77
  var zlib__default = /*#__PURE__*/_interopDefault(zlib);
77
78
  var Anthropic__default = /*#__PURE__*/_interopDefault(Anthropic);
78
79
  var OpenAI__default = /*#__PURE__*/_interopDefault(OpenAI);
79
80
  var Database2__default = /*#__PURE__*/_interopDefault(Database2);
80
- var path35__default = /*#__PURE__*/_interopDefault(path35);
81
- var fs32__default = /*#__PURE__*/_interopDefault(fs32);
81
+ var path36__default = /*#__PURE__*/_interopDefault(path36);
82
+ var fs33__default = /*#__PURE__*/_interopDefault(fs33);
82
83
  var os10__default = /*#__PURE__*/_interopDefault(os10);
83
84
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
84
85
  var React2__default = /*#__PURE__*/_interopDefault(React2);
@@ -1848,7 +1849,7 @@ var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SEC
1848
1849
  var init_constants = __esm({
1849
1850
  "src/constants.ts"() {
1850
1851
  init_pricing();
1851
- CASCADE_VERSION = "0.76.0";
1852
+ CASCADE_VERSION = "0.77.0";
1852
1853
  CASCADE_CONFIG_FILE = ".cascade/config.json";
1853
1854
  CASCADE_DB_FILE = ".cascade/memory.db";
1854
1855
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
@@ -1861,64 +1862,88 @@ var init_constants = __esm({
1861
1862
  DEFAULT_CONTEXT_LIMIT = 2e5;
1862
1863
  DEFAULT_AUTO_SUMMARIZE_AT = 15e4;
1863
1864
  MODELS = {
1864
- // Anthropic
1865
- "claude-opus-4": {
1866
- id: "claude-opus-4-5",
1867
- name: "Claude Opus 4",
1865
+ // Anthropic — active Claude API models (2026-08-27).
1866
+ "claude-fable-5": {
1867
+ id: "claude-fable-5",
1868
+ name: "Claude Fable 5",
1868
1869
  provider: "anthropic",
1869
- contextWindow: 2e5,
1870
+ contextWindow: 1e6,
1870
1871
  isVisionCapable: true,
1871
- inputCostPer1kTokens: 0.015,
1872
- outputCostPer1kTokens: 0.075,
1873
- maxOutputTokens: 32e3,
1872
+ inputCostPer1kTokens: 0.01,
1873
+ outputCostPer1kTokens: 0.05,
1874
+ maxOutputTokens: 128e3,
1874
1875
  supportsStreaming: true,
1875
1876
  isLocal: false
1876
1877
  },
1877
- "claude-sonnet-4": {
1878
- id: "claude-sonnet-4-5",
1879
- name: "Claude Sonnet 4",
1878
+ "claude-opus-5": {
1879
+ id: "claude-opus-5",
1880
+ name: "Claude Opus 5",
1880
1881
  provider: "anthropic",
1881
- contextWindow: 2e5,
1882
+ contextWindow: 1e6,
1882
1883
  isVisionCapable: true,
1883
- inputCostPer1kTokens: 3e-3,
1884
- outputCostPer1kTokens: 0.015,
1885
- maxOutputTokens: 16e3,
1884
+ inputCostPer1kTokens: 5e-3,
1885
+ outputCostPer1kTokens: 0.025,
1886
+ maxOutputTokens: 128e3,
1886
1887
  supportsStreaming: true,
1887
1888
  isLocal: false
1888
1889
  },
1889
- "claude-haiku-3-5": {
1890
- id: "claude-haiku-3-5-20251001",
1891
- name: "Claude Haiku 3.5",
1890
+ "claude-opus-4-8": {
1891
+ id: "claude-opus-4-8",
1892
+ name: "Claude Opus 4.8",
1892
1893
  provider: "anthropic",
1893
- contextWindow: 2e5,
1894
+ contextWindow: 1e6,
1894
1895
  isVisionCapable: true,
1895
- inputCostPer1kTokens: 8e-4,
1896
- outputCostPer1kTokens: 4e-3,
1897
- maxOutputTokens: 8e3,
1896
+ inputCostPer1kTokens: 5e-3,
1897
+ outputCostPer1kTokens: 0.025,
1898
+ maxOutputTokens: 128e3,
1898
1899
  supportsStreaming: true,
1899
1900
  isLocal: false
1900
1901
  },
1901
- "claude-haiku-4-5": {
1902
- id: "claude-haiku-4-5-20251001",
1903
- name: "Claude Haiku 4.5",
1902
+ "claude-opus-4-7": {
1903
+ id: "claude-opus-4-7",
1904
+ name: "Claude Opus 4.7",
1904
1905
  provider: "anthropic",
1905
- contextWindow: 2e5,
1906
+ contextWindow: 1e6,
1906
1907
  isVisionCapable: true,
1907
- inputCostPer1kTokens: 8e-4,
1908
- outputCostPer1kTokens: 4e-3,
1909
- maxOutputTokens: 8e3,
1908
+ inputCostPer1kTokens: 5e-3,
1909
+ outputCostPer1kTokens: 0.025,
1910
+ maxOutputTokens: 128e3,
1910
1911
  supportsStreaming: true,
1911
1912
  isLocal: false
1912
1913
  },
1913
- "claude-opus-4-8": {
1914
- id: "claude-opus-4-8",
1915
- name: "Claude Opus 4.8",
1914
+ "claude-opus-4-6": {
1915
+ id: "claude-opus-4-6",
1916
+ name: "Claude Opus 4.6",
1917
+ provider: "anthropic",
1918
+ contextWindow: 1e6,
1919
+ isVisionCapable: true,
1920
+ inputCostPer1kTokens: 5e-3,
1921
+ outputCostPer1kTokens: 0.025,
1922
+ maxOutputTokens: 128e3,
1923
+ supportsStreaming: true,
1924
+ isLocal: false
1925
+ },
1926
+ "claude-opus-4-5": {
1927
+ id: "claude-opus-4-5-20251101",
1928
+ name: "Claude Opus 4.5",
1916
1929
  provider: "anthropic",
1917
1930
  contextWindow: 2e5,
1918
1931
  isVisionCapable: true,
1919
- inputCostPer1kTokens: 0.015,
1920
- outputCostPer1kTokens: 0.075,
1921
- maxOutputTokens: 32e3,
1932
+ inputCostPer1kTokens: 5e-3,
1933
+ outputCostPer1kTokens: 0.025,
1934
+ maxOutputTokens: 64e3,
1935
+ supportsStreaming: true,
1936
+ isLocal: false
1937
+ },
1938
+ "claude-sonnet-5": {
1939
+ id: "claude-sonnet-5",
1940
+ name: "Claude Sonnet 5",
1941
+ provider: "anthropic",
1942
+ contextWindow: 1e6,
1943
+ isVisionCapable: true,
1944
+ inputCostPer1kTokens: 2e-3,
1945
+ outputCostPer1kTokens: 0.01,
1946
+ maxOutputTokens: 128e3,
1922
1947
  supportsStreaming: true,
1923
1948
  isLocal: false
1924
1949
  },
@@ -1926,76 +1951,135 @@ var init_constants = __esm({
1926
1951
  id: "claude-sonnet-4-6",
1927
1952
  name: "Claude Sonnet 4.6",
1928
1953
  provider: "anthropic",
1954
+ contextWindow: 1e6,
1955
+ isVisionCapable: true,
1956
+ inputCostPer1kTokens: 3e-3,
1957
+ outputCostPer1kTokens: 0.015,
1958
+ maxOutputTokens: 64e3,
1959
+ supportsStreaming: true,
1960
+ isLocal: false
1961
+ },
1962
+ "claude-sonnet-4-5": {
1963
+ id: "claude-sonnet-4-5-20250929",
1964
+ name: "Claude Sonnet 4.5",
1965
+ provider: "anthropic",
1929
1966
  contextWindow: 2e5,
1930
1967
  isVisionCapable: true,
1931
1968
  inputCostPer1kTokens: 3e-3,
1932
1969
  outputCostPer1kTokens: 0.015,
1933
- maxOutputTokens: 16e3,
1970
+ maxOutputTokens: 64e3,
1934
1971
  supportsStreaming: true,
1935
1972
  isLocal: false
1936
1973
  },
1937
- // OpenAI
1938
- "gpt-4o": {
1939
- id: "gpt-4o",
1940
- name: "GPT-4o",
1974
+ "claude-haiku-4-5": {
1975
+ id: "claude-haiku-4-5-20251001",
1976
+ name: "Claude Haiku 4.5",
1977
+ provider: "anthropic",
1978
+ contextWindow: 2e5,
1979
+ isVisionCapable: true,
1980
+ inputCostPer1kTokens: 1e-3,
1981
+ outputCostPer1kTokens: 5e-3,
1982
+ maxOutputTokens: 64e3,
1983
+ supportsStreaming: true,
1984
+ isLocal: false
1985
+ },
1986
+ // OpenAI — active chat-capable models used by Cascade's Chat Completions path.
1987
+ "gpt-5.6-sol": {
1988
+ id: "gpt-5.6-sol",
1989
+ name: "GPT-5.6 Sol",
1941
1990
  provider: "openai",
1942
- contextWindow: 128e3,
1991
+ contextWindow: 105e4,
1992
+ isVisionCapable: true,
1993
+ inputCostPer1kTokens: 4e-3,
1994
+ outputCostPer1kTokens: 0.02,
1995
+ maxOutputTokens: 128e3,
1996
+ supportsStreaming: true,
1997
+ isLocal: false
1998
+ },
1999
+ "gpt-5.6-terra": {
2000
+ id: "gpt-5.6-terra",
2001
+ name: "GPT-5.6 Terra",
2002
+ provider: "openai",
2003
+ contextWindow: 105e4,
2004
+ isVisionCapable: true,
2005
+ inputCostPer1kTokens: 2e-3,
2006
+ outputCostPer1kTokens: 0.012,
2007
+ maxOutputTokens: 128e3,
2008
+ supportsStreaming: true,
2009
+ isLocal: false
2010
+ },
2011
+ "gpt-5.6-luna": {
2012
+ id: "gpt-5.6-luna",
2013
+ name: "GPT-5.6 Luna",
2014
+ provider: "openai",
2015
+ contextWindow: 105e4,
2016
+ isVisionCapable: true,
2017
+ inputCostPer1kTokens: 2e-4,
2018
+ outputCostPer1kTokens: 12e-4,
2019
+ maxOutputTokens: 128e3,
2020
+ supportsStreaming: true,
2021
+ isLocal: false
2022
+ },
2023
+ "gpt-5.5": {
2024
+ id: "gpt-5.5",
2025
+ name: "GPT-5.5",
2026
+ provider: "openai",
2027
+ contextWindow: 105e4,
1943
2028
  isVisionCapable: true,
1944
2029
  inputCostPer1kTokens: 5e-3,
1945
- outputCostPer1kTokens: 0.015,
1946
- maxOutputTokens: 16e3,
2030
+ outputCostPer1kTokens: 0.03,
2031
+ maxOutputTokens: 128e3,
1947
2032
  supportsStreaming: true,
1948
2033
  isLocal: false
1949
2034
  },
1950
- "gpt-4o-mini": {
1951
- id: "gpt-4o-mini",
1952
- name: "GPT-4o Mini",
2035
+ "gpt-5.4": {
2036
+ id: "gpt-5.4",
2037
+ name: "GPT-5.4",
1953
2038
  provider: "openai",
1954
- contextWindow: 128e3,
2039
+ contextWindow: 105e4,
1955
2040
  isVisionCapable: true,
1956
- inputCostPer1kTokens: 15e-5,
1957
- outputCostPer1kTokens: 6e-4,
1958
- maxOutputTokens: 16e3,
2041
+ inputCostPer1kTokens: 25e-4,
2042
+ outputCostPer1kTokens: 0.015,
2043
+ maxOutputTokens: 128e3,
1959
2044
  supportsStreaming: true,
1960
2045
  isLocal: false
1961
2046
  },
1962
- "gpt-4.1": {
1963
- id: "gpt-4.1",
1964
- name: "GPT-4.1",
2047
+ "gpt-5.4-mini": {
2048
+ id: "gpt-5.4-mini",
2049
+ name: "GPT-5.4 Mini",
1965
2050
  provider: "openai",
1966
- contextWindow: 1047576,
2051
+ contextWindow: 4e5,
1967
2052
  isVisionCapable: true,
1968
- inputCostPer1kTokens: 2e-3,
1969
- outputCostPer1kTokens: 8e-3,
1970
- maxOutputTokens: 32768,
2053
+ inputCostPer1kTokens: 75e-5,
2054
+ outputCostPer1kTokens: 45e-4,
2055
+ maxOutputTokens: 128e3,
1971
2056
  supportsStreaming: true,
1972
2057
  isLocal: false
1973
2058
  },
1974
- "gpt-4.1-mini": {
1975
- id: "gpt-4.1-mini",
1976
- name: "GPT-4.1 Mini",
2059
+ "gpt-5.4-nano": {
2060
+ id: "gpt-5.4-nano",
2061
+ name: "GPT-5.4 Nano",
1977
2062
  provider: "openai",
1978
- contextWindow: 1047576,
2063
+ contextWindow: 4e5,
1979
2064
  isVisionCapable: true,
1980
- inputCostPer1kTokens: 4e-4,
1981
- outputCostPer1kTokens: 16e-4,
1982
- maxOutputTokens: 32768,
2065
+ inputCostPer1kTokens: 2e-4,
2066
+ outputCostPer1kTokens: 125e-5,
2067
+ maxOutputTokens: 128e3,
1983
2068
  supportsStreaming: true,
1984
2069
  isLocal: false
1985
2070
  },
1986
- "gpt-4.1-nano": {
1987
- id: "gpt-4.1-nano",
1988
- name: "GPT-4.1 Nano",
2071
+ "gpt-5.2": {
2072
+ id: "gpt-5.2",
2073
+ name: "GPT-5.2",
1989
2074
  provider: "openai",
1990
- contextWindow: 1047576,
2075
+ contextWindow: 4e5,
1991
2076
  isVisionCapable: true,
1992
- inputCostPer1kTokens: 1e-4,
1993
- outputCostPer1kTokens: 4e-4,
1994
- maxOutputTokens: 32768,
2077
+ inputCostPer1kTokens: 175e-5,
2078
+ outputCostPer1kTokens: 0.014,
2079
+ maxOutputTokens: 128e3,
1995
2080
  supportsStreaming: true,
1996
2081
  isLocal: false
1997
2082
  },
1998
- // GPT-5 family (offline baseline pricing; the live fetch keeps it current).
1999
2083
  "gpt-5": {
2000
2084
  id: "gpt-5",
2001
2085
  name: "GPT-5",
@@ -2032,79 +2116,149 @@ var init_constants = __esm({
2032
2116
  supportsStreaming: true,
2033
2117
  isLocal: false
2034
2118
  },
2035
- // GPT-5 point releases — genuinely distinct capability, so they route as their
2036
- // own families (not folded into gpt-5). Offline baseline pricing; live fetch
2037
- // keeps it current. gpt-5.5 "Spud" (Apr 2026) is the current SWE-bench leader.
2038
- "gpt-5.5": {
2039
- id: "gpt-5.5",
2040
- name: "GPT-5.5",
2119
+ "gpt-4.1": {
2120
+ id: "gpt-4.1",
2121
+ name: "GPT-4.1",
2041
2122
  provider: "openai",
2042
- contextWindow: 4e5,
2123
+ contextWindow: 1047576,
2043
2124
  isVisionCapable: true,
2044
- inputCostPer1kTokens: 25e-4,
2045
- outputCostPer1kTokens: 0.02,
2046
- maxOutputTokens: 128e3,
2125
+ inputCostPer1kTokens: 2e-3,
2126
+ outputCostPer1kTokens: 8e-3,
2127
+ maxOutputTokens: 32768,
2047
2128
  supportsStreaming: true,
2048
2129
  isLocal: false
2049
2130
  },
2050
- "gpt-5.4": {
2051
- id: "gpt-5.4",
2052
- name: "GPT-5.4",
2131
+ "gpt-4.1-mini": {
2132
+ id: "gpt-4.1-mini",
2133
+ name: "GPT-4.1 Mini",
2053
2134
  provider: "openai",
2054
- contextWindow: 4e5,
2135
+ contextWindow: 1047576,
2055
2136
  isVisionCapable: true,
2056
- inputCostPer1kTokens: 125e-5,
2137
+ inputCostPer1kTokens: 4e-4,
2138
+ outputCostPer1kTokens: 16e-4,
2139
+ maxOutputTokens: 32768,
2140
+ supportsStreaming: true,
2141
+ isLocal: false
2142
+ },
2143
+ "gpt-4.1-nano": {
2144
+ id: "gpt-4.1-nano",
2145
+ name: "GPT-4.1 Nano",
2146
+ provider: "openai",
2147
+ contextWindow: 1047576,
2148
+ isVisionCapable: true,
2149
+ inputCostPer1kTokens: 1e-4,
2150
+ outputCostPer1kTokens: 4e-4,
2151
+ maxOutputTokens: 32768,
2152
+ supportsStreaming: true,
2153
+ isLocal: false
2154
+ },
2155
+ "gpt-4o": {
2156
+ id: "gpt-4o",
2157
+ name: "GPT-4o",
2158
+ provider: "openai",
2159
+ contextWindow: 128e3,
2160
+ isVisionCapable: true,
2161
+ inputCostPer1kTokens: 25e-4,
2057
2162
  outputCostPer1kTokens: 0.01,
2058
- maxOutputTokens: 128e3,
2163
+ maxOutputTokens: 16e3,
2059
2164
  supportsStreaming: true,
2060
2165
  isLocal: false
2061
2166
  },
2062
- "gpt-5.4-mini": {
2063
- id: "gpt-5.4-mini",
2064
- name: "GPT-5.4 Mini",
2167
+ "gpt-4o-mini": {
2168
+ id: "gpt-4o-mini",
2169
+ name: "GPT-4o Mini",
2065
2170
  provider: "openai",
2066
- contextWindow: 4e5,
2171
+ contextWindow: 128e3,
2067
2172
  isVisionCapable: true,
2068
- inputCostPer1kTokens: 25e-5,
2069
- outputCostPer1kTokens: 2e-3,
2070
- maxOutputTokens: 128e3,
2173
+ inputCostPer1kTokens: 15e-5,
2174
+ outputCostPer1kTokens: 6e-4,
2175
+ maxOutputTokens: 16e3,
2071
2176
  supportsStreaming: true,
2072
2177
  isLocal: false
2073
2178
  },
2074
- // Google
2075
- "gemini-2.0-flash": {
2076
- id: "gemini-2.0-flash",
2077
- name: "Gemini 2.0 Flash",
2179
+ // Google Gemini — active GA + preview chat models. Gemini 2.0/1.5 are no
2180
+ // longer advertised because the Gemini API shut those generations down.
2181
+ "gemini-3.7-flash": {
2182
+ id: "gemini-3.7-flash",
2183
+ name: "Gemini 3.7 Flash",
2078
2184
  provider: "gemini",
2079
- contextWindow: 1e6,
2185
+ contextWindow: 1048576,
2080
2186
  isVisionCapable: true,
2081
- inputCostPer1kTokens: 1e-4,
2082
- outputCostPer1kTokens: 4e-4,
2083
- maxOutputTokens: 8e3,
2187
+ inputCostPer1kTokens: 75e-5,
2188
+ outputCostPer1kTokens: 375e-5,
2189
+ maxOutputTokens: 65536,
2084
2190
  supportsStreaming: true,
2085
2191
  isLocal: false
2086
2192
  },
2087
- "gemini-1.5-pro": {
2088
- id: "gemini-1.5-pro",
2089
- name: "Gemini 1.5 Pro",
2193
+ "gemini-3.6-flash": {
2194
+ id: "gemini-3.6-flash",
2195
+ name: "Gemini 3.6 Flash",
2090
2196
  provider: "gemini",
2091
- contextWindow: 1e6,
2197
+ contextWindow: 1048576,
2092
2198
  isVisionCapable: true,
2093
- inputCostPer1kTokens: 125e-5,
2094
- outputCostPer1kTokens: 5e-3,
2095
- maxOutputTokens: 8e3,
2199
+ inputCostPer1kTokens: 75e-5,
2200
+ outputCostPer1kTokens: 375e-5,
2201
+ maxOutputTokens: 65536,
2096
2202
  supportsStreaming: true,
2097
2203
  isLocal: false
2098
2204
  },
2099
- "gemini-2.0-flash-lite": {
2100
- id: "gemini-2.0-flash-lite",
2101
- name: "Gemini 2.0 Flash-Lite",
2205
+ "gemini-3.5-flash": {
2206
+ id: "gemini-3.5-flash",
2207
+ name: "Gemini 3.5 Flash",
2102
2208
  provider: "gemini",
2103
- contextWindow: 1e6,
2209
+ contextWindow: 1048576,
2104
2210
  isVisionCapable: true,
2105
- inputCostPer1kTokens: 5e-5,
2106
- outputCostPer1kTokens: 2e-4,
2107
- maxOutputTokens: 8e3,
2211
+ inputCostPer1kTokens: 15e-4,
2212
+ outputCostPer1kTokens: 9e-3,
2213
+ maxOutputTokens: 65536,
2214
+ supportsStreaming: true,
2215
+ isLocal: false
2216
+ },
2217
+ "gemini-3.5-flash-lite": {
2218
+ id: "gemini-3.5-flash-lite",
2219
+ name: "Gemini 3.5 Flash-Lite",
2220
+ provider: "gemini",
2221
+ contextWindow: 1048576,
2222
+ isVisionCapable: true,
2223
+ inputCostPer1kTokens: 3e-4,
2224
+ outputCostPer1kTokens: 25e-4,
2225
+ maxOutputTokens: 65536,
2226
+ supportsStreaming: true,
2227
+ isLocal: false
2228
+ },
2229
+ "gemini-3.1-flash-lite": {
2230
+ id: "gemini-3.1-flash-lite",
2231
+ name: "Gemini 3.1 Flash-Lite",
2232
+ provider: "gemini",
2233
+ contextWindow: 1048576,
2234
+ isVisionCapable: true,
2235
+ inputCostPer1kTokens: 25e-5,
2236
+ outputCostPer1kTokens: 15e-4,
2237
+ maxOutputTokens: 65536,
2238
+ supportsStreaming: true,
2239
+ isLocal: false
2240
+ },
2241
+ "gemini-3.1-pro-preview": {
2242
+ id: "gemini-3.1-pro-preview",
2243
+ name: "Gemini 3.1 Pro Preview",
2244
+ provider: "gemini",
2245
+ contextWindow: 1048576,
2246
+ isVisionCapable: true,
2247
+ inputCostPer1kTokens: 2e-3,
2248
+ outputCostPer1kTokens: 0.012,
2249
+ maxOutputTokens: 65536,
2250
+ supportsStreaming: true,
2251
+ isLocal: false
2252
+ },
2253
+ "gemini-3-flash-preview": {
2254
+ id: "gemini-3-flash-preview",
2255
+ name: "Gemini 3 Flash Preview",
2256
+ provider: "gemini",
2257
+ contextWindow: 1048576,
2258
+ isVisionCapable: true,
2259
+ inputCostPer1kTokens: 5e-4,
2260
+ outputCostPer1kTokens: 3e-3,
2261
+ maxOutputTokens: 65536,
2108
2262
  supportsStreaming: true,
2109
2263
  isLocal: false
2110
2264
  },
@@ -2112,7 +2266,7 @@ var init_constants = __esm({
2112
2266
  id: "gemini-2.5-pro",
2113
2267
  name: "Gemini 2.5 Pro",
2114
2268
  provider: "gemini",
2115
- contextWindow: 1e6,
2269
+ contextWindow: 1048576,
2116
2270
  isVisionCapable: true,
2117
2271
  inputCostPer1kTokens: 125e-5,
2118
2272
  outputCostPer1kTokens: 0.01,
@@ -2124,10 +2278,22 @@ var init_constants = __esm({
2124
2278
  id: "gemini-2.5-flash",
2125
2279
  name: "Gemini 2.5 Flash",
2126
2280
  provider: "gemini",
2127
- contextWindow: 1e6,
2281
+ contextWindow: 1048576,
2128
2282
  isVisionCapable: true,
2129
- inputCostPer1kTokens: 15e-5,
2130
- outputCostPer1kTokens: 6e-4,
2283
+ inputCostPer1kTokens: 3e-4,
2284
+ outputCostPer1kTokens: 25e-4,
2285
+ maxOutputTokens: 65536,
2286
+ supportsStreaming: true,
2287
+ isLocal: false
2288
+ },
2289
+ "gemini-2.5-flash-lite": {
2290
+ id: "gemini-2.5-flash-lite",
2291
+ name: "Gemini 2.5 Flash-Lite",
2292
+ provider: "gemini",
2293
+ contextWindow: 1048576,
2294
+ isVisionCapable: true,
2295
+ inputCostPer1kTokens: 1e-4,
2296
+ outputCostPer1kTokens: 4e-4,
2131
2297
  maxOutputTokens: 65536,
2132
2298
  supportsStreaming: true,
2133
2299
  isLocal: false
@@ -2197,43 +2363,57 @@ var init_constants = __esm({
2197
2363
  _m.pricingUnknown = false;
2198
2364
  }
2199
2365
  T1_MODEL_PRIORITY = [
2366
+ "gpt-5.6-sol",
2367
+ "claude-fable-5",
2368
+ "claude-opus-5",
2200
2369
  "gpt-5.5",
2201
2370
  "gpt-5.4",
2371
+ "gemini-3.1-pro-preview",
2372
+ "claude-opus-4-8",
2373
+ "claude-sonnet-5",
2374
+ "gemini-3.7-flash",
2375
+ "gpt-5.2",
2202
2376
  "gpt-5",
2203
- "claude-opus-4",
2204
- "claude-sonnet-4",
2205
2377
  "gemini-2.5-pro",
2206
2378
  "gpt-4.1",
2207
- "gpt-4o",
2208
- "gemini-1.5-pro"
2379
+ "gpt-4o"
2209
2380
  ];
2210
2381
  T2_MODEL_PRIORITY = [
2382
+ "gpt-5.6-terra",
2383
+ "claude-sonnet-5",
2384
+ "gemini-3.7-flash",
2385
+ "gemini-3.6-flash",
2211
2386
  "gpt-5.4-mini",
2212
2387
  "gpt-5-mini",
2213
- "claude-sonnet-4",
2388
+ "claude-sonnet-4-6",
2214
2389
  "claude-haiku-4-5",
2215
- "claude-haiku-3-5",
2390
+ "gemini-3.5-flash",
2216
2391
  "gemini-2.5-flash",
2217
2392
  "gpt-4.1-mini",
2218
2393
  "gpt-4o-mini",
2219
- "gemini-2.0-flash",
2220
2394
  "llama3:70b"
2221
2395
  ];
2222
2396
  T3_MODEL_PRIORITY = [
2397
+ "gpt-5.6-luna",
2398
+ "gpt-5.4-nano",
2399
+ "gemini-3.5-flash-lite",
2400
+ "gemini-3.1-flash-lite",
2401
+ "claude-haiku-4-5",
2223
2402
  "gpt-5-nano",
2403
+ "gemini-2.5-flash-lite",
2224
2404
  "llama3.2:3b",
2225
2405
  "mistral:7b",
2226
- "claude-haiku-4-5",
2227
- "claude-haiku-3-5",
2228
2406
  "gpt-4.1-nano",
2229
2407
  "gpt-4o-mini",
2230
- "gemini-2.5-flash",
2231
- "gemini-2.0-flash"
2408
+ "gemini-2.5-flash"
2232
2409
  ];
2233
2410
  VISION_MODEL_PRIORITY = [
2234
- "claude-sonnet-4",
2411
+ "gpt-5.6-sol",
2412
+ "claude-fable-5",
2413
+ "claude-sonnet-5",
2414
+ "gemini-3.7-flash",
2235
2415
  "gpt-4o",
2236
- "gemini-2.0-flash",
2416
+ "gemini-2.5-flash",
2237
2417
  "llava"
2238
2418
  ];
2239
2419
  COMPLEXITY_T2_COUNT = {
@@ -2290,8 +2470,8 @@ function normalizeEndpoint(url) {
2290
2470
  if (!raw) return "";
2291
2471
  try {
2292
2472
  const u = new URL(raw);
2293
- const path40 = stripTrailingSlashes(`${u.pathname}${u.search}`);
2294
- return `${u.protocol}//${u.host.toLowerCase()}${path40}`;
2473
+ const path41 = stripTrailingSlashes(`${u.pathname}${u.search}`);
2474
+ return `${u.protocol}//${u.host.toLowerCase()}${path41}`;
2295
2475
  } catch {
2296
2476
  return raw.toLowerCase();
2297
2477
  }
@@ -2423,7 +2603,7 @@ var CLIENT_VERSION_SEGMENT, REDIRECT_STATUSES, NULL_BODY_STATUSES, MAX_REDIRECTS
2423
2603
  var init_net = __esm({
2424
2604
  "src/utils/net.ts"() {
2425
2605
  try {
2426
- dns__default.default.setDefaultResultOrder("ipv4first");
2606
+ dnsCallback__default.default.setDefaultResultOrder("ipv4first");
2427
2607
  } catch {
2428
2608
  }
2429
2609
  CLIENT_VERSION_SEGMENT = {
@@ -3155,6 +3335,56 @@ var init_openai = __esm({
3155
3335
  }
3156
3336
  });
3157
3337
 
3338
+ // src/providers/cloud-model-catalog.json
3339
+ var cloud_model_catalog_default;
3340
+ var init_cloud_model_catalog = __esm({
3341
+ "src/providers/cloud-model-catalog.json"() {
3342
+ cloud_model_catalog_default = {
3343
+ schemaVersion: 1,
3344
+ verifiedAt: "2026-08-27",
3345
+ scope: "GA and Preview text/chat models that Cascade can route through its maintained cloud providers. Retired, shutdown, realtime/audio-only, embedding, image-only, video-only, and access-restricted models are excluded.",
3346
+ note: "Capabilities belong to the canonical model. Pricing deliberately does not live here: Cascade resolves price by model x provider x region in core/router/pricing-data.json. A model exposed by multiple providers may therefore have different prices without duplicating its capability profile.",
3347
+ models: [
3348
+ { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
3349
+ { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
3350
+ { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
3351
+ { id: "gpt-5.5", name: "GPT-5.5", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
3352
+ { id: "gpt-5.4", name: "GPT-5.4", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
3353
+ { id: "gpt-5.4-mini", name: "GPT-5.4 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
3354
+ { id: "gpt-5.4-nano", name: "GPT-5.4 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
3355
+ { id: "gpt-5", name: "GPT-5", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
3356
+ { id: "gpt-5-mini", name: "GPT-5 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
3357
+ { id: "gpt-5-nano", name: "GPT-5 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
3358
+ { id: "gpt-4.1", name: "GPT-4.1", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
3359
+ { id: "gpt-4.1-mini", name: "GPT-4.1 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
3360
+ { id: "gpt-4.1-nano", name: "GPT-4.1 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
3361
+ { id: "gpt-4o", name: "GPT-4o", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 128e3, maxOutputTokens: 16384, vision: true, toolUse: true },
3362
+ { id: "gpt-4o-mini", name: "GPT-4o Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 128e3, maxOutputTokens: 16384, vision: true, toolUse: true },
3363
+ { id: "claude-fable-5", name: "Claude Fable 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
3364
+ { id: "claude-opus-5", name: "Claude Opus 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
3365
+ { id: "claude-opus-4-8", name: "Claude Opus 4.8", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
3366
+ { id: "claude-opus-4-7", name: "Claude Opus 4.7", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
3367
+ { id: "claude-opus-4-6", name: "Claude Opus 4.6", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, vision: true, toolUse: true },
3368
+ { id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, vision: true, toolUse: true },
3369
+ { id: "claude-sonnet-5", name: "Claude Sonnet 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
3370
+ { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
3371
+ { id: "claude-sonnet-4-5-20250929", name: "Claude Sonnet 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, vision: true, toolUse: true },
3372
+ { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, maxOutputTokens: 64e3, vision: true, toolUse: true },
3373
+ { id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3374
+ { id: "gemini-3.6-flash", name: "Gemini 3.6 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3375
+ { id: "gemini-3.5-flash", name: "Gemini 3.5 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3376
+ { id: "gemini-3.5-flash-lite", name: "Gemini 3.5 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3377
+ { id: "gemini-3.1-flash-lite", name: "Gemini 3.1 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3378
+ { id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro", providers: ["gemini"], lifecycle: "preview", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3379
+ { id: "gemini-3-flash-preview", name: "Gemini 3 Flash", providers: ["gemini"], lifecycle: "preview", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3380
+ { id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3381
+ { id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
3382
+ { id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true }
3383
+ ]
3384
+ };
3385
+ }
3386
+ });
3387
+
3158
3388
  // src/providers/azure.ts
3159
3389
  var azure_exports = {};
3160
3390
  __export(azure_exports, {
@@ -3163,36 +3393,81 @@ __export(azure_exports, {
3163
3393
  azureModelForDeployment: () => azureModelForDeployment,
3164
3394
  inferAzureBaseModel: () => inferAzureBaseModel
3165
3395
  });
3396
+ function compactModelId(id) {
3397
+ return id.toLowerCase().replace(/[^a-z0-9.]/g, "");
3398
+ }
3399
+ function catalogEntry(id) {
3400
+ const n = id.toLowerCase();
3401
+ return CLOUD_MODELS.find((m) => m.id.toLowerCase() === n);
3402
+ }
3166
3403
  function inferAzureBaseModel(deploymentName) {
3167
3404
  const n = deploymentName.toLowerCase();
3168
- for (const [re, base] of AZURE_BASE_MODEL_RULES) if (re.test(n)) return base;
3405
+ const compact = compactModelId(n);
3406
+ const hasBareGpt56Alias = compact.includes("gpt5.6");
3407
+ for (const model of AZURE_CATALOG) {
3408
+ if (hasBareGpt56Alias && model.id === "gpt-5") continue;
3409
+ if (n.includes(model.id.toLowerCase()) || compact.includes(compactModelId(model.id))) {
3410
+ return model.id;
3411
+ }
3412
+ }
3413
+ if (hasBareGpt56Alias) return "gpt-5.6-sol";
3414
+ const gpt5At = compact.indexOf("gpt5");
3415
+ if (gpt5At !== -1) {
3416
+ const suffix = compact.slice(gpt5At + "gpt5".length);
3417
+ if (suffix.includes("nano")) return "gpt-5-nano";
3418
+ if (suffix.includes("mini")) return "gpt-5-mini";
3419
+ return "gpt-5";
3420
+ }
3421
+ if (compact.includes("gpt4.1nano")) return "gpt-4.1-nano";
3422
+ if (compact.includes("gpt4.1mini")) return "gpt-4.1-mini";
3423
+ if (compact.includes("gpt4.1")) return "gpt-4.1";
3424
+ if (compact.includes("gpt4omini")) return "gpt-4o-mini";
3425
+ if (compact.includes("gpt4o")) return "gpt-4o";
3169
3426
  return null;
3170
3427
  }
3428
+ function catalogModelInfo(baseModelId) {
3429
+ const entry = catalogEntry(baseModelId);
3430
+ if (!entry?.contextWindow) return void 0;
3431
+ return {
3432
+ id: entry.id,
3433
+ name: entry.name,
3434
+ provider: "openai",
3435
+ contextWindow: entry.contextWindow,
3436
+ isVisionCapable: entry.vision ?? false,
3437
+ inputCostPer1kTokens: 0,
3438
+ outputCostPer1kTokens: 0,
3439
+ maxOutputTokens: entry.maxOutputTokens ?? 16e3,
3440
+ supportsStreaming: true,
3441
+ isLocal: false,
3442
+ supportsToolUse: entry.toolUse ?? true,
3443
+ pricingUnknown: true
3444
+ };
3445
+ }
3171
3446
  function azureModelForDeployment(cfg) {
3172
3447
  if (cfg.type !== "azure" || !cfg.deploymentName?.trim()) return null;
3173
3448
  const id = cfg.deploymentName.trim();
3174
3449
  const name = cfg.label?.trim() || id;
3175
3450
  const baseModelId = cfg.model?.trim() || inferAzureBaseModel(id) || void 0;
3176
- const base = baseModelId ? MODELS[baseModelId] : void 0;
3177
- if (base) {
3451
+ const base = baseModelId ? MODELS[baseModelId] ?? catalogModelInfo(baseModelId) : void 0;
3452
+ if (base && baseModelId) {
3178
3453
  const azurePrice = resolvePricing(
3179
3454
  { id, provider: "azure", isLocal: false, baseModelId },
3180
3455
  { region: cfg.region }
3181
3456
  );
3457
+ const exactAzurePrice = !azurePrice.unknown && !azurePrice.estimatedFromProvider;
3458
+ const nativeToolsOnThisTransport = /^gpt-5\.6-(?:sol|terra|luna)$/i.test(baseModelId) ? false : base.supportsToolUse ?? true;
3182
3459
  return {
3183
3460
  ...base,
3184
3461
  id,
3185
- // callable deployment name
3186
3462
  name,
3187
3463
  provider: "azure",
3188
3464
  baseModelId,
3189
- // real identity for benchmark + live pricing
3190
- supportsToolUse: base.supportsToolUse ?? true,
3191
- ...azurePrice.unknown ? {} : {
3465
+ supportsToolUse: nativeToolsOnThisTransport,
3466
+ ...!azurePrice.unknown ? {
3192
3467
  inputCostPer1kTokens: azurePrice.input,
3193
3468
  outputCostPer1kTokens: azurePrice.output,
3194
- pricingUnknown: false
3195
- }
3469
+ pricingUnknown: !exactAzurePrice
3470
+ } : { pricingUnknown: true }
3196
3471
  };
3197
3472
  }
3198
3473
  return {
@@ -3207,39 +3482,34 @@ function azureModelForDeployment(cfg) {
3207
3482
  maxOutputTokens: 16e3,
3208
3483
  supportsStreaming: true,
3209
3484
  isLocal: false,
3210
- supportsToolUse: true
3485
+ supportsToolUse: true,
3486
+ pricingUnknown: true
3211
3487
  };
3212
3488
  }
3213
- var DEFAULT_AZURE_API_VERSION, AZURE_BASE_MODEL_RULES, AZURE_BASE_MODELS, AzureOpenAIProvider;
3489
+ function stripTrailingSlashes3(value) {
3490
+ let end = value.length;
3491
+ while (end > 0 && value.charCodeAt(end - 1) === 47) end--;
3492
+ return value.slice(0, end);
3493
+ }
3494
+ var DEFAULT_AZURE_API_VERSION, CLOUD_MODELS, AZURE_CATALOG, AZURE_BASE_MODELS, AzureOpenAIProvider;
3214
3495
  var init_azure = __esm({
3215
3496
  "src/providers/azure.ts"() {
3216
3497
  init_constants();
3217
3498
  init_openai();
3218
3499
  init_pricing();
3500
+ init_cloud_model_catalog();
3219
3501
  DEFAULT_AZURE_API_VERSION = "2024-12-01-preview";
3220
- AZURE_BASE_MODEL_RULES = [
3221
- [/gpt-?5\.5/, "gpt-5.5"],
3222
- [/gpt-?5\.4.*mini/, "gpt-5.4-mini"],
3223
- [/gpt-?5\.4/, "gpt-5.4"],
3224
- [/gpt-?5.*nano/, "gpt-5-nano"],
3225
- [/gpt-?5.*mini/, "gpt-5-mini"],
3226
- [/gpt-?5/, "gpt-5"],
3227
- [/gpt-?4\.1-nano/, "gpt-4.1-nano"],
3228
- [/gpt-?4\.1-mini/, "gpt-4.1-mini"],
3229
- [/gpt-?4\.1/, "gpt-4.1"],
3230
- [/gpt-?4o-mini/, "gpt-4o-mini"],
3231
- [/gpt-?4o/, "gpt-4o"]
3232
- ];
3233
- AZURE_BASE_MODELS = AZURE_BASE_MODEL_RULES.map(([, id]) => id);
3502
+ CLOUD_MODELS = cloud_model_catalog_default.models;
3503
+ AZURE_CATALOG = CLOUD_MODELS.filter((m) => m.providers.includes("azure")).slice().sort((a, b) => b.id.length - a.id.length || a.id.localeCompare(b.id));
3504
+ AZURE_BASE_MODELS = AZURE_CATALOG.map((m) => m.id);
3234
3505
  AzureOpenAIProvider = class extends OpenAIProvider {
3235
3506
  constructor(config, model) {
3236
3507
  const rawUrl = config.baseUrl ?? AZURE_BASE_URL_TEMPLATE.replace("{resource}", "YOUR_RESOURCE");
3237
- const endpoint = rawUrl.replace(/\/+$/, "");
3508
+ const endpoint = stripTrailingSlashes3(rawUrl);
3238
3509
  super(
3239
3510
  {
3240
3511
  ...config,
3241
3512
  baseUrl: endpoint
3242
- // Kept for superclass compatibility if it reads it
3243
3513
  },
3244
3514
  model
3245
3515
  );
@@ -4223,16 +4493,16 @@ var require_isarray = __commonJS({
4223
4493
  }
4224
4494
  });
4225
4495
 
4226
- // node_modules/jszip/node_modules/readable-stream/lib/internal/streams/stream.js
4496
+ // node_modules/readable-stream/lib/internal/streams/stream.js
4227
4497
  var require_stream = __commonJS({
4228
- "node_modules/jszip/node_modules/readable-stream/lib/internal/streams/stream.js"(exports, module) {
4498
+ "node_modules/readable-stream/lib/internal/streams/stream.js"(exports, module) {
4229
4499
  module.exports = __require("stream");
4230
4500
  }
4231
4501
  });
4232
4502
 
4233
- // node_modules/jszip/node_modules/safe-buffer/index.js
4503
+ // node_modules/readable-stream/node_modules/safe-buffer/index.js
4234
4504
  var require_safe_buffer = __commonJS({
4235
- "node_modules/jszip/node_modules/safe-buffer/index.js"(exports, module) {
4505
+ "node_modules/readable-stream/node_modules/safe-buffer/index.js"(exports, module) {
4236
4506
  var buffer = __require("buffer");
4237
4507
  var Buffer2 = buffer.Buffer;
4238
4508
  function copyProps(src, dst) {
@@ -4350,7 +4620,7 @@ var require_util = __commonJS({
4350
4620
  typeof arg === "undefined";
4351
4621
  }
4352
4622
  exports.isPrimitive = isPrimitive;
4353
- exports.isBuffer = Buffer.isBuffer;
4623
+ exports.isBuffer = __require("buffer").Buffer.isBuffer;
4354
4624
  function objectToString(o) {
4355
4625
  return Object.prototype.toString.call(o);
4356
4626
  }
@@ -4403,9 +4673,9 @@ var require_inherits = __commonJS({
4403
4673
  }
4404
4674
  });
4405
4675
 
4406
- // node_modules/jszip/node_modules/readable-stream/lib/internal/streams/BufferList.js
4676
+ // node_modules/readable-stream/lib/internal/streams/BufferList.js
4407
4677
  var require_BufferList = __commonJS({
4408
- "node_modules/jszip/node_modules/readable-stream/lib/internal/streams/BufferList.js"(exports, module) {
4678
+ "node_modules/readable-stream/lib/internal/streams/BufferList.js"(exports, module) {
4409
4679
  function _classCallCheck(instance, Constructor) {
4410
4680
  if (!(instance instanceof Constructor)) {
4411
4681
  throw new TypeError("Cannot call a class as a function");
@@ -4480,9 +4750,9 @@ var require_BufferList = __commonJS({
4480
4750
  }
4481
4751
  });
4482
4752
 
4483
- // node_modules/jszip/node_modules/readable-stream/lib/internal/streams/destroy.js
4753
+ // node_modules/readable-stream/lib/internal/streams/destroy.js
4484
4754
  var require_destroy = __commonJS({
4485
- "node_modules/jszip/node_modules/readable-stream/lib/internal/streams/destroy.js"(exports, module) {
4755
+ "node_modules/readable-stream/lib/internal/streams/destroy.js"(exports, module) {
4486
4756
  var pna = require_process_nextick_args();
4487
4757
  function destroy(err, cb) {
4488
4758
  var _this = this;
@@ -4555,9 +4825,9 @@ var require_node = __commonJS({
4555
4825
  }
4556
4826
  });
4557
4827
 
4558
- // node_modules/jszip/node_modules/readable-stream/lib/_stream_writable.js
4828
+ // node_modules/readable-stream/lib/_stream_writable.js
4559
4829
  var require_stream_writable = __commonJS({
4560
- "node_modules/jszip/node_modules/readable-stream/lib/_stream_writable.js"(exports, module) {
4830
+ "node_modules/readable-stream/lib/_stream_writable.js"(exports, module) {
4561
4831
  var pna = require_process_nextick_args();
4562
4832
  module.exports = Writable;
4563
4833
  function CorkedRequest(state) {
@@ -4994,9 +5264,9 @@ var require_stream_writable = __commonJS({
4994
5264
  }
4995
5265
  });
4996
5266
 
4997
- // node_modules/jszip/node_modules/readable-stream/lib/_stream_duplex.js
5267
+ // node_modules/readable-stream/lib/_stream_duplex.js
4998
5268
  var require_stream_duplex = __commonJS({
4999
- "node_modules/jszip/node_modules/readable-stream/lib/_stream_duplex.js"(exports, module) {
5269
+ "node_modules/readable-stream/lib/_stream_duplex.js"(exports, module) {
5000
5270
  var pna = require_process_nextick_args();
5001
5271
  var objectKeys = Object.keys || function(obj) {
5002
5272
  var keys2 = [];
@@ -5070,10 +5340,67 @@ var require_stream_duplex = __commonJS({
5070
5340
  }
5071
5341
  });
5072
5342
 
5073
- // node_modules/jszip/node_modules/string_decoder/lib/string_decoder.js
5343
+ // node_modules/string_decoder/node_modules/safe-buffer/index.js
5344
+ var require_safe_buffer2 = __commonJS({
5345
+ "node_modules/string_decoder/node_modules/safe-buffer/index.js"(exports, module) {
5346
+ var buffer = __require("buffer");
5347
+ var Buffer2 = buffer.Buffer;
5348
+ function copyProps(src, dst) {
5349
+ for (var key in src) {
5350
+ dst[key] = src[key];
5351
+ }
5352
+ }
5353
+ if (Buffer2.from && Buffer2.alloc && Buffer2.allocUnsafe && Buffer2.allocUnsafeSlow) {
5354
+ module.exports = buffer;
5355
+ } else {
5356
+ copyProps(buffer, exports);
5357
+ exports.Buffer = SafeBuffer;
5358
+ }
5359
+ function SafeBuffer(arg, encodingOrOffset, length) {
5360
+ return Buffer2(arg, encodingOrOffset, length);
5361
+ }
5362
+ copyProps(Buffer2, SafeBuffer);
5363
+ SafeBuffer.from = function(arg, encodingOrOffset, length) {
5364
+ if (typeof arg === "number") {
5365
+ throw new TypeError("Argument must not be a number");
5366
+ }
5367
+ return Buffer2(arg, encodingOrOffset, length);
5368
+ };
5369
+ SafeBuffer.alloc = function(size, fill, encoding) {
5370
+ if (typeof size !== "number") {
5371
+ throw new TypeError("Argument must be a number");
5372
+ }
5373
+ var buf = Buffer2(size);
5374
+ if (fill !== void 0) {
5375
+ if (typeof encoding === "string") {
5376
+ buf.fill(fill, encoding);
5377
+ } else {
5378
+ buf.fill(fill);
5379
+ }
5380
+ } else {
5381
+ buf.fill(0);
5382
+ }
5383
+ return buf;
5384
+ };
5385
+ SafeBuffer.allocUnsafe = function(size) {
5386
+ if (typeof size !== "number") {
5387
+ throw new TypeError("Argument must be a number");
5388
+ }
5389
+ return Buffer2(size);
5390
+ };
5391
+ SafeBuffer.allocUnsafeSlow = function(size) {
5392
+ if (typeof size !== "number") {
5393
+ throw new TypeError("Argument must be a number");
5394
+ }
5395
+ return buffer.SlowBuffer(size);
5396
+ };
5397
+ }
5398
+ });
5399
+
5400
+ // node_modules/string_decoder/lib/string_decoder.js
5074
5401
  var require_string_decoder = __commonJS({
5075
- "node_modules/jszip/node_modules/string_decoder/lib/string_decoder.js"(exports) {
5076
- var Buffer2 = require_safe_buffer().Buffer;
5402
+ "node_modules/string_decoder/lib/string_decoder.js"(exports) {
5403
+ var Buffer2 = require_safe_buffer2().Buffer;
5077
5404
  var isEncoding = Buffer2.isEncoding || function(encoding) {
5078
5405
  encoding = "" + encoding;
5079
5406
  switch (encoding && encoding.toLowerCase()) {
@@ -5307,9 +5634,9 @@ var require_string_decoder = __commonJS({
5307
5634
  }
5308
5635
  });
5309
5636
 
5310
- // node_modules/jszip/node_modules/readable-stream/lib/_stream_readable.js
5637
+ // node_modules/readable-stream/lib/_stream_readable.js
5311
5638
  var require_stream_readable = __commonJS({
5312
- "node_modules/jszip/node_modules/readable-stream/lib/_stream_readable.js"(exports, module) {
5639
+ "node_modules/readable-stream/lib/_stream_readable.js"(exports, module) {
5313
5640
  var pna = require_process_nextick_args();
5314
5641
  module.exports = Readable2;
5315
5642
  var isArray = require_isarray();
@@ -5992,9 +6319,9 @@ var require_stream_readable = __commonJS({
5992
6319
  }
5993
6320
  });
5994
6321
 
5995
- // node_modules/jszip/node_modules/readable-stream/lib/_stream_transform.js
6322
+ // node_modules/readable-stream/lib/_stream_transform.js
5996
6323
  var require_stream_transform = __commonJS({
5997
- "node_modules/jszip/node_modules/readable-stream/lib/_stream_transform.js"(exports, module) {
6324
+ "node_modules/readable-stream/lib/_stream_transform.js"(exports, module) {
5998
6325
  module.exports = Transform;
5999
6326
  var Duplex = require_stream_duplex();
6000
6327
  var util = Object.create(require_util());
@@ -6091,9 +6418,9 @@ var require_stream_transform = __commonJS({
6091
6418
  }
6092
6419
  });
6093
6420
 
6094
- // node_modules/jszip/node_modules/readable-stream/lib/_stream_passthrough.js
6421
+ // node_modules/readable-stream/lib/_stream_passthrough.js
6095
6422
  var require_stream_passthrough = __commonJS({
6096
- "node_modules/jszip/node_modules/readable-stream/lib/_stream_passthrough.js"(exports, module) {
6423
+ "node_modules/readable-stream/lib/_stream_passthrough.js"(exports, module) {
6097
6424
  module.exports = PassThrough;
6098
6425
  var Transform = require_stream_transform();
6099
6426
  var util = Object.create(require_util());
@@ -6109,9 +6436,9 @@ var require_stream_passthrough = __commonJS({
6109
6436
  }
6110
6437
  });
6111
6438
 
6112
- // node_modules/jszip/node_modules/readable-stream/readable.js
6439
+ // node_modules/readable-stream/readable.js
6113
6440
  var require_readable = __commonJS({
6114
- "node_modules/jszip/node_modules/readable-stream/readable.js"(exports, module) {
6441
+ "node_modules/readable-stream/readable.js"(exports, module) {
6115
6442
  var Stream = __require("stream");
6116
6443
  if (process.env.READABLE_STREAM === "disable" && Stream) {
6117
6444
  module.exports = Stream;
@@ -7020,8 +7347,8 @@ var require_utils = __commonJS({
7020
7347
  var result = transform[inputType][outputType](input);
7021
7348
  return result;
7022
7349
  };
7023
- exports.resolve = function(path40) {
7024
- var parts = path40.split("/");
7350
+ exports.resolve = function(path41) {
7351
+ var parts = path41.split("/");
7025
7352
  var result = [];
7026
7353
  for (var index = 0; index < parts.length; index++) {
7027
7354
  var part = parts[index];
@@ -12836,18 +13163,18 @@ var require_object = __commonJS({
12836
13163
  var object = new ZipObject(name, zipObjectContent, o);
12837
13164
  this.files[name] = object;
12838
13165
  };
12839
- var parentFolder = function(path40) {
12840
- if (path40.slice(-1) === "/") {
12841
- path40 = path40.substring(0, path40.length - 1);
13166
+ var parentFolder = function(path41) {
13167
+ if (path41.slice(-1) === "/") {
13168
+ path41 = path41.substring(0, path41.length - 1);
12842
13169
  }
12843
- var lastSlash = path40.lastIndexOf("/");
12844
- return lastSlash > 0 ? path40.substring(0, lastSlash) : "";
13170
+ var lastSlash = path41.lastIndexOf("/");
13171
+ return lastSlash > 0 ? path41.substring(0, lastSlash) : "";
12845
13172
  };
12846
- var forceTrailingSlash = function(path40) {
12847
- if (path40.slice(-1) !== "/") {
12848
- path40 += "/";
13173
+ var forceTrailingSlash = function(path41) {
13174
+ if (path41.slice(-1) !== "/") {
13175
+ path41 += "/";
12849
13176
  }
12850
- return path40;
13177
+ return path41;
12851
13178
  };
12852
13179
  var folderAdd = function(name, createFolders) {
12853
13180
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -13846,12 +14173,12 @@ var init_audit_logger = __esm({
13846
14173
  constructor(workspacePath, debugMode = false) {
13847
14174
  this.workspacePath = workspacePath;
13848
14175
  this.debugMode = debugMode;
13849
- const cascadeDir = path35__default.default.join(workspacePath, ".cascade");
13850
- if (!fs32__default.default.existsSync(cascadeDir)) {
13851
- fs32__default.default.mkdirSync(cascadeDir, { recursive: true });
14176
+ const cascadeDir = path36__default.default.join(workspacePath, ".cascade");
14177
+ if (!fs33__default.default.existsSync(cascadeDir)) {
14178
+ fs33__default.default.mkdirSync(cascadeDir, { recursive: true });
13852
14179
  }
13853
- this.keyPath = path35__default.default.join(cascadeDir, "audit_log.key");
13854
- this.dbPath = path35__default.default.join(cascadeDir, "audit_log.db");
14180
+ this.keyPath = path36__default.default.join(cascadeDir, "audit_log.key");
14181
+ this.dbPath = path36__default.default.join(cascadeDir, "audit_log.db");
13855
14182
  this.initEncryptionKey();
13856
14183
  this.db = new Database2__default.default(this.dbPath);
13857
14184
  this.db.pragma("journal_mode = WAL");
@@ -13881,11 +14208,11 @@ var init_audit_logger = __esm({
13881
14208
  return crypto__default.default.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
13882
14209
  }
13883
14210
  initEncryptionKey() {
13884
- if (fs32__default.default.existsSync(this.keyPath)) {
13885
- this.encryptionKey = fs32__default.default.readFileSync(this.keyPath);
14211
+ if (fs33__default.default.existsSync(this.keyPath)) {
14212
+ this.encryptionKey = fs33__default.default.readFileSync(this.keyPath);
13886
14213
  } else {
13887
14214
  this.encryptionKey = crypto__default.default.randomBytes(32);
13888
- fs32__default.default.writeFileSync(this.keyPath, this.encryptionKey);
14215
+ fs33__default.default.writeFileSync(this.keyPath, this.encryptionKey);
13889
14216
  }
13890
14217
  }
13891
14218
  encrypt(text) {
@@ -13963,9 +14290,9 @@ var init_audit_logger = __esm({
13963
14290
  dumpDebugIfNeeded() {
13964
14291
  if (!this.debugMode) return;
13965
14292
  try {
13966
- const dumpPath = path35__default.default.join(os10__default.default.tmpdir(), "cascade_audit_logs_debug.json");
14293
+ const dumpPath = path36__default.default.join(os10__default.default.tmpdir(), "cascade_audit_logs_debug.json");
13967
14294
  const entries = this.getAllLogs();
13968
- fs32__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
14295
+ fs33__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
13969
14296
  } catch (err) {
13970
14297
  console.error("Failed to dump debug audit logs", err);
13971
14298
  }
@@ -14020,7 +14347,7 @@ var Keystore = class {
14020
14347
  const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
14021
14348
  this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
14022
14349
  this.backend = "keytar";
14023
- if (password && fs32__default.default.existsSync(this.storePath)) {
14350
+ if (password && fs33__default.default.existsSync(this.storePath)) {
14024
14351
  try {
14025
14352
  const fileEntries = this.decryptFile(password);
14026
14353
  for (const [k, v] of Object.entries(fileEntries)) {
@@ -14039,7 +14366,7 @@ var Keystore = class {
14039
14366
  "Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
14040
14367
  );
14041
14368
  }
14042
- if (!fs32__default.default.existsSync(this.storePath)) {
14369
+ if (!fs33__default.default.existsSync(this.storePath)) {
14043
14370
  const salt = crypto__default.default.randomBytes(SALT_LEN);
14044
14371
  this.masterKey = this.deriveKey(password, salt);
14045
14372
  this.writeWithSalt({}, salt);
@@ -14053,7 +14380,7 @@ var Keystore = class {
14053
14380
  }
14054
14381
  /** Synchronous legacy unlock kept for AES-only environments. */
14055
14382
  unlockSync(password) {
14056
- if (!fs32__default.default.existsSync(this.storePath)) {
14383
+ if (!fs33__default.default.existsSync(this.storePath)) {
14057
14384
  const salt = crypto__default.default.randomBytes(SALT_LEN);
14058
14385
  this.masterKey = this.deriveKey(password, salt);
14059
14386
  this.writeWithSalt({}, salt);
@@ -14111,7 +14438,7 @@ var Keystore = class {
14111
14438
  }
14112
14439
  }
14113
14440
  decryptFile(password, knownSalt) {
14114
- if (!fs32__default.default.existsSync(this.storePath)) return {};
14441
+ if (!fs33__default.default.existsSync(this.storePath)) return {};
14115
14442
  try {
14116
14443
  const { salt, ciphertext, iv, tag } = this.readRaw();
14117
14444
  const useSalt = knownSalt ?? salt;
@@ -14133,8 +14460,8 @@ var Keystore = class {
14133
14460
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
14134
14461
  const tag = cipher.getAuthTag();
14135
14462
  const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
14136
- fs32__default.default.mkdirSync(path35__default.default.dirname(this.storePath), { recursive: true });
14137
- fs32__default.default.writeFileSync(this.storePath, out, { mode: 384 });
14463
+ fs33__default.default.mkdirSync(path36__default.default.dirname(this.storePath), { recursive: true });
14464
+ fs33__default.default.writeFileSync(this.storePath, out, { mode: 384 });
14138
14465
  }
14139
14466
  writeWithSalt(data, salt) {
14140
14467
  if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
@@ -14144,11 +14471,11 @@ var Keystore = class {
14144
14471
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
14145
14472
  const tag = cipher.getAuthTag();
14146
14473
  const out = Buffer.concat([salt, iv, tag, ciphertext]);
14147
- fs32__default.default.mkdirSync(path35__default.default.dirname(this.storePath), { recursive: true });
14148
- fs32__default.default.writeFileSync(this.storePath, out, { mode: 384 });
14474
+ fs33__default.default.mkdirSync(path36__default.default.dirname(this.storePath), { recursive: true });
14475
+ fs33__default.default.writeFileSync(this.storePath, out, { mode: 384 });
14149
14476
  }
14150
14477
  readRaw() {
14151
- const buf = fs32__default.default.readFileSync(this.storePath);
14478
+ const buf = fs33__default.default.readFileSync(this.storePath);
14152
14479
  let offset = 0;
14153
14480
  const salt = buf.subarray(offset, offset + SALT_LEN);
14154
14481
  offset += SALT_LEN;
@@ -14181,7 +14508,7 @@ var CascadeIgnore = class {
14181
14508
  ]);
14182
14509
  }
14183
14510
  async load(workspacePath) {
14184
- const filePath = path35__default.default.join(workspacePath, ".cascadeignore");
14511
+ const filePath = path36__default.default.join(workspacePath, ".cascadeignore");
14185
14512
  try {
14186
14513
  const content = await fs11__default.default.readFile(filePath, "utf-8");
14187
14514
  const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
@@ -14192,7 +14519,7 @@ var CascadeIgnore = class {
14192
14519
  }
14193
14520
  isIgnored(filePath, workspacePath) {
14194
14521
  try {
14195
- const relative = workspacePath ? path35__default.default.relative(workspacePath, filePath) : filePath;
14522
+ const relative = workspacePath ? path36__default.default.relative(workspacePath, filePath) : filePath;
14196
14523
  return this.ig.ignores(relative);
14197
14524
  } catch {
14198
14525
  return false;
@@ -14203,7 +14530,7 @@ var CascadeIgnore = class {
14203
14530
  }
14204
14531
  };
14205
14532
  async function createDefaultIgnoreFile(workspacePath) {
14206
- const filePath = path35__default.default.join(workspacePath, ".cascadeignore");
14533
+ const filePath = path36__default.default.join(workspacePath, ".cascadeignore");
14207
14534
  const content = `# .cascadeignore \u2014 Files Cascade agents cannot read or modify
14208
14535
  # Syntax identical to .gitignore
14209
14536
 
@@ -14233,7 +14560,7 @@ Thumbs.db
14233
14560
  await fs11__default.default.writeFile(filePath, content, "utf-8");
14234
14561
  }
14235
14562
  async function loadCascadeMd(workspacePath) {
14236
- const filePath = path35__default.default.join(workspacePath, "CASCADE.md");
14563
+ const filePath = path36__default.default.join(workspacePath, "CASCADE.md");
14237
14564
  try {
14238
14565
  const raw = await fs11__default.default.readFile(filePath, "utf-8");
14239
14566
  return parseCascadeMd(raw);
@@ -14262,7 +14589,7 @@ ${raw.trim()}`;
14262
14589
  return { raw, sections, systemPrompt };
14263
14590
  }
14264
14591
  async function createDefaultCascadeMd(workspacePath) {
14265
- const filePath = path35__default.default.join(workspacePath, "CASCADE.md");
14592
+ const filePath = path36__default.default.join(workspacePath, "CASCADE.md");
14266
14593
  const content = `# Cascade Project Instructions
14267
14594
 
14268
14595
  This file contains project-specific instructions for the Cascade AI agent.
@@ -14297,7 +14624,7 @@ List any areas the agent should not touch.
14297
14624
  var MemoryStore = class _MemoryStore {
14298
14625
  db;
14299
14626
  constructor(dbPath) {
14300
- fs32__default.default.mkdirSync(path35__default.default.dirname(dbPath), { recursive: true });
14627
+ fs33__default.default.mkdirSync(path36__default.default.dirname(dbPath), { recursive: true });
14301
14628
  try {
14302
14629
  this.db = new Database2__default.default(dbPath, { timeout: 5e3 });
14303
14630
  this.db.pragma("journal_mode = WAL");
@@ -15195,7 +15522,18 @@ var WebSearchConfigSchema = zod.z.object({
15195
15522
  /** Tavily API key — get one at https://tavily.com */
15196
15523
  tavilyApiKey: zod.z.string().optional(),
15197
15524
  /** Max results per search (default 5) */
15198
- maxResults: zod.z.number().default(5)
15525
+ maxResults: zod.z.number().default(5),
15526
+ /**
15527
+ * Send `searxngUrl` through the SSRF guard. Off for a local run, where the
15528
+ * URL is the machine owner's own config and a self-hosted SearXNG is
15529
+ * routinely on a private address; on for a host that accepts it from a
15530
+ * request. See `src/tools/web-search.ts`.
15531
+ *
15532
+ * It has to be named HERE, not just on the TypeScript interfaces: the config
15533
+ * passes through `CascadeConfigSchema.parse()` on its way into a run, and
15534
+ * zod drops every key this object does not declare.
15535
+ */
15536
+ guardSearxngUrl: zod.z.boolean().optional()
15199
15537
  });
15200
15538
  var ToolsConfigSchema = zod.z.object({
15201
15539
  shellAllowlist: zod.z.array(zod.z.string()).default([]),
@@ -15604,7 +15942,7 @@ function credentialEndpointsConflict(type, rowEndpoint, credentialEndpoint) {
15604
15942
 
15605
15943
  // src/config/global-credentials.ts
15606
15944
  function credentialsPath(globalDir2) {
15607
- return path35__default.default.join(globalDir2, GLOBAL_CREDENTIALS_FILE);
15945
+ return path36__default.default.join(globalDir2, GLOBAL_CREDENTIALS_FILE);
15608
15946
  }
15609
15947
  function providerKey(p) {
15610
15948
  return p.type;
@@ -15634,7 +15972,7 @@ function isPersistable(p) {
15634
15972
  }
15635
15973
  function loadGlobalCredentials(globalDir2) {
15636
15974
  try {
15637
- const raw = fs32__default.default.readFileSync(credentialsPath(globalDir2), "utf-8");
15975
+ const raw = fs33__default.default.readFileSync(credentialsPath(globalDir2), "utf-8");
15638
15976
  const parsed = JSON.parse(raw);
15639
15977
  if (!Array.isArray(parsed.providers)) return [];
15640
15978
  return parsed.providers.filter(
@@ -15646,13 +15984,13 @@ function loadGlobalCredentials(globalDir2) {
15646
15984
  }
15647
15985
  function saveGlobalCredentials(globalDir2, providers) {
15648
15986
  const filePath = credentialsPath(globalDir2);
15649
- fs32__default.default.mkdirSync(globalDir2, { recursive: true, mode: 448 });
15987
+ fs33__default.default.mkdirSync(globalDir2, { recursive: true, mode: 448 });
15650
15988
  const body = { version: 1, providers: providers.filter(isPersistable) };
15651
15989
  const tmp = `${filePath}.tmp`;
15652
- fs32__default.default.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
15653
- fs32__default.default.renameSync(tmp, filePath);
15990
+ fs33__default.default.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
15991
+ fs33__default.default.renameSync(tmp, filePath);
15654
15992
  try {
15655
- fs32__default.default.chmodSync(filePath, 384);
15993
+ fs33__default.default.chmodSync(filePath, 384);
15656
15994
  } catch {
15657
15995
  }
15658
15996
  }
@@ -16057,7 +16395,7 @@ var ConfigManager = class {
16057
16395
  /** `globalDirOverride` exists for tests — never point it at the real home dir there. */
16058
16396
  constructor(workspacePath = process.cwd(), globalDirOverride) {
16059
16397
  this.workspacePath = workspacePath;
16060
- this.globalDir = globalDirOverride ?? path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR);
16398
+ this.globalDir = globalDirOverride ?? path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR);
16061
16399
  }
16062
16400
  async load() {
16063
16401
  this.config = await this.loadConfig();
@@ -16071,8 +16409,8 @@ var ConfigManager = class {
16071
16409
  this.ignore = new CascadeIgnore();
16072
16410
  await this.ignore.load(this.workspacePath);
16073
16411
  this.cascadeMd = await loadCascadeMd(this.workspacePath);
16074
- this.keystore = new Keystore(path35__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
16075
- this.store = new MemoryStore(path35__default.default.join(this.workspacePath, CASCADE_DB_FILE));
16412
+ this.keystore = new Keystore(path36__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
16413
+ this.store = new MemoryStore(path36__default.default.join(this.workspacePath, CASCADE_DB_FILE));
16076
16414
  const globalRevoked = stripRevokedCredentials(loadGlobalCredentials(this.globalDir));
16077
16415
  const globalCreds = filterRetiredCredentials(globalRevoked.kept);
16078
16416
  if (globalRevoked.removed > 0) this.revokedCredentials += globalRevoked.removed;
@@ -16145,8 +16483,8 @@ var ConfigManager = class {
16145
16483
  * never reached the disk (see `commitSettings`).
16146
16484
  */
16147
16485
  async save(config = this.config) {
16148
- const configPath = path35__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
16149
- await fs11__default.default.mkdir(path35__default.default.dirname(configPath), { recursive: true });
16486
+ const configPath = path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
16487
+ await fs11__default.default.mkdir(path36__default.default.dirname(configPath), { recursive: true });
16150
16488
  await fs11__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
16151
16489
  try {
16152
16490
  saveGlobalCredentials(this.globalDir, config.providers);
@@ -16196,7 +16534,7 @@ var ConfigManager = class {
16196
16534
  this.revokedCredentials = 0;
16197
16535
  this.revokedPins = [];
16198
16536
  this.revokedNotice = void 0;
16199
- const configPath = path35__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
16537
+ const configPath = path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
16200
16538
  try {
16201
16539
  const raw = await fs11__default.default.readFile(configPath, "utf-8");
16202
16540
  const parsed = JSON.parse(raw);
@@ -16290,7 +16628,7 @@ var ConfigManager = class {
16290
16628
  return onResource;
16291
16629
  }
16292
16630
  async persistClearedPins(tiers) {
16293
- const configPath = path35__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
16631
+ const configPath = path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
16294
16632
  try {
16295
16633
  const raw = JSON.parse(await fs11__default.default.readFile(configPath, "utf-8"));
16296
16634
  const models = raw["models"];
@@ -17584,13 +17922,13 @@ var DeadModelStore = class {
17584
17922
  function fileDeadModelPersistence(filePath) {
17585
17923
  return {
17586
17924
  load() {
17587
- if (!fs32.existsSync(filePath)) return [];
17588
- const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
17925
+ if (!fs33.existsSync(filePath)) return [];
17926
+ const raw = JSON.parse(fs33.readFileSync(filePath, "utf-8"));
17589
17927
  return Array.isArray(raw) ? raw : [];
17590
17928
  },
17591
17929
  save(records) {
17592
- fs32.mkdirSync(path35.dirname(filePath), { recursive: true });
17593
- fs32.writeFileSync(filePath, JSON.stringify(records, null, 2));
17930
+ fs33.mkdirSync(path36.dirname(filePath), { recursive: true });
17931
+ fs33.writeFileSync(filePath, JSON.stringify(records, null, 2));
17594
17932
  }
17595
17933
  };
17596
17934
  }
@@ -18014,7 +18352,7 @@ init_pricing();
18014
18352
  var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
18015
18353
  var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
18016
18354
  var FETCH_TIMEOUT_MS = 8e3;
18017
- var DEFAULT_CACHE_FILE = path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
18355
+ var DEFAULT_CACHE_FILE = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
18018
18356
  function normalizeModelId2(id) {
18019
18357
  let s = id.toLowerCase();
18020
18358
  const slash = s.lastIndexOf("/");
@@ -18161,7 +18499,7 @@ var LiveDataProvider = class {
18161
18499
  }
18162
18500
  async saveCache() {
18163
18501
  try {
18164
- await fs11__default.default.mkdir(path35__default.default.dirname(this.opts.cacheFile), { recursive: true });
18502
+ await fs11__default.default.mkdir(path36__default.default.dirname(this.opts.cacheFile), { recursive: true });
18165
18503
  const cache = {
18166
18504
  fetchedAt: this.fetchedAt,
18167
18505
  snapshot: this.snapshot ?? void 0,
@@ -18268,9 +18606,15 @@ var FAMILY_BENCHMARKS = {
18268
18606
  "claude-opus": { code: 95, analysis: 92, creative: 90, data: 88 },
18269
18607
  "claude-sonnet": { code: 93, analysis: 88, creative: 87, data: 85 },
18270
18608
  "claude-haiku": { code: 80, analysis: 75, creative: 76, data: 72 },
18271
- // OpenAI GPT-5 family — current flagship; strongest all-round. Offline
18272
- // baseline (the live fetch refreshes these). Distinct point releases carry
18273
- // their own scores: gpt-5.5 "Spud" leads SWE-bench (88.7%); gpt-5.4 ~80%.
18609
+ // OpenAI GPT-5 family. GPT-5.6 is split by durable capability tier instead
18610
+ // of being folded into generic gpt-5: Sol, Terra and Luna have materially
18611
+ // different benchmark/cost envelopes and should compete independently.
18612
+ // Scores are the committed normalized snapshot; provenance is in
18613
+ // scripts/benchmarks/sources/openai-gpt-5.6-2026-08.json.
18614
+ "gpt-5.6-sol": { code: 100, analysis: 98, creative: 88, data: 88 },
18615
+ "gpt-5.6-terra": { code: 97, analysis: 92, creative: 82, data: 85 },
18616
+ "gpt-5.6-luna": { code: 93, analysis: 85, creative: 74, data: 60 },
18617
+ // Older GPT-5 point releases remain distinct where public data supports it.
18274
18618
  "gpt-5.5": { code: 97, analysis: 96, creative: 94, data: 95 },
18275
18619
  "gpt-5.4": { code: 94, analysis: 94, creative: 92, data: 92 },
18276
18620
  "gpt-5.4-mini": { code: 86, analysis: 85, creative: 85, data: 83 },
@@ -18306,13 +18650,19 @@ var FAMILY_BENCHMARKS = {
18306
18650
  "llama-small": { code: 55, analysis: 56, creative: 60, data: 54 }
18307
18651
  };
18308
18652
  var FAMILY_MATCHERS = [
18653
+ [/fable/i, "claude-opus"],
18309
18654
  [/opus/i, "claude-opus"],
18310
18655
  [/sonnet/i, "claude-sonnet"],
18311
18656
  [/haiku/i, "claude-haiku"],
18312
- // GPT-5 family — ordered most-specific first. Distinct point releases
18313
- // (gpt-5.5, gpt-5.4, gpt-5.4-mini) match their own family; nano/mini before
18314
- // the base; unrecognised gpt-5.x still fold into the gpt-5 base.
18657
+ // GPT-5 family — ordered most-specific first. GPT-5.6 durable tiers carry
18658
+ // their own profiles. 5.4 Nano intentionally maps to the nano class rather
18659
+ // than being swallowed by the generic 5.4 rule.
18660
+ [/gpt-?5\.6.*sol/i, "gpt-5.6-sol"],
18661
+ [/gpt-?5\.6.*terra/i, "gpt-5.6-terra"],
18662
+ [/gpt-?5\.6.*luna/i, "gpt-5.6-luna"],
18663
+ [/gpt-?5\.6(?:\s|$)/i, "gpt-5.6-sol"],
18315
18664
  [/gpt-?5\.5/i, "gpt-5.5"],
18665
+ [/gpt-?5\.4.*nano/i, "gpt-5-nano"],
18316
18666
  [/gpt-?5\.4.*mini/i, "gpt-5.4-mini"],
18317
18667
  [/gpt-?5\.4/i, "gpt-5.4"],
18318
18668
  [/gpt-?5.*nano/i, "gpt-5-nano"],
@@ -21372,8 +21722,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
21372
21722
  // Media generation can run for a minute; without this a cancelled run
21373
21723
  // still pays for an image nobody will see.
21374
21724
  ...this.signal ? { signal: this.signal } : {},
21375
- saveSnapshot: async (path40, content) => {
21376
- this.store?.addFileSnapshot(this.taskId, path40, content);
21725
+ saveSnapshot: async (path41, content) => {
21726
+ this.store?.addFileSnapshot(this.taskId, path41, content);
21377
21727
  },
21378
21728
  sendPeerSync: (to, syncType, content) => {
21379
21729
  this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
@@ -21576,7 +21926,7 @@ ${assignment.expectedOutput}`;
21576
21926
  const { promisify: promisify4 } = await import('util');
21577
21927
  const execFileAsync2 = promisify4(execFile3);
21578
21928
  for (const artifactPath of artifactPaths) {
21579
- const absolutePath = path35__default.default.resolve(this.artifactRoot(), artifactPath);
21929
+ const absolutePath = path36__default.default.resolve(this.artifactRoot(), artifactPath);
21580
21930
  try {
21581
21931
  const stat = await fs11__default.default.stat(absolutePath);
21582
21932
  if (!stat.isFile()) {
@@ -21611,7 +21961,7 @@ ${assignment.expectedOutput}`;
21611
21961
  issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
21612
21962
  continue;
21613
21963
  }
21614
- const ext = path35__default.default.extname(absolutePath).toLowerCase();
21964
+ const ext = path36__default.default.extname(absolutePath).toLowerCase();
21615
21965
  const check = ext === ".ts" || ext === ".tsx" ? { cmd: "npx", args: ["tsc", "--noEmit", absolutePath] } : ext === ".js" || ext === ".jsx" ? { cmd: process.execPath, args: ["--check", absolutePath] } : ext === ".py" ? { cmd: "python", args: ["-m", "py_compile", absolutePath] } : null;
21616
21966
  if (check) {
21617
21967
  try {
@@ -21703,7 +22053,7 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
21703
22053
  async checkAcceptance(assignment) {
21704
22054
  const criteria = assignment.acceptance ?? [];
21705
22055
  if (!criteria.length) return [];
21706
- const resolve2 = (target) => path35__default.default.resolve(this.artifactRoot(), target);
22056
+ const resolve2 = (target) => path36__default.default.resolve(this.artifactRoot(), target);
21707
22057
  const workerCanWriteFiles = canProduceFiles(this.tools.map((t) => t.name));
21708
22058
  return evaluateAcceptance(criteria, assignment.files ?? [], {
21709
22059
  stat: async (target) => {
@@ -25014,16 +25364,16 @@ function resolveInWorkspace(workspaceRoot, input) {
25014
25364
  if (typeof input !== "string" || input.length === 0) {
25015
25365
  throw new WorkspaceSandboxError(String(input), workspaceRoot);
25016
25366
  }
25017
- const root = path35__default.default.resolve(workspaceRoot);
25018
- const abs = path35__default.default.isAbsolute(input) ? path35__default.default.resolve(input) : path35__default.default.resolve(root, input);
25019
- const rel = path35__default.default.relative(root, abs);
25020
- if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path35__default.default.isAbsolute(rel)) {
25367
+ const root = path36__default.default.resolve(workspaceRoot);
25368
+ const abs = path36__default.default.isAbsolute(input) ? path36__default.default.resolve(input) : path36__default.default.resolve(root, input);
25369
+ const rel = path36__default.default.relative(root, abs);
25370
+ if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path36__default.default.isAbsolute(rel)) {
25021
25371
  throw new WorkspaceSandboxError(input, root);
25022
25372
  }
25023
25373
  try {
25024
- const real = fs32__default.default.realpathSync(abs);
25025
- const realRel = path35__default.default.relative(root, real);
25026
- if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path35__default.default.isAbsolute(realRel))) {
25374
+ const real = fs33__default.default.realpathSync(abs);
25375
+ const realRel = path36__default.default.relative(root, real);
25376
+ if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path36__default.default.isAbsolute(realRel))) {
25027
25377
  throw new WorkspaceSandboxError(input, root);
25028
25378
  }
25029
25379
  } catch (e) {
@@ -25572,9 +25922,9 @@ async function animatePptx(bytes, scheme) {
25572
25922
  const zip = await import_jszip.default.loadAsync(bytes);
25573
25923
  const slidePaths = Object.keys(zip.files).filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n));
25574
25924
  if (slidePaths.length === 0) return bytes;
25575
- for (const path40 of slidePaths) {
25576
- const xml = await zip.file(path40).async("string");
25577
- zip.file(path40, animateSlideXml(xml, scheme));
25925
+ for (const path41 of slidePaths) {
25926
+ const xml = await zip.file(path41).async("string");
25927
+ zip.file(path41, animateSlideXml(xml, scheme));
25578
25928
  }
25579
25929
  return await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
25580
25930
  } catch {
@@ -25955,7 +26305,7 @@ var GenerateDocumentTool = class extends BaseTool {
25955
26305
  }
25956
26306
  }
25957
26307
  const bytes = await renderDocument(ext, content, { name: filePath, loadImageBytes });
25958
- await fs11__default.default.mkdir(path35__default.default.dirname(absPath), { recursive: true });
26308
+ await fs11__default.default.mkdir(path36__default.default.dirname(absPath), { recursive: true });
25959
26309
  await fs11__default.default.writeFile(absPath, bytes);
25960
26310
  const charts = countCharts(content);
25961
26311
  return [
@@ -26176,7 +26526,7 @@ var FileWriteTool = class extends BaseTool {
26176
26526
  } catch {
26177
26527
  }
26178
26528
  }
26179
- await fs11__default.default.mkdir(path35__default.default.dirname(absPath), { recursive: true });
26529
+ await fs11__default.default.mkdir(path36__default.default.dirname(absPath), { recursive: true });
26180
26530
  await fs11__default.default.writeFile(absPath, content, "utf-8");
26181
26531
  return `Written ${content.length} characters to ${filePath}`;
26182
26532
  }
@@ -26534,12 +26884,10 @@ ${data.description}`;
26534
26884
  }
26535
26885
  }
26536
26886
  };
26537
-
26538
- // src/tools/browser.ts
26539
26887
  var BROWSER_LAUNCH_TIMEOUT_MS = 15e3;
26540
26888
  var BrowserTool = class extends BaseTool {
26541
26889
  name = "browser";
26542
- description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots. Only available with multimodal models.";
26890
+ description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots. Screenshots are saved to a file for image_analyze to read.";
26543
26891
  inputSchema = {
26544
26892
  type: "object",
26545
26893
  properties: {
@@ -26606,7 +26954,14 @@ var BrowserTool = class extends BaseTool {
26606
26954
  }
26607
26955
  case "screenshot": {
26608
26956
  const buf = await page2.screenshot({ type: "png" });
26609
- return `data:image/png;base64,${buf.toString("base64")}`;
26957
+ const rel = path36__default.default.join(".cascade", "screenshots", `screenshot-${Date.now()}-${crypto.randomUUID().slice(0, 8)}.png`);
26958
+ const abs = resolveInWorkspace(this.workspaceRoot, rel);
26959
+ await fs11__default.default.mkdir(path36__default.default.dirname(abs), { recursive: true });
26960
+ await fs11__default.default.writeFile(abs, buf);
26961
+ return [
26962
+ `Screenshot saved to ${abs} (${Math.round(buf.byteLength / 1024)} KB).`,
26963
+ `Call image_analyze with that path to see what is on it \u2014 that needs a vision-capable model.`
26964
+ ].join(" ");
26610
26965
  }
26611
26966
  case "evaluate": {
26612
26967
  const result = await page2.evaluate(input["script"]);
@@ -26674,7 +27029,7 @@ var ImageAnalyzeTool = class extends BaseTool {
26674
27029
  };
26675
27030
  async function fileToImageAttachment(filePath) {
26676
27031
  const data = await fs11__default.default.readFile(filePath);
26677
- const ext = path35__default.default.extname(filePath).toLowerCase();
27032
+ const ext = path36__default.default.extname(filePath).toLowerCase();
26678
27033
  const mimeMap = {
26679
27034
  ".jpg": "image/jpeg",
26680
27035
  ".jpeg": "image/jpeg",
@@ -26708,14 +27063,14 @@ var PDFCreateTool = class extends BaseTool {
26708
27063
  const filePath = input["path"];
26709
27064
  const content = input["content"];
26710
27065
  const title = input["title"];
26711
- const dir = path35__default.default.dirname(filePath);
26712
- if (!fs32__default.default.existsSync(dir)) {
26713
- fs32__default.default.mkdirSync(dir, { recursive: true });
27066
+ const dir = path36__default.default.dirname(filePath);
27067
+ if (!fs33__default.default.existsSync(dir)) {
27068
+ fs33__default.default.mkdirSync(dir, { recursive: true });
26714
27069
  }
26715
27070
  return new Promise((resolve2, reject) => {
26716
27071
  try {
26717
27072
  const doc = new PDFDocument__default.default({ margin: 50 });
26718
- const stream = fs32__default.default.createWriteStream(filePath);
27073
+ const stream = fs33__default.default.createWriteStream(filePath);
26719
27074
  doc.pipe(stream);
26720
27075
  if (title) {
26721
27076
  doc.info["Title"] = title;
@@ -26793,22 +27148,22 @@ var CodeInterpreterTool = class extends BaseTool {
26793
27148
  }
26794
27149
  cmdPrefix = NODE_CMD;
26795
27150
  }
26796
- const tmpDir = path35__default.default.join(this.workspaceRoot, ".cascade", "tmp");
26797
- if (!fs32__default.default.existsSync(tmpDir)) {
26798
- fs32__default.default.mkdirSync(tmpDir, { recursive: true });
27151
+ const tmpDir = path36__default.default.join(this.workspaceRoot, ".cascade", "tmp");
27152
+ if (!fs33__default.default.existsSync(tmpDir)) {
27153
+ fs33__default.default.mkdirSync(tmpDir, { recursive: true });
26799
27154
  }
26800
27155
  const extension = language === "python" ? "py" : "js";
26801
27156
  const fileName = `intp_${crypto.randomUUID().slice(0, 8)}.${extension}`;
26802
- const filePath = path35__default.default.join(tmpDir, fileName);
26803
- fs32__default.default.writeFileSync(filePath, code, "utf-8");
27157
+ const filePath = path36__default.default.join(tmpDir, fileName);
27158
+ fs33__default.default.writeFileSync(filePath, code, "utf-8");
26804
27159
  const execArgs = [filePath, ...args];
26805
27160
  return new Promise((resolve2) => {
26806
27161
  const startMs = Date.now();
26807
27162
  child_process.execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
26808
27163
  const duration = Date.now() - startMs;
26809
27164
  try {
26810
- if (fs32__default.default.existsSync(filePath)) {
26811
- fs32__default.default.unlinkSync(filePath);
27165
+ if (fs33__default.default.existsSync(filePath)) {
27166
+ fs33__default.default.unlinkSync(filePath);
26812
27167
  }
26813
27168
  } catch (cleanupErr) {
26814
27169
  console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
@@ -26888,18 +27243,178 @@ ${formatted}`;
26888
27243
  return `Unknown action: ${action}`;
26889
27244
  }
26890
27245
  };
27246
+ var SsrfBlockedError = class extends Error {
27247
+ constructor(message) {
27248
+ super(message);
27249
+ this.name = "SsrfBlockedError";
27250
+ }
27251
+ };
27252
+ var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
27253
+ var MAX_REDIRECTS2 = 5;
27254
+ function allowLocal() {
27255
+ return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
27256
+ }
27257
+ function isPrivateAddress(ip) {
27258
+ const type = net__default.default.isIP(ip);
27259
+ if (type === 4) return isPrivateIPv4(ip);
27260
+ if (type === 6) return isPrivateIPv6(ip);
27261
+ return true;
27262
+ }
27263
+ function isPrivateIPv4(ip) {
27264
+ const parts = ip.split(".").map((p) => Number(p));
27265
+ if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
27266
+ const [a, b] = parts;
27267
+ if (a === 0) return true;
27268
+ if (a === 10) return true;
27269
+ if (a === 127) return true;
27270
+ if (a === 169 && b === 254) return true;
27271
+ if (a === 172 && b >= 16 && b <= 31) return true;
27272
+ if (a === 192 && b === 168) return true;
27273
+ if (a === 100 && b >= 64 && b <= 127) return true;
27274
+ if (a >= 224) return true;
27275
+ return false;
27276
+ }
27277
+ function ipv6Groups(ip) {
27278
+ let s = ip.toLowerCase().replace(/^\[|\]$/g, "");
27279
+ const zone = s.indexOf("%");
27280
+ if (zone !== -1) s = s.slice(0, zone);
27281
+ const dotted = /^(.*:)(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(s);
27282
+ if (dotted) {
27283
+ const o = [dotted[2], dotted[3], dotted[4], dotted[5]].map((p) => Number(p));
27284
+ if (o.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;
27285
+ s = `${dotted[1]}${(o[0] << 8 | o[1]).toString(16)}:${(o[2] << 8 | o[3]).toString(16)}`;
27286
+ }
27287
+ const halves = s.split("::");
27288
+ if (halves.length > 2) return null;
27289
+ const split = (part) => part ? part.split(":") : [];
27290
+ const head = split(halves[0] ?? "");
27291
+ const tail = halves.length === 2 ? split(halves[1] ?? "") : [];
27292
+ const zeros = halves.length === 2 ? 8 - head.length - tail.length : 0;
27293
+ if (zeros < 0) return null;
27294
+ const groups = [...head, ...Array(zeros).fill("0"), ...tail];
27295
+ if (groups.length !== 8) return null;
27296
+ const out = [];
27297
+ for (const g of groups) {
27298
+ if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
27299
+ out.push(parseInt(g, 16));
27300
+ }
27301
+ return out;
27302
+ }
27303
+ function embeddedIPv4(g) {
27304
+ const quad = (hi, lo) => `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
27305
+ const leadingZero = (n) => g.slice(0, n).every((x) => x === 0);
27306
+ if (leadingZero(5) && g[5] === 65535) return quad(g[6], g[7]);
27307
+ if (leadingZero(6)) return quad(g[6], g[7]);
27308
+ if (g[0] === 100 && g[1] === 65435) {
27309
+ if (g[2] === 1) {
27310
+ return quad(g[3], (g[4] & 255) << 8 | g[5] >> 8);
27311
+ }
27312
+ if (g[2] === 0 && g[3] === 0 && g[4] === 0 && g[5] === 0) return quad(g[6], g[7]);
27313
+ return null;
27314
+ }
27315
+ if (g[0] === 8194) return quad(g[1], g[2]);
27316
+ return null;
27317
+ }
27318
+ function isPrivateIPv6(ip) {
27319
+ const g = ipv6Groups(ip);
27320
+ if (!g) return true;
27321
+ const v4 = embeddedIPv4(g);
27322
+ if (v4) return isPrivateIPv4(v4);
27323
+ if (g[0] === 100 && g[1] === 65435) return true;
27324
+ if (g.every((x) => x === 0)) return true;
27325
+ if (leading7Zero(g) && g[7] === 1) return true;
27326
+ if ((g[0] & 65472) === 65152) return true;
27327
+ if ((g[0] & 65024) === 64512) return true;
27328
+ if ((g[0] & 65280) === 65280) return true;
27329
+ return false;
27330
+ }
27331
+ function leading7Zero(g) {
27332
+ return g.slice(0, 7).every((x) => x === 0);
27333
+ }
27334
+ async function assertPublicUrl(rawUrl) {
27335
+ let url;
27336
+ try {
27337
+ url = new URL(rawUrl);
27338
+ } catch {
27339
+ throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
27340
+ }
27341
+ if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
27342
+ throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
27343
+ }
27344
+ if (allowLocal()) return url;
27345
+ const host = url.hostname.replace(/^\[|\]$/g, "");
27346
+ if (net__default.default.isIP(host)) {
27347
+ if (isPrivateAddress(host)) {
27348
+ throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
27349
+ }
27350
+ return url;
27351
+ }
27352
+ if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
27353
+ throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
27354
+ }
27355
+ let addresses;
27356
+ try {
27357
+ const records = await dns2__default.default.lookup(host, { all: true });
27358
+ addresses = records.map((r) => r.address);
27359
+ } catch {
27360
+ throw new SsrfBlockedError(`Could not resolve host "${host}".`);
27361
+ }
27362
+ if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
27363
+ throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
27364
+ }
27365
+ return url;
27366
+ }
27367
+ var ssrfAgent = new undici.Agent({
27368
+ connect: {
27369
+ lookup(hostname, options, callback) {
27370
+ if (allowLocal()) {
27371
+ dnsCallback__default.default.lookup(hostname, options, callback);
27372
+ return;
27373
+ }
27374
+ dnsCallback__default.default.lookup(hostname, { ...options, all: true }, (err, addresses) => {
27375
+ if (err) return callback(err, "", 0);
27376
+ const list = Array.isArray(addresses) ? addresses : [addresses];
27377
+ const bad = list.find((a) => isPrivateAddress(a.address));
27378
+ if (bad) {
27379
+ return callback(
27380
+ new SsrfBlockedError(`Blocked connection to "${hostname}" \u2014 resolved to non-public address ${bad.address}.`),
27381
+ "",
27382
+ 0
27383
+ );
27384
+ }
27385
+ if (options.all) return callback(null, list);
27386
+ const first = list[0];
27387
+ return callback(null, first.address, first.family);
27388
+ });
27389
+ }
27390
+ }
27391
+ });
27392
+ async function safeFetch(rawUrl, init = {}) {
27393
+ let currentUrl = (await assertPublicUrl(rawUrl)).toString();
27394
+ for (let i = 0; i <= MAX_REDIRECTS2; i++) {
27395
+ const resp = await fetch(currentUrl, { ...init, redirect: "manual", dispatcher: ssrfAgent });
27396
+ if (resp.status < 300 || resp.status >= 400) return resp;
27397
+ const location = resp.headers.get("location");
27398
+ if (!location) return resp;
27399
+ const next = new URL(location, currentUrl);
27400
+ await assertPublicUrl(next.toString());
27401
+ currentUrl = next.toString();
27402
+ }
27403
+ throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
27404
+ }
26891
27405
 
26892
27406
  // src/tools/web-search.ts
26893
- async function searchSearXNG(query, baseUrl, maxResults) {
27407
+ async function searchSearXNG(query, baseUrl, maxResults, guardUrl = false) {
26894
27408
  const url = new URL("/search", baseUrl);
26895
27409
  url.searchParams.set("q", query);
26896
27410
  url.searchParams.set("format", "json");
26897
27411
  url.searchParams.set("categories", "general");
26898
27412
  url.searchParams.set("engines", "google,bing,duckduckgo");
26899
- const resp = await fetch(url.toString(), {
27413
+ const init = {
26900
27414
  headers: { "User-Agent": "Cascade-AI/1.0 WebSearchTool" },
26901
27415
  signal: AbortSignal.timeout(1e4)
26902
- });
27416
+ };
27417
+ const resp = guardUrl ? await safeFetch(url.toString(), init) : await fetch(url.toString(), init);
26903
27418
  if (!resp.ok) {
26904
27419
  throw new Error(`SearXNG returned HTTP ${resp.status}`);
26905
27420
  }
@@ -27028,7 +27543,11 @@ var WebSearchTool = class extends BaseTool {
27028
27543
  searxngUrl: config.searxngUrl ?? process.env["SEARXNG_URL"],
27029
27544
  braveApiKey: config.braveApiKey ?? process.env["BRAVE_SEARCH_API_KEY"],
27030
27545
  tavilyApiKey: config.tavilyApiKey ?? process.env["TAVILY_API_KEY"],
27031
- maxResults: config.maxResults ?? 5
27546
+ maxResults: config.maxResults ?? 5,
27547
+ // Note this reads only from `config`, never from the environment: the
27548
+ // guard tracks the trustworthiness of the URL the host passed in, and an
27549
+ // env var says nothing about that.
27550
+ guardSearxngUrl: config.guardSearxngUrl ?? false
27032
27551
  };
27033
27552
  }
27034
27553
  async execute(input, _options) {
@@ -27043,7 +27562,7 @@ var WebSearchTool = class extends BaseTool {
27043
27562
  let anyBackendReachable = false;
27044
27563
  if (this.config.searxngUrl) {
27045
27564
  try {
27046
- results = await searchSearXNG(query, this.config.searxngUrl, maxResults);
27565
+ results = await searchSearXNG(query, this.config.searxngUrl, maxResults, this.config.guardSearxngUrl);
27047
27566
  anyBackendReachable = true;
27048
27567
  if (results.length > 0) return this.formatResults(query, results);
27049
27568
  errors.push("SearXNG: returned 0 results");
@@ -27120,7 +27639,7 @@ var GlobTool = class extends BaseTool {
27120
27639
  };
27121
27640
  async execute(input, _options) {
27122
27641
  const pattern = input["pattern"];
27123
- const searchPath = input["path"] ? path35__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
27642
+ const searchPath = input["path"] ? path36__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
27124
27643
  const matches = await glob.glob(pattern, {
27125
27644
  cwd: searchPath,
27126
27645
  ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
@@ -27133,7 +27652,7 @@ var GlobTool = class extends BaseTool {
27133
27652
  const withMtime = await Promise.all(
27134
27653
  matches.map(async (rel) => {
27135
27654
  try {
27136
- const stat = await fs11__default.default.stat(path35__default.default.join(searchPath, rel));
27655
+ const stat = await fs11__default.default.stat(path36__default.default.join(searchPath, rel));
27137
27656
  return { rel, mtime: stat.mtimeMs };
27138
27657
  } catch {
27139
27658
  return { rel, mtime: 0 };
@@ -27182,7 +27701,7 @@ var GrepTool = class extends BaseTool {
27182
27701
  };
27183
27702
  async execute(input, _options) {
27184
27703
  const pattern = input["pattern"];
27185
- const searchPath = input["path"] ? path35__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
27704
+ const searchPath = input["path"] ? path36__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
27186
27705
  const globPattern = input["glob"];
27187
27706
  const outputMode = input["output_mode"] ?? "content";
27188
27707
  const context = input["context"] ?? 0;
@@ -27236,12 +27755,12 @@ var GrepTool = class extends BaseTool {
27236
27755
  nodir: true
27237
27756
  });
27238
27757
  } catch {
27239
- files = [path35__default.default.relative(searchPath, searchPath) || "."];
27758
+ files = [path36__default.default.relative(searchPath, searchPath) || "."];
27240
27759
  }
27241
27760
  const results = [];
27242
27761
  let totalCount = 0;
27243
27762
  for (const rel of files) {
27244
- const abs = path35__default.default.join(searchPath, rel);
27763
+ const abs = path36__default.default.join(searchPath, rel);
27245
27764
  let content;
27246
27765
  try {
27247
27766
  content = await fs11__default.default.readFile(abs, "utf-8");
@@ -27282,92 +27801,6 @@ Total: ${totalCount} matches`);
27282
27801
  return results.join("\n");
27283
27802
  }
27284
27803
  };
27285
- var SsrfBlockedError = class extends Error {
27286
- constructor(message) {
27287
- super(message);
27288
- this.name = "SsrfBlockedError";
27289
- }
27290
- };
27291
- var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
27292
- var MAX_REDIRECTS2 = 5;
27293
- function allowLocal() {
27294
- return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
27295
- }
27296
- function isPrivateAddress(ip) {
27297
- const type = net__default.default.isIP(ip);
27298
- if (type === 4) return isPrivateIPv4(ip);
27299
- if (type === 6) return isPrivateIPv6(ip);
27300
- return true;
27301
- }
27302
- function isPrivateIPv4(ip) {
27303
- const parts = ip.split(".").map((p) => Number(p));
27304
- if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
27305
- const [a, b] = parts;
27306
- if (a === 0) return true;
27307
- if (a === 10) return true;
27308
- if (a === 127) return true;
27309
- if (a === 169 && b === 254) return true;
27310
- if (a === 172 && b >= 16 && b <= 31) return true;
27311
- if (a === 192 && b === 168) return true;
27312
- if (a === 100 && b >= 64 && b <= 127) return true;
27313
- if (a >= 224) return true;
27314
- return false;
27315
- }
27316
- function isPrivateIPv6(ip) {
27317
- const lower = ip.toLowerCase().replace(/^\[|\]$/g, "");
27318
- if (lower === "::1" || lower === "::") return true;
27319
- if (lower.startsWith("fe80")) return true;
27320
- if (lower.startsWith("fc") || lower.startsWith("fd")) return true;
27321
- const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(lower);
27322
- if (mapped) return isPrivateIPv4(mapped[1]);
27323
- return false;
27324
- }
27325
- async function assertPublicUrl(rawUrl) {
27326
- let url;
27327
- try {
27328
- url = new URL(rawUrl);
27329
- } catch {
27330
- throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
27331
- }
27332
- if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
27333
- throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
27334
- }
27335
- if (allowLocal()) return url;
27336
- const host = url.hostname.replace(/^\[|\]$/g, "");
27337
- if (net__default.default.isIP(host)) {
27338
- if (isPrivateAddress(host)) {
27339
- throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
27340
- }
27341
- return url;
27342
- }
27343
- if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
27344
- throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
27345
- }
27346
- let addresses;
27347
- try {
27348
- const records = await dns2__default.default.lookup(host, { all: true });
27349
- addresses = records.map((r) => r.address);
27350
- } catch {
27351
- throw new SsrfBlockedError(`Could not resolve host "${host}".`);
27352
- }
27353
- if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
27354
- throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
27355
- }
27356
- return url;
27357
- }
27358
- async function safeFetch(rawUrl, init = {}) {
27359
- let currentUrl = (await assertPublicUrl(rawUrl)).toString();
27360
- for (let i = 0; i <= MAX_REDIRECTS2; i++) {
27361
- const resp = await fetch(currentUrl, { ...init, redirect: "manual" });
27362
- if (resp.status < 300 || resp.status >= 400) return resp;
27363
- const location = resp.headers.get("location");
27364
- if (!location) return resp;
27365
- const next = new URL(location, currentUrl);
27366
- await assertPublicUrl(next.toString());
27367
- currentUrl = next.toString();
27368
- }
27369
- throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
27370
- }
27371
27804
 
27372
27805
  // src/tools/web-fetch.ts
27373
27806
  var MAX_CHARS = 5e4;
@@ -27640,10 +28073,10 @@ var ToolRegistry = class extends EventEmitter__default.default {
27640
28073
  }
27641
28074
  isIgnored(filePath) {
27642
28075
  if (!filePath) return false;
27643
- const abs = path35__default.default.resolve(this.workspaceRoot, filePath);
27644
- const rel = path35__default.default.relative(this.workspaceRoot, abs);
27645
- if (!rel || rel.startsWith("..") || path35__default.default.isAbsolute(rel)) return true;
27646
- const posixRel = rel.split(path35__default.default.sep).join("/");
28076
+ const abs = path36__default.default.resolve(this.workspaceRoot, filePath);
28077
+ const rel = path36__default.default.relative(this.workspaceRoot, abs);
28078
+ if (!rel || rel.startsWith("..") || path36__default.default.isAbsolute(rel)) return true;
28079
+ const posixRel = rel.split(path36__default.default.sep).join("/");
27647
28080
  return this.ignoreMatcher.ignores(posixRel);
27648
28081
  }
27649
28082
  };
@@ -27877,22 +28310,22 @@ var FileMcpOAuthStore = class {
27877
28310
  filePath;
27878
28311
  load() {
27879
28312
  try {
27880
- return JSON.parse(fs32__default.default.readFileSync(this.filePath, "utf-8"));
28313
+ return JSON.parse(fs33__default.default.readFileSync(this.filePath, "utf-8"));
27881
28314
  } catch {
27882
28315
  return void 0;
27883
28316
  }
27884
28317
  }
27885
28318
  save(state) {
27886
- fs32__default.default.mkdirSync(path35__default.default.dirname(this.filePath), { recursive: true, mode: 448 });
27887
- fs32__default.default.writeFileSync(this.filePath, JSON.stringify(state, null, 2), { mode: 384 });
28319
+ fs33__default.default.mkdirSync(path36__default.default.dirname(this.filePath), { recursive: true, mode: 448 });
28320
+ fs33__default.default.writeFileSync(this.filePath, JSON.stringify(state, null, 2), { mode: 384 });
27888
28321
  try {
27889
- fs32__default.default.chmodSync(this.filePath, 384);
28322
+ fs33__default.default.chmodSync(this.filePath, 384);
27890
28323
  } catch {
27891
28324
  }
27892
28325
  }
27893
28326
  clear() {
27894
28327
  try {
27895
- fs32__default.default.rmSync(this.filePath);
28328
+ fs33__default.default.rmSync(this.filePath);
27896
28329
  } catch {
27897
28330
  }
27898
28331
  }
@@ -28712,7 +29145,7 @@ var TaskAnalyzer = class {
28712
29145
 
28713
29146
  // src/core/router/model-performance-tracker.ts
28714
29147
  init_pricing();
28715
- var DEFAULT_STATS_FILE = path35__default.default.join(os10__default.default.homedir(), ".cascade", "model-perf.json");
29148
+ var DEFAULT_STATS_FILE = path36__default.default.join(os10__default.default.homedir(), ".cascade", "model-perf.json");
28716
29149
  var ModelPerformanceTracker = class _ModelPerformanceTracker {
28717
29150
  stats = /* @__PURE__ */ new Map();
28718
29151
  featureStats = /* @__PURE__ */ new Map();
@@ -28752,7 +29185,7 @@ var ModelPerformanceTracker = class _ModelPerformanceTracker {
28752
29185
  async save() {
28753
29186
  if (this.readOnly) return;
28754
29187
  try {
28755
- await fs11__default.default.mkdir(path35__default.default.dirname(this.statsFile), { recursive: true });
29188
+ await fs11__default.default.mkdir(path36__default.default.dirname(this.statsFile), { recursive: true });
28756
29189
  const modelsObj = {};
28757
29190
  const featuresObj = {};
28758
29191
  for (const [key, stat] of this.stats) modelsObj[key] = stat;
@@ -29385,7 +29818,7 @@ Required capability: ${description.slice(0, 300)}`;
29385
29818
  * any dangerous action, so a silently-reloaded tool can't act without approval. */
29386
29819
  async loadPersistedTools() {
29387
29820
  if (!this.workspacePath || !this.persistEnabled) return;
29388
- const file = path35__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
29821
+ const file = path36__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
29389
29822
  try {
29390
29823
  const raw = await fs11__default.default.readFile(file, "utf-8");
29391
29824
  const specs = JSON.parse(raw);
@@ -29409,8 +29842,8 @@ Required capability: ${description.slice(0, 300)}`;
29409
29842
  }
29410
29843
  async persist() {
29411
29844
  if (!this.workspacePath || !this.persistEnabled) return;
29412
- const dir = path35__default.default.join(this.workspacePath, ".cascade");
29413
- const file = path35__default.default.join(dir, DYNAMIC_TOOLS_FILE);
29845
+ const dir = path36__default.default.join(this.workspacePath, ".cascade");
29846
+ const file = path36__default.default.join(dir, DYNAMIC_TOOLS_FILE);
29414
29847
  try {
29415
29848
  await fs11__default.default.mkdir(dir, { recursive: true });
29416
29849
  await fs11__default.default.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
@@ -29430,12 +29863,12 @@ var WorldStateDB = class {
29430
29863
  constructor(workspacePath, debugMode = false) {
29431
29864
  this.workspacePath = workspacePath;
29432
29865
  this.debugMode = debugMode;
29433
- const cascadeDir = path35__default.default.join(workspacePath, ".cascade");
29434
- if (!fs32__default.default.existsSync(cascadeDir)) {
29435
- fs32__default.default.mkdirSync(cascadeDir, { recursive: true });
29866
+ const cascadeDir = path36__default.default.join(workspacePath, ".cascade");
29867
+ if (!fs33__default.default.existsSync(cascadeDir)) {
29868
+ fs33__default.default.mkdirSync(cascadeDir, { recursive: true });
29436
29869
  }
29437
- this.keyPath = path35__default.default.join(cascadeDir, "world_state.key");
29438
- this.dbPath = path35__default.default.join(cascadeDir, "world_state.db");
29870
+ this.keyPath = path36__default.default.join(cascadeDir, "world_state.key");
29871
+ this.dbPath = path36__default.default.join(cascadeDir, "world_state.db");
29439
29872
  this.initEncryptionKey();
29440
29873
  this.db = new Database2__default.default(this.dbPath);
29441
29874
  this.db.pragma("journal_mode = WAL");
@@ -29492,11 +29925,11 @@ var WorldStateDB = class {
29492
29925
  ).run(e, r, row.encrypted_value, row.source_worker, row.timestamp, validTo, change);
29493
29926
  }
29494
29927
  initEncryptionKey() {
29495
- if (fs32__default.default.existsSync(this.keyPath)) {
29496
- this.encryptionKey = fs32__default.default.readFileSync(this.keyPath);
29928
+ if (fs33__default.default.existsSync(this.keyPath)) {
29929
+ this.encryptionKey = fs33__default.default.readFileSync(this.keyPath);
29497
29930
  } else {
29498
29931
  this.encryptionKey = crypto__default.default.randomBytes(32);
29499
- fs32__default.default.writeFileSync(this.keyPath, this.encryptionKey);
29932
+ fs33__default.default.writeFileSync(this.keyPath, this.encryptionKey);
29500
29933
  }
29501
29934
  }
29502
29935
  encrypt(text) {
@@ -29782,9 +30215,9 @@ var WorldStateDB = class {
29782
30215
  dumpDebugIfNeeded() {
29783
30216
  if (!this.debugMode) return;
29784
30217
  try {
29785
- const dumpPath = path35__default.default.join(os10__default.default.tmpdir(), "cascade_world_state_debug.json");
30218
+ const dumpPath = path36__default.default.join(os10__default.default.tmpdir(), "cascade_world_state_debug.json");
29786
30219
  const entries = this.getAllEntries();
29787
- fs32__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
30220
+ fs33__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
29788
30221
  } catch (err) {
29789
30222
  console.error("Failed to dump debug world state", err);
29790
30223
  }
@@ -29840,11 +30273,11 @@ var ResumeStore = class {
29840
30273
  version: RESUME_SCHEMA_VERSION,
29841
30274
  createdAt: checkpoint.createdAt ?? new Date(timestamp).toISOString()
29842
30275
  };
29843
- await fs32.promises.mkdir(this.dir, { recursive: true });
29844
- const finalPath = path35__default.default.join(this.dir, `${timestamp}-${safeId(record.taskId)}.json`);
30276
+ await fs33.promises.mkdir(this.dir, { recursive: true });
30277
+ const finalPath = path36__default.default.join(this.dir, `${timestamp}-${safeId(record.taskId)}.json`);
29845
30278
  const tempPath = `${finalPath}.${process.pid}.tmp`;
29846
- await fs32.promises.writeFile(tempPath, JSON.stringify(record, null, 2), "utf-8");
29847
- await fs32.promises.rename(tempPath, finalPath);
30279
+ await fs33.promises.writeFile(tempPath, JSON.stringify(record, null, 2), "utf-8");
30280
+ await fs33.promises.rename(tempPath, finalPath);
29848
30281
  await this.prune();
29849
30282
  return true;
29850
30283
  } catch {
@@ -29855,7 +30288,7 @@ var ResumeStore = class {
29855
30288
  async list() {
29856
30289
  let entries;
29857
30290
  try {
29858
- entries = await fs32.promises.readdir(this.dir);
30291
+ entries = await fs33.promises.readdir(this.dir);
29859
30292
  } catch {
29860
30293
  return [];
29861
30294
  }
@@ -29864,7 +30297,7 @@ var ResumeStore = class {
29864
30297
  const checkpoints = [];
29865
30298
  for (const candidate of candidates2) {
29866
30299
  try {
29867
- const raw = await fs32.promises.readFile(path35__default.default.join(this.dir, candidate.name), "utf-8");
30300
+ const raw = await fs33.promises.readFile(path36__default.default.join(this.dir, candidate.name), "utf-8");
29868
30301
  const parsed = JSON.parse(raw);
29869
30302
  if (isCheckpoint(parsed)) checkpoints.push(parsed);
29870
30303
  } catch {
@@ -29898,12 +30331,12 @@ var ResumeStore = class {
29898
30331
  const target = taskId ? all.find((c) => c.taskId === taskId) : all[0];
29899
30332
  if (!target) return null;
29900
30333
  try {
29901
- const entries = await fs32.promises.readdir(this.dir);
30334
+ const entries = await fs33.promises.readdir(this.dir);
29902
30335
  const wanted = safeId(target.taskId);
29903
30336
  const source = entries.find((name) => FILE_PATTERN.exec(name)?.[2] === wanted);
29904
30337
  if (!source) return null;
29905
30338
  const claimId = `${this.now()}-${safeId(target.taskId)}`;
29906
- await fs32.promises.rename(path35__default.default.join(this.dir, source), path35__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`));
30339
+ await fs33.promises.rename(path36__default.default.join(this.dir, source), path36__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`));
29907
30340
  return { checkpoint: target, claimId };
29908
30341
  } catch {
29909
30342
  return null;
@@ -29912,9 +30345,9 @@ var ResumeStore = class {
29912
30345
  /** Give a claim back, so the work stays recoverable after a failed start. */
29913
30346
  async release(claimId) {
29914
30347
  try {
29915
- await fs32.promises.rename(
29916
- path35__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`),
29917
- path35__default.default.join(this.dir, `${claimId}.json`)
30348
+ await fs33.promises.rename(
30349
+ path36__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`),
30350
+ path36__default.default.join(this.dir, `${claimId}.json`)
29918
30351
  );
29919
30352
  } catch {
29920
30353
  }
@@ -29922,7 +30355,7 @@ var ResumeStore = class {
29922
30355
  /** Discard a claim for good — the resumed run has taken ownership. */
29923
30356
  async settle(claimId) {
29924
30357
  try {
29925
- await fs32.promises.rm(path35__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`), { force: true });
30358
+ await fs33.promises.rm(path36__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`), { force: true });
29926
30359
  } catch {
29927
30360
  }
29928
30361
  }
@@ -29944,15 +30377,15 @@ var ResumeStore = class {
29944
30377
  async reclaimStale() {
29945
30378
  let reclaimed = 0;
29946
30379
  try {
29947
- const entries = await fs32.promises.readdir(this.dir);
30380
+ const entries = await fs33.promises.readdir(this.dir);
29948
30381
  const cutoff = this.now() - this.claimTimeoutMs;
29949
30382
  for (const name of entries) {
29950
30383
  if (!name.endsWith(CLAIM_SUFFIX)) continue;
29951
30384
  const stamp2 = Number(/^(\d+)-/.exec(name)?.[1]);
29952
30385
  if (!Number.isFinite(stamp2) || stamp2 >= cutoff) continue;
29953
- await fs32.promises.rename(
29954
- path35__default.default.join(this.dir, name),
29955
- path35__default.default.join(this.dir, `${name.slice(0, -CLAIM_SUFFIX.length)}.json`)
30386
+ await fs33.promises.rename(
30387
+ path36__default.default.join(this.dir, name),
30388
+ path36__default.default.join(this.dir, `${name.slice(0, -CLAIM_SUFFIX.length)}.json`)
29956
30389
  );
29957
30390
  reclaimed++;
29958
30391
  }
@@ -29963,12 +30396,12 @@ var ResumeStore = class {
29963
30396
  /** Remove every checkpoint for a task id. */
29964
30397
  async delete(taskId) {
29965
30398
  try {
29966
- const entries = await fs32.promises.readdir(this.dir);
30399
+ const entries = await fs33.promises.readdir(this.dir);
29967
30400
  const wanted = safeId(taskId);
29968
30401
  await Promise.all(entries.map(async (name) => {
29969
30402
  const match = FILE_PATTERN.exec(name);
29970
30403
  if (match?.[2] === wanted) {
29971
- await fs32.promises.rm(path35__default.default.join(this.dir, name), { force: true });
30404
+ await fs33.promises.rm(path36__default.default.join(this.dir, name), { force: true });
29972
30405
  }
29973
30406
  }));
29974
30407
  } catch {
@@ -29977,13 +30410,13 @@ var ResumeStore = class {
29977
30410
  /** Drop expired checkpoints, then any beyond the newest `maxCheckpoints`. */
29978
30411
  async prune() {
29979
30412
  try {
29980
- const entries = await fs32.promises.readdir(this.dir);
30413
+ const entries = await fs33.promises.readdir(this.dir);
29981
30414
  const cutoff = this.now() - this.maxAgeMs;
29982
30415
  const files = entries.map((name) => ({ name, match: FILE_PATTERN.exec(name) })).filter((e) => e.match !== null).map((e) => ({ name: e.name, timestamp: Number(e.match[1]) })).sort((a, b) => b.timestamp - a.timestamp);
29983
30416
  const doomed = files.filter((f, index) => f.timestamp < cutoff || index >= this.maxCheckpoints);
29984
30417
  const staleTemps = entries.filter((name) => name.endsWith(".tmp")).filter((name) => (Number(FILE_PATTERN.exec(name.replace(/\.\d+\.tmp$/, ".json"))?.[1]) || 0) < cutoff);
29985
30418
  await Promise.all([...doomed.map((f) => f.name), ...staleTemps].map(
29986
- (name) => fs32.promises.rm(path35__default.default.join(this.dir, name), { force: true })
30419
+ (name) => fs33.promises.rm(path36__default.default.join(this.dir, name), { force: true })
29987
30420
  ));
29988
30421
  } catch {
29989
30422
  }
@@ -30360,7 +30793,7 @@ var WorkspaceIndex = class {
30360
30793
  isIgnored;
30361
30794
  chunker;
30362
30795
  constructor(opts) {
30363
- this.root = path35__default.default.resolve(opts.root);
30796
+ this.root = path36__default.default.resolve(opts.root);
30364
30797
  this.db = opts.db;
30365
30798
  this.namespace = opts.namespace ?? "code";
30366
30799
  this.extensions = new Set((opts.extensions ?? DEFAULT_EXTENSIONS).map((e) => e.replace(/^\./, "").toLowerCase()));
@@ -30392,14 +30825,14 @@ var WorkspaceIndex = class {
30392
30825
  return;
30393
30826
  }
30394
30827
  for (const entry of entries) {
30395
- const abs = path35__default.default.join(dir, entry.name);
30828
+ const abs = path36__default.default.join(dir, entry.name);
30396
30829
  if (entry.isDirectory()) {
30397
30830
  if (HARD_SKIP_DIRS.has(entry.name) || this.isIgnored(abs)) continue;
30398
30831
  await walk(abs);
30399
30832
  } else if (entry.isFile()) {
30400
- const ext = path35__default.default.extname(entry.name).slice(1).toLowerCase();
30833
+ const ext = path36__default.default.extname(entry.name).slice(1).toLowerCase();
30401
30834
  if (!this.extensions.has(ext) || this.isIgnored(abs)) continue;
30402
- out.push(path35__default.default.relative(this.root, abs));
30835
+ out.push(path36__default.default.relative(this.root, abs));
30403
30836
  }
30404
30837
  }
30405
30838
  };
@@ -30429,7 +30862,7 @@ var WorkspaceIndex = class {
30429
30862
  const entries = [];
30430
30863
  for (const rel of rels) {
30431
30864
  try {
30432
- const abs = path35__default.default.join(this.root, rel);
30865
+ const abs = path36__default.default.join(this.root, rel);
30433
30866
  const stat = await fs11__default.default.stat(abs);
30434
30867
  if (stat.size > this.maxFileBytes) continue;
30435
30868
  const buf = await fs11__default.default.readFile(abs);
@@ -30919,13 +31352,13 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
30919
31352
  this.toolRegistry = new ToolRegistry(this.config.tools, workspacePath);
30920
31353
  try {
30921
31354
  this.router.setDeadModelStore(new DeadModelStore(
30922
- fileDeadModelPersistence(path35__default.default.join(workspacePath, ".cascade", "dead-models.json"))
31355
+ fileDeadModelPersistence(path36__default.default.join(workspacePath, ".cascade", "dead-models.json"))
30923
31356
  ));
30924
31357
  } catch {
30925
31358
  }
30926
31359
  try {
30927
31360
  this.resumeStore = new ResumeStore({
30928
- dir: path35__default.default.join(workspacePath, ".cascade", "resume")
31361
+ dir: path36__default.default.join(workspacePath, ".cascade", "resume")
30929
31362
  });
30930
31363
  } catch {
30931
31364
  }
@@ -31013,8 +31446,8 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
31013
31446
  this.emit("log", { level: "warn", message: "Code index enabled but no embeddings-capable provider is configured \u2014 skipping." });
31014
31447
  return;
31015
31448
  }
31016
- const dbPath = ci.dbPath || path35__default.default.join(this.workspacePath, ".cascade", "code-index.db");
31017
- fs32__default.default.mkdirSync(path35__default.default.dirname(dbPath), { recursive: true });
31449
+ const dbPath = ci.dbPath || path36__default.default.join(this.workspacePath, ".cascade", "code-index.db");
31450
+ fs33__default.default.mkdirSync(path36__default.default.dirname(dbPath), { recursive: true });
31018
31451
  const db = new Database2__default.default(dbPath);
31019
31452
  db.pragma("journal_mode = WAL");
31020
31453
  const complete = chatCompleterFromProviders(this.config.providers);
@@ -32482,14 +32915,14 @@ ${finalOutput}` : ""
32482
32915
  init_constants();
32483
32916
  var CLOUD_SESSION_FILE = "cloud-session.json";
32484
32917
  function globalDir(homeDir = os10__default.default.homedir()) {
32485
- return path35__default.default.join(homeDir, GLOBAL_CONFIG_DIR);
32918
+ return path36__default.default.join(homeDir, GLOBAL_CONFIG_DIR);
32486
32919
  }
32487
32920
  function cloudSessionPath(dir = globalDir()) {
32488
- return path35__default.default.join(dir, CLOUD_SESSION_FILE);
32921
+ return path36__default.default.join(dir, CLOUD_SESSION_FILE);
32489
32922
  }
32490
32923
  function loadCloudSession(dir = globalDir()) {
32491
32924
  try {
32492
- const raw = fs32__default.default.readFileSync(cloudSessionPath(dir), "utf-8");
32925
+ const raw = fs33__default.default.readFileSync(cloudSessionPath(dir), "utf-8");
32493
32926
  const parsed = JSON.parse(raw);
32494
32927
  if (!parsed?.refreshToken || !parsed?.serverUrl) return null;
32495
32928
  return parsed;
@@ -32498,17 +32931,17 @@ function loadCloudSession(dir = globalDir()) {
32498
32931
  }
32499
32932
  }
32500
32933
  function saveCloudSession(session, dir = globalDir()) {
32501
- fs32__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
32934
+ fs33__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
32502
32935
  const file = cloudSessionPath(dir);
32503
- fs32__default.default.writeFileSync(file, JSON.stringify(session, null, 2), { mode: 384 });
32936
+ fs33__default.default.writeFileSync(file, JSON.stringify(session, null, 2), { mode: 384 });
32504
32937
  try {
32505
- fs32__default.default.chmodSync(file, 384);
32938
+ fs33__default.default.chmodSync(file, 384);
32506
32939
  } catch {
32507
32940
  }
32508
32941
  }
32509
32942
  function clearCloudSession(dir = globalDir()) {
32510
32943
  try {
32511
- fs32__default.default.rmSync(cloudSessionPath(dir));
32944
+ fs33__default.default.rmSync(cloudSessionPath(dir));
32512
32945
  } catch {
32513
32946
  }
32514
32947
  }
@@ -32680,16 +33113,16 @@ var CloudClient = class _CloudClient {
32680
33113
  return (await this.refresh()).accessToken;
32681
33114
  }
32682
33115
  /** Authenticated request that refreshes once on a 401 (e.g. server-side revoke). */
32683
- async authed(path40, init = {}) {
32684
- const call = (token) => fetch(this.url(path40), { ...init, headers: { ...init.headers ?? {}, Authorization: `Bearer ${token}` } });
33116
+ async authed(path41, init = {}) {
33117
+ const call = (token) => fetch(this.url(path41), { ...init, headers: { ...init.headers ?? {}, Authorization: `Bearer ${token}` } });
32685
33118
  let res = await call(await this.accessToken());
32686
33119
  if (res.status === 401) res = await call((await this.refresh()).accessToken);
32687
33120
  if (!res.ok) throw new Error(`Request failed (${res.status}).`);
32688
33121
  const text = await res.text();
32689
33122
  return text ? JSON.parse(text) : {};
32690
33123
  }
32691
- authedGet(path40) {
32692
- return this.authed(path40);
33124
+ authedGet(path41) {
33125
+ return this.authed(path41);
32693
33126
  }
32694
33127
  async fetchMe(token) {
32695
33128
  const res = await fetch(this.url("/api/me"), { headers: { Authorization: `Bearer ${token}` } });
@@ -34463,9 +34896,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
34463
34896
  }
34464
34897
  } catch {
34465
34898
  }
34466
- const configPath = path35__default.default.join(workspacePath, ".cascade", "config.json");
34899
+ const configPath = path36__default.default.join(workspacePath, ".cascade", "config.json");
34467
34900
  try {
34468
- await fs11__default.default.mkdir(path35__default.default.dirname(configPath), { recursive: true });
34901
+ await fs11__default.default.mkdir(path36__default.default.dirname(configPath), { recursive: true });
34469
34902
  await fs11__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
34470
34903
  } catch (err) {
34471
34904
  const msg = err instanceof Error ? err.message : String(err);
@@ -34517,9 +34950,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
34517
34950
  if (msg.includes("non-text parts")) return;
34518
34951
  originalLog(...args);
34519
34952
  };
34520
- const store = new MemoryStore(path35__default.default.join(workspacePath, CASCADE_DB_FILE));
34953
+ const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
34521
34954
  storeRef.current = store;
34522
- globalStoreRef.current = new MemoryStore(path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
34955
+ globalStoreRef.current = new MemoryStore(path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
34523
34956
  const identityRows = store.listIdentities().map((i) => ({ id: i.id, name: i.name, isDefault: i.isDefault }));
34524
34957
  setIdentities(identityRows);
34525
34958
  let initialIdentityId = config.defaultIdentityId ?? identityRows.find((i) => i.isDefault)?.id ?? identityRows[0]?.id;
@@ -34599,7 +35032,7 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
34599
35032
  onThemeChange: (name) => setTheme(getTheme(name)),
34600
35033
  onExport: async (fmt) => {
34601
35034
  const stamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
34602
- const exportPath = path35__default.default.join(workspacePath, `cascade-export-${stamp2}.${fmt === "json" ? "json" : "md"}`);
35035
+ const exportPath = path36__default.default.join(workspacePath, `cascade-export-${stamp2}.${fmt === "json" ? "json" : "md"}`);
34603
35036
  if (fmt === "json") {
34604
35037
  await fs11__default.default.writeFile(exportPath, JSON.stringify({ sessionId: sessionIdRef.current, messages: state.messages }, null, 2), "utf-8");
34605
35038
  } else {
@@ -35519,7 +35952,7 @@ function stringifySlashOutput(val) {
35519
35952
  }
35520
35953
  async function searchSessionsAndMessages(query, workspacePath) {
35521
35954
  if (!query) return "Usage: /search <query>";
35522
- const dbPath = path35__default.default.join(workspacePath, CASCADE_DB_FILE);
35955
+ const dbPath = path36__default.default.join(workspacePath, CASCADE_DB_FILE);
35523
35956
  try {
35524
35957
  await fs11__default.default.access(dbPath);
35525
35958
  } catch {
@@ -35553,7 +35986,7 @@ async function searchSessionsAndMessages(query, workspacePath) {
35553
35986
  async function diagnoseRuntime(config, workspacePath) {
35554
35987
  const providers = config.providers.map((p) => `${p.type}${p.apiKey ? " (key set)" : " (no key)"}`).join("\n");
35555
35988
  const models = [`T1: ${config.models.t1 ?? "default"}`, `T2: ${config.models.t2 ?? "default"}`, `T3: ${config.models.t3 ?? "default"}`].join("\n");
35556
- const store = new MemoryStore(path35__default.default.join(workspacePath, CASCADE_DB_FILE));
35989
+ const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
35557
35990
  try {
35558
35991
  const sessions2 = store.listSessions(void 0, 3);
35559
35992
  return [
@@ -35572,7 +36005,7 @@ async function diagnoseRuntime(config, workspacePath) {
35572
36005
  }
35573
36006
  async function showRecentLogs(args, workspacePath) {
35574
36007
  const limit = Number.parseInt(args[0] ?? "10", 10) || 10;
35575
- const store = new MemoryStore(path35__default.default.join(workspacePath, CASCADE_DB_FILE));
36008
+ const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
35576
36009
  try {
35577
36010
  const logs = store.listRuntimeNodeLogs(void 0, void 0, limit);
35578
36011
  if (!logs.length) return "No recent runtime logs.";
@@ -35584,7 +36017,7 @@ async function showRecentLogs(args, workspacePath) {
35584
36017
  async function loadSessionSnapshot(args, workspacePath) {
35585
36018
  const sessionId = args[0];
35586
36019
  if (!sessionId) return "Usage: /resume <sessionId>";
35587
- const store = new MemoryStore(path35__default.default.join(workspacePath, CASCADE_DB_FILE));
36020
+ const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
35588
36021
  try {
35589
36022
  const session = store.getSession(sessionId);
35590
36023
  if (!session) return `Session not found: ${sessionId}`;
@@ -36004,9 +36437,9 @@ function SetupWizard({ workspacePath, onComplete }) {
36004
36437
  ...anyAuto ? { cascadeAuto: true } : {}
36005
36438
  };
36006
36439
  const config = CascadeConfigSchema.parse(rawConfig);
36007
- const configDir = path35__default.default.join(workspacePath, ".cascade");
36440
+ const configDir = path36__default.default.join(workspacePath, ".cascade");
36008
36441
  await fs11__default.default.mkdir(configDir, { recursive: true });
36009
- const configPath = path35__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
36442
+ const configPath = path36__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
36010
36443
  await fs11__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
36011
36444
  savedConfigRef.current = config;
36012
36445
  dispatchRef.current({ type: "GO_DONE" });
@@ -36347,14 +36780,14 @@ function printTelemetryBanner() {
36347
36780
  async function initCommand(workspacePath = process.cwd()) {
36348
36781
  const spin = ora__default.default({ text: "Initializing Cascade project\u2026", color: "magenta" }).start();
36349
36782
  try {
36350
- const configDir = path35__default.default.join(workspacePath, ".cascade");
36783
+ const configDir = path36__default.default.join(workspacePath, ".cascade");
36351
36784
  await fs11__default.default.mkdir(configDir, { recursive: true });
36352
- const mdPath = path35__default.default.join(workspacePath, "CASCADE.md");
36785
+ const mdPath = path36__default.default.join(workspacePath, "CASCADE.md");
36353
36786
  if (!await fileExists(mdPath)) {
36354
36787
  await createDefaultCascadeMd(workspacePath);
36355
36788
  spin.succeed(chalk8__default.default.green("Created CASCADE.md"));
36356
36789
  }
36357
- const ignorePath = path35__default.default.join(workspacePath, ".cascadeignore");
36790
+ const ignorePath = path36__default.default.join(workspacePath, ".cascadeignore");
36358
36791
  if (!await fileExists(ignorePath)) {
36359
36792
  await createDefaultIgnoreFile(workspacePath);
36360
36793
  spin.succeed(chalk8__default.default.green("Created .cascadeignore"));
@@ -36363,7 +36796,7 @@ async function initCommand(workspacePath = process.cwd()) {
36363
36796
  console.log();
36364
36797
  console.log(chalk8__default.default.magenta(" \u25C8 Cascade AI \u2014 Project initialized"));
36365
36798
  console.log();
36366
- const configPath = path35__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
36799
+ const configPath = path36__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
36367
36800
  if (await fileExists(configPath)) {
36368
36801
  console.log(chalk8__default.default.yellow(" .cascade/config.json already exists \u2014 launching wizard to reconfigure."));
36369
36802
  console.log();
@@ -36514,8 +36947,8 @@ function fromEnv(env) {
36514
36947
  return out;
36515
36948
  }
36516
36949
  async function fromClaudeCode(home, env) {
36517
- const dir = str(env["CLAUDE_CONFIG_DIR"]) ?? path35__default.default.join(home, ".claude");
36518
- const file = path35__default.default.join(dir, ".credentials.json");
36950
+ const dir = str(env["CLAUDE_CONFIG_DIR"]) ?? path36__default.default.join(home, ".claude");
36951
+ const file = path36__default.default.join(dir, ".credentials.json");
36519
36952
  const data = await readJson(file);
36520
36953
  if (!data) return [];
36521
36954
  const found = [];
@@ -36539,7 +36972,7 @@ async function fromClaudeCode(home, env) {
36539
36972
  return found;
36540
36973
  }
36541
36974
  async function fromCodex(home) {
36542
- const file = path35__default.default.join(home, ".codex", "auth.json");
36975
+ const file = path36__default.default.join(home, ".codex", "auth.json");
36543
36976
  const data = await readJson(file);
36544
36977
  if (!data) return [];
36545
36978
  const apiKey = str(data["OPENAI_API_KEY"]);
@@ -36562,7 +36995,7 @@ async function fromCodex(home) {
36562
36995
  return [];
36563
36996
  }
36564
36997
  async function fromGemini(home) {
36565
- const file = path35__default.default.join(home, ".gemini", "oauth_creds.json");
36998
+ const file = path36__default.default.join(home, ".gemini", "oauth_creds.json");
36566
36999
  const data = await readJson(file);
36567
37000
  const accessToken = str(data?.["access_token"]);
36568
37001
  if (!accessToken) return [];
@@ -36578,7 +37011,7 @@ async function fromGemini(home) {
36578
37011
  }
36579
37012
  async function fromCopilot(home) {
36580
37013
  for (const name of ["apps.json", "hosts.json"]) {
36581
- const file = path35__default.default.join(home, ".config", "github-copilot", name);
37014
+ const file = path36__default.default.join(home, ".config", "github-copilot", name);
36582
37015
  const data = await readJson(file);
36583
37016
  if (!data) continue;
36584
37017
  for (const value of Object.values(data)) {
@@ -36879,7 +37312,7 @@ async function doctorCommand() {
36879
37312
  checks.push({
36880
37313
  label: "Cascade config",
36881
37314
  ok: true,
36882
- detail: `Loaded ${path35__default.default.join(process.cwd(), CASCADE_CONFIG_FILE)}`
37315
+ detail: `Loaded ${path36__default.default.join(process.cwd(), CASCADE_CONFIG_FILE)}`
36883
37316
  });
36884
37317
  const providers = [
36885
37318
  { type: "anthropic", name: "Anthropic" },
@@ -36989,7 +37422,7 @@ async function checkEndpoint(url) {
36989
37422
  }
36990
37423
  }
36991
37424
  async function indexCommand(dirPath) {
36992
- const workspace = path35__default.default.resolve(dirPath || process.cwd());
37425
+ const workspace = path36__default.default.resolve(dirPath || process.cwd());
36993
37426
  const cm = new ConfigManager(workspace);
36994
37427
  await cm.load();
36995
37428
  const config = cm.getConfig();
@@ -37000,8 +37433,8 @@ async function indexCommand(dirPath) {
37000
37433
  process.exitCode = 1;
37001
37434
  return;
37002
37435
  }
37003
- const dbPath = config.codeIndex?.dbPath || path35__default.default.join(workspace, ".cascade", "code-index.db");
37004
- fs32__default.default.mkdirSync(path35__default.default.dirname(dbPath), { recursive: true });
37436
+ const dbPath = config.codeIndex?.dbPath || path36__default.default.join(workspace, ".cascade", "code-index.db");
37437
+ fs33__default.default.mkdirSync(path36__default.default.dirname(dbPath), { recursive: true });
37005
37438
  const db = new Database2__default.default(dbPath);
37006
37439
  db.pragma("journal_mode = WAL");
37007
37440
  const complete = chatCompleterFromProviders(config.providers);
@@ -37533,7 +37966,7 @@ function openBrowser2(url) {
37533
37966
  }
37534
37967
  function storePathFor(name) {
37535
37968
  const safe = name.replace(/[^a-z0-9._-]/gi, "_").slice(0, 64) || "server";
37536
- return path35__default.default.join(os10__default.default.homedir(), ".cascade-ai", "mcp-oauth", `${safe}.json`);
37969
+ return path36__default.default.join(os10__default.default.homedir(), ".cascade-ai", "mcp-oauth", `${safe}.json`);
37537
37970
  }
37538
37971
  function defaultName(url) {
37539
37972
  try {
@@ -37920,8 +38353,8 @@ function replaceInPlace(target, source) {
37920
38353
  }
37921
38354
  function writeConfigFile(configPath, config) {
37922
38355
  try {
37923
- fs32__default.default.mkdirSync(path35__default.default.dirname(configPath), { recursive: true });
37924
- fs32__default.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
38356
+ fs33__default.default.mkdirSync(path36__default.default.dirname(configPath), { recursive: true });
38357
+ fs33__default.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
37925
38358
  return { ok: true };
37926
38359
  } catch (err) {
37927
38360
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
@@ -38337,7 +38770,7 @@ function aggregateCostStats(sessions2, opts = {}) {
38337
38770
  }
38338
38771
 
38339
38772
  // src/dashboard/server.ts
38340
- var __dirname$1 = path35__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
38773
+ var __dirname$1 = path36__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
38341
38774
  var ORPHANED_ESCALATION_GRACE_MS = 3e4;
38342
38775
  var MASK = "***";
38343
38776
  function masked(key, value) {
@@ -38656,7 +39089,7 @@ var DashboardServer = class {
38656
39089
  * silently and still typecheck.
38657
39090
  */
38658
39091
  persistConfig(config) {
38659
- const result = writeConfigFile(path35__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE), config);
39092
+ const result = writeConfigFile(path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE), config);
38660
39093
  if (!result.ok) console.warn(`[dashboard] Failed to persist config: ${result.error}`);
38661
39094
  return result;
38662
39095
  }
@@ -38675,7 +39108,7 @@ var DashboardServer = class {
38675
39108
  */
38676
39109
  syncGlobalCredentials() {
38677
39110
  try {
38678
- saveGlobalCredentials(path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
39111
+ saveGlobalCredentials(path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
38679
39112
  } catch (err) {
38680
39113
  console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
38681
39114
  }
@@ -38690,15 +39123,15 @@ var DashboardServer = class {
38690
39123
  resolveDashboardSecret() {
38691
39124
  const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
38692
39125
  if (fromConfig) return fromConfig;
38693
- const secretPath = path35__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
39126
+ const secretPath = path36__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
38694
39127
  try {
38695
- if (fs32__default.default.existsSync(secretPath)) {
38696
- const existing = fs32__default.default.readFileSync(secretPath, "utf-8").trim();
39128
+ if (fs33__default.default.existsSync(secretPath)) {
39129
+ const existing = fs33__default.default.readFileSync(secretPath, "utf-8").trim();
38697
39130
  if (existing.length >= 16) return existing;
38698
39131
  }
38699
39132
  const generated = crypto.randomUUID();
38700
- fs32__default.default.mkdirSync(path35__default.default.dirname(secretPath), { recursive: true });
38701
- fs32__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
39133
+ fs33__default.default.mkdirSync(path36__default.default.dirname(secretPath), { recursive: true });
39134
+ fs33__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
38702
39135
  if (this.config.dashboard.auth) {
38703
39136
  console.warn(
38704
39137
  `Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
@@ -38725,7 +39158,7 @@ var DashboardServer = class {
38725
39158
  // ── Setup ─────────────────────────────────────
38726
39159
  getGlobalStore() {
38727
39160
  if (!this.globalStore) {
38728
- const globalDbPath = path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39161
+ const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
38729
39162
  this.globalStore = new MemoryStore(globalDbPath);
38730
39163
  }
38731
39164
  return this.globalStore;
@@ -39105,12 +39538,12 @@ ${prompt}`;
39105
39538
  }
39106
39539
  }
39107
39540
  watchRuntimeChanges() {
39108
- const workspaceDbPath = path35__default.default.join(this.workspacePath, CASCADE_DB_FILE);
39109
- const globalDbPath = path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39541
+ const workspaceDbPath = path36__default.default.join(this.workspacePath, CASCADE_DB_FILE);
39542
+ const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39110
39543
  const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
39111
39544
  for (const watchPath of watchPaths) {
39112
- if (!fs32__default.default.existsSync(watchPath)) continue;
39113
- fs32__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
39545
+ if (!fs33__default.default.existsSync(watchPath)) continue;
39546
+ fs33__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
39114
39547
  this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
39115
39548
  });
39116
39549
  }
@@ -39232,7 +39665,7 @@ ${prompt}`;
39232
39665
  const sessionId = req.params.id;
39233
39666
  this.store.deleteSession(sessionId);
39234
39667
  this.store.deleteRuntimeSession(sessionId);
39235
- const globalDbPath = path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39668
+ const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39236
39669
  const globalStore = new MemoryStore(globalDbPath);
39237
39670
  try {
39238
39671
  globalStore.deleteRuntimeSession(sessionId);
@@ -39325,7 +39758,7 @@ ${prompt}`;
39325
39758
  });
39326
39759
  this.app.delete("/api/sessions", auth2, (req, res) => {
39327
39760
  const body = req.body;
39328
- const globalDbPath = path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39761
+ const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39329
39762
  if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
39330
39763
  const globalStore = new MemoryStore(globalDbPath);
39331
39764
  try {
@@ -39348,7 +39781,7 @@ ${prompt}`;
39348
39781
  });
39349
39782
  this.app.delete("/api/runtime", auth2, (_req, res) => {
39350
39783
  this.store.deleteAllRuntimeNodes();
39351
- const globalDbPath = path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39784
+ const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39352
39785
  const globalStore = new MemoryStore(globalDbPath);
39353
39786
  try {
39354
39787
  globalStore.deleteAllRuntimeNodes();
@@ -39433,12 +39866,12 @@ ${prompt}`;
39433
39866
  if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
39434
39867
  if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
39435
39868
  try {
39436
- const configPath = path35__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
39437
- const existing = fs32__default.default.existsSync(configPath) ? JSON.parse(fs32__default.default.readFileSync(configPath, "utf-8")) : {};
39869
+ const configPath = path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
39870
+ const existing = fs33__default.default.existsSync(configPath) ? JSON.parse(fs33__default.default.readFileSync(configPath, "utf-8")) : {};
39438
39871
  const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
39439
39872
  const tmp = configPath + ".tmp";
39440
- fs32__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
39441
- fs32__default.default.renameSync(tmp, configPath);
39873
+ fs33__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
39874
+ fs33__default.default.renameSync(tmp, configPath);
39442
39875
  res.json({ ok: true });
39443
39876
  } catch (err) {
39444
39877
  res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
@@ -39466,7 +39899,7 @@ ${prompt}`;
39466
39899
  this.app.get("/api/runtime", auth2, (req, res) => {
39467
39900
  const scope = req.query["scope"] ?? "workspace";
39468
39901
  if (scope === "global") {
39469
- const globalDbPath = path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39902
+ const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
39470
39903
  const globalStore = new MemoryStore(globalDbPath);
39471
39904
  try {
39472
39905
  res.json({
@@ -39807,13 +40240,13 @@ ${prompt}`;
39807
40240
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
39808
40241
  }
39809
40242
  });
39810
- const prodPath = path35__default.default.resolve(__dirname$1, "../web/dist");
39811
- const devPath = path35__default.default.resolve(__dirname$1, "../../web/dist");
39812
- const webDistPath = fs32__default.default.existsSync(prodPath) ? prodPath : devPath;
39813
- if (fs32__default.default.existsSync(webDistPath)) {
40243
+ const prodPath = path36__default.default.resolve(__dirname$1, "../web/dist");
40244
+ const devPath = path36__default.default.resolve(__dirname$1, "../../web/dist");
40245
+ const webDistPath = fs33__default.default.existsSync(prodPath) ? prodPath : devPath;
40246
+ if (fs33__default.default.existsSync(webDistPath)) {
39814
40247
  this.app.use(express__default.default.static(webDistPath));
39815
40248
  this.app.get("*", (_req, res) => {
39816
- res.sendFile(path35__default.default.join(webDistPath, "index.html"));
40249
+ res.sendFile(path36__default.default.join(webDistPath, "index.html"));
39817
40250
  });
39818
40251
  } else {
39819
40252
  this.app.get("/", (_req, res) => {
@@ -39834,7 +40267,7 @@ init_constants();
39834
40267
  async function dashboardCommand(config, workspacePath = process.cwd()) {
39835
40268
  const port = config.dashboard.port ?? DEFAULT_DASHBOARD_PORT;
39836
40269
  const spin = ora__default.default({ text: `Starting dashboard on port ${port}\u2026`, color: "magenta" }).start();
39837
- const store = new MemoryStore(path35__default.default.join(workspacePath, CASCADE_DB_FILE));
40270
+ const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
39838
40271
  const server = new DashboardServer(config, store, workspacePath);
39839
40272
  server.watchRuntimeChanges();
39840
40273
  const gThis = globalThis;
@@ -39866,7 +40299,7 @@ init_constants();
39866
40299
  function makeIdentityCommand(workspacePath = process.cwd()) {
39867
40300
  const identity = new commander.Command("identity").alias("id").description("Manage Cascade identities");
39868
40301
  identity.command("list").description("List all available identities").action(() => {
39869
- const store = new MemoryStore(path35__default.default.join(workspacePath, CASCADE_DB_FILE));
40302
+ const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
39870
40303
  try {
39871
40304
  const identities = store.listIdentities();
39872
40305
  console.log(chalk8__default.default.bold("\n Identities:"));
@@ -39886,7 +40319,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
39886
40319
  }
39887
40320
  });
39888
40321
  identity.command("create <name>").description("Create a new identity").option("-d, --desc <text>", "Description of the identity").option("-s, --system <text>", "System prompt").option("--default", "Set as default identity").action((name, options) => {
39889
- const store = new MemoryStore(path35__default.default.join(workspacePath, CASCADE_DB_FILE));
40322
+ const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
39890
40323
  try {
39891
40324
  if (options.default) {
39892
40325
  const existingDefault = store.getDefaultIdentity();
@@ -39912,7 +40345,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
39912
40345
  }
39913
40346
  });
39914
40347
  identity.command("set-default <name>").description("Set an identity as default by name or ID").action((query) => {
39915
- const store = new MemoryStore(path35__default.default.join(workspacePath, CASCADE_DB_FILE));
40348
+ const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
39916
40349
  try {
39917
40350
  const identities = store.listIdentities();
39918
40351
  const match = identities.find((i) => i.id === query || i.name.toLowerCase() === query.toLowerCase());
@@ -40061,8 +40494,8 @@ async function exportCommand(options = {}) {
40061
40494
  let store;
40062
40495
  try {
40063
40496
  const workspacePath = options.workspacePath ?? process.cwd();
40064
- const workspaceDbPath = path35__default.default.join(workspacePath, CASCADE_DB_FILE);
40065
- const globalDbPath = path35__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
40497
+ const workspaceDbPath = path36__default.default.join(workspacePath, CASCADE_DB_FILE);
40498
+ const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
40066
40499
  let dbPath = globalDbPath;
40067
40500
  try {
40068
40501
  await fs11__default.default.access(workspaceDbPath);
@@ -40104,7 +40537,7 @@ async function exportCommand(options = {}) {
40104
40537
  const ext = format === "json" ? ".json" : ".md";
40105
40538
  const safeName = session.title.replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").slice(0, 60);
40106
40539
  const defaultFile = `cascade-export-${safeName}${ext}`;
40107
- const outPath = options.output ? path35__default.default.resolve(options.output) : path35__default.default.join(process.cwd(), defaultFile);
40540
+ const outPath = options.output ? path36__default.default.resolve(options.output) : path36__default.default.join(process.cwd(), defaultFile);
40108
40541
  await fs11__default.default.writeFile(outPath, content, "utf-8");
40109
40542
  spin.succeed(chalk8__default.default.green(`Exported to ${chalk8__default.default.white(outPath)}`));
40110
40543
  const messageCount = Array.isArray(session.messages) ? session.messages.length : 0;
@@ -40244,7 +40677,7 @@ var SHOW_CURSOR = "\x1B[?25h";
40244
40677
  function writeNow(stdout, text) {
40245
40678
  if (typeof stdout.fd === "number") {
40246
40679
  try {
40247
- fs32__default.default.writeSync(stdout.fd, text);
40680
+ fs33__default.default.writeSync(stdout.fd, text);
40248
40681
  return;
40249
40682
  } catch {
40250
40683
  }
@@ -40267,11 +40700,11 @@ function restoreTerminal(stdin, stdout) {
40267
40700
  dotenv__default.default.config();
40268
40701
  function warnIfBuildIsStale() {
40269
40702
  try {
40270
- const here = path35__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
40703
+ const here = path36__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
40271
40704
  for (const rel of ["..", "../.."]) {
40272
- const pkgPath = path35__default.default.join(here, rel, "package.json");
40273
- if (!fs32__default.default.existsSync(pkgPath)) continue;
40274
- const pkg = JSON.parse(fs32__default.default.readFileSync(pkgPath, "utf-8"));
40705
+ const pkgPath = path36__default.default.join(here, rel, "package.json");
40706
+ if (!fs33__default.default.existsSync(pkgPath)) continue;
40707
+ const pkg = JSON.parse(fs33__default.default.readFileSync(pkgPath, "utf-8"));
40275
40708
  if (pkg.name !== "cascade-ai") continue;
40276
40709
  if (pkg.version && pkg.version !== CASCADE_VERSION) {
40277
40710
  console.error(