cascade-ai 0.76.0 → 0.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli.cjs +1215 -518
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1198 -501
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +24605 -3171
- package/dist/index.cjs +1167 -478
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1390 -16
- package/dist/index.d.ts +1390 -16
- package/dist/index.js +1149 -462
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/web/dist/assets/index-DoU1RNKq.js +246 -0
- package/web/dist/assets/react-DmRRAgLv.js +1 -0
- package/web/dist/assets/{reactflow-Clz8xC7C.js → reactflow-CkXCC9kc.js} +2 -2
- package/web/dist/assets/{socketio-BcxXcwBL.js → socketio-SYmKJpIj.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/dist/assets/index-DFRrUnoJ.js +0 -246
- package/web/dist/assets/react-BP1N17hq.js +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
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
|
|
13
|
-
var
|
|
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
|
|
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
|
|
81
|
-
var
|
|
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.
|
|
1852
|
+
CASCADE_VERSION = "0.78.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-
|
|
1866
|
-
id: "claude-
|
|
1867
|
-
name: "Claude
|
|
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:
|
|
1870
|
+
contextWindow: 1e6,
|
|
1870
1871
|
isVisionCapable: true,
|
|
1871
|
-
inputCostPer1kTokens: 0.
|
|
1872
|
-
outputCostPer1kTokens: 0.
|
|
1873
|
-
maxOutputTokens:
|
|
1872
|
+
inputCostPer1kTokens: 0.01,
|
|
1873
|
+
outputCostPer1kTokens: 0.05,
|
|
1874
|
+
maxOutputTokens: 128e3,
|
|
1874
1875
|
supportsStreaming: true,
|
|
1875
1876
|
isLocal: false
|
|
1876
1877
|
},
|
|
1877
|
-
"claude-
|
|
1878
|
-
id: "claude-
|
|
1879
|
-
name: "Claude
|
|
1878
|
+
"claude-opus-5": {
|
|
1879
|
+
id: "claude-opus-5",
|
|
1880
|
+
name: "Claude Opus 5",
|
|
1880
1881
|
provider: "anthropic",
|
|
1881
|
-
contextWindow:
|
|
1882
|
+
contextWindow: 1e6,
|
|
1882
1883
|
isVisionCapable: true,
|
|
1883
|
-
inputCostPer1kTokens:
|
|
1884
|
-
outputCostPer1kTokens: 0.
|
|
1885
|
-
maxOutputTokens:
|
|
1884
|
+
inputCostPer1kTokens: 5e-3,
|
|
1885
|
+
outputCostPer1kTokens: 0.025,
|
|
1886
|
+
maxOutputTokens: 128e3,
|
|
1886
1887
|
supportsStreaming: true,
|
|
1887
1888
|
isLocal: false
|
|
1888
1889
|
},
|
|
1889
|
-
"claude-
|
|
1890
|
-
id: "claude-
|
|
1891
|
-
name: "Claude
|
|
1890
|
+
"claude-opus-4-8": {
|
|
1891
|
+
id: "claude-opus-4-8",
|
|
1892
|
+
name: "Claude Opus 4.8",
|
|
1892
1893
|
provider: "anthropic",
|
|
1893
|
-
contextWindow:
|
|
1894
|
+
contextWindow: 1e6,
|
|
1894
1895
|
isVisionCapable: true,
|
|
1895
|
-
inputCostPer1kTokens:
|
|
1896
|
-
outputCostPer1kTokens:
|
|
1897
|
-
maxOutputTokens:
|
|
1896
|
+
inputCostPer1kTokens: 5e-3,
|
|
1897
|
+
outputCostPer1kTokens: 0.025,
|
|
1898
|
+
maxOutputTokens: 128e3,
|
|
1898
1899
|
supportsStreaming: true,
|
|
1899
1900
|
isLocal: false
|
|
1900
1901
|
},
|
|
1901
|
-
"claude-
|
|
1902
|
-
id: "claude-
|
|
1903
|
-
name: "Claude
|
|
1902
|
+
"claude-opus-4-7": {
|
|
1903
|
+
id: "claude-opus-4-7",
|
|
1904
|
+
name: "Claude Opus 4.7",
|
|
1904
1905
|
provider: "anthropic",
|
|
1905
|
-
contextWindow:
|
|
1906
|
+
contextWindow: 1e6,
|
|
1906
1907
|
isVisionCapable: true,
|
|
1907
|
-
inputCostPer1kTokens:
|
|
1908
|
-
outputCostPer1kTokens:
|
|
1909
|
-
maxOutputTokens:
|
|
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-
|
|
1914
|
-
id: "claude-opus-4-
|
|
1915
|
-
name: "Claude Opus 4.
|
|
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:
|
|
1920
|
-
outputCostPer1kTokens: 0.
|
|
1921
|
-
maxOutputTokens:
|
|
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:
|
|
1970
|
+
maxOutputTokens: 64e3,
|
|
1934
1971
|
supportsStreaming: true,
|
|
1935
1972
|
isLocal: false
|
|
1936
1973
|
},
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
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:
|
|
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.
|
|
1946
|
-
maxOutputTokens:
|
|
2030
|
+
outputCostPer1kTokens: 0.03,
|
|
2031
|
+
maxOutputTokens: 128e3,
|
|
1947
2032
|
supportsStreaming: true,
|
|
1948
2033
|
isLocal: false
|
|
1949
2034
|
},
|
|
1950
|
-
"gpt-
|
|
1951
|
-
id: "gpt-
|
|
1952
|
-
name: "GPT-
|
|
2035
|
+
"gpt-5.4": {
|
|
2036
|
+
id: "gpt-5.4",
|
|
2037
|
+
name: "GPT-5.4",
|
|
1953
2038
|
provider: "openai",
|
|
1954
|
-
contextWindow:
|
|
2039
|
+
contextWindow: 105e4,
|
|
1955
2040
|
isVisionCapable: true,
|
|
1956
|
-
inputCostPer1kTokens:
|
|
1957
|
-
outputCostPer1kTokens:
|
|
1958
|
-
maxOutputTokens:
|
|
2041
|
+
inputCostPer1kTokens: 25e-4,
|
|
2042
|
+
outputCostPer1kTokens: 0.015,
|
|
2043
|
+
maxOutputTokens: 128e3,
|
|
1959
2044
|
supportsStreaming: true,
|
|
1960
2045
|
isLocal: false
|
|
1961
2046
|
},
|
|
1962
|
-
"gpt-4
|
|
1963
|
-
id: "gpt-4
|
|
1964
|
-
name: "GPT-4
|
|
2047
|
+
"gpt-5.4-mini": {
|
|
2048
|
+
id: "gpt-5.4-mini",
|
|
2049
|
+
name: "GPT-5.4 Mini",
|
|
1965
2050
|
provider: "openai",
|
|
1966
|
-
contextWindow:
|
|
2051
|
+
contextWindow: 4e5,
|
|
1967
2052
|
isVisionCapable: true,
|
|
1968
|
-
inputCostPer1kTokens:
|
|
1969
|
-
outputCostPer1kTokens:
|
|
1970
|
-
maxOutputTokens:
|
|
2053
|
+
inputCostPer1kTokens: 75e-5,
|
|
2054
|
+
outputCostPer1kTokens: 45e-4,
|
|
2055
|
+
maxOutputTokens: 128e3,
|
|
1971
2056
|
supportsStreaming: true,
|
|
1972
2057
|
isLocal: false
|
|
1973
2058
|
},
|
|
1974
|
-
"gpt-4
|
|
1975
|
-
id: "gpt-4
|
|
1976
|
-
name: "GPT-4
|
|
2059
|
+
"gpt-5.4-nano": {
|
|
2060
|
+
id: "gpt-5.4-nano",
|
|
2061
|
+
name: "GPT-5.4 Nano",
|
|
1977
2062
|
provider: "openai",
|
|
1978
|
-
contextWindow:
|
|
2063
|
+
contextWindow: 4e5,
|
|
1979
2064
|
isVisionCapable: true,
|
|
1980
|
-
inputCostPer1kTokens:
|
|
1981
|
-
outputCostPer1kTokens:
|
|
1982
|
-
maxOutputTokens:
|
|
2065
|
+
inputCostPer1kTokens: 2e-4,
|
|
2066
|
+
outputCostPer1kTokens: 125e-5,
|
|
2067
|
+
maxOutputTokens: 128e3,
|
|
1983
2068
|
supportsStreaming: true,
|
|
1984
2069
|
isLocal: false
|
|
1985
2070
|
},
|
|
1986
|
-
"gpt-
|
|
1987
|
-
id: "gpt-
|
|
1988
|
-
name: "GPT-
|
|
2071
|
+
"gpt-5.2": {
|
|
2072
|
+
id: "gpt-5.2",
|
|
2073
|
+
name: "GPT-5.2",
|
|
1989
2074
|
provider: "openai",
|
|
1990
|
-
contextWindow:
|
|
2075
|
+
contextWindow: 4e5,
|
|
1991
2076
|
isVisionCapable: true,
|
|
1992
|
-
inputCostPer1kTokens:
|
|
1993
|
-
outputCostPer1kTokens:
|
|
1994
|
-
maxOutputTokens:
|
|
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
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
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:
|
|
2123
|
+
contextWindow: 1047576,
|
|
2043
2124
|
isVisionCapable: true,
|
|
2044
|
-
inputCostPer1kTokens:
|
|
2045
|
-
outputCostPer1kTokens:
|
|
2046
|
-
maxOutputTokens:
|
|
2125
|
+
inputCostPer1kTokens: 2e-3,
|
|
2126
|
+
outputCostPer1kTokens: 8e-3,
|
|
2127
|
+
maxOutputTokens: 32768,
|
|
2047
2128
|
supportsStreaming: true,
|
|
2048
2129
|
isLocal: false
|
|
2049
2130
|
},
|
|
2050
|
-
"gpt-
|
|
2051
|
-
id: "gpt-
|
|
2052
|
-
name: "GPT-
|
|
2131
|
+
"gpt-4.1-mini": {
|
|
2132
|
+
id: "gpt-4.1-mini",
|
|
2133
|
+
name: "GPT-4.1 Mini",
|
|
2053
2134
|
provider: "openai",
|
|
2054
|
-
contextWindow:
|
|
2135
|
+
contextWindow: 1047576,
|
|
2055
2136
|
isVisionCapable: true,
|
|
2056
|
-
inputCostPer1kTokens:
|
|
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:
|
|
2163
|
+
maxOutputTokens: 16e3,
|
|
2059
2164
|
supportsStreaming: true,
|
|
2060
2165
|
isLocal: false
|
|
2061
2166
|
},
|
|
2062
|
-
"gpt-
|
|
2063
|
-
id: "gpt-
|
|
2064
|
-
name: "GPT-
|
|
2167
|
+
"gpt-4o-mini": {
|
|
2168
|
+
id: "gpt-4o-mini",
|
|
2169
|
+
name: "GPT-4o Mini",
|
|
2065
2170
|
provider: "openai",
|
|
2066
|
-
contextWindow:
|
|
2171
|
+
contextWindow: 128e3,
|
|
2067
2172
|
isVisionCapable: true,
|
|
2068
|
-
inputCostPer1kTokens:
|
|
2069
|
-
outputCostPer1kTokens:
|
|
2070
|
-
maxOutputTokens:
|
|
2173
|
+
inputCostPer1kTokens: 15e-5,
|
|
2174
|
+
outputCostPer1kTokens: 6e-4,
|
|
2175
|
+
maxOutputTokens: 16e3,
|
|
2071
2176
|
supportsStreaming: true,
|
|
2072
2177
|
isLocal: false
|
|
2073
2178
|
},
|
|
2074
|
-
// Google
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
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:
|
|
2185
|
+
contextWindow: 1048576,
|
|
2080
2186
|
isVisionCapable: true,
|
|
2081
|
-
inputCostPer1kTokens:
|
|
2082
|
-
outputCostPer1kTokens:
|
|
2083
|
-
maxOutputTokens:
|
|
2187
|
+
inputCostPer1kTokens: 75e-5,
|
|
2188
|
+
outputCostPer1kTokens: 375e-5,
|
|
2189
|
+
maxOutputTokens: 65536,
|
|
2084
2190
|
supportsStreaming: true,
|
|
2085
2191
|
isLocal: false
|
|
2086
2192
|
},
|
|
2087
|
-
"gemini-
|
|
2088
|
-
id: "gemini-
|
|
2089
|
-
name: "Gemini
|
|
2193
|
+
"gemini-3.6-flash": {
|
|
2194
|
+
id: "gemini-3.6-flash",
|
|
2195
|
+
name: "Gemini 3.6 Flash",
|
|
2090
2196
|
provider: "gemini",
|
|
2091
|
-
contextWindow:
|
|
2197
|
+
contextWindow: 1048576,
|
|
2092
2198
|
isVisionCapable: true,
|
|
2093
|
-
inputCostPer1kTokens:
|
|
2094
|
-
outputCostPer1kTokens:
|
|
2095
|
-
maxOutputTokens:
|
|
2199
|
+
inputCostPer1kTokens: 75e-5,
|
|
2200
|
+
outputCostPer1kTokens: 375e-5,
|
|
2201
|
+
maxOutputTokens: 65536,
|
|
2096
2202
|
supportsStreaming: true,
|
|
2097
2203
|
isLocal: false
|
|
2098
2204
|
},
|
|
2099
|
-
"gemini-
|
|
2100
|
-
id: "gemini-
|
|
2101
|
-
name: "Gemini
|
|
2205
|
+
"gemini-3.5-flash": {
|
|
2206
|
+
id: "gemini-3.5-flash",
|
|
2207
|
+
name: "Gemini 3.5 Flash",
|
|
2102
2208
|
provider: "gemini",
|
|
2103
|
-
contextWindow:
|
|
2209
|
+
contextWindow: 1048576,
|
|
2104
2210
|
isVisionCapable: true,
|
|
2105
|
-
inputCostPer1kTokens:
|
|
2106
|
-
outputCostPer1kTokens:
|
|
2107
|
-
maxOutputTokens:
|
|
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:
|
|
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:
|
|
2281
|
+
contextWindow: 1048576,
|
|
2128
2282
|
isVisionCapable: true,
|
|
2129
|
-
inputCostPer1kTokens:
|
|
2130
|
-
outputCostPer1kTokens:
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
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
|
|
2294
|
-
return `${u.protocol}//${u.host.toLowerCase()}${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3190
|
-
|
|
3191
|
-
...azurePrice.unknown ? {} : {
|
|
3465
|
+
supportsToolUse: nativeToolsOnThisTransport,
|
|
3466
|
+
...!azurePrice.unknown ? {
|
|
3192
3467
|
inputCostPer1kTokens: azurePrice.input,
|
|
3193
3468
|
outputCostPer1kTokens: azurePrice.output,
|
|
3194
|
-
pricingUnknown:
|
|
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
|
-
|
|
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
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
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
|
|
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/
|
|
4496
|
+
// node_modules/readable-stream/lib/internal/streams/stream.js
|
|
4227
4497
|
var require_stream = __commonJS({
|
|
4228
|
-
"node_modules/
|
|
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/
|
|
4503
|
+
// node_modules/readable-stream/node_modules/safe-buffer/index.js
|
|
4234
4504
|
var require_safe_buffer = __commonJS({
|
|
4235
|
-
"node_modules/
|
|
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/
|
|
4676
|
+
// node_modules/readable-stream/lib/internal/streams/BufferList.js
|
|
4407
4677
|
var require_BufferList = __commonJS({
|
|
4408
|
-
"node_modules/
|
|
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/
|
|
4753
|
+
// node_modules/readable-stream/lib/internal/streams/destroy.js
|
|
4484
4754
|
var require_destroy = __commonJS({
|
|
4485
|
-
"node_modules/
|
|
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/
|
|
4828
|
+
// node_modules/readable-stream/lib/_stream_writable.js
|
|
4559
4829
|
var require_stream_writable = __commonJS({
|
|
4560
|
-
"node_modules/
|
|
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/
|
|
5267
|
+
// node_modules/readable-stream/lib/_stream_duplex.js
|
|
4998
5268
|
var require_stream_duplex = __commonJS({
|
|
4999
|
-
"node_modules/
|
|
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/
|
|
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/
|
|
5076
|
-
var Buffer2 =
|
|
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/
|
|
5637
|
+
// node_modules/readable-stream/lib/_stream_readable.js
|
|
5311
5638
|
var require_stream_readable = __commonJS({
|
|
5312
|
-
"node_modules/
|
|
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/
|
|
6322
|
+
// node_modules/readable-stream/lib/_stream_transform.js
|
|
5996
6323
|
var require_stream_transform = __commonJS({
|
|
5997
|
-
"node_modules/
|
|
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/
|
|
6421
|
+
// node_modules/readable-stream/lib/_stream_passthrough.js
|
|
6095
6422
|
var require_stream_passthrough = __commonJS({
|
|
6096
|
-
"node_modules/
|
|
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/
|
|
6439
|
+
// node_modules/readable-stream/readable.js
|
|
6113
6440
|
var require_readable = __commonJS({
|
|
6114
|
-
"node_modules/
|
|
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(
|
|
7024
|
-
var parts =
|
|
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(
|
|
12840
|
-
if (
|
|
12841
|
-
|
|
13166
|
+
var parentFolder = function(path41) {
|
|
13167
|
+
if (path41.slice(-1) === "/") {
|
|
13168
|
+
path41 = path41.substring(0, path41.length - 1);
|
|
12842
13169
|
}
|
|
12843
|
-
var lastSlash =
|
|
12844
|
-
return lastSlash > 0 ?
|
|
13170
|
+
var lastSlash = path41.lastIndexOf("/");
|
|
13171
|
+
return lastSlash > 0 ? path41.substring(0, lastSlash) : "";
|
|
12845
13172
|
};
|
|
12846
|
-
var forceTrailingSlash = function(
|
|
12847
|
-
if (
|
|
12848
|
-
|
|
13173
|
+
var forceTrailingSlash = function(path41) {
|
|
13174
|
+
if (path41.slice(-1) !== "/") {
|
|
13175
|
+
path41 += "/";
|
|
12849
13176
|
}
|
|
12850
|
-
return
|
|
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 =
|
|
13850
|
-
if (!
|
|
13851
|
-
|
|
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 =
|
|
13854
|
-
this.dbPath =
|
|
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 (
|
|
13885
|
-
this.encryptionKey =
|
|
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
|
-
|
|
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 =
|
|
14293
|
+
const dumpPath = path36__default.default.join(os10__default.default.tmpdir(), "cascade_audit_logs_debug.json");
|
|
13967
14294
|
const entries = this.getAllLogs();
|
|
13968
|
-
|
|
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 &&
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
14137
|
-
|
|
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
|
-
|
|
14148
|
-
|
|
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 =
|
|
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 =
|
|
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 ?
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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,13 +15522,38 @@ 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([]),
|
|
15202
15540
|
shellBlocklist: zod.z.array(zod.z.string()).default(["rm -rf", "sudo rm", "format", "mkfs"]),
|
|
15203
15541
|
requireApprovalFor: zod.z.array(zod.z.string()).default([]),
|
|
15204
15542
|
browserEnabled: zod.z.boolean().default(false),
|
|
15543
|
+
/**
|
|
15544
|
+
* Let a run ACT on the page open in the desktop's built-in browser — click,
|
|
15545
|
+
* type, submit — not just read it.
|
|
15546
|
+
*
|
|
15547
|
+
* Distinct from `browserEnabled`, which turns on the headless Playwright
|
|
15548
|
+
* tool. That one drives a throwaway browser with an empty cookie jar; this
|
|
15549
|
+
* one drives the session the user is signed into, which is a different order
|
|
15550
|
+
* of consequence and so gets its own switch rather than riding on that one.
|
|
15551
|
+
*
|
|
15552
|
+
* Off by default. Turning it on does not hand anything over silently: the
|
|
15553
|
+
* tool still goes through the approval chain, and the browser panel carries a
|
|
15554
|
+
* stop control for the run that is acting.
|
|
15555
|
+
*/
|
|
15556
|
+
agentBrowserControl: zod.z.boolean().default(false),
|
|
15205
15557
|
mcpServers: zod.z.array(McpServerConfigSchema).optional(),
|
|
15206
15558
|
mcpTrusted: zod.z.array(zod.z.string()).optional(),
|
|
15207
15559
|
/** Web search backends — at least one should be configured for best results */
|
|
@@ -15604,7 +15956,7 @@ function credentialEndpointsConflict(type, rowEndpoint, credentialEndpoint) {
|
|
|
15604
15956
|
|
|
15605
15957
|
// src/config/global-credentials.ts
|
|
15606
15958
|
function credentialsPath(globalDir2) {
|
|
15607
|
-
return
|
|
15959
|
+
return path36__default.default.join(globalDir2, GLOBAL_CREDENTIALS_FILE);
|
|
15608
15960
|
}
|
|
15609
15961
|
function providerKey(p) {
|
|
15610
15962
|
return p.type;
|
|
@@ -15634,7 +15986,7 @@ function isPersistable(p) {
|
|
|
15634
15986
|
}
|
|
15635
15987
|
function loadGlobalCredentials(globalDir2) {
|
|
15636
15988
|
try {
|
|
15637
|
-
const raw =
|
|
15989
|
+
const raw = fs33__default.default.readFileSync(credentialsPath(globalDir2), "utf-8");
|
|
15638
15990
|
const parsed = JSON.parse(raw);
|
|
15639
15991
|
if (!Array.isArray(parsed.providers)) return [];
|
|
15640
15992
|
return parsed.providers.filter(
|
|
@@ -15646,13 +15998,13 @@ function loadGlobalCredentials(globalDir2) {
|
|
|
15646
15998
|
}
|
|
15647
15999
|
function saveGlobalCredentials(globalDir2, providers) {
|
|
15648
16000
|
const filePath = credentialsPath(globalDir2);
|
|
15649
|
-
|
|
16001
|
+
fs33__default.default.mkdirSync(globalDir2, { recursive: true, mode: 448 });
|
|
15650
16002
|
const body = { version: 1, providers: providers.filter(isPersistable) };
|
|
15651
16003
|
const tmp = `${filePath}.tmp`;
|
|
15652
|
-
|
|
15653
|
-
|
|
16004
|
+
fs33__default.default.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
|
|
16005
|
+
fs33__default.default.renameSync(tmp, filePath);
|
|
15654
16006
|
try {
|
|
15655
|
-
|
|
16007
|
+
fs33__default.default.chmodSync(filePath, 384);
|
|
15656
16008
|
} catch {
|
|
15657
16009
|
}
|
|
15658
16010
|
}
|
|
@@ -16057,7 +16409,7 @@ var ConfigManager = class {
|
|
|
16057
16409
|
/** `globalDirOverride` exists for tests — never point it at the real home dir there. */
|
|
16058
16410
|
constructor(workspacePath = process.cwd(), globalDirOverride) {
|
|
16059
16411
|
this.workspacePath = workspacePath;
|
|
16060
|
-
this.globalDir = globalDirOverride ??
|
|
16412
|
+
this.globalDir = globalDirOverride ?? path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR);
|
|
16061
16413
|
}
|
|
16062
16414
|
async load() {
|
|
16063
16415
|
this.config = await this.loadConfig();
|
|
@@ -16071,8 +16423,8 @@ var ConfigManager = class {
|
|
|
16071
16423
|
this.ignore = new CascadeIgnore();
|
|
16072
16424
|
await this.ignore.load(this.workspacePath);
|
|
16073
16425
|
this.cascadeMd = await loadCascadeMd(this.workspacePath);
|
|
16074
|
-
this.keystore = new Keystore(
|
|
16075
|
-
this.store = new MemoryStore(
|
|
16426
|
+
this.keystore = new Keystore(path36__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
|
|
16427
|
+
this.store = new MemoryStore(path36__default.default.join(this.workspacePath, CASCADE_DB_FILE));
|
|
16076
16428
|
const globalRevoked = stripRevokedCredentials(loadGlobalCredentials(this.globalDir));
|
|
16077
16429
|
const globalCreds = filterRetiredCredentials(globalRevoked.kept);
|
|
16078
16430
|
if (globalRevoked.removed > 0) this.revokedCredentials += globalRevoked.removed;
|
|
@@ -16145,8 +16497,8 @@ var ConfigManager = class {
|
|
|
16145
16497
|
* never reached the disk (see `commitSettings`).
|
|
16146
16498
|
*/
|
|
16147
16499
|
async save(config = this.config) {
|
|
16148
|
-
const configPath =
|
|
16149
|
-
await fs11__default.default.mkdir(
|
|
16500
|
+
const configPath = path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16501
|
+
await fs11__default.default.mkdir(path36__default.default.dirname(configPath), { recursive: true });
|
|
16150
16502
|
await fs11__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
16151
16503
|
try {
|
|
16152
16504
|
saveGlobalCredentials(this.globalDir, config.providers);
|
|
@@ -16196,7 +16548,7 @@ var ConfigManager = class {
|
|
|
16196
16548
|
this.revokedCredentials = 0;
|
|
16197
16549
|
this.revokedPins = [];
|
|
16198
16550
|
this.revokedNotice = void 0;
|
|
16199
|
-
const configPath =
|
|
16551
|
+
const configPath = path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16200
16552
|
try {
|
|
16201
16553
|
const raw = await fs11__default.default.readFile(configPath, "utf-8");
|
|
16202
16554
|
const parsed = JSON.parse(raw);
|
|
@@ -16290,7 +16642,7 @@ var ConfigManager = class {
|
|
|
16290
16642
|
return onResource;
|
|
16291
16643
|
}
|
|
16292
16644
|
async persistClearedPins(tiers) {
|
|
16293
|
-
const configPath =
|
|
16645
|
+
const configPath = path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16294
16646
|
try {
|
|
16295
16647
|
const raw = JSON.parse(await fs11__default.default.readFile(configPath, "utf-8"));
|
|
16296
16648
|
const models = raw["models"];
|
|
@@ -17584,13 +17936,13 @@ var DeadModelStore = class {
|
|
|
17584
17936
|
function fileDeadModelPersistence(filePath) {
|
|
17585
17937
|
return {
|
|
17586
17938
|
load() {
|
|
17587
|
-
if (!
|
|
17588
|
-
const raw = JSON.parse(
|
|
17939
|
+
if (!fs33.existsSync(filePath)) return [];
|
|
17940
|
+
const raw = JSON.parse(fs33.readFileSync(filePath, "utf-8"));
|
|
17589
17941
|
return Array.isArray(raw) ? raw : [];
|
|
17590
17942
|
},
|
|
17591
17943
|
save(records) {
|
|
17592
|
-
|
|
17593
|
-
|
|
17944
|
+
fs33.mkdirSync(path36.dirname(filePath), { recursive: true });
|
|
17945
|
+
fs33.writeFileSync(filePath, JSON.stringify(records, null, 2));
|
|
17594
17946
|
}
|
|
17595
17947
|
};
|
|
17596
17948
|
}
|
|
@@ -18014,7 +18366,7 @@ init_pricing();
|
|
|
18014
18366
|
var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
|
|
18015
18367
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
18016
18368
|
var FETCH_TIMEOUT_MS = 8e3;
|
|
18017
|
-
var DEFAULT_CACHE_FILE =
|
|
18369
|
+
var DEFAULT_CACHE_FILE = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
|
|
18018
18370
|
function normalizeModelId2(id) {
|
|
18019
18371
|
let s = id.toLowerCase();
|
|
18020
18372
|
const slash = s.lastIndexOf("/");
|
|
@@ -18161,7 +18513,7 @@ var LiveDataProvider = class {
|
|
|
18161
18513
|
}
|
|
18162
18514
|
async saveCache() {
|
|
18163
18515
|
try {
|
|
18164
|
-
await fs11__default.default.mkdir(
|
|
18516
|
+
await fs11__default.default.mkdir(path36__default.default.dirname(this.opts.cacheFile), { recursive: true });
|
|
18165
18517
|
const cache = {
|
|
18166
18518
|
fetchedAt: this.fetchedAt,
|
|
18167
18519
|
snapshot: this.snapshot ?? void 0,
|
|
@@ -18268,9 +18620,15 @@ var FAMILY_BENCHMARKS = {
|
|
|
18268
18620
|
"claude-opus": { code: 95, analysis: 92, creative: 90, data: 88 },
|
|
18269
18621
|
"claude-sonnet": { code: 93, analysis: 88, creative: 87, data: 85 },
|
|
18270
18622
|
"claude-haiku": { code: 80, analysis: 75, creative: 76, data: 72 },
|
|
18271
|
-
// OpenAI GPT-5 family
|
|
18272
|
-
//
|
|
18273
|
-
//
|
|
18623
|
+
// OpenAI GPT-5 family. GPT-5.6 is split by durable capability tier instead
|
|
18624
|
+
// of being folded into generic gpt-5: Sol, Terra and Luna have materially
|
|
18625
|
+
// different benchmark/cost envelopes and should compete independently.
|
|
18626
|
+
// Scores are the committed normalized snapshot; provenance is in
|
|
18627
|
+
// scripts/benchmarks/sources/openai-gpt-5.6-2026-08.json.
|
|
18628
|
+
"gpt-5.6-sol": { code: 100, analysis: 98, creative: 88, data: 88 },
|
|
18629
|
+
"gpt-5.6-terra": { code: 97, analysis: 92, creative: 82, data: 85 },
|
|
18630
|
+
"gpt-5.6-luna": { code: 93, analysis: 85, creative: 74, data: 60 },
|
|
18631
|
+
// Older GPT-5 point releases remain distinct where public data supports it.
|
|
18274
18632
|
"gpt-5.5": { code: 97, analysis: 96, creative: 94, data: 95 },
|
|
18275
18633
|
"gpt-5.4": { code: 94, analysis: 94, creative: 92, data: 92 },
|
|
18276
18634
|
"gpt-5.4-mini": { code: 86, analysis: 85, creative: 85, data: 83 },
|
|
@@ -18306,13 +18664,19 @@ var FAMILY_BENCHMARKS = {
|
|
|
18306
18664
|
"llama-small": { code: 55, analysis: 56, creative: 60, data: 54 }
|
|
18307
18665
|
};
|
|
18308
18666
|
var FAMILY_MATCHERS = [
|
|
18667
|
+
[/fable/i, "claude-opus"],
|
|
18309
18668
|
[/opus/i, "claude-opus"],
|
|
18310
18669
|
[/sonnet/i, "claude-sonnet"],
|
|
18311
18670
|
[/haiku/i, "claude-haiku"],
|
|
18312
|
-
// GPT-5 family — ordered most-specific first.
|
|
18313
|
-
//
|
|
18314
|
-
//
|
|
18671
|
+
// GPT-5 family — ordered most-specific first. GPT-5.6 durable tiers carry
|
|
18672
|
+
// their own profiles. 5.4 Nano intentionally maps to the nano class rather
|
|
18673
|
+
// than being swallowed by the generic 5.4 rule.
|
|
18674
|
+
[/gpt-?5\.6.*sol/i, "gpt-5.6-sol"],
|
|
18675
|
+
[/gpt-?5\.6.*terra/i, "gpt-5.6-terra"],
|
|
18676
|
+
[/gpt-?5\.6.*luna/i, "gpt-5.6-luna"],
|
|
18677
|
+
[/gpt-?5\.6(?:\s|$)/i, "gpt-5.6-sol"],
|
|
18315
18678
|
[/gpt-?5\.5/i, "gpt-5.5"],
|
|
18679
|
+
[/gpt-?5\.4.*nano/i, "gpt-5-nano"],
|
|
18316
18680
|
[/gpt-?5\.4.*mini/i, "gpt-5.4-mini"],
|
|
18317
18681
|
[/gpt-?5\.4/i, "gpt-5.4"],
|
|
18318
18682
|
[/gpt-?5.*nano/i, "gpt-5-nano"],
|
|
@@ -21114,6 +21478,23 @@ Now execute your subtask using this context where relevant.`
|
|
|
21114
21478
|
this.setStatus("FAILED", finalOutput);
|
|
21115
21479
|
this.peerBus?.publish(this.id, assignment.subtaskId, finalOutput, "FAILED");
|
|
21116
21480
|
return this.buildResult("ESCALATED", finalOutput, { checksRun, passed, failed }, issues, correctionAttempts);
|
|
21481
|
+
} finally {
|
|
21482
|
+
this.releaseBrowserControl();
|
|
21483
|
+
}
|
|
21484
|
+
}
|
|
21485
|
+
/**
|
|
21486
|
+
* End this worker's hold on the shared browser.
|
|
21487
|
+
*
|
|
21488
|
+
* Duck-typed rather than imported: `browser_control` is registered only by a
|
|
21489
|
+
* host that HAS a browser (the desktop), so in the CLI and hosted runs there
|
|
21490
|
+
* is no such tool and this is a no-op. Importing the class to instanceof-check
|
|
21491
|
+
* it would couple the worker to a tool that usually is not there.
|
|
21492
|
+
*/
|
|
21493
|
+
releaseBrowserControl() {
|
|
21494
|
+
try {
|
|
21495
|
+
const tool = this.toolRegistry.getTool?.("browser_control");
|
|
21496
|
+
tool?.releaseActor?.(this.id);
|
|
21497
|
+
} catch {
|
|
21117
21498
|
}
|
|
21118
21499
|
}
|
|
21119
21500
|
sendToPeer(toId, content) {
|
|
@@ -21372,8 +21753,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
|
|
|
21372
21753
|
// Media generation can run for a minute; without this a cancelled run
|
|
21373
21754
|
// still pays for an image nobody will see.
|
|
21374
21755
|
...this.signal ? { signal: this.signal } : {},
|
|
21375
|
-
saveSnapshot: async (
|
|
21376
|
-
this.store?.addFileSnapshot(this.taskId,
|
|
21756
|
+
saveSnapshot: async (path41, content) => {
|
|
21757
|
+
this.store?.addFileSnapshot(this.taskId, path41, content);
|
|
21377
21758
|
},
|
|
21378
21759
|
sendPeerSync: (to, syncType, content) => {
|
|
21379
21760
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
@@ -21576,7 +21957,7 @@ ${assignment.expectedOutput}`;
|
|
|
21576
21957
|
const { promisify: promisify4 } = await import('util');
|
|
21577
21958
|
const execFileAsync2 = promisify4(execFile3);
|
|
21578
21959
|
for (const artifactPath of artifactPaths) {
|
|
21579
|
-
const absolutePath =
|
|
21960
|
+
const absolutePath = path36__default.default.resolve(this.artifactRoot(), artifactPath);
|
|
21580
21961
|
try {
|
|
21581
21962
|
const stat = await fs11__default.default.stat(absolutePath);
|
|
21582
21963
|
if (!stat.isFile()) {
|
|
@@ -21611,7 +21992,7 @@ ${assignment.expectedOutput}`;
|
|
|
21611
21992
|
issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
|
|
21612
21993
|
continue;
|
|
21613
21994
|
}
|
|
21614
|
-
const ext =
|
|
21995
|
+
const ext = path36__default.default.extname(absolutePath).toLowerCase();
|
|
21615
21996
|
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
21997
|
if (check) {
|
|
21617
21998
|
try {
|
|
@@ -21703,7 +22084,7 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
21703
22084
|
async checkAcceptance(assignment) {
|
|
21704
22085
|
const criteria = assignment.acceptance ?? [];
|
|
21705
22086
|
if (!criteria.length) return [];
|
|
21706
|
-
const resolve2 = (target) =>
|
|
22087
|
+
const resolve2 = (target) => path36__default.default.resolve(this.artifactRoot(), target);
|
|
21707
22088
|
const workerCanWriteFiles = canProduceFiles(this.tools.map((t) => t.name));
|
|
21708
22089
|
return evaluateAcceptance(criteria, assignment.files ?? [], {
|
|
21709
22090
|
stat: async (target) => {
|
|
@@ -25014,16 +25395,16 @@ function resolveInWorkspace(workspaceRoot, input) {
|
|
|
25014
25395
|
if (typeof input !== "string" || input.length === 0) {
|
|
25015
25396
|
throw new WorkspaceSandboxError(String(input), workspaceRoot);
|
|
25016
25397
|
}
|
|
25017
|
-
const root =
|
|
25018
|
-
const abs =
|
|
25019
|
-
const rel =
|
|
25020
|
-
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") ||
|
|
25398
|
+
const root = path36__default.default.resolve(workspaceRoot);
|
|
25399
|
+
const abs = path36__default.default.isAbsolute(input) ? path36__default.default.resolve(input) : path36__default.default.resolve(root, input);
|
|
25400
|
+
const rel = path36__default.default.relative(root, abs);
|
|
25401
|
+
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path36__default.default.isAbsolute(rel)) {
|
|
25021
25402
|
throw new WorkspaceSandboxError(input, root);
|
|
25022
25403
|
}
|
|
25023
25404
|
try {
|
|
25024
|
-
const real =
|
|
25025
|
-
const realRel =
|
|
25026
|
-
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") ||
|
|
25405
|
+
const real = fs33__default.default.realpathSync(abs);
|
|
25406
|
+
const realRel = path36__default.default.relative(root, real);
|
|
25407
|
+
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path36__default.default.isAbsolute(realRel))) {
|
|
25027
25408
|
throw new WorkspaceSandboxError(input, root);
|
|
25028
25409
|
}
|
|
25029
25410
|
} catch (e) {
|
|
@@ -25572,9 +25953,9 @@ async function animatePptx(bytes, scheme) {
|
|
|
25572
25953
|
const zip = await import_jszip.default.loadAsync(bytes);
|
|
25573
25954
|
const slidePaths = Object.keys(zip.files).filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n));
|
|
25574
25955
|
if (slidePaths.length === 0) return bytes;
|
|
25575
|
-
for (const
|
|
25576
|
-
const xml = await zip.file(
|
|
25577
|
-
zip.file(
|
|
25956
|
+
for (const path41 of slidePaths) {
|
|
25957
|
+
const xml = await zip.file(path41).async("string");
|
|
25958
|
+
zip.file(path41, animateSlideXml(xml, scheme));
|
|
25578
25959
|
}
|
|
25579
25960
|
return await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
|
|
25580
25961
|
} catch {
|
|
@@ -25955,7 +26336,7 @@ var GenerateDocumentTool = class extends BaseTool {
|
|
|
25955
26336
|
}
|
|
25956
26337
|
}
|
|
25957
26338
|
const bytes = await renderDocument(ext, content, { name: filePath, loadImageBytes });
|
|
25958
|
-
await fs11__default.default.mkdir(
|
|
26339
|
+
await fs11__default.default.mkdir(path36__default.default.dirname(absPath), { recursive: true });
|
|
25959
26340
|
await fs11__default.default.writeFile(absPath, bytes);
|
|
25960
26341
|
const charts = countCharts(content);
|
|
25961
26342
|
return [
|
|
@@ -26176,7 +26557,7 @@ var FileWriteTool = class extends BaseTool {
|
|
|
26176
26557
|
} catch {
|
|
26177
26558
|
}
|
|
26178
26559
|
}
|
|
26179
|
-
await fs11__default.default.mkdir(
|
|
26560
|
+
await fs11__default.default.mkdir(path36__default.default.dirname(absPath), { recursive: true });
|
|
26180
26561
|
await fs11__default.default.writeFile(absPath, content, "utf-8");
|
|
26181
26562
|
return `Written ${content.length} characters to ${filePath}`;
|
|
26182
26563
|
}
|
|
@@ -26534,12 +26915,10 @@ ${data.description}`;
|
|
|
26534
26915
|
}
|
|
26535
26916
|
}
|
|
26536
26917
|
};
|
|
26537
|
-
|
|
26538
|
-
// src/tools/browser.ts
|
|
26539
26918
|
var BROWSER_LAUNCH_TIMEOUT_MS = 15e3;
|
|
26540
26919
|
var BrowserTool = class extends BaseTool {
|
|
26541
26920
|
name = "browser";
|
|
26542
|
-
description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots.
|
|
26921
|
+
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
26922
|
inputSchema = {
|
|
26544
26923
|
type: "object",
|
|
26545
26924
|
properties: {
|
|
@@ -26606,7 +26985,14 @@ var BrowserTool = class extends BaseTool {
|
|
|
26606
26985
|
}
|
|
26607
26986
|
case "screenshot": {
|
|
26608
26987
|
const buf = await page2.screenshot({ type: "png" });
|
|
26609
|
-
|
|
26988
|
+
const rel = path36__default.default.join(".cascade", "screenshots", `screenshot-${Date.now()}-${crypto.randomUUID().slice(0, 8)}.png`);
|
|
26989
|
+
const abs = resolveInWorkspace(this.workspaceRoot, rel);
|
|
26990
|
+
await fs11__default.default.mkdir(path36__default.default.dirname(abs), { recursive: true });
|
|
26991
|
+
await fs11__default.default.writeFile(abs, buf);
|
|
26992
|
+
return [
|
|
26993
|
+
`Screenshot saved to ${abs} (${Math.round(buf.byteLength / 1024)} KB).`,
|
|
26994
|
+
`Call image_analyze with that path to see what is on it \u2014 that needs a vision-capable model.`
|
|
26995
|
+
].join(" ");
|
|
26610
26996
|
}
|
|
26611
26997
|
case "evaluate": {
|
|
26612
26998
|
const result = await page2.evaluate(input["script"]);
|
|
@@ -26674,7 +27060,7 @@ var ImageAnalyzeTool = class extends BaseTool {
|
|
|
26674
27060
|
};
|
|
26675
27061
|
async function fileToImageAttachment(filePath) {
|
|
26676
27062
|
const data = await fs11__default.default.readFile(filePath);
|
|
26677
|
-
const ext =
|
|
27063
|
+
const ext = path36__default.default.extname(filePath).toLowerCase();
|
|
26678
27064
|
const mimeMap = {
|
|
26679
27065
|
".jpg": "image/jpeg",
|
|
26680
27066
|
".jpeg": "image/jpeg",
|
|
@@ -26708,14 +27094,14 @@ var PDFCreateTool = class extends BaseTool {
|
|
|
26708
27094
|
const filePath = input["path"];
|
|
26709
27095
|
const content = input["content"];
|
|
26710
27096
|
const title = input["title"];
|
|
26711
|
-
const dir =
|
|
26712
|
-
if (!
|
|
26713
|
-
|
|
27097
|
+
const dir = path36__default.default.dirname(filePath);
|
|
27098
|
+
if (!fs33__default.default.existsSync(dir)) {
|
|
27099
|
+
fs33__default.default.mkdirSync(dir, { recursive: true });
|
|
26714
27100
|
}
|
|
26715
27101
|
return new Promise((resolve2, reject) => {
|
|
26716
27102
|
try {
|
|
26717
27103
|
const doc = new PDFDocument__default.default({ margin: 50 });
|
|
26718
|
-
const stream =
|
|
27104
|
+
const stream = fs33__default.default.createWriteStream(filePath);
|
|
26719
27105
|
doc.pipe(stream);
|
|
26720
27106
|
if (title) {
|
|
26721
27107
|
doc.info["Title"] = title;
|
|
@@ -26793,22 +27179,22 @@ var CodeInterpreterTool = class extends BaseTool {
|
|
|
26793
27179
|
}
|
|
26794
27180
|
cmdPrefix = NODE_CMD;
|
|
26795
27181
|
}
|
|
26796
|
-
const tmpDir =
|
|
26797
|
-
if (!
|
|
26798
|
-
|
|
27182
|
+
const tmpDir = path36__default.default.join(this.workspaceRoot, ".cascade", "tmp");
|
|
27183
|
+
if (!fs33__default.default.existsSync(tmpDir)) {
|
|
27184
|
+
fs33__default.default.mkdirSync(tmpDir, { recursive: true });
|
|
26799
27185
|
}
|
|
26800
27186
|
const extension = language === "python" ? "py" : "js";
|
|
26801
27187
|
const fileName = `intp_${crypto.randomUUID().slice(0, 8)}.${extension}`;
|
|
26802
|
-
const filePath =
|
|
26803
|
-
|
|
27188
|
+
const filePath = path36__default.default.join(tmpDir, fileName);
|
|
27189
|
+
fs33__default.default.writeFileSync(filePath, code, "utf-8");
|
|
26804
27190
|
const execArgs = [filePath, ...args];
|
|
26805
27191
|
return new Promise((resolve2) => {
|
|
26806
27192
|
const startMs = Date.now();
|
|
26807
27193
|
child_process.execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
|
|
26808
27194
|
const duration = Date.now() - startMs;
|
|
26809
27195
|
try {
|
|
26810
|
-
if (
|
|
26811
|
-
|
|
27196
|
+
if (fs33__default.default.existsSync(filePath)) {
|
|
27197
|
+
fs33__default.default.unlinkSync(filePath);
|
|
26812
27198
|
}
|
|
26813
27199
|
} catch (cleanupErr) {
|
|
26814
27200
|
console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
|
|
@@ -26888,18 +27274,178 @@ ${formatted}`;
|
|
|
26888
27274
|
return `Unknown action: ${action}`;
|
|
26889
27275
|
}
|
|
26890
27276
|
};
|
|
27277
|
+
var SsrfBlockedError = class extends Error {
|
|
27278
|
+
constructor(message) {
|
|
27279
|
+
super(message);
|
|
27280
|
+
this.name = "SsrfBlockedError";
|
|
27281
|
+
}
|
|
27282
|
+
};
|
|
27283
|
+
var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
27284
|
+
var MAX_REDIRECTS2 = 5;
|
|
27285
|
+
function allowLocal() {
|
|
27286
|
+
return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
|
|
27287
|
+
}
|
|
27288
|
+
function isPrivateAddress(ip) {
|
|
27289
|
+
const type = net__default.default.isIP(ip);
|
|
27290
|
+
if (type === 4) return isPrivateIPv4(ip);
|
|
27291
|
+
if (type === 6) return isPrivateIPv6(ip);
|
|
27292
|
+
return true;
|
|
27293
|
+
}
|
|
27294
|
+
function isPrivateIPv4(ip) {
|
|
27295
|
+
const parts = ip.split(".").map((p) => Number(p));
|
|
27296
|
+
if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
|
|
27297
|
+
const [a, b] = parts;
|
|
27298
|
+
if (a === 0) return true;
|
|
27299
|
+
if (a === 10) return true;
|
|
27300
|
+
if (a === 127) return true;
|
|
27301
|
+
if (a === 169 && b === 254) return true;
|
|
27302
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
27303
|
+
if (a === 192 && b === 168) return true;
|
|
27304
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
27305
|
+
if (a >= 224) return true;
|
|
27306
|
+
return false;
|
|
27307
|
+
}
|
|
27308
|
+
function ipv6Groups(ip) {
|
|
27309
|
+
let s = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
27310
|
+
const zone = s.indexOf("%");
|
|
27311
|
+
if (zone !== -1) s = s.slice(0, zone);
|
|
27312
|
+
const dotted = /^(.*:)(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(s);
|
|
27313
|
+
if (dotted) {
|
|
27314
|
+
const o = [dotted[2], dotted[3], dotted[4], dotted[5]].map((p) => Number(p));
|
|
27315
|
+
if (o.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;
|
|
27316
|
+
s = `${dotted[1]}${(o[0] << 8 | o[1]).toString(16)}:${(o[2] << 8 | o[3]).toString(16)}`;
|
|
27317
|
+
}
|
|
27318
|
+
const halves = s.split("::");
|
|
27319
|
+
if (halves.length > 2) return null;
|
|
27320
|
+
const split = (part) => part ? part.split(":") : [];
|
|
27321
|
+
const head = split(halves[0] ?? "");
|
|
27322
|
+
const tail = halves.length === 2 ? split(halves[1] ?? "") : [];
|
|
27323
|
+
const zeros = halves.length === 2 ? 8 - head.length - tail.length : 0;
|
|
27324
|
+
if (zeros < 0) return null;
|
|
27325
|
+
const groups = [...head, ...Array(zeros).fill("0"), ...tail];
|
|
27326
|
+
if (groups.length !== 8) return null;
|
|
27327
|
+
const out = [];
|
|
27328
|
+
for (const g of groups) {
|
|
27329
|
+
if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
|
|
27330
|
+
out.push(parseInt(g, 16));
|
|
27331
|
+
}
|
|
27332
|
+
return out;
|
|
27333
|
+
}
|
|
27334
|
+
function embeddedIPv4(g) {
|
|
27335
|
+
const quad = (hi, lo) => `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
|
|
27336
|
+
const leadingZero = (n) => g.slice(0, n).every((x) => x === 0);
|
|
27337
|
+
if (leadingZero(5) && g[5] === 65535) return quad(g[6], g[7]);
|
|
27338
|
+
if (leadingZero(6)) return quad(g[6], g[7]);
|
|
27339
|
+
if (g[0] === 100 && g[1] === 65435) {
|
|
27340
|
+
if (g[2] === 1) {
|
|
27341
|
+
return quad(g[3], (g[4] & 255) << 8 | g[5] >> 8);
|
|
27342
|
+
}
|
|
27343
|
+
if (g[2] === 0 && g[3] === 0 && g[4] === 0 && g[5] === 0) return quad(g[6], g[7]);
|
|
27344
|
+
return null;
|
|
27345
|
+
}
|
|
27346
|
+
if (g[0] === 8194) return quad(g[1], g[2]);
|
|
27347
|
+
return null;
|
|
27348
|
+
}
|
|
27349
|
+
function isPrivateIPv6(ip) {
|
|
27350
|
+
const g = ipv6Groups(ip);
|
|
27351
|
+
if (!g) return true;
|
|
27352
|
+
const v4 = embeddedIPv4(g);
|
|
27353
|
+
if (v4) return isPrivateIPv4(v4);
|
|
27354
|
+
if (g[0] === 100 && g[1] === 65435) return true;
|
|
27355
|
+
if (g.every((x) => x === 0)) return true;
|
|
27356
|
+
if (leading7Zero(g) && g[7] === 1) return true;
|
|
27357
|
+
if ((g[0] & 65472) === 65152) return true;
|
|
27358
|
+
if ((g[0] & 65024) === 64512) return true;
|
|
27359
|
+
if ((g[0] & 65280) === 65280) return true;
|
|
27360
|
+
return false;
|
|
27361
|
+
}
|
|
27362
|
+
function leading7Zero(g) {
|
|
27363
|
+
return g.slice(0, 7).every((x) => x === 0);
|
|
27364
|
+
}
|
|
27365
|
+
async function assertPublicUrl(rawUrl) {
|
|
27366
|
+
let url;
|
|
27367
|
+
try {
|
|
27368
|
+
url = new URL(rawUrl);
|
|
27369
|
+
} catch {
|
|
27370
|
+
throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
|
|
27371
|
+
}
|
|
27372
|
+
if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
|
|
27373
|
+
throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
|
|
27374
|
+
}
|
|
27375
|
+
if (allowLocal()) return url;
|
|
27376
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
27377
|
+
if (net__default.default.isIP(host)) {
|
|
27378
|
+
if (isPrivateAddress(host)) {
|
|
27379
|
+
throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
|
|
27380
|
+
}
|
|
27381
|
+
return url;
|
|
27382
|
+
}
|
|
27383
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
|
|
27384
|
+
throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
|
|
27385
|
+
}
|
|
27386
|
+
let addresses;
|
|
27387
|
+
try {
|
|
27388
|
+
const records = await dns2__default.default.lookup(host, { all: true });
|
|
27389
|
+
addresses = records.map((r) => r.address);
|
|
27390
|
+
} catch {
|
|
27391
|
+
throw new SsrfBlockedError(`Could not resolve host "${host}".`);
|
|
27392
|
+
}
|
|
27393
|
+
if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
|
|
27394
|
+
throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
|
|
27395
|
+
}
|
|
27396
|
+
return url;
|
|
27397
|
+
}
|
|
27398
|
+
var ssrfAgent = new undici.Agent({
|
|
27399
|
+
connect: {
|
|
27400
|
+
lookup(hostname, options, callback) {
|
|
27401
|
+
if (allowLocal()) {
|
|
27402
|
+
dnsCallback__default.default.lookup(hostname, options, callback);
|
|
27403
|
+
return;
|
|
27404
|
+
}
|
|
27405
|
+
dnsCallback__default.default.lookup(hostname, { ...options, all: true }, (err, addresses) => {
|
|
27406
|
+
if (err) return callback(err, "", 0);
|
|
27407
|
+
const list = Array.isArray(addresses) ? addresses : [addresses];
|
|
27408
|
+
const bad = list.find((a) => isPrivateAddress(a.address));
|
|
27409
|
+
if (bad) {
|
|
27410
|
+
return callback(
|
|
27411
|
+
new SsrfBlockedError(`Blocked connection to "${hostname}" \u2014 resolved to non-public address ${bad.address}.`),
|
|
27412
|
+
"",
|
|
27413
|
+
0
|
|
27414
|
+
);
|
|
27415
|
+
}
|
|
27416
|
+
if (options.all) return callback(null, list);
|
|
27417
|
+
const first = list[0];
|
|
27418
|
+
return callback(null, first.address, first.family);
|
|
27419
|
+
});
|
|
27420
|
+
}
|
|
27421
|
+
}
|
|
27422
|
+
});
|
|
27423
|
+
async function safeFetch(rawUrl, init = {}) {
|
|
27424
|
+
let currentUrl = (await assertPublicUrl(rawUrl)).toString();
|
|
27425
|
+
for (let i = 0; i <= MAX_REDIRECTS2; i++) {
|
|
27426
|
+
const resp = await fetch(currentUrl, { ...init, redirect: "manual", dispatcher: ssrfAgent });
|
|
27427
|
+
if (resp.status < 300 || resp.status >= 400) return resp;
|
|
27428
|
+
const location = resp.headers.get("location");
|
|
27429
|
+
if (!location) return resp;
|
|
27430
|
+
const next = new URL(location, currentUrl);
|
|
27431
|
+
await assertPublicUrl(next.toString());
|
|
27432
|
+
currentUrl = next.toString();
|
|
27433
|
+
}
|
|
27434
|
+
throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
|
|
27435
|
+
}
|
|
26891
27436
|
|
|
26892
27437
|
// src/tools/web-search.ts
|
|
26893
|
-
async function searchSearXNG(query, baseUrl, maxResults) {
|
|
27438
|
+
async function searchSearXNG(query, baseUrl, maxResults, guardUrl = false) {
|
|
26894
27439
|
const url = new URL("/search", baseUrl);
|
|
26895
27440
|
url.searchParams.set("q", query);
|
|
26896
27441
|
url.searchParams.set("format", "json");
|
|
26897
27442
|
url.searchParams.set("categories", "general");
|
|
26898
27443
|
url.searchParams.set("engines", "google,bing,duckduckgo");
|
|
26899
|
-
const
|
|
27444
|
+
const init = {
|
|
26900
27445
|
headers: { "User-Agent": "Cascade-AI/1.0 WebSearchTool" },
|
|
26901
27446
|
signal: AbortSignal.timeout(1e4)
|
|
26902
|
-
}
|
|
27447
|
+
};
|
|
27448
|
+
const resp = guardUrl ? await safeFetch(url.toString(), init) : await fetch(url.toString(), init);
|
|
26903
27449
|
if (!resp.ok) {
|
|
26904
27450
|
throw new Error(`SearXNG returned HTTP ${resp.status}`);
|
|
26905
27451
|
}
|
|
@@ -27028,7 +27574,11 @@ var WebSearchTool = class extends BaseTool {
|
|
|
27028
27574
|
searxngUrl: config.searxngUrl ?? process.env["SEARXNG_URL"],
|
|
27029
27575
|
braveApiKey: config.braveApiKey ?? process.env["BRAVE_SEARCH_API_KEY"],
|
|
27030
27576
|
tavilyApiKey: config.tavilyApiKey ?? process.env["TAVILY_API_KEY"],
|
|
27031
|
-
maxResults: config.maxResults ?? 5
|
|
27577
|
+
maxResults: config.maxResults ?? 5,
|
|
27578
|
+
// Note this reads only from `config`, never from the environment: the
|
|
27579
|
+
// guard tracks the trustworthiness of the URL the host passed in, and an
|
|
27580
|
+
// env var says nothing about that.
|
|
27581
|
+
guardSearxngUrl: config.guardSearxngUrl ?? false
|
|
27032
27582
|
};
|
|
27033
27583
|
}
|
|
27034
27584
|
async execute(input, _options) {
|
|
@@ -27043,7 +27593,7 @@ var WebSearchTool = class extends BaseTool {
|
|
|
27043
27593
|
let anyBackendReachable = false;
|
|
27044
27594
|
if (this.config.searxngUrl) {
|
|
27045
27595
|
try {
|
|
27046
|
-
results = await searchSearXNG(query, this.config.searxngUrl, maxResults);
|
|
27596
|
+
results = await searchSearXNG(query, this.config.searxngUrl, maxResults, this.config.guardSearxngUrl);
|
|
27047
27597
|
anyBackendReachable = true;
|
|
27048
27598
|
if (results.length > 0) return this.formatResults(query, results);
|
|
27049
27599
|
errors.push("SearXNG: returned 0 results");
|
|
@@ -27120,7 +27670,7 @@ var GlobTool = class extends BaseTool {
|
|
|
27120
27670
|
};
|
|
27121
27671
|
async execute(input, _options) {
|
|
27122
27672
|
const pattern = input["pattern"];
|
|
27123
|
-
const searchPath = input["path"] ?
|
|
27673
|
+
const searchPath = input["path"] ? path36__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
27124
27674
|
const matches = await glob.glob(pattern, {
|
|
27125
27675
|
cwd: searchPath,
|
|
27126
27676
|
ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
|
|
@@ -27133,7 +27683,7 @@ var GlobTool = class extends BaseTool {
|
|
|
27133
27683
|
const withMtime = await Promise.all(
|
|
27134
27684
|
matches.map(async (rel) => {
|
|
27135
27685
|
try {
|
|
27136
|
-
const stat = await fs11__default.default.stat(
|
|
27686
|
+
const stat = await fs11__default.default.stat(path36__default.default.join(searchPath, rel));
|
|
27137
27687
|
return { rel, mtime: stat.mtimeMs };
|
|
27138
27688
|
} catch {
|
|
27139
27689
|
return { rel, mtime: 0 };
|
|
@@ -27182,7 +27732,7 @@ var GrepTool = class extends BaseTool {
|
|
|
27182
27732
|
};
|
|
27183
27733
|
async execute(input, _options) {
|
|
27184
27734
|
const pattern = input["pattern"];
|
|
27185
|
-
const searchPath = input["path"] ?
|
|
27735
|
+
const searchPath = input["path"] ? path36__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
27186
27736
|
const globPattern = input["glob"];
|
|
27187
27737
|
const outputMode = input["output_mode"] ?? "content";
|
|
27188
27738
|
const context = input["context"] ?? 0;
|
|
@@ -27236,12 +27786,12 @@ var GrepTool = class extends BaseTool {
|
|
|
27236
27786
|
nodir: true
|
|
27237
27787
|
});
|
|
27238
27788
|
} catch {
|
|
27239
|
-
files = [
|
|
27789
|
+
files = [path36__default.default.relative(searchPath, searchPath) || "."];
|
|
27240
27790
|
}
|
|
27241
27791
|
const results = [];
|
|
27242
27792
|
let totalCount = 0;
|
|
27243
27793
|
for (const rel of files) {
|
|
27244
|
-
const abs =
|
|
27794
|
+
const abs = path36__default.default.join(searchPath, rel);
|
|
27245
27795
|
let content;
|
|
27246
27796
|
try {
|
|
27247
27797
|
content = await fs11__default.default.readFile(abs, "utf-8");
|
|
@@ -27282,92 +27832,6 @@ Total: ${totalCount} matches`);
|
|
|
27282
27832
|
return results.join("\n");
|
|
27283
27833
|
}
|
|
27284
27834
|
};
|
|
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
27835
|
|
|
27372
27836
|
// src/tools/web-fetch.ts
|
|
27373
27837
|
var MAX_CHARS = 5e4;
|
|
@@ -27640,10 +28104,10 @@ var ToolRegistry = class extends EventEmitter__default.default {
|
|
|
27640
28104
|
}
|
|
27641
28105
|
isIgnored(filePath) {
|
|
27642
28106
|
if (!filePath) return false;
|
|
27643
|
-
const abs =
|
|
27644
|
-
const rel =
|
|
27645
|
-
if (!rel || rel.startsWith("..") ||
|
|
27646
|
-
const posixRel = rel.split(
|
|
28107
|
+
const abs = path36__default.default.resolve(this.workspaceRoot, filePath);
|
|
28108
|
+
const rel = path36__default.default.relative(this.workspaceRoot, abs);
|
|
28109
|
+
if (!rel || rel.startsWith("..") || path36__default.default.isAbsolute(rel)) return true;
|
|
28110
|
+
const posixRel = rel.split(path36__default.default.sep).join("/");
|
|
27647
28111
|
return this.ignoreMatcher.ignores(posixRel);
|
|
27648
28112
|
}
|
|
27649
28113
|
};
|
|
@@ -27877,22 +28341,22 @@ var FileMcpOAuthStore = class {
|
|
|
27877
28341
|
filePath;
|
|
27878
28342
|
load() {
|
|
27879
28343
|
try {
|
|
27880
|
-
return JSON.parse(
|
|
28344
|
+
return JSON.parse(fs33__default.default.readFileSync(this.filePath, "utf-8"));
|
|
27881
28345
|
} catch {
|
|
27882
28346
|
return void 0;
|
|
27883
28347
|
}
|
|
27884
28348
|
}
|
|
27885
28349
|
save(state) {
|
|
27886
|
-
|
|
27887
|
-
|
|
28350
|
+
fs33__default.default.mkdirSync(path36__default.default.dirname(this.filePath), { recursive: true, mode: 448 });
|
|
28351
|
+
fs33__default.default.writeFileSync(this.filePath, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27888
28352
|
try {
|
|
27889
|
-
|
|
28353
|
+
fs33__default.default.chmodSync(this.filePath, 384);
|
|
27890
28354
|
} catch {
|
|
27891
28355
|
}
|
|
27892
28356
|
}
|
|
27893
28357
|
clear() {
|
|
27894
28358
|
try {
|
|
27895
|
-
|
|
28359
|
+
fs33__default.default.rmSync(this.filePath);
|
|
27896
28360
|
} catch {
|
|
27897
28361
|
}
|
|
27898
28362
|
}
|
|
@@ -28712,7 +29176,7 @@ var TaskAnalyzer = class {
|
|
|
28712
29176
|
|
|
28713
29177
|
// src/core/router/model-performance-tracker.ts
|
|
28714
29178
|
init_pricing();
|
|
28715
|
-
var DEFAULT_STATS_FILE =
|
|
29179
|
+
var DEFAULT_STATS_FILE = path36__default.default.join(os10__default.default.homedir(), ".cascade", "model-perf.json");
|
|
28716
29180
|
var ModelPerformanceTracker = class _ModelPerformanceTracker {
|
|
28717
29181
|
stats = /* @__PURE__ */ new Map();
|
|
28718
29182
|
featureStats = /* @__PURE__ */ new Map();
|
|
@@ -28752,7 +29216,7 @@ var ModelPerformanceTracker = class _ModelPerformanceTracker {
|
|
|
28752
29216
|
async save() {
|
|
28753
29217
|
if (this.readOnly) return;
|
|
28754
29218
|
try {
|
|
28755
|
-
await fs11__default.default.mkdir(
|
|
29219
|
+
await fs11__default.default.mkdir(path36__default.default.dirname(this.statsFile), { recursive: true });
|
|
28756
29220
|
const modelsObj = {};
|
|
28757
29221
|
const featuresObj = {};
|
|
28758
29222
|
for (const [key, stat] of this.stats) modelsObj[key] = stat;
|
|
@@ -29385,7 +29849,7 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
29385
29849
|
* any dangerous action, so a silently-reloaded tool can't act without approval. */
|
|
29386
29850
|
async loadPersistedTools() {
|
|
29387
29851
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
29388
|
-
const file =
|
|
29852
|
+
const file = path36__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
|
|
29389
29853
|
try {
|
|
29390
29854
|
const raw = await fs11__default.default.readFile(file, "utf-8");
|
|
29391
29855
|
const specs = JSON.parse(raw);
|
|
@@ -29409,8 +29873,8 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
29409
29873
|
}
|
|
29410
29874
|
async persist() {
|
|
29411
29875
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
29412
|
-
const dir =
|
|
29413
|
-
const file =
|
|
29876
|
+
const dir = path36__default.default.join(this.workspacePath, ".cascade");
|
|
29877
|
+
const file = path36__default.default.join(dir, DYNAMIC_TOOLS_FILE);
|
|
29414
29878
|
try {
|
|
29415
29879
|
await fs11__default.default.mkdir(dir, { recursive: true });
|
|
29416
29880
|
await fs11__default.default.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
|
|
@@ -29430,12 +29894,12 @@ var WorldStateDB = class {
|
|
|
29430
29894
|
constructor(workspacePath, debugMode = false) {
|
|
29431
29895
|
this.workspacePath = workspacePath;
|
|
29432
29896
|
this.debugMode = debugMode;
|
|
29433
|
-
const cascadeDir =
|
|
29434
|
-
if (!
|
|
29435
|
-
|
|
29897
|
+
const cascadeDir = path36__default.default.join(workspacePath, ".cascade");
|
|
29898
|
+
if (!fs33__default.default.existsSync(cascadeDir)) {
|
|
29899
|
+
fs33__default.default.mkdirSync(cascadeDir, { recursive: true });
|
|
29436
29900
|
}
|
|
29437
|
-
this.keyPath =
|
|
29438
|
-
this.dbPath =
|
|
29901
|
+
this.keyPath = path36__default.default.join(cascadeDir, "world_state.key");
|
|
29902
|
+
this.dbPath = path36__default.default.join(cascadeDir, "world_state.db");
|
|
29439
29903
|
this.initEncryptionKey();
|
|
29440
29904
|
this.db = new Database2__default.default(this.dbPath);
|
|
29441
29905
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -29492,11 +29956,11 @@ var WorldStateDB = class {
|
|
|
29492
29956
|
).run(e, r, row.encrypted_value, row.source_worker, row.timestamp, validTo, change);
|
|
29493
29957
|
}
|
|
29494
29958
|
initEncryptionKey() {
|
|
29495
|
-
if (
|
|
29496
|
-
this.encryptionKey =
|
|
29959
|
+
if (fs33__default.default.existsSync(this.keyPath)) {
|
|
29960
|
+
this.encryptionKey = fs33__default.default.readFileSync(this.keyPath);
|
|
29497
29961
|
} else {
|
|
29498
29962
|
this.encryptionKey = crypto__default.default.randomBytes(32);
|
|
29499
|
-
|
|
29963
|
+
fs33__default.default.writeFileSync(this.keyPath, this.encryptionKey);
|
|
29500
29964
|
}
|
|
29501
29965
|
}
|
|
29502
29966
|
encrypt(text) {
|
|
@@ -29782,9 +30246,9 @@ var WorldStateDB = class {
|
|
|
29782
30246
|
dumpDebugIfNeeded() {
|
|
29783
30247
|
if (!this.debugMode) return;
|
|
29784
30248
|
try {
|
|
29785
|
-
const dumpPath =
|
|
30249
|
+
const dumpPath = path36__default.default.join(os10__default.default.tmpdir(), "cascade_world_state_debug.json");
|
|
29786
30250
|
const entries = this.getAllEntries();
|
|
29787
|
-
|
|
30251
|
+
fs33__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
29788
30252
|
} catch (err) {
|
|
29789
30253
|
console.error("Failed to dump debug world state", err);
|
|
29790
30254
|
}
|
|
@@ -29840,11 +30304,11 @@ var ResumeStore = class {
|
|
|
29840
30304
|
version: RESUME_SCHEMA_VERSION,
|
|
29841
30305
|
createdAt: checkpoint.createdAt ?? new Date(timestamp).toISOString()
|
|
29842
30306
|
};
|
|
29843
|
-
await
|
|
29844
|
-
const finalPath =
|
|
30307
|
+
await fs33.promises.mkdir(this.dir, { recursive: true });
|
|
30308
|
+
const finalPath = path36__default.default.join(this.dir, `${timestamp}-${safeId(record.taskId)}.json`);
|
|
29845
30309
|
const tempPath = `${finalPath}.${process.pid}.tmp`;
|
|
29846
|
-
await
|
|
29847
|
-
await
|
|
30310
|
+
await fs33.promises.writeFile(tempPath, JSON.stringify(record, null, 2), "utf-8");
|
|
30311
|
+
await fs33.promises.rename(tempPath, finalPath);
|
|
29848
30312
|
await this.prune();
|
|
29849
30313
|
return true;
|
|
29850
30314
|
} catch {
|
|
@@ -29855,7 +30319,7 @@ var ResumeStore = class {
|
|
|
29855
30319
|
async list() {
|
|
29856
30320
|
let entries;
|
|
29857
30321
|
try {
|
|
29858
|
-
entries = await
|
|
30322
|
+
entries = await fs33.promises.readdir(this.dir);
|
|
29859
30323
|
} catch {
|
|
29860
30324
|
return [];
|
|
29861
30325
|
}
|
|
@@ -29864,7 +30328,7 @@ var ResumeStore = class {
|
|
|
29864
30328
|
const checkpoints = [];
|
|
29865
30329
|
for (const candidate of candidates2) {
|
|
29866
30330
|
try {
|
|
29867
|
-
const raw = await
|
|
30331
|
+
const raw = await fs33.promises.readFile(path36__default.default.join(this.dir, candidate.name), "utf-8");
|
|
29868
30332
|
const parsed = JSON.parse(raw);
|
|
29869
30333
|
if (isCheckpoint(parsed)) checkpoints.push(parsed);
|
|
29870
30334
|
} catch {
|
|
@@ -29898,12 +30362,12 @@ var ResumeStore = class {
|
|
|
29898
30362
|
const target = taskId ? all.find((c) => c.taskId === taskId) : all[0];
|
|
29899
30363
|
if (!target) return null;
|
|
29900
30364
|
try {
|
|
29901
|
-
const entries = await
|
|
30365
|
+
const entries = await fs33.promises.readdir(this.dir);
|
|
29902
30366
|
const wanted = safeId(target.taskId);
|
|
29903
30367
|
const source = entries.find((name) => FILE_PATTERN.exec(name)?.[2] === wanted);
|
|
29904
30368
|
if (!source) return null;
|
|
29905
30369
|
const claimId = `${this.now()}-${safeId(target.taskId)}`;
|
|
29906
|
-
await
|
|
30370
|
+
await fs33.promises.rename(path36__default.default.join(this.dir, source), path36__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`));
|
|
29907
30371
|
return { checkpoint: target, claimId };
|
|
29908
30372
|
} catch {
|
|
29909
30373
|
return null;
|
|
@@ -29912,9 +30376,9 @@ var ResumeStore = class {
|
|
|
29912
30376
|
/** Give a claim back, so the work stays recoverable after a failed start. */
|
|
29913
30377
|
async release(claimId) {
|
|
29914
30378
|
try {
|
|
29915
|
-
await
|
|
29916
|
-
|
|
29917
|
-
|
|
30379
|
+
await fs33.promises.rename(
|
|
30380
|
+
path36__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`),
|
|
30381
|
+
path36__default.default.join(this.dir, `${claimId}.json`)
|
|
29918
30382
|
);
|
|
29919
30383
|
} catch {
|
|
29920
30384
|
}
|
|
@@ -29922,7 +30386,7 @@ var ResumeStore = class {
|
|
|
29922
30386
|
/** Discard a claim for good — the resumed run has taken ownership. */
|
|
29923
30387
|
async settle(claimId) {
|
|
29924
30388
|
try {
|
|
29925
|
-
await
|
|
30389
|
+
await fs33.promises.rm(path36__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`), { force: true });
|
|
29926
30390
|
} catch {
|
|
29927
30391
|
}
|
|
29928
30392
|
}
|
|
@@ -29944,15 +30408,15 @@ var ResumeStore = class {
|
|
|
29944
30408
|
async reclaimStale() {
|
|
29945
30409
|
let reclaimed = 0;
|
|
29946
30410
|
try {
|
|
29947
|
-
const entries = await
|
|
30411
|
+
const entries = await fs33.promises.readdir(this.dir);
|
|
29948
30412
|
const cutoff = this.now() - this.claimTimeoutMs;
|
|
29949
30413
|
for (const name of entries) {
|
|
29950
30414
|
if (!name.endsWith(CLAIM_SUFFIX)) continue;
|
|
29951
30415
|
const stamp2 = Number(/^(\d+)-/.exec(name)?.[1]);
|
|
29952
30416
|
if (!Number.isFinite(stamp2) || stamp2 >= cutoff) continue;
|
|
29953
|
-
await
|
|
29954
|
-
|
|
29955
|
-
|
|
30417
|
+
await fs33.promises.rename(
|
|
30418
|
+
path36__default.default.join(this.dir, name),
|
|
30419
|
+
path36__default.default.join(this.dir, `${name.slice(0, -CLAIM_SUFFIX.length)}.json`)
|
|
29956
30420
|
);
|
|
29957
30421
|
reclaimed++;
|
|
29958
30422
|
}
|
|
@@ -29963,12 +30427,12 @@ var ResumeStore = class {
|
|
|
29963
30427
|
/** Remove every checkpoint for a task id. */
|
|
29964
30428
|
async delete(taskId) {
|
|
29965
30429
|
try {
|
|
29966
|
-
const entries = await
|
|
30430
|
+
const entries = await fs33.promises.readdir(this.dir);
|
|
29967
30431
|
const wanted = safeId(taskId);
|
|
29968
30432
|
await Promise.all(entries.map(async (name) => {
|
|
29969
30433
|
const match = FILE_PATTERN.exec(name);
|
|
29970
30434
|
if (match?.[2] === wanted) {
|
|
29971
|
-
await
|
|
30435
|
+
await fs33.promises.rm(path36__default.default.join(this.dir, name), { force: true });
|
|
29972
30436
|
}
|
|
29973
30437
|
}));
|
|
29974
30438
|
} catch {
|
|
@@ -29977,13 +30441,13 @@ var ResumeStore = class {
|
|
|
29977
30441
|
/** Drop expired checkpoints, then any beyond the newest `maxCheckpoints`. */
|
|
29978
30442
|
async prune() {
|
|
29979
30443
|
try {
|
|
29980
|
-
const entries = await
|
|
30444
|
+
const entries = await fs33.promises.readdir(this.dir);
|
|
29981
30445
|
const cutoff = this.now() - this.maxAgeMs;
|
|
29982
30446
|
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
30447
|
const doomed = files.filter((f, index) => f.timestamp < cutoff || index >= this.maxCheckpoints);
|
|
29984
30448
|
const staleTemps = entries.filter((name) => name.endsWith(".tmp")).filter((name) => (Number(FILE_PATTERN.exec(name.replace(/\.\d+\.tmp$/, ".json"))?.[1]) || 0) < cutoff);
|
|
29985
30449
|
await Promise.all([...doomed.map((f) => f.name), ...staleTemps].map(
|
|
29986
|
-
(name) =>
|
|
30450
|
+
(name) => fs33.promises.rm(path36__default.default.join(this.dir, name), { force: true })
|
|
29987
30451
|
));
|
|
29988
30452
|
} catch {
|
|
29989
30453
|
}
|
|
@@ -30360,7 +30824,7 @@ var WorkspaceIndex = class {
|
|
|
30360
30824
|
isIgnored;
|
|
30361
30825
|
chunker;
|
|
30362
30826
|
constructor(opts) {
|
|
30363
|
-
this.root =
|
|
30827
|
+
this.root = path36__default.default.resolve(opts.root);
|
|
30364
30828
|
this.db = opts.db;
|
|
30365
30829
|
this.namespace = opts.namespace ?? "code";
|
|
30366
30830
|
this.extensions = new Set((opts.extensions ?? DEFAULT_EXTENSIONS).map((e) => e.replace(/^\./, "").toLowerCase()));
|
|
@@ -30392,14 +30856,14 @@ var WorkspaceIndex = class {
|
|
|
30392
30856
|
return;
|
|
30393
30857
|
}
|
|
30394
30858
|
for (const entry of entries) {
|
|
30395
|
-
const abs =
|
|
30859
|
+
const abs = path36__default.default.join(dir, entry.name);
|
|
30396
30860
|
if (entry.isDirectory()) {
|
|
30397
30861
|
if (HARD_SKIP_DIRS.has(entry.name) || this.isIgnored(abs)) continue;
|
|
30398
30862
|
await walk(abs);
|
|
30399
30863
|
} else if (entry.isFile()) {
|
|
30400
|
-
const ext =
|
|
30864
|
+
const ext = path36__default.default.extname(entry.name).slice(1).toLowerCase();
|
|
30401
30865
|
if (!this.extensions.has(ext) || this.isIgnored(abs)) continue;
|
|
30402
|
-
out.push(
|
|
30866
|
+
out.push(path36__default.default.relative(this.root, abs));
|
|
30403
30867
|
}
|
|
30404
30868
|
}
|
|
30405
30869
|
};
|
|
@@ -30429,7 +30893,7 @@ var WorkspaceIndex = class {
|
|
|
30429
30893
|
const entries = [];
|
|
30430
30894
|
for (const rel of rels) {
|
|
30431
30895
|
try {
|
|
30432
|
-
const abs =
|
|
30896
|
+
const abs = path36__default.default.join(this.root, rel);
|
|
30433
30897
|
const stat = await fs11__default.default.stat(abs);
|
|
30434
30898
|
if (stat.size > this.maxFileBytes) continue;
|
|
30435
30899
|
const buf = await fs11__default.default.readFile(abs);
|
|
@@ -30834,6 +31298,127 @@ var CurrentPageTool = class extends BaseTool {
|
|
|
30834
31298
|
}
|
|
30835
31299
|
};
|
|
30836
31300
|
|
|
31301
|
+
// src/tools/browser-control.ts
|
|
31302
|
+
var BrowserControlTool = class extends BaseTool {
|
|
31303
|
+
name = "browser_control";
|
|
31304
|
+
description = "Act on the web page the user has open in the built-in browser: navigate, click an element, type into a field, press a key, or wait for something to appear. This drives the REAL browser the user is signed into and can see, so actions have real consequences \u2014 prefer read_current_page when you only need to look. CSS selectors are matched against the live page; call extract_text first if you need to find one. There is one browser: if another part of this run is using it, this call waits its turn, so a slow reply means queued, not stuck.";
|
|
31305
|
+
inputSchema = {
|
|
31306
|
+
type: "object",
|
|
31307
|
+
properties: {
|
|
31308
|
+
action: {
|
|
31309
|
+
type: "string",
|
|
31310
|
+
enum: ["navigate", "click", "fill", "press", "wait_for", "extract_text"],
|
|
31311
|
+
description: "What to do."
|
|
31312
|
+
},
|
|
31313
|
+
url: { type: "string", description: "For navigate: the http(s) address to open." },
|
|
31314
|
+
selector: {
|
|
31315
|
+
type: "string",
|
|
31316
|
+
description: "CSS selector for click, fill and wait_for. Optional for extract_text (defaults to the whole page)."
|
|
31317
|
+
},
|
|
31318
|
+
value: { type: "string", description: "For fill: the text to type." },
|
|
31319
|
+
key: { type: "string", description: 'For press: a key name such as "Enter", "Tab" or "Escape".' },
|
|
31320
|
+
timeoutMs: { type: "number", description: "For wait_for: how long to wait before giving up (default 10000, max 30000)." }
|
|
31321
|
+
},
|
|
31322
|
+
required: ["action"]
|
|
31323
|
+
};
|
|
31324
|
+
controller;
|
|
31325
|
+
release;
|
|
31326
|
+
/** One-way. See `revoke`. */
|
|
31327
|
+
revoked = false;
|
|
31328
|
+
constructor(controller, release) {
|
|
31329
|
+
super();
|
|
31330
|
+
this.controller = controller;
|
|
31331
|
+
this.release = release;
|
|
31332
|
+
}
|
|
31333
|
+
/**
|
|
31334
|
+
* A worker has finished. Ends its hold on the browser.
|
|
31335
|
+
*
|
|
31336
|
+
* Called from the tier's terminal path rather than by the model, because a
|
|
31337
|
+
* model cannot be relied on to announce that it is done — which is why the
|
|
31338
|
+
* host cannot use idle time as the boundary instead.
|
|
31339
|
+
*/
|
|
31340
|
+
releaseActor(actorId) {
|
|
31341
|
+
this.release?.(actorId);
|
|
31342
|
+
}
|
|
31343
|
+
/**
|
|
31344
|
+
* Permanently disable this tool, whatever the registry still holds.
|
|
31345
|
+
*
|
|
31346
|
+
* Registration-time gates cannot express "this run turned out to be
|
|
31347
|
+
* unattended": a host that wires the controller and only then declares the
|
|
31348
|
+
* run unattended has already registered the tool, and a check that ran at
|
|
31349
|
+
* registration has no second chance. Enforcing at EXECUTION time makes the
|
|
31350
|
+
* guarantee independent of call order, which is the whole point of stating
|
|
31351
|
+
* the invariant rather than relying on the scheduler's current sequence.
|
|
31352
|
+
*
|
|
31353
|
+
* One-way on purpose. A safety gate that can be re-opened by a later call is
|
|
31354
|
+
* a gate that a refactor re-opens by accident.
|
|
31355
|
+
*/
|
|
31356
|
+
revoke() {
|
|
31357
|
+
this.revoked = true;
|
|
31358
|
+
}
|
|
31359
|
+
// Acting on a session the user is signed into is the highest-consequence
|
|
31360
|
+
// thing in the tool set — higher than shell, arguably, because the blast
|
|
31361
|
+
// radius is someone else's server rather than this machine.
|
|
31362
|
+
isDangerous() {
|
|
31363
|
+
return true;
|
|
31364
|
+
}
|
|
31365
|
+
async execute(input, options) {
|
|
31366
|
+
if (this.revoked) {
|
|
31367
|
+
return "Error: browser control is not available in this run. It requires a person watching who can stop it, and nobody is.";
|
|
31368
|
+
}
|
|
31369
|
+
const kind = input["action"];
|
|
31370
|
+
if (!kind) return "Error: action is required.";
|
|
31371
|
+
if (options?.signal?.aborted) return "Error: the run was cancelled before this action ran.";
|
|
31372
|
+
const missing = requiredFieldFor(kind, input);
|
|
31373
|
+
if (missing) return `Error: "${kind}" needs ${missing}.`;
|
|
31374
|
+
const action = {
|
|
31375
|
+
kind,
|
|
31376
|
+
...typeof input["url"] === "string" ? { url: input["url"] } : {},
|
|
31377
|
+
...typeof input["selector"] === "string" ? { selector: input["selector"] } : {},
|
|
31378
|
+
...typeof input["value"] === "string" ? { value: input["value"] } : {},
|
|
31379
|
+
...typeof input["key"] === "string" ? { key: input["key"] } : {},
|
|
31380
|
+
...typeof input["timeoutMs"] === "number" ? { timeoutMs: Math.min(Math.max(input["timeoutMs"], 0), 3e4) } : {}
|
|
31381
|
+
};
|
|
31382
|
+
let outcome;
|
|
31383
|
+
try {
|
|
31384
|
+
outcome = await this.controller(action, {
|
|
31385
|
+
// `sessionId` is the run. The host scopes both revocation and its
|
|
31386
|
+
// single-owner lease on it, so passing it is not telemetry — drop it
|
|
31387
|
+
// and a Stop in one run silently stops every later one.
|
|
31388
|
+
sessionId: options?.sessionId ?? "",
|
|
31389
|
+
actorId: options?.tierId ?? options?.sessionId ?? "",
|
|
31390
|
+
...options?.signal ? { signal: options.signal } : {}
|
|
31391
|
+
});
|
|
31392
|
+
} catch (err) {
|
|
31393
|
+
return `Error: the browser could not perform "${kind}" \u2014 ${err instanceof Error ? err.message : String(err)}`;
|
|
31394
|
+
}
|
|
31395
|
+
const where = outcome.url ? `
|
|
31396
|
+
Page: ${outcome.title ? `${outcome.title} \u2014 ` : ""}${outcome.url}` : "";
|
|
31397
|
+
return `${outcome.ok ? "" : "Failed: "}${outcome.detail}${where}`;
|
|
31398
|
+
}
|
|
31399
|
+
};
|
|
31400
|
+
function requiredFieldFor(kind, input) {
|
|
31401
|
+
const has = (k) => typeof input[k] === "string" && input[k].length > 0;
|
|
31402
|
+
const given = (k) => typeof input[k] === "string";
|
|
31403
|
+
switch (kind) {
|
|
31404
|
+
case "navigate":
|
|
31405
|
+
return has("url") ? null : "a url";
|
|
31406
|
+
case "click":
|
|
31407
|
+
return has("selector") ? null : "a selector";
|
|
31408
|
+
case "fill":
|
|
31409
|
+
return has("selector") ? given("value") ? null : "a value" : "a selector";
|
|
31410
|
+
case "press":
|
|
31411
|
+
return has("key") ? null : "a key";
|
|
31412
|
+
case "wait_for":
|
|
31413
|
+
return has("selector") ? null : "a selector";
|
|
31414
|
+
// extract_text works with or without a selector.
|
|
31415
|
+
case "extract_text":
|
|
31416
|
+
return null;
|
|
31417
|
+
default:
|
|
31418
|
+
return null;
|
|
31419
|
+
}
|
|
31420
|
+
}
|
|
31421
|
+
|
|
30837
31422
|
// src/core/cascade.ts
|
|
30838
31423
|
var ESCALATION_DECISION_TIMEOUT_MS = 5 * 6e4;
|
|
30839
31424
|
function buildContextualPrompt(prompt, history = []) {
|
|
@@ -30919,13 +31504,13 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
|
|
|
30919
31504
|
this.toolRegistry = new ToolRegistry(this.config.tools, workspacePath);
|
|
30920
31505
|
try {
|
|
30921
31506
|
this.router.setDeadModelStore(new DeadModelStore(
|
|
30922
|
-
fileDeadModelPersistence(
|
|
31507
|
+
fileDeadModelPersistence(path36__default.default.join(workspacePath, ".cascade", "dead-models.json"))
|
|
30923
31508
|
));
|
|
30924
31509
|
} catch {
|
|
30925
31510
|
}
|
|
30926
31511
|
try {
|
|
30927
31512
|
this.resumeStore = new ResumeStore({
|
|
30928
|
-
dir:
|
|
31513
|
+
dir: path36__default.default.join(workspacePath, ".cascade", "resume")
|
|
30929
31514
|
});
|
|
30930
31515
|
} catch {
|
|
30931
31516
|
}
|
|
@@ -31013,8 +31598,8 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
|
|
|
31013
31598
|
this.emit("log", { level: "warn", message: "Code index enabled but no embeddings-capable provider is configured \u2014 skipping." });
|
|
31014
31599
|
return;
|
|
31015
31600
|
}
|
|
31016
|
-
const dbPath = ci.dbPath ||
|
|
31017
|
-
|
|
31601
|
+
const dbPath = ci.dbPath || path36__default.default.join(this.workspacePath, ".cascade", "code-index.db");
|
|
31602
|
+
fs33__default.default.mkdirSync(path36__default.default.dirname(dbPath), { recursive: true });
|
|
31018
31603
|
const db = new Database2__default.default(dbPath);
|
|
31019
31604
|
db.pragma("journal_mode = WAL");
|
|
31020
31605
|
const complete = chatCompleterFromProviders(this.config.providers);
|
|
@@ -31326,6 +31911,49 @@ ${input}` }],
|
|
|
31326
31911
|
if ((this.config.tools?.disabledTools ?? []).includes("read_current_page")) return;
|
|
31327
31912
|
this.toolRegistry.register(new CurrentPageTool(provider));
|
|
31328
31913
|
}
|
|
31914
|
+
/**
|
|
31915
|
+
* True when nobody is watching this run — a scheduled/cron task, which
|
|
31916
|
+
* auto-approves every tool because there is no one to ask.
|
|
31917
|
+
*
|
|
31918
|
+
* That auto-approval is fine for ordinary tools and disqualifying for this
|
|
31919
|
+
* one: `browser_control` drives the signed-in browser, and "every run asks
|
|
31920
|
+
* first" is the promise the Settings copy makes. The property used to hold
|
|
31921
|
+
* only because `runScheduledTask` happens not to call `setBrowserController`
|
|
31922
|
+
* — a guarantee resting on an ABSENT call, which is exactly what a later
|
|
31923
|
+
* refactor adds without noticing. Stated and enforced instead.
|
|
31924
|
+
*/
|
|
31925
|
+
unattended = false;
|
|
31926
|
+
/**
|
|
31927
|
+
* Declare that no human is watching this run, so capabilities that depend on
|
|
31928
|
+
* a person being able to see and stop them are refused outright.
|
|
31929
|
+
*/
|
|
31930
|
+
setUnattended(on) {
|
|
31931
|
+
this.unattended = on;
|
|
31932
|
+
if (!on) return;
|
|
31933
|
+
const tool = this.toolRegistry.getTool("browser_control");
|
|
31934
|
+
tool?.revoke?.();
|
|
31935
|
+
}
|
|
31936
|
+
/**
|
|
31937
|
+
* Wire the host's browser so a run can ACT on the open page, not just read it.
|
|
31938
|
+
*
|
|
31939
|
+
* Two gates before this registers anything, and both matter:
|
|
31940
|
+
*
|
|
31941
|
+
* - `tools.agentBrowserControl` must be on. Unlike read_current_page, which
|
|
31942
|
+
* reaches nothing the user has not already opened themselves, this drives
|
|
31943
|
+
* the session they are signed into. That is opt-in, not a default.
|
|
31944
|
+
* - `disabledTools` still removes it, the same as every other tool.
|
|
31945
|
+
* - The run must not be UNATTENDED. See `setUnattended`.
|
|
31946
|
+
*
|
|
31947
|
+
* Host-supplied like the page provider: the tool exists only where there is a
|
|
31948
|
+
* real browser to drive, so it never has to be refused in the CLI or a hosted
|
|
31949
|
+
* run — it is simply not there.
|
|
31950
|
+
*/
|
|
31951
|
+
setBrowserController(controller, release) {
|
|
31952
|
+
if (this.unattended) return;
|
|
31953
|
+
if (!this.config.tools?.agentBrowserControl) return;
|
|
31954
|
+
if ((this.config.tools?.disabledTools ?? []).includes("browser_control")) return;
|
|
31955
|
+
this.toolRegistry.register(new BrowserControlTool(controller, release));
|
|
31956
|
+
}
|
|
31329
31957
|
registerMediaTools(workspacePath) {
|
|
31330
31958
|
const denied = new Set(this.config.tools?.disabledTools ?? []);
|
|
31331
31959
|
const permitted = (name) => !denied.has(name);
|
|
@@ -31931,6 +32559,7 @@ ${prompt}` : prompt;
|
|
|
31931
32559
|
this.router.setRunSignal(options.signal);
|
|
31932
32560
|
const startMs = Date.now();
|
|
31933
32561
|
const taskId = crypto.randomUUID();
|
|
32562
|
+
this.emit("run:started", { taskId });
|
|
31934
32563
|
this.decisionLog = [];
|
|
31935
32564
|
this.currentRunCompleted = [];
|
|
31936
32565
|
this.wroteCheckpointThisRun = false;
|
|
@@ -32482,14 +33111,14 @@ ${finalOutput}` : ""
|
|
|
32482
33111
|
init_constants();
|
|
32483
33112
|
var CLOUD_SESSION_FILE = "cloud-session.json";
|
|
32484
33113
|
function globalDir(homeDir = os10__default.default.homedir()) {
|
|
32485
|
-
return
|
|
33114
|
+
return path36__default.default.join(homeDir, GLOBAL_CONFIG_DIR);
|
|
32486
33115
|
}
|
|
32487
33116
|
function cloudSessionPath(dir = globalDir()) {
|
|
32488
|
-
return
|
|
33117
|
+
return path36__default.default.join(dir, CLOUD_SESSION_FILE);
|
|
32489
33118
|
}
|
|
32490
33119
|
function loadCloudSession(dir = globalDir()) {
|
|
32491
33120
|
try {
|
|
32492
|
-
const raw =
|
|
33121
|
+
const raw = fs33__default.default.readFileSync(cloudSessionPath(dir), "utf-8");
|
|
32493
33122
|
const parsed = JSON.parse(raw);
|
|
32494
33123
|
if (!parsed?.refreshToken || !parsed?.serverUrl) return null;
|
|
32495
33124
|
return parsed;
|
|
@@ -32498,17 +33127,17 @@ function loadCloudSession(dir = globalDir()) {
|
|
|
32498
33127
|
}
|
|
32499
33128
|
}
|
|
32500
33129
|
function saveCloudSession(session, dir = globalDir()) {
|
|
32501
|
-
|
|
33130
|
+
fs33__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
32502
33131
|
const file = cloudSessionPath(dir);
|
|
32503
|
-
|
|
33132
|
+
fs33__default.default.writeFileSync(file, JSON.stringify(session, null, 2), { mode: 384 });
|
|
32504
33133
|
try {
|
|
32505
|
-
|
|
33134
|
+
fs33__default.default.chmodSync(file, 384);
|
|
32506
33135
|
} catch {
|
|
32507
33136
|
}
|
|
32508
33137
|
}
|
|
32509
33138
|
function clearCloudSession(dir = globalDir()) {
|
|
32510
33139
|
try {
|
|
32511
|
-
|
|
33140
|
+
fs33__default.default.rmSync(cloudSessionPath(dir));
|
|
32512
33141
|
} catch {
|
|
32513
33142
|
}
|
|
32514
33143
|
}
|
|
@@ -32680,16 +33309,16 @@ var CloudClient = class _CloudClient {
|
|
|
32680
33309
|
return (await this.refresh()).accessToken;
|
|
32681
33310
|
}
|
|
32682
33311
|
/** Authenticated request that refreshes once on a 401 (e.g. server-side revoke). */
|
|
32683
|
-
async authed(
|
|
32684
|
-
const call = (token) => fetch(this.url(
|
|
33312
|
+
async authed(path41, init = {}) {
|
|
33313
|
+
const call = (token) => fetch(this.url(path41), { ...init, headers: { ...init.headers ?? {}, Authorization: `Bearer ${token}` } });
|
|
32685
33314
|
let res = await call(await this.accessToken());
|
|
32686
33315
|
if (res.status === 401) res = await call((await this.refresh()).accessToken);
|
|
32687
33316
|
if (!res.ok) throw new Error(`Request failed (${res.status}).`);
|
|
32688
33317
|
const text = await res.text();
|
|
32689
33318
|
return text ? JSON.parse(text) : {};
|
|
32690
33319
|
}
|
|
32691
|
-
authedGet(
|
|
32692
|
-
return this.authed(
|
|
33320
|
+
authedGet(path41) {
|
|
33321
|
+
return this.authed(path41);
|
|
32693
33322
|
}
|
|
32694
33323
|
async fetchMe(token) {
|
|
32695
33324
|
const res = await fetch(this.url("/api/me"), { headers: { Authorization: `Bearer ${token}` } });
|
|
@@ -34463,9 +35092,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
|
|
|
34463
35092
|
}
|
|
34464
35093
|
} catch {
|
|
34465
35094
|
}
|
|
34466
|
-
const configPath =
|
|
35095
|
+
const configPath = path36__default.default.join(workspacePath, ".cascade", "config.json");
|
|
34467
35096
|
try {
|
|
34468
|
-
await fs11__default.default.mkdir(
|
|
35097
|
+
await fs11__default.default.mkdir(path36__default.default.dirname(configPath), { recursive: true });
|
|
34469
35098
|
await fs11__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
34470
35099
|
} catch (err) {
|
|
34471
35100
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -34517,9 +35146,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
|
|
|
34517
35146
|
if (msg.includes("non-text parts")) return;
|
|
34518
35147
|
originalLog(...args);
|
|
34519
35148
|
};
|
|
34520
|
-
const store = new MemoryStore(
|
|
35149
|
+
const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
|
|
34521
35150
|
storeRef.current = store;
|
|
34522
|
-
globalStoreRef.current = new MemoryStore(
|
|
35151
|
+
globalStoreRef.current = new MemoryStore(path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
|
|
34523
35152
|
const identityRows = store.listIdentities().map((i) => ({ id: i.id, name: i.name, isDefault: i.isDefault }));
|
|
34524
35153
|
setIdentities(identityRows);
|
|
34525
35154
|
let initialIdentityId = config.defaultIdentityId ?? identityRows.find((i) => i.isDefault)?.id ?? identityRows[0]?.id;
|
|
@@ -34599,7 +35228,7 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
|
|
|
34599
35228
|
onThemeChange: (name) => setTheme(getTheme(name)),
|
|
34600
35229
|
onExport: async (fmt) => {
|
|
34601
35230
|
const stamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
34602
|
-
const exportPath =
|
|
35231
|
+
const exportPath = path36__default.default.join(workspacePath, `cascade-export-${stamp2}.${fmt === "json" ? "json" : "md"}`);
|
|
34603
35232
|
if (fmt === "json") {
|
|
34604
35233
|
await fs11__default.default.writeFile(exportPath, JSON.stringify({ sessionId: sessionIdRef.current, messages: state.messages }, null, 2), "utf-8");
|
|
34605
35234
|
} else {
|
|
@@ -35519,7 +36148,7 @@ function stringifySlashOutput(val) {
|
|
|
35519
36148
|
}
|
|
35520
36149
|
async function searchSessionsAndMessages(query, workspacePath) {
|
|
35521
36150
|
if (!query) return "Usage: /search <query>";
|
|
35522
|
-
const dbPath =
|
|
36151
|
+
const dbPath = path36__default.default.join(workspacePath, CASCADE_DB_FILE);
|
|
35523
36152
|
try {
|
|
35524
36153
|
await fs11__default.default.access(dbPath);
|
|
35525
36154
|
} catch {
|
|
@@ -35553,7 +36182,7 @@ async function searchSessionsAndMessages(query, workspacePath) {
|
|
|
35553
36182
|
async function diagnoseRuntime(config, workspacePath) {
|
|
35554
36183
|
const providers = config.providers.map((p) => `${p.type}${p.apiKey ? " (key set)" : " (no key)"}`).join("\n");
|
|
35555
36184
|
const models = [`T1: ${config.models.t1 ?? "default"}`, `T2: ${config.models.t2 ?? "default"}`, `T3: ${config.models.t3 ?? "default"}`].join("\n");
|
|
35556
|
-
const store = new MemoryStore(
|
|
36185
|
+
const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
|
|
35557
36186
|
try {
|
|
35558
36187
|
const sessions2 = store.listSessions(void 0, 3);
|
|
35559
36188
|
return [
|
|
@@ -35572,7 +36201,7 @@ async function diagnoseRuntime(config, workspacePath) {
|
|
|
35572
36201
|
}
|
|
35573
36202
|
async function showRecentLogs(args, workspacePath) {
|
|
35574
36203
|
const limit = Number.parseInt(args[0] ?? "10", 10) || 10;
|
|
35575
|
-
const store = new MemoryStore(
|
|
36204
|
+
const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
|
|
35576
36205
|
try {
|
|
35577
36206
|
const logs = store.listRuntimeNodeLogs(void 0, void 0, limit);
|
|
35578
36207
|
if (!logs.length) return "No recent runtime logs.";
|
|
@@ -35584,7 +36213,7 @@ async function showRecentLogs(args, workspacePath) {
|
|
|
35584
36213
|
async function loadSessionSnapshot(args, workspacePath) {
|
|
35585
36214
|
const sessionId = args[0];
|
|
35586
36215
|
if (!sessionId) return "Usage: /resume <sessionId>";
|
|
35587
|
-
const store = new MemoryStore(
|
|
36216
|
+
const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
|
|
35588
36217
|
try {
|
|
35589
36218
|
const session = store.getSession(sessionId);
|
|
35590
36219
|
if (!session) return `Session not found: ${sessionId}`;
|
|
@@ -36004,9 +36633,9 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
36004
36633
|
...anyAuto ? { cascadeAuto: true } : {}
|
|
36005
36634
|
};
|
|
36006
36635
|
const config = CascadeConfigSchema.parse(rawConfig);
|
|
36007
|
-
const configDir =
|
|
36636
|
+
const configDir = path36__default.default.join(workspacePath, ".cascade");
|
|
36008
36637
|
await fs11__default.default.mkdir(configDir, { recursive: true });
|
|
36009
|
-
const configPath =
|
|
36638
|
+
const configPath = path36__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
|
|
36010
36639
|
await fs11__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
36011
36640
|
savedConfigRef.current = config;
|
|
36012
36641
|
dispatchRef.current({ type: "GO_DONE" });
|
|
@@ -36347,14 +36976,14 @@ function printTelemetryBanner() {
|
|
|
36347
36976
|
async function initCommand(workspacePath = process.cwd()) {
|
|
36348
36977
|
const spin = ora__default.default({ text: "Initializing Cascade project\u2026", color: "magenta" }).start();
|
|
36349
36978
|
try {
|
|
36350
|
-
const configDir =
|
|
36979
|
+
const configDir = path36__default.default.join(workspacePath, ".cascade");
|
|
36351
36980
|
await fs11__default.default.mkdir(configDir, { recursive: true });
|
|
36352
|
-
const mdPath =
|
|
36981
|
+
const mdPath = path36__default.default.join(workspacePath, "CASCADE.md");
|
|
36353
36982
|
if (!await fileExists(mdPath)) {
|
|
36354
36983
|
await createDefaultCascadeMd(workspacePath);
|
|
36355
36984
|
spin.succeed(chalk8__default.default.green("Created CASCADE.md"));
|
|
36356
36985
|
}
|
|
36357
|
-
const ignorePath =
|
|
36986
|
+
const ignorePath = path36__default.default.join(workspacePath, ".cascadeignore");
|
|
36358
36987
|
if (!await fileExists(ignorePath)) {
|
|
36359
36988
|
await createDefaultIgnoreFile(workspacePath);
|
|
36360
36989
|
spin.succeed(chalk8__default.default.green("Created .cascadeignore"));
|
|
@@ -36363,7 +36992,7 @@ async function initCommand(workspacePath = process.cwd()) {
|
|
|
36363
36992
|
console.log();
|
|
36364
36993
|
console.log(chalk8__default.default.magenta(" \u25C8 Cascade AI \u2014 Project initialized"));
|
|
36365
36994
|
console.log();
|
|
36366
|
-
const configPath =
|
|
36995
|
+
const configPath = path36__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
|
|
36367
36996
|
if (await fileExists(configPath)) {
|
|
36368
36997
|
console.log(chalk8__default.default.yellow(" .cascade/config.json already exists \u2014 launching wizard to reconfigure."));
|
|
36369
36998
|
console.log();
|
|
@@ -36514,8 +37143,8 @@ function fromEnv(env) {
|
|
|
36514
37143
|
return out;
|
|
36515
37144
|
}
|
|
36516
37145
|
async function fromClaudeCode(home, env) {
|
|
36517
|
-
const dir = str(env["CLAUDE_CONFIG_DIR"]) ??
|
|
36518
|
-
const file =
|
|
37146
|
+
const dir = str(env["CLAUDE_CONFIG_DIR"]) ?? path36__default.default.join(home, ".claude");
|
|
37147
|
+
const file = path36__default.default.join(dir, ".credentials.json");
|
|
36519
37148
|
const data = await readJson(file);
|
|
36520
37149
|
if (!data) return [];
|
|
36521
37150
|
const found = [];
|
|
@@ -36539,7 +37168,7 @@ async function fromClaudeCode(home, env) {
|
|
|
36539
37168
|
return found;
|
|
36540
37169
|
}
|
|
36541
37170
|
async function fromCodex(home) {
|
|
36542
|
-
const file =
|
|
37171
|
+
const file = path36__default.default.join(home, ".codex", "auth.json");
|
|
36543
37172
|
const data = await readJson(file);
|
|
36544
37173
|
if (!data) return [];
|
|
36545
37174
|
const apiKey = str(data["OPENAI_API_KEY"]);
|
|
@@ -36562,7 +37191,7 @@ async function fromCodex(home) {
|
|
|
36562
37191
|
return [];
|
|
36563
37192
|
}
|
|
36564
37193
|
async function fromGemini(home) {
|
|
36565
|
-
const file =
|
|
37194
|
+
const file = path36__default.default.join(home, ".gemini", "oauth_creds.json");
|
|
36566
37195
|
const data = await readJson(file);
|
|
36567
37196
|
const accessToken = str(data?.["access_token"]);
|
|
36568
37197
|
if (!accessToken) return [];
|
|
@@ -36578,7 +37207,7 @@ async function fromGemini(home) {
|
|
|
36578
37207
|
}
|
|
36579
37208
|
async function fromCopilot(home) {
|
|
36580
37209
|
for (const name of ["apps.json", "hosts.json"]) {
|
|
36581
|
-
const file =
|
|
37210
|
+
const file = path36__default.default.join(home, ".config", "github-copilot", name);
|
|
36582
37211
|
const data = await readJson(file);
|
|
36583
37212
|
if (!data) continue;
|
|
36584
37213
|
for (const value of Object.values(data)) {
|
|
@@ -36879,7 +37508,7 @@ async function doctorCommand() {
|
|
|
36879
37508
|
checks.push({
|
|
36880
37509
|
label: "Cascade config",
|
|
36881
37510
|
ok: true,
|
|
36882
|
-
detail: `Loaded ${
|
|
37511
|
+
detail: `Loaded ${path36__default.default.join(process.cwd(), CASCADE_CONFIG_FILE)}`
|
|
36883
37512
|
});
|
|
36884
37513
|
const providers = [
|
|
36885
37514
|
{ type: "anthropic", name: "Anthropic" },
|
|
@@ -36989,7 +37618,7 @@ async function checkEndpoint(url) {
|
|
|
36989
37618
|
}
|
|
36990
37619
|
}
|
|
36991
37620
|
async function indexCommand(dirPath) {
|
|
36992
|
-
const workspace =
|
|
37621
|
+
const workspace = path36__default.default.resolve(dirPath || process.cwd());
|
|
36993
37622
|
const cm = new ConfigManager(workspace);
|
|
36994
37623
|
await cm.load();
|
|
36995
37624
|
const config = cm.getConfig();
|
|
@@ -37000,8 +37629,8 @@ async function indexCommand(dirPath) {
|
|
|
37000
37629
|
process.exitCode = 1;
|
|
37001
37630
|
return;
|
|
37002
37631
|
}
|
|
37003
|
-
const dbPath = config.codeIndex?.dbPath ||
|
|
37004
|
-
|
|
37632
|
+
const dbPath = config.codeIndex?.dbPath || path36__default.default.join(workspace, ".cascade", "code-index.db");
|
|
37633
|
+
fs33__default.default.mkdirSync(path36__default.default.dirname(dbPath), { recursive: true });
|
|
37005
37634
|
const db = new Database2__default.default(dbPath);
|
|
37006
37635
|
db.pragma("journal_mode = WAL");
|
|
37007
37636
|
const complete = chatCompleterFromProviders(config.providers);
|
|
@@ -37533,7 +38162,7 @@ function openBrowser2(url) {
|
|
|
37533
38162
|
}
|
|
37534
38163
|
function storePathFor(name) {
|
|
37535
38164
|
const safe = name.replace(/[^a-z0-9._-]/gi, "_").slice(0, 64) || "server";
|
|
37536
|
-
return
|
|
38165
|
+
return path36__default.default.join(os10__default.default.homedir(), ".cascade-ai", "mcp-oauth", `${safe}.json`);
|
|
37537
38166
|
}
|
|
37538
38167
|
function defaultName(url) {
|
|
37539
38168
|
try {
|
|
@@ -37828,6 +38457,7 @@ function applySettingsPayload(config, data) {
|
|
|
37828
38457
|
if (["auto", "T1", "T2", "T3"].includes(a["forceTier"])) config.routing = { ...config.routing ?? {}, forceTier: a["forceTier"] };
|
|
37829
38458
|
if (typeof a["benchmarksLive"] === "boolean") config.benchmarks = { ...config.benchmarks ?? {}, live: a["benchmarksLive"] };
|
|
37830
38459
|
if (["isolate", "worker", "auto"].includes(a["dynamicToolSandbox"])) config.tools = { ...config.tools ?? {}, dynamicToolSandbox: a["dynamicToolSandbox"] };
|
|
38460
|
+
if (typeof a["agentBrowserControl"] === "boolean") config.tools = { ...config.tools ?? {}, agentBrowserControl: a["agentBrowserControl"] };
|
|
37831
38461
|
if (typeof a["factsExtraction"] === "boolean") config.knowledge = { ...config.knowledge ?? {}, factsExtraction: a["factsExtraction"] };
|
|
37832
38462
|
if (typeof a["rememberSessions"] === "boolean") config.memory = { ...config.memory ?? {}, rememberSessions: a["rememberSessions"] };
|
|
37833
38463
|
if (typeof a["enableToolCreation"] === "boolean") config.enableToolCreation = a["enableToolCreation"];
|
|
@@ -37885,6 +38515,7 @@ function settingsSnapshot(config) {
|
|
|
37885
38515
|
forceTier: config.routing?.forceTier,
|
|
37886
38516
|
benchmarksLive: config.benchmarks?.live,
|
|
37887
38517
|
dynamicToolSandbox: config.tools?.dynamicToolSandbox,
|
|
38518
|
+
agentBrowserControl: config.tools?.agentBrowserControl,
|
|
37888
38519
|
factsExtraction: config.knowledge?.factsExtraction,
|
|
37889
38520
|
rememberSessions: config.memory?.rememberSessions,
|
|
37890
38521
|
enableToolCreation: config.enableToolCreation,
|
|
@@ -37920,8 +38551,8 @@ function replaceInPlace(target, source) {
|
|
|
37920
38551
|
}
|
|
37921
38552
|
function writeConfigFile(configPath, config) {
|
|
37922
38553
|
try {
|
|
37923
|
-
|
|
37924
|
-
|
|
38554
|
+
fs33__default.default.mkdirSync(path36__default.default.dirname(configPath), { recursive: true });
|
|
38555
|
+
fs33__default.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
37925
38556
|
return { ok: true };
|
|
37926
38557
|
} catch (err) {
|
|
37927
38558
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -38337,7 +38968,7 @@ function aggregateCostStats(sessions2, opts = {}) {
|
|
|
38337
38968
|
}
|
|
38338
38969
|
|
|
38339
38970
|
// src/dashboard/server.ts
|
|
38340
|
-
var __dirname$1 =
|
|
38971
|
+
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
38972
|
var ORPHANED_ESCALATION_GRACE_MS = 3e4;
|
|
38342
38973
|
var MASK = "***";
|
|
38343
38974
|
function masked(key, value) {
|
|
@@ -38395,6 +39026,14 @@ var DashboardServer = class {
|
|
|
38395
39026
|
* always failing.
|
|
38396
39027
|
*/
|
|
38397
39028
|
currentPageProvider;
|
|
39029
|
+
/**
|
|
39030
|
+
* Supplied by the desktop shell so a run can ACT on that page — click, type,
|
|
39031
|
+
* submit — not just read it. Same reasoning as the provider above: absent in
|
|
39032
|
+
* the CLI, so the tool is not registered rather than present and failing.
|
|
39033
|
+
* Cascade applies the `tools.agentBrowserControl` gate on top of this.
|
|
39034
|
+
*/
|
|
39035
|
+
browserController;
|
|
39036
|
+
browserActorRelease;
|
|
38398
39037
|
activeControllers = /* @__PURE__ */ new Map();
|
|
38399
39038
|
/**
|
|
38400
39039
|
* Run taskIds per chat session — file snapshots are keyed by the run's
|
|
@@ -38456,6 +39095,7 @@ var DashboardServer = class {
|
|
|
38456
39095
|
});
|
|
38457
39096
|
this.socket.onConfigUpdate(async (data) => {
|
|
38458
39097
|
const result = await commitSettings(this.config, data, (committed) => this.persistConfig(committed));
|
|
39098
|
+
if (result.ok) this.settingsChangeHook?.(this.config);
|
|
38459
39099
|
const refused = result.refused.map((r) => ({
|
|
38460
39100
|
type: r.type,
|
|
38461
39101
|
reason: r.reason,
|
|
@@ -38479,6 +39119,7 @@ var DashboardServer = class {
|
|
|
38479
39119
|
const cascade = new Cascade(cfg, this.workspacePath, this.store);
|
|
38480
39120
|
this.activeSessions.set(sessionId, cascade);
|
|
38481
39121
|
if (this.currentPageProvider) cascade.setCurrentPageProvider(this.currentPageProvider);
|
|
39122
|
+
if (this.browserController) cascade.setBrowserController(this.browserController, this.browserActorRelease);
|
|
38482
39123
|
cascade.on("stream:token", (e) => {
|
|
38483
39124
|
this.emitToSessionClients(socketId, sessionId, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
38484
39125
|
});
|
|
@@ -38502,13 +39143,16 @@ var DashboardServer = class {
|
|
|
38502
39143
|
if (ev.requestId) this.clearPendingEscalation(ev.requestId);
|
|
38503
39144
|
this.emitToSessionClients(socketId, sessionId, "escalation:timeout", { sessionId, ...e });
|
|
38504
39145
|
});
|
|
39146
|
+
cascade.on("run:started", (e) => {
|
|
39147
|
+
const id = e.taskId;
|
|
39148
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
39149
|
+
});
|
|
38505
39150
|
try {
|
|
38506
39151
|
const result = await cascade.run({
|
|
38507
39152
|
prompt: runPrompt,
|
|
38508
39153
|
signal: abortController.signal,
|
|
38509
39154
|
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
38510
39155
|
});
|
|
38511
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
38512
39156
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
38513
39157
|
this.captureWhy(sessionId, cascade, result);
|
|
38514
39158
|
this.emitToSessionClients(socketId, sessionId, "session:complete", { sessionId, result });
|
|
@@ -38574,6 +39218,49 @@ var DashboardServer = class {
|
|
|
38574
39218
|
setCurrentPageProvider(provider) {
|
|
38575
39219
|
this.currentPageProvider = provider;
|
|
38576
39220
|
}
|
|
39221
|
+
/** Let runs act on that view, not just read it (desktop only). */
|
|
39222
|
+
setBrowserController(controller, release) {
|
|
39223
|
+
this.browserController = controller;
|
|
39224
|
+
this.browserActorRelease = release;
|
|
39225
|
+
}
|
|
39226
|
+
/**
|
|
39227
|
+
* Told whenever settings are committed through THIS server.
|
|
39228
|
+
*
|
|
39229
|
+
* Settings reach the live config by two routes — the desktop's Electron IPC
|
|
39230
|
+
* handler and this server's `config:update` socket — and both run the same
|
|
39231
|
+
* `commitSettings`. The desktop mirrors `tools.agentBrowserControl` into its
|
|
39232
|
+
* browser module after its own route, but had no way to learn about the
|
|
39233
|
+
* other, so a socket write left the module's copy stale: an enable the module
|
|
39234
|
+
* still refused, or worse a disable the module still honoured for a run
|
|
39235
|
+
* already holding the tool. This is the same sibling-writer drift
|
|
39236
|
+
* `settings-payload.ts` exists to prevent, one layer up.
|
|
39237
|
+
*/
|
|
39238
|
+
settingsChangeHook;
|
|
39239
|
+
onSettingsChanged(hook) {
|
|
39240
|
+
this.settingsChangeHook = hook;
|
|
39241
|
+
}
|
|
39242
|
+
/**
|
|
39243
|
+
* Told when a run ends, so a host holding run-scoped state can release it.
|
|
39244
|
+
*
|
|
39245
|
+
* The desktop uses this to retire a run's browser Stop control: that has to
|
|
39246
|
+
* outlive each individual action (the gap between two actions is when the
|
|
39247
|
+
* user most wants it) but must not outlive the run.
|
|
39248
|
+
*/
|
|
39249
|
+
/**
|
|
39250
|
+
* Fired when a run ends, carrying BOTH identifiers explicitly.
|
|
39251
|
+
*
|
|
39252
|
+
* They are different things and conflating them was a live bug: a chat
|
|
39253
|
+
* `sessionId` can contain many runs (hence `sessionTaskIds`), while
|
|
39254
|
+
* `Cascade.run()` mints a fresh random `taskId` per run — and it is the
|
|
39255
|
+
* taskId that tools see as `ToolExecuteOptions.sessionId`. Passing only the
|
|
39256
|
+
* chat id meant browser cleanup keyed on the task id matched nothing, so a
|
|
39257
|
+
* finished run kept its Stop banner armed and held its browser lease until
|
|
39258
|
+
* the deadlock ceiling.
|
|
39259
|
+
*/
|
|
39260
|
+
runEndedHook;
|
|
39261
|
+
onRunEnded(hook) {
|
|
39262
|
+
this.runEndedHook = hook;
|
|
39263
|
+
}
|
|
38577
39264
|
async start() {
|
|
38578
39265
|
const isLoopback = this.host === "127.0.0.1" || this.host === "::1" || this.host === "localhost";
|
|
38579
39266
|
if (!isLoopback) {
|
|
@@ -38656,7 +39343,7 @@ var DashboardServer = class {
|
|
|
38656
39343
|
* silently and still typecheck.
|
|
38657
39344
|
*/
|
|
38658
39345
|
persistConfig(config) {
|
|
38659
|
-
const result = writeConfigFile(
|
|
39346
|
+
const result = writeConfigFile(path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE), config);
|
|
38660
39347
|
if (!result.ok) console.warn(`[dashboard] Failed to persist config: ${result.error}`);
|
|
38661
39348
|
return result;
|
|
38662
39349
|
}
|
|
@@ -38675,7 +39362,7 @@ var DashboardServer = class {
|
|
|
38675
39362
|
*/
|
|
38676
39363
|
syncGlobalCredentials() {
|
|
38677
39364
|
try {
|
|
38678
|
-
saveGlobalCredentials(
|
|
39365
|
+
saveGlobalCredentials(path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
|
|
38679
39366
|
} catch (err) {
|
|
38680
39367
|
console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
|
|
38681
39368
|
}
|
|
@@ -38690,15 +39377,15 @@ var DashboardServer = class {
|
|
|
38690
39377
|
resolveDashboardSecret() {
|
|
38691
39378
|
const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
|
|
38692
39379
|
if (fromConfig) return fromConfig;
|
|
38693
|
-
const secretPath =
|
|
39380
|
+
const secretPath = path36__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
|
|
38694
39381
|
try {
|
|
38695
|
-
if (
|
|
38696
|
-
const existing =
|
|
39382
|
+
if (fs33__default.default.existsSync(secretPath)) {
|
|
39383
|
+
const existing = fs33__default.default.readFileSync(secretPath, "utf-8").trim();
|
|
38697
39384
|
if (existing.length >= 16) return existing;
|
|
38698
39385
|
}
|
|
38699
39386
|
const generated = crypto.randomUUID();
|
|
38700
|
-
|
|
38701
|
-
|
|
39387
|
+
fs33__default.default.mkdirSync(path36__default.default.dirname(secretPath), { recursive: true });
|
|
39388
|
+
fs33__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
|
|
38702
39389
|
if (this.config.dashboard.auth) {
|
|
38703
39390
|
console.warn(
|
|
38704
39391
|
`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 +39412,7 @@ var DashboardServer = class {
|
|
|
38725
39412
|
// ── Setup ─────────────────────────────────────
|
|
38726
39413
|
getGlobalStore() {
|
|
38727
39414
|
if (!this.globalStore) {
|
|
38728
|
-
const globalDbPath =
|
|
39415
|
+
const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
38729
39416
|
this.globalStore = new MemoryStore(globalDbPath);
|
|
38730
39417
|
}
|
|
38731
39418
|
return this.globalStore;
|
|
@@ -38764,6 +39451,8 @@ var DashboardServer = class {
|
|
|
38764
39451
|
}
|
|
38765
39452
|
/** Record run end: the assistant reply (when there is one) and the runtime row's final status. */
|
|
38766
39453
|
persistRunEnd(sessionId, title, latestPrompt, reply, status, result) {
|
|
39454
|
+
const endedTaskId = result?.taskId ?? this.sessionTaskIds.get(sessionId)?.at(-1);
|
|
39455
|
+
this.runEndedHook?.({ sessionId, ...endedTaskId ? { taskId: endedTaskId } : {} });
|
|
38767
39456
|
try {
|
|
38768
39457
|
if (reply && reply.trim()) {
|
|
38769
39458
|
this.store.addMessage({ id: crypto.randomUUID(), sessionId, role: "assistant", content: reply, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -38857,6 +39546,7 @@ var DashboardServer = class {
|
|
|
38857
39546
|
const prompt = task.prompt;
|
|
38858
39547
|
const title = this.persistRunStart(sessionId, `[${task.name}] ${prompt}`);
|
|
38859
39548
|
const cascade = new Cascade(this.config, task.workspacePath ?? this.workspacePath, this.store);
|
|
39549
|
+
cascade.setUnattended(true);
|
|
38860
39550
|
this.activeSessions.set(sessionId, cascade);
|
|
38861
39551
|
cascade.on("tier:status", (e) => {
|
|
38862
39552
|
this.socket.broadcast("tier:status", { sessionId, ...e });
|
|
@@ -38864,13 +39554,16 @@ var DashboardServer = class {
|
|
|
38864
39554
|
cascade.on("peer:message", (e) => {
|
|
38865
39555
|
this.socket.emitPeerMessage(e);
|
|
38866
39556
|
});
|
|
39557
|
+
cascade.on("run:started", (e) => {
|
|
39558
|
+
const id = e.taskId;
|
|
39559
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
39560
|
+
});
|
|
38867
39561
|
try {
|
|
38868
39562
|
const result = await cascade.run({
|
|
38869
39563
|
prompt,
|
|
38870
39564
|
identityId: task.identityId,
|
|
38871
39565
|
approvalCallback: async () => ({ approved: true, always: false })
|
|
38872
39566
|
});
|
|
38873
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
38874
39567
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
38875
39568
|
this.captureWhy(sessionId, cascade, result);
|
|
38876
39569
|
this.socket.broadcast("session:complete", { sessionId, result });
|
|
@@ -39105,12 +39798,12 @@ ${prompt}`;
|
|
|
39105
39798
|
}
|
|
39106
39799
|
}
|
|
39107
39800
|
watchRuntimeChanges() {
|
|
39108
|
-
const workspaceDbPath =
|
|
39109
|
-
const globalDbPath =
|
|
39801
|
+
const workspaceDbPath = path36__default.default.join(this.workspacePath, CASCADE_DB_FILE);
|
|
39802
|
+
const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39110
39803
|
const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
|
|
39111
39804
|
for (const watchPath of watchPaths) {
|
|
39112
|
-
if (!
|
|
39113
|
-
|
|
39805
|
+
if (!fs33__default.default.existsSync(watchPath)) continue;
|
|
39806
|
+
fs33__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
|
|
39114
39807
|
this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
|
|
39115
39808
|
});
|
|
39116
39809
|
}
|
|
@@ -39232,7 +39925,7 @@ ${prompt}`;
|
|
|
39232
39925
|
const sessionId = req.params.id;
|
|
39233
39926
|
this.store.deleteSession(sessionId);
|
|
39234
39927
|
this.store.deleteRuntimeSession(sessionId);
|
|
39235
|
-
const globalDbPath =
|
|
39928
|
+
const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39236
39929
|
const globalStore = new MemoryStore(globalDbPath);
|
|
39237
39930
|
try {
|
|
39238
39931
|
globalStore.deleteRuntimeSession(sessionId);
|
|
@@ -39325,7 +40018,7 @@ ${prompt}`;
|
|
|
39325
40018
|
});
|
|
39326
40019
|
this.app.delete("/api/sessions", auth2, (req, res) => {
|
|
39327
40020
|
const body = req.body;
|
|
39328
|
-
const globalDbPath =
|
|
40021
|
+
const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39329
40022
|
if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
|
|
39330
40023
|
const globalStore = new MemoryStore(globalDbPath);
|
|
39331
40024
|
try {
|
|
@@ -39348,7 +40041,7 @@ ${prompt}`;
|
|
|
39348
40041
|
});
|
|
39349
40042
|
this.app.delete("/api/runtime", auth2, (_req, res) => {
|
|
39350
40043
|
this.store.deleteAllRuntimeNodes();
|
|
39351
|
-
const globalDbPath =
|
|
40044
|
+
const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39352
40045
|
const globalStore = new MemoryStore(globalDbPath);
|
|
39353
40046
|
try {
|
|
39354
40047
|
globalStore.deleteAllRuntimeNodes();
|
|
@@ -39433,12 +40126,12 @@ ${prompt}`;
|
|
|
39433
40126
|
if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
|
|
39434
40127
|
if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
|
|
39435
40128
|
try {
|
|
39436
|
-
const configPath =
|
|
39437
|
-
const existing =
|
|
40129
|
+
const configPath = path36__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
40130
|
+
const existing = fs33__default.default.existsSync(configPath) ? JSON.parse(fs33__default.default.readFileSync(configPath, "utf-8")) : {};
|
|
39438
40131
|
const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
|
|
39439
40132
|
const tmp = configPath + ".tmp";
|
|
39440
|
-
|
|
39441
|
-
|
|
40133
|
+
fs33__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
|
|
40134
|
+
fs33__default.default.renameSync(tmp, configPath);
|
|
39442
40135
|
res.json({ ok: true });
|
|
39443
40136
|
} catch (err) {
|
|
39444
40137
|
res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
|
|
@@ -39466,7 +40159,7 @@ ${prompt}`;
|
|
|
39466
40159
|
this.app.get("/api/runtime", auth2, (req, res) => {
|
|
39467
40160
|
const scope = req.query["scope"] ?? "workspace";
|
|
39468
40161
|
if (scope === "global") {
|
|
39469
|
-
const globalDbPath =
|
|
40162
|
+
const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39470
40163
|
const globalStore = new MemoryStore(globalDbPath);
|
|
39471
40164
|
try {
|
|
39472
40165
|
res.json({
|
|
@@ -39503,6 +40196,7 @@ ${prompt}`;
|
|
|
39503
40196
|
const cascade = new Cascade(this.config, this.workspacePath, this.store);
|
|
39504
40197
|
this.activeSessions.set(sessionId, cascade);
|
|
39505
40198
|
if (this.currentPageProvider) cascade.setCurrentPageProvider(this.currentPageProvider);
|
|
40199
|
+
if (this.browserController) cascade.setBrowserController(this.browserController, this.browserActorRelease);
|
|
39506
40200
|
cascade.on("stream:token", (e) => {
|
|
39507
40201
|
this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
39508
40202
|
});
|
|
@@ -39526,13 +40220,16 @@ ${prompt}`;
|
|
|
39526
40220
|
if (ev.requestId) this.clearPendingEscalation(ev.requestId);
|
|
39527
40221
|
this.socket.broadcastToRoom(`session:${sessionId}`, "escalation:timeout", { sessionId, ...e });
|
|
39528
40222
|
});
|
|
40223
|
+
cascade.on("run:started", (e) => {
|
|
40224
|
+
const id = e.taskId;
|
|
40225
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
40226
|
+
});
|
|
39529
40227
|
try {
|
|
39530
40228
|
const result = await cascade.run({
|
|
39531
40229
|
prompt: runPrompt,
|
|
39532
40230
|
identityId: body.identityId,
|
|
39533
40231
|
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
39534
40232
|
});
|
|
39535
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
39536
40233
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
39537
40234
|
this.captureWhy(sessionId, cascade, result);
|
|
39538
40235
|
this.socket.broadcast("cost:update", {
|
|
@@ -39807,13 +40504,13 @@ ${prompt}`;
|
|
|
39807
40504
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
39808
40505
|
}
|
|
39809
40506
|
});
|
|
39810
|
-
const prodPath =
|
|
39811
|
-
const devPath =
|
|
39812
|
-
const webDistPath =
|
|
39813
|
-
if (
|
|
40507
|
+
const prodPath = path36__default.default.resolve(__dirname$1, "../web/dist");
|
|
40508
|
+
const devPath = path36__default.default.resolve(__dirname$1, "../../web/dist");
|
|
40509
|
+
const webDistPath = fs33__default.default.existsSync(prodPath) ? prodPath : devPath;
|
|
40510
|
+
if (fs33__default.default.existsSync(webDistPath)) {
|
|
39814
40511
|
this.app.use(express__default.default.static(webDistPath));
|
|
39815
40512
|
this.app.get("*", (_req, res) => {
|
|
39816
|
-
res.sendFile(
|
|
40513
|
+
res.sendFile(path36__default.default.join(webDistPath, "index.html"));
|
|
39817
40514
|
});
|
|
39818
40515
|
} else {
|
|
39819
40516
|
this.app.get("/", (_req, res) => {
|
|
@@ -39834,7 +40531,7 @@ init_constants();
|
|
|
39834
40531
|
async function dashboardCommand(config, workspacePath = process.cwd()) {
|
|
39835
40532
|
const port = config.dashboard.port ?? DEFAULT_DASHBOARD_PORT;
|
|
39836
40533
|
const spin = ora__default.default({ text: `Starting dashboard on port ${port}\u2026`, color: "magenta" }).start();
|
|
39837
|
-
const store = new MemoryStore(
|
|
40534
|
+
const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
|
|
39838
40535
|
const server = new DashboardServer(config, store, workspacePath);
|
|
39839
40536
|
server.watchRuntimeChanges();
|
|
39840
40537
|
const gThis = globalThis;
|
|
@@ -39866,7 +40563,7 @@ init_constants();
|
|
|
39866
40563
|
function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
39867
40564
|
const identity = new commander.Command("identity").alias("id").description("Manage Cascade identities");
|
|
39868
40565
|
identity.command("list").description("List all available identities").action(() => {
|
|
39869
|
-
const store = new MemoryStore(
|
|
40566
|
+
const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
|
|
39870
40567
|
try {
|
|
39871
40568
|
const identities = store.listIdentities();
|
|
39872
40569
|
console.log(chalk8__default.default.bold("\n Identities:"));
|
|
@@ -39886,7 +40583,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
|
39886
40583
|
}
|
|
39887
40584
|
});
|
|
39888
40585
|
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(
|
|
40586
|
+
const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
|
|
39890
40587
|
try {
|
|
39891
40588
|
if (options.default) {
|
|
39892
40589
|
const existingDefault = store.getDefaultIdentity();
|
|
@@ -39912,7 +40609,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
|
39912
40609
|
}
|
|
39913
40610
|
});
|
|
39914
40611
|
identity.command("set-default <name>").description("Set an identity as default by name or ID").action((query) => {
|
|
39915
|
-
const store = new MemoryStore(
|
|
40612
|
+
const store = new MemoryStore(path36__default.default.join(workspacePath, CASCADE_DB_FILE));
|
|
39916
40613
|
try {
|
|
39917
40614
|
const identities = store.listIdentities();
|
|
39918
40615
|
const match = identities.find((i) => i.id === query || i.name.toLowerCase() === query.toLowerCase());
|
|
@@ -40061,8 +40758,8 @@ async function exportCommand(options = {}) {
|
|
|
40061
40758
|
let store;
|
|
40062
40759
|
try {
|
|
40063
40760
|
const workspacePath = options.workspacePath ?? process.cwd();
|
|
40064
|
-
const workspaceDbPath =
|
|
40065
|
-
const globalDbPath =
|
|
40761
|
+
const workspaceDbPath = path36__default.default.join(workspacePath, CASCADE_DB_FILE);
|
|
40762
|
+
const globalDbPath = path36__default.default.join(os10__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
|
|
40066
40763
|
let dbPath = globalDbPath;
|
|
40067
40764
|
try {
|
|
40068
40765
|
await fs11__default.default.access(workspaceDbPath);
|
|
@@ -40104,7 +40801,7 @@ async function exportCommand(options = {}) {
|
|
|
40104
40801
|
const ext = format === "json" ? ".json" : ".md";
|
|
40105
40802
|
const safeName = session.title.replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").slice(0, 60);
|
|
40106
40803
|
const defaultFile = `cascade-export-${safeName}${ext}`;
|
|
40107
|
-
const outPath = options.output ?
|
|
40804
|
+
const outPath = options.output ? path36__default.default.resolve(options.output) : path36__default.default.join(process.cwd(), defaultFile);
|
|
40108
40805
|
await fs11__default.default.writeFile(outPath, content, "utf-8");
|
|
40109
40806
|
spin.succeed(chalk8__default.default.green(`Exported to ${chalk8__default.default.white(outPath)}`));
|
|
40110
40807
|
const messageCount = Array.isArray(session.messages) ? session.messages.length : 0;
|
|
@@ -40244,7 +40941,7 @@ var SHOW_CURSOR = "\x1B[?25h";
|
|
|
40244
40941
|
function writeNow(stdout, text) {
|
|
40245
40942
|
if (typeof stdout.fd === "number") {
|
|
40246
40943
|
try {
|
|
40247
|
-
|
|
40944
|
+
fs33__default.default.writeSync(stdout.fd, text);
|
|
40248
40945
|
return;
|
|
40249
40946
|
} catch {
|
|
40250
40947
|
}
|
|
@@ -40267,11 +40964,11 @@ function restoreTerminal(stdin, stdout) {
|
|
|
40267
40964
|
dotenv__default.default.config();
|
|
40268
40965
|
function warnIfBuildIsStale() {
|
|
40269
40966
|
try {
|
|
40270
|
-
const here =
|
|
40967
|
+
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
40968
|
for (const rel of ["..", "../.."]) {
|
|
40272
|
-
const pkgPath =
|
|
40273
|
-
if (!
|
|
40274
|
-
const pkg = JSON.parse(
|
|
40969
|
+
const pkgPath = path36__default.default.join(here, rel, "package.json");
|
|
40970
|
+
if (!fs33__default.default.existsSync(pkgPath)) continue;
|
|
40971
|
+
const pkg = JSON.parse(fs33__default.default.readFileSync(pkgPath, "utf-8"));
|
|
40275
40972
|
if (pkg.name !== "cascade-ai") continue;
|
|
40276
40973
|
if (pkg.version && pkg.version !== CASCADE_VERSION) {
|
|
40277
40974
|
console.error(
|